Solana Staking Program Breakdown
Resource: https://github.com/step-finance/step-staking
TLDR;
Users send tokens to the token vault in exchange for an “equity” of the vault, represented as xToken. The program tracks the total amount of xToken distributed as well as the amount owned by each individual. As the vault grows from an external source of revenue, each xToken is worth more. When the user unstake, the program will calculate the user share of the vault base on the xToken.
Initialize
The program creates a Staking Account and initializes a Token Vault on initializing. The token vault is owned by the token program and holds the actual balance of the token, whereas the staking account is used to track information regarding the vault.
Initlizer_key
The public key of the vault initializer. This is used to update the lock_end_date and freeze_program.
Total_x_token
The total amount of x_token distributed.
Usage
Params:
vaultBump, stakingBump, lockEndDate
Accounts:
{ tokenMint, tokenVault, stakingAccount, initializer, systemProgram, tokenProgram, rent }
Stake (Part 1)
In this scenario, User A decided to stake 1,000 tokens into an empty vault. Because it is an empty vault, the exchange rate is set to 1. So, for 1,000 tokens, User A will receive 1,000 xTokens.
Usage
Params:
vaultBump, stakingBump, userStakingBump, amount
Accounts:
{ tokenMint, tokenFrom, tokenFromAuthority, tokenVault, stakingAccount, userStakingAccount, systemProgram, tokenProgram, rent }
Stake (Part 2)
A few minutes later, an external source of revenue yielded 300 tokens and was deposited into the token vault. User B decided to stake 3,000 tokens into the vault that now contains 1,300 tokens, 1,000 from User A, and 300 from an external source. The formula used to calculate the exchange rate for token/xToken is;
amount * total_xToken / total_token
With this, the User B will receive 2,307 xToken (3,000 * 1,000 / 1,300). Note that the staking account keeps track of the total_xToken in circulation.
Stake (Part 3)
After a couple of minutes, an external source of revenue yielded another 500 tokens and was deposited into the token vault. User C decided to stake 5,000 tokens into the vault that now contains 4,800 tokens. Using the same formula we user C will receive 3,444 xToken (5,000 * 3,307 / 4,800).
Unstake
If any of the users wish to unstake, the token amount the user will is based on the proportion of xToken the user holds. The formula used to calculate this is;
amount * token_token / total_xToken
As User A was around before the first and second revenue injection, User A would receive a total of 1,451 tokens, an addition of 451 tokens. While User B receives 3,348 tokens, an addition of 348 tokens for joining before the second revenue injection. On the other hand, User C will only receive the amount staked as there wasn’t any revenue injection.
Usage
Params:
vaultBump, stakingBump, userStakingBump, amount
Accounts:
{ tokenMint, xTokenFromAuthority, tokenVault, stakingAccount, userStakingAccount, tokenTo, tokenProgram }
Emit Reward
Usage
const res = await program.simulate.emitReward({
accounts: { tokenMint, tokenVault, stakingAccount, tokenFromAuthority, userStakingAccount }});let reward = res.events[0].data;
console.log(‘Deposit Amount: ‘, reward.deposit.toString());
console.log(‘Reward Amount: ‘, reward.reward.toString());