TIP-20 Rewards Distribution
Abstract
An opt-in, scalable, pro-rata reward distribution mechanism built into TIP-20 tokens. The system uses a "reward-per-token" accumulator pattern to distribute rewards proportionally to opted-in holders without requiring staking or per-holder iteration. Rewards are distributed instantly; time-based streaming distributions are planned for a future upgrade.
Motivation
Many applications require pro-rata distribution of tokens to existing holders (incentive programs, deterministic inflation, staking rewards). Building this into TIP-20 allows efficient distribution without forcing users to stake tokens elsewhere or requiring distributors to loop over all holders.
Specification
The rewards mechanism allows anyone to distribute token rewards to opted-in holders proportionally based on holdings. Users must opt in to receiving rewards and may delegate rewards to a recipient address.
TIP-20 Rewards Functions
These functions are part of the ITIP20 interface:
/// @notice Distribute rewards to opted-in token holders
/// @param amount Amount of tokens to distribute
/// @param seconds_ Must be 0; passing > 0 reverts with ScheduledRewardsDisabled()
/// @return Always returns 0 for an instant distribution
function startReward(uint256 amount, uint32 seconds_) external returns (uint64);
/// @notice Set the reward recipient for the caller (opt in/out of rewards)
/// @param newRewardRecipient Recipient address (address(0) to opt out)
function setRewardRecipient(address newRewardRecipient) external;
/// @notice Claim all pending rewards for the caller
/// @return maxAmount Amount claimed
function claimRewards() external returns (uint256 maxAmount);
/// @notice Get user reward info
function userRewardInfo(address user) external view returns (
address rewardRecipient,
uint256 rewardPerToken,
uint256 rewardBalance
);
// State variables
function globalRewardPerToken() external view returns (uint256);
function optedInSupply() external view returns (uint128);Accrual Mechanism
The system uses an accumulator pattern:
globalRewardPerToken: Cumulative rewards per token (scaled by 1e18)- Each user stores a
rewardPerTokensnapshot; pending rewards =(globalRewardPerToken - snapshot) * balance
Instant distributions (seconds_ == 0) add directly to globalRewardPerToken as:
deltaRPT = amount * 1e18 / optedInSupply.
Opt-In Model
Users must call setRewardRecipient(recipient) to opt in. When opted in:
- User's balance contributes to
optedInSupply - Rewards accrue to
rewardBalanceon balance-changing operations - Users can delegate rewards to another address
Setting recipient to address(0) opts out.
TIP-403 Integration
All token movements must pass TIP-403 policy checks:
startReward: Validates funder authorizationsetRewardRecipient: Validates holder and recipientclaimRewards: Validates msg.sender
Invariants
globalRewardPerTokenmust monotonically increaseoptedInSupplymust equal the sum of balances for all opted-in users- All token movements must comply with TIP-403 policies