科普 | Compound的cToken及相关核心函数
去中心化金融(DeFi)协议 Compound 支持的所有资产,都是通过cToken智能合约来封装集成的,用户通过铸造代币(cToken)向协议提供资产,然后你只要持有cToken,就可以赚取到利息,而当你选择赎回后,每个cToken都可兑换成相应的基础资产。
可以说,cToken是与Compound协议交互的主要方式。每个cToken合约都创造了自己的货币市场,而用户铸币、赎回、借款、偿还借款、清算借款或转让cToken时,他们将使用到cToken合约。
目前,Compound协议有两种类型的cToken:CErc20和CEther,其中CErc20封装的是ERC-20资产,而CEther则封装的是以太币。
(当前Compound协议支持的7种cToken及对应合约地址)
而在这篇文章中,我们将了解Compound协议的核心函数:- Mint(铸币);
- Redeem(赎回);
- Redeem Underlying(赎回基础资产);
- Borrow(借款);
- Repay Borrow(偿还借款);
- Repay Borrow Behalf;
- Liquidate Borrow(清算借款);
- 故障信息
- Exchange Rate(兑换汇率);
- Get Cash(获取Cash);
- Total Borrow(借款总额);
- Borrow Balance(借款余额);
- Borrow Rate(借款利率);
- Total Supply(总供给量);
- Supply Balance(供应余额);
- Supply Balance Underlying(基础资产供应余额);
- Supply Rate(供应率);
- Total Reserves(总储备金);
- Reserve Factor(储备金率);
1、铸币(Mint)
铸币(Mint)函数负责将资产转移到货币市场,后者根据资产的当前供应率计算利息。铸造的代币数量,根据用户所提供的基础资产数量除以当前汇率得出。
铸币(Mint)相当于Compound 协议的供应来源。
CErc20
function mint(uint mintAmount) returns (uint)
-
msg.sender
: 提供资产并拥有铸造cToken的帐户; -
mintAmount
: 以基础资产为单位进行铸币的金额; -
RETURN
:返回0表示成功,其它则是错误代码;
CEther
function mint() payable
-
msg.value
(payable):以太币待提供金额,单位为wei; -
msg.sender
:提供以太币并拥有铸造cToken的账户; -
RETURN
: 没有返回,出错时还原;
Erc20 underlying = Erc20(0xToken...); // get a handle for the underlying asset contract
CErc20 cToken = CErc20(0x3FDA...); // get a handle for the corresponding cToken contract
underlying.approve(address(cToken), 100); // approve the transfer
assert(cToken.mint(100) == 0); // mint the cTokens and assert there is no error
Web3 1.0
const cToken = CEther.at(0x3FDB...);
await cToken.methods.mint().send({from: myAccount, value: 50});
2、赎回
赎回函数负责将基础资产从货币市场转移至用户,以交换之前铸造的cToken。基础资产赎回金额根据cToken的数量乘以当前汇率得出。赎回金额必须小于用户帐户的流动性以及市场的可用流动性。
赎回相当于Compound 协议的退出功能。
CErc20 / CEther
function redeem(uint redeemTokens) returns (uint)
-
msg.sender
: 赎回资金转移账户; -
redeemTokens
: 要赎回的cToken数量; -
RETURN
: 返回0表示成功,其它则是错误代码;
CEther cToken = CEther(0x3FDB...);
require(cToken.redeem(7) == 0, "something went wrong");
Web3 1.0
const cToken = CErc20.at(0x3FDA...);
cToken.methods.redeem(1).send({from: ...});
3、赎回基础资产
赎回基础资产函数,负责将基础资产从货币市场转移给用户,以换取之前铸造的CToken。cToken赎回的数量是基础资产的金额除以当前汇率。赎回金额必须小于用户帐户的流动性和市场的可用流动性。
CErc20 / CEther
function redeemUnderlying(uint redeemAmount) returns (uint)
-
msg.sender
: 赎回资金转移账户; -
redeemAmount
: 要赎回的基础资产金额; -
RETURN
: 返回0表示成功,其它则是错误代码;
CEther cToken = CEther(0x3FDB...);
require(cToken.redeemUnderlying(50) == 0, "something went wrong");
Web3 1.0
const cToken = CErc20.at(0x3FDA...);
cToken.methods.redeemUnderlying(10).send({from: ...});
4、借款
借款函数负责将资产从货币市场转移给用户,并创建一个借款余额,该余额根据资产的借款利率累积利息。
借款金额必须少于用户的借款能力以及市场的可用流动性。用户必须维持一个抵押品要求以避免清算。
请注意,借款人将收到一笔基础资产交易。对CEther来说,这将是以太币,因此借款人必须是可偿付的。
CErc20 / CEther
function borrow(uint borrowAmount) returns (uint)
-
msg.sender
: 借款资金转移账户; -
borrowAmount
: 待借基础资产金额; -
RETURN
:返回0表示成功,其它则是错误代码;
CErc20 cToken = CErc20(0x3FDA...);
require(cToken.borrow(100) == 0, "got collateral?");
Web3 1.0
const cToken = CEther.at(0x3FDB...);
await cToken.methods.borrow(50).send({from: 0xMyAccount});
5、偿还借款
偿还函数负责将资产转移到货币市场,以减少用户的借款余额。
CErc20
function repayBorrow(uint repayAmount) returns (uint)
-
msg.sender
: 借用资产并应偿还借款的账户; -
repayAmount
: 待偿还标的借款资产的金额,值-1 (即2^256 -1)可用于偿还全部金额; -
RETURN
: 返回0表示成功,其它则是错误代码;
CEther
function repayBorrow() payable
-
msg.value
(payable):偿还的以太币数量,单位为wei; -
msg.sender
:借用资产,并应偿还借款的账户; -
RETURN
: 没有return,出错时还原;
CEther cToken = CEther(0x3FDB...);
require(cToken.repayBorrow.value(100)() == 0, "transfer approved?");
Web3 1.0
const cToken = CErc20.at(0x3FDA...);
cToken.methods.repayBorrow(10000).send({from: ...});
6、偿还Borrow Behalf
该偿还函数负责将资产转移到货币市场,以减少用户的借款余额。
CErc20
function repayBorrowBehalf(address borrower, uint repayAmount) returns (uint)
-
msg.sender
: 应偿还借款的账户; -
borrower
: 借用待偿还资产的账户; -
repayAmount
: 待偿还标的借款资产的金额,值-1 (即2^256 -1)可用于偿还全部金额; -
RETURN
: 返回0表示成功,其它则是错误代码;
CEther
function repayBorrowBehalf(address borrower) payable
-
msg.value
(payable):偿还的以太币数量,单位为wei; -
msg.sender
:应偿还借款的账户; -
borrower
: 借用偿还资产的账户; -
RETURN
: 没有return,出错时还原;
CEther cToken = CEther(0x3FDB...);
require(cToken.repayBorrowBehalf.value(100)(0xBorrower) == 0, "transfer approved?");
Web3 1.0
const cToken = CErc20.at(0x3FDA...);
await cToken.methods.repayBorrowBehalf(0xBorrower, 10000).send({from: 0xPayer});
7、清算借款
账户流动性为负的用户将被协议的其他用户清算,以使其账户流动性恢复为正(即高于抵押品要求)。当清算发生时,清算人可代表借款人偿还部分或全部未偿还借款,作为回报,清算人可获得借款人持有的抵押物,这被定义为清算激励。
清算人可将“清算账户”(即低于抵押品要求的账户)的任何未偿还借款的某一固定百分比结清。与v1不同的是,清算人必须与每个cToken合约进行互动,在这些合约中,他们希望偿还借款并扣押另一资产作为抵押品。当抵押品被扣押时,清算人被转移至cToken,他们可赎回这些抵押品,就像他们自己提供了资产一样。用户在将资金转入合约之前,必须先批准每个cToken合约,然后才能进行清算。
CErc20
function liquidateBorrow(address borrower, uint amount, address collateral) returns (uint)
-
msg.sender
: 通过偿还债务和扣押抵押品清算借款人的账户; -
borrower
: 应清算的账户流动性为负的账户; -
repayAmount
: 以标的借款资产为单位,待偿还的借款金额; -
cTokenCollateral
: 清算人应扣押的借款人持有的cToken地址; -
RETURN
: 返回0表示成功,其它则是错误代码;
CEther
function liquidateBorrow(address borrower, address cTokenCollateral) payable
-
msg.value
(payable):待偿还且被转换为抵押品的以太币,单位为wei; -
msg.sender
: 通过偿还债务和扣押抵押品,用于清算借款人的账户; -
borrower
: 应清算的账户流动性为负的账户; -
cTokenCollateral
:清算人应扣押的,借款人目前作为抵押品持有的cToken地址; -
RETURN
: 没有return,出错时还原;
CEther cToken = CEther(0x3FDB...);
CErc20 cTokenCollateral = CErc20(0x3FDA...);
require(cToken.liquidateBorrow.value(100)(0xBorrower, cTokenCollateral) == 0, "borrower underwater??");
Web3 1.0
const cToken = CErc20.at(0x3FDA...);
const cTokenCollateral = CEther.at(0x3FDB...);
await cToken.methods.liquidateBorrow(0xBorrower, 33, cTokenCollateral).send({from: 0xLiquidator});
8、故障信息
9、兑换汇率
随着市场利息的增加,每个cToken都可转换成(不断增长的)基础资产。cToken与基础资产之间的汇率等于:
exchangeRate = (getCash() + totalBorrows() - totalReserves()) / totalSupply()
CErc20 / CEther
function exchangeRateCurrent() returns (uint)
RETURN : 当前汇率为无符号整数,按1e18缩放。
Solidity
CErc20 cToken = CToken(0x3FDA...);
uint exchangeRateMantissa = cToken.exchangeRateCurrent();
Web3 1.0
const cToken = CEther.at(0x3FDB...);
const exchangeRate = (await cToken.methods.exchangeRateCurrent().call()) / 1e18;
提示:注意使用call和send从链外调用该函数,则不会产生gas成本。
10、Get Cash
Cash是该cToken合约所拥有的基础资产余额。人们可查询这个市场目前可用的cash总额。
CErc20 / CEther
function getCash() returns (uint)
RETURN : 合约所拥有基础资产的数量。
Solidity
CErc20 cToken = CToken(0x3FDA...);
uint cash = cToken.getCash();
Web3 1.0
const cToken = CEther.at(0x3FDB...);
const cash = (await cToken.methods.getCash().call());
11、借款总额
借款总额是指市场目前借出的基础资产总金额,加上相应的利息金额。
CErc20 / CEther
function totalBorrowsCurrent() returns (uint)
RETURN : 加上利息的基础资产借款总额;
Solidity
CErc20 cToken = CToken(0x3FDA...);
uint borrows = cToken.totalBorrowsCurrent();
Web3 1.0
const cToken = CEther.at(0x3FDB...);
const borrows = (await cToken.methods.totalBorrowsCurrent().call());
12、借款余额
从协议中借款的用户,将根据当前借款利率支付累计利息。利息是每个区块累积计算的,我们可使用此函数获得用户(带利息)借款余额的当前值;
CErc20 / CEther
function borrowBalanceCurrent(address account) returns (uint)
-
account
: 借用资产的账户; -
RETURN
: 用户当前的标的资产借款余额(含利息);
CErc20 cToken = CToken(0x3FDA...);
uint borrows = cToken.borrowBalanceCurrent(msg.caller);
Web3 1.0
const cToken = CEther.at(0x3FDB...);
const borrows = await cToken.methods.borrowBalanceCurrent(account).call();
13、借款利率
在任何时候,人们都可以查询合约,以获得每个区块的当前借款利率。
CErc20 / CEther
function borrowRatePerBlock() returns (uint)
RETURN :当前借款利率为无符号整数,按1e18缩放。
Solidity
CErc20 cToken = CToken(0x3FDA...);
uint borrowRateMantissa = cToken.borrowRatePerBlock();
Web3 1.0
const cToken = CEther.at(0x3FDB...);
const borrowRate = (await cToken.methods.borrowRatePerBlock().call()) / 1e18;
14、总供给量
所谓总供给量,是指当前在这个cToken市场上流通的代币数量。它是cToken合约EIP-20接口的一部分。
CErc20 / CEther
function totalSupply() returns (uint)
RETURN : 市场流通的代币总数。
Solidity
CErc20 cToken = CToken(0x3FDA...);
uint tokens = cToken.totalSupply();
Web3 1.0
const cToken = CEther.at(0x3FDB...);
const tokens = (await cToken.methods.totalSupply().call());
15、供应余额
向协议提供资产的用户,将收到利息代币作为报酬。在任何给定时间,我们可通过汇率查询带息代币的价值。我们还可以查询特定用户拥有的代币数量,这是cToken合约EIP-20接口的一部分。
CErc20 / CEther
function balanceOf(address account) returns (uint)
-
account
: 获取代币余额的账户; -
RETURN
: 帐户当前拥有的代币数;
CErc20 cToken = CToken(0x3FDA...);
uint tokens = cToken.balanceOf(msg.caller);
Web3 1.0
const cToken = CEther.at(0x3FDB...);
const tokens = await cToken.methods.balanceOf(account).call();
16、基础资产供应余额
CToken还有一种方便的方法,可根据基础资产金额(即供应余额乘以汇率)来确定供应余额。
CErc20 / CEther
function balanceOfUnderlying(address account) returns (uint)
-
account
: 要获取基础资产余额的帐户; -
RETURN
: 帐户当前拥有的基础资产金额;
CErc20 cToken = CToken(0x3FDA...);
uint tokens = cToken.balanceOfUnderlying(msg.caller);
Web3 1.0
const cToken = CEther.at(0x3FDB...);
const tokens = await cToken.methods.balanceOfUnderlying(account).call();
17、供应率
在任何时候,人们都可以查询合约,以获得每个区块的当前供应率。供应率由借款利率、储备金率以及借款总额得出。
CErc20 / CEther
function supplyRatePerBlock() returns (uint)
RETURN
: 当前供应率为无符号整数,按1e18缩放。
Solidity
CErc20 cToken = CToken(0x3FDA...);
uint supplyRateMantissa = cToken.supplyRatePerBlock();
Web3 1.0
const cToken = CEther.at(0x3FDB...);
const supplyRate = (await cToken.methods.supplyRatePerBlock().call()) / 1e18;
18、总储备金
储备金是协议本身的权益,以便为其运作提供资金。储备金也构成了cash的一部分,可用于贷给市场上的借款人。借款人的一小部分利息计入协议,而它是由准备金率决定的。
CErc20 / CEther
function totalReserves() returns (uint)
RETURN
: 协议持有的储备金总额;
Solidity
CErc20 cToken = CToken(0x3FDA...);
uint reserves = cToken.totalReserves();
Web3 1.0
const cToken = CEther.at(0x3FDB...);
const reserves = (await cToken.methods.totalReserves().call());
19、储备金率
储备金率定义了应计入储备金的一小部分利息。
CErc20 / CEther
function reserveFactorMantissa() returns (uint)
RETURN
: 当前储备金率为无符号整数,按1e18缩放。
Solidity
CErc20 cToken = CToken(0x3FDA...);
uint reserveFactorMantissa = cToken.reserveFactorMantissa();
Web3 1.0
const cToken = CEther.at(0x3FDB...);
const reserveFactor = (await cToken.methods.reserveFactorMantissa().call()) / 1e18;