深入解析AAVE智能合约:计算和利率

概述

读者可前往我的博客获得更好的阅读体验。

本文主要讨论AAVE V3中的数学计算模块,该模块位于src/protocol/libraries/math文件夹内,基础合约为WadRayMath

本文主要包含以下内容:

  1. 浮点数的在solidity内的表示及四则运算
  2. 单利和复利的计算
  3. 百分比的乘除

相比于上一篇文章,本文较为简单且篇幅较短。

浮点数表示与计算

在AAVE中,我们使用定点浮点数进行浮点数表示,具体单位如下:

uint256 internal constant WAD = 1e18;
uint256 internal constant RAY = 1e27;

显然,使用wad表示的浮点数精度为小数点后18位,而使用ray进行标识则精度为小数点后27位。

WayRayMath中,我们也定义了一些必要的常量以用于后续的计算,如下:

uint256 internal constant HALF_WAD = 0.5e18;

uint256 internal constant HALF_RAY = 0.5e27;

uint256 internal constant WAD_RAY_RATIO = 1e9;

我们定义了基础的数据类型后,我们需要完善其基础的乘除操作。此处仅完善乘除操作是因为自行定义的方法可以保证乘除操作可以实现四舍五入。

我们首先分析wad的乘法操作:

function wadMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
    // to avoid overflow, a <= (type(uint256).max - HALF_WAD) / b
    assembly {
        if iszero(
            or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_WAD), b))))
        ) {
            revert(0, 0)
        }

        c := div(add(mul(a, b), HALF_WAD), WAD)
    }
}

此函数首先进行了条件判断,要求同时满足以下条件:

  1. b的数值不为0
  2. a 小于 (type(uint256).max - HALF_WAD) / b

这些条件存在的原因为:

  1. 避免相除时分母为 0
  2. 避免a * b溢出

为什么使用(type(uint256).max - HALF_WAD) / b而不是type(uint256).max / b 作为判断是否溢出的条件?正如前文所述,此乘法会进行四舍五入,所以如果使用type(uint256).max / b进行判断,可能导致进一后溢出。

此处,对于第二个条件的判定稍有复杂,我们在此处特别分析iszero(gt(a, div(sub(not(0), HALF_WAD), b)))部分代码。not(0)的数值大小即为type(uint256).max(0使用not全部取反后即为type(uint256).max)。sub(a, b)操作符的含义即为a - b,而gt(a, b)则会判断a > b,如不符合条件,则返回0,符合条件则返回1

上述操作符可以在EVM Codes内找到其具体内容

如果不满足上述条件则会revert抛出异常。部分读者可以对revert(offset, size)操作符不熟悉,简单来说,此操作符会中止函数运行并回滚,退回未使用gas,并将从offset开始的长度为size大小的内容作为异常信息抛出。

revert的更加详细的介绍请参考evm codes

介绍了对参数的具体要求后,我们接下来介绍真正的计算步骤c := div(add(mul(a, b), HALF_WAD), WAD),使用数学语言描述为(a * b + HALF_WAD) // WAD,其中//即为整除符号,但此整除仅会向下取整。

我们通过将a * b的实际值与HALF_WAD(相当于 0.5)相加再与WAD(相当于1)进行整除获得四舍五入的结果。

此处除去了一个WAD的原因有以下两点:

  1. 保证单位仍未WAD
  2. 通过整除实现四舍五入

此处,我们可以讨论一下此函数的函数类型,此函数的函数类型为:

  1. internal 表示此函数仅能用于合约内部调用
  2. pure 表示此函数不读取合约内的状态变量也不修改合约变量

在介绍完乘法相关的运算的方法后,我们介绍除法的具体实现,代码如下:

function wadDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
    // to avoid overflow, a <= (type(uint256).max - halfB) / WAD
    assembly {
        if or(
            iszero(b),
            iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), WAD))))
        ) {
            revert(0, 0)
        }

        c := div(add(mul(a, WAD), div(b, 2)), b)
    }
}

在讨论具体的代码限制条件前,我们首先分析其具体的逻辑代码部分即c := div(add(mul(a, WAD), div(b, 2)), b),改写为数学表达式为((a * WAD) + b // 2) // b

具体推导过程如下:
[ a / b ] = ⌊ a / b + 1 2 ⌋ = ( ( a / b ) + 1 2 ) / / 1 = ( a ∗ 1 + b 2 ) / / b \begin{align} \lbrack{a / b}\rbrack &= \lfloor{a / b + \frac{1}{2}}\rfloor \\ &= ((a / b) + \frac{1}{2}) // 1 \\ &= (a * 1 + \frac{b}{2}) // b \end{align}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WongSSH

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值