AAVE_v1_code
代码架构
借贷池
存款
- 解释:我们存入标的资产,获得aToken,aToken可以1:1兑换标的资产,换句话说aToken是我们资产的凭证。
- 用法:如果想存入ETH,则_reserve是0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE,并且msg.value大于等于存入的ETH数量;如果是存入ERC20代币,则msg.value必须等于0
参数
_reserve:标的资产(underlying token)的地址
_amount:存多少钱
_referralCode:推荐代码,一般填0,跟推广有关,已经过时了就懒得去了解。
1 | function deposit(address _reserve, uint256 _amount, uint16 _referralCode) |
然后进入transferToReserve()
1 | function transferToReserve(address _reserve, address payable _user, uint256 _amount) |
取款
- _reserve:取出的标的资产地址
- _user:取款地址,可以是自己,也可以是别人
- _amount:取款数额
- _aTokenBalanceAfterRedeem:不知道有啥用
1 | function redeemUnderlying( |
设置抵押
用户可以将自己存入AAVE的资产作为抵押品,这是一个设置开关,为后续调用借款做准备
- _reserve:标的资产的地址
- _useAsCollateral:是否设置作为抵押品
1 | function setUserUseReserveAsCollateral(address _reserve, bool _useAsCollateral) |
然后进入core合约进行设置可以抵押
1 | function setUserUseReserveAsCollateral(address _reserve, address _user, bool _useAsCollateral) |
使用方法:
1 | // 获取借贷池接口 |
借款
超额抵押借款,调用这个方法之前要保证你已经存入了足够数量的抵押品
- _reserve:要借款的标的资产地址
- _amount:借入金额
- _interestRateMode:利率模型,1代表稳定利率,2代表可变利率
- _referralCode:推荐代码,一般填0,跟推广有关,已经过时了就懒得去了解。
1 | function borrow( |
使用方法:
1 | // 下面的操作之前,需要先质押ETH或者代币到AAVE,并且设置抵押 |
还款
- _reserve:标的资产,如果是还ETH,则填写0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE
- _amount:还款金额。如果是自己还款,可以用-1表示还所有钱;帮别人还不能用-1,建议发送略高于借款金额的钱
- _onBehalfOf:自己还则填写自己的地址,帮别人还则填写别人的地址
1 | function repay(address _reserve, uint256 _amount, address payable _onBehalfOf) |
使用方法:
1 | import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; |
清算
- 清算条件:当健康因子小于1的时候,可以进行清算。可以用
getUserAccountData()
查看健康因子 - 清算过程:可以清算部分资产,也可以清算全部,并且获得打了折扣的抵押品作为回报(相当于清算奖励)。清算人可以指定获取aToken或者标的资产作为清算奖励。清算完之后,健康因子会回到1以上
- 清算比例:清算人最多清算待偿还金额的 50%,清算折扣就是按照该金额计算的
- 清算前准备:清算人必须approve给AAVE _collateral ,否则无法清算
- 注意事项
- 在大多数情况下,清算人会选择尽可能多的清算(接近50%),将_purchaseAmount设置为-1表示AAVE最大允许的清算比例
- 如果是用ETH清算,要保证msg.value=_purchaseAmount
清算接口如下:
1 | function liquidationCall( |
实际的清算会跳转LendingPoolLiquidationManager合约执行liquidationCall()
:
1 | function liquidationCall( |
使用方法:
1 | import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; |
闪电贷
- _receiver:调用闪电贷的合约
- _reserve:借贷多少金额
- _params:回调函数的参数
1 | function flashLoan(address _receiver, address _reserve, uint256 _amount, bytes memory _params) |
使用方法:
1 | // 合约解接口 |
其他
- 借款利率模式:改变借款的模式,要做一定的检验才可以转换模式
1 | function swapBorrowRateMode(address _reserve) |
- 改变稳定模式的利率:市场的流动性比稳定利率还大时,用户想更新自己借款的稳定模式利率
1 | function rebalanceStableBorrowRate(address _reserve, address _user) |
view方法
getReserveConfigurationData()
:查看AAVE池子的基本信息,包括清算阈值、利率、是否启用质押、是否启用可借款等getReserveData()
:返回池子的详细信息,包括总流动性、可用流动性、总借款、流动性比例、稳定模式利率等getUserAccountData()
:查看用户在池子中总资产的情况,包括总质押数量、总可借款数量、健康因子、清算阈值、总抵押数量等,全部用ETH计价getUserReserveData()
:查看用户在池子中某种资产的情况,包括总质押数量、总可借款数量、健康因子、清算阈值、总抵押数量等,全部用ETH计价
AAVE代币
链下签名
链下签名授权
1 | function permit( |
使用方法:
1 | import { signTypedData_v4 } from 'eth-sig-util' |
快照
配备了快照机制,每次转账,mint,销毁之前,都会拍一次快照
1 | /** |
安全模块
质押
- onBehalfOf:获得stkToken的地址,一般是给自己
- amount:质押AAVE Token的数量
1 | function stake(address onBehalfOf, uint256 amount) external override { |
获取利润
可以看出,利润的token又是另外一种新的token
- to:接收利润token的地址
- amount:提取多少利润,-1表示全部提取
1 | function claimRewards(address to, uint256 amount) external override { |
取款
设计
- 存款之后,需要等待COOLDOWN_SECONDS之后,才可以取款,并且要在UNSTAKE_WINDOW时间内取款,否则无法取款
- 如果超过了取款时间,则需要调用
cooldown()
刷新时间,重新等待
这么设计的原因
- COOLDOWN_SECONDS:防止用户短时间内不断重复质押提现这个操作
- UNSTAKE_WINDOW:AAVE的业务逻辑,我们猜不到他为什么这么设计
参数
- to:接收存款的地址
- amount:提取多少存款,如果提取金额大于质押金额,则表示提取完所有,否则提取部分
1 | function redeem(address to, uint256 amount) external override { |
冷却
根据发送者/接收者时间戳计算冷却时间戳。
- fromCooldownTimestamp:发送者的冷却时间戳
- amountToReceive:要发送的 stkAAVE 代币数量
- toAddress:接收者地址
- toBalance:接收者余额
1 | function getNextCooldownTimestamp( |
view方法
stakersCooldowns()
:mapping,获取用户的冷却时间
治理
合约
- AaveProtoGovernance:处理大部分投票逻辑
- AssetVotingWeightProvider:将资产列入白名单并设置投票权重
- AavePropositionPower:控制协议治理的权限,例如注册新提案
- GovernanceParamsProvider:存储全局协议治理参数,例如注册新提案所需的提案权阈值
执行流程
- 创建:提案由具有足够提案权的用户创建。在早期阶段,这是 Genesis 团队。
- 投票:提案进入“投票”阶段,持续时间为
_votingBlocksDuration
。通过AaveProtoGovernance的submitVoteByVoter()
进行投票 - 验证:如果投票数额达到
_threshold
,该提案将进入“验证”阶段。验证持续时间为_validatingBlocksDuration。- 如果投票数没达到
_threshold
,则提案将保留在“投票”阶段,直至_threshold
达成。 - 在“验证”阶段,任何人都可以通过调用
challengeVoters()
来质疑(并取消无效)投票。如果当前投票代币余额低于投票时的投票代币余额,则投票被视为无效。 - 这个从“验证”到“投票”的过程最多可以发生
_maxMovesToVotingAllowed
次,之后如果没有通过,则被认为是“过期Expired”。
- 如果投票数没达到
- 结束:_validatingBlocksDuration之后,提案执行,状态更改为‘Executed’
- 如果投票数目超过
_threshold
,则调用execute()
执行提案 - .如果尚未达到投票数
_threshold
,则不会执行任何提案代码。
- 如果投票数目超过
如何投票
- 获取提案 ID 列表
- 使用
AaveProtoGovernance
合约发出的事件:ProposalCreated()
- 链上:使用
try...catch
模式迭代提案 ID,从索引 0 开始 - 通过 GraphQL:查询AAVE的subgraph以接收提案及其 ID 的列表
- 使用
- 查询提案数据
- 链上:使用
getProposalBasicData()
- 通过 GraphQL:subgraph
- 链上:使用
- 投票
- 有了提案ID,调用
submitVoteByVoter()
进行投票,如果调用了一次之后再次调用,则下一次调用覆盖上次调用,以最新的投票为准 - 使用
cancelVoteByVoter()
进行取消投票
- 有了提案ID,调用
- 获取提案状态
- 链上调用
getProposalBasicData()
- subgraph
- 链上调用
投票
输入提案ID,投票数目
- _proposalId:提案ID
- _vote:0表示弃权,1表示同意,2表示否决
- _asset:锁定的资产,用于投票,只有在合约中列入白名单的情况下才允许投票
1 | function submitVoteByVoter(uint256 _proposalId, uint256 _vote, IERC20 _asset) external { |
跳到实际逻辑:
1 | function internalSubmitVote(uint256 _proposalId, uint256 _vote, address _voter, IERC20 _asset) internal { |
判断提案是否进入有效模式
1 | function tryToMoveToValidating(uint256 _proposalId) public { |
取消投票
这个方法用于防止重复投票攻击,任何人发现了有人尝试重复投票攻击,可以进行质疑:当投票者当前的投票资产余额小于投票时资产的余额时,投票被视为无效,因为他可能拿着这笔钱干其他事情,比如转给另外一个人,然后继续投票;或者拿这个钱干其他事情。
举个例子:Alice 的余额为 100 LEND,对提案 1 进行投票,并在“验证”阶段结束之前将 50 LEND 发送到交易所。由于她当前的余额少于投票时的余额,她的投票将无效。
- 只有在提案有效的时候才可以质疑
1 | function challengeVoters(uint256 _proposalId, address[] calldata _voters) external { |
view方法
getLimitBlockOfProposal()
:查看某个提案到达哪个取款之后是无效的getLeadingChoice():查看某个提案哪个票多,赞成?弃权?反对?
getProposalBasicData()
:获取某个提案的详细信息getVoterData()
:获取某人对某个提案的投票情况getVotesData()
:获取给定提案 ID 的投票数据,即每个选项的累积投票数。例如,[1, 2, 3]
翻译为:1人齐全,2人同意,3人反对
aToken
- aToken是生息衍生代币,deposit的时候被铸造,提现的时候被销毁。
- aTokens的价值与相应存入资产的价值以1:1的比例挂钩,可以安全地存储、转移或交易。
- AAVE收取的手续费将会给到aToken的持有者,aToken会不断增值
取款
1 | function redeem(uint256 _amount) external { |
转账
用于将代币转移msg.sender
到指定的recipient
。
注意,如果要转账的aToken被用作抵押品,则调用会失败
1 | function transfer(address recipient, uint256 amount) public |
利息重定向
将aToken生成的利息给谁,_from
不可以是 _to
1 | function redirectInterestStreamInternal( |
允许_to执行利息重定向。此方法允许第三方代表存款人设置利息流重定向。
1 | function allowInterestRedirectionTo(address _to) |
_from授权给msg.sender了,它可以进一步重定向利润给其他人
1 | function redirectInterestStreamOf(address _from, address _to) external |
view方法
isTransferAllowed()
:用于查看如果调用transfer或者取款函数会不会失败,因为这两个操作会影响健康因子
信用委托
使用OpenLaw实现,AAVE并没有部署相关的合约,可以在区块链浏览器找到完整代码
部署Vault
部署一个_asset
可供借用的保管库,返回新部署的 AaveCollateralVault 的地址
1 | function deployVault(address _asset) external returns (address) { |
利率模型
在稳定利率或可变利率之间更改利率模型。
利率模型:1
是稳定利率,2
是可变利率。默认为2
.
1 | function setModel(uint _model) external onlyOwner { |
增加限制
增加对spender的限制
- vault:已部署的AaveCollateralVault地址。
- spender:借款人
- addedValue:
spender
能够借入的最大金额(以资产的基本单位表示,例如WBTC有6位小数,USDC有8位小数,ETH有18位小数)。
1 | function increaseLimit(address vault, address spender, uint addedValue) external { |
启动委托信贷
将 aToken 抵押品存入AaveCollateralVault
以启用委托信贷。
- vault:已部署的
AaveCollateralVault
地址。 - aToken:aToken地址
- amount:数额
1 | function deposit(AaveCollateralVault vault, address aToken, uint amount) external { |
借款
只有低于最大可借金额的spender
才可以调用该方法借入金额
1 | function borrow(AaveCollateralVault vault, address reserve, uint amount) external { |
还款
在调用之前,调用者必须approve授权给AaveCollateralVaultProxy
1 | function repay(AaveCollateralVault vault, address reserve, uint amount) external { |
取款
信用委托人可以在适当的时候提取 aToken 抵押品
1 | function withdraw(AaveCollateralVault vault, address aToken, uint amount) external { |
价格预言机
见文档