# defi:uniswap质押挖矿合约的奖励算法推导

By [daxiong](https://paragraph.com/@daxiong) · 2021-12-28

---

质押挖矿的奖励产生
=========

质押挖矿是将质押代币存入矿池中，矿池每分钟或者每个区块产生一些奖励代币，然后按照质押代币的份额将奖励代币派发给用户。

质押挖矿的算法推导
=========

### 一般思路

1.  将每分钟产生的奖励代币除以当时的质押代币总份额，计算每个份额的奖励；
    
2.  遍历所有质押的用户，按照持有的质押代币份额 \* 每份额的奖励 给每个用户增加相应的奖励；
    

这个思路在其他语言中可行，但是在solidity中是不行。原因就在于，EVM的状态改变是需要成本而且是有gas限制的，如果遍历的用户特别多，就会造成gas耗尽，导致失败。

### 换个思路

既然不能用循环，那么能不能根据最后一次奖励的时间和第一次质押的时间计算总奖励呢？

我们用 ：

a表示每分钟的奖励

Pn表示第n分钟开始的时候矿池的质押代币总量

FAn表示第n分钟开始的时候用户A获得的总奖励

Tn表示第n分钟开始的时候，累计的每质押代币可以分配的奖励之和，即:Tn = a / P1 + a / P2 + a / P3 + … + a / Pn

假设 用户A在第2分钟开始的时候质押了b个质押代币，用户B在第4分钟开始的时候质押了c个质押代币，那么第6分钟开始的时候，用户A的奖励 :

> FAn = (a/P1\*0)+(a/P2\*0)+(a/P3\*b) + (a/P4\*b) + (a/P5\*b)+ (a/P6\*b)
> 
> \= b \*（a/P3 + a/P4 + a/P5 + a/P6）
> 
> \= b \* ((a/P1 + a/P2 + a/P3 + a/P4 + a/P5 + a/P6) - (a/P1 + a/P2))
> 
> \= b \* (T6 - T2)
> 
> T6 表示 用户A在第6分钟开始结算的时候，累积的每代币分配奖励之和
> 
> T2 表示 用户A在第2分钟开始质押的时候，累积的每代币分配奖励之和
> 
> b 表示结算的时候用户A的总质押代币数量
> 
> 所以，**_用户在结算的时候的奖励公式 = （结算的时候累计的每代币分配奖励之和 - 质押的时候累计的每代币分配奖励之和） \* 结算的时候总质押代币数量_**。
> 
> 所以，如果某个用户在第m分钟开始的时候质押b个质押代币，在第n分钟开始的时候结算，那么公式
> 
> **_Fn = b \* (Tn - Tm)_**

以上的公式只适用于用户A的质押代币数量没有变化的情况，如果用户A的质押代币数量增加（或减少）了呢，那要怎么计算？

假设 用户A在第2分钟开始的时候质押了b个质押代币，用户B在第4分钟开始的时候质押了c个质押代币，用户A又在第5分钟开始的时候质押了d个质押代币，那么第6分钟开始的时候，用户A的奖励 :

> 此时，用户A在结算时的质押代币数量我们用e表示，e=b+d
> 
> 我们可以将用户A的两次质押：
> 
> *   第一次是：第2分钟开始质押，第5分钟开始结算，质押代币数量是b；
>     
> *   第二次是：第5分钟开始质押，第6分钟开始结算，质押代币数量是e。
>     
> 
> **_F5 = b \* (T5 - T2)_**
> 
> **_F6 = e \* (T6 - T5)_**
> 
> **_总和 =_** **_F6 + F5 = e \* (T6 - T5) + F5_**
> 
> e 表示用户A在结算的时候的总质押代币数量
> 
> T6 表示用户A在第6分钟开始结算的时候，累积的每代币分配奖励之和
> 
> T5 表示用户A在第5分钟开始结算的时候，累积的每代币分配奖励之和
> 
> F5 表示用户A在第5分钟开始质押的时候，计算的总奖励
> 
> 因此，我们只要记录用户在每次质押的时候的总奖励以及累计的每代币分配奖励之和，即可计算用户在下次结算的时候总奖励
> 
> **所以最终的公式为：**
> 
> **某个用户在第m分钟开始的时候质押b个质押代币，在第n分钟开始的时候结算，那么公式：**
> 
> **_奖励总和 = b \* (Tn - Tm) + Fm_**

---

*Originally published on [daxiong](https://paragraph.com/@daxiong/defi-uniswap)*
