Skip to content

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 rewardPerToken snapshot; 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 rewardBalance on 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 authorization
  • setRewardRecipient: Validates holder and recipient
  • claimRewards: Validates msg.sender

Invariants

  • globalRewardPerToken must monotonically increase
  • optedInSupply must equal the sum of balances for all opted-in users
  • All token movements must comply with TIP-403 policies