Compounded Dynamic Staking
AnonymousBlockchain Architect of OpenPad\text{Anonymous}\\ \text{Blockchain Architect of OpenPad}

Compound Dynamic Staking

OpenPad employs an auto-compounded vault mechanism for $OPN staking, compounding investor stakes to generate exponential returns and calculate the token sale allocations based on the compounded capital. An auto-compound mechanism is used to reinvest rewards generated from the staking pool into the same staking pool, creating a compound effect.

Reward, APR, & APY Calculations

A time-based mathematical formula is used to track the rewards and staking balances of every staker in the vault and the formula is updated according to the last update time and current time after every staking amount changing transaction.
The parameters are as follows:
  • LastUpdateTime (can be any user)
  • TotalSupply (total staked amount)
  • UserBalance
  • Rewards[.] (total reward added to the user so far)
  • PaidReward[.] (Total rewards paid to all users so far)
  • RewardRate (reward to pay in return for a single token per second)
Let’s imagine user A staking 100 $OPN tokens. The updateRewardsfunction is called before the staking action and the initial parameters are updated as follows.
rewardsPerTokenStored=0 (Since totalSupply is zero initially)lastUpdateTime=t0rewardsPerTokenStored = 0 \text{ (Since totalSupply is zero initially)} \\lastUpdateTime=t_0
​Then, the reward parameters of user A, rewards[A] and userRewardPerTokenPaid[A], are updated as follows.
rewards[A]=0 (Since balance of A is 0 initially) userRewardPerTokenPaid[A]=rewardsPerTokenStored (0 in this case) rewards[A] = 0 \text{ (Since balance of A is 0 initially) } \\ userRewardPerTokenPaid[A] = rewardsPerTokenStored \text{ (0 in this case) }
​After updating these parameters, the algorithm continues with the accumulation of totalSupply and balances[A].
totalSupply=0+100=100balances[A]=0+100=100totalSupply=0+100=100 \\ balances[A]=0+100=100
Now that we have initialized the first staking parameters and we have a non-zero totalSupply; let’s imagine a new user B staking 200 tokens assuming t1 is the current time.
rewardsPerTokenStored=0+(t1t0)rewardRate100totalSupplylastUpdateTime=t1rewardsPerTokenStored = 0 + (t_1-t_0) * \dfrac{rewardRate}{100 * totalSupply} \\ lastUpdateTime=t_1 \\
​​Then, the reward parameters of user B, rewards[B] and userRewardPerTokenPaid[B], are updated as follows.
rewards[B]=0 (Since balance of B is 0 initially)userRewardPerTokenPaid[B]=rewardsPerTokenStored=(t1t0)rewardRate100totalSupplyrewards[B]= 0 \text{ (Since balance of B is 0 initially)} \\ userRewardPerTokenPaid[B] = rewardsPerTokenStored = \dfrac{(t_1-t_0) * rewardRate}{100 * totalSupply}
After updating these parameters, the algorithm continues with the accumulation of totalSupply and balances[B].
totalSupply=100+200=300balances[B]=0+200=200totalSupply=100+200=300 \\ balances[B]=0+200=200
Now, let's imagine that another user joins the staking pool, say user C, staking 300 $OPN tokens.​ rewardsPerTokenStored and lastUpdateTime is updated as follows.
rewardsPerTokenStored=(t1t0)rewardRate100+(t2t1)rewardRate300lastUpdateTime=t2rewardsPerTokenStored= (t_1-t_0) * \dfrac{rewardRate}{100}+ (t_2-t_1) * \dfrac{rewardRate}{300} \\ lastUpdateTime=t_2
​Then, the reward parameters of user C, rewards[C] and userRewardPerTokenPaid[C], are updated as follows.
rewards[C]=0 (Since balance of B is 0 initially)userRewardPerTokenPaid[C]=rewardsPerTokenStored=(t1t0)rewardRate100(t2t1)rewardRate300 \\ rewards[C]=0 \text{ (Since balance of B is 0 initially)} \\ userRewardPerTokenPaid[C]=rewardsPerTokenStored =\dfrac{(t_1-t_0) * rewardRate}{100} * \dfrac{(t_2-t_1) * rewardRate}{300}
After updating these parameters, the algorithm continues with the accumulation of totalSupply and balances[C].
totalSupply=300+300=600balances[C]=0+300=300totalSupply=300+300=600 \\ balances[C]=0+300=300
Now the essential part of the algorithm comes to play. What if user B wants to claim their rewards? How does the algorithm calculate how much B has earned since they staked after A but before C? getReward function also triggers the updateRewards function so the parameters are updated.
rewardsPerTokenStored=(t1t0)rewardRate100+(t2t1)rewardRate300+(t3t2)rewardRate600lastUpdateTime=t3rewardsPerTokenStored= (t_1-t_0) * \dfrac{rewardRate}{100} + (t_2-t_1) * \dfrac{rewardRate}{300} + (t_3-t_2) * \dfrac{rewardRate}{600} \\ lastUpdateTime=t3
​Then, the reward parameters of user B, rewards[B], are updated as follows.
rewards[B]=balances[B](rewardsPerTokenStoreduserRewardPerTokenPaid[B])+rewards[B]rewards[B]=200((t1t0)rewardRate100+(t2t1)rewardRate300+(t3t2)rewardRate600((t1t0)rewardRate100totalSupply))rewards[B]=200((t2t1)rewardRate300+(t3t2)rewardRate600rewards[B]=balances[B]* (rewardsPerTokenStored-userRewardPerTokenPaid[B])+rewards[B] \\ rewards[B]=200* ((t_1-t_0)* \dfrac{rewardRate}{100}+ (t_2-t_1)* \dfrac{rewardRate}{300}+ (t_3-t_2) * \dfrac{rewardRate}{600}- (\dfrac{(t_1 - t_0) *rewardRate}{100 * totalSupply})) \\ rewards[B]=200* ((t_2-t_1) * \dfrac{rewardRate}{300} + (t_3-t_2) * \dfrac{rewardRate}{600}\\
​Finally, userRewardPerTokenPaid[B] is calculated as follows.
userRewardPerTokenPaid[B]=(t1t0)rewardRate100+(t2t1)rewardRate300+(t3t2)rewardRate600userRewardPerTokenPaid[B]= \dfrac{(t_1-t_0) * rewardRate}{100} + \dfrac{(t_2-t_1) * rewardRate}{300} + \dfrac{(t_3-t_2) * rewardRate}{600}
Now the necessary updates have been completed and user B is ready to claim their rewards which are stored in the rewards[B] mapping that we just updated. The reward is sent to the user and is reset to 0 until they claim again at a later time. As seen from the calculations, the reward is calculated so that it does not account for the time period where B has not staked (There was only user A) but accounts for the time periods where there is<A, B> and <A, B, C> separately. This algorithm handles every user’s rewards in the same way.

Auto-Compound Strategy

To understand auto-compound strategy in $OPN staking, the concept of APR (Annual Percentage Rate) and APY (Annual Percentage Yield)should be understood. Simply, APR is simple and APY is compound interest, reflecting the interest you make in your interest.
APR=((InterestReturnPrincipal)n365)100APR = (\dfrac{(\dfrac{InterestReturn}{Principal})}{n} * 365) *100
where InterestReturn is the total interest paid over the life of the staking, principal is the total deposit, and n is the number of days in the total reward period. Hence, there is no rebasing or reinvesting included whereas, in the APY calculation, interest on the earned interest (reinvesting effect) is also considered.
APY=(1+APRn)N1APY = (1 + \dfrac{APR}{n})^N - 1
where N is the compounding frequency (e.g., daily, weekly, monthly, quarterly, etc.) With every rebase, the returned interest reward is added to the stakers' balance automatically resulting in a higher compound rate in the next period.
To compare APR and APY, let's imagine that $OPN staking offers 100% APR at a certain timeframe, and imagine this APR continues to remain the same for over one year. Then, let N = 365, i.e., if compounding frequency is daily, and let Principal = $10,000 then APY, TotalInterest andFutureValue will be
APY=(1+100365)3651=171.4567%APY = (1 + \dfrac{100}{365})^{365} - 1 = 171.4567 \%
Hence, there is a 71.4567%interest rate difference between APR and APY.
FutureValue=$27,145.67TotalInterest=FutureValuePrincipal=17.145,67\\ Future Value = \$27,145.67 \\ Total Interest=FutureValue -Principal = 17.145,67
​Daily compound interest accumulated FutureValueof the user to 27,145.67which will be 20,000 if compound interest wouldn't be considered, creating a 7,145.67 extra interest. To compare the compound effect of the $OPN auto-compound staking pool with the simple staking pools, here is the graph representing the accumulated $OPN amount over time; the difference between auto-compounded capital and simple capital is increasing over time, reaching 7,145.67 at the end of the first year and 43688.76 at the end of the second year.
The dark filled area represents the difference between $OPN capitals over time

Protocol Fees

Note that staking/vault fees are not considered here; however, the calculation of the APR can be computed considering network fees as follows.
RealizedAPR=APR(1FP)RealizedAPY=(1+APR(1FP)n)N1Realized APR= APR * (1 - F_P) \\ RealizedAPY = (1 + \dfrac{APR * (1 - F_P)}{n})^N - 1
​where RealizedAPR is the actual APR rate after subtracting protocol fees FP.
Copy link
On this page
Compound Dynamic Staking
Reward, APR, & APY Calculations
Auto-Compound Strategy