18

优化 DeFi 合约利息计算 gas 消耗

 4 years ago
source link: https://learnblockchain.cn/article/767
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

以太坊上合约开发,如何节省gas一直是个很头痛的问题. 在defi应用中, 由于利率是不停的动态变化的,导致用户利息的结算非常复杂, gas消耗非常大,甚至出现, 突破gaslimit, 以下这个图是多少合约开发的噩梦.

uyY7Nfa.png!web

常规计算利息方法

最简单的情况, 如果利率是固定的,一年的利息计算是: 利息= 本金 * 利率

用符号表示即为:

$yi = B * yr $

通常我们说的利率是指年利率(yr: year rate)

用户一年后可提取连本带息的资金(记为 YI : Year Interest):

$ YI = yi + B = B *( 1 + yr) $

以上是按年整存整取的计算,实际中通常是随存随取的,我们需要把利率折算按天(根据需要可以按区块,按秒)利息的复利,假设每天的利率记为 dr ,则一年的收益为:

$ YI = B *( 1 + dr)^{365} $

年利率与天利率的换算是:$( 1 + yr) = ( 1 + dr)^{365} $.

为了方便我们把$1 + dr$记为 $dR$ , 则用户$n$天后可提取金额(记为$DI_t$)为:

$ DI_t = B * dR^n $

如: 第100天时一共可提取:

$ DI_{100} = B * dR^{100} $

以上是固定利率的计算方式, 在实际的金融应用中, 汇率会随资金利用率波动(通常有新存款,利率下降,有新贷款利率上升), 实际收益的计算就会是这样:

$dR_i$ 表示某一时间段利率, $n_i$ 表示$dR_i$持续的时间, $t_i$ 表示某段结束时刻,即:$tn = n1+n2+n3+...+ni$

$ DI_{t1} = B * dR_1^{n1} $

$ DI_{t2} = DI_{t1} * dR_2^{n2} $

$ DI_{t3} = DI_{t2} * dR_3^{n3} $

这里注意,每次计算的本金实际是复利计算后的结果。

很明显,使用这个计算方式, 随着时间的推移, 计算量指数上涨, 很容易突破区块 gas_limit 。

并且使用这个方式需要存储很多的状态变量:用户存款的金额, 存款的时间, 以及记录下每次利率变化的时间和变化后的利率.

使用单位累积利率

单位累积利率其实是我创新的一个词,我等下给出定义,我们先对上面的公式把$DI_t$代入做一个演算:

$ DI_{t1} = B * $($dR_1^{n1}$)

$ DI_{t2} = B * $($dR_1^{n1} * dR_2^{n2}$)

$ DI_{t3} = B * $($dR_1^{n1} * dR_2^{n2} * dR_3^{n3} $)

我们把上述括号的部分定义为 单位累积利率(chi : cumulate interest) , 这样每个节点可提取金额就可以简化为:

$ DI = B * chi $

标记为chi,是因为 MakerDao 合约代码里也是使用 chi

然后在每次有 汇率更新的时候更新chi (chi初始是1),它与上一次的chi的关系如下:

$ chi_{ti} = chi_{n-1} * dR ^{ni} $

如果一个用户从初始时刻进行存款, 那么它可提取金额总是 $$ DI = B * chi $$

如果用户在$t$时刻(对应$chi_t$)进行存款, 继续按 $ DI =B * chi $结算的话, 就会相当于多算了$chi_t$的利息, 因此只需要在结算的时候除以$chi_t$。 (也可以用户存款时保存 $B/chi_t$ , 可以理解为单位累积利率下的本金)

采用这个方式计算,$chi$的计算量一直是固定的, 系统只需要记录一个全局的$chi$以及每个用户的本金即可, 非常高效。

做一个简单演算验证, 初始日利息为1.01, 初始 chi = 1, 用户1存入10, 则: (若)第二天用户1提款: chi = 1.01,可提取为 10 *1.01 = 10.1; 第三天用户1提款: chi = 1.0201 (1.01的平方) ,用户1可提取为10 *1.0201 = 10.201,此时用户2存入100, 日利息降为1.009. 第四天用户2提款: chi = 1.0292809 , 用户2可提取为 100 / 1.0201 * 1.0292809 = 100.9

结果会和常规方式结算的结果完全一致。

本文发布于深入浅出区块链,这里是国内最受欢迎的区块链技术社区,你还没有加入吗?

未经授权,请勿转载。


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK