How Does It Work?
Reward Streams operates in two modes of rewards distribution: staking and balance-tracking. Each mode has a separate contract implementation.
Reward Streams Mechanism
Tracking Reward Distribution
The balance-tracking TrackingRewardStreams
implementation inherits from the BaseRewardStreams
contract. It defines the IBalanceTracker.balanceTrackerHook
function, which is required to be called on every transfer of the rewarded token if a user opted in for the hook to be called. Provided that the account opted in for their balance to be tracked, on every balance change of the account which is a result of the deposit, withdrawal, shares transfer etc., the credit vault contract calls the balanceTrackerHook
function. Once the hook is called, the contract updates the account's balance and all the enabled rewards distribution data in order to track the rewards accrual.
The already deployed TrackingRewardStreams
(referred to as BalanceTracker
in the deployed contract addresses section) can be used by any other newly developed ERC20-like contract. Assuming that one wants to embed the reward distribution mechanism into their own contract, it is enough that the token contract calls the balanceTrackerHook
function on every balance change of the account. For inspiration, one can take a look at the mock ERC20 token contract here.
Staking Reward Distribution
The staking StakingRewardStreams
implementation also inherits from the BaseRewardStreams
contract. It defines two functions: stake
and unstake
, which are used to stake and unstake the rewarded token. This contract has been audited but haven't been yet deployed. If you are interested in using it, please reach out to us so we can deploy it for you.
Internal Mechanics
In both modes, each distributor contract defines an EPOCH_DURATION
constant, which is the duration of a single epoch. This duration cannot be less than 1 week and more than 10 weeks. The currently deployed TrackingRewardStreams
instance used by the EVK vaults has an epoch duration of 2 weeks.
When registering a new reward stream for the rewarded
token, one must specify the startEpoch
number when the new stream will come into effect. To protect users from obvious mistakes, the distributor contract enforces a soft requirement that ensures the startEpoch
is not more than 5 epochs into the future. Moreover, one must specify the rewardAmounts
array, which instructs the contract how much reward
one wants to distribute in each epoch starting from startEpoch
. The rewardAmounts
array must have a length of at most 25 for one function call.
If rewarded epochs of multiple reward streams overlap, the amounts will be combined and the effective distribution will be the sum of the amounts in the overlapping epochs.
Permissionless Reward
Unlike other permissioned distributors based on the billion-dollar algorithm, Reward Streams distributors do not have an owner or admin meaning that none of the assets can be directly recovered from them. This property is required in order for the system to work in a permissionless manner, allowing anyone to transfer rewards token to a distributor and register a new reward stream. The drawback of this approach is that reward tokens may get lost if nobody earns them at the given moment (i.e. nobody stakes required assets or nobody enabled earning those rewards). In order to prevent reward tokens from being lost when nobody earns them at the moment, the rewards get virtually accrued by address(0)
and, in exchange for updating given distribution data, are claimable by anyone with use of updateReward
function.