Compound 协议所支持的每项资产都通过 cToken 合约进行整合,cToken 合约是提供给协议的余额的 EIP-20 合格表示。
请注意,在翻译时遇到一个单词
Underlying
相关的词组有Underlying Balance
和Underlying assets
。个人觉得翻译成基础余额、底层余额、底层资产、基础资产等听着不对味,所以我根据理解,将其翻译为标的余额、标的资产。有来喷的吗?点击右侧加号可以留言!cToken 就是对 Token 的封装,这个 Token 就可以视为交易的标的物。
Compound 协议所支持的每项资产都通过 cToken 合约进行整合,cToken 合约是提供给协议的余额的 EIP-20 合格表示。通过铸造 cToken,用户(1)通过 cToken 的汇率赚取利息,该汇率相对于标的资产的价值增加,以及(2)获得试用 cToken 作为抵押品的能力。
cToken 是与 Compound 协议交互的主要手段;当用户在使用 cToken 合约时,会使用 cToken 合约来进行铸币(mint)、赎回(redeem)、借款(borrow)、偿还借款(repay)、清算借款(liquidate)或者划转 cToken (transfer)。
目前有两种类型的 cToken : CErc20 和 CEther。尽管两者都暴露了 EIP-20 接口,但 CErc20 封装了 ERC-20 标的资产,而 CEther 只是封装了 Ether 本身。因此,根据类型的不同,涉及将资产传输到协议中的核心功能的接口也略有不同,每种类型的接口如下所示。
mint 方法将一个资产转移到协议中,协议开始根据该资产的当前[供应率]()开始累积利息。用户收到的 cToken 数量等于标的代币数量除以当前[汇率]()。
CErc20
1 function mint(uint mintAmount) returns (uint)
供应资产前,用户必须先 批准(approve), cToken 才能访问其代币余额。
CEther
1 function mint() payable
Solidity
1 Erc20 underlying = Erc20(0xToken...); // 获取标的资产合约句柄
2 CErc20 cToken = CErc20(0x3FDA...); // 获取相应 cToken 合约的句柄
3 underlying.approve(address(cToken), 100); // 批准划转
4 assert(cToken.mint(100) == 0); // 铸 cToken 币,并断言是否有错误
Web3 1.0
1 const cToken = CEther.at(0x3FDB...);
2 await cToken.methods.mint().send({from: myAccount, value: 50});
redeem 方法将指定数量的 cToken 转换为标的资产,并将其返还给用户。收到的标的数量等于赎回的 cToken 数量乘以当前[汇率]()。赎回额必须小于用户的账户流动性和市场可用的流动性。
CErc20 / CEther
function redeem(uint redeemTokens) returns (uint)
Solidity
1 CEther cToken = CEther(0x3FDB...);
2 require(cToken.redeem(7) == 0, "something went wrong");
redeem underlying 方法将 cToken兑换成指定数量的标的资产,并返回给用户。赎回的 cToken的数量等于收到的标的数量除以当前[汇率]()。赎回额必须小于用户的账户流动性和市场可用的流动性。
CErc20 / CEther
1 function redeemUnderlying(uint redeemAmount) returns (uint)
Solidity
1 CEther cToken = CEther(0x3FDB...);
2 require(cToken.redeemUnderlying(50) == 0, "something went wrong");
Web3 1.0
1 const cToken = CErc20.at(0x3FDA...);
2 cToken.methods.redeemUnderlying(10).send({from: ...});
borrow 方法将协议中的资产转移给用户,并创建一个借款余额,根据该资产的[借款利率]()开始累积利息。借款额必须小于用户的账户流动性和市场可用的流动性。
要想借入以太,借款人必须是 'payable' (Solidity)。
CErc20 / CEther
1 function borrow(uint borrowAmount) returns (uint)
Solidity
1 CErc20 cToken = CErc20(0x3FDA...);
2 require(cToken.borrow(100) == 0, "got collateral?");
Web3 1.0
1 const cToken = CEther.at(0x3FDB...);
2 await cToken.methods.borrow(50).send({from: 0xMyAccount});
repay 方法将资产转移到协议中,并减少用户的借款余额。
CErc20
1 function repayBorrow(uint repayAmount) returns (uint)
偿还资产前,用户必须先 批准(approve), cToken 才能访问其代币余额。
CEther
1 function repayBorrow() payable
Solidity
1 CEther cToken = CEther(0x3FDB...);
2 require(cToken.repayBorrow.value(100)() == 0, "transfer approved?");
Web3 1.0
1 const cToken = CErc20.at(0x3FDA...);
2 cToken.methods.repayBorrow(10000).send({from: ...});
repay 方法将资产转移到协议中,并减少目标用户的借款余额。
CErc20
1 function repayBorrowBehalf(address borrower, uint repayAmount) returns (uint)
偿还资产前,用户必须先 批准(approve), cToken 才能访问其代币余额。
CEther
1 function repayBorrowBehalf(address borrower) payable
Solidity
1 CEther cToken = CEther(0x3FDB...);
2 require(cToken.repayBorrowBehalf.value(100)(0xBorrower) == 0, "transfer approved?");
Web3 1.0
1 const cToken = CErc20.at(0x3FDA...);
2 await cToken.methods.repayBorrowBehalf(0xBorrower, 10000).send({from: 0xPayer});
账户流动性为负值的用户,由协议的其他用户进行[清算](),使其账户流动性恢复到正值(即高于抵押品要求)。当发生清算时,清算人(liquidator)可以代表借款人偿还部分或全部未偿还的借款,作为回报,可以获得借款人持有的抵押品的折价;这种折价被定义为清算奖励。
清算人可将水下账户的任何一个未偿还借款以一定的固定比例(即结算系数)进行结算。与 V1 中不同的是,清算者必须与每一个 cToken 合约中偿还借款并扣押另一项资产作为抵押品的 cToken 进行交互。当抵押品被扣押时,清算人会被转移 cToken,他们可以赎回这些 cToken,就像他们自己提供资产一样。用户必须在调用清算前批准每个 cToken 合约(即在他们要偿还的借款资产上),因为他们正在将资金转移到合约中。
CErc20
1 function liquidateBorrow(address borrower, uint amount, address collateral) returns (uint)
偿还资产前,用户必须先 批准(approve), cToken 才能访问其代币余额。
CEther
1 function liquidateBorrow(address borrower, address cTokenCollateral) payable
Solidity
1 CEther cToken = CEther(0x3FDB...);
2 CErc20 cTokenCollateral = CErc20(0x3FDA...);
3 require(cToken.liquidateBorrow.value(100)(0xBorrower, cTokenCollateral) == 0, "borrower underwater??");
Web3 1.0
1 const cToken = CErc20.at(0x3FDA...);
2 const cTokenCollateral = CEther.at(0x3FDB...);
3 await cToken.methods.liquidateBorrow(0xBorrower, 33, cTokenCollateral).send({from: 0xLiquidator});
Event | Description |
---|---|
Mint(address minter, uint mintAmount, uint mintTokens) | 铸币成功后发出 |
Redeem(address redeemer, uint redeemAmount, uint redeemTokens) | [赎回]()成功后发出 |
Borrow(address borrower, uint borrowAmount, uint accountBorrows, uint totalBorrows) | 借款成功后发出 |
RepayBorrow(address payer, address borrower, uint repayAmount, uint accountBorrows, uint totalBorrows) | [偿还借款]()成功后发出 |
LiquidateBorrow(address liquidator, address borrower, uint repayAmount, address cTokenCollateral, uint seizeTokens) | [借款清算]()成功后发出 |
Code | Name | Description |
---|---|---|
0 | NO_ERROR | 不是失败。 |
1 | UNAUTHORIZED | 发件人无权实施这一操作。 |
2 | BAD_INPUT | 调用者提供了一个无效参数。 |
3 | COMPTROLLER_REJECTION | 该操作将违反审计官政策。 |
4 | COMPTROLLER_CALCULATION_ERROR | 审计内部计算失败 。 |
5 | INTEREST_RATE_MODEL_ERROR | 利率模型返回了一个无效值。 |
6 | INVALID_ACCOUNT_PAIR | 指定的账户组合是无效的。 |
7 | INVALID_CLOSE_AMOUNT_REQUESTED | 清算金额无效。 |
8 | INVALID_COLLATERAL_FACTOR | 抵押因子无效。 |
9 | MATH_ERROR | 出现了数学计算错误。 |
10 | MARKET_NOT_FRESH | 利息没有正确产生。 |
11 | MARKET_NOT_LISTED | 目前,市场没有由审计员列出。 |
12 | TOKEN_INSUFFICIENT_ALLOWANCE | ERC-20 合约必须允许(allow)货币市场合约调用transferForm 。当前 allow 额为0或小于请求的供给、偿还借款或清算金额。 |
13 | TOKEN_INSUFFICIENT_BALANCE | 调用者在 ERC-20 合约中没有足够的余额来完成所需的操作。 |
14 | TOKEN_INSUFFICIENT_CASH | 市场上没有足够的现金金额来完成交易。您可以稍后再尝试此项交易。 |
15 | TOKEN_TRANSFER_IN_FAILED | 在 ERC-20 代币转入市场时失败。 |
16 | TOKEN_TRANSFER_OUT_FAILED | 在 ERC-20 代币转出市场时失败。 |
Code | Name |
---|---|
0 | ACCEPT_ADMIN_PENDING_ADMIN_CHECK |
1 | ACCRUE_INTEREST_ACCUMULATED_INTEREST_CALCULATION_FAILED |
2 | ACCRUE_INTEREST_BORROW_RATE_CALCULATION_FAILED |
3 | ACCRUE_INTEREST_NEW_BORROW_INDEX_CALCULATION_FAILED |
4 | ACCRUE_INTEREST_NEW_TOTAL_BORROWS_CALCULATION_FAILED |
5 | ACCRUE_INTEREST_NEW_TOTAL_RESERVES_CALCULATION_FAILED |
6 | ACCRUE_INTEREST_SIMPLE_INTEREST_FACTOR_CALCULATION_FAILED |
7 | BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED |
8 | BORROW_ACCRUE_INTEREST_FAILED |
9 | BORROW_CASH_NOT_AVAILABLE |
10 | BORROW_FRESHNESS_CHECK |
11 | BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED |
12 | BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED |
13 | BORROW_MARKET_NOT_LISTED |
14 | BORROW_COMPTROLLER_REJECTION |
15 | LIQUIDATE_ACCRUE_BORROW_INTEREST_FAILED |
16 | LIQUIDATE_ACCRUE_COLLATERAL_INTEREST_FAILED |
17 | LIQUIDATE_COLLATERAL_FRESHNESS_CHECK |
18 | LIQUIDATE_COMPTROLLER_REJECTION |
19 | LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED |
20 | LIQUIDATE_CLOSE_AMOUNT_IS_UINT_MAX |
21 | LIQUIDATE_CLOSE_AMOUNT_IS_ZERO |
22 | LIQUIDATE_FRESHNESS_CHECK |
23 | LIQUIDATE_LIQUIDATOR_IS_BORROWER |
24 | LIQUIDATE_REPAY_BORROW_FRESH_FAILED |
25 | LIQUIDATE_SEIZE_BALANCE_INCREMENT_FAILED |
26 | LIQUIDATE_SEIZE_BALANCE_DECREMENT_FAILED |
27 | LIQUIDATE_SEIZE_COMPTROLLER_REJECTION |
28 | LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER |
29 | LIQUIDATE_SEIZE_TOO_MUCH |
30 | MINT_ACCRUE_INTEREST_FAILED |
31 | MINT_COMPTROLLER_REJECTION |
32 | MINT_EXCHANGE_CALCULATION_FAILED |
33 | MINT_EXCHANGE_RATE_READ_FAILED |
34 | MINT_FRESHNESS_CHECK |
35 | MINT_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED |
36 | MINT_NEW_TOTAL_SUPPLY_CALCULATION_FAILED |
37 | MINT_TRANSFER_IN_FAILED |
38 | MINT_TRANSFER_IN_NOT_POSSIBLE |
39 | REDEEM_ACCRUE_INTEREST_FAILED |
40 | REDEEM_COMPTROLLER_REJECTION |
41 | REDEEM_EXCHANGE_TOKENS_CALCULATION_FAILED |
42 | REDEEM_EXCHANGE_AMOUNT_CALCULATION_FAILED |
43 | REDEEM_EXCHANGE_RATE_READ_FAILED |
44 | REDEEM_FRESHNESS_CHECK |
45 | REDEEM_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED |
46 | REDEEM_NEW_TOTAL_SUPPLY_CALCULATION_FAILED |
47 | REDEEM_TRANSFER_OUT_NOT_POSSIBLE |
48 | REDUCE_RESERVES_ACCRUE_INTEREST_FAILED |
49 | REDUCE_RESERVES_ADMIN_CHECK |
50 | REDUCE_RESERVES_CASH_NOT_AVAILABLE |
51 | REDUCE_RESERVES_FRESH_CHECK |
52 | REDUCE_RESERVES_VALIDATION |
53 | REPAY_BEHALF_ACCRUE_INTEREST_FAILED |
54 | REPAY_BORROW_ACCRUE_INTEREST_FAILED |
55 | REPAY_BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED |
56 | REPAY_BORROW_COMPTROLLER_REJECTION |
57 | REPAY_BORROW_FRESHNESS_CHECK |
58 | REPAY_BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED |
59 | REPAY_BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED |
60 | REPAY_BORROW_TRANSFER_IN_NOT_POSSIBLE |
61 | SET_COLLATERAL_FACTOR_OWNER_CHECK |
62 | SET_COLLATERAL_FACTOR_VALIDATION |
63 | SET_COMPTROLLER_OWNER_CHECK |
64 | SET_INTEREST_RATE_MODEL_ACCRUE_INTEREST_FAILED |
65 | SET_INTEREST_RATE_MODEL_FRESH_CHECK |
66 | SET_INTEREST_RATE_MODEL_OWNER_CHECK |
67 | SET_MAX_ASSETS_OWNER_CHECK |
68 | SET_ORACLE_MARKET_NOT_LISTED |
69 | SET_PENDING_ADMIN_OWNER_CHECK |
70 | SET_RESERVE_FACTOR_ACCRUE_INTEREST_FAILED |
71 | SET_RESERVE_FACTOR_ADMIN_CHECK |
72 | SET_RESERVE_FACTOR_FRESH_CHECK |
73 | SET_RESERVE_FACTOR_BOUNDS_CHECK |
74 | TRANSFER_COMPTROLLER_REJECTION |
75 | TRANSFER_NOT_ALLOWED |
76 | TRANSFER_NOT_ENOUGH |
77 | TRANSFER_TOO_MUCH |
每个cToken 都可以兑换成数量不断增加的标的资产,因为在市场上会产生利息。cToken 与相关资产之间的汇率等于:
1 exchangeRate = (getCash() + totalBorrows() - totalReserves()) / totalSupply()
CErc20 / CEther
1 function exchangeRateCurrent() returns (uint)
Solidity
1 CErc20 cToken = CToken(0x3FDA...);
2 uint exchangeRateMantissa = cToken.exchangeRateCurrent();
Web3 1.0
1 const cToken = CEther.at(0x3FDB...);
2 const exchangeRate = (await cToken.methods.exchangeRateCurrent().call()) / 1e18;
请注意,对比 send 使用
call
是在链外调用方法,而不需要导致 Gas 成本。
现金是指该 cToken 合约所拥有的标的资产余额。人民可以查询到目前这个市场上的现金总量。
CErc20 / CEther
1 function getCash() returns (uint)
Solidity
1 CErc20 cToken = CToken(0x3FDA...);
2 uint cash = cToken.getCash();
Web3 1.0
1 const cToken = CEther.at(0x3FDB...);
2 const cash = (await cToken.methods.getCash().call());
总借贷额是指目前市场上借出的标的额,以及累计向市场上的供应商支付的利息。
CErc20 / CEther
1 function totalBorrowsCurrent() returns (uint)
Solidity
1 CErc20 cToken = CToken(0x3FDA...);
2 uint borrows = cToken.totalBorrowsCurrent();
Web3 1.0
1 const cToken = CEther.at(0x3FDB...);
2 const borrows = (await cToken.methods.totalBorrowsCurrent().call());
从协议中借入资产的用户要根据借款利率进行累计利息。利息是每块都要累计的,集成商可以通过这个功能来获得用户的借款余额的当前价值与利息。
CErc20 / CEther
1 function borrowBalanceCurrent(address account) returns (uint)
Solidity
1 CErc20 cToken = CToken(0x3FDA...);
2 uint borrows = cToken.borrowBalanceCurrent(msg.caller);
Web3 1.0
1 const cToken = CEther.at(0x3FDB...);
2 const borrows = await cToken.methods.borrowBalanceCurrent(account).call();
在任何时候,任何人都可以通过查询合约来获取当前每个区块的借款利率。
CErc20 / CEther
1 function borrowRatePerBlock() returns (uint)
Solidity
1 CErc20 cToken = CToken(0x3FDA...);
2 uint borrowRateMantissa = cToken.borrowRatePerBlock();
Web3 1.0
1 const cToken = CEther.at(0x3FDB...);
2 const borrowRate = (await cToken.methods.borrowRatePerBlock().call()) / 1e18;
总供应量是指目前在 cToken 市场上流通的代币数量。它是 cToken 合约的 EIP-20 接口的一部分 。
CErc20 / CEther
1 function totalSupply() returns (uint)
Solidity
1 CErc20 cToken = CToken(0x3FDA...);
2 uint tokens = cToken.totalSupply();
Web3 1.0
1 const cToken = CEther.at(0x3FDB...);
2 const tokens = (await cToken.methods.totalSupply().call());
用户的标的月,代币他们在协议中的资产,等于用户的 cToken 余额乘以[汇率]()。
CErc20 / CEther
1 function balanceOfUnderlying(address account) returns (uint)
Solidity
1 CErc20 cToken = CToken(0x3FDA...);
2 uint tokens = cToken.balanceOfUnderlying(msg.caller);
Web3 1.0
1 const cToken = CEther.at(0x3FDB...);
2 const tokens = await cToken.methods.balanceOfUnderlying(account).call();
在任何时候,人们都可以通过查询合约来获取当前每块的供给率。供给率是由[借款利率]()、准备金系数(reserve factor)和[总借款额]()得出的。
CErc20 / CEther
1 function supplyRatePerBlock() returns (uint)
Solidity
1 CErc20 cToken = CToken(0x3FDA...);
2 uint supplyRateMantissa = cToken.supplyRatePerBlock();
Web3 1.0
1 const cToken = CEther.at(0x3FDB...);
2 const supplyRate = (await cToken.methods.supplyRatePerBlock().call()) / 1e18;
储备金是每个 cToken 合约中的会计分录,代币历史利息的一部分,作为现金预留,可以通过协议的治理来提取或转移。借款人利息的一小部分应计入协议中,由准备金系数 reserve factor决定。
CErc20 / CEther
1 function totalReserves() returns (uint)
Solidity
1 CErc20 cToken = CToken(0x3FDA...);
2 uint reserves = cToken.totalReserves();
Web3 1.0
1 const cToken = CEther.at(0x3FDB...);
2 const reserves = (await cToken.methods.totalReserves().call());
准备金系数确定了借款人利息中转化为准备金的部分。
CErc20 / CEther
1 function reserveFactorMantissa() returns (uint)
Solidity
1 CErc20 cToken = CToken(0x3FDA...);
2uint reserveFactorMantissa = cToken.reserveFactorMantissa();
Web3 1.0
1 const cToken = CEther.at(0x3FDB...);
2 const reserveFactor = (await cToken.methods.reserveFactorMantissa().call()) / 1e18;
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!