Crate: dojoswap_staking
The dojoswap_staking contract is responsible for handling requests to bond and unbond stake and for computing and distributing staking rewards.
Its state includes
A governance address, which is capable of updating the reward-distribution schedule, updating the governance address, and migrating the staking contract to a new address/version
The CW20 token that rewards are denominated in,
dojo_token
The CW20 token that can be staked,
staking_token
The distribution schedule, a vector of
(u64, u64, Uint128)
s (interpreted as start time, end time, and tokens per second)The most recent block time that rewards were computed for,
last_distributed
The current total amount of bonded stake,
total_bond_amount
The cumulative sum of the ratio of rewarded tokens per bonded stake,
global_reward_index
, used for computing individual stakers' rewards on demand
as well as the following, per staking address:
A snapshot of
global_reward_index
as of the last time rewards were computed for this particular staker,reward_index
The amount of bonded
staking_token
s for this staker,bond_amount
The amount of
dojo_tokens
that have been accumulated for this staker, but not yet withdrawn,pending_reward
Function: instantiate
The instantiate
function configures the initial state of the contract. The sender of the instantiate message is stored as the governance address. The dojo_token
, staking_token
, and initial distribution_schedule
are set from the contents of the instantiate message. Additionally, last_distributed
is set to the current block time, and total_bond_amount
and global_reward_index
are initialized to zero.
Function: receive_cw20
The receive_cw20
function is called in response to being sent CW20 tokens. It rejects tokens of any type other than staking_token
, and it accepts staking_token
s, passing the sender and amount to bond
.
Function: bond
The bond
function increases a staker's bonded tokens, initializing the tracking thereof if the staker previously had no stake.
Prior to increasing the staker's bond amount, it updates the global state with compute_reward
and the per-staker state with compute_staker_reward
so that the rewards up to the current block use the previous bonding amount. It increases the staker's bond amount with increase_bond_amount
, then stores the updated per-staker and global state in storage.
Function: compute_reward
The compute_reward
function updates last_distributed
to the current block time.
If there is a nonzero amount of total bonded stake, it additionally iterates through the schedule entries to calculate a cumulative sum of the rewards accumulated since the last block in which compute_reward
was called and adds it (divided by the total amount of stake) to global_reward_index
.
Function: compute_staker_reward
The compute_staker_reward
function updates the reward allocated to an individual staker. The difference between the current global_reward_index
and the staker's reward_index
, each multiplied by the staker's amount of bonded stake, is added to the staker's pending_reward
, and its reward_index
is updated to the current global_reward_index
. This calculation can be made more overflow resistant; see Finding ref↗.
Function: unbond
The unbond
function decreases a staker's bonded tokens, sending them to the staker.
It enforces that a staker cannot withdraw more than the amount of tokens they have staked.
Prior to decreasing the staker's bond amount, it updates the global and per-staker state via compute_reward
and compute_staker_reward
so that the rewards up to the current block use the previous bond amount; it then decreases the staker's bond amount with decrease_bond_amount
.
If the staker has no pending reward to withdraw and decreased their bond amount all the way to zero, it removes the staking entry for the staker entirely (which saves storage space); otherwise, it stores the modified state for the staker.
It then updates the global state and sends the unbonded staking tokens to the staker.
Function: withdraw
The withdraw
function allows a staker to withdraw their pending rewards.
It updates the global and per-staker state via compute_reward
and compute_staker_reward
, reads the updated pending_reward
, then zeroes it in the to-be-updated state.
If the staker has zero bond amount (i.e., they unbonded previously, potentially in the same block), it removes their staking entry from the state; otherwise, it stores their modified entry in the state.
It then updates the global state and sends the withdrawn Dojo tokens to the staker.
Function: update_config
The update_config
function allows the governance address to append entries to the distribution schedule.
It validates that the message sender is the governance address. It validates the schedule by calling the assert_new_schedules
function; it then copies the validated schedule and the previous configuration's dojo_token
and staking_token
to a configuration object that it writes to the state.
Function: assert_new_schedules
The assert_new_schedules
function validates that a proposed distribution schedule does not remove any schedule entries that have started nor adds schedule entries that would have retroactively started.
Function: update_gov
The update_gov
function allows the governance address to set a replacement governance address.
It validates that the message sender is the current governance address. It does not check that the replacement governance address is a valid address, with similar impact to Finding ref↗.
Function: migrate_staking
The migrate_staking
function allows the governance address to migrate the staking contract to a new, potentially updated instance. It is intended to send the reward tokens that have not yet been distributed to the new contract and leave the staking tokens and pending rewards with the current contract to be withdrawn by stakers.
It validates that the message sender is the governance address. It updates the global reward state via compute_reward
(but does not update the per-staker rewards; those will be updated on subsequent calls to unbond
/withdraw
).
It clears all future (not yet started) schedule entries from the state, then iterates all past and current schedule entries to account for how many rewards have been distributed, but this overestimates schedule entries during which there was zero total bonded stake; see Finding ref↗.
It writes the updated schedule and global rewards to the state, then sends the reward tokens that have not yet been distributed to the new contract.