"
48 | export DEV_ETH_MNEMONIC="clutch captain shoe salt awake harvest setup primary inmate ugly among become"
49 |
50 | ## Runs the Hardhat node locally
51 | ## Also seeds your first mnemonic account with test Ether and ERC20s
52 | node ./scripts/run-localhost-fork.js
53 | ```
54 |
55 | ## Borrowing Assets Directly via Web3 JSON RPC
56 | These code examples can be run by a web browser or with Node.js. If you want to use a web browser, you'll need to import a library in your HTML or JS file.
57 |
58 | Running these scripts will give your wallet borrowed **ETH** and **Dai**. cTokens are ERC20 Tokens that can be **used to redeem an ever-increasing amount of the underlying asset**. The cToken exchange rate **increases every Ethereum block**, they can be transferred, and can be used to redeem at any time, as long as the underlying collateral does not support an open borrow.
59 |
60 | ### Localhost Test Net
61 | - Run your local testnet in a second command line window **using the command above**. This will seed your account with ERC20 tokens. Look at the script file to find other ERC20 tokens that can be seeded into the account.
62 | - `node examples-js/web3-js/borrow-erc20-with-eth-collateral.js` To borrow Dai with ETH collateral.
63 | - `node examples-js/web3-js/borrow-eth-with-erc20-collateral.js` To borrow ETH with Dai collateral.
64 | - Check out the other examples for Ethers.js and Compound.js in the `examples-js` folder; They all do the same thing.
65 |
66 | ### Public Test Net or Main Net
67 | - Make sure you have a wallet with ETH for the Ethereum network you plan to interface with (Main, Ropsten, Kovan, etc.).
68 | - Insert the private key of your wallet in the scripts where noted. It's a best practice to insert the private key using an environment variable instead of revealing it in the code with a string literal.
69 | - Replace the HTTP provider in the `web3` constructors in the JS scripts in `web3-js-examples/`. Replace it using the string provided by the "Endpoint" selector in your Infura project dashboard. The localhost test net provider is `http://127.0.0.1:8545`.
70 | - Next, replace the contract addresses in the JSON file with the most recent ones. You can find Compound's cToken contract addresses for each network on this page: [https://compound.finance/docs#networks](https://compound.finance/docs#networks).
71 |
72 | ## Borrowing Assets With a Solidity Smart Contract
73 | The examples send ETH or DAI to a smart contract, which then mints cETH or cDAI. The contract also marks the assets as collateral for borrowing. Next other assets can be borrowed from the protocol.
74 |
75 | ### Localhost Testnet
76 | - Run your local testnet in a second command line window **using the command above**. This will seed your account with ERC20 tokens. Look at the script file to find other ERC20 tokens that can be seeded into the account.
77 | - Compile the smart contract in `./contracts/` by running `npx hardhat compile`
78 | - Next, deploy the smart contract to the localhost blockchain. `npx hardhat run ./scripts/deploy.js --network localhost`
79 | - Now that the contract is deployed, copy the address that is logged by the deploy script and paste it into the example script, so it knows where to direct its transactions. All JS files in the `examples-solidity` directory have a variable called `myContractAddress` which is where the `MyContract` address should be supplanted.
80 | - Now you can run any of the following examples to supply via smart contract.
81 | - `node ./examples-solidity/web3-js/borrow-erc20-via-solidity.js` To supply ETH as collateral and borrow Dai.
82 | - `node ./examples-solidity/web3-js/borrow-eth-via-solidity.js` To supply Dai as collateral and borrow ETH.
83 |
84 | ### Public Testnet or Mainnet
85 | See the Hardhat docs for more information on deploying to public Ethereum networks. https://hardhat.org/guides/deploying.html
86 |
87 | ## Output Examples
88 |
89 | **Borrow ERC20 via Web3 JavaScript**
90 |
91 | Output Example
92 |
93 |
94 | ```
95 | node examples-js/web3-js/borrow-erc20-with-eth-collateral.js
96 |
97 | My Wallet's ETH Balance: 10000
98 | My Wallet's cETH Balance: 0
99 | My Wallet's DAI Balance: 100
100 |
101 | Supplying ETH to the protocol as collateral (you will get cETH in return)...
102 |
103 | My Wallet's ETH Balance: 9998.999600295
104 | My Wallet's cETH Balance: 49.86112032
105 | My Wallet's DAI Balance: 100
106 |
107 | Entering market (via Comptroller contract) for ETH (as collateral)...
108 | Calculating your liquid assets in the protocol...
109 | Fetching cETH collateral factor...
110 | Fetching DAI price from the price feed...
111 | Fetching borrow rate per block for DAI borrowing...
112 |
113 | You have 3384.441740171433 of LIQUID assets (worth of USD) pooled in the protocol.
114 | You can borrow up to 75% of your TOTAL collateral supplied to the protocol as DAI.
115 | 1 DAI == 1.000000 USD
116 | You can borrow up to 3384.441740171433 DAI from the protocol.
117 | NEVER borrow near the maximum amount because your account will be instantly liquidated.
118 |
119 | Your borrowed amount INCREASES (2.2076358156e-8 * borrowed amount) DAI per block.
120 | This is based on the current borrow rate.
121 |
122 | Now attempting to borrow 50 DAI...
123 | My Wallet's ETH Balance: 9998.998558725
124 | My Wallet's cETH Balance: 49.86112032
125 | My Wallet's DAI Balance: 150
126 |
127 | Fetching DAI borrow balance from cDAI contract...
128 | Borrow balance is 50 DAI
129 |
130 | This part is when you do something with those borrowed assets!
131 |
132 | Now repaying the borrow...
133 | Approving DAI to be transferred from your wallet to the cDAI contract...
134 |
135 | Borrow repaid.
136 |
137 | My Wallet's ETH Balance: 9998.99801022
138 | My Wallet's cETH Balance: 49.86112032
139 | My Wallet's DAI Balance: 100
140 |
141 | ```
142 |
143 |
144 |
145 | **Borrow ETH via Web3 JavaScript**
146 |
147 | Output Example
148 |
149 |
150 | ```
151 | node examples-js/web3-js/borrow-eth-with-erc20-collateral.js
152 |
153 | My Wallet's ETH Balance: 10000
154 | My Wallet's cDAI Balance: 0
155 | My Wallet's DAI Balance: 100
156 |
157 | Approving DAI to be transferred from your wallet to the cDAI contract...
158 |
159 | Supplying DAI to the protocol as collateral (you will get cDAI in return)...
160 |
161 | My Wallet's ETH Balance: 9999.99525988
162 | My Wallet's cDAI Balance: 691.40915384
163 | My Wallet's DAI Balance: 85
164 |
165 | Entering market (via Comptroller contract) for ETH (as collateral)...
166 | Calculating your liquid assets in the protocol...
167 | Fetching the protocol's DAI collateral factor...
168 | Fetching DAI price from the price feed...
169 | Fetching borrow rate per block for ETH borrowing...
170 |
171 | You have 11.249999999912092756 of LIQUID assets (worth of USD) pooled in the protocol.
172 | You can borrow up to 75% of your TOTAL assets supplied to the protocol as ETH.
173 | 1 DAI == 1.000000 USD
174 | You can borrow up to 11.249999999912092756 USD worth of assets from the protocol.
175 | NEVER borrow near the maximum amount because your account will be instantly liquidated.
176 |
177 | Your borrowed amount INCREASES (1.1208317598e-8 * borrowed amount) ETH per block.
178 | This is based on the current borrow rate.
179 |
180 | Now attempting to borrow 0.002 ETH...
181 |
182 | ETH borrow successful.
183 |
184 | My Wallet's ETH Balance: 9999.98912288
185 | My Wallet's cDAI Balance: 691.40915384
186 | My Wallet's DAI Balance: 85
187 |
188 | Fetching your ETH borrow balance from cETH contract...
189 | Borrow balance is 0.002 ETH
190 |
191 | This part is when you do something with those borrowed assets!
192 |
193 | Now repaying the borrow...
194 |
195 | Borrow repaid.
196 |
197 | My Wallet's ETH Balance: 9999.98426352
198 | My Wallet's cDAI Balance: 691.40915384
199 | My Wallet's DAI Balance: 85
200 |
201 | ```
202 |
203 |
204 |
205 | **Borrow ERC20 via Solidity**
206 |
207 | Output Example
208 |
209 |
210 | ```
211 | node examples-solidity/web3-js/borrow-erc20-via-solidity.js
212 |
213 | My Wallet's ETH Balance: 10000
214 | MyContract's ETH Balance: 0
215 | MyContract's cETH Balance: 0
216 | MyContract's DAI Balance: 0
217 |
218 | Calling MyContract.borrowErc20Example with 1 ETH for collateral...
219 |
220 | My Wallet's ETH Balance: 9998.98883272
221 | MyContract's ETH Balance: 0
222 | MyContract's cETH Balance: 49.86111985
223 | MyContract's DAI Balance: 10
224 |
225 | Now repaying the borrow...
226 |
227 | My Wallet's ETH Balance: 9998.9852758
228 | MyContract's ETH Balance: 0
229 | MyContract's cETH Balance: 49.86111985
230 | MyContract's DAI Balance: 0
231 | ```
232 |
233 |
234 |
235 | **Borrow ETH Token via Solidity**
236 |
237 | Output Example
238 |
239 |
240 | ```
241 | node examples-solidity/web3-js/borrow-eth-via-solidity.js
242 | My Wallet's DAI Balance: 100
243 | MyContract's ETH Balance: 0
244 | MyContract's cETH Balance: 0
245 | MyContract's DAI Balance: 0
246 | MyContract's cDAI Balance: 0
247 |
248 | Sending 25 DAI to MyContract so it can provide collateral...
249 |
250 | My Wallet's DAI Balance: 75
251 | MyContract's ETH Balance: 0
252 | MyContract's cETH Balance: 0
253 | MyContract's DAI Balance: 25
254 | MyContract's cDAI Balance: 0
255 |
256 | Calling MyContract.borrowEthExample with 25 DAI as collateral...
257 |
258 | My Wallet's DAI Balance: 75
259 | MyContract's ETH Balance: 0.002
260 | MyContract's cETH Balance: 0
261 | MyContract's DAI Balance: 0
262 | MyContract's cDAI Balance: 1152.34787526
263 |
264 | Now repaying the borrow...
265 |
266 | My Wallet's DAI Balance: 75
267 | MyContract's ETH Balance: 0
268 | MyContract's cETH Balance: 0
269 | MyContract's DAI Balance: 0
270 | MyContract's cDAI Balance: 1152.34787526
271 |
272 | ```
273 |
274 |
275 |
276 | ## Minting Localhost Test ERC20s
277 |
278 | All assets supported by the Compound protocol can be seeded into the first account when doing localhost testing. See the `amounts` object at the top of the script `./scripts/run-localhost-fork.js`. You can add assets and amounts to this object. When the localhost fork script is run, Hardhat will move tokens from a whale (cToken contract) to the first wallet of your selected mnemonic (in your environment variable). You can then use these assets freely on your localhost fork.
279 |
280 | ## Ethers.js & Compound.js Examples
281 |
282 | There are several other code examples for [ethers.js](https://ethers.org/) and [Compound.js](https://github.com/compound-finance/compound-js). These SDKs can be used instead of Web3.js in each instance. Each version of the script does the same operations. To try the other code examples, run the scripts in the other folders.
283 |
284 | ```bash
285 | ## Ethers.js Examples
286 | node ./examples-solidity/ethers-js/borrow-erc20-via-solidity.js
287 | node ./examples-solidity/ethers-js/borrow-eth-via-solidity.js
288 | node ./examples-js/ethers-js/borrow-erc20-with-eth-collateral.js
289 | node ./examples-js/ethers-js/borrow-eth-with-erc20-collateral.js
290 |
291 | ## Compound.js Examples
292 | node ./examples-solidity/compound-js/borrow-erc20-via-solidity.js
293 | node ./examples-solidity/compound-js/borrow-eth-via-solidity.js
294 | node ./examples-js/compound-js/borrow-erc20-with-eth-collateral.js
295 | node ./examples-js/compound-js/borrow-eth-with-erc20-collateral.js
296 |
297 | ## Web3.js
298 | node ./examples-solidity/web3-js/borrow-erc20-via-solidity.js
299 | node ./examples-solidity/web3-js/borrow-eth-via-solidity.js
300 | node ./examples-js/web3-js/borrow-erc20-with-eth-collateral.js
301 | node ./examples-js/web3-js/borrow-eth-with-erc20-collateral.js
302 | ```
--------------------------------------------------------------------------------
/contracts.json:
--------------------------------------------------------------------------------
1 | {
2 | "cEthAbi": [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"amount","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"mint","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"reserveFactorMantissa","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"account","type":"address"}],"name":"borrowBalanceCurrent","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"exchangeRateStored","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"pendingAdmin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"owner","type":"address"}],"name":"balanceOfUnderlying","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getCash","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newComptroller","type":"address"}],"name":"_setComptroller","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalBorrows","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"repayBorrow","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"comptroller","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"reduceAmount","type":"uint256"}],"name":"_reduceReserves","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"initialExchangeRateMantissa","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"accrualBlockNumber","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"totalBorrowsCurrent","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"redeemAmount","type":"uint256"}],"name":"redeemUnderlying","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalReserves","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"borrowBalanceStored","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"accrueInterest","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"borrowIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"borrower","type":"address"},{"name":"cTokenCollateral","type":"address"}],"name":"liquidateBorrow","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"supplyRatePerBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"liquidator","type":"address"},{"name":"borrower","type":"address"},{"name":"seizeTokens","type":"uint256"}],"name":"seize","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newPendingAdmin","type":"address"}],"name":"_setPendingAdmin","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"exchangeRateCurrent","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"getAccountSnapshot","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"borrowAmount","type":"uint256"}],"name":"borrow","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"redeemTokens","type":"uint256"}],"name":"redeem","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"borrower","type":"address"}],"name":"repayBorrowBehalf","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"_acceptAdmin","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newInterestRateModel","type":"address"}],"name":"_setInterestRateModel","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"interestRateModel","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"admin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"borrowRatePerBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newReserveFactorMantissa","type":"uint256"}],"name":"_setReserveFactor","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"isCToken","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"comptroller_","type":"address"},{"name":"interestRateModel_","type":"address"},{"name":"initialExchangeRateMantissa_","type":"uint256"},{"name":"name_","type":"string"},{"name":"symbol_","type":"string"},{"name":"decimals_","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"interestAccumulated","type":"uint256"},{"indexed":false,"name":"borrowIndex","type":"uint256"},{"indexed":false,"name":"totalBorrows","type":"uint256"}],"name":"AccrueInterest","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"minter","type":"address"},{"indexed":false,"name":"mintAmount","type":"uint256"},{"indexed":false,"name":"mintTokens","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"redeemer","type":"address"},{"indexed":false,"name":"redeemAmount","type":"uint256"},{"indexed":false,"name":"redeemTokens","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"borrower","type":"address"},{"indexed":false,"name":"borrowAmount","type":"uint256"},{"indexed":false,"name":"accountBorrows","type":"uint256"},{"indexed":false,"name":"totalBorrows","type":"uint256"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"payer","type":"address"},{"indexed":false,"name":"borrower","type":"address"},{"indexed":false,"name":"repayAmount","type":"uint256"},{"indexed":false,"name":"accountBorrows","type":"uint256"},{"indexed":false,"name":"totalBorrows","type":"uint256"}],"name":"RepayBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"liquidator","type":"address"},{"indexed":false,"name":"borrower","type":"address"},{"indexed":false,"name":"repayAmount","type":"uint256"},{"indexed":false,"name":"cTokenCollateral","type":"address"},{"indexed":false,"name":"seizeTokens","type":"uint256"}],"name":"LiquidateBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldPendingAdmin","type":"address"},{"indexed":false,"name":"newPendingAdmin","type":"address"}],"name":"NewPendingAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldAdmin","type":"address"},{"indexed":false,"name":"newAdmin","type":"address"}],"name":"NewAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldComptroller","type":"address"},{"indexed":false,"name":"newComptroller","type":"address"}],"name":"NewComptroller","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldInterestRateModel","type":"address"},{"indexed":false,"name":"newInterestRateModel","type":"address"}],"name":"NewMarketInterestRateModel","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldReserveFactorMantissa","type":"uint256"},{"indexed":false,"name":"newReserveFactorMantissa","type":"uint256"}],"name":"NewReserveFactor","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"admin","type":"address"},{"indexed":false,"name":"reduceAmount","type":"uint256"},{"indexed":false,"name":"newTotalReserves","type":"uint256"}],"name":"ReservesReduced","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"error","type":"uint256"},{"indexed":false,"name":"info","type":"uint256"},{"indexed":false,"name":"detail","type":"uint256"}],"name":"Failure","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Approval","type":"event"}],
3 | "comptrollerAbi": [{"constant":true,"inputs":[],"name":"pendingAdmin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x26782247"},{"constant":false,"inputs":[{"name":"newPendingAdmin","type":"address"}],"name":"_setPendingAdmin","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xb71d1a0c"},{"constant":true,"inputs":[],"name":"comptrollerImplementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xbb82aa5e"},{"constant":false,"inputs":[],"name":"_acceptImplementation","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xc1e80334"},{"constant":true,"inputs":[],"name":"pendingComptrollerImplementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xdcfbc0c7"},{"constant":false,"inputs":[{"name":"newPendingImplementation","type":"address"}],"name":"_setPendingImplementation","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xe992a041"},{"constant":false,"inputs":[],"name":"_acceptAdmin","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xe9c714f2"},{"constant":true,"inputs":[],"name":"admin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf851a440"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldPendingImplementation","type":"address"},{"indexed":false,"name":"newPendingImplementation","type":"address"}],"name":"NewPendingImplementation","type":"event","signature":"0xe945ccee5d701fc83f9b8aa8ca94ea4219ec1fcbd4f4cab4f0ea57c5c3e1d815"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldImplementation","type":"address"},{"indexed":false,"name":"newImplementation","type":"address"}],"name":"NewImplementation","type":"event","signature":"0xd604de94d45953f9138079ec1b82d533cb2160c906d1076d1f7ed54befbca97a"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldPendingAdmin","type":"address"},{"indexed":false,"name":"newPendingAdmin","type":"address"}],"name":"NewPendingAdmin","type":"event","signature":"0xca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldAdmin","type":"address"},{"indexed":false,"name":"newAdmin","type":"address"}],"name":"NewAdmin","type":"event","signature":"0xf9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc"},{"anonymous":false,"inputs":[{"indexed":false,"name":"error","type":"uint256"},{"indexed":false,"name":"info","type":"uint256"},{"indexed":false,"name":"detail","type":"uint256"}],"name":"Failure","type":"event","signature":"0x45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0"},{"constant":true,"inputs":[],"name":"isComptroller","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x007e3dd2"},{"constant":false,"inputs":[{"name":"cToken","type":"address"},{"name":"payer","type":"address"},{"name":"borrower","type":"address"},{"name":"repayAmount","type":"uint256"},{"name":"borrowerIndex","type":"uint256"}],"name":"repayBorrowVerify","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x1ededc91"},{"constant":false,"inputs":[{"name":"cToken","type":"address"},{"name":"payer","type":"address"},{"name":"borrower","type":"address"},{"name":"repayAmount","type":"uint256"}],"name":"repayBorrowAllowed","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x24008a62"},{"constant":true,"inputs":[],"name":"pendingAdmin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x26782247"},{"constant":false,"inputs":[{"name":"newCloseFactorMantissa","type":"uint256"}],"name":"_setCloseFactor","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x317b0b77"},{"constant":false,"inputs":[{"name":"unitroller","type":"address"},{"name":"_oracle","type":"address"},{"name":"_closeFactorMantissa","type":"uint256"},{"name":"_maxAssets","type":"uint256"},{"name":"reinitializing","type":"bool"}],"name":"_become","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x32000e00"},{"constant":false,"inputs":[{"name":"cToken","type":"address"},{"name":"minter","type":"address"},{"name":"mintAmount","type":"uint256"},{"name":"mintTokens","type":"uint256"}],"name":"mintVerify","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x41c728b9"},{"constant":false,"inputs":[{"name":"cTokenBorrowed","type":"address"},{"name":"cTokenCollateral","type":"address"},{"name":"liquidator","type":"address"},{"name":"borrower","type":"address"},{"name":"repayAmount","type":"uint256"},{"name":"seizeTokens","type":"uint256"}],"name":"liquidateBorrowVerify","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x47ef3b3b"},{"constant":true,"inputs":[],"name":"liquidationIncentiveMantissa","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x4ada90af"},{"constant":false,"inputs":[{"name":"cToken","type":"address"},{"name":"minter","type":"address"},{"name":"mintAmount","type":"uint256"}],"name":"mintAllowed","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x4ef4c3e1"},{"constant":false,"inputs":[{"name":"newLiquidationIncentiveMantissa","type":"uint256"}],"name":"_setLiquidationIncentive","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x4fd42e17"},{"constant":false,"inputs":[{"name":"cToken","type":"address"},{"name":"redeemer","type":"address"},{"name":"redeemAmount","type":"uint256"},{"name":"redeemTokens","type":"uint256"}],"name":"redeemVerify","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x51dff989"},{"constant":false,"inputs":[{"name":"newOracle","type":"address"}],"name":"_setPriceOracle","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x55ee1fe1"},{"constant":false,"inputs":[{"name":"cToken","type":"address"},{"name":"borrower","type":"address"},{"name":"borrowAmount","type":"uint256"}],"name":"borrowVerify","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x5c778605"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"getAccountLiquidity","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x5ec88c79"},{"constant":false,"inputs":[{"name":"cTokenBorrowed","type":"address"},{"name":"cTokenCollateral","type":"address"},{"name":"liquidator","type":"address"},{"name":"borrower","type":"address"},{"name":"repayAmount","type":"uint256"}],"name":"liquidateBorrowAllowed","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x5fc7e71e"},{"constant":false,"inputs":[{"name":"cToken","type":"address"},{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"transferTokens","type":"uint256"}],"name":"transferVerify","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x6a56947e"},{"constant":false,"inputs":[{"name":"cTokenCollateral","type":"address"},{"name":"cTokenBorrowed","type":"address"},{"name":"liquidator","type":"address"},{"name":"borrower","type":"address"},{"name":"seizeTokens","type":"uint256"}],"name":"seizeVerify","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x6d35bf91"},{"constant":true,"inputs":[],"name":"oracle","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x7dc0d1d0"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"markets","outputs":[{"name":"isListed","type":"bool"},{"name":"collateralFactorMantissa","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x8e8f294b"},{"constant":true,"inputs":[{"name":"account","type":"address"},{"name":"cToken","type":"address"}],"name":"checkMembership","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x929fe9a1"},{"constant":true,"inputs":[],"name":"maxAssets","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x94b2294b"},{"constant":false,"inputs":[{"name":"cToken","type":"address"}],"name":"_supportMarket","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa76b3fda"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"getAssetsIn","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xabfceffc"},{"constant":true,"inputs":[],"name":"comptrollerImplementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xbb82aa5e"},{"constant":false,"inputs":[{"name":"cToken","type":"address"},{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"transferTokens","type":"uint256"}],"name":"transferAllowed","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xbdcdc258"},{"constant":false,"inputs":[{"name":"cTokens","type":"address[]"}],"name":"enterMarkets","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xc2998238"},{"constant":true,"inputs":[{"name":"cTokenBorrowed","type":"address"},{"name":"cTokenCollateral","type":"address"},{"name":"repayAmount","type":"uint256"}],"name":"liquidateCalculateSeizeTokens","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xc488847b"},{"constant":false,"inputs":[{"name":"cTokenCollateral","type":"address"},{"name":"cTokenBorrowed","type":"address"},{"name":"liquidator","type":"address"},{"name":"borrower","type":"address"},{"name":"seizeTokens","type":"uint256"}],"name":"seizeAllowed","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xd02f7351"},{"constant":false,"inputs":[{"name":"newMaxAssets","type":"uint256"}],"name":"_setMaxAssets","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xd9226ced"},{"constant":false,"inputs":[{"name":"cToken","type":"address"},{"name":"borrower","type":"address"},{"name":"borrowAmount","type":"uint256"}],"name":"borrowAllowed","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xda3d454c"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"uint256"}],"name":"accountAssets","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xdce15449"},{"constant":true,"inputs":[],"name":"pendingComptrollerImplementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xdcfbc0c7"},{"constant":false,"inputs":[{"name":"cToken","type":"address"},{"name":"newCollateralFactorMantissa","type":"uint256"}],"name":"_setCollateralFactor","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xe4028eee"},{"constant":true,"inputs":[],"name":"closeFactorMantissa","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xe8755446"},{"constant":false,"inputs":[{"name":"cToken","type":"address"},{"name":"redeemer","type":"address"},{"name":"redeemTokens","type":"uint256"}],"name":"redeemAllowed","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xeabe7d91"},{"constant":false,"inputs":[{"name":"cTokenAddress","type":"address"}],"name":"exitMarket","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xede4edd0"},{"constant":true,"inputs":[],"name":"admin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf851a440"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"cToken","type":"address"}],"name":"MarketListed","type":"event","signature":"0xcf583bb0c569eb967f806b11601c4cb93c10310485c67add5f8362c2f212321f"},{"anonymous":false,"inputs":[{"indexed":false,"name":"cToken","type":"address"},{"indexed":false,"name":"account","type":"address"}],"name":"MarketEntered","type":"event","signature":"0x3ab23ab0d51cccc0c3085aec51f99228625aa1a922b3a8ca89a26b0f2027a1a5"},{"anonymous":false,"inputs":[{"indexed":false,"name":"cToken","type":"address"},{"indexed":false,"name":"account","type":"address"}],"name":"MarketExited","type":"event","signature":"0xe699a64c18b07ac5b7301aa273f36a2287239eb9501d81950672794afba29a0d"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldCloseFactorMantissa","type":"uint256"},{"indexed":false,"name":"newCloseFactorMantissa","type":"uint256"}],"name":"NewCloseFactor","type":"event","signature":"0x3b9670cf975d26958e754b57098eaa2ac914d8d2a31b83257997b9f346110fd9"},{"anonymous":false,"inputs":[{"indexed":false,"name":"cToken","type":"address"},{"indexed":false,"name":"oldCollateralFactorMantissa","type":"uint256"},{"indexed":false,"name":"newCollateralFactorMantissa","type":"uint256"}],"name":"NewCollateralFactor","type":"event","signature":"0x70483e6592cd5182d45ac970e05bc62cdcc90e9d8ef2c2dbe686cf383bcd7fc5"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldLiquidationIncentiveMantissa","type":"uint256"},{"indexed":false,"name":"newLiquidationIncentiveMantissa","type":"uint256"}],"name":"NewLiquidationIncentive","type":"event","signature":"0xaeba5a6c40a8ac138134bff1aaa65debf25971188a58804bad717f82f0ec1316"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldMaxAssets","type":"uint256"},{"indexed":false,"name":"newMaxAssets","type":"uint256"}],"name":"NewMaxAssets","type":"event","signature":"0x7093cf1eb653f749c3ff531d6df7f92764536a7fa0d13530cd26e070780c32ea"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldPriceOracle","type":"address"},{"indexed":false,"name":"newPriceOracle","type":"address"}],"name":"NewPriceOracle","type":"event","signature":"0xd52b2b9b7e9ee655fcb95d2e5b9e0c9f69e7ef2b8e9d2d0ea78402d576d22e22"},{"anonymous":false,"inputs":[{"indexed":false,"name":"error","type":"uint256"},{"indexed":false,"name":"info","type":"uint256"},{"indexed":false,"name":"detail","type":"uint256"}],"name":"Failure","type":"event","signature":"0x45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0"}],
4 | "priceFeedAbi": [{"inputs":[{"internalType":"contract OpenOraclePriceData","name":"priceData_","type":"address"},{"internalType":"address","name":"reporter_","type":"address"},{"internalType":"uint256","name":"anchorToleranceMantissa_","type":"uint256"},{"internalType":"uint256","name":"anchorPeriod_","type":"uint256"},{"components":[{"internalType":"address","name":"cToken","type":"address"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"bytes32","name":"symbolHash","type":"bytes32"},{"internalType":"uint256","name":"baseUnit","type":"uint256"},{"internalType":"enum UniswapConfig.PriceSource","name":"priceSource","type":"uint8"},{"internalType":"uint256","name":"fixedPrice","type":"uint256"},{"internalType":"address","name":"uniswapMarket","type":"address"},{"internalType":"bool","name":"isUniswapReversed","type":"bool"}],"internalType":"struct UniswapConfig.TokenConfig[]","name":"configs","type":"tuple[]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"symbol","type":"string"},{"indexed":false,"internalType":"uint256","name":"anchorPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldTimestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTimestamp","type":"uint256"}],"name":"AnchorPriceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"symbol","type":"string"},{"indexed":false,"internalType":"uint256","name":"reporter","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"anchor","type":"uint256"}],"name":"PriceGuarded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"symbol","type":"string"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"}],"name":"PriceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"reporter","type":"address"}],"name":"ReporterInvalidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"symbolHash","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"oldTimestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTimestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newPrice","type":"uint256"}],"name":"UniswapWindowUpdated","type":"event"},{"inputs":[],"name":"anchorPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ethBaseUnit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"expScale","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"i","type":"uint256"}],"name":"getTokenConfig","outputs":[{"components":[{"internalType":"address","name":"cToken","type":"address"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"bytes32","name":"symbolHash","type":"bytes32"},{"internalType":"uint256","name":"baseUnit","type":"uint256"},{"internalType":"enum UniswapConfig.PriceSource","name":"priceSource","type":"uint8"},{"internalType":"uint256","name":"fixedPrice","type":"uint256"},{"internalType":"address","name":"uniswapMarket","type":"address"},{"internalType":"bool","name":"isUniswapReversed","type":"bool"}],"internalType":"struct UniswapConfig.TokenConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"cToken","type":"address"}],"name":"getTokenConfigByCToken","outputs":[{"components":[{"internalType":"address","name":"cToken","type":"address"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"bytes32","name":"symbolHash","type":"bytes32"},{"internalType":"uint256","name":"baseUnit","type":"uint256"},{"internalType":"enum UniswapConfig.PriceSource","name":"priceSource","type":"uint8"},{"internalType":"uint256","name":"fixedPrice","type":"uint256"},{"internalType":"address","name":"uniswapMarket","type":"address"},{"internalType":"bool","name":"isUniswapReversed","type":"bool"}],"internalType":"struct UniswapConfig.TokenConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"symbol","type":"string"}],"name":"getTokenConfigBySymbol","outputs":[{"components":[{"internalType":"address","name":"cToken","type":"address"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"bytes32","name":"symbolHash","type":"bytes32"},{"internalType":"uint256","name":"baseUnit","type":"uint256"},{"internalType":"enum UniswapConfig.PriceSource","name":"priceSource","type":"uint8"},{"internalType":"uint256","name":"fixedPrice","type":"uint256"},{"internalType":"address","name":"uniswapMarket","type":"address"},{"internalType":"bool","name":"isUniswapReversed","type":"bool"}],"internalType":"struct UniswapConfig.TokenConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"symbolHash","type":"bytes32"}],"name":"getTokenConfigBySymbolHash","outputs":[{"components":[{"internalType":"address","name":"cToken","type":"address"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"bytes32","name":"symbolHash","type":"bytes32"},{"internalType":"uint256","name":"baseUnit","type":"uint256"},{"internalType":"enum UniswapConfig.PriceSource","name":"priceSource","type":"uint8"},{"internalType":"uint256","name":"fixedPrice","type":"uint256"},{"internalType":"address","name":"uniswapMarket","type":"address"},{"internalType":"bool","name":"isUniswapReversed","type":"bool"}],"internalType":"struct UniswapConfig.TokenConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"underlying","type":"address"}],"name":"getTokenConfigByUnderlying","outputs":[{"components":[{"internalType":"address","name":"cToken","type":"address"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"bytes32","name":"symbolHash","type":"bytes32"},{"internalType":"uint256","name":"baseUnit","type":"uint256"},{"internalType":"enum UniswapConfig.PriceSource","name":"priceSource","type":"uint8"},{"internalType":"uint256","name":"fixedPrice","type":"uint256"},{"internalType":"address","name":"uniswapMarket","type":"address"},{"internalType":"bool","name":"isUniswapReversed","type":"bool"}],"internalType":"struct UniswapConfig.TokenConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"cToken","type":"address"}],"name":"getUnderlyingPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"message","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"invalidateReporter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lowerBoundAnchorRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"newObservations","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"acc","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"oldObservations","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"acc","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"messages","type":"bytes[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"string[]","name":"symbols","type":"string[]"}],"name":"postPrices","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"symbol","type":"string"}],"name":"price","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceData","outputs":[{"internalType":"contract OpenOraclePriceData","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"prices","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reporter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reporterInvalidated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"message","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"source","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"upperBoundAnchorRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}],
5 | "cErcAbi": [{"inputs":[{"internalType":"address","name":"underlying_","type":"address"},{"internalType":"contract ComptrollerInterface","name":"comptroller_","type":"address"},{"internalType":"contract InterestRateModel","name":"interestRateModel_","type":"address"},{"internalType":"uint256","name":"initialExchangeRateMantissa_","type":"uint256"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"},{"internalType":"address payable","name":"admin_","type":"address"},{"internalType":"address","name":"implementation_","type":"address"},{"internalType":"bytes","name":"becomeImplementationData","type":"bytes"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"cashPrior","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestAccumulated","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"AccrueInterest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accountBorrows","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"error","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"info","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"detail","type":"uint256"}],"name":"Failure","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liquidator","type":"address"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"cTokenCollateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"LiquidateBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint256","name":"mintAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mintTokens","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"NewAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract ComptrollerInterface","name":"oldComptroller","type":"address"},{"indexed":false,"internalType":"contract ComptrollerInterface","name":"newComptroller","type":"address"}],"name":"NewComptroller","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldImplementation","type":"address"},{"indexed":false,"internalType":"address","name":"newImplementation","type":"address"}],"name":"NewImplementation","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract InterestRateModel","name":"oldInterestRateModel","type":"address"},{"indexed":false,"internalType":"contract InterestRateModel","name":"newInterestRateModel","type":"address"}],"name":"NewMarketInterestRateModel","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldPendingAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newPendingAdmin","type":"address"}],"name":"NewPendingAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldReserveFactorMantissa","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newReserveFactorMantissa","type":"uint256"}],"name":"NewReserveFactor","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"redeemer","type":"address"},{"indexed":false,"internalType":"uint256","name":"redeemAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"redeemTokens","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"payer","type":"address"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accountBorrows","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"RepayBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"benefactor","type":"address"},{"indexed":false,"internalType":"uint256","name":"addAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTotalReserves","type":"uint256"}],"name":"ReservesAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"uint256","name":"reduceAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTotalReserves","type":"uint256"}],"name":"ReservesReduced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":false,"inputs":[],"name":"_acceptAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"addAmount","type":"uint256"}],"name":"_addReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"reduceAmount","type":"uint256"}],"name":"_reduceReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"contract ComptrollerInterface","name":"newComptroller","type":"address"}],"name":"_setComptroller","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"implementation_","type":"address"},{"internalType":"bool","name":"allowResign","type":"bool"},{"internalType":"bytes","name":"becomeImplementationData","type":"bytes"}],"name":"_setImplementation","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"contract InterestRateModel","name":"newInterestRateModel","type":"address"}],"name":"_setInterestRateModel","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable","name":"newPendingAdmin","type":"address"}],"name":"_setPendingAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"newReserveFactorMantissa","type":"uint256"}],"name":"_setReserveFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"accrualBlockNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"accrueInterest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"admin","outputs":[{"internalType":"address payable","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOfUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"borrowAmount","type":"uint256"}],"name":"borrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"borrowBalanceCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"borrowBalanceStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"borrowIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"borrowRatePerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"comptroller","outputs":[{"internalType":"contract ComptrollerInterface","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"delegateToImplementation","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"delegateToViewImplementation","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"exchangeRateCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"exchangeRateStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAccountSnapshot","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getCash","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"interestRateModel","outputs":[{"internalType":"contract InterestRateModel","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isCToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"contract CTokenInterface","name":"cTokenCollateral","type":"address"}],"name":"liquidateBorrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"mintAmount","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pendingAdmin","outputs":[{"internalType":"address payable","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"redeemTokens","type":"uint256"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"redeemAmount","type":"uint256"}],"name":"redeemUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repayBorrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repayBorrowBehalf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"reserveFactorMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"liquidator","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"seize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"supplyRatePerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalBorrows","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"totalBorrowsCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"underlying","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}],
6 | "erc20Abi": [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]
7 | }
--------------------------------------------------------------------------------
/contracts/MyContracts.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.6;
4 |
5 |
6 | interface Erc20 {
7 | function approve(address, uint256) external returns (bool);
8 |
9 | function transfer(address, uint256) external returns (bool);
10 | }
11 |
12 |
13 | interface CErc20 {
14 | function mint(uint256) external returns (uint256);
15 |
16 | function borrow(uint256) external returns (uint256);
17 |
18 | function borrowRatePerBlock() external view returns (uint256);
19 |
20 | function borrowBalanceCurrent(address) external returns (uint256);
21 |
22 | function repayBorrow(uint256) external returns (uint256);
23 | }
24 |
25 |
26 | interface CEth {
27 | function mint() external payable;
28 |
29 | function borrow(uint256) external returns (uint256);
30 |
31 | function repayBorrow() external payable;
32 |
33 | function borrowBalanceCurrent(address) external returns (uint256);
34 | }
35 |
36 |
37 | interface Comptroller {
38 | function markets(address) external returns (bool, uint256);
39 |
40 | function enterMarkets(address[] calldata)
41 | external
42 | returns (uint256[] memory);
43 |
44 | function getAccountLiquidity(address)
45 | external
46 | view
47 | returns (uint256, uint256, uint256);
48 | }
49 |
50 |
51 | interface PriceFeed {
52 | function getUnderlyingPrice(address cToken) external view returns (uint);
53 | }
54 |
55 |
56 | contract MyContract {
57 | event MyLog(string, uint256);
58 |
59 | // Seed the contract with a supported underyling asset before running this
60 | function borrowErc20Example(
61 | address payable _cEtherAddress,
62 | address _comptrollerAddress,
63 | address _priceFeedAddress,
64 | address _cTokenAddress,
65 | uint _underlyingDecimals
66 | ) public payable returns (uint256) {
67 | CEth cEth = CEth(_cEtherAddress);
68 | Comptroller comptroller = Comptroller(_comptrollerAddress);
69 | PriceFeed priceFeed = PriceFeed(_priceFeedAddress);
70 | CErc20 cToken = CErc20(_cTokenAddress);
71 |
72 | // Supply ETH as collateral, get cETH in return
73 | cEth.mint{ value: msg.value, gas: 250000 }();
74 |
75 | // Enter the ETH market so you can borrow another type of asset
76 | address[] memory cTokens = new address[](1);
77 | cTokens[0] = _cEtherAddress;
78 | uint256[] memory errors = comptroller.enterMarkets(cTokens);
79 | if (errors[0] != 0) {
80 | revert("Comptroller.enterMarkets failed.");
81 | }
82 |
83 | // Get my account's total liquidity value in Compound
84 | (uint256 error, uint256 liquidity, uint256 shortfall) = comptroller
85 | .getAccountLiquidity(address(this));
86 | if (error != 0) {
87 | revert("Comptroller.getAccountLiquidity failed.");
88 | }
89 | require(shortfall == 0, "account underwater");
90 | require(liquidity > 0, "account has excess collateral");
91 |
92 | // Get the collateral factor for our collateral
93 | // (
94 | // bool isListed,
95 | // uint collateralFactorMantissa
96 | // ) = comptroller.markets(_cEthAddress);
97 | // emit MyLog('ETH Collateral Factor', collateralFactorMantissa);
98 |
99 | // Get the amount of underlying added to your borrow each block
100 | // uint borrowRateMantissa = cToken.borrowRatePerBlock();
101 | // emit MyLog('Current Borrow Rate', borrowRateMantissa);
102 |
103 | // Get the underlying price in USD from the Price Feed,
104 | // so we can find out the maximum amount of underlying we can borrow.
105 | uint256 underlyingPrice = priceFeed.getUnderlyingPrice(_cTokenAddress);
106 | uint256 maxBorrowUnderlying = liquidity / underlyingPrice;
107 |
108 | // Borrowing near the max amount will result
109 | // in your account being liquidated instantly
110 | emit MyLog("Maximum underlying Borrow (borrow far less!)", maxBorrowUnderlying);
111 |
112 | // Borrow underlying
113 | uint256 numUnderlyingToBorrow = 10;
114 |
115 | // Borrow, check the underlying balance for this contract's address
116 | cToken.borrow(numUnderlyingToBorrow * 10**_underlyingDecimals);
117 |
118 | // Get the borrow balance
119 | uint256 borrows = cToken.borrowBalanceCurrent(address(this));
120 | emit MyLog("Current underlying borrow amount", borrows);
121 |
122 | return borrows;
123 | }
124 |
125 | function myErc20RepayBorrow(
126 | address _erc20Address,
127 | address _cErc20Address,
128 | uint256 amount
129 | ) public returns (bool) {
130 | Erc20 underlying = Erc20(_erc20Address);
131 | CErc20 cToken = CErc20(_cErc20Address);
132 |
133 | underlying.approve(_cErc20Address, amount);
134 | uint256 error = cToken.repayBorrow(amount);
135 |
136 | require(error == 0, "CErc20.repayBorrow Error");
137 | return true;
138 | }
139 |
140 | function borrowEthExample(
141 | address payable _cEtherAddress,
142 | address _comptrollerAddress,
143 | address _cTokenAddress,
144 | address _underlyingAddress,
145 | uint256 _underlyingToSupplyAsCollateral
146 | ) public returns (uint) {
147 | CEth cEth = CEth(_cEtherAddress);
148 | Comptroller comptroller = Comptroller(_comptrollerAddress);
149 | CErc20 cToken = CErc20(_cTokenAddress);
150 | Erc20 underlying = Erc20(_underlyingAddress);
151 |
152 | // Approve transfer of underlying
153 | underlying.approve(_cTokenAddress, _underlyingToSupplyAsCollateral);
154 |
155 | // Supply underlying as collateral, get cToken in return
156 | uint256 error = cToken.mint(_underlyingToSupplyAsCollateral);
157 | require(error == 0, "CErc20.mint Error");
158 |
159 | // Enter the market so you can borrow another type of asset
160 | address[] memory cTokens = new address[](1);
161 | cTokens[0] = _cTokenAddress;
162 | uint256[] memory errors = comptroller.enterMarkets(cTokens);
163 | if (errors[0] != 0) {
164 | revert("Comptroller.enterMarkets failed.");
165 | }
166 |
167 | // Get my account's total liquidity value in Compound
168 | (uint256 error2, uint256 liquidity, uint256 shortfall) = comptroller
169 | .getAccountLiquidity(address(this));
170 | if (error2 != 0) {
171 | revert("Comptroller.getAccountLiquidity failed.");
172 | }
173 | require(shortfall == 0, "account underwater");
174 | require(liquidity > 0, "account has excess collateral");
175 |
176 | // Borrowing near the max amount will result
177 | // in your account being liquidated instantly
178 | emit MyLog("Maximum ETH Borrow (borrow far less!)", liquidity);
179 |
180 | // // Get the collateral factor for our collateral
181 | // (
182 | // bool isListed,
183 | // uint collateralFactorMantissa
184 | // ) = comptroller.markets(_cTokenAddress);
185 | // emit MyLog('Collateral Factor', collateralFactorMantissa);
186 |
187 | // // Get the amount of ETH added to your borrow each block
188 | // uint borrowRateMantissa = cEth.borrowRatePerBlock();
189 | // emit MyLog('Current ETH Borrow Rate', borrowRateMantissa);
190 |
191 | // Borrow a fixed amount of ETH below our maximum borrow amount
192 | uint256 numWeiToBorrow = 2000000000000000; // 0.002 ETH
193 |
194 | // Borrow, then check the underlying balance for this contract's address
195 | cEth.borrow(numWeiToBorrow);
196 |
197 | uint256 borrows = cEth.borrowBalanceCurrent(address(this));
198 | emit MyLog("Current ETH borrow amount", borrows);
199 |
200 | return borrows;
201 | }
202 |
203 | function myEthRepayBorrow(address _cEtherAddress, uint256 amount, uint256 gas)
204 | public
205 | returns (bool)
206 | {
207 | CEth cEth = CEth(_cEtherAddress);
208 | cEth.repayBorrow{ value: amount, gas: gas }();
209 | return true;
210 | }
211 |
212 | // Need this to receive ETH when `borrowEthExample` executes
213 | receive() external payable {}
214 | }
215 |
--------------------------------------------------------------------------------
/examples-js/compound-js/borrow-erc20-with-eth-collateral.js:
--------------------------------------------------------------------------------
1 | // Example to supply ETH as collateral and borrow a supported ERC-20 token
2 | const Compound = require('@compound-finance/compound-js');
3 | const providerUrl = 'http://localhost:8545';
4 |
5 | // Your Ethereum wallet private key
6 | const myWalletAddress = '0xa0df350d2637096571F7A701CBc1C5fdE30dF76A';
7 | const privateKey = 'b8c1b5c1d81f9475fdf2e334517d29f733bdfa40682207571b12fc1142cbf329';
8 |
9 | const compound = new Compound(providerUrl, { privateKey });
10 |
11 | // Mainnet Contract for the Compound Protocol's Comptroller
12 | const comptrollerAddress = Compound.util.getAddress(Compound.Comptroller);
13 |
14 | const assetName = Compound.DAI;
15 | const underlyingAddress = Compound.util.getAddress(assetName);
16 | const cTokenAddress = Compound.util.getAddress('c' + assetName);
17 | const underlyingDecimals = Compound.decimals[assetName]; // Number of decimals defined in this ERC20 token's contract
18 |
19 | const logBalances = () => {
20 | return new Promise(async (resolve, reject) => {
21 | let myWalletEthBalance = +(await Compound.eth.getBalance(myWalletAddress, providerUrl)) / 1e18;
22 | let myWalletCEthBalance = await _balanceOf(Compound.cETH, myWalletAddress);
23 | let myWalletUnderlyingBalance = await _balanceOf(assetName, myWalletAddress);
24 |
25 | console.log("My Wallet's ETH Balance:", myWalletEthBalance);
26 | console.log("My Wallet's cETH Balance:", myWalletCEthBalance);
27 | console.log(`My Wallet's ${assetName} Balance:`, myWalletUnderlyingBalance);
28 |
29 | resolve();
30 | });
31 | };
32 |
33 | const main = async () => {
34 | await logBalances();
35 |
36 | const ethToSupplyAsCollateral = 1;
37 |
38 | console.log('\nSupplying ETH to the protocol as collateral (you will get cETH in return)...\n');
39 | let tx = await compound.supply(Compound.ETH, ethToSupplyAsCollateral);
40 | await tx.wait(1); // wait until the transaction has 1 confirmation on the blockchain
41 |
42 | await logBalances();
43 |
44 | console.log('\nEntering market (via Comptroller contract) for ETH (as collateral)...');
45 | let markets = [ Compound.ETH ]; // This is the collateral asset
46 | tx = await compound.enterMarkets(markets);
47 | await tx.wait(1);
48 |
49 | console.log('Calculating your liquid assets in the protocol...');
50 | let liquidity = await _getAccountLiquidity(myWalletAddress, comptrollerAddress);
51 |
52 | console.log('Fetching cETH collateral factor...');
53 | let collateralFactor = await _getCollateralFactor(cTokenAddress, comptrollerAddress); // as percentage
54 |
55 | console.log(`Fetching ${assetName} price from the price feed...`);
56 | let underlyingPriceInUsd = await compound.getPrice(assetName);
57 |
58 | console.log(`Fetching borrow rate per block for ${assetName} borrowing...`);
59 | let borrowRate = await _borrowRatePerBlock(cTokenAddress);
60 |
61 | console.log(`\nYou have ${liquidity} of LIQUID assets (worth of USD) pooled in the protocol.`);
62 | console.log(`You can borrow up to ${collateralFactor}% of your TOTAL collateral supplied to the protocol as ${assetName}.`);
63 | console.log(`1 ${assetName} == ${underlyingPriceInUsd.toFixed(6)} USD`);
64 | console.log(`You can borrow up to ${liquidity/underlyingPriceInUsd} ${assetName} from the protocol.`);
65 | console.log(`NEVER borrow near the maximum amount because your account will be instantly liquidated.`);
66 | console.log(`\nYour borrowed amount INCREASES (${borrowRate} * borrowed amount) ${assetName} per block.\nThis is based on the current borrow rate.\n`);
67 |
68 | const underlyingToBorrow = 50;
69 | console.log(`Now attempting to borrow ${underlyingToBorrow} ${assetName}...`);
70 | tx = await compound.borrow(assetName, underlyingToBorrow);
71 | await tx.wait(1);
72 | // console.log('Borrow Transaction', tx);
73 |
74 | await logBalances();
75 |
76 | console.log(`\nFetching ${assetName} borrow balance from c${assetName} contract...`);
77 | let balance = await _borrowBalanceCurrent(cTokenAddress, myWalletAddress);
78 | console.log(`Borrow balance is ${balance} ${assetName}`);
79 |
80 | console.log(`\nThis part is when you do something with those borrowed assets!\n`);
81 |
82 | console.log(`Now repaying the borrow...`);
83 | console.log(`Approving ${assetName} to be transferred from your wallet to the c${assetName} contract...`);
84 |
85 | const underlyingToRepay = underlyingToBorrow;
86 |
87 | tx = await compound.repayBorrow(assetName, underlyingToRepay);
88 | const repayBorrowResult = await tx.wait(1);
89 |
90 | const failure = repayBorrowResult.events.find(_ => _.event === 'Failure');
91 | if (failure) {
92 | const errorCode = failure.args.error;
93 | console.error(`repayBorrow error, code ${errorCode}`);
94 | process.exit(1);
95 | }
96 |
97 | console.log(`\nBorrow repaid.\n`);
98 | await logBalances();
99 | };
100 |
101 | main().catch((err) => {
102 | console.error('ERROR:', err);
103 | });
104 |
105 | async function _balanceOf(asset, account) {
106 | const assetAddress = Compound.util.getAddress(Compound[asset]);
107 | let balance;
108 | try {
109 | balance = await Compound.eth.read(
110 | assetAddress,
111 | 'function balanceOf(address) returns (uint)',
112 | [ account ],
113 | { provider: providerUrl }
114 | );
115 | } catch(error) {
116 | console.error(error);
117 | }
118 |
119 | return +balance / Math.pow(10, Compound.decimals[asset]);
120 | }
121 |
122 | async function _getAccountLiquidity(account, _comptrollerAddress) {
123 | let error, liquidity, shortfall;
124 | try {
125 | [ error, liquidity, shortfall ] = await Compound.eth.read(
126 | _comptrollerAddress,
127 | 'function getAccountLiquidity(address account) view returns (uint, uint, uint)',
128 | [ account ],
129 | { provider: providerUrl }
130 | );
131 | } catch(error) {
132 | console.error(error);
133 | }
134 |
135 | return liquidity / 1e18;
136 | }
137 |
138 | async function _getCollateralFactor(_cTokenAddress, _comptrollerAddress) {
139 | let isListed, collateralFactor, isComped;
140 |
141 | try {
142 | [ isListed, collateralFactor, isComped ] = await Compound.eth.read(
143 | _comptrollerAddress,
144 | 'function markets(address cTokenAddress) view returns (bool, uint, bool)',
145 | [ _cTokenAddress ],
146 | { provider: providerUrl }
147 | );
148 | } catch(error) {
149 | console.error(error);
150 | }
151 |
152 | return (collateralFactor / 1e18) * 100;
153 | }
154 |
155 | async function _borrowRatePerBlock(_cTokenAddress) {
156 | let borrowRatePerBlock;
157 | try {
158 | borrowRatePerBlock = await Compound.eth.read(
159 | _cTokenAddress,
160 | 'function borrowRatePerBlock() returns (uint)',
161 | [],
162 | { provider: providerUrl }
163 | );
164 | } catch(error) {
165 | console.error(error);
166 | }
167 |
168 | return borrowRatePerBlock / Math.pow(10, underlyingDecimals);
169 | }
170 |
171 | async function _borrowBalanceCurrent(_cTokenAddress, account) {
172 | let borrowBalance;
173 | try {
174 | borrowBalance = await Compound.eth.read(
175 | _cTokenAddress,
176 | 'function borrowBalanceCurrent(address account) returns (uint)',
177 | [ account ],
178 | { provider: providerUrl }
179 | );
180 | } catch(error) {
181 | console.error(error);
182 | }
183 |
184 | return borrowBalance / Math.pow(10, underlyingDecimals);
185 | }
186 |
--------------------------------------------------------------------------------
/examples-js/compound-js/borrow-eth-with-erc20-collateral.js:
--------------------------------------------------------------------------------
1 | // Example to supply a supported ERC20 token as collateral and borrow ETH
2 | // YOU MUST HAVE DAI IN YOUR WALLET before you run this script
3 | const Compound = require('@compound-finance/compound-js');
4 | const providerUrl = 'http://localhost:8545';
5 |
6 | // Your Ethereum wallet private key
7 | const myWalletAddress = '0xa0df350d2637096571F7A701CBc1C5fdE30dF76A';
8 | const privateKey = 'b8c1b5c1d81f9475fdf2e334517d29f733bdfa40682207571b12fc1142cbf329';
9 |
10 | const compound = new Compound(providerUrl, { privateKey });
11 |
12 | // Mainnet Contract for Compound's Comptroller
13 | const comptrollerAddress = Compound.util.getAddress(Compound.Comptroller);
14 |
15 | const assetName = Compound.DAI;
16 | const cTokenName = Compound.cDAI;
17 | const underlyingAddress = Compound.util.getAddress(assetName);
18 | const cTokenAddress = Compound.util.getAddress(cTokenName);
19 | const underlyingDecimals = Compound.decimals[assetName]; // Number of decimals defined in this ERC20 token's contract
20 |
21 | const logBalances = () => {
22 | return new Promise(async (resolve, reject) => {
23 | let myWalletEthBalance = +(await Compound.eth.getBalance(myWalletAddress, providerUrl)) / 1e18;
24 | let myWalletCTokenBalance = await _balanceOf(cTokenName, myWalletAddress);
25 | let myWalletUnderlyingBalance = await _balanceOf(assetName, myWalletAddress);
26 |
27 | console.log("My Wallet's ETH Balance:", myWalletEthBalance);
28 | console.log(`My Wallet's c${assetName} Balance:`, myWalletCTokenBalance);
29 | console.log(`My Wallet's ${assetName} Balance:`, myWalletUnderlyingBalance);
30 |
31 | resolve();
32 | });
33 | };
34 |
35 | const main = async () => {
36 | await logBalances();
37 |
38 | let underlyingAsCollateral = 15;
39 |
40 | console.log(`Supplying ${assetName} to the protocol as collateral (you will get c${assetName} in return)...\n`);
41 | let tx = await compound.supply(assetName, underlyingAsCollateral);
42 | const mintResult = await tx.wait(1); // wait until the transaction has 1 confirmation on the blockchain
43 |
44 | let failure = mintResult.events.find(_ => _.event === 'Failure');
45 | if (failure) {
46 | const errorCode = failure.args.error;
47 | throw new Error(
48 | `See https://compound.finance/docs/ctokens#ctoken-error-codes\n` +
49 | `Code: ${errorCode}\n`
50 | );
51 | }
52 |
53 | await logBalances();
54 |
55 | console.log('\nEntering market (via Comptroller contract) for ETH (as collateral)...');
56 | let markets = [ assetName ]; // This is the collateral asset
57 | tx = await compound.enterMarkets(markets);
58 | await tx.wait(1);
59 |
60 | console.log('Calculating your liquid assets in the protocol...');
61 | let liquidity = await _getAccountLiquidity(myWalletAddress, comptrollerAddress);
62 |
63 | console.log(`Fetching the protocol's ${assetName} collateral factor...`);
64 | let collateralFactor = await _getCollateralFactor(cTokenAddress, comptrollerAddress); // as percentage
65 |
66 | console.log(`Fetching ${assetName} price from the price feed...`);
67 | let underlyingPriceInUsd = await compound.getPrice(assetName);
68 |
69 | console.log('Fetching borrow rate per block for ETH borrowing...');
70 | let borrowRate = await _borrowRatePerBlock(cTokenAddress);
71 |
72 | console.log(`\nYou have ${liquidity} of LIQUID assets (worth of USD) pooled in the protocol.`);
73 | console.log(`You can borrow up to ${collateralFactor}% of your TOTAL assets supplied to the protocol as ETH.`);
74 | console.log(`1 ${assetName} == ${underlyingPriceInUsd.toFixed(6)} USD`);
75 | console.log(`You can borrow up to ${liquidity} USD worth of assets from the protocol.`);
76 | console.log(`NEVER borrow near the maximum amount because your account will be instantly liquidated.`);
77 | console.log(`\nYour borrowed amount INCREASES (${borrowRate} * borrowed amount) ETH per block.\nThis is based on the current borrow rate.`);
78 |
79 | // Let's try to borrow 0.002 ETH (or another amount far below the borrow limit)
80 | const ethToBorrow = 0.002;
81 | console.log(`\nNow attempting to borrow ${ethToBorrow} ETH...`);
82 | tx = await compound.borrow(Compound.ETH, ethToBorrow);
83 | const borrowResult = await tx.wait(1);
84 |
85 | if (isNaN(borrowResult)) {
86 | console.log(`\nETH borrow successful.\n`);
87 | } else {
88 | throw new Error(
89 | `See https://compound.finance/docs/ctokens#ctoken-error-codes\n` +
90 | `Code: ${borrowResult}\n`
91 | );
92 | }
93 |
94 | await logBalances();
95 |
96 | console.log('\nFetching your ETH borrow balance from cETH contract...');
97 | const cEthAddress = Compound.util.getAddress(Compound.cETH);
98 | let balance = await _borrowBalanceCurrent(cEthAddress, myWalletAddress);
99 | console.log(`Borrow balance is ${balance} ETH`);
100 |
101 | console.log(`\nThis part is when you do something with those borrowed assets!\n`);
102 |
103 | console.log(`Now repaying the borrow...`);
104 |
105 | const ethToRepay = ethToBorrow;
106 |
107 | tx = await compound.repayBorrow(Compound.ETH, ethToRepay);
108 | const repayBorrowResult = await tx.wait(1);
109 |
110 | failure = repayBorrowResult.events.find(_ => _.event === 'Failure');
111 | if (failure) {
112 | const errorCode = failure.args.error;
113 | console.error(`repayBorrow error, code ${errorCode}`);
114 | process.exit(1);
115 | }
116 |
117 | console.log(`\nBorrow repaid.\n`);
118 | await logBalances();
119 | };
120 |
121 | main().catch((err) => {
122 | console.error('ERROR:', err);
123 | });
124 |
125 | async function _balanceOf(asset, account) {
126 | const assetAddress = Compound.util.getAddress(Compound[asset]);
127 | let balance;
128 | try {
129 | balance = await Compound.eth.read(
130 | assetAddress,
131 | 'function balanceOf(address) returns (uint)',
132 | [ account ],
133 | { provider: providerUrl }
134 | );
135 | } catch(error) {
136 | console.error(error);
137 | }
138 |
139 | return +balance / Math.pow(10, Compound.decimals[asset]);
140 | }
141 |
142 | async function _getAccountLiquidity(account, _comptrollerAddress) {
143 | let error, liquidity, shortfall;
144 | try {
145 | [ error, liquidity, shortfall ] = await Compound.eth.read(
146 | _comptrollerAddress,
147 | 'function getAccountLiquidity(address account) view returns (uint, uint, uint)',
148 | [ account ],
149 | { provider: providerUrl }
150 | );
151 | } catch(error) {
152 | console.error(error);
153 | }
154 |
155 | return liquidity / 1e18;
156 | }
157 |
158 | async function _getCollateralFactor(_cTokenAddress, _comptrollerAddress) {
159 | let isListed, collateralFactor, isComped;
160 |
161 | try {
162 | [ isListed, collateralFactor, isComped ] = await Compound.eth.read(
163 | _comptrollerAddress,
164 | 'function markets(address cTokenAddress) view returns (bool, uint, bool)',
165 | [ _cTokenAddress ],
166 | { provider: providerUrl }
167 | );
168 | } catch(error) {
169 | console.error(error);
170 | }
171 |
172 | return (collateralFactor / 1e18) * 100;
173 | }
174 |
175 | async function _borrowRatePerBlock(_cTokenAddress) {
176 | let borrowRatePerBlock;
177 | try {
178 | borrowRatePerBlock = await Compound.eth.read(
179 | _cTokenAddress,
180 | 'function borrowRatePerBlock() returns (uint)',
181 | [],
182 | { provider: providerUrl }
183 | );
184 | } catch(error) {
185 | console.error(error);
186 | }
187 |
188 | return borrowRatePerBlock / Math.pow(10, underlyingDecimals);
189 | }
190 |
191 | async function _borrowBalanceCurrent(_cTokenAddress, account) {
192 | let borrowBalance;
193 | try {
194 | borrowBalance = await Compound.eth.read(
195 | _cTokenAddress,
196 | 'function borrowBalanceCurrent(address account) returns (uint)',
197 | [ account ],
198 | { provider: providerUrl }
199 | );
200 | } catch(error) {
201 | console.error(error);
202 | }
203 |
204 | return borrowBalance / Math.pow(10, underlyingDecimals);
205 | }
206 |
--------------------------------------------------------------------------------
/examples-js/ethers-js/borrow-erc20-with-eth-collateral.js:
--------------------------------------------------------------------------------
1 | // Example to supply ETH as collateral and borrow a supported ERC-20 token
2 | const ethers = require('ethers');
3 | const provider = new ethers.providers.JsonRpcProvider('http://localhost:8545');
4 | const {
5 | cEthAbi,
6 | comptrollerAbi,
7 | priceFeedAbi,
8 | cErcAbi,
9 | erc20Abi,
10 | } = require('../../contracts.json');
11 |
12 | // Your Ethereum wallet private key
13 | const privateKey = 'b8c1b5c1d81f9475fdf2e334517d29f733bdfa40682207571b12fc1142cbf329';
14 | const wallet = new ethers.Wallet(privateKey, provider);
15 | const myWalletAddress = wallet.address;
16 |
17 | // Mainnet Contract for cETH (the collateral-supply process is different for cERC20 tokens)
18 | const cEthAddress = '0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5';
19 | const cEth = new ethers.Contract(cEthAddress, cEthAbi, wallet);
20 |
21 | // Mainnet Contract for the Compound Protocol's Comptroller
22 | const comptrollerAddress = '0x3d9819210a31b4961b30ef54be2aed79b9c9cd3b';
23 | const comptroller = new ethers.Contract(comptrollerAddress, comptrollerAbi, wallet);
24 |
25 | // Mainnet Contract for the Open Price Feed
26 | const priceFeedAddress = '0x6d2299c48a8dd07a872fdd0f8233924872ad1071';
27 | const priceFeed = new ethers.Contract(priceFeedAddress, priceFeedAbi, wallet);
28 |
29 | // Mainnet address of underlying token (like DAI or USDC)
30 | const underlyingAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; // Dai
31 | const underlying = new ethers.Contract(underlyingAddress, erc20Abi, wallet);
32 |
33 | // Mainnet address for a cToken (like cDai, https://compound.finance/docs#networks)
34 | const cTokenAddress = '0x5d3a536e4d6dbd6114cc1ead35777bab948e3643'; // cDai
35 | const cToken = new ethers.Contract(cTokenAddress, cErcAbi, wallet);
36 | const assetName = 'DAI'; // for the log output lines
37 | const underlyingDecimals = 18; // Number of decimals defined in this ERC20 token's contract
38 |
39 | const logBalances = () => {
40 | return new Promise(async (resolve, reject) => {
41 | let myWalletEthBalance = await provider.getBalance(myWalletAddress) / 1e18;
42 | let myWalletCEthBalance = await cEth.callStatic.balanceOf(myWalletAddress) / 1e8;
43 | let myWalletUnderlyingBalance = await underlying.callStatic.balanceOf(myWalletAddress) / Math.pow(10, underlyingDecimals);
44 |
45 | console.log("My Wallet's ETH Balance:", myWalletEthBalance);
46 | console.log("My Wallet's cETH Balance:", myWalletCEthBalance);
47 | console.log(`My Wallet's ${assetName} Balance:`, myWalletUnderlyingBalance);
48 |
49 | resolve();
50 | });
51 | };
52 |
53 | const main = async () => {
54 | await logBalances();
55 |
56 | const ethToSupplyAsCollateral = 1;
57 |
58 | console.log('\nSupplying ETH to the protocol as collateral (you will get cETH in return)...\n');
59 | let mint = await cEth.mint({
60 | value: (ethToSupplyAsCollateral * 1e18).toString()
61 | });
62 |
63 | await logBalances();
64 |
65 | console.log('\nEntering market (via Comptroller contract) for ETH (as collateral)...');
66 | let markets = [ cEthAddress ]; // This is the cToken contract(s) for your collateral
67 | let enterMarkets = await comptroller.enterMarkets(markets);
68 | await enterMarkets.wait(1);
69 |
70 | console.log('Calculating your liquid assets in the protocol...');
71 | let { 1:liquidity } = await comptroller.callStatic.getAccountLiquidity(myWalletAddress);
72 | liquidity = liquidity / 1e18;
73 |
74 | console.log('Fetching cETH collateral factor...');
75 | let { 1:collateralFactor } = await comptroller.callStatic.markets(cEthAddress);
76 | collateralFactor = (collateralFactor / 1e18) * 100; // Convert to percent
77 |
78 | console.log(`Fetching ${assetName} price from the price feed...`);
79 | let underlyingPriceInUsd = await priceFeed.callStatic.price(assetName);
80 | underlyingPriceInUsd = underlyingPriceInUsd / 1e6; // Price feed provides price in USD with 6 decimal places
81 |
82 | console.log(`Fetching borrow rate per block for ${assetName} borrowing...`);
83 | let borrowRate = await cToken.callStatic.borrowRatePerBlock();
84 | borrowRate = borrowRate / Math.pow(10, underlyingDecimals);
85 |
86 | console.log(`\nYou have ${liquidity} of LIQUID assets (worth of USD) pooled in the protocol.`);
87 | console.log(`You can borrow up to ${collateralFactor}% of your TOTAL collateral supplied to the protocol as ${assetName}.`);
88 | console.log(`1 ${assetName} == ${underlyingPriceInUsd.toFixed(6)} USD`);
89 | console.log(`You can borrow up to ${liquidity/underlyingPriceInUsd} ${assetName} from the protocol.`);
90 | console.log(`NEVER borrow near the maximum amount because your account will be instantly liquidated.`);
91 | console.log(`\nYour borrowed amount INCREASES (${borrowRate} * borrowed amount) ${assetName} per block.\nThis is based on the current borrow rate.\n`);
92 |
93 | const underlyingToBorrow = 50;
94 | console.log(`Now attempting to borrow ${underlyingToBorrow} ${assetName}...`);
95 | const scaledUpBorrowAmount = (underlyingToBorrow * Math.pow(10, underlyingDecimals)).toString();
96 | const trx = await cToken.borrow(scaledUpBorrowAmount);
97 | await trx.wait(1);
98 | // console.log('Borrow Transaction', trx);
99 |
100 | await logBalances();
101 |
102 | console.log(`\nFetching ${assetName} borrow balance from c${assetName} contract...`);
103 | let balance = await cToken.callStatic.borrowBalanceCurrent(myWalletAddress);
104 | balance = balance / Math.pow(10, underlyingDecimals);
105 | console.log(`Borrow balance is ${balance} ${assetName}`);
106 |
107 | console.log(`\nThis part is when you do something with those borrowed assets!\n`);
108 |
109 | console.log(`Now repaying the borrow...`);
110 | console.log(`Approving ${assetName} to be transferred from your wallet to the c${assetName} contract...`);
111 | const underlyingToRepay = (underlyingToBorrow * Math.pow(10, underlyingDecimals)).toString();
112 | const approve = await underlying.approve(cTokenAddress, underlyingToRepay);
113 | await approve.wait(1);
114 |
115 | const repayBorrow = await cToken.repayBorrow(underlyingToRepay);
116 | const repayBorrowResult = await repayBorrow.wait(1);
117 |
118 | const failure = repayBorrowResult.events.find(_ => _.event === 'Failure');
119 | if (failure) {
120 | const errorCode = failure.args.error;
121 | console.error(`repayBorrow error, code ${errorCode}`);
122 | process.exit(1);
123 | }
124 |
125 | console.log(`\nBorrow repaid.\n`);
126 | await logBalances();
127 | };
128 |
129 | main().catch((err) => {
130 | console.error('ERROR:', err);
131 | });
132 |
--------------------------------------------------------------------------------
/examples-js/ethers-js/borrow-eth-with-erc20-collateral.js:
--------------------------------------------------------------------------------
1 | // Example to supply a supported ERC20 token as collateral and borrow ETH
2 | // YOU MUST HAVE DAI IN YOUR WALLET before you run this script
3 | const ethers = require('ethers');
4 | const provider = new ethers.providers.JsonRpcProvider('http://localhost:8545');
5 | const {
6 | cEthAbi,
7 | comptrollerAbi,
8 | priceFeedAbi,
9 | cErcAbi,
10 | erc20Abi,
11 | } = require('../../contracts.json');
12 |
13 | // Your Ethereum wallet private key
14 | const privateKey = 'b8c1b5c1d81f9475fdf2e334517d29f733bdfa40682207571b12fc1142cbf329';
15 | const wallet = new ethers.Wallet(privateKey, provider);
16 | const myWalletAddress = wallet.address;
17 |
18 | // Mainnet Contract for cETH
19 | const cEthAddress = '0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5';
20 | const cEth = new ethers.Contract(cEthAddress, cEthAbi, wallet);
21 |
22 | // Mainnet Contract for Compound's Comptroller
23 | const comptrollerAddress = '0x3d9819210a31b4961b30ef54be2aed79b9c9cd3b';
24 | const comptroller = new ethers.Contract(comptrollerAddress, comptrollerAbi, wallet);
25 |
26 | // Mainnet Contract for the Open Price Feed
27 | const priceFeedAddress = '0x6d2299c48a8dd07a872fdd0f8233924872ad1071';
28 | const priceFeed = new ethers.Contract(priceFeedAddress, priceFeedAbi, wallet);
29 |
30 | // Mainnet address of underlying token (like DAI or USDC)
31 | const underlyingAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; // Dai
32 | const underlying = new ethers.Contract(underlyingAddress, erc20Abi, wallet);
33 |
34 | // Mainnet address for a cToken (like cDai, https://compound.finance/docs#networks)
35 | const cTokenAddress = '0x5d3a536e4d6dbd6114cc1ead35777bab948e3643'; // cDai
36 | const cToken = new ethers.Contract(cTokenAddress, cErcAbi, wallet);
37 | const assetName = 'DAI'; // for the log output lines
38 | const underlyingDecimals = 18; // Number of decimals defined in this ERC20 token's contract
39 |
40 | const logBalances = () => {
41 | return new Promise(async (resolve, reject) => {
42 | let myWalletEthBalance = await provider.getBalance(myWalletAddress) / 1e18;
43 | let myWalletCTokenBalance = await cToken.callStatic.balanceOf(myWalletAddress) / 1e8;
44 | let myWalletUnderlyingBalance = await underlying.callStatic.balanceOf(myWalletAddress) / Math.pow(10, underlyingDecimals);
45 |
46 | console.log("My Wallet's ETH Balance:", myWalletEthBalance);
47 | console.log(`My Wallet's c${assetName} Balance:`, myWalletCTokenBalance);
48 | console.log(`My Wallet's ${assetName} Balance:`, myWalletUnderlyingBalance);
49 |
50 | resolve();
51 | });
52 | };
53 |
54 | const main = async () => {
55 | await logBalances();
56 |
57 | let underlyingAsCollateral = 15;
58 |
59 | // Convert the token amount to a scaled up number, then a string.
60 | underlyingAsCollateral = underlyingAsCollateral * Math.pow(10, underlyingDecimals);
61 | underlyingAsCollateral = underlyingAsCollateral.toString();
62 |
63 | console.log(`\nApproving ${assetName} to be transferred from your wallet to the c${assetName} contract...\n`);
64 | const approve = await underlying.approve(cTokenAddress, underlyingAsCollateral);
65 | await approve.wait(1);
66 |
67 | console.log(`Supplying ${assetName} to the protocol as collateral (you will get c${assetName} in return)...\n`);
68 | let mint = await cToken.mint(underlyingAsCollateral);
69 | const mintResult = await mint.wait(1);
70 |
71 | let failure = mintResult.events.find(_ => _.event === 'Failure');
72 | if (failure) {
73 | const errorCode = failure.args.error;
74 | throw new Error(
75 | `See https://compound.finance/docs/ctokens#ctoken-error-codes\n` +
76 | `Code: ${errorCode}\n`
77 | );
78 | }
79 |
80 | await logBalances();
81 |
82 | console.log('\nEntering market (via Comptroller contract) for ETH (as collateral)...');
83 | let markets = [cTokenAddress]; // This is the cToken contract(s) for your collateral
84 | let enterMarkets = await comptroller.enterMarkets(markets);
85 | await enterMarkets.wait(1);
86 |
87 | console.log('Calculating your liquid assets in the protocol...');
88 | let {1:liquidity} = await comptroller.callStatic.getAccountLiquidity(myWalletAddress);
89 | liquidity = (+liquidity / 1e18).toString();
90 |
91 | console.log(`Fetching the protocol's ${assetName} collateral factor...`);
92 | let {1:collateralFactor} = await comptroller.callStatic.markets(cTokenAddress);
93 | collateralFactor = (collateralFactor / Math.pow(10, underlyingDecimals)) * 100; // Convert to percent
94 |
95 | console.log(`Fetching ${assetName} price from the price feed...`);
96 | let underlyingPriceInUsd = await priceFeed.callStatic.price(assetName);
97 | underlyingPriceInUsd = underlyingPriceInUsd / 1e6; // Price feed provides price in USD with 6 decimal places
98 |
99 | console.log('Fetching borrow rate per block for ETH borrowing...');
100 | let borrowRate = await cEth.callStatic.borrowRatePerBlock();
101 | borrowRate = borrowRate / 1e18;
102 |
103 | console.log(`\nYou have ${liquidity} of LIQUID assets (worth of USD) pooled in the protocol.`);
104 | console.log(`You can borrow up to ${collateralFactor}% of your TOTAL assets supplied to the protocol as ETH.`);
105 | console.log(`1 ${assetName} == ${underlyingPriceInUsd.toFixed(6)} USD`);
106 | console.log(`You can borrow up to ${liquidity} USD worth of assets from the protocol.`);
107 | console.log(`NEVER borrow near the maximum amount because your account will be instantly liquidated.`);
108 | console.log(`\nYour borrowed amount INCREASES (${borrowRate} * borrowed amount) ETH per block.\nThis is based on the current borrow rate.`);
109 |
110 | // Let's try to borrow 0.002 ETH (or another amount far below the borrow limit)
111 | const ethToBorrow = 0.002;
112 | console.log(`\nNow attempting to borrow ${ethToBorrow} ETH...`);
113 | const borrow = await cEth.borrow(ethers.utils.parseEther(ethToBorrow.toString()));
114 | const borrowResult = await borrow.wait(1);
115 |
116 | if (isNaN(borrowResult)) {
117 | console.log(`\nETH borrow successful.\n`);
118 | } else {
119 | throw new Error(
120 | `See https://compound.finance/docs/ctokens#ctoken-error-codes\n` +
121 | `Code: ${borrowResult}\n`
122 | );
123 | }
124 |
125 | await logBalances();
126 |
127 | console.log('\nFetching your ETH borrow balance from cETH contract...');
128 | let balance = await cEth.callStatic.borrowBalanceCurrent(myWalletAddress);
129 | balance = balance / 1e18; // because DAI is a 1e18 scaled token.
130 | console.log(`Borrow balance is ${balance} ETH`);
131 |
132 | console.log(`\nThis part is when you do something with those borrowed assets!\n`);
133 |
134 | console.log(`Now repaying the borrow...`);
135 |
136 | const ethToRepay = ethToBorrow;
137 | const repayBorrow = await cEth.repayBorrow({
138 | value: ethers.utils.parseEther(ethToRepay.toString())
139 | });
140 | const repayBorrowResult = await repayBorrow.wait(1);
141 |
142 | failure = repayBorrowResult.events.find(_ => _.event === 'Failure');
143 | if (failure) {
144 | const errorCode = failure.args.error;
145 | console.error(`repayBorrow error, code ${errorCode}`);
146 | process.exit(1);
147 | }
148 |
149 | console.log(`\nBorrow repaid.\n`);
150 | await logBalances();
151 | };
152 |
153 | main().catch((err) => {
154 | console.error('ERROR:', err);
155 | });
156 |
--------------------------------------------------------------------------------
/examples-js/web3-js/borrow-erc20-with-eth-collateral.js:
--------------------------------------------------------------------------------
1 | // Example to supply ETH as collateral and borrow a supported ERC-20 token
2 | const Web3 = require('web3');
3 | const web3 = new Web3('http://127.0.0.1:8545');
4 | const {
5 | cEthAbi,
6 | comptrollerAbi,
7 | priceFeedAbi,
8 | cErcAbi,
9 | erc20Abi,
10 | } = require('../../contracts.json');
11 |
12 | // Your Ethereum wallet private key
13 | const privateKey = 'b8c1b5c1d81f9475fdf2e334517d29f733bdfa40682207571b12fc1142cbf329';
14 |
15 | // Add your Ethereum wallet to the Web3 object
16 | web3.eth.accounts.wallet.add('0x' + privateKey);
17 | const myWalletAddress = web3.eth.accounts.wallet[0].address;
18 |
19 | // Mainnet Contract for cETH (the collateral-supply process is different for cERC20 tokens)
20 | const cEthAddress = '0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5';
21 | const cEth = new web3.eth.Contract(cEthAbi, cEthAddress);
22 |
23 | // Mainnet Contract for the Compound Protocol's Comptroller
24 | const comptrollerAddress = '0x3d9819210a31b4961b30ef54be2aed79b9c9cd3b';
25 | const comptroller = new web3.eth.Contract(comptrollerAbi, comptrollerAddress);
26 |
27 | // Mainnet Contract for the Open Price Feed
28 | const priceFeedAddress = '0x6d2299c48a8dd07a872fdd0f8233924872ad1071';
29 | const priceFeed = new web3.eth.Contract(priceFeedAbi, priceFeedAddress);
30 |
31 | // Mainnet address of underlying token (like DAI or USDC)
32 | const underlyingAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; // Dai
33 | const underlying = new web3.eth.Contract(erc20Abi, underlyingAddress);
34 |
35 | // Mainnet address for a cToken (like cDai, https://compound.finance/docs#networks)
36 | const cTokenAddress = '0x5d3a536e4d6dbd6114cc1ead35777bab948e3643'; // cDai
37 | const cToken = new web3.eth.Contract(cErcAbi, cTokenAddress);
38 | const assetName = 'DAI'; // for the log output lines
39 | const underlyingDecimals = 18; // Number of decimals defined in this ERC20 token's contract
40 |
41 | // Web3 transaction information, we'll use this for every transaction we'll send
42 | const fromMyWallet = {
43 | from: myWalletAddress,
44 | gasLimit: web3.utils.toHex(400000),
45 | };
46 |
47 | const logBalances = () => {
48 | return new Promise(async (resolve, reject) => {
49 | let myWalletEthBalance = +web3.utils.fromWei(await web3.eth.getBalance(myWalletAddress));
50 | let myWalletCEthBalance = await cEth.methods.balanceOf(myWalletAddress).call() / 1e8;
51 | let myWalletUnderlyingBalance = +await underlying.methods.balanceOf(myWalletAddress).call() / Math.pow(10, underlyingDecimals);
52 |
53 | console.log("My Wallet's ETH Balance:", myWalletEthBalance);
54 | console.log("My Wallet's cETH Balance:", myWalletCEthBalance);
55 | console.log(`My Wallet's ${assetName} Balance:`, myWalletUnderlyingBalance);
56 |
57 | resolve();
58 | });
59 | };
60 |
61 | const main = async () => {
62 | await logBalances();
63 |
64 | const ethToSupplyAsCollateral = 1;
65 |
66 | console.log('\nSupplying ETH to the protocol as collateral (you will get cETH in return)...\n');
67 | let mint = await cEth.methods.mint().send({
68 | from: myWalletAddress,
69 | gasLimit: web3.utils.toHex(175000),
70 | value: web3.utils.toHex(ethToSupplyAsCollateral * 1e18)
71 | });
72 |
73 | await logBalances();
74 |
75 | console.log('\nEntering market (via Comptroller contract) for ETH (as collateral)...');
76 | let markets = [cEthAddress]; // This is the cToken contract(s) for your collateral
77 | let enterMarkets = await comptroller.methods.enterMarkets(markets).send(fromMyWallet);
78 |
79 | console.log('Calculating your liquid assets in the protocol...');
80 | let { 1:liquidity } = await comptroller.methods.getAccountLiquidity(myWalletAddress).call();
81 | liquidity = liquidity / 1e18;
82 |
83 | console.log('Fetching cETH collateral factor...');
84 | let { 1:collateralFactor } = await comptroller.methods.markets(cEthAddress).call();
85 | collateralFactor = (collateralFactor / 1e18) * 100; // Convert to percent
86 |
87 | console.log(`Fetching ${assetName} price from the price feed...`);
88 | let underlyingPriceInUsd = await priceFeed.methods.price(assetName).call();
89 | underlyingPriceInUsd = underlyingPriceInUsd / 1e6; // Price feed provides price in USD with 6 decimal places
90 |
91 | console.log(`Fetching borrow rate per block for ${assetName} borrowing...`);
92 | let borrowRate = await cToken.methods.borrowRatePerBlock().call();
93 | borrowRate = borrowRate / Math.pow(10, underlyingDecimals);
94 |
95 | console.log(`\nYou have ${liquidity} of LIQUID assets (worth of USD) pooled in the protocol.`);
96 | console.log(`You can borrow up to ${collateralFactor}% of your TOTAL collateral supplied to the protocol as ${assetName}.`);
97 | console.log(`1 ${assetName} == ${underlyingPriceInUsd.toFixed(6)} USD`);
98 | console.log(`You can borrow up to ${liquidity/underlyingPriceInUsd} ${assetName} from the protocol.`);
99 | console.log(`NEVER borrow near the maximum amount because your account will be instantly liquidated.`);
100 | console.log(`\nYour borrowed amount INCREASES (${borrowRate} * borrowed amount) ${assetName} per block.\nThis is based on the current borrow rate.\n`);
101 |
102 | const underlyingToBorrow = 50;
103 | console.log(`Now attempting to borrow ${underlyingToBorrow} ${assetName}...`);
104 | const scaledUpBorrowAmount = (underlyingToBorrow * Math.pow(10, underlyingDecimals)).toString();
105 | const trx = await cToken.methods.borrow(scaledUpBorrowAmount).send(fromMyWallet);
106 | // console.log('Borrow Transaction', trx);
107 |
108 | await logBalances();
109 |
110 | console.log(`\nFetching ${assetName} borrow balance from c${assetName} contract...`);
111 | let balance = await cToken.methods.borrowBalanceCurrent(myWalletAddress).call();
112 | balance = balance / Math.pow(10, underlyingDecimals);
113 | console.log(`Borrow balance is ${balance} ${assetName}`);
114 |
115 | console.log(`\nThis part is when you do something with those borrowed assets!\n`);
116 |
117 | console.log(`Now repaying the borrow...`);
118 | console.log(`Approving ${assetName} to be transferred from your wallet to the c${assetName} contract...`);
119 | const underlyingToRepay = (underlyingToBorrow * Math.pow(10, underlyingDecimals)).toString();
120 | await underlying.methods.approve(cTokenAddress, underlyingToRepay).send(fromMyWallet);
121 |
122 | const repayBorrow = await cToken.methods.repayBorrow(underlyingToRepay).send(fromMyWallet);
123 |
124 | if (repayBorrow.events && repayBorrow.events.Failure) {
125 | const errorCode = repayBorrow.events.Failure.returnValues.error;
126 | console.error(`repayBorrow error, code ${errorCode}`);
127 | process.exit(1);
128 | }
129 |
130 | console.log(`\nBorrow repaid.\n`);
131 | await logBalances();
132 | };
133 |
134 | main().catch((err) => {
135 | console.error('ERROR:', err);
136 | });
137 |
--------------------------------------------------------------------------------
/examples-js/web3-js/borrow-eth-with-erc20-collateral.js:
--------------------------------------------------------------------------------
1 | // Example to supply a supported ERC20 token as collateral and borrow ETH
2 | // YOU MUST HAVE DAI IN YOUR WALLET before you run this script
3 | const Web3 = require('web3');
4 | const web3 = new Web3('http://127.0.0.1:8545');
5 | const {
6 | cEthAbi,
7 | comptrollerAbi,
8 | priceFeedAbi,
9 | cErcAbi,
10 | erc20Abi,
11 | } = require('../../contracts.json');
12 |
13 | // Your Ethereum wallet private key
14 | const privateKey = 'b8c1b5c1d81f9475fdf2e334517d29f733bdfa40682207571b12fc1142cbf329';
15 |
16 | // Add your Ethereum wallet to the Web3 object
17 | web3.eth.accounts.wallet.add('0x' + privateKey);
18 | const myWalletAddress = web3.eth.accounts.wallet[0].address;
19 |
20 | // Mainnet Contract for cETH
21 | const cEthAddress = '0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5';
22 | const cEth = new web3.eth.Contract(cEthAbi, cEthAddress);
23 |
24 | // Mainnet Contract for Compound's Comptroller
25 | const comptrollerAddress = '0x3d9819210a31b4961b30ef54be2aed79b9c9cd3b';
26 | const comptroller = new web3.eth.Contract(comptrollerAbi, comptrollerAddress);
27 |
28 | // Mainnet Contract for the Open Price Feed
29 | const priceFeedAddress = '0x6d2299c48a8dd07a872fdd0f8233924872ad1071';
30 | const priceFeed = new web3.eth.Contract(priceFeedAbi, priceFeedAddress);
31 |
32 | // Mainnet address of underlying token (like DAI or USDC)
33 | const underlyingAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; // Dai
34 | const underlying = new web3.eth.Contract(erc20Abi, underlyingAddress);
35 |
36 | // Mainnet address for a cToken (like cDai, https://compound.finance/docs#networks)
37 | const cTokenAddress = '0x5d3a536e4d6dbd6114cc1ead35777bab948e3643'; // cDai
38 | const cToken = new web3.eth.Contract(cErcAbi, cTokenAddress);
39 | const assetName = 'DAI'; // for the log output lines
40 | const underlyingDecimals = 18; // Number of decimals defined in this ERC20 token's contract
41 |
42 | // Web3 transaction information, we'll use this for every transaction we'll send
43 | const fromMyWallet = {
44 | from: myWalletAddress,
45 | gasLimit: web3.utils.toHex(500000),
46 | gasPrice: web3.utils.toHex(20000000000) // use ethgasstation.info (mainnet only)
47 | };
48 |
49 | const logBalances = () => {
50 | return new Promise(async (resolve, reject) => {
51 | let myWalletEthBalance = +web3.utils.fromWei(await web3.eth.getBalance(myWalletAddress));
52 | let myWalletCTokenBalance = await cToken.methods.balanceOf(myWalletAddress).call() / 1e8;
53 | let myWalletUnderlyingBalance = +await underlying.methods.balanceOf(myWalletAddress).call() / 1e18;
54 |
55 | console.log(`My Wallet's ETH Balance:`, myWalletEthBalance);
56 | console.log(`My Wallet's c${assetName} Balance:`, myWalletCTokenBalance);
57 | console.log(`My Wallet's ${assetName} Balance:`, myWalletUnderlyingBalance);
58 |
59 | resolve();
60 | });
61 | };
62 |
63 | const main = async () => {
64 | await logBalances();
65 |
66 | let underlyingAsCollateral = 15;
67 |
68 | // Convert the token amount to a scaled up number, then a string.
69 | underlyingAsCollateral = underlyingAsCollateral * Math.pow(10, underlyingDecimals);
70 | underlyingAsCollateral = underlyingAsCollateral.toString();
71 |
72 | console.log(`\nApproving ${assetName} to be transferred from your wallet to the c${assetName} contract...\n`);
73 | await underlying.methods.approve(cTokenAddress, underlyingAsCollateral).send(fromMyWallet);
74 |
75 | console.log(`Supplying ${assetName} to the protocol as collateral (you will get c${assetName} in return)...\n`);
76 | let mint = await cToken.methods.mint(underlyingAsCollateral).send(fromMyWallet);
77 |
78 | if (mint.events && mint.events.Failure) {
79 | throw new Error(
80 | `See https://compound.finance/docs/ctokens#ctoken-error-codes\n` +
81 | `Code: ${mint.events.Failure.returnValues[0]}\n`
82 | );
83 | }
84 |
85 | await logBalances();
86 |
87 | console.log('\nEntering market (via Comptroller contract) for ETH (as collateral)...');
88 | let markets = [cTokenAddress]; // This is the cToken contract(s) for your collateral
89 | let enterMarkets = await comptroller.methods.enterMarkets(markets).send(fromMyWallet);
90 |
91 | console.log('Calculating your liquid assets in the protocol...');
92 | let {1:liquidity} = await comptroller.methods.getAccountLiquidity(myWalletAddress).call();
93 | liquidity = web3.utils.fromWei(liquidity).toString();
94 |
95 | console.log(`Fetching the protocol's ${assetName} collateral factor...`);
96 | let {1:collateralFactor} = await comptroller.methods.markets(cTokenAddress).call();
97 | collateralFactor = (collateralFactor / Math.pow(10, underlyingDecimals)) * 100; // Convert to percent
98 |
99 | console.log(`Fetching ${assetName} price from the price feed...`);
100 | let underlyingPriceInUsd = await priceFeed.methods.price(assetName).call();
101 | underlyingPriceInUsd = underlyingPriceInUsd / 1e6; // Price feed provides price in USD with 6 decimal places
102 |
103 | console.log('Fetching borrow rate per block for ETH borrowing...');
104 | let borrowRate = await cEth.methods.borrowRatePerBlock().call();
105 | borrowRate = borrowRate / 1e18;
106 |
107 | console.log(`\nYou have ${liquidity} of LIQUID assets (worth of USD) pooled in the protocol.`);
108 | console.log(`You can borrow up to ${collateralFactor}% of your TOTAL assets supplied to the protocol as ETH.`);
109 | console.log(`1 ${assetName} == ${underlyingPriceInUsd.toFixed(6)} USD`);
110 | console.log(`You can borrow up to ${liquidity} USD worth of assets from the protocol.`);
111 | console.log(`NEVER borrow near the maximum amount because your account will be instantly liquidated.`);
112 | console.log(`\nYour borrowed amount INCREASES (${borrowRate} * borrowed amount) ETH per block.\nThis is based on the current borrow rate.`);
113 |
114 | // Let's try to borrow 0.002 ETH (or another amount far below the borrow limit)
115 | const ethToBorrow = 0.002;
116 | console.log(`\nNow attempting to borrow ${ethToBorrow} ETH...`);
117 | const borrowResult = await cEth.methods.borrow(web3.utils.toWei(ethToBorrow.toString(), 'ether')).send(fromMyWallet);
118 |
119 | if (isNaN(borrowResult)) {
120 | console.log(`\nETH borrow successful.\n`);
121 | } else {
122 | throw new Error(
123 | `See https://compound.finance/docs/ctokens#ctoken-error-codes\n` +
124 | `Code: ${borrowResult}\n`
125 | );
126 | }
127 |
128 | await logBalances();
129 |
130 | console.log('\nFetching your ETH borrow balance from cETH contract...');
131 | let balance = await cEth.methods.borrowBalanceCurrent(myWalletAddress).call();
132 | balance = balance / 1e18; // because DAI is a 1e18 scaled token.
133 | console.log(`Borrow balance is ${balance} ETH`);
134 |
135 | console.log(`\nThis part is when you do something with those borrowed assets!\n`);
136 |
137 | console.log(`Now repaying the borrow...`);
138 |
139 | const ethToRepay = ethToBorrow;
140 | const repayBorrow = await cEth.methods.repayBorrow().send({
141 | from: myWalletAddress,
142 | gasLimit: web3.utils.toHex(600000),
143 | gasPrice: web3.utils.toHex(20000000000), // use ethgasstation.info (mainnet only)
144 | value: web3.utils.toWei(ethToRepay.toString(), 'ether')
145 | });
146 |
147 | if (repayBorrow.events && repayBorrow.events.Failure) {
148 | const errorCode = repayBorrow.events.Failure.returnValues.error;
149 | console.error(`repayBorrow error, code ${errorCode}`);
150 | process.exit(1);
151 | }
152 |
153 | console.log(`\nBorrow repaid.\n`);
154 | await logBalances();
155 | };
156 |
157 | main().catch((err) => {
158 | console.error('ERROR:', err);
159 | });
160 |
--------------------------------------------------------------------------------
/examples-solidity/compound-js/borrow-erc20-via-solidity.js:
--------------------------------------------------------------------------------
1 | // Example to borrow DAI (or any ERC20 token) using ETH as collateral
2 | // from a Solidity smart contract
3 | const Compound = require('@compound-finance/compound-js');
4 | const providerUrl = 'http://localhost:8545';
5 |
6 | // Your Ethereum wallet private key
7 | const myWalletAddress = '0xa0df350d2637096571F7A701CBc1C5fdE30dF76A';
8 | const privateKey = 'b8c1b5c1d81f9475fdf2e334517d29f733bdfa40682207571b12fc1142cbf329';
9 |
10 | // Mainnet Contracts for the Compound Protocol
11 | const comptrollerAddress = Compound.util.getAddress(Compound.Comptroller);
12 | const priceFeedAddress = Compound.util.getAddress(Compound.PriceFeed);
13 | const cEthAddress = Compound.util.getAddress(Compound.cETH);
14 |
15 | const assetName = Compound.DAI;
16 | const underlyingAddress = Compound.util.getAddress(assetName);
17 | const cTokenAddress = Compound.util.getAddress('c' + assetName);
18 | const underlyingDecimals = Compound.decimals[assetName]; // Number of decimals defined in this ERC20 token's contract
19 |
20 | // MyContract
21 | const myContractAddress = '0x0Bb909b7c3817F8fB7188e8fbaA2763028956E30';
22 |
23 | const logBalances = () => {
24 | return new Promise(async (resolve, reject) => {
25 | let myWalletEthBalance = +(await Compound.eth.getBalance(myWalletAddress, providerUrl)) / 1e18;
26 | let myContractEthBalance = +(await Compound.eth.getBalance(myContractAddress, providerUrl)) / 1e18;
27 | let myContractCEthBalance = await _balanceOf(Compound.cETH, myContractAddress);
28 | let myContractUnderlyingBalance = await _balanceOf(assetName, myContractAddress);
29 |
30 | console.log("My Wallet's ETH Balance:", myWalletEthBalance);
31 | console.log("MyContract's ETH Balance:", myContractEthBalance);
32 | console.log("MyContract's cETH Balance:", myContractCEthBalance);
33 | console.log(`MyContract's ${assetName} Balance:`, myContractUnderlyingBalance);
34 |
35 | resolve();
36 | });
37 | };
38 |
39 | const main = async () => {
40 | const ethersProvider = new Compound._ethers.providers.JsonRpcProvider(providerUrl);
41 | const contractIsDeployed = (await ethersProvider.getCode(myContractAddress)) !== '0x';
42 |
43 | if (!contractIsDeployed) {
44 | throw Error('MyContract is not deployed! Deploy it by running the deploy script.');
45 | }
46 |
47 | await logBalances();
48 |
49 | const ethToSupplyAsCollateral = 1;
50 |
51 | console.log(`\nCalling MyContract.borrowErc20Example with ${ethToSupplyAsCollateral} ETH for collateral...\n`);
52 | let tx = await _borrowErc20Example(
53 | myContractAddress,
54 | cEthAddress,
55 | comptrollerAddress,
56 | priceFeedAddress,
57 | cTokenAddress,
58 | underlyingDecimals,
59 | (ethToSupplyAsCollateral * 1e18).toString()
60 | );
61 | let result = await tx.wait(1);
62 |
63 | // See the solidity functions logs from "MyLog" event
64 | // console.log(JSON.stringify(result.events));
65 |
66 | await logBalances();
67 |
68 | console.log(`\nNow repaying the borrow...\n`);
69 | const underlyingToRepayBorrow = 10;
70 | tx = await _myErc20RepayBorrow(
71 | myContractAddress,
72 | underlyingAddress,
73 | cTokenAddress,
74 | (underlyingToRepayBorrow * Math.pow(10, underlyingDecimals)).toString()
75 | );
76 | result = tx.wait(1);
77 |
78 | await logBalances();
79 | };
80 |
81 | main().catch(async (err) => {
82 | console.error('ERROR:', err);
83 |
84 | // Create "events" and "emit" them in your Solidity code.
85 | // Current contract does not have any.
86 | let logs = await myContract.getPastEvents('allEvents');
87 | console.log('Logs: ', logs);
88 | });
89 |
90 | async function _balanceOf(asset, account) {
91 | const assetAddress = Compound.util.getAddress(Compound[asset]);
92 | let balance;
93 | try {
94 | balance = await Compound.eth.read(
95 | assetAddress,
96 | 'function balanceOf(address) returns (uint)',
97 | [ account ],
98 | { provider: providerUrl }
99 | );
100 | } catch(error) {
101 | console.error(error);
102 | }
103 |
104 | return +balance / Math.pow(10, Compound.decimals[asset]);
105 | }
106 |
107 | async function _borrowErc20Example(
108 | _myContractAddress,
109 | _cEthAddress,
110 | _comptrollerAddress,
111 | _priceFeedAddress,
112 | _cTokenAddress,
113 | _underlyingDecimals,
114 | ethAmount
115 | ) {
116 | let tx;
117 | try {
118 | tx = await Compound.eth.trx(
119 | _myContractAddress,
120 | 'function borrowErc20Example(address payable, address, address, address, uint) public payable returns (uint)',
121 | [
122 | _cEthAddress,
123 | _comptrollerAddress,
124 | _priceFeedAddress,
125 | _cTokenAddress,
126 | _underlyingDecimals
127 | ],
128 | {
129 | value: ethAmount,
130 | provider: providerUrl,
131 | privateKey
132 | }
133 | );
134 | } catch(error) {
135 | console.error(error);
136 | }
137 |
138 | return tx;
139 | }
140 |
141 | async function _myErc20RepayBorrow(
142 | _myContractAddress,
143 | _underlyingAddress,
144 | _cTokenAddress,
145 | _underlyingToRepayBorrow
146 | ) {
147 | let tx;
148 | try {
149 | tx = await Compound.eth.trx(
150 | _myContractAddress,
151 | 'function myErc20RepayBorrow(address, address, uint) public returns (bool)',
152 | [ _underlyingAddress, _cTokenAddress, _underlyingToRepayBorrow ],
153 | { provider: providerUrl, privateKey }
154 | );
155 | } catch(error) {
156 | console.error(error);
157 | }
158 |
159 | return tx;
160 | }
161 |
--------------------------------------------------------------------------------
/examples-solidity/compound-js/borrow-eth-via-solidity.js:
--------------------------------------------------------------------------------
1 | // Example to supply DAI as collateral and borrow ETH
2 | // YOU MUST HAVE DAI IN YOUR WALLET before you run this script
3 | const Compound = require('@compound-finance/compound-js');
4 | const providerUrl = 'http://localhost:8545';
5 |
6 | // Your Ethereum wallet private key
7 | const myWalletAddress = '0xa0df350d2637096571F7A701CBc1C5fdE30dF76A';
8 | const privateKey = 'b8c1b5c1d81f9475fdf2e334517d29f733bdfa40682207571b12fc1142cbf329';
9 |
10 | // Mainnet Contracts for the Compound Protocol
11 | const comptrollerAddress = Compound.util.getAddress(Compound.Comptroller);
12 | const priceFeedAddress = Compound.util.getAddress(Compound.PriceFeed);
13 | const cEthAddress = Compound.util.getAddress(Compound.cETH);
14 |
15 | const assetName = Compound.DAI;
16 | const cTokenName = 'c' + assetName;
17 | const underlyingAddress = Compound.util.getAddress(assetName);
18 | const cTokenAddress = Compound.util.getAddress('c' + assetName);
19 | const underlyingDecimals = Compound.decimals[assetName]; // Number of decimals defined in this ERC20 token's contract
20 |
21 | // MyContract
22 | const myContractAddress = '0x0Bb909b7c3817F8fB7188e8fbaA2763028956E30';
23 |
24 | const logBalances = () => {
25 | return new Promise(async (resolve, reject) => {
26 | const myWalletUnderlyingBalance = await _balanceOf(assetName, myWalletAddress);
27 | const myContractEthBalance = +(await Compound.eth.getBalance(myContractAddress, providerUrl)) / 1e18;
28 | const myContractCEthBalance = await _balanceOf(Compound.cETH, myContractAddress);
29 | const myContractUnderlyingBalance = await _balanceOf(assetName, myContractAddress);
30 | const myContractCTokenBalance = await _balanceOf(cTokenName, myContractAddress);
31 |
32 | console.log(`My Wallet's ${assetName} Balance:`, myWalletUnderlyingBalance);
33 | console.log(`MyContract's ETH Balance:`, myContractEthBalance);
34 | console.log(`MyContract's cETH Balance:`, myContractCEthBalance);
35 | console.log(`MyContract's ${assetName} Balance:`, myContractUnderlyingBalance);
36 | console.log(`MyContract's c${assetName} Balance:`, myContractCTokenBalance);
37 |
38 | resolve();
39 | });
40 | };
41 |
42 | const main = async () => {
43 | const ethersProvider = new Compound._ethers.providers.JsonRpcProvider(providerUrl);
44 | const contractIsDeployed = (await ethersProvider.getCode(myContractAddress)) !== '0x';
45 |
46 | if (!contractIsDeployed) {
47 | throw Error('MyContract is not deployed! Deploy it by running the deploy script.');
48 | }
49 |
50 | await logBalances();
51 |
52 | const underlyingAsCollateral = 25;
53 | const mantissa = (underlyingAsCollateral * Math.pow(10, underlyingDecimals)).toString();
54 | console.log(`\nSending ${underlyingAsCollateral} ${assetName} to MyContract so it can provide collateral...\n`);
55 |
56 | // Send underlying to MyContract before attempting the supply
57 | let tx = await _transfer(underlyingAddress, myContractAddress, mantissa);
58 | await tx.wait(1);
59 |
60 | await logBalances();
61 |
62 | console.log(`\nCalling MyContract.borrowEthExample with ${underlyingAsCollateral} ${assetName} as collateral...\n`);
63 |
64 | tx = await _borrowEthExample(
65 | myContractAddress,
66 | cEthAddress,
67 | comptrollerAddress,
68 | cTokenAddress,
69 | underlyingAddress,
70 | mantissa
71 | );
72 | let result = await tx.wait(1);
73 |
74 | // See the solidity functions logs from "MyLog" event
75 | // console.log(JSON.stringify(result), '\n');
76 |
77 | await logBalances();
78 |
79 | console.log(`\nNow repaying the borrow...\n`);
80 | const ethToRepayBorrow = 0.002; // hard coded borrow in contract
81 | tx = await _myEthRepayBorrow(
82 | myContractAddress,
83 | cEthAddress,
84 | (ethToRepayBorrow * 1e18),
85 | 300000 // gas for the "cEth.repayBorrow" function
86 | );
87 | await tx.wait(1);
88 |
89 | await logBalances();
90 | };
91 |
92 | main().catch(console.error);
93 |
94 | async function _balanceOf(asset, account) {
95 | const assetAddress = Compound.util.getAddress(Compound[asset]);
96 | let balance;
97 | try {
98 | balance = await Compound.eth.read(
99 | assetAddress,
100 | 'function balanceOf(address) returns (uint)',
101 | [ account ],
102 | { provider: providerUrl }
103 | );
104 | } catch(error) {
105 | console.error(error);
106 | }
107 |
108 | return +balance / Math.pow(10, Compound.decimals[asset]);
109 | }
110 |
111 | async function _transfer(
112 | _erc20Address,
113 | _recipientAddress,
114 | _amountScaledUp,
115 | ) {
116 | let tx;
117 | try {
118 | tx = await Compound.eth.trx(
119 | _erc20Address,
120 | 'function transfer(address, uint) public returns (bool)',
121 | [ _recipientAddress, _amountScaledUp ],
122 | { provider: providerUrl, privateKey }
123 | );
124 | } catch(error) {
125 | console.error(error);
126 | }
127 |
128 | return tx;
129 | }
130 |
131 | async function _borrowEthExample(
132 | _myContractAddress,
133 | _cEthAddress,
134 | _comptrollerAddress,
135 | _cTokenAddress,
136 | _underlyingAddress,
137 | _mantissa
138 | ) {
139 | let tx;
140 | try {
141 | tx = await Compound.eth.trx(
142 | _myContractAddress,
143 | 'function borrowEthExample(address payable, address, address, address, uint) public returns (uint)',
144 | [
145 | _cEthAddress,
146 | _comptrollerAddress,
147 | _cTokenAddress,
148 | _underlyingAddress,
149 | _mantissa,
150 | ],
151 | { provider: providerUrl, privateKey }
152 | );
153 | } catch(error) {
154 | console.error(error);
155 | }
156 |
157 | return tx;
158 | }
159 |
160 | async function _myEthRepayBorrow(
161 | _myContractAddress,
162 | _cEthAddress,
163 | _ethToRepayBorrow,
164 | _gas,
165 | ) {
166 | let tx;
167 | try {
168 | tx = await Compound.eth.trx(
169 | _myContractAddress,
170 | 'function myEthRepayBorrow(address, uint, uint) public returns (bool)',
171 | [ _cEthAddress, _ethToRepayBorrow, _gas ],
172 | { provider: providerUrl, privateKey }
173 | );
174 | } catch(error) {
175 | console.error(error);
176 | }
177 |
178 | return tx;
179 | }
180 |
--------------------------------------------------------------------------------
/examples-solidity/ethers-js/borrow-erc20-via-solidity.js:
--------------------------------------------------------------------------------
1 | // Example to borrow DAI (or any ERC20 token) using ETH as collateral
2 | // from a Solidity smart contract
3 | const ethers = require('ethers');
4 | const provider = new ethers.providers.JsonRpcProvider('http://localhost:8545');
5 | const {
6 | cEthAbi,
7 | cErcAbi,
8 | erc20Abi,
9 | } = require('../../contracts.json');
10 |
11 | // Your Ethereum wallet private key
12 | const privateKey = 'b8c1b5c1d81f9475fdf2e334517d29f733bdfa40682207571b12fc1142cbf329';
13 | const wallet = new ethers.Wallet(privateKey, provider);
14 | const myWalletAddress = wallet.address;
15 |
16 | // Mainnet Contract for cETH (the collateral-supply process is different for cERC20 tokens)
17 | const cEthAddress = '0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5';
18 | const cEth = new ethers.Contract(cEthAddress, cEthAbi, wallet);
19 |
20 | // Mainnet Contract for the Comptroller & Open Price Feed
21 | const comptrollerAddress = '0x3d9819210a31b4961b30ef54be2aed79b9c9cd3b';
22 | const priceFeedAddress = '0x6d2299c48a8dd07a872fdd0f8233924872ad1071';
23 |
24 | // Mainnet address of underlying token (like DAI or USDC)
25 | const underlyingAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; // Dai
26 | const underlying = new ethers.Contract(underlyingAddress, erc20Abi, wallet);
27 |
28 | // Mainnet address for a cToken (like cDai, https://compound.finance/docs#networks)
29 | const cTokenAddress = '0x5d3a536e4d6dbd6114cc1ead35777bab948e3643'; // cDai
30 | const cToken = new ethers.Contract(cTokenAddress, cErcAbi, wallet);
31 | const assetName = 'DAI'; // for the log output lines
32 | const underlyingDecimals = 18; // Number of decimals defined in this ERC20 token's contract
33 |
34 | // MyContract
35 | const myContractAbi = require('../../artifacts/contracts/MyContracts.sol/MyContract.json').abi;
36 | const myContractAddress = '0x0Bb909b7c3817F8fB7188e8fbaA2763028956E30';
37 | const myContract = new ethers.Contract(myContractAddress, myContractAbi, wallet);
38 |
39 | const logBalances = () => {
40 | return new Promise(async (resolve, reject) => {
41 | let myWalletEthBalance = await provider.getBalance(myWalletAddress) / 1e18;
42 | let myContractEthBalance = await provider.getBalance(myContractAddress) / 1e18;
43 | let myContractCEthBalance = await cEth.callStatic.balanceOf(myContractAddress) / 1e8;
44 | let myContractUnderlyingBalance = await underlying.callStatic.balanceOf(myContractAddress) / Math.pow(10, underlyingDecimals);
45 |
46 | console.log("My Wallet's ETH Balance:", myWalletEthBalance);
47 | console.log("MyContract's ETH Balance:", myContractEthBalance);
48 | console.log("MyContract's cETH Balance:", myContractCEthBalance);
49 | console.log(`MyContract's ${assetName} Balance:`, myContractUnderlyingBalance);
50 |
51 | resolve();
52 | });
53 | };
54 |
55 | const main = async () => {
56 | const contractIsDeployed = (await provider.getCode(myContractAddress)) !== '0x';
57 |
58 | if (!contractIsDeployed) {
59 | throw Error('MyContract is not deployed! Deploy it by running the deploy script.');
60 | }
61 |
62 | await logBalances();
63 |
64 | const ethToSupplyAsCollateral = 1;
65 |
66 | console.log(`\nCalling MyContract.borrowErc20Example with ${ethToSupplyAsCollateral} ETH for collateral...\n`);
67 | let borrowTx = await myContract.borrowErc20Example(
68 | cEthAddress,
69 | comptrollerAddress,
70 | priceFeedAddress,
71 | cTokenAddress,
72 | underlyingDecimals,
73 | { value: (ethToSupplyAsCollateral * 1e18).toString() }
74 | );
75 | let result = await borrowTx.wait(1);
76 |
77 | // See the solidity functions logs from "MyLog" event
78 | // console.log(JSON.stringify(result.events));
79 |
80 | await logBalances();
81 |
82 | console.log(`\nNow repaying the borrow...\n`);
83 | const underlyingToRepayBorrow = 10;
84 | const repayTx = await myContract.myErc20RepayBorrow(
85 | underlyingAddress,
86 | cTokenAddress,
87 | (underlyingToRepayBorrow * Math.pow(10, underlyingDecimals)).toString()
88 | );
89 | result = repayTx.wait(1);
90 |
91 | await logBalances();
92 | };
93 |
94 | main().catch(async (err) => {
95 | console.error('ERROR:', err);
96 |
97 | // Create "events" and "emit" them in your Solidity code.
98 | // Current contract does not have any.
99 | let logs = await myContract.getPastEvents('allEvents');
100 | console.log('Logs: ', logs);
101 | });
102 |
--------------------------------------------------------------------------------
/examples-solidity/ethers-js/borrow-eth-via-solidity.js:
--------------------------------------------------------------------------------
1 | // Example to supply DAI as collateral and borrow ETH
2 | // YOU MUST HAVE DAI IN YOUR WALLET before you run this script
3 | const ethers = require('ethers');
4 | const provider = new ethers.providers.JsonRpcProvider('http://localhost:8545');
5 | const {
6 | cEthAbi,
7 | cErcAbi,
8 | erc20Abi,
9 | } = require('../../contracts.json');
10 |
11 | // Your Ethereum wallet private key
12 | const privateKey = 'b8c1b5c1d81f9475fdf2e334517d29f733bdfa40682207571b12fc1142cbf329';
13 | const wallet = new ethers.Wallet(privateKey, provider);
14 | const myWalletAddress = wallet.address;
15 |
16 | // Mainnet Contract for the Comptroller
17 | const comptrollerAddress = '0x3d9819210a31b4961b30ef54be2aed79b9c9cd3b';
18 |
19 | // Mainnet Contract for cETH
20 | const cEthAddress = '0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5';
21 | const cEth = new ethers.Contract(cEthAddress, cEthAbi, wallet);
22 |
23 | // Mainnet address of underlying token (like DAI or USDC)
24 | const underlyingAddress = '0x6b175474e89094c44da98b954eedeac495271d0f'; // Dai
25 | const underlying = new ethers.Contract(underlyingAddress, erc20Abi, wallet);
26 |
27 | // Mainnet address for a cToken (like cDai, https://compound.finance/docs#networks)
28 | const cTokenAddress = '0x5d3a536e4d6dbd6114cc1ead35777bab948e3643'; // cDai
29 | const cToken = new ethers.Contract(cTokenAddress, cErcAbi, wallet);
30 | const assetName = 'DAI'; // for the log output lines
31 | const underlyingDecimals = 18; // Number of decimals defined in this ERC20 token's contract
32 |
33 | // MyContract
34 | const myContractAbi = require('../../artifacts/contracts/MyContracts.sol/MyContract.json').abi;
35 | const myContractAddress = '0x0Bb909b7c3817F8fB7188e8fbaA2763028956E30';
36 | const myContract = new ethers.Contract(myContractAddress, myContractAbi, wallet);
37 |
38 | const logBalances = () => {
39 | return new Promise(async (resolve, reject) => {
40 | const myWalletUnderlyingBalance = await underlying.callStatic.balanceOf(myWalletAddress) / Math.pow(10, underlyingDecimals);
41 | const myContractEthBalance = await provider.getBalance(myContractAddress) / 1e18;
42 | const myContractCEthBalance = await cEth.callStatic.balanceOf(myContractAddress) / 1e8;
43 | const myContractUnderlyingBalance = await underlying.callStatic.balanceOf(myContractAddress) / Math.pow(10, underlyingDecimals);
44 | const myContractCTokenBalance = await cToken.callStatic.balanceOf(myContractAddress) / 1e8;
45 |
46 | console.log(`My Wallet's ${assetName} Balance:`, myWalletUnderlyingBalance);
47 | console.log(`MyContract's ETH Balance:`, myContractEthBalance);
48 | console.log(`MyContract's cETH Balance:`, myContractCEthBalance);
49 | console.log(`MyContract's ${assetName} Balance:`, myContractUnderlyingBalance);
50 | console.log(`MyContract's c${assetName} Balance:`, myContractCTokenBalance);
51 |
52 | resolve();
53 | });
54 | };
55 |
56 | const main = async () => {
57 | const contractIsDeployed = (await provider.getCode(myContractAddress)) !== '0x';
58 |
59 | if (!contractIsDeployed) {
60 | throw Error('MyContract is not deployed! Deploy it by running the deploy script.');
61 | }
62 |
63 | await logBalances();
64 |
65 | const underlyingAsCollateral = 25;
66 | const mantissa = (underlyingAsCollateral * Math.pow(10, underlyingDecimals)).toString();
67 | console.log(`\nSending ${underlyingAsCollateral} ${assetName} to MyContract so it can provide collateral...\n`);
68 |
69 | // Send underlying to MyContract before attempting the supply
70 | const transferTx = await underlying.transfer(myContractAddress, mantissa);
71 | await transferTx.wait(1);
72 |
73 | await logBalances();
74 |
75 | console.log(`\nCalling MyContract.borrowEthExample with ${underlyingAsCollateral} ${assetName} as collateral...\n`);
76 |
77 | const borrowTx = await myContract.borrowEthExample(
78 | cEthAddress,
79 | comptrollerAddress,
80 | cTokenAddress,
81 | underlyingAddress,
82 | mantissa
83 | );
84 | let result = await borrowTx.wait(1);
85 |
86 | // See the solidity functions logs from "MyLog" event
87 | // console.log(JSON.stringify(result), '\n');
88 |
89 | await logBalances();
90 |
91 | console.log(`\nNow repaying the borrow...\n`);
92 | const ethToRepayBorrow = 0.002; // hard coded borrow in contract
93 | const repayTx = await myContract.myEthRepayBorrow(
94 | cEthAddress,
95 | ethers.utils.parseEther(ethToRepayBorrow.toString()),
96 | 300000 // gas for the "cEth.repayBorrow" function
97 | );
98 | await repayTx.wait(1);
99 |
100 | await logBalances();
101 | };
102 |
103 | main().catch(console.error);
104 |
--------------------------------------------------------------------------------
/examples-solidity/web3-js/borrow-erc20-via-solidity.js:
--------------------------------------------------------------------------------
1 | // Example to borrow DAI (or any ERC20 token) using ETH as collateral
2 | // from a Solidity smart contract
3 | const Web3 = require('web3');
4 | const web3 = new Web3('http://127.0.0.1:8545');
5 | const {
6 | cEthAbi,
7 | cErcAbi,
8 | erc20Abi,
9 | } = require('../../contracts.json');
10 |
11 | // Your Ethereum wallet private key
12 | const privateKey = 'b8c1b5c1d81f9475fdf2e334517d29f733bdfa40682207571b12fc1142cbf329';
13 |
14 | // Add your Ethereum wallet to the Web3 object
15 | web3.eth.accounts.wallet.add('0x' + privateKey);
16 | const myWalletAddress = web3.eth.accounts.wallet[0].address;
17 |
18 | // Mainnet Contract for cETH (the collateral-supply process is different for cERC20 tokens)
19 | const cEthAddress = '0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5';
20 | const cEth = new web3.eth.Contract(cEthAbi, cEthAddress);
21 |
22 | // Mainnet Contract for the Comptroller & Open Price Feed
23 | const comptrollerAddress = '0x3d9819210a31b4961b30ef54be2aed79b9c9cd3b';
24 | const priceFeedAddress = '0x6d2299c48a8dd07a872fdd0f8233924872ad1071';
25 |
26 | // Mainnet address of underlying token (like DAI or USDC)
27 | const underlyingAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; // Dai
28 | const underlying = new web3.eth.Contract(erc20Abi, underlyingAddress);
29 |
30 | // Mainnet address for a cToken (like cDai, https://compound.finance/docs#networks)
31 | const cTokenAddress = '0x5d3a536e4d6dbd6114cc1ead35777bab948e3643'; // cDai
32 | const cToken = new web3.eth.Contract(cErcAbi, cTokenAddress);
33 | const assetName = 'DAI'; // for the log output lines
34 | const underlyingDecimals = 18; // Number of decimals defined in this ERC20 token's contract
35 |
36 | // MyContract
37 | const myContractAbi = require('../../artifacts/contracts/MyContracts.sol/MyContract.json').abi;
38 | const myContractAddress = '0x0Bb909b7c3817F8fB7188e8fbaA2763028956E30';
39 | const myContract = new web3.eth.Contract(myContractAbi, myContractAddress);
40 |
41 | const logBalances = () => {
42 | return new Promise(async (resolve, reject) => {
43 | let myWalletEthBalance = +web3.utils.fromWei(await web3.eth.getBalance(myWalletAddress));
44 | let myContractEthBalance = +web3.utils.fromWei(await web3.eth.getBalance(myContractAddress));
45 | let myContractCEthBalance = await cEth.methods.balanceOf(myContractAddress).call() / 1e8;
46 | let myContractUnderlyingBalance = +await underlying.methods.balanceOf(myContractAddress).call() / Math.pow(10, underlyingDecimals);
47 |
48 | console.log("My Wallet's ETH Balance:", myWalletEthBalance);
49 | console.log("MyContract's ETH Balance:", myContractEthBalance);
50 | console.log("MyContract's cETH Balance:", myContractCEthBalance);
51 | console.log(`MyContract's ${assetName} Balance:`, myContractUnderlyingBalance);
52 |
53 | resolve();
54 | });
55 | };
56 |
57 | const main = async () => {
58 | const contractIsDeployed = (await web3.eth.getCode(myContractAddress)) !== '0x';
59 |
60 | if (!contractIsDeployed) {
61 | throw Error('MyContract is not deployed! Deploy it by running the deploy script.');
62 | }
63 |
64 | await logBalances();
65 |
66 | const ethToSupplyAsCollateral = 1;
67 |
68 | console.log(`\nCalling MyContract.borrowErc20Example with ${ethToSupplyAsCollateral} ETH for collateral...\n`);
69 | let result = await myContract.methods.borrowErc20Example(
70 | cEthAddress,
71 | comptrollerAddress,
72 | priceFeedAddress,
73 | cTokenAddress,
74 | underlyingDecimals
75 | ).send({
76 | from: myWalletAddress,
77 | gasLimit: web3.utils.toHex(5000000),
78 | gasPrice: web3.utils.toHex(20000000000), // use ethgasstation.info (mainnet only)
79 | value: (ethToSupplyAsCollateral * 1e18).toString()
80 | });
81 |
82 | // See the solidity functions logs from "MyLog" event
83 | // console.log(result.events.MyLog);
84 |
85 | await logBalances();
86 |
87 | console.log(`\nNow repaying the borrow...\n`);
88 | const underlyingToRepayBorrow = 10;
89 | result = await myContract.methods.myErc20RepayBorrow(
90 | underlyingAddress,
91 | cTokenAddress,
92 | (underlyingToRepayBorrow * Math.pow(10, underlyingDecimals)).toString()
93 | ).send({
94 | from: myWalletAddress,
95 | gasLimit: web3.utils.toHex(5000000),
96 | gasPrice: web3.utils.toHex(20000000000), // use ethgasstation.info (mainnet only)
97 | });
98 |
99 | await logBalances();
100 | };
101 |
102 | main().catch(async (err) => {
103 | console.error('ERROR:', err);
104 |
105 | // Create "events" and "emit" them in your Solidity code.
106 | // Current contract does not have any.
107 | let logs = await myContract.getPastEvents('allEvents');
108 | console.log('Logs: ', logs);
109 | });
110 |
--------------------------------------------------------------------------------
/examples-solidity/web3-js/borrow-eth-via-solidity.js:
--------------------------------------------------------------------------------
1 | // Example to supply DAI as collateral and borrow ETH
2 | // YOU MUST HAVE DAI IN YOUR WALLET before you run this script
3 | const Web3 = require('web3');
4 | const web3 = new Web3('http://127.0.0.1:8545');
5 | const {
6 | cEthAbi,
7 | cErcAbi,
8 | erc20Abi,
9 | } = require('../../contracts.json');
10 |
11 | // Your Ethereum wallet private key
12 | const privateKey = 'b8c1b5c1d81f9475fdf2e334517d29f733bdfa40682207571b12fc1142cbf329';
13 |
14 | // Add your Ethereum wallet to the Web3 object
15 | web3.eth.accounts.wallet.add('0x' + privateKey);
16 | const myWalletAddress = web3.eth.accounts.wallet[0].address;
17 |
18 | // Mainnet Contract for the Comptroller
19 | const comptrollerAddress = '0x3d9819210a31b4961b30ef54be2aed79b9c9cd3b';
20 |
21 | // Mainnet Contract for cETH
22 | const cEthAddress = '0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5';
23 | const cEth = new web3.eth.Contract(cEthAbi, cEthAddress);
24 |
25 | // Mainnet address of underlying token (like DAI or USDC)
26 | const underlyingAddress = '0x6b175474e89094c44da98b954eedeac495271d0f'; // Dai
27 | const underlying = new web3.eth.Contract(erc20Abi, underlyingAddress);
28 |
29 | // Mainnet address for a cToken (like cDai, https://compound.finance/docs#networks)
30 | const cTokenAddress = '0x5d3a536e4d6dbd6114cc1ead35777bab948e3643'; // cDai
31 | const cToken = new web3.eth.Contract(cErcAbi, cTokenAddress);
32 | const assetName = 'DAI'; // for the log output lines
33 | const underlyingDecimals = 18; // Number of decimals defined in this ERC20 token's contract
34 |
35 | // MyContract
36 | const myContractAbi = require('../../artifacts/contracts/MyContracts.sol/MyContract.json').abi;
37 | const myContractAddress = '0x0Bb909b7c3817F8fB7188e8fbaA2763028956E30';
38 | const myContract = new web3.eth.Contract(myContractAbi, myContractAddress);
39 |
40 | // Web3 transaction information, we'll use this for every transaction we'll send
41 | const fromMyWallet = {
42 | from: myWalletAddress,
43 | gasLimit: web3.utils.toHex(4000000),
44 | gasPrice: web3.utils.toHex(25000000000) // use ethgasstation.info (mainnet only)
45 | };
46 |
47 | const logBalances = () => {
48 | return new Promise(async (resolve, reject) => {
49 | const myWalletUnderlyingBalance = +await underlying.methods.balanceOf(myWalletAddress).call() / 1e18;
50 | const myContractEthBalance = +web3.utils.fromWei(await web3.eth.getBalance(myContractAddress));
51 | const myContractCEthBalance = await cEth.methods.balanceOf(myContractAddress).call() / 1e8;
52 | const myContractUnderlyingBalance = +await underlying.methods.balanceOf(myContractAddress).call() / 1e18;
53 | const myContractCTokenBalance = +await cToken.methods.balanceOf(myContractAddress).call() / 1e8;
54 |
55 | console.log(`My Wallet's ${assetName} Balance:`, myWalletUnderlyingBalance);
56 | console.log(`MyContract's ETH Balance:`, myContractEthBalance);
57 | console.log(`MyContract's cETH Balance:`, myContractCEthBalance);
58 | console.log(`MyContract's ${assetName} Balance:`, myContractUnderlyingBalance);
59 | console.log(`MyContract's c${assetName} Balance:`, myContractCTokenBalance);
60 |
61 | resolve();
62 | });
63 | };
64 |
65 | const main = async () => {
66 | const contractIsDeployed = (await web3.eth.getCode(myContractAddress)) !== '0x';
67 |
68 | if (!contractIsDeployed) {
69 | throw Error('MyContract is not deployed! Deploy it by running the deploy script.');
70 | }
71 |
72 | await logBalances();
73 |
74 | const underlyingAsCollateral = 25;
75 | const mantissa = (underlyingAsCollateral * Math.pow(10, underlyingDecimals)).toString();
76 | console.log(`\nSending ${underlyingAsCollateral} ${assetName} to MyContract so it can provide collateral...\n`);
77 |
78 | // Send underlying to MyContract before attempting the supply
79 | await underlying.methods.transfer(myContractAddress, mantissa).send(fromMyWallet);
80 |
81 | await logBalances();
82 |
83 | console.log(`\nCalling MyContract.borrowEthExample with ${underlyingAsCollateral} ${assetName} as collateral...\n`);
84 |
85 | let result = await myContract.methods.borrowEthExample(
86 | cEthAddress,
87 | comptrollerAddress,
88 | cTokenAddress,
89 | underlyingAddress,
90 | mantissa
91 | ).send(fromMyWallet);
92 |
93 | // See the solidity functions logs from "MyLog" event
94 | // console.log(JSON.stringify(result), '\n');
95 |
96 | await logBalances();
97 |
98 | console.log(`\nNow repaying the borrow...\n`);
99 | const ethToRepayBorrow = 0.002; // hard coded borrow in contract
100 | result = await myContract.methods.myEthRepayBorrow(
101 | cEthAddress,
102 | web3.utils.toWei(ethToRepayBorrow.toString(), 'ether'),
103 | 300000 // gas for the "cEth.repayBorrow" function
104 | ).send(fromMyWallet);
105 |
106 | await logBalances();
107 | };
108 |
109 | main().catch(console.error);
110 |
--------------------------------------------------------------------------------
/hardhat.config.js:
--------------------------------------------------------------------------------
1 | require('@nomiclabs/hardhat-web3');
2 | require('@nomiclabs/hardhat-ethers');
3 | const ethers = require('ethers');
4 |
5 | const providerUrl = process.env.MAINNET_PROVIDER_URL;
6 | const developmentMnemonic = process.env.DEV_ETH_MNEMONIC;
7 |
8 | if (!providerUrl) {
9 | console.error('Missing JSON RPC provider URL as environment variable `MAINNET_PROVIDER_URL`\n');
10 | process.exit(1);
11 | }
12 |
13 | if (!developmentMnemonic) {
14 | console.error('Missing development Ethereum account mnemonic as environment variable `DEV_ETH_MNEMONIC`\n');
15 | process.exit(1);
16 | }
17 |
18 | function getPrivateKeysFromMnemonic(mnemonic, numberOfPrivateKeys = 20) {
19 | const result = [];
20 | for (let i = 0; i < numberOfPrivateKeys; i++) {
21 | result.push(ethers.Wallet.fromMnemonic(mnemonic, `m/44'/60'/0'/0/${i}`).privateKey);
22 | }
23 | }
24 |
25 | module.exports = {
26 | solidity: {
27 | version: '0.8.6',
28 | settings: {
29 | optimizer: {
30 | enabled: true,
31 | runs: 1000
32 | }
33 | }
34 | },
35 | networks: {
36 | hardhat: {
37 | forking: {
38 | url: providerUrl,
39 | },
40 | gasPrice: 0,
41 | initialBaseFeePerGas: 0,
42 | loggingEnabled: false,
43 | accounts: {
44 | mnemonic: developmentMnemonic,
45 | },
46 | chainId: 1, // metamask -> accounts -> settings -> networks -> localhost 8545 -> set chainId to 1
47 | },
48 | localhost: {
49 | url: 'http://localhost:8545',
50 | accounts: getPrivateKeysFromMnemonic(developmentMnemonic),
51 | }
52 | },
53 | };
54 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "compound-borrow-guide",
3 | "version": "2.0.0",
4 | "description": "Code examples for borrowing Ethereum assets from the Compound Protocol. https://compound.finance/",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node ./scripts/run-localhost-fork.js",
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "keywords": [
11 | "compound finance",
12 | "ethereum",
13 | "web3"
14 | ],
15 | "author": "Compound Labs, Inc.",
16 | "license": "MIT",
17 | "dependencies": {
18 | "@compound-finance/compound-js": "^0.4.1",
19 | "@nomiclabs/hardhat-ethers": "^2.0.2",
20 | "@nomiclabs/hardhat-web3": "^2.0.0",
21 | "ethers": "^5.4.7",
22 | "hardhat": "^2.6.6",
23 | "web3": "^1.6.0"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/scripts/deploy.js:
--------------------------------------------------------------------------------
1 | async function main() {
2 | const [deployer] = await ethers.getSigners();
3 |
4 | console.log("Deploying contracts with the account:", deployer.address);
5 |
6 | console.log("Account balance:", (await deployer.getBalance()).toString());
7 |
8 | const MyContract = await ethers.getContractFactory("MyContract");
9 | const myContract = await MyContract.deploy();
10 |
11 | console.log("MyContract address:", myContract.address);
12 | }
13 |
14 | main()
15 | .then(() => process.exit(0))
16 | .catch((error) => {
17 | console.error(error);
18 | process.exit(1);
19 | });
--------------------------------------------------------------------------------
/scripts/run-localhost-fork.js:
--------------------------------------------------------------------------------
1 | const hre = require('hardhat');
2 | const { TASK_NODE_CREATE_SERVER } = require('hardhat/builtin-tasks/task-names');
3 | const Compound = require('@compound-finance/compound-js');
4 | const jsonRpcUrl = 'http://localhost:8545';
5 | let provider;
6 |
7 | // Amount of tokens to seed in the 0th account on localhost
8 | // Uncomment a line below to seed the account with that asset
9 | // The amounts are limited by the current cToken asset balance
10 | // You can do the same thing with any high-balance Ethereum account (whales)
11 | const amounts = {
12 | // 'aave': 25,
13 | // 'bat': 100,
14 | // 'comp': 25,
15 | 'dai': 100,
16 | // 'link': 25,
17 | // 'mkr': 2,
18 | // 'sushi': 25,
19 | 'uni': 10,
20 | 'usdc': 100,
21 | // 'usdt': 100,
22 | // 'wbtc': 2,
23 | // 'yfi': 2,
24 | // 'zrx': 100
25 | };
26 |
27 | // Set up localhost fork with Hardhat
28 | (async function () {
29 | console.log(`\nRunning a hardhat localhost fork of mainnet at ${jsonRpcUrl}\n`);
30 |
31 | const jsonRpcServer = await hre.run(TASK_NODE_CREATE_SERVER, {
32 | hostname: 'localhost',
33 | port: 8545,
34 | provider: hre.network.provider,
35 | });
36 |
37 | await jsonRpcServer.listen();
38 |
39 | // Seed first account with ERC-20 tokens on localhost
40 | const assetsToSeed = Object.keys(amounts);
41 | const seedRequests = [];
42 | assetsToSeed.forEach((asset) => { seedRequests.push(seed(asset.toUpperCase(), amounts[asset])) });
43 | await Promise.all(seedRequests);
44 | console.log('\nReady to test locally! To exit, hold Ctrl+C.\n');
45 | })().catch(console.error)
46 |
47 | // Moves tokens from cToken contracts to the localhost address
48 | // but this will work with any Ethereum address with a lot of tokens
49 | async function seed(asset, amount) {
50 | const cTokenAddress = Compound.util.getAddress('c' + asset);
51 | provider = new Compound._ethers.providers.JsonRpcProvider(jsonRpcUrl);
52 | const accounts = await provider.listAccounts();
53 |
54 | // Impersonate this address (only works in local testnet)
55 | console.log('Impersonating address on localhost... ', Compound.util.getAddress('c' + asset));
56 | await hre.network.provider.request({
57 | method: 'hardhat_impersonateAccount',
58 | params: [ cTokenAddress ],
59 | });
60 |
61 | // Number of underlying tokens to mint, scaled up so it is an integer
62 | const numbTokensToSeed = (amount * Math.pow(10, Compound.decimals[asset])).toString();
63 |
64 | const signer = provider.getSigner(cTokenAddress);
65 |
66 | const gasPrice = '0'; // only works in the localhost dev environment
67 | // const gasPrice = await provider.getGasPrice();
68 | const transferTrx = await Compound.eth.trx(
69 | Compound.util.getAddress(asset),
70 | 'function transfer(address, uint256) public returns (bool)',
71 | [ accounts[0], numbTokensToSeed ],
72 | { provider: signer, gasPrice }
73 | );
74 | await transferTrx.wait(1);
75 |
76 | console.log('Local test account successfully seeded with ' + asset);
77 |
78 | const balanceOf = await Compound.eth.read(
79 | Compound.util.getAddress(asset),
80 | 'function balanceOf(address) public returns (uint256)',
81 | [ accounts[0] ],
82 | { provider }
83 | );
84 |
85 | const tokens = +balanceOf / Math.pow(10, Compound.decimals[asset]);
86 | console.log(asset + ' amount in first localhost account wallet:', tokens);
87 | }
88 |
--------------------------------------------------------------------------------