1. ERC4626简介
ERC4626 协议是一种用于代币化保险库的标准,它可以优化和统一收益保险库的技术参数。收益保险库是指使用不同策略来为用户提供最佳收益的合约,例如借贷市场、聚合器或本身具有利息的代币。ERC4626 协议提供了一个标准的 API,用于表示单个底层 ERC-20 代币的收益保险库份额。
用户通过存入 ERC20 Token,从而获取一定比例的 vToken。在erc20 Token 存入的过程中,会在一定的时间内产生收益。在收益到期后,用户可以通过持有的vToken个数,获得一定比例的收益汇报。
此外ERC4626继承了ERC20,具有ERC20所具有的所有功能,而且还必须实现
IERC20Metadata
接口。举个例子来理解这个系统, ERC4626的基础代币可以比作是 黄金(_asset), 而通过存入 黄金来获取钞票(shares),既可以通过存入黄金来获取
shares
,也可以通过shares
兑换出黄金。例子不是很准确,但是可以初步了解这是一个什么东西。
2. ERC4626代码解读
源代码:链接。
接口中定义的函数如下:
- asset(): 返回保险库使用的底层代币的地址,必须是一个 ERC-20 合约。
- totalAssets(): 返回保险库持有的底层资产的总量,应该包括任何由收益产生的复利。
- convertToShares(): 返回保险库为给定数量的底层资产兑换的份额数量。
- convertToAssets(): 返回保险库为给定数量的份额兑换的底层资产数量。
- maxDeposit(): 返回接收者在单次存款调用中可以存入的底层资产的最大数量。
- previewDeposit(): 允许用户在当前区块模拟他们的存款效果。
- deposit(): 将底层资产存入保险库,并将份额授予接收者。
- maxMint(): 返回接收者在单次铸造调用中可以铸造的份额的最大数量。
- previewMint(): 允许用户在当前区块模拟他们的铸造效果。
- mint(): 将份额铸造给接收者,并从保险库中取出相应数量的底层资产。
- maxWithdraw(): 返回接收者在单次取款调用中可以取出的底层资产的最大数量。
- previewWithdraw(): 允许用户在当前区块模拟他们的取款效果。
- withdraw(): 将份额从接收者处销毁,并将相应数量的底层资产从保险库中取出。
- maxRedeem(): 返回接收者在单次赎回调用中可以赎回的份额的最大数量。
- previewRedeem(): 允许用户在当前区块模拟他们的赎回效果。
- redeem(): 将份额从接收者处销毁,并将相应数量的底层资产从保险库中取出。
主合约:
1 | IERC20 private immutable _asset; |
这是该金库 vault的基础代币,相当于黄金。
1 | function _tryGetAssetDecimals(IERC20 asset_) private view returns (bool, uint8) |
该函数的作用是获取 基础代币的精度值,通过 staticcall
来调用assert_
的 decimals()
函数获取该角度值,如果调用成功则返回(true, uint8(returnedDecimals))
,否则(false, 0)
。
1 | function maxDeposit(address) public view virtual returns (uint256) |
查询指定地址可以存入/铸造最多的数目,二者都返回type(uint256).max
。
1 | function maxRedeem(address owner) public view virtual returns (uint256) |
查询可以赎回的最大资产,这里返回的是vToken
的数目。
1 | function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256) { |
将资产转化为shares
,可以理解为黄金兑换为钞票,这步计算可以抽象看作是
1 | (assets * (totalSupply() + 10 ** _decimalsOffset())) / (totalAssets() + 1) |
1 | function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256) { |
将shares
转化为资产,可以理解为钞票兑换为黄金,这步计算可以抽象看作是:
1 | (shares * ((totalAssets() + 1) / (totalSupply() + 10 ** _decimalsOffset()))) |
1 | function previewDeposit(uint256 assets) public view virtual returns (uint256) { |
该函数的功能是此时计算出指定的资产可以兑换多少shares
。
1 | function previewMint(uint256 shares) public view virtual returns (uint256) { |
该函数的功能是此时计算指定的shares
可以兑换成多少资产。
1 | function previewWithdraw(uint256 assets) public view virtual returns (uint256) { |
该函数的功能是此时计算指定的shares
可以兑换成多少资产。和previewMint()
函数极其相似。
1 | function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual { |
这是存入资产的内部函数,通过SafeERC20
的库函数,完成caller
向address(this)
转移_asset
代币操作。
1 | function deposit(uint256 assets, address receiver) public virtual returns (uint256) |
外部的存款函数,功能是msg.sender
往该 vault存入执行数额的资产,并将兑换出的vToken
发送到receiver
地址,通过mint()
函数,铸币数量是根据此时金库状态计算的uint256 assets = previewMint(shares)
。
1 | function _withdraw( |
该功能是取出资产的内部函数,要求调用者msg.sender
必须是owner
,或者msg.sender
被owner
授权。销毁owner
数量为shares
的vToken,并从金库向receiver
转移_asset
代币。
1 | function withdraw(uint256 assets, address receiver, address owner) public virtual returns (uint256) { |
外部的取款函数,实现逻辑是调用内部的_withdraw()
函数。参数是传入待取出的资产数量。
1 | function redeem(uint256 shares, address receiver, address owner) public virtual returns (uint256) { |
这也是一个取款函数,内部调用的也是_withdraw()
函数,但是和withdraw()
函数不同的点在于,参数传入的是shares
。
3. 总结
ERC4626 协议是一种用于代币化保险库的标准,它可以优化和统一收益保险库的技术参数。它为单个底层 ERC-20 代币的收益保险库提供了一个标准的 API,并为存入、取出、铸造、赎回等操作提供了基本功能。它在 DeFi 领域有很多潜在的应用场景,可以为用户提供更多的选择和便利。它也有助于推动收益保险库的发展和创新,为 DeFi 生态系统增加更多的价值。