Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

RewardDistributor

Git Source

Inherits: Owned

Distributes reward tokens to the vault to maintain the target APR

This contract integrates with the Infrared controller to distribute rewards to a specified vault. Calculates the reward amount based on the target APR, total staked supply, and reward duration. Uses the vault's rewardRate and periodFinish to compute remaining rewards, accounting for the vault's logic where periodFinish is updated to block.timestamp + rewardsDuration and rewardRate is set to (leftover + newAmount) / rewardsDuration. Protected against sandwich attacks through keeper restrictions and slippage protection on total supply.

Notes:

  • security: The contract grants unlimited approval to the Infrared controller for reward token transfers during initialization. Uses keeper whitelist and slippage protection to prevent manipulation. Only the owner can update configuration parameters or withdraw tokens.

  • access: Only whitelisted keepers can call distribute. Only the owner can call setTargetAPR, setDistributionInterval, updateKeeper, setMaxSupplyDeviation, and withdrawRewards.

State Variables

SECONDS_PER_YEAR

The number of seconds in a year for APR calculations

uint256 private constant SECONDS_PER_YEAR = 36525 * 24 * 60 * 60 / 100;

BASIS_POINTS

The number of basis points in 100% (1% = 100 basis points)

uint256 private constant BASIS_POINTS = 10_000;

infrared

The address of the Infrared controller contract.

IInfrared public immutable infrared;

stakingToken

The address of the staking token associated with the vault.

address public immutable stakingToken;

rewardsToken

The ERC20 token used for rewards distribution.

ERC20 public immutable rewardsToken;

targetAPR

The target APR in basis points (e.g., 100 = 1%)

uint256 public targetAPR;

keepers

Mapping of addresses authorized to call the distribute function

Keepers are trusted entities that can trigger reward distributions

mapping(address => bool) public keepers;

maxSupplyDeviation

Maximum allowed deviation in total supply to prevent sandwich attacks

Expressed in basis points (e.g., 200 = 2% allowed increase)

uint256 public maxSupplyDeviation = 100;

distributionInterval

The interval between distributions in seconds

uint256 public distributionInterval;

lastDistributionTime

The timestamp of the last reward distribution.

uint256 public lastDistributionTime;

Functions

constructor

Initializes the RewardDistributor contract.

constructor(
    address _gov,
    address _infrared,
    address _stakingToken,
    address _rewardsToken,
    address _keeper,
    uint256 _initialTargetAPR,
    uint256 _initialDistributionInterval
) Owned(_gov);

Parameters

NameTypeDescription
_govaddressThe address of the contract owner/governance.
_infraredaddressThe address of the Infrared controller contract.
_stakingTokenaddressThe address of the staking token associated with the vault.
_rewardsTokenaddressThe address of the ERC20 token used for rewards.
_keeperaddressInitial keeper address
_initialTargetAPRuint256The initial target APR in basis points (e.g., 100 = 1%)
_initialDistributionIntervaluint256The initial interval between distributions in seconds

onlyKeeper

Restricts function access to whitelisted keepers only

Reverts if msg.sender is not an active keeper

modifier onlyKeeper();

getMaxTotalSupply

Calculates the maximum acceptable total supply for slippage protection

Returns current total supply plus the allowed deviation percentage

function getMaxTotalSupply() external view returns (uint256 totalSupply);

Returns

NameTypeDescription
totalSupplyuint256The maximum acceptable total supply

getExpectedAmount

Calculates the expected reward amount to be distributed in the next distribution cycle

Accounts for leftover rewards from incomplete periods and residual amounts in vault Returns 0 if distribution conditions are not met (too soon, zero supply, insufficient balance)

Notes:

  • formula: totalRewardsNeeded = (targetAPR * totalSupply * rewardsDuration) / (SECONDS_PER_YEAR * BASIS_POINTS)

  • formula: amount = totalRewardsNeeded - (leftover + residual)

function getExpectedAmount() external view returns (uint256 amount);

Returns

NameTypeDescription
amountuint256The amount of reward tokens expected to be distributed, or 0 if conditions not met

getCurrentAPR

Returns the current effective APR based on the vault's active reward rate

Calculates APR from the current rewardRate and totalSupply Returns 0 if no active rewards period or zero total supply

Note: formula: apr = (rewardRate * SECONDS_PER_YEAR * BASIS_POINTS) / totalSupply

function getCurrentAPR() external view returns (uint256 apr);

Returns

NameTypeDescription
apruint256The current APR in basis points (e.g., 1500 = 15%)

getAPRForAmount

Calculates the APR that would result from distributing a specific amount of rewards

Useful for determining how much rewards are needed to achieve a target APR

Note: formula: apr = (amount * SECONDS_PER_YEAR * BASIS_POINTS) / (rewardsDuration * totalSupply)

function getAPRForAmount(uint256 amount) external view returns (uint256 apr);

Parameters

NameTypeDescription
amountuint256The amount of reward tokens to calculate APR for

Returns

NameTypeDescription
apruint256The resulting APR in basis points if the amount were distributed

distribute

Distributes reward tokens to the vault to maintain the target APR

Calculates reward amount based on target APR and current supply, protected against sandwich attacks via keeper restriction and slippage check. Accounts for leftover rewards from previous periods and residual amounts in the vault.

Notes:

  • security: Requires msg.sender to be a whitelisted keeper

  • security: Reverts if current totalSupply exceeds _maxTotalSupply (sandwich attack protection)

  • security: Updates state before external calls to prevent reentrancy

function distribute(uint256 _maxTotalSupply) external onlyKeeper;

Parameters

NameTypeDescription
_maxTotalSupplyuint256Maximum acceptable total supply (slippage protection)

updateKeeper

Updates the keeper status for a given address

Only callable by owner, reverts if status would not change

Note: security: Consider checking _keeper != address(0) to prevent accidents

function updateKeeper(address _keeper, bool _active) external onlyOwner;

Parameters

NameTypeDescription
_keeperaddressThe address to update keeper status for
_activeboolWhether the address should be an active keeper

setTargetAPR

Sets the target APR for reward distributions

Updates the target APR after validating it is non-zero and emits a TargetAPRUpdated event

function setTargetAPR(uint256 _apr) external onlyKeeper;

Parameters

NameTypeDescription
_apruint256The new target APR in basis points (e.g., 100 = 1%)

setDistributionInterval

Sets the interval between reward distributions

Updates the distribution interval after validating it is non-zero and emits a DistributionIntervalUpdated event

function setDistributionInterval(uint256 _interval) external onlyOwner;

Parameters

NameTypeDescription
_intervaluint256The new distribution interval in seconds

setMaxSupplyDeviation

Sets the maximum allowed supply deviation for slippage protection

Prevents distributions when supply increases beyond this threshold

function setMaxSupplyDeviation(uint256 _deviation) external onlyOwner;

Parameters

NameTypeDescription
_deviationuint256The new maximum deviation in basis points (e.g., 200 = 2%)

withdrawRewards

Allows the owner to withdraw reward tokens from the contract.

Transfers the specified amount of reward tokens to the owner using safe transfer.

function withdrawRewards(uint256 _amount) external onlyOwner;

Parameters

NameTypeDescription
_amountuint256The amount of reward tokens to withdraw.

recoverERC20

Recover non-reward tokens sent to this contract by mistake

Cannot be used to withdraw reward tokens - use withdrawRewards instead Transfers entire balance of the specified token to the recipient

Notes:

  • security: Only callable by owner, prevents recovery of reward tokens

  • error: UseWithdrawRewards if attempting to recover reward tokens

  • error: ZeroAmount if token balance is zero

function recoverERC20(address tokenAddress, address to) external onlyOwner;

Parameters

NameTypeDescription
tokenAddressaddressAddress of the ERC20 token to recover
toaddressAddress to send recovered tokens to

Events

RewardsDistributed

Emitted when rewards are successfully distributed to the vault.

event RewardsDistributed(address vault, uint256 amount);

Parameters

NameTypeDescription
vaultaddressThe address of the Infrared vault receiving the rewards.
amountuint256The amount of reward tokens distributed.

TargetAPRUpdated

Emitted when the target APR is updated

event TargetAPRUpdated(uint256 oldAPR, uint256 newAPR);

Parameters

NameTypeDescription
oldAPRuint256The previous target APR in basis points
newAPRuint256The new target APR in basis points

DistributionIntervalUpdated

Emitted when the distribution interval is updated

event DistributionIntervalUpdated(uint256 oldInterval, uint256 newInterval);

Parameters

NameTypeDescription
oldIntervaluint256The previous distribution interval in seconds
newIntervaluint256The new distribution interval in seconds

KeeperUpdated

Emitted when a keeper's status is updated

event KeeperUpdated(address indexed keeper, bool active);

Parameters

NameTypeDescription
keeperaddressThe address of the keeper
activeboolWhether the keeper is now active or inactive

MaxSupplyDeviationUpdated

Emitted when the maximum supply deviation is updated

event MaxSupplyDeviationUpdated(uint256 oldDeviation, uint256 newDeviation);

Parameters

NameTypeDescription
oldDeviationuint256The previous maximum deviation in basis points
newDeviationuint256The new maximum deviation in basis points

TokensRecovered

Emitted when tokens are withdrawn by the owner

event TokensRecovered(address indexed token, address to, uint256 amount);

Parameters

NameTypeDescription
tokenaddressAddress of token recovered
toaddressAddress of recipient
amountuint256Tokens recovered

Errors

ZeroRewardDuration

Thrown when the reward duration from the vault is zero.

error ZeroRewardDuration();

DistributionTooSoon

Thrown when a distribution is attempted before the reward duration has elapsed.

error DistributionTooSoon();

InsufficientRewardBalance

Thrown when the contract has insufficient reward token balance for distribution.

error InsufficientRewardBalance();

ZeroFixedAmount

Thrown when attempting to set a zero fixed reward amount.

error ZeroFixedAmount();

ZeroAddress

Thrown when attempting to set a zero address.

error ZeroAddress();

ZeroTargetAPR

Thrown when attempting to set a zero target APR

error ZeroTargetAPR();

ZeroDistributionInterval

Thrown when attempting to set a zero distribution interval

error ZeroDistributionInterval();

ZeroTotalSupply

Thrown when the total staked supply in the vault is zero

error ZeroTotalSupply();

NothingToAdd

Thrown when amount to distribute is zero

error NothingToAdd();

NoVault

Thrown when there is no vault for the staking token

error NoVault();

TotalSupplySlippage

Thrown when the current total supply exceeds the maximum allowed (slippage protection)

Prevents distribution during potential sandwich attacks

error TotalSupplySlippage();

NothingToUpdate

Thrown when attempting to update a value that would not change

error NothingToUpdate();

NotKeeper

Thrown when attempting to call onlyKeeper function from non-keeper address

error NotKeeper();

UseWithdrawRewards

Thrown when using recoverERC20 for withdrawing reward tokens

error UseWithdrawRewards();

ZeroAmount

Thrown when calling recoverERC20 with no token balance

error ZeroAmount();