Added documentation to MixinStakingPoolRewards

This commit is contained in:
Greg Hysen
2019-06-27 17:35:30 -07:00
parent 44c44a2b9c
commit d5249425af
4 changed files with 140 additions and 107 deletions

View File

@@ -108,7 +108,7 @@ contract MixinStakingPool is
poolById[poolId] = pool; poolById[poolId] = pool;
// register pool in reward vault // register pool in reward vault
_createStakingPoolInStakingPoolRewardVault(poolId, operatorShare); _registerStakingPoolInRewardVault(poolId, operatorShare);
// notify // notify
emit StakingPoolCreated(poolId, operatorAddress, operatorShare); emit StakingPoolCreated(poolId, operatorAddress, operatorShare);

View File

@@ -69,8 +69,10 @@ contract MixinStakingPoolRewards is
/// their realized balance *increases* while their ownership of the pool *decreases*. To reflect this, we /// their realized balance *increases* while their ownership of the pool *decreases*. To reflect this, we
/// decrease their Shadow Balance, the Total Shadow Balance, their Real Balance, and the Total Real Balance. /// decrease their Shadow Balance, the Total Shadow Balance, their Real Balance, and the Total Real Balance.
/// @dev Returns the sum total reward balance in ETH of a staking pool, across all members and the pool operator.
function getRewardBalance(bytes32 poolId) /// @param poolId Unique id of pool.
/// @return Balance.
function getTotalRewardBalanceOfStakingPool(bytes32 poolId)
external external
view view
returns (uint256) returns (uint256)
@@ -78,7 +80,21 @@ contract MixinStakingPoolRewards is
return getBalanceInStakingPoolRewardVault(poolId); return getBalanceInStakingPoolRewardVault(poolId);
} }
function getRewardBalanceOfOperator(bytes32 poolId) /// @dev Returns the total shadow balance of a staking pool.
/// @param poolId Unique id of pool.
/// @return Balance.
function getTotalShadowBalanceOfStakingPool(bytes32 poolId)
public
view
returns (uint256)
{
return shadowRewardsByPoolId[poolId];
}
/// @dev Returns the reward balance in ETH of the pool operator.
/// @param poolId Unique id of pool.
/// @return Balance.
function getRewardBalanceOfStakingPoolOperator(bytes32 poolId)
external external
view view
returns (uint256) returns (uint256)
@@ -86,30 +102,10 @@ contract MixinStakingPoolRewards is
return getBalanceOfOperatorInStakingPoolRewardVault(poolId); return getBalanceOfOperatorInStakingPoolRewardVault(poolId);
} }
function getRewardBalanceOfPool(bytes32 poolId) /// @dev Withdraws an amount in ETH of the reward for the pool operator.
external /// @param poolId Unique id of pool.
view /// @param amount The amount to withdraw.
returns (uint256) function withdrawRewardForStakingPoolOperator(bytes32 poolId, uint256 amount)
{
return getBalanceOfPoolInStakingPoolRewardVault(poolId);
}
function computeRewardBalance(bytes32 poolId, address owner)
public
view
returns (uint256)
{
uint256 poolBalance = getBalanceOfPoolInStakingPoolRewardVault(poolId);
return LibRewardMath._computePayoutDenominatedInRealAsset(
delegatedStakeToPoolByOwner[owner][poolId],
delegatedStakeByPoolId[poolId],
shadowRewardsInPoolByOwner[owner][poolId],
shadowRewardsByPoolId[poolId],
poolBalance
);
}
function withdrawOperatorReward(bytes32 poolId, uint256 amount)
external external
onlyStakingPoolOperator(poolId) onlyStakingPoolOperator(poolId)
{ {
@@ -117,24 +113,10 @@ contract MixinStakingPoolRewards is
poolById[poolId].operatorAddress.transfer(amount); poolById[poolId].operatorAddress.transfer(amount);
} }
function withdrawReward(bytes32 poolId, uint256 amount) /// @dev Withdraws the total balance in ETH of the reward for the pool operator.
external /// @param poolId Unique id of pool.
{ /// @return The amount withdrawn.
address payable owner = msg.sender; function withdrawTotalRewardForStakingPoolOperator(bytes32 poolId)
uint256 ownerBalance = computeRewardBalance(poolId, owner);
require(
amount <= ownerBalance,
"INVALID_AMOUNT"
);
shadowRewardsInPoolByOwner[owner][poolId] = shadowRewardsInPoolByOwner[owner][poolId]._add(amount);
shadowRewardsByPoolId[poolId] = shadowRewardsByPoolId[poolId]._add(amount);
_withdrawFromPoolInStakingPoolRewardVault(poolId, amount);
owner.transfer(amount);
}
function withdrawTotalOperatorReward(bytes32 poolId)
external external
onlyStakingPoolOperator(poolId) onlyStakingPoolOperator(poolId)
returns (uint256) returns (uint256)
@@ -146,38 +128,89 @@ contract MixinStakingPoolRewards is
return amount; return amount;
} }
function withdrawTotalReward(bytes32 poolId) /// @dev Returns the reward balance in ETH co-owned by the members of a pool.
/// @param poolId Unique id of pool.
/// @return Balance.
function getRewardBalanceOfStakingPoolMembers(bytes32 poolId)
external
view
returns (uint256)
{
return getBalanceOfPoolInStakingPoolRewardVault(poolId);
}
/// @dev Returns the shadow balance of a specific member of a staking pool.
/// @param poolId Unique id of pool.
/// @param member The member of the pool.
/// @return Balance.
function getShadowBalanceOfStakingPoolMember(bytes32 poolId, address member)
public
view
returns (uint256)
{
return shadowRewardsInPoolByOwner[member][poolId];
}
/// @dev Computes the reward balance in ETH of a specific member of a pool.
/// @param poolId Unique id of pool.
/// @param member The member of the pool.
/// @return Balance.
function computeRewardBalanceOfStakingPoolMember(bytes32 poolId, address member)
public
view
returns (uint256)
{
uint256 poolBalance = getBalanceOfPoolInStakingPoolRewardVault(poolId);
return LibRewardMath._computePayoutDenominatedInRealAsset(
delegatedStakeToPoolByOwner[member][poolId],
delegatedStakeByPoolId[poolId],
shadowRewardsInPoolByOwner[member][poolId],
shadowRewardsByPoolId[poolId],
poolBalance
);
}
/// @dev Withdraws an amount in ETH of the reward for a pool member.
/// @param poolId Unique id of pool.
/// @param amount The amount to withdraw.
function withdrawRewardForStakingPoolMember(bytes32 poolId, uint256 amount)
external
{
// sanity checks
address payable member = msg.sender;
uint256 memberBalance = computeRewardBalanceOfStakingPoolMember(poolId, member);
require(
amount <= memberBalance,
"INVALID_AMOUNT"
);
// update shadow rewards
shadowRewardsInPoolByOwner[member][poolId] = shadowRewardsInPoolByOwner[member][poolId]._add(amount);
shadowRewardsByPoolId[poolId] = shadowRewardsByPoolId[poolId]._add(amount);
// perform withdrawal
_withdrawFromPoolInStakingPoolRewardVault(poolId, amount);
member.transfer(amount);
}
/// @dev Withdraws the total balance in ETH of the reward for a pool member.
/// @param poolId Unique id of pool.
/// @return The amount withdrawn.
function withdrawTotalRewardForStakingPoolMember(bytes32 poolId)
external external
returns (uint256) returns (uint256)
{ {
address payable owner = msg.sender; // sanity checks
uint256 amount = computeRewardBalance(poolId, owner); address payable member = msg.sender;
uint256 amount = computeRewardBalanceOfStakingPoolMember(poolId, member);
shadowRewardsInPoolByOwner[owner][poolId] = shadowRewardsInPoolByOwner[owner][poolId]._add(amount); // update shadow rewards
shadowRewardsInPoolByOwner[member][poolId] = shadowRewardsInPoolByOwner[member][poolId]._add(amount);
shadowRewardsByPoolId[poolId] = shadowRewardsByPoolId[poolId]._add(amount); shadowRewardsByPoolId[poolId] = shadowRewardsByPoolId[poolId]._add(amount);
// perform withdrawal and return amount withdrawn
_withdrawFromPoolInStakingPoolRewardVault(poolId, amount); _withdrawFromPoolInStakingPoolRewardVault(poolId, amount);
owner.transfer(amount); member.transfer(amount);
return amount; return amount;
} }
function getShadowBalanceByPoolId(bytes32 poolId)
public
view
returns (uint256)
{
return shadowRewardsByPoolId[poolId];
}
function getShadowBalanceInPoolByOwner(address owner, bytes32 poolId)
public
view
returns (uint256)
{
return shadowRewardsInPoolByOwner[owner][poolId];
}
} }

View File

@@ -49,17 +49,17 @@ export class Simulation {
await this._stakingWrapper.skipToNextEpochAsync(); await this._stakingWrapper.skipToNextEpochAsync();
// everyone has been paid out into the vault. check balances. // everyone has been paid out into the vault. check balances.
await this._assertVaultBalancesAsync(this._p); await this._assertVaultBalancesAsync(this._p);
await this._withdrawRewardForOperatorsAsync(this._p); await this._withdrawRewardForStakingPoolMemberForOperatorsAsync(this._p);
if (this._p.withdrawByUndelegating) { if (this._p.withdrawByUndelegating) {
await this._withdrawRewardForDelegatorsAsync(this._p); await this._withdrawRewardForStakingPoolMemberForDelegatorsAsync(this._p);
} else { } else {
await this._withdrawRewardForDelegatorsByUndelegatingAsync(this._p); await this._withdrawRewardForStakingPoolMemberForDelegatorsByUndelegatingAsync(this._p);
} }
// @TODO cleanup state and verify the staking contract is empty // @TODO cleanup state and verify the staking contract is empty
} }
private async _withdrawRewardForDelegatorsByUndelegatingAsync(p: SimulationParams): Promise<void> { private async _withdrawRewardForStakingPoolMemberForDelegatorsByUndelegatingAsync(p: SimulationParams): Promise<void> {
let delegatorIdx = 0; let delegatorIdx = 0;
let poolIdx = 0; let poolIdx = 0;
for (const numberOfDelegatorsInPool of p.numberOfDelegatorsPerPool) { for (const numberOfDelegatorsInPool of p.numberOfDelegatorsPerPool) {
@@ -88,7 +88,7 @@ export class Simulation {
} }
} }
private async _withdrawRewardForDelegatorsAsync(p: SimulationParams): Promise<void> { private async _withdrawRewardForStakingPoolMemberForDelegatorsAsync(p: SimulationParams): Promise<void> {
let delegatorIdx = 0; let delegatorIdx = 0;
let poolIdx = 0; let poolIdx = 0;
for (const numberOfDelegatorsInPool of p.numberOfDelegatorsPerPool) { for (const numberOfDelegatorsInPool of p.numberOfDelegatorsPerPool) {
@@ -98,7 +98,7 @@ export class Simulation {
const delegator = this._delegators[delegatorIdx]; const delegator = this._delegators[delegatorIdx];
const delegatorAddress = delegator.getOwner(); const delegatorAddress = delegator.getOwner();
const initEthBalance = await this._stakingWrapper.getEthBalanceAsync(delegatorAddress); const initEthBalance = await this._stakingWrapper.getEthBalanceAsync(delegatorAddress);
await this._stakingWrapper.withdrawTotalRewardAsync(poolId, delegatorAddress); await this._stakingWrapper.withdrawTotalRewardForStakingPoolMemberAsync(poolId, delegatorAddress);
const finalEthBalance = await this._stakingWrapper.getEthBalanceAsync(delegatorAddress); const finalEthBalance = await this._stakingWrapper.getEthBalanceAsync(delegatorAddress);
const reward = finalEthBalance.minus(initEthBalance); const reward = finalEthBalance.minus(initEthBalance);
const rewardTrimmed = StakingWrapper.trimFloat( const rewardTrimmed = StakingWrapper.trimFloat(
@@ -228,7 +228,7 @@ export class Simulation {
`expected balance in vault for pool with id ${poolId}`, `expected balance in vault for pool with id ${poolId}`,
).to.be.bignumber.equal(expectedRewardBalance); ).to.be.bignumber.equal(expectedRewardBalance);
// check operator's balance // check operator's balance
const poolOperatorVaultBalance = await this._stakingWrapper.getRewardBalanceOfOperatorAsync(poolId); const poolOperatorVaultBalance = await this._stakingWrapper.getRewardBalanceOfStakingPoolOperatorAsync(poolId);
const poolOperatorVaultBalanceTrimmed = StakingWrapper.trimFloat( const poolOperatorVaultBalanceTrimmed = StakingWrapper.trimFloat(
StakingWrapper.toFloatingPoint(poolOperatorVaultBalance, 18), StakingWrapper.toFloatingPoint(poolOperatorVaultBalance, 18),
5, 5,
@@ -239,7 +239,7 @@ export class Simulation {
`operator balance in vault for pool with id ${poolId}`, `operator balance in vault for pool with id ${poolId}`,
).to.be.bignumber.equal(expectedPoolOperatorVaultBalance); ).to.be.bignumber.equal(expectedPoolOperatorVaultBalance);
// check balance of pool members // check balance of pool members
const membersVaultBalance = await this._stakingWrapper.getRewardBalanceOfPoolAsync(poolId); const membersVaultBalance = await this._stakingWrapper.getRewardBalanceOfStakingPoolMembersAsync(poolId);
const membersVaultBalanceTrimmed = StakingWrapper.trimFloat( const membersVaultBalanceTrimmed = StakingWrapper.trimFloat(
StakingWrapper.toFloatingPoint(membersVaultBalance, 18), StakingWrapper.toFloatingPoint(membersVaultBalance, 18),
5, 5,
@@ -253,7 +253,7 @@ export class Simulation {
} }
} }
private async _withdrawRewardForOperatorsAsync(p: SimulationParams): Promise<void> { private async _withdrawRewardForStakingPoolMemberForOperatorsAsync(p: SimulationParams): Promise<void> {
// tslint:disable-next-line no-unused-variable // tslint:disable-next-line no-unused-variable
for (const i of _.range(p.numberOfPools)) { for (const i of _.range(p.numberOfPools)) {
// @TODO - we trim balances in here because payouts are accurate only to 5 decimal places. // @TODO - we trim balances in here because payouts are accurate only to 5 decimal places.
@@ -263,7 +263,7 @@ export class Simulation {
const poolOperator = this._poolOperators[i]; const poolOperator = this._poolOperators[i];
const poolOperatorAddress = poolOperator.getOwner(); const poolOperatorAddress = poolOperator.getOwner();
const initEthBalance = await this._stakingWrapper.getEthBalanceAsync(poolOperatorAddress); const initEthBalance = await this._stakingWrapper.getEthBalanceAsync(poolOperatorAddress);
await this._stakingWrapper.withdrawTotalOperatorRewardAsync(poolId, poolOperatorAddress); await this._stakingWrapper.withdrawTotalRewardForStakingPoolOperatorAsync(poolId, poolOperatorAddress);
const finalEthBalance = await this._stakingWrapper.getEthBalanceAsync(poolOperatorAddress); const finalEthBalance = await this._stakingWrapper.getEthBalanceAsync(poolOperatorAddress);
const reward = finalEthBalance.minus(initEthBalance); const reward = finalEthBalance.minus(initEthBalance);
const rewardTrimmed = StakingWrapper.trimFloat(StakingWrapper.toFloatingPoint(reward, 18), 5); const rewardTrimmed = StakingWrapper.trimFloat(StakingWrapper.toFloatingPoint(reward, 18), 5);

View File

@@ -525,73 +525,73 @@ export class StakingWrapper {
return txReceipt; return txReceipt;
} }
///// REWARDS ///// ///// REWARDS /////
public async getRewardBalanceAsync(poolId: string): Promise<BigNumber> { public async getTotalRewardBalanceOfStakingPoolAsync(poolId: string): Promise<BigNumber> {
const calldata = this.getStakingContract().getRewardBalance.getABIEncodedTransactionData(poolId); const calldata = this.getStakingContract().getTotalRewardBalanceOfStakingPool.getABIEncodedTransactionData(poolId);
const returnData = await this._callAsync(calldata); const returnData = await this._callAsync(calldata);
const value = this.getStakingContract().getRewardBalance.getABIDecodedReturnData(returnData); const value = this.getStakingContract().getTotalRewardBalanceOfStakingPool.getABIDecodedReturnData(returnData);
return value; return value;
} }
public async getRewardBalanceOfOperatorAsync(poolId: string): Promise<BigNumber> { public async getRewardBalanceOfStakingPoolOperatorAsync(poolId: string): Promise<BigNumber> {
const calldata = this.getStakingContract().getRewardBalanceOfOperator.getABIEncodedTransactionData(poolId); const calldata = this.getStakingContract().getRewardBalanceOfStakingPoolOperator.getABIEncodedTransactionData(poolId);
const returnData = await this._callAsync(calldata); const returnData = await this._callAsync(calldata);
const value = this.getStakingContract().getRewardBalanceOfOperator.getABIDecodedReturnData(returnData); const value = this.getStakingContract().getRewardBalanceOfStakingPoolOperator.getABIDecodedReturnData(returnData);
return value; return value;
} }
public async getRewardBalanceOfPoolAsync(poolId: string): Promise<BigNumber> { public async getRewardBalanceOfStakingPoolMembersAsync(poolId: string): Promise<BigNumber> {
const calldata = this.getStakingContract().getRewardBalanceOfPool.getABIEncodedTransactionData(poolId); const calldata = this.getStakingContract().getRewardBalanceOfStakingPoolMembers.getABIEncodedTransactionData(poolId);
const returnData = await this._callAsync(calldata); const returnData = await this._callAsync(calldata);
const value = this.getStakingContract().getRewardBalanceOfPool.getABIDecodedReturnData(returnData); const value = this.getStakingContract().getRewardBalanceOfStakingPoolMembers.getABIDecodedReturnData(returnData);
return value; return value;
} }
public async computeRewardBalanceAsync(poolId: string, owner: string): Promise<BigNumber> { public async computeRewardBalanceOfStakingPoolMemberAsync(poolId: string, owner: string): Promise<BigNumber> {
const calldata = this.getStakingContract().computeRewardBalance.getABIEncodedTransactionData(poolId, owner); const calldata = this.getStakingContract().computeRewardBalanceOfStakingPoolMember.getABIEncodedTransactionData(poolId, owner);
const returnData = await this._callAsync(calldata); const returnData = await this._callAsync(calldata);
const value = this.getStakingContract().computeRewardBalance.getABIDecodedReturnData(returnData); const value = this.getStakingContract().computeRewardBalanceOfStakingPoolMember.getABIDecodedReturnData(returnData);
return value; return value;
} }
public async getShadowBalanceByPoolIdAsync(poolId: string): Promise<BigNumber> { public async getTotalShadowBalanceOfStakingPoolAsync(poolId: string): Promise<BigNumber> {
const calldata = this.getStakingContract().getShadowBalanceByPoolId.getABIEncodedTransactionData(poolId); const calldata = this.getStakingContract().getTotalShadowBalanceOfStakingPool.getABIEncodedTransactionData(poolId);
const returnData = await this._callAsync(calldata); const returnData = await this._callAsync(calldata);
const value = this.getStakingContract().getShadowBalanceByPoolId.getABIDecodedReturnData(returnData); const value = this.getStakingContract().getTotalShadowBalanceOfStakingPool.getABIDecodedReturnData(returnData);
return value; return value;
} }
public async getShadowBalanceInPoolByOwnerAsync(owner: string, poolId: string): Promise<BigNumber> { public async getShadowBalanceOfStakingPoolMemberAsync(owner: string, poolId: string): Promise<BigNumber> {
const calldata = this.getStakingContract().getShadowBalanceInPoolByOwner.getABIEncodedTransactionData( const calldata = this.getStakingContract().getShadowBalanceOfStakingPoolMember.getABIEncodedTransactionData(
owner, owner,
poolId, poolId,
); );
const returnData = await this._callAsync(calldata); const returnData = await this._callAsync(calldata);
const value = this.getStakingContract().getShadowBalanceInPoolByOwner.getABIDecodedReturnData(returnData); const value = this.getStakingContract().getShadowBalanceOfStakingPoolMember.getABIDecodedReturnData(returnData);
return value; return value;
} }
public async withdrawOperatorRewardAsync( public async withdrawRewardForStakingPoolOperatorAsync(
poolId: string, poolId: string,
amount: BigNumber, amount: BigNumber,
operatorAddress: string, operatorAddress: string,
): Promise<TransactionReceiptWithDecodedLogs> { ): Promise<TransactionReceiptWithDecodedLogs> {
const calldata = this.getStakingContract().withdrawOperatorReward.getABIEncodedTransactionData(poolId, amount); const calldata = this.getStakingContract().withdrawRewardForStakingPoolOperator.getABIEncodedTransactionData(poolId, amount);
const txReceipt = await this._executeTransactionAsync(calldata, operatorAddress); const txReceipt = await this._executeTransactionAsync(calldata, operatorAddress);
return txReceipt; return txReceipt;
} }
public async withdrawRewardAsync( public async withdrawRewardForStakingPoolMemberAsync(
poolId: string, poolId: string,
amount: BigNumber, amount: BigNumber,
owner: string, owner: string,
): Promise<TransactionReceiptWithDecodedLogs> { ): Promise<TransactionReceiptWithDecodedLogs> {
const calldata = this.getStakingContract().withdrawReward.getABIEncodedTransactionData(poolId, amount); const calldata = this.getStakingContract().withdrawRewardForStakingPoolMember.getABIEncodedTransactionData(poolId, amount);
const txReceipt = await this._executeTransactionAsync(calldata, owner); const txReceipt = await this._executeTransactionAsync(calldata, owner);
return txReceipt; return txReceipt;
} }
public async withdrawTotalOperatorRewardAsync( public async withdrawTotalRewardForStakingPoolOperatorAsync(
poolId: string, poolId: string,
operatorAddress: string, operatorAddress: string,
): Promise<TransactionReceiptWithDecodedLogs> { ): Promise<TransactionReceiptWithDecodedLogs> {
const calldata = this.getStakingContract().withdrawTotalOperatorReward.getABIEncodedTransactionData(poolId); const calldata = this.getStakingContract().withdrawTotalRewardForStakingPoolOperator.getABIEncodedTransactionData(poolId);
const txReceipt = await this._executeTransactionAsync(calldata, operatorAddress); const txReceipt = await this._executeTransactionAsync(calldata, operatorAddress);
return txReceipt; return txReceipt;
} }
public async withdrawTotalRewardAsync(poolId: string, owner: string): Promise<TransactionReceiptWithDecodedLogs> { public async withdrawTotalRewardForStakingPoolMemberAsync(poolId: string, owner: string): Promise<TransactionReceiptWithDecodedLogs> {
const calldata = this.getStakingContract().withdrawTotalReward.getABIEncodedTransactionData(poolId); const calldata = this.getStakingContract().withdrawTotalRewardForStakingPoolMember.getABIEncodedTransactionData(poolId);
const txReceipt = await this._executeTransactionAsync(calldata, owner); const txReceipt = await this._executeTransactionAsync(calldata, owner);
return txReceipt; return txReceipt;
} }