Documented MixinStake.sol

This commit is contained in:
Greg Hysen
2019-06-27 21:48:57 -07:00
parent 42430290d5
commit 93844343de
5 changed files with 85 additions and 21 deletions

View File

@@ -44,33 +44,90 @@ contract MixinStake is
MixinTimelockedStake
{
/// @dev This mixin contains logic for managing ZRX tokens and Stake.
/// Stake is minted when ZRX is deposited and burned when ZRX is withdrawn.
/// Stake can exist in one of many states:
/// 1. Activated
/// 2. Activated & Delegated
/// 3. Deactivated & Timelocked
/// 4. Deactivated & Withdrawable
///
/// -- State Definitions --
/// Activated Stake
/// Stake in this state can be used as a utility within the 0x ecosystem.
/// For example, it carries weight when computing fee-based rewards (see MixinExchangeFees).
/// In the future, it may be used to participate in the 0x governance system.
///
/// Activated & Delegated Stake
/// Stake in this state also serves as a utility that is shared between the delegator and delegate.
/// For example, if delegated to a staking pool then it carries weight when computing fee-based rewards for
/// the staking pool; however, in this case, delegated stake carries less weight that regular stake (see MixinStakingPool).
///
/// Deactivated & Timelocked Stake
/// Stake in this state cannot be used as a utility within the 0x ecosystem.
/// Stake is timelocked when it moves out of activated states (Activated / Activated & Delagated).
/// By limiting the portability of stake, we mitigate undesirable behavior such as switching staking pools
/// in the middle of an epoch.
///
/// Deactivated & Withdrawable
/// Stake in this state cannot be used as a utility with in the 0x ecosystem.
/// This stake can, however, be burned and withdrawn as Zrx tokens.
/// ----------------------------
///
/// -- Valid State Transtions --
/// Activated -> Deactivated & Timelocked
///
/// Activated & Delegated -> Deactivated & Timelocked
///
/// Deactivated & Timelocked -> Deactivated & Withdrawable
///
/// Deactivated & Withdrawable -> Activated
/// Deactivated & Withdrawable -> Activated & Delegated
/// Deactivated & Withdrawable -> Deactivated & Withdrawable
/// ----------------------------
///
/// Freshly minted stake is in the "Deactvated & Withdrawable" State, so it can
/// either be activated, delegated or withdrawn.
/// See MixinDelegatedStake and MixinTimelockedStake for more on respective state transitions.
using LibSafeMath for uint256;
function deposit(uint256 amount)
/// @dev Deposit Zrx. This mints stake for the sender that is in the "Deactivated & Withdrawable" state.
/// @param amount of Zrx to deposit / Stake to mint.
function depositZrxAndMintDeactivatedStake(uint256 amount)
external
{
_mintStake(msg.sender, amount);
}
function depositAndStake(uint256 amount)
/// @dev Deposit Zrx and mint stake in the activated stake.
/// This is a convenience function, and can be used in-place of
/// calling `depositZrxAndMintDeactivatedStake` and `activateStake`.
/// This mints stake for the sender that is in the "Activated" state.
/// @param amount of Zrx to deposit / Stake to mint.
function depositZrxAndMintActivatedStake(uint256 amount)
external
{
_mintStake(msg.sender, amount);
activateStake(amount);
}
function withdraw(uint256 amount)
/// @dev Burns deactivated stake and withdraws the corresponding amount of Zrx.
/// @param amount of Stake to burn / Zrx to withdraw
function burnDeactivatedStakeAndWithdrawZrx(uint256 amount)
external
{
address owner = msg.sender;
_syncTimelockedStake(owner);
require(
getDeactivatedStake(owner) >= amount,
amount <= getDeactivatedStake(owner),
"INSUFFICIENT_BALANCE"
);
_burnStake(owner, amount);
}
/// @dev Activates stake that is presently in the Deactivated & Withdrawable state.
/// @param amount of Stake to activate.
function activateStake(uint256 amount)
public
{
@@ -84,6 +141,8 @@ contract MixinStake is
totalActivatedStake = totalActivatedStake._add(amount);
}
/// @dev Deactivate & Timelock stake that is currently in the Activated state.
/// @param amount of Stake to deactivate and timelock.
function deactivateAndTimelockStake(uint256 amount)
public
{
@@ -98,7 +157,9 @@ contract MixinStake is
_timelockStake(owner, amount);
}
/// @dev Mints Stake in the Deactivated & Withdrawable state.
/// @param owner to mint Stake for.
/// @param amount of Stake to mint.
function _mintStake(address owner, uint256 amount)
internal
{
@@ -115,6 +176,9 @@ contract MixinStake is
);
}
/// @dev Burns Stake in the Deactivated & Withdrawable state.
/// @param owner to mint Stake for.
/// @param amount of Stake to mint.
function _burnStake(address owner, uint256 amount)
internal
{

View File

@@ -17,16 +17,16 @@ export class StakerActor extends BaseActor {
constructor(owner: string, stakingWrapper: StakingWrapper) {
super(owner, stakingWrapper);
}
public async depositAsync(amount: BigNumber, revertReason?: RevertReason): Promise<void> {
await this._stakingWrapper.depositAsync(this._owner, amount);
public async depositZrxAndMintDeactivatedStakeAsync(amount: BigNumber, revertReason?: RevertReason): Promise<void> {
await this._stakingWrapper.depositZrxAndMintDeactivatedStakeAsync(this._owner, amount);
throw new Error('Checks Unimplemented');
}
public async depositAndStakeAsync(amount: BigNumber, revertReason?: RevertReason): Promise<void> {
public async depositZrxAndMintActivatedStakeAsync(amount: BigNumber, revertReason?: RevertReason): Promise<void> {
// query init balances
const initZrxBalanceOfVault = await this._stakingWrapper.getZrxTokenBalanceOfZrxVaultAsync();
const initStakerBalances = await this.getBalancesAsync();
// deposit stake
const txReceiptPromise = this._stakingWrapper.depositAndStakeAsync(this._owner, amount);
const txReceiptPromise = this._stakingWrapper.depositZrxAndMintActivatedStakeAsync(this._owner, amount);
if (revertReason !== undefined) {
await expectTransactionFailedAsync(txReceiptPromise, revertReason);
return;
@@ -81,12 +81,12 @@ export class StakerActor extends BaseActor {
expectedStakerBalances.deactivatedStakeBalance = initStakerBalances.deactivatedStakeBalance.plus(amount);
await this.assertBalancesAsync(expectedStakerBalances);
}
public async withdrawAsync(amount: BigNumber, revertReason?: RevertReason): Promise<void> {
public async burnDeactivatedStakeAndWithdrawZrxAsync(amount: BigNumber, revertReason?: RevertReason): Promise<void> {
// query init balances
const initZrxBalanceOfVault = await this._stakingWrapper.getZrxTokenBalanceOfZrxVaultAsync();
const initStakerBalances = await this.getBalancesAsync();
// withdraw stake
const txReceiptPromise = this._stakingWrapper.withdrawAsync(this._owner, amount);
const txReceiptPromise = this._stakingWrapper.burnDeactivatedStakeAndWithdrawZrxAsync(this._owner, amount);
if (revertReason !== undefined) {
await expectTransactionFailedAsync(txReceiptPromise, revertReason);
return;

View File

@@ -66,7 +66,7 @@ describe('Staking & Delegating', () => {
const amountToWithdraw = StakingWrapper.toBaseUnitAmount(1.5);
// run test - this actor will validate its own state
const staker = new StakerActor(stakers[0], stakingWrapper);
await staker.depositAndStakeAsync(amountToStake);
await staker.depositZrxAndMintActivatedStakeAsync(amountToStake);
await staker.deactivateAndTimelockStakeAsync(amountToDeactivate);
// note - we cannot re-activate this timelocked stake until at least one full timelock period has passed.
// attempting to do so should revert.
@@ -79,7 +79,7 @@ describe('Staking & Delegating', () => {
await staker.forceTimelockSyncAsync();
// now we can activate stake
await staker.activateStakeAsync(amountToReactivate);
await staker.withdrawAsync(amountToWithdraw);
await staker.burnDeactivatedStakeAndWithdrawZrxAsync(amountToWithdraw);
});
});
@@ -108,7 +108,7 @@ describe('Staking & Delegating', () => {
await delegator.forceTimelockSyncAsync();
// now we can activate stake
await delegator.activateAndDelegateStakeAsync(poolId, amountToReactivate);
await delegator.withdrawAsync(amountToWithdraw);
await delegator.burnDeactivatedStakeAndWithdrawZrxAsync(amountToWithdraw);
});
});
});

View File

@@ -131,7 +131,7 @@ export class Simulation {
this._poolOperatorsAsDelegators.push(poolOperatorAsDelegator);
// add stake to the operator's pool
const amountOfStake = p.stakeByPoolOperator[i];
await poolOperatorAsDelegator.depositAndStakeAsync(amountOfStake);
await poolOperatorAsDelegator.depositZrxAndMintActivatedStakeAsync(amountOfStake);
}
}

View File

@@ -180,13 +180,13 @@ export class StakingWrapper {
return balance;
}
///// STAKE /////
public async depositAsync(owner: string, amount: BigNumber): Promise<TransactionReceiptWithDecodedLogs> {
const calldata = this.getStakingContract().deposit.getABIEncodedTransactionData(amount);
public async depositZrxAndMintDeactivatedStakeAsync(owner: string, amount: BigNumber): Promise<TransactionReceiptWithDecodedLogs> {
const calldata = this.getStakingContract().depositZrxAndMintDeactivatedStake.getABIEncodedTransactionData(amount);
const txReceipt = await this._executeTransactionAsync(calldata, owner);
return txReceipt;
}
public async depositAndStakeAsync(owner: string, amount: BigNumber): Promise<TransactionReceiptWithDecodedLogs> {
const calldata = this.getStakingContract().depositAndStake.getABIEncodedTransactionData(amount);
public async depositZrxAndMintActivatedStakeAsync(owner: string, amount: BigNumber): Promise<TransactionReceiptWithDecodedLogs> {
const calldata = this.getStakingContract().depositZrxAndMintActivatedStake.getABIEncodedTransactionData(amount);
const txReceipt = await this._executeTransactionAsync(calldata, owner);
return txReceipt;
}
@@ -236,8 +236,8 @@ export class StakingWrapper {
const txReceipt = await this._executeTransactionAsync(calldata, owner, new BigNumber(0), true);
return txReceipt;
}
public async withdrawAsync(owner: string, amount: BigNumber): Promise<TransactionReceiptWithDecodedLogs> {
const calldata = this.getStakingContract().withdraw.getABIEncodedTransactionData(amount);
public async burnDeactivatedStakeAndWithdrawZrxAsync(owner: string, amount: BigNumber): Promise<TransactionReceiptWithDecodedLogs> {
const calldata = this.getStakingContract().burnDeactivatedStakeAndWithdrawZrx.getABIEncodedTransactionData(amount);
const txReceipt = await this._executeTransactionAsync(calldata, owner);
return txReceipt;
}