Documentation for MixinDelegatedStake
This commit is contained in:
@@ -47,10 +47,10 @@ contract Staking is
|
||||
MixinStakeBalances,
|
||||
MixinStakingPool,
|
||||
MixinTimelockedStake,
|
||||
MixinStake,
|
||||
MixinDelegatedStake,
|
||||
MixinStakingPoolRewards,
|
||||
MixinExchangeFees
|
||||
MixinExchangeFees,
|
||||
MixinStake,
|
||||
MixinDelegatedStake
|
||||
{
|
||||
|
||||
// this contract can receive ETH
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../libs/LibSafeMath.sol";
|
||||
import "../libs/LibRewardMath.sol";
|
||||
import "../immutable/MixinConstants.sol";
|
||||
import "../immutable/MixinStorage.sol";
|
||||
import "../interfaces/IStakingEvents.sol";
|
||||
@@ -29,6 +28,7 @@ import "./MixinScheduler.sol";
|
||||
import "./MixinStakeBalances.sol";
|
||||
import "./MixinTimelockedStake.sol";
|
||||
import "./MixinStake.sol";
|
||||
import "./MixinStakingPoolRewards.sol";
|
||||
|
||||
|
||||
contract MixinDelegatedStake is
|
||||
@@ -42,21 +42,40 @@ contract MixinDelegatedStake is
|
||||
MixinScheduler,
|
||||
MixinStakingPoolRewardVault,
|
||||
MixinStakeBalances,
|
||||
MixinStakingPool,
|
||||
MixinTimelockedStake,
|
||||
MixinStakingPoolRewards,
|
||||
MixinStake
|
||||
{
|
||||
|
||||
/// @dev This mixin contains logic for managing delegated stake.
|
||||
/// **** Read MixinStake before continuing ****
|
||||
/// Stake can be delegated to staking pools in order to trustlessly
|
||||
/// leverage the weight of several stakers. The meaning of this
|
||||
/// leverage depends on the context in which stake the is being utilized.
|
||||
/// For example, the amount of fee-based rewards a market maker receives
|
||||
/// is correlated to how much stake has been delegated to their pool (see MixinExchangeFees).
|
||||
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
function depositAndDelegate(bytes32 poolId, uint256 amount)
|
||||
/// @dev Deposit Zrx and mint stake in the "Activated & Delegated" state.
|
||||
/// Note that the sender must be payable, as they may receive rewards in ETH from their staking pool.
|
||||
/// @param poolId Unique Id of staking pool to delegate stake to.
|
||||
/// @param amount of Zrx to deposit / Stake to mint.
|
||||
function depositZrxAndDelegateToStakingPool(bytes32 poolId, uint256 amount)
|
||||
external
|
||||
{
|
||||
address owner = msg.sender;
|
||||
address payable owner = msg.sender;
|
||||
_mintStake(owner, amount);
|
||||
activateStake(amount);
|
||||
_delegateStake(owner, poolId, amount);
|
||||
}
|
||||
|
||||
/// @dev Activates stake that is presently in the Deactivated & Withdrawable state.
|
||||
/// Note that the sender must be payable, as they may receive rewards in ETH from their staking pool.
|
||||
/// The newly activated stake is then delegated to a staking pool.
|
||||
/// @param poolId Unique Id of staking pool to delegate stake to.
|
||||
/// @param amount of Stake to activate & delegate.
|
||||
function activateAndDelegateStake(
|
||||
bytes32 poolId,
|
||||
uint256 amount
|
||||
@@ -64,10 +83,14 @@ contract MixinDelegatedStake is
|
||||
public
|
||||
{
|
||||
activateStake(amount);
|
||||
address owner = msg.sender;
|
||||
address payable owner = msg.sender;
|
||||
_delegateStake(owner, poolId, amount);
|
||||
}
|
||||
|
||||
/// @dev Deactivate & Timelock stake that is currently in the Activated & Delegated state.
|
||||
/// Note that the sender must be payable, as they may receive rewards in ETH from their staking pool.
|
||||
/// @param poolId Unique Id of staking pool that the Stake is currently delegated to.
|
||||
/// @param amount of Stake to deactivate and timelock.
|
||||
function deactivateAndTimelockDelegatedStake(bytes32 poolId, uint256 amount)
|
||||
public
|
||||
{
|
||||
@@ -76,7 +99,11 @@ contract MixinDelegatedStake is
|
||||
_undelegateStake(owner, poolId, amount);
|
||||
}
|
||||
|
||||
function _delegateStake(address owner, bytes32 poolId, uint256 amount)
|
||||
/// @dev Delegates stake from `owner` to the staking pool with id `poolId`
|
||||
/// @param owner of Stake
|
||||
/// @param poolId Unique Id of staking pool to delegate stake to.
|
||||
/// @param amount of Stake to delegate.
|
||||
function _delegateStake(address payable owner, bytes32 poolId, uint256 amount)
|
||||
private
|
||||
{
|
||||
// take snapshot of parameters before any computation
|
||||
@@ -84,6 +111,14 @@ contract MixinDelegatedStake is
|
||||
uint256 _delegatedStakeToPoolByOwner = delegatedStakeToPoolByOwner[owner][poolId];
|
||||
uint256 _delegatedStakeByPoolId = delegatedStakeByPoolId[poolId];
|
||||
|
||||
// join staking pool
|
||||
_joinStakingPool(
|
||||
poolId,
|
||||
owner,
|
||||
amount,
|
||||
_delegatedStakeByPoolId
|
||||
);
|
||||
|
||||
// increment how much stake the owner has delegated
|
||||
delegatedStakeByOwner[owner] = _delegatedStakeByOwner._add(amount);
|
||||
|
||||
@@ -92,20 +127,6 @@ contract MixinDelegatedStake is
|
||||
|
||||
// increment how much stake has been delegated to pool
|
||||
delegatedStakeByPoolId[poolId] = _delegatedStakeByPoolId._add(amount);
|
||||
|
||||
// update delegator's share of reward pool
|
||||
// note that this uses the snapshot parameters
|
||||
uint256 poolBalance = getBalanceOfMembersInStakingPoolRewardVault(poolId);
|
||||
uint256 buyIn = LibRewardMath._computeBuyInDenominatedInShadowAsset(
|
||||
amount,
|
||||
_delegatedStakeByPoolId,
|
||||
shadowRewardsByPoolId[poolId],
|
||||
poolBalance
|
||||
);
|
||||
if (buyIn > 0) {
|
||||
shadowRewardsInPoolByOwner[owner][poolId] = shadowRewardsInPoolByOwner[owner][poolId]._add(buyIn);
|
||||
shadowRewardsByPoolId[poolId] = shadowRewardsByPoolId[poolId]._add(buyIn);
|
||||
}
|
||||
}
|
||||
|
||||
// question - should we then return the amount withdrawn?
|
||||
@@ -117,6 +138,15 @@ contract MixinDelegatedStake is
|
||||
uint256 _delegatedStakeToPoolByOwner = delegatedStakeToPoolByOwner[owner][poolId];
|
||||
uint256 _delegatedStakeByPoolId = delegatedStakeByPoolId[poolId];
|
||||
|
||||
// leave the staking pool
|
||||
_leaveStakingPool(
|
||||
poolId,
|
||||
owner,
|
||||
amount,
|
||||
_delegatedStakeToPoolByOwner,
|
||||
_delegatedStakeByPoolId
|
||||
);
|
||||
|
||||
// decrement how much stake the owner has delegated
|
||||
delegatedStakeByOwner[owner] = _delegatedStakeByOwner._sub(amount);
|
||||
|
||||
@@ -125,39 +155,5 @@ contract MixinDelegatedStake is
|
||||
|
||||
// decrement how much stake has been delegated to pool
|
||||
delegatedStakeByPoolId[poolId] = _delegatedStakeByPoolId._sub(amount);
|
||||
|
||||
// get payout
|
||||
uint256 poolBalance = getBalanceOfMembersInStakingPoolRewardVault(poolId);
|
||||
uint256 payoutInRealAsset;
|
||||
uint256 payoutInShadowAsset;
|
||||
if (_delegatedStakeToPoolByOwner == amount) {
|
||||
// full payout
|
||||
payoutInShadowAsset = shadowRewardsInPoolByOwner[owner][poolId];
|
||||
payoutInRealAsset = LibRewardMath._computePayoutDenominatedInRealAsset(
|
||||
amount,
|
||||
_delegatedStakeByPoolId,
|
||||
payoutInShadowAsset,
|
||||
shadowRewardsByPoolId[poolId],
|
||||
poolBalance
|
||||
);
|
||||
} else {
|
||||
// partial payout
|
||||
(payoutInRealAsset, payoutInShadowAsset) = LibRewardMath._computePartialPayout(
|
||||
amount,
|
||||
_delegatedStakeToPoolByOwner,
|
||||
_delegatedStakeByPoolId,
|
||||
shadowRewardsInPoolByOwner[owner][poolId],
|
||||
shadowRewardsByPoolId[poolId],
|
||||
poolBalance
|
||||
);
|
||||
}
|
||||
shadowRewardsInPoolByOwner[owner][poolId] = shadowRewardsInPoolByOwner[owner][poolId]._sub(payoutInShadowAsset);
|
||||
shadowRewardsByPoolId[poolId] = shadowRewardsByPoolId[poolId]._sub(payoutInShadowAsset);
|
||||
|
||||
// withdraw payout for delegator
|
||||
if (payoutInRealAsset > 0) {
|
||||
_withdrawFromMemberInStakingPoolRewardVault(poolId, payoutInRealAsset);
|
||||
owner.transfer(payoutInRealAsset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,4 +218,73 @@ contract MixinStakingPoolRewards is
|
||||
poolBalance
|
||||
);
|
||||
}
|
||||
|
||||
function _joinStakingPool(
|
||||
bytes32 poolId,
|
||||
address payable member,
|
||||
uint256 amountOfStakeToDelegate,
|
||||
uint256 totalStakeDelegatedToPool
|
||||
)
|
||||
internal
|
||||
{
|
||||
// update delegator's share of reward pool
|
||||
// note that this uses the snapshot parameters
|
||||
uint256 poolBalance = getBalanceOfMembersInStakingPoolRewardVault(poolId);
|
||||
uint256 buyIn = LibRewardMath._computeBuyInDenominatedInShadowAsset(
|
||||
amountOfStakeToDelegate,
|
||||
totalStakeDelegatedToPool,
|
||||
shadowRewardsByPoolId[poolId],
|
||||
poolBalance
|
||||
);
|
||||
if (buyIn > 0) {
|
||||
shadowRewardsInPoolByOwner[member][poolId] = shadowRewardsInPoolByOwner[member][poolId]._add(buyIn);
|
||||
shadowRewardsByPoolId[poolId] = shadowRewardsByPoolId[poolId]._add(buyIn);
|
||||
}
|
||||
}
|
||||
|
||||
function _leaveStakingPool(
|
||||
bytes32 poolId,
|
||||
address payable member,
|
||||
uint256 amountOfStakeToUndelegate,
|
||||
uint256 totalStakeDelegatedToPoolByMember,
|
||||
uint256 totalStakeDelegatedToPool
|
||||
)
|
||||
internal
|
||||
{
|
||||
// get payout
|
||||
uint256 poolBalance = getBalanceOfMembersInStakingPoolRewardVault(poolId);
|
||||
uint256 payoutInRealAsset = 0;
|
||||
uint256 payoutInShadowAsset = 0;
|
||||
if (totalStakeDelegatedToPoolByMember == amountOfStakeToUndelegate) {
|
||||
// full payout; this is computed separately to avoid extra computation and rounding.
|
||||
payoutInShadowAsset = shadowRewardsInPoolByOwner[member][poolId];
|
||||
payoutInRealAsset = LibRewardMath._computePayoutDenominatedInRealAsset(
|
||||
amountOfStakeToUndelegate,
|
||||
totalStakeDelegatedToPool,
|
||||
payoutInShadowAsset,
|
||||
shadowRewardsByPoolId[poolId],
|
||||
poolBalance
|
||||
);
|
||||
} else {
|
||||
// partial payout
|
||||
(payoutInRealAsset, payoutInShadowAsset) = LibRewardMath._computePartialPayout(
|
||||
amountOfStakeToUndelegate,
|
||||
totalStakeDelegatedToPoolByMember,
|
||||
totalStakeDelegatedToPool,
|
||||
shadowRewardsInPoolByOwner[member][poolId],
|
||||
shadowRewardsByPoolId[poolId],
|
||||
poolBalance
|
||||
);
|
||||
}
|
||||
|
||||
// update shadow rewards
|
||||
shadowRewardsInPoolByOwner[member][poolId] = shadowRewardsInPoolByOwner[member][poolId]._sub(payoutInShadowAsset);
|
||||
shadowRewardsByPoolId[poolId] = shadowRewardsByPoolId[poolId]._sub(payoutInShadowAsset);
|
||||
|
||||
// withdraw payout for member
|
||||
if (payoutInRealAsset > 0) {
|
||||
_withdrawFromMemberInStakingPoolRewardVault(poolId, payoutInRealAsset);
|
||||
member.transfer(payoutInRealAsset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export class DelegatorActor extends StakerActor {
|
||||
constructor(owner: string, stakingWrapper: StakingWrapper) {
|
||||
super(owner, stakingWrapper);
|
||||
}
|
||||
public async depositAndDelegateAsync(
|
||||
public async depositZrxAndDelegateToStakingPoolAsync(
|
||||
poolId: string,
|
||||
amount: BigNumber,
|
||||
revertReason?: RevertReason,
|
||||
@@ -26,7 +26,7 @@ export class DelegatorActor extends StakerActor {
|
||||
const initZrxBalanceOfVault = await this._stakingWrapper.getZrxTokenBalanceOfZrxVaultAsync();
|
||||
const initDelegatorBalances = await this.getBalancesAsync([poolId]);
|
||||
// deposit stake
|
||||
const txReceiptPromise = this._stakingWrapper.depositAndDelegateAsync(this._owner, poolId, amount);
|
||||
const txReceiptPromise = this._stakingWrapper.depositZrxAndDelegateToStakingPoolAsync(this._owner, poolId, amount);
|
||||
if (revertReason !== undefined) {
|
||||
await expectTransactionFailedAsync(txReceiptPromise, revertReason);
|
||||
return;
|
||||
|
||||
@@ -95,7 +95,7 @@ describe('Staking & Delegating', () => {
|
||||
const poolId = await stakingWrapper.createStakingPoolAsync(poolOperator, operatorShare);
|
||||
// run test
|
||||
const delegator = new DelegatorActor(stakers[0], stakingWrapper);
|
||||
await delegator.depositAndDelegateAsync(poolId, amountToDelegate);
|
||||
await delegator.depositZrxAndDelegateToStakingPoolAsync(poolId, amountToDelegate);
|
||||
await delegator.deactivateAndTimelockDelegatedStakeAsync(poolId, amountToDeactivate);
|
||||
// note - we cannot re-activate this timelocked stake until at least one full timelock period has passed.
|
||||
// attempting to do so should revert.
|
||||
|
||||
@@ -179,7 +179,7 @@ export class Simulation {
|
||||
for (const j of _.range(numberOfDelegatorsInPool)) {
|
||||
const delegator = this._delegators[delegatorIdx];
|
||||
const amount = p.stakeByDelegator[delegatorIdx];
|
||||
await delegator.depositAndDelegateAsync(poolId, amount);
|
||||
await delegator.depositZrxAndDelegateToStakingPoolAsync(poolId, amount);
|
||||
delegatorIdx += 1;
|
||||
}
|
||||
poolIdx += 1;
|
||||
|
||||
@@ -190,12 +190,12 @@ export class StakingWrapper {
|
||||
const txReceipt = await this._executeTransactionAsync(calldata, owner);
|
||||
return txReceipt;
|
||||
}
|
||||
public async depositAndDelegateAsync(
|
||||
public async depositZrxAndDelegateToStakingPoolAsync(
|
||||
owner: string,
|
||||
poolId: string,
|
||||
amount: BigNumber,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const calldata = this.getStakingContract().depositAndDelegate.getABIEncodedTransactionData(poolId, amount);
|
||||
const calldata = this.getStakingContract().depositZrxAndDelegateToStakingPool.getABIEncodedTransactionData(poolId, amount);
|
||||
const txReceipt = await this._executeTransactionAsync(calldata, owner, new BigNumber(0), true);
|
||||
return txReceipt;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user