├── .gitignore ├── LICENSE ├── README.md ├── contracts.json ├── contracts └── MyContracts.sol ├── examples-js ├── compound-js │ ├── borrow-erc20-with-eth-collateral.js │ └── borrow-eth-with-erc20-collateral.js ├── ethers-js │ ├── borrow-erc20-with-eth-collateral.js │ └── borrow-eth-with-erc20-collateral.js └── web3-js │ ├── borrow-erc20-with-eth-collateral.js │ └── borrow-eth-with-erc20-collateral.js ├── examples-solidity ├── compound-js │ ├── borrow-erc20-via-solidity.js │ └── borrow-eth-via-solidity.js ├── ethers-js │ ├── borrow-erc20-via-solidity.js │ └── borrow-eth-via-solidity.js └── web3-js │ ├── borrow-erc20-via-solidity.js │ └── borrow-eth-via-solidity.js ├── hardhat.config.js ├── package-lock.json ├── package.json └── scripts ├── deploy.js └── run-localhost-fork.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | .build/ 4 | artifacts 5 | cache 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Compound Labs, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Quick Start: Borrowing Assets from the Compound Protocol 2 | 3 | Examples for borrowing Ethereum assets from the [Compound Protocol](https://compound.finance/?ref=github&user=ajb413&repo=compound-borrow-examples). 4 | 5 | **[Full Quick Start Tutorial on the Compound Medium Blog](https://medium.com/compound-finance/borrowing-assets-from-compound-quick-start-guide-f5e69af4b8f4)** 6 | 7 | If you want to borrow assets directly from the protocol from your Ethereum wallet using JSON RPC and Web3.js, see the `examples-js` folder. There are examples for popular web3 libraries like **Web3.js**, **Ethers.js**, and **Compound.js**. 8 | 9 | JSON RPC can be utilized in the **web browser, with Node.js, or any other programming language with a web3 library**. 10 | 11 | If you want to borrow assets from the protocol from your Ethereum smart contract, see the `examples-solidity` folder. 12 | 13 | 14 | ## What is Compound? 15 | Compound is an open-source, autonomous protocol built for developers, to unlock a universe of new financial applications. Interest and borrowing, for the open financial system. Learn more on the website: 16 | 17 | 18 | Compound Finance 19 | 20 | 21 | ## Setup 22 | The code in this repository can be used to borrow assets from Compound on the Ethereum mainnet, any public test net, or your own localhost with [Hardhat](https://hardhat.org/getting-started/). 23 | 24 | If you haven't already, install [Node.js](https://nodejs.org/) LTS. Clone this repository, `cd` to the root directory of the project, and run: 25 | ```bash 26 | git clone git@github.com:compound-developers/compound-borrow-examples.git 27 | cd compound-borrow-examples/ 28 | npm install 29 | ``` 30 | 31 | We'll need `npx` for this project. If you don't have `npx`, install it using this command: 32 | ```bash 33 | npm install -g npx 34 | ``` 35 | 36 | If you want to use the JS examples in the **web browser**, you'll need to first import your web3 library of choice into your HTML (Web3.js, Ethers.js, or Compound.js). This step is **not necessary** if you are running the examples with only Node.js. 37 | 38 | ### Running a Local Ethereum Testnet with Hardhat 39 | To get the localhost testnet running, use the following commands in a second command line window. The command runs a local Hardhat node and forks Ethereum Mainnet to your machine. 40 | 41 | **If you are not running your own Ethereum node, make an [Infura](https://infura.io/) account at [https://infura.io/](https://infura.io/) or at [Alchemy.com](https://alchemy.com).** Accounts are free. Get a **project ID** and supplant it into your [environment variable settings](https://www.twilio.com/blog/2017/01/how-to-set-environment-variables.html), like below. 42 | 43 | ```bash 44 | cd compound-supply-examples/ 45 | 46 | ## Set environment variables for the script to use 47 | export MAINNET_PROVIDER_URL="https://mainnet.infura.io/v3/" 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 | --------------------------------------------------------------------------------