Lending Protocol
BendDao 借贷协议包括以下合约
LendPool
: 借贷协议的主合约,包括资金的存取、借款、还款、拍卖、赎回、清算LendPoolLoan
: NFT 借贷管理器。当使用 NFT 作为抵押物时,会生成唯一的贷款凭证,并维持 NFT 和贷款之间的关系LendPoolAddressesProvider
: 借贷协议的链上地址管理器BToken
: 带息存款的一种 ERC20 代币,并添加了一些特定的方法DebtToken
: ERC20 代币化的一种债务 token, 且不可转让boundNFTs
: 本票(Promissory note), 是 Bend 协议中使用的代币化抵押品。且不可转让LendPoolConfigurator
: 为 LendPool 合约提供配置功能InterestRate
: 保存计算和更新特定储备利率所需的信息NFTOracle
: 提供 Bend 协议所需的储备和 NFT 资产价格数据
LendPool
借贷协议的主合约,与 BendDAO 的大多数交互都通过该合约进行。合约变量定义在LendPoolStorage.sol
contract LendPoolStorage {
using ReserveLogic for DataTypes.ReserveData;
using NftLogic for DataTypes.NftData;
ILendPoolAddressesProvider internal _addressesProvider;
// 储备金配置
mapping(address => DataTypes.ReserveData) internal _reserves;
// 可借贷nft的配置
mapping(address => DataTypes.NftData) internal _nfts;
// 储备金对应资产的合约地址
mapping(uint256 => address) internal _reservesList;
// 储备金的数量
uint256 internal _reservesCount;
// 可借贷 nft 列表
mapping(uint256 => address) internal _nftsList;
// 可借贷 nft数量
uint256 internal _nftsCount;
bool internal _paused;
uint256 internal _maxNumberOfReserves;
uint256 internal _maxNumberOfNfts;
// !!! Never add new variable at here, because this contract is inherited by LendPool !!!
// !!! Add new variable at LendPool directly !!!
}
存款 deposit
向 BendDAO 存入一定数量的资金,并获得对应的的 BToken, 例如存入 ETH, 可获得一定的 bendWETH
function deposit(
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode
) external override nonReentrant whenNotPaused {
SupplyLogic.executeDeposit(
_reserves,
DataTypes.ExecuteDepositParams({
initiator: _msgSender(),
asset: asset,
amount: amount,
onBehalfOf: onBehalfOf,
referralCode: referralCode
})
);
}
具体逻辑在 SupplyLogic.executeDeposit
方法中
function executeDeposit(
mapping(address => DataTypes.ReserveData) storage reservesData,
DataTypes.ExecuteDepositParams memory params
) external {}
方法内部流程如下:
- 验证 onBehalfOf 不为空地址,onBehalfOf 为接收 BToken 的地址
- 验证对于存入的资金是否有对应的 BToken
- 验证存入的资金参数
- 更新储备金状态
- 更新储备金利率
- 转移资产到 BToken 合约中
- 铸造 BToken 发送给 onBehalfOf
提现 withdraw
取出存入的资金
function withdraw(
address asset,
uint256 amount,
address to
) external override nonReentrant whenNotPaused returns (uint256) {
return
SupplyLogic.executeWithdraw(
_reserves,
DataTypes.ExecuteWithdrawParams({initiator: _msgSender(), asset: asset, amount: amount, to: to})
);
}
具体逻辑在 SupplyLogic.executeWithdraw
方法中
function executeWithdraw(
mapping(address => DataTypes.ReserveData) storage reservesData,
DataTypes.ExecuteWithdrawParams memory params
) external returns (uint256) {}
方法内部流程如下:
- 验证接收地址不为空地址
- 验证对于存入的资金是否有对应的 BToken
- 获取调用者的 BToken 余额
- 验证提现参数
- 更新储备金状态
- 更新储备金利率
- 销毁调用者的 BToken
- 返回提现数量
借款 borrow
抵押资产,进行借贷。
function borrow(
address asset,
uint256 amount,
address nftAsset,
uint256 nftTokenId,
address onBehalfOf,
uint16 referralCode
) external override nonReentrant whenNotPaused {
BorrowLogic.executeBorrow(
_addressesProvider,
_reserves,
_nfts,
DataTypes.ExecuteBorrowParams({
initiator: _msgSender(),
asset: asset,
amount: amount,
nftAsset: nftAsset,
nftTokenId: nftTokenId,
onBehalfOf: onBehalfOf,
referralCode: referralCode
})
);
}
具体逻辑在 BorrowLogic.executeBorrow
方法中
function executeBorrow(
ILendPoolAddressesProvider addressesProvider,
mapping(address => DataTypes.ReserveData) storage reservesData,
mapping(address => DataTypes.NftData) storage nftsData,
DataTypes.ExecuteBorrowParams memory params
) external {
_borrow(addressesProvider, reservesData, nftsData, params);
}
function _borrow(
ILendPoolAddressesProvider addressesProvider,
mapping(address => DataTypes.ReserveData) storage reservesData,
mapping(address => DataTypes.NftData) storage nftsData,
DataTypes.ExecuteBorrowParams memory params
) internal {}
还款 repay
借贷后可以选择主动还款。
function repay(
address nftAsset,
uint256 nftTokenId,
uint256 amount
) external override nonReentrant whenNotPaused returns (uint256, bool) {
return
BorrowLogic.executeRepay(
_addressesProvider,
_reserves,
_nfts,
DataTypes.ExecuteRepayParams({
initiator: _msgSender(),
nftAsset: nftAsset,
nftTokenId: nftTokenId,
amount: amount
})
);
}
具体逻辑在 BorrowLogic.executeRepay
方法中
function executeRepay(
ILendPoolAddressesProvider addressesProvider,
mapping(address => DataTypes.ReserveData) storage reservesData,
mapping(address => DataTypes.NftData) storage nftsData,
DataTypes.ExecuteRepayParams memory params
) external returns (uint256, bool) {
return _repay(addressesProvider, reservesData, nftsData, params);
}
function _repay(
ILendPoolAddressesProvider addressesProvider,
mapping(address => DataTypes.ReserveData) storage reservesData,
mapping(address => DataTypes.NftData) storage nftsData,
DataTypes.ExecuteRepayParams memory params
) internal returns (uint256, bool) {}
拍卖 auction
当抵押的资产被清算时会执行拍卖流程
function auction(
address nftAsset,
uint256 nftTokenId,
uint256 bidPrice,
address onBehalfOf
) external override nonReentrant whenNotPaused {
LiquidateLogic.executeAuction(
_addressesProvider,
_reserves,
_nfts,
_buildLendPoolVars(),
DataTypes.ExecuteAuctionParams({
initiator: _msgSender(),
nftAsset: nftAsset,
nftTokenId: nftTokenId,
bidPrice: bidPrice,
onBehalfOf: onBehalfOf
})
);
}
赎回 redeem
借款人抵押的资产被清算并处于拍卖状态时,在赎回期内选择赎回资产
function redeem(
address nftAsset,
uint256 nftTokenId,
uint256 amount,
uint256 bidFine
) external override nonReentrant whenNotPaused returns (uint256) {
return
LiquidateLogic.executeRedeem(
_addressesProvider,
_reserves,
_nfts,
_buildLendPoolVars(),
DataTypes.ExecuteRedeemParams({
initiator: _msgSender(),
nftAsset: nftAsset,
nftTokenId: nftTokenId,
amount: amount,
bidFine: bidFine
})
);
}
清算 liquidate
购买被清算用户的抵押资产。
function liquidate(
address nftAsset,
uint256 nftTokenId,
uint256 amount
) external override nonReentrant whenNotPaused returns (uint256) {
return
LiquidateLogic.executeLiquidate(
_addressesProvider,
_reserves,
_nfts,
_buildLendPoolVars(),
DataTypes.ExecuteLiquidateParams({
initiator: _msgSender(),
nftAsset: nftAsset,
nftTokenId: nftTokenId,
amount: amount
})
);
}
LendPoolLoan
NFT 借贷管理器,在借贷中使用 NFT 作为抵押物时,会生成唯一的借贷 ID
全局变量
// 借贷ID的索引器, 每次产生借贷时,值自动+1
CountersUpgradeable.Counter private _loanIdTracker;
// 存储借贷信息,key 是借贷 ID
mapping(uint256 => DataTypes.LoanData) private _loans;
// nftAsset + nftTokenId => loanId
// nft 合约地址映射 tokenId 对应的 借贷ID
mapping(address => mapping(uint256 => uint256)) private _nftToLoanIds;
// nft 借贷的数量
mapping(address => uint256) private _nftTotalCollateral;
// 用户地址 => nft 合约地址 => 抵押的nft数量
mapping(address => mapping(address => uint256)) private _userNftCollateral;
创建借贷
function createLoan(
address initiator,
address onBehalfOf,
address nftAsset,
uint256 nftTokenId,
address bNftAddress,
address reserveAsset,
uint256 amount,
uint256 borrowIndex
) external override onlyLendPool returns (uint256) {}
方法内部流程如下:
- 生成借贷 ID
- 根据参数保存数据到 _nftToLoanIds
- 将抵押的 nft 转移到当前合约
- 铸造 BNFT 发送给 onBehalfOf
- 更新变量 _loans、_nftTotalCollateral、_userNftCollateral
更新借贷
根据传入的参数更新借贷数据
function updateLoan(
address initiator,
uint256 loanId,
uint256 amountAdded,
uint256 amountTaken,
uint256 borrowIndex
) external override onlyLendPool {}
偿还借贷
function repayLoan(
address initiator,
uint256 loanId,
address bNftAddress,
uint256 amount,
uint256 borrowIndex
) external override onlyLendPool {}
方法内部流程如下:
- 检查借贷状态是 active
- 销毁借贷 ID: 更新 _nftToLoanIds 中的借贷 ID 为 0,_userNftCollateral 和 _nftTotalCollateral 中对应的数量减去 1
- 销毁 BNFT
- 转移质押的 NFT 给借款人
拍卖借贷
function auctionLoan(
address initiator,
uint256 loanId,
address onBehalfOf,
uint256 bidPrice,
uint256 borrowAmount,
uint256 borrowIndex
) external override onlyLendPool {}
方法内部流程如下:
- 根据借贷 ID 获取借贷信息
- 根据借贷的 bidStartTimestamp 是否为 0
- 为 0 表示此前未有过拍卖,则设置借贷状态为可拍卖状态,bidStartTimestamp 为当前区块时间
- 不为 0 表示此前有过拍卖, 则要求借贷状态为拍卖状态
- 更新借贷其他信息
赎回借贷
function redeemLoan(
address initiator,
uint256 loanId,
uint256 amountTaken,
uint256 borrowIndex
) external override onlyLendPool {}
方法内部流程如下:
- 根据借贷 ID 获取借贷信息
- 检查借贷的状态必须是拍卖状态
- 检查 amountTaken 值
- 重置借贷状态为 active
清算借贷
function liquidateLoan(
address initiator,
uint256 loanId,
address bNftAddress,
uint256 borrowAmount,
uint256 borrowIndex
) external override onlyLendPool {}
方法内部流程如下:
- 只有拍卖状态的借贷才可以清算
- 销毁借贷 ID: 更新 _nftToLoanIds 中的借贷 ID 为 0,_userNftCollateral 和 _nftTotalCollateral 中对应的数量减去 1
- 销毁 BNFT
- 将质押的 NFT 转移给用户