Intended functionality in these contracts

There are 5 contracts to be audited. We intend to deploy these 5 contracts as an extension to our core system.

  • FeePool.sol
  • SportXVault.sol
  • Staking.sol
  • StakingParameters.sol
  • SportX.sol
  • SportXStakingRewardsPool.sol

We inherit functionality from open-zeppelin's open source contracts (their latest version), and we leverage their RBAC contract called AccessControl significantly to provide permissioning.

These contracts create a staking system for SportX token holders where they are able to earn a token-weighted amount of fees and participate in governance of the protocol (off-chain).

Contracts

SportX is the native token to the protocol. It is standard ERC20 with a custom permit method to allow for gas-less approvals.

SportXVault is where all SportX tokens are stored when staked. It features an emergency hatch to withdraw all tokens in case of a crippling bug in the protocol. Its methods are only callable by the Staking contract. The emergency hatch opening is only callable by those with the DEFAULT_ADMIN role

FeePool is a simple contract that holds the fees generated from the protocol. On mainnet currently it is set to an externally owned address but it will be set to this contract when we deploy this extension. Hence, this FeePool will contain balances of

  • DAI
  • ETH
  • other trusted trading tokens in the future.

It contains a emergency withdraw method only callable by the system administrators.

SportXStakingRewardsPool contains rewards denominated in SportX token distributed to stakers. It is essentially a pool for SportX tokens only.

It contains a emergency withdraw method only callable by the system administrators.

StakingParameters contains parameters regarding staking including

  • Epoch length - essentially how often token holders are paid out
  • Withdraw delay - how long after a user unstakes can they withdraw their SportX tokens
  • Pool tokens - a whitelist of tokens permitted to be distributed by the staking contracts. This means that if a random token is traded in the core protocol, it will not be distributed to stakers unless it is in this list.
  • Reward multipliers - the % of the balance for a particular token in the FeePool to be distributed at the end of each epoch. For example a value of 5% for DAI would mean that at the end of this next epoch, 5% of the DAI available in the fee pool would be available to be distributed to token holders.

These are only configurable by those with the SYSTEM_PARAMETERS role.

Staking contains the main logic. Users can stake, unstake, and withdraw their unstaked SportX tokens to earn a token-weighted proportional share of the fees generated by the protocol. They can do these 3 actions in a meta fashion as well, in which a 3rd party calls the function for them so that they dont have to pay gas.

  • Users are able stake SportX at any time.
  • Users are able to unstake SportX at any time, but have to wait for the withdraw delay to be able to withdraw their tokens. During this time, they will miss out on rewards generated by the protocol.
  • Users can withdraw their SportX after the withdraw delay after they unstake. Note that if a user unstakes again during this time, the delay will be reset to the initial value of withdraw delay.
  • At the end of each epoch, using finalizeEpoch, eligible fees are snapshotted by taking the balance of each token of the FeePool contract and then stored. It uses the reward multipliers to determine how much of the balance is eligible to be distributed for each whitelisted token. To avoid looping through all token holders, we do not snapshot token balances here.
  • Once finalizeEpoch is called, users can call claimRewards for each token to receive their token-weighted share of the rewards in the last epoch. Moreover, it uses the present staked amount to claim rewards.
  • claimRewards has some additional logic that should be noted.
    • If finalize epoch is called for epoch 0, it is possible to stake before the end of epoch 1 and still claim rewards for epoch 0. This is by design and a consequence of not snapshotting token balances. Presumably all token holders would have claimed their rewards by then.
    • A user who stakes after all rewards have been claimed will not be able to claim rewards for the past epoch. This function cannot claim rewards greater than the allocated amount computed in finalizeEpoch.
    • To prevent people from staking, claiming rewards, then quickly unstaking, we set withdrawDelay (greater than epochLength) to a high number to lock up their tokens.
    • If the token is SportX, then it will auto-stake their rewards from the SportXStakingRewards, otherwise they will be claimed from FeePool.