Skip to content
This repository has been archived by the owner on Jul 9, 2021. It is now read-only.

Sync Rewards + Refactored Reward Vault #2156

Merged
merged 6 commits into from
Sep 18, 2019
Merged

Conversation

hysz
Copy link
Contributor

@hysz hysz commented Sep 14, 2019

Description

Reward Vault now only stores ETH for the delegators. The operator's reward goes directly to the ETH Vault. The reward vault has been stripped down and made much more lean. All pool logic has been moved to the staking contract.

Testing instructions

Types of changes

Checklist:

  • Prefix PR title with [WIP] if necessary.
  • Add tests to cover changes as needed.
  • Update documentation as needed.
  • Add new entries to the relevant CHANGELOG.jsons.

@hysz hysz force-pushed the feature/staking/syncingRewards branch from 04a253e to 58be544 Compare September 17, 2019 02:25
@hysz hysz changed the base branch from feature/staking/refCountRewards to 3.0 September 17, 2019 02:26
@hysz hysz assigned moodlezoup and unassigned abandeali1 Sep 17, 2019
@@ -0,0 +1,217 @@
/*
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was all existing code that was refactored out of MixinStakingPool.sol

@@ -0,0 +1,81 @@
/*
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code in this file already existed but was refactored into its own Mixin.

bytes32 poolId,
uint256 reward,
uint256 amountOfDelegatedStake,
uint256 epoch
)
internal
{

IStructs.Pool memory pool = poolById[poolId];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic was copied from the Staking Pool Vault.

@hysz hysz force-pushed the feature/staking/syncingRewards branch 2 times, most recently from a8fbfeb to b89c595 Compare September 17, 2019 04:18
@hysz hysz changed the title [WIP] Sync Rewards + Refactored Reward Vault Sync Rewards + Refactored Reward Vault Sep 17, 2019
@hysz hysz force-pushed the feature/staking/syncingRewards branch from b89c595 to ef2331b Compare September 17, 2019 05:02
@coveralls
Copy link

coveralls commented Sep 17, 2019

Coverage Status

Coverage remained the same at 75.804% when pulling 2869dd3 on feature/staking/syncingRewards into b631fc6 on 3.0.

Copy link
Contributor

@dorothy-zbornak dorothy-zbornak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is lovely! 🥂

@@ -237,7 +237,7 @@ contract MixinExchangeFees is

// compute weighted stake
uint256 totalStakeDelegatedToPool = getTotalStakeDelegatedToPool(poolId).currentEpochBalance;
uint256 stakeHeldByPoolOperator = getStakeDelegatedToPoolByOwner(rewardVault.operatorOf(poolId), poolId).currentEpochBalance;
uint256 stakeHeldByPoolOperator = getStakeDelegatedToPoolByOwner(poolById[poolId].operator, poolId).currentEpochBalance;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

/// @param poolId Unique Id of pool.
/// @param amount Amount in ETH to transfer.
function transferMemberBalanceToEthVault(
function transferToEthVault(
bytes32 poolId,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do we think about just passing in the ethVault address here? Then we wouldn't have to set up _ethVault, etc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it a lot. Then they can't get out of sync either.

Copy link
Contributor

@moodlezoup moodlezoup left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good to me! just a couple nits and then 🚀

@@ -95,4 +95,16 @@ interface IStructs {
uint256 cumulativeRewardEpoch;
IStructs.Fraction cumulativeReward;
}

/// @dev Holds the balances and other data for a staking pool.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// @dev Holds the balances and other data for a staking pool.
/// @dev Holds metadata for a staking pool.

// Maker has joined to the pool, awaiting operator confirmation
emit PendingAddMakerToPool(
// load pool and assert that we can decrease
IStructs.Pool memory pool = poolById[poolId];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we only need poolById[poolId].operatorShare

/// @param poolId Unique Id of pool to transfer reward for,
/// @param operator of the pool.
/// @param amount of ETH to transfer.
function _transferOperatorRewardToEthVault(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This entire contract was deleted in #2158 (it was not being used at all). We also no longer need to check if the vault addresses are null after #2166. I'd vote for keeping this contract deleted and moving this logic to wherever it is being used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RIP

Ownable,
MixinStorage,
MixinZrxVault,
MixinStakingPoolRewardVault,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going forward let's make sure that there is no redundant inheritance in new contracts. This has been cleaned up in #2158 .

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These will get added back every time we run the linearize script. I'll remove them here but we'll probably have to do this again after MBF is merged.

MixinConstants,
Ownable,
MixinStorage,
MixinZrxVault,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's cleanup these inherited contracts as well.

/// @param oldOperatorShare Previous share of rewards owned by operator.
/// @param newOperatorShare Newly decreased share of rewards owned by operator.
event OperatorShareDecreased(
bytes32 poolId,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's index poolId in any events where it is used.

/// @dev Asserts that the sender is the operator of the input pool.
/// @param poolId Pool sender must be operator of.
modifier onlyStakingPoolOperator(bytes32 poolId) {
address poolOperator = poolById[poolId].operator;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we use poolOperator, operator, and operatorAddress in various places throughout these contracts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: we chatted offline. Dropped the Address suffix and updated instances of operatorAddress and poolOperator to operator.

/// @dev Syncs rewards for a delegator. This includes transferring rewards from
/// the Reward Vault to the Eth Vault, and adding/removing dependencies on cumulative rewards.
/// @param poolId Unique id of pool.
function syncDelegatorRewards(bytes32 poolId)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Under what circumstances is this called? Who is expected to call it? It feels strange to have an external function callable by anyone that modifies state.

Copy link
Contributor Author

@hysz hysz Sep 17, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used by a delegator when they want to sync their rewards without delegating/undelegating. It's effectively the same as delegating zero stake. I've updated the description.

Note also that this uses msg.sender and can't force a sync for an arbitrary delegator.


// mapping from poolId to Pool metadata
mapping (bytes32 => Pool) internal poolById;
mapping (bytes32 => uint256) internal balanceByPoolId;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: prefix with _

/// @param poolId Unique Id of pool.
/// @param amount Amount in ETH to transfer.
function transferMemberBalanceToEthVault(
function transferToEthVault(
bytes32 poolId,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it a lot. Then they can't get out of sync either.

/// @param amount The amount in ETH withdrawn.
/// @param operator of the pool.
/// @param poolId The pool the reward was deposited for.
event OperatorRewardTransferredToEthVault(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this event? We already log all of this information on any deposit into the EthVault.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I see what you mean. Basically, the goal was to document the flow of value as it enters and leaves a contract. That said, we can probably nix now that the vaults are set during initialization (and they also emit both ingress/egress value flow).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Removed)

@hysz hysz force-pushed the feature/staking/syncingRewards branch from ef2331b to 019df1e Compare September 18, 2019 00:26
@hysz hysz force-pushed the feature/staking/syncingRewards branch 2 times, most recently from aa6eef4 to b54e499 Compare September 18, 2019 00:47
@hysz hysz force-pushed the feature/staking/syncingRewards branch from b54e499 to 5a22579 Compare September 18, 2019 00:51
Copy link
Member

@abandeali1 abandeali1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few lingering nits, then looks good to go! Nice job on this one, it is much cleaner than before.

function getStakingPoolIdOfMaker(address makerAddress)
/// @dev Returns the unique id that will be assigned to the next pool that is created.
/// @return Pool id.
function getNextStakingPoolId()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function can be removed since nextPoolId is now public (reminder to remove from any interfaces as well).

/// @dev Returns the current number of makers in a given pool.
/// @param poolId Unique id of pool.
/// @return Size of pool.
function getNumberOfMakersInStakingPool(bytes32 poolId)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm kind of meh on having all of these getters. The rest of them are actually pretty frequently used so I'm fine leaving them in, but we should remove this one. When we're using getters for individual properties, it is easy to incur extra costs from sloads (example below).

}

// Is the pool already full?
if (getNumberOfMakersInStakingPool(poolId) == maximumMakersInPool) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already reference poolById[poolId] above, so this call is using an extra sload. We can load the pool in memory and reference that instead.

address operator;
if (
msg.sender != makerAddress &&
msg.sender != (operator = poolById[poolId].operator)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pattern seems a bit strange to me. Why not just set operator when we declare it above?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this actually. Short-circuits the sload if the first condition fails. I think in practice makers will be passive accounts so we'll pretty much always hit the second condition, though, so maybe it's better to prioritize readability here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code isn't going to run very often, and I agree this will almost always be called by the operator. I think readability is more important here, although I wouldn't be opposed to using this pattern in code that gets hit frequently.

@hysz hysz merged commit 549697d into 3.0 Sep 18, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants