Try   HackMD

ERC-4000 Staking-Reward Pool Standard

eip 4000 (*unofficial)
title ERC-4000 Staking-Reward Pool Standard
authors Dotta, vfat, DkNinja
type Standards Track
category ERC
status Idea
created 2021-04-15

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
Join the Discussion:
Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Context

Below is a draft EIP for a staking-reward pool standard. The goal is to define an easy-to-use convention for staking-reward pools.

Staking-reward pools are a common way for projects to incentivize desired behaviors like providing liquidity, bonding, token distribution, or loan origination.

The popularity of the staking-reward pool mechanism can be seen on websites like vfat.tools and apy.vision which track hundreds of reward pools across the ecosystem.

Nearly every new project launches reward pool contracts and nearly every project implements the interface differently.

The standard below is based on the author's interactions with hundreds of "Masterchef-" and "Synthetix-"style staking-rewards pools.

Adoption of a standard for staking-reward pools will benefit the ecosystem by composibility, similar to that seen with ERC20 tokens.

Request for Comments

The authors request your comments and feedback on this document before submitting for official EIP consideration in late-April 2021.

Blockquotes indicate thoughts and open questions and will be removed from the final EIP draft

Dotta
April 15, 2021

Simple Summary

A standard interface for staking pools with rewards.

Abstract

The following standard allows for the implementation of a standard API for staking-rewards pools within smart contracts.

This standard provides basic functionality to stake tokens in smart contracts with multiple pools and inspect the rewards for doing so.

Motivation

Staking-reward pools are a common way for projects to incentivize desired behaviors such as providing liquidity, bonding, token distribution, or loan origination.

This document specifies a standard interface that allows any staking-reward pools on Ethereum to be re-used by other applications such as wallets, user interfaces, yield aggregators, and other smart contracts.

Specification

Pool

Methods

NOTES:

  • The following specifications use syntax from Solidity 0.8.3 (or above)

poolLength

Returns the total number of pools

function poolLength() public view returns (uint256)

poolInfo

Returns the information about a pool.

poolInfo(uint256 poolIdx) public view returns (
  address stakingToken, 
  address rewardToken, 
  uint256 stakedAmount,
  uint256 rewardAmount,
  uint256 rewardPerBlock,
  uint256 startBlock
)

Where:

  • stakingToken is the address of the token to be staked in poolIdx
  • rewardToken is the address of the token which is rewarded for staking in poolIdx
  • stakedAmount is the total number of tokens currently staked in poolIdx
  • rewardAmount is the total number of tokens currently allocated as rewards to the pool at poolIdx
  • rewardPerBlock is the sum total of reward tokens accrued to all stakers in poolIdx for the current block
  • startBlock is the first block number at which rewards will accrue for the pool at poolIdx

userInfo

Returns the information about userAddress at poolIdx

userInfo(uint256 poolIdx, address userAddress) public view returns (
  uint256 stakedAmount,
  uint256 rewardAmount
)

Where:

  • stakedAmount is the number of tokens userAddress has staked in poolIdx
  • rewardAmount is the number of tokens credited to userAddress in poolIdx that have not yet been withdrawn

rewardForBlock

Returns the sum total of reward tokens accrued to all stakers in poolIdx at blockNum.

OPTIONAL - This method can be used to improve usability, but interfaces and other contracts MUST NOT expect these values to be present.

rewardForBlock(uint256 poolIdx, uint256 blockNum) public view returns (uint256)

Note - The purpose of this optional method is to allow for calculation of non-linear reward accrual curves.

deposit

  • Transfers amount of staking tokens from the calling user to the pool at poolIdx and
  • MUST fire the Deposit event and
  • Transfers all claimable reward tokens from the pool at poolIdx to the calling user and
  • MUST fire the Claim event.
deposit(uint256 poolIdx, uint256 amount) public nonpayable

Note - amount of 0 MUST be allowed, which has the effect of withdrawing all claimable rewards.

withdraw

  • Transfers amount of staking tokens from the pool at poolIdx to the calling user and
  • MUST fire the Withdraw event and
  • Transfers all claimable reward tokens from the pool at poolIdx to the calling user and
  • MUST fire the Claim event.
withdraw(uint256 poolIdx, uint256 amount) public nonpayable

Note - amount of 0 MUST be allowed, which has the effect of withdrawing all claimable rewards.

claim

  • Transfers all claimable reward tokens from the pool at poolIdx to the calling user and
  • MUST fire the Claim event.
claim(uint256 poolIdx) public nonpayable

Events

Deposit

MUST trigger when staking tokens are deposited, including zero value deposits.

event Deposit(address indexed user, uint256 indexed poolIdx, uint256 stakeAmount);

Where:

  • user is the address of the depositing user
  • poolIdx is the index of the pool
  • stakeAmount is the amount of the stake token deposited

Withdraw

MUST trigger when staking tokens are withdrawn, including zero value withdrawals.

event Withdraw(address indexed user, uint256 indexed poolIdx, uint256 stakeAmount);

Where:

  • user is the address of the depositing user
  • poolIdx is the index of the pool
  • stakeAmount is the amount of the stake token withdrawn

Claim

MUST trigger when reward tokens are withdrawn, including zero value withdrawals.

event Claim(address indexed user, uint256 indexed poolIdx, uint256 rewardAmount);

Where:

  • user is the address of the depositing user
  • poolIdx is the index of the pool
  • rewardAmount is the amount of the reward token withdrawn

Open Questions

Q: Should we specify ERC20 as part of this standard?
A: Probably not because this ERC might be useful for other sorts of tokens e.g. ERC-721 NFTs

Q: What about allocPoint, getMultiplier, lastRewardBlock, rewardDebt etc?
A: While commonly exposed in "Masterchef" contracts, these values are internals that don't need to be exposed in the standard. A contract can expose them if they desire.

Q: What about a global rewardPerBlock that shows the total emission per block for the entire contract?
A: This standard allows for differnt reward tokens to be emitted for each pool, so a global rewardPerBlock would not be useful

Q: What about vesting and locking both stake and rewards?
A: This standard doesn't address those issues.

Q: What about legacy staking-reward pool contracts that don't conform to this standard?
A: It should be straightforward to write a Masterchef-to-ERC-4000 proxy-adapter contract (cf. multicall) that adapts the values for any standard Masterchef- or Synthetix-style pool.

Implementation

Example implementations are available at

  • TODO - any takers?

History

Historical links related to this standard:

Copyright and related rights waived via CC0.