├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── copy.png ├── favicon.ico ├── index.html ├── loading.gif ├── manifest.json └── small-loading.gif └── src ├── App.css ├── App.js ├── App.test.js ├── components ├── AccountsTable │ ├── AccountsTable.css │ └── index.js ├── AccountsTableHeader │ └── AccountsTableHeader.js ├── AddressInspector │ ├── AddressInspector.css │ └── index.js └── BalanceTable │ ├── BalanceTable.css │ └── index.js ├── constants ├── Compound.js ├── CompoundStaging.js └── ERC20.js ├── index.css ├── index.js ├── samplejson.json └── serviceWorker.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | build/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # compound-liquidator 2 | 3 | # TODO 4 | 5 | - Optimize supply and borrow balance requests. 6 | - General cleanup. (inspected_address should be an object with all associated data) 7 | 8 | # stretch goals 9 | 10 | - Automate this for all the gains. 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "compound-liquidator", 3 | "version": "0.1.0", 4 | "private": true, 5 | "homepage": "https://conlan.github.io/compound-liquidator", 6 | "dependencies": { 7 | "axios": "^0.18.0", 8 | "bignumber.js": "^8.0.1", 9 | "react": "^16.7.0-alpha.0", 10 | "react-dom": "^16.7.0-alpha.0", 11 | "react-scripts": "^2.1.3", 12 | "react-table": "^6.8.6", 13 | "web3": "^1.0.0-beta.37", 14 | "web3-react": "^1.1.1" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test", 20 | "eject": "react-scripts eject", 21 | "predeploy": "npm run build", 22 | "deploy": "gh-pages -d build" 23 | }, 24 | "eslintConfig": { 25 | "extends": "react-app" 26 | }, 27 | "browserslist": [ 28 | ">0.2%", 29 | "not dead", 30 | "not ie <= 11", 31 | "not op_mini all" 32 | ], 33 | "devDependencies": { 34 | "gh-pages": "^2.0.1" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /public/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conlan/compound-liquidator/87dd7d0080b85c127e9a8b8c927def4fdb137fdb/public/copy.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conlan/compound-liquidator/87dd7d0080b85c127e9a8b8c927def4fdb137fdb/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 22 | Compound Liquidator 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /public/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conlan/compound-liquidator/87dd7d0080b85c127e9a8b8c927def4fdb137fdb/public/loading.gif -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /public/small-loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conlan/compound-liquidator/87dd7d0080b85c127e9a8b8c927def4fdb137fdb/public/small-loading.gif -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .Loading { 2 | display:block; 3 | margin:0 auto; 4 | text-align:center; 5 | margin-top:100px; 6 | } 7 | 8 | p.SameLine { 9 | display:inline; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | import AccountsTable from "./components/AccountsTable/index.js"; 4 | import AddressInspector from "./components/AddressInspector/index.js"; 5 | 6 | import Compound from "./constants/Compound.js" 7 | import CompoundStaging from "./constants/CompoundStaging.js" 8 | 9 | import ERC20 from "./constants/ERC20.js" 10 | 11 | import axios from "axios"; 12 | 13 | import { useWeb3Context } from "web3-react/hooks"; 14 | 15 | import "./App.css"; 16 | 17 | // import sampleJson from "./samplejson.json"; 18 | 19 | let web3 = null; 20 | 21 | function Web3Setter(props) { 22 | if (web3 === null) { 23 | var app = props.app; 24 | 25 | web3 = useWeb3Context(); 26 | 27 | if (web3.networkId === app.state.MAIN_NETWORK_ID) { 28 | app.state.MONEY_MARKET_ABI = Compound.moneyMarketABI; 29 | app.state.MONEY_MARKET_ADDRESS = Compound.moneyMarketAddress; 30 | 31 | app.state.TOKENS = Compound.tokens; 32 | 33 | app.state.LIQUIDATION_ADDRESS = Compound.liquidationAddress; 34 | app.state.LIQUIDATION_ABI = Compound.liquidationABI; 35 | 36 | app.state.ETHERSCAN_PREFIX = "https://etherscan.io/"; 37 | 38 | app.state.MIN_COLLATERAL_RATIO = Compound.minCollateralRatio; 39 | app.state.SAFE_COLLATERAL_RATIO = Compound.safeCollateralRatio; 40 | } else if (web3.networkId === app.state.STAGING_NETWORK_ID) { 41 | app.state.MONEY_MARKET_ABI = CompoundStaging.moneyMarketABI; 42 | app.state.MONEY_MARKET_ADDRESS = CompoundStaging.moneyMarketAddress; 43 | 44 | app.state.TOKENS = CompoundStaging.tokens; 45 | 46 | app.state.LIQUIDATION_ADDRESS = CompoundStaging.liquidationAddress; 47 | app.state.LIQUIDATION_ABI = CompoundStaging.liquidationABI; 48 | 49 | app.state.ETHERSCAN_PREFIX = "https://rinkeby.etherscan.io/"; 50 | 51 | app.state.MIN_COLLATERAL_RATIO = CompoundStaging.minCollateralRatio; 52 | app.state.SAFE_COLLATERAL_RATIO = CompoundStaging.safeCollateralRatio; 53 | } 54 | 55 | app.refreshAccountList(); 56 | app.refreshAllowanceStates(); 57 | } 58 | 59 | return (
) 60 | } 61 | 62 | /* 63 | * Parse response data from server into an array of account objects. Can point to local json or server response 64 | */ 65 | function ParseAccountDataResponse(json, app) { 66 | var newAccounts = []; 67 | 68 | json.account_values.forEach(accountData => { 69 | 70 | var account = { 71 | address: accountData.address, 72 | 73 | // how much the borrower has borrowed in ETH 74 | totalEthBorrow: accountData.total_borrow_value_in_eth.value, 75 | 76 | // how much the borrower has supplied in ETH 77 | totalEthSupply: accountData.total_supply_value_in_eth.value, 78 | 79 | // when this borrower was last updated (ETH block) 80 | blockUpdated: accountData.block_updated 81 | }; 82 | 83 | newAccounts.push(account); 84 | }); 85 | 86 | var inspectedAddressParam = ""; 87 | try { 88 | // check for URL Search Params support 89 | if ("URLSearchParams" in window) { 90 | // extract factory token from URL if found 91 | var urlParams = new URLSearchParams(window.location.search); 92 | 93 | if (urlParams.has("address")) { 94 | var addressInput = urlParams.get("address"); 95 | 96 | // validate the address input before assuming it's a valid address 97 | if (web3.web3js.utils.isAddress(addressInput)) { 98 | inspectedAddressParam = addressInput; 99 | } 100 | } 101 | } 102 | } catch (e) { 103 | console.log(e); 104 | } 105 | 106 | app.setState({ 107 | accounts: newAccounts, 108 | inspected_address : inspectedAddressParam 109 | }); 110 | } 111 | 112 | class App extends Component { 113 | constructor() { 114 | super(); 115 | 116 | this.state = { 117 | accounts: [], 118 | 119 | // the address we're currently inspecting 120 | inspected_address: "", 121 | // the state of that address (risky, safe, unsafe) TODO this should be wrapped in a single address object 122 | inspected_address_state : "", 123 | 124 | // used when inspecting an address to hold how much the account has borrowed or supplied 125 | borrow_balances: {}, 126 | supply_balances: {}, 127 | // which balances are currently being requested from server 128 | pending_balances: {}, 129 | 130 | pending_allowances: {}, 131 | allowance_states: {}, 132 | 133 | asset_prices: {}, 134 | 135 | // the asset that the user has toggled to repay for the borrow 136 | asset_repay: "", 137 | // the asset that the user has toggled to collect from borrower 138 | asset_collect: "", 139 | 140 | // holds the submitted liquidation transation hash 141 | repaySubmittedTxHash : "", 142 | 143 | // the discount for liquidating borrows (gets fetched later) 144 | liquidationDiscount : -1, 145 | 146 | // whether the user can choose assets on the address inspect. Blocked by default unless that account has a negative account liquidity 147 | liquidateBlocked : true, 148 | 149 | currentBlock : "", 150 | 151 | // CONSTANTS 152 | MAIN_NETWORK_ID : 1, 153 | STAGING_NETWORK_ID : 4, 154 | 155 | MONEY_MARKET_ADDRESS : "", 156 | MONEY_MARKET_ABI : "", 157 | 158 | TOKENS : [], 159 | 160 | LIQUIDATION_ADDRESS : "", 161 | LIQUIDATION_ABI : "", 162 | 163 | ETHERSCAN_PREFIX : "", 164 | 165 | MIN_COLLATERAL_RATIO : 0, 166 | SAFE_COLLATERAL_RATIO : 0 167 | }; 168 | } 169 | 170 | componentDidMount() {} 171 | 172 | render() { 173 | // if we're inspecting an address 174 | if (this.state.inspected_address.length > 0) { 175 | return ( 176 | 177 | ); 178 | } else { 179 | // else we're not inspecting an address, check if there's any accounts. if not then show loading gif 180 | if (this.state.accounts.length === 0) { 181 | return ( 182 |
183 | 184 | Loading 185 |
186 | ); 187 | } else { 188 | // show the accounts list 189 | return ( 190 | 191 | ); 192 | } 193 | } 194 | } 195 | 196 | refreshAllowanceStates() { 197 | // find out how much the liquidation address can spend on user's behalf. If 0 then the token is not "enabled" for liquidation 198 | let that = this; 199 | 200 | var compoundContract = new web3.web3js.eth.Contract(this.state.MONEY_MARKET_ABI, this.state.MONEY_MARKET_ADDRESS); 201 | 202 | this.state.TOKENS.forEach((t) => { 203 | if ((t.address in this.state.allowance_states) === false) { 204 | 205 | var tokenContract = new web3.web3js.eth.Contract(ERC20.ABI, t.address); 206 | 207 | tokenContract.methods.allowance(web3.account, this.state.LIQUIDATION_ADDRESS).call(function(error, allowance) { 208 | if (error === null) { 209 | if (allowance > 0) { 210 | that.state.allowance_states[t.address] = true; 211 | 212 | that.setState({}); 213 | } 214 | } 215 | }); 216 | } 217 | 218 | if ((t.address in this.state.asset_prices) === false) { 219 | var tokenDecimals = Math.pow(10, t.decimals); 220 | 221 | compoundContract.methods.assetPrices(t.address).call(function(error, price) { 222 | if (error === null) { 223 | price = price / tokenDecimals; 224 | 225 | // console.log(t.symbol + " = " + price); 226 | that.state.asset_prices[t.address] = price; 227 | } 228 | }); 229 | } 230 | }); 231 | } 232 | 233 | refreshAccountList() { 234 | // TESTING 235 | // ParseAccountDataResponse(sampleJson, this); 236 | var URL = null; 237 | 238 | if (web3.networkId === this.state.MAIN_NETWORK_ID) { 239 | // mainnet 240 | URL = "https://api.compound.finance/api/risk/v1/get_account_values"; 241 | } else if (web3.networkId === this.state.STAGING_NETWORK_ID) { 242 | // Staging 243 | URL = "https://api.stage.compound.finance/api/risk/v1/get_account_values"; 244 | } 245 | 246 | axios({ 247 | method: "post", 248 | url: URL, 249 | headers: { 250 | Accept: "application/json", 251 | "Content-Type": "application/json" 252 | // ,'compound-api-key' : 'xxx' TODO implement this when CORS response headers are fixed 253 | }, 254 | // TODO put input fields on main page for user to set 255 | data: { 256 | page_size: 100, 257 | page_number: 1, 258 | min_borrow_value_in_eth: { 259 | value: "10000000000000000" 260 | }, 261 | max_collateral_ratio: { 262 | value: "10" 263 | } 264 | } 265 | }).then(response => { 266 | console.log(response); 267 | 268 | ParseAccountDataResponse(response.data, this); 269 | }) 270 | .catch(error => { 271 | console.error(error); 272 | }); 273 | } 274 | } 275 | 276 | export default App; -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /src/components/AccountsTable/AccountsTable.css: -------------------------------------------------------------------------------- 1 | .AccountsLabel { 2 | display: inline; 3 | } 4 | 5 | .CurrentBlock { 6 | float: right; 7 | } 8 | 9 | .AccountsTable { 10 | margin-top:20px; 11 | margin-left:20px; 12 | width: calc(100% - 40px); 13 | } 14 | 15 | .center { 16 | text-align: center; 17 | } 18 | 19 | .right { 20 | text-align: right; 21 | } 22 | 23 | .InspectButton { 24 | width:100%; 25 | 26 | padding: 2px; 27 | text-align: center; 28 | 29 | display: inline-block; 30 | font-size: 12px; 31 | } 32 | 33 | div.rt-tr { 34 | align-items: center; 35 | } 36 | 37 | .ReactTable { 38 | 39 | } -------------------------------------------------------------------------------- /src/components/AccountsTable/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import ReactTable from "react-table"; 4 | import AccountsTableHeader from "../AccountsTableHeader/AccountsTableHeader.js" 5 | 6 | import "react-table/react-table.css"; 7 | 8 | import "./AccountsTable.css"; 9 | 10 | let app; 11 | 12 | function InspectAddress(address, state) { 13 | app.setState({ 14 | inspected_address: address, 15 | inspected_address_state: state 16 | }); 17 | } 18 | 19 | function AccountsTable(props) { 20 | app = props.app; 21 | 22 | const data = []; 23 | 24 | props.accounts.forEach(account => { 25 | var supplyAmount = (account.totalEthSupply / 1e18).toFixed(6); 26 | var borrowAmount = (account.totalEthBorrow / 1e18).toFixed(6); 27 | 28 | var ratio = +(supplyAmount / borrowAmount).toFixed(6); 29 | 30 | var minCollateralRatio = app.state.MIN_COLLATERAL_RATIO; 31 | var safeCollateralRatio = app.state.SAFE_COLLATERAL_RATIO; 32 | 33 | // calculate the account liquidity 34 | 35 | var accountLiquidity = (supplyAmount - (app.state.MIN_COLLATERAL_RATIO * borrowAmount)).toFixed(6); 36 | 37 | var state = ""; 38 | 39 | if (ratio < minCollateralRatio) { 40 | state = "unsafe"; 41 | } else if (ratio <= safeCollateralRatio) { 42 | state = "risky"; 43 | } else { 44 | state = "safe"; 45 | } 46 | 47 | var ratioDisplay = (ratio * 100).toFixed(6) + "%"; 48 | 49 | var accountObj = { 50 | address: account.address, 51 | supply: Number(supplyAmount), 52 | borrow: Number(borrowAmount), 53 | accountLiquidity: Number(accountLiquidity), 54 | ratio: ratioDisplay, 55 | state: state, 56 | block: Number(account.blockUpdated) 57 | }; 58 | data.push(accountObj); 59 | }); 60 | 61 | var etherScanPrefix = app.state.ETHERSCAN_PREFIX; 62 | 63 | const columns = [ 64 | { 65 | Header: "Address", 66 | accessor: "address", 67 | maxWidth: 500, 68 | Cell: row => ( 69 | 70 | {row.value} 71 | 72 | ) 73 | }, 74 | { 75 | Header: "Last Updated", 76 | accessor: "block", 77 | maxWidth: 200, 78 | className: "right", 79 | Cell: row => ( 80 | 81 | {row.value} 82 | 83 | ) 84 | }, 85 | { 86 | Header: "Supply", 87 | accessor: "supply", 88 | maxWidth: 150, 89 | className: "right" 90 | }, 91 | { 92 | Header: "Borrow", 93 | accessor: "borrow", 94 | maxWidth: 150, 95 | className: "right" 96 | }, 97 | { 98 | Header: "Ratio", 99 | accessor: "ratio", 100 | maxWidth: 150, 101 | className: "right" 102 | }, 103 | { 104 | Header: "Account Liquidity", 105 | accessor: "accountLiquidity", 106 | maxWidth: 150, 107 | className: "right" 108 | }, 109 | { 110 | Header: "State", 111 | accessor: "state", 112 | maxWidth: 200, 113 | Cell: row => ( 114 | 115 | 127 | ● 128 | {" "} 129 | {row.value === "safe" 130 | ? "Safe" 131 | : row.value === "risky" 132 | ? "Risky" 133 | : "Unsafe"} 134 | 135 | ) 136 | }, 137 | { 138 | Header: "", 139 | accessor: "liquidate", 140 | maxWidth: 200, 141 | Cell: row => ( 142 | 148 | ) 149 | } 150 | ]; 151 | 152 | var showPageSizeOptions = false; 153 | var defaultPageSize = 15; 154 | 155 | // var minRows = defaultPageSize; 156 | 157 | return ( 158 |
159 | 160 |
161 | 162 | 169 |

170 | 171 | Github 172 | {" "} 173 | |{" "} 174 | 175 | Compound 176 | {" "} 177 | |{" "} 178 | 179 | Documentation 180 | {" "} 181 | | Use at your own{" "} 182 | 183 | risk! 184 | 185 |

186 |
187 | ); 188 | } 189 | 190 | export default AccountsTable; 191 | -------------------------------------------------------------------------------- /src/components/AccountsTableHeader/AccountsTableHeader.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { useWeb3Context } from "web3-react/hooks"; 4 | 5 | function AccountsTableHeader(props) { 6 | var app = props.app; 7 | 8 | var currentBlockText = "" 9 | var currentBlockLink = app.state.ETHERSCAN_PREFIX + "block/" + props.currentBlock; 10 | 11 | var web3 = useWeb3Context(); 12 | 13 | if (props.currentBlock.length > 0) { 14 | currentBlockText = "Current Block ("; 15 | 16 | if (web3.networkId === app.state.MAIN_NETWORK_ID) { 17 | currentBlockText += "Mainnet"; 18 | } else if (web3.networkId === app.state.STAGING_NETWORK_ID) { 19 | currentBlockText += "Staging"; 20 | } else { 21 | currentBlockText += "Unknown"; 22 | } 23 | 24 | currentBlockText += "): "; 25 | } else { 26 | web3.web3js.eth.getBlockNumber().then((currentBlock) => { 27 | app.setState({ 28 | currentBlock : currentBlock.toString() 29 | }); 30 | }); 31 | } 32 | 33 | return ( 34 |
35 |
36 |

37 | Accounts 38 |

39 |
40 | 41 |
42 |

{currentBlockText}

43 | 44 | {props.currentBlock} 45 | 46 |
47 |
48 | ); 49 | } 50 | 51 | export default AccountsTableHeader; 52 | -------------------------------------------------------------------------------- /src/components/AddressInspector/AddressInspector.css: -------------------------------------------------------------------------------- 1 | .AddressInspector { 2 | margin-left: 20px; 3 | width:calc(100% - 40px); 4 | } 5 | 6 | .CopyButton { 7 | padding-top:2px; 8 | width: 10px; 9 | } 10 | 11 | .ButtonDiv { 12 | width:100%; 13 | 14 | display: inline-block; 15 | } 16 | 17 | .BackButton { 18 | width:200px; 19 | 20 | padding: 10px; 21 | 22 | font-size: 16px; 23 | } 24 | 25 | .TransactionPendingDiv img { 26 | width: 14px; 27 | height: auto; 28 | } 29 | 30 | .EnableButton { 31 | } 32 | 33 | .RefreshButton { 34 | width:100px; 35 | 36 | padding: 5px; 37 | 38 | float:right; 39 | 40 | font-size: 14px; 41 | } 42 | 43 | .LiquidateButton { 44 | width:200px; 45 | 46 | float:right; 47 | 48 | padding: 10px; 49 | 50 | font-size: 14px; 51 | } 52 | 53 | .LiquidationDetails { 54 | text-align: right; 55 | } 56 | 57 | .TransactionSubmissionDetails { 58 | text-align: right; 59 | } 60 | 61 | 62 | .slider { 63 | width: 25%; 64 | padding-top: 10px; 65 | padding-bottom:10px; 66 | float:right; 67 | margin-right:20px; 68 | } -------------------------------------------------------------------------------- /src/components/AddressInspector/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | // import ReactTable from "react-table"; 4 | import BalanceTable from "../../components/BalanceTable/index.js" 5 | import { BigNumber } from "bignumber.js"; 6 | 7 | import { useWeb3Context } from "web3-react/hooks"; 8 | 9 | import "./AddressInspector.css" 10 | 11 | var app; 12 | var accountLiquidity = 0; 13 | var web3; 14 | 15 | var maxRepayAmount = 0; 16 | var tokenAddressToBeRepaid = ""; 17 | 18 | function GetIntendedRepayAmount() { 19 | var repaySlider = document.getElementById('repaySlider'); 20 | 21 | return new BigNumber(repaySlider.value / repaySlider.max * maxRepayAmount).toFixed(4); 22 | } 23 | 24 | function OnRepaySliderValueChange() { 25 | // update the liquidation button text 26 | var repayAmount = GetIntendedRepayAmount(); 27 | 28 | var liquidationButton = document.getElementById('LiquidateButton'); 29 | 30 | liquidationButton.innerText = "Repay " + repayAmount + " " + app.state.asset_repay; 31 | 32 | // update the estimated collection amount text 33 | var assetCollateralAddress = null; 34 | 35 | // first determine which asset the user will be collecting 36 | app.state.TOKENS.forEach(t => { 37 | if (t.symbol === app.state.asset_collect) { 38 | assetCollateralAddress = t.address; 39 | } 40 | }); 41 | 42 | var liduidationDetailsText = document.getElementById('LiquidationDetailsText'); 43 | 44 | if ((assetCollateralAddress !== null) && (repayAmount > 0)) { 45 | // first take the repay amount and convert to eth 46 | var assetRepayExchangeRate = app.state.asset_prices[tokenAddressToBeRepaid]; 47 | // factor in the liquidation discount amount 48 | var estimatedCollectionAmountInEth = (repayAmount * assetRepayExchangeRate) * (1 + app.state.liquidationDiscount); 49 | // then get the exchange rate for the collection asset 50 | var assetCollectExchangeRate = app.state.asset_prices[assetCollateralAddress]; 51 | // console.log(assetCollectExchangeRate); 52 | // and determine how much the user will receive in the collection asset 53 | var estimatedCollectionAmountInAsset = (estimatedCollectionAmountInEth / assetCollectExchangeRate).toFixed(6); 54 | 55 | // the exchange rate between the asset that we're repaying / collecting 56 | var repayForCollectExchangeRate = (repayAmount / estimatedCollectionAmountInAsset).toFixed(4); 57 | 58 | liduidationDetailsText.innerText = "You will collect an (estimated) ~" + estimatedCollectionAmountInAsset + " " + 59 | app.state.asset_collect + ". (Rate = " + repayForCollectExchangeRate + " " + app.state.asset_repay + "/" + 60 | app.state.asset_collect + ")"; 61 | } else { 62 | liduidationDetailsText.innerText = "."; 63 | } 64 | } 65 | 66 | function OnRefreshClicked() { 67 | accountLiquidity = 0; 68 | tokenAddressToBeRepaid = ""; 69 | 70 | document.getElementById('repaySlider').value = 50; 71 | 72 | document.getElementById('LiquidateButton').innerText = "Repay"; 73 | 74 | document.getElementById('LiquidationDetailsText').innerText = "."; 75 | 76 | app.setState({ 77 | borrow_balances : {}, 78 | supply_balances : {}, 79 | 80 | pending_balances: {}, // what we're currently fetching 81 | 82 | asset_repay: "", 83 | asset_collect: "", 84 | 85 | repaySubmittedTxHash : "", 86 | 87 | liquidateBlocked : true 88 | }); 89 | } 90 | 91 | function OnBackClicked() { 92 | accountLiquidity = 0; 93 | tokenAddressToBeRepaid = ""; 94 | 95 | app.setState({ 96 | inspected_address: "", 97 | 98 | borrow_balances: {}, 99 | supply_balances: {}, 100 | 101 | pending_balances: {}, // what we're currently fetching 102 | 103 | asset_repay: "", 104 | asset_collect: "", 105 | 106 | repaySubmittedTxHash : "", 107 | 108 | liquidateBlocked : true 109 | }); 110 | } 111 | 112 | function OnCopyAddressClicked() { 113 | // build the URL we want to copy 114 | var url = "https://conlan.github.io/compound-liquidator?address=" + app.state.inspected_address; 115 | 116 | // hack to copy text to clipboard 117 | const el = document.createElement('textarea'); 118 | el.value = url; 119 | el.setAttribute('readonly', ''); 120 | el.style.position = 'absolute'; 121 | el.style.left = '-9999px'; 122 | document.body.appendChild(el); 123 | el.select(); 124 | document.execCommand('copy'); 125 | document.body.removeChild(el); 126 | 127 | // tell the user what happened 128 | window.alert("\"" + url + "\" copied to clipboard."); 129 | } 130 | 131 | function InitiateLiquidate() { 132 | var requestAmountClose = GetIntendedRepayAmount(); 133 | 134 | if (Number(requestAmountClose) === 0) { 135 | window.alert("Please set an amount greater than 0."); 136 | } else { 137 | var myAccount = web3.account; 138 | var targetAccount = app.state.inspected_address; 139 | 140 | // determine the asset borrow and collateral 141 | var assetBorrow = ""; 142 | var assetBorrowDecimals = 0; 143 | 144 | var assetCollateral = ""; 145 | 146 | app.state.TOKENS.forEach(t => { 147 | if (t.symbol === app.state.asset_collect) { 148 | assetCollateral = t.address; // the asset we're collecting is the one that the target collateralized 149 | } 150 | 151 | if (t.symbol === app.state.asset_repay) { 152 | assetBorrow = t.address; // the asset that the target borrowed is the one that we are repaying on behalf of them 153 | 154 | assetBorrowDecimals = Math.pow(10, t.decimals); 155 | } 156 | }); 157 | 158 | // TODO enforce user's balance available 159 | requestAmountClose = new BigNumber(requestAmountClose * assetBorrowDecimals).toFixed(); 160 | 161 | console.log(requestAmountClose); 162 | 163 | var compoundContract = new web3.web3js.eth.Contract(app.state.LIQUIDATION_ABI, app.state.LIQUIDATION_ADDRESS); 164 | 165 | compoundContract.methods.liquidateBorrow(targetAccount, assetBorrow, assetCollateral, requestAmountClose).send( 166 | { from: myAccount } 167 | ).on('transactionHash', (txHash) => { 168 | // clear out the estimated collection liquidation details 169 | document.getElementById('LiquidationDetailsText').innerText = "."; 170 | 171 | app.setState({ 172 | asset_repay: "", 173 | asset_collect: "", 174 | 175 | repaySubmittedTxHash : txHash 176 | });// TODO await confirmation 177 | }).on("confirmation", (err, receipt) => { 178 | if (app.state.repaySubmittedTxHash === receipt.transactionHash) { 179 | OnRefreshClicked(); 180 | } 181 | }); 182 | } 183 | } 184 | 185 | function GetInspectedAccount() { 186 | var inspected_account = null; 187 | 188 | app.state.accounts.forEach(account => { 189 | if (account.address.toLowerCase() == app.state.inspected_address.toLowerCase()) { 190 | inspected_account = account; 191 | } 192 | }); 193 | 194 | return inspected_account; 195 | } 196 | 197 | function AddressInspector (props) { 198 | app = props.app; 199 | 200 | web3 = useWeb3Context(); 201 | 202 | if (accountLiquidity === 0) { 203 | var compoundContract = new web3.web3js.eth.Contract(app.state.MONEY_MARKET_ABI, app.state.MONEY_MARKET_ADDRESS); 204 | 205 | // only if we're not fetching a pending balance 206 | if (Object.keys(app.state.pending_balances).length === 0) { 207 | compoundContract.methods.getAccountLiquidity(app.state.inspected_address).call(function(error, result) { 208 | if (error == null) { 209 | accountLiquidity = new BigNumber(result / 1e18); 210 | 211 | var liquidateBlocked = (accountLiquidity >= 0); 212 | 213 | app.setState({ 214 | liquidateBlocked : liquidateBlocked 215 | }); 216 | 217 | // reset the repay slider to min 218 | var repaySlider = document.getElementById('repaySlider'); 219 | repaySlider.value = repaySlider.min; 220 | } else { 221 | console.log(error); 222 | } 223 | }); 224 | } 225 | 226 | if (app.state.liquidationDiscount < 0) { 227 | compoundContract.methods.liquidationDiscount().call(function(error, result) { 228 | if (error == null) { 229 | result = result / 1e18; 230 | 231 | app.setState({ 232 | liquidationDiscount : result 233 | }); 234 | } 235 | }); 236 | } 237 | } 238 | 239 | // refresh not disabled by default 240 | var refreshDisabled = false; 241 | 242 | // but check that we have all the borrow balances fetched 243 | if ((Object.keys(app.state.borrow_balances).length) < Object.keys(app.state.TOKENS).length) { 244 | refreshDisabled = true; 245 | } else if ((Object.keys(app.state.supply_balances).length) < Object.keys(app.state.TOKENS).length) { 246 | // and all the supply balances fetched. If either of these aren't fully fetched then disable refresh 247 | refreshDisabled = true; 248 | } 249 | 250 | var canLiquidate = false; 251 | 252 | var liquidationText = "."; 253 | 254 | var transactionSubmittedText = ""; 255 | var transationSubmittedLink = ""; 256 | var transactionSpinnerVisibility = 'hidden'; 257 | 258 | var repaySliderDisabled = true; 259 | 260 | // only enable liquidate button if both asset to repay and collect have been set 261 | if ((app.state.asset_repay.length > 0) && (app.state.asset_collect.length > 0)) { 262 | if (app.state.asset_repay !== app.state.asset_collect) { 263 | canLiquidate = true; 264 | 265 | repaySliderDisabled = false; 266 | 267 | // find the address for the token that the user has selected to repay 268 | app.state.TOKENS.forEach(t => { 269 | if (t.symbol === app.state.asset_repay) { 270 | tokenAddressToBeRepaid = t.address; 271 | } 272 | }); 273 | 274 | // calculate the maximum amount that the user can liquidate 275 | var inspected_account = GetInspectedAccount(); 276 | // we can actually liquidate more than just their account liquidity since after seizing assets from their supply, the account's ratio will go under 1.5x and so forth. 277 | // this determines the maximum amount that we can seize in 1 liquidation 278 | var maxRepayAmountInEth = ((app.state.MIN_COLLATERAL_RATIO * inspected_account.totalEthBorrow) - inspected_account.totalEthSupply) / (app.state.MIN_COLLATERAL_RATIO - app.state.liquidationDiscount - 1); 279 | maxRepayAmountInEth /= 1e18; // convert from wei 280 | 281 | if (tokenAddressToBeRepaid in app.state.asset_prices) { 282 | var assetRepayExchangeRate = app.state.asset_prices[tokenAddressToBeRepaid]; 283 | 284 | maxRepayAmount = (maxRepayAmountInEth / assetRepayExchangeRate); 285 | 286 | // factor in the borrower's balance as the max repay amount too (can't pay more than they borrowed!) 287 | maxRepayAmount = Math.min(maxRepayAmount, app.state.borrow_balances[tokenAddressToBeRepaid]); 288 | } else { 289 | maxRepayAmount = 0; 290 | } 291 | } else { 292 | liquidationText = "Unable to repay " + app.state.asset_repay + " and collect same asset " + app.state.asset_collect + "."; 293 | } 294 | } 295 | 296 | if (app.state.repaySubmittedTxHash.length > 0) { 297 | transactionSubmittedText = "Repay submitted! View your tx: " 298 | transationSubmittedLink = app.state.ETHERSCAN_PREFIX + "tx/" + app.state.repaySubmittedTxHash; 299 | 300 | // show the spinner 301 | transactionSpinnerVisibility = 'visible'; 302 | } 303 | 304 | var liquidationDiscountDisplay = ""; 305 | if (app.state.liquidationDiscount < 0) { 306 | liquidationDiscountDisplay = "-"; 307 | } else { 308 | liquidationDiscountDisplay = (app.state.liquidationDiscount * 100); 309 | } 310 | 311 | var accountLiquidityDisplay = ""; 312 | if (accountLiquidity !== 0) { 313 | accountLiquidityDisplay = accountLiquidity + " ETH"; 314 | } else { 315 | // if account liquidity not set then disable refresh 316 | refreshDisabled = true; 317 | } 318 | 319 | var stateColor = (app.state.inspected_address_state === 'risky') ? '#ffbf00' : 320 | (app.state.inspected_address_state === 'safe') ? '#57d500' : '#ff2e00'; 321 | 322 | var stateText = app.state.inspected_address_state; 323 | 324 | return ( 325 |
326 |
327 |

Address: {app.state.inspected_address}

328 | 329 | 330 | 331 |
332 |

Account Liquidity: {accountLiquidityDisplay}

333 |

State: {stateText}

334 | 335 |

Choose an asset to collect at {liquidationDiscountDisplay}% discount:

336 | 337 | 338 |

Choose a different asset to repay on behalf of borrower to return their Account Liquidity to 0:

339 | 340 | 341 |
342 | 343 |
344 | 345 | 346 | 349 | 350 | OnRepaySliderValueChange()} min={0} max={100} 351 | className="slider" id="repaySlider" disabled={repaySliderDisabled}/> 352 | 353 |
354 | 355 |

{liquidationText}

356 | 357 |
358 |

{transactionSubmittedText}{transationSubmittedLink} loading

360 |
361 |
362 | 363 | ) 364 | } 365 | 366 | export default AddressInspector; -------------------------------------------------------------------------------- /src/components/BalanceTable/BalanceTable.css: -------------------------------------------------------------------------------- 1 | .LiquidateRadioInput { 2 | width: 20px; 3 | position:relative; 4 | } 5 | 6 | .BalanceLoading img { 7 | width: 14px; 8 | height: auto; 9 | } -------------------------------------------------------------------------------- /src/components/BalanceTable/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactTable from "react-table"; 3 | 4 | import BigNumber from "bignumber.js"; 5 | import { useWeb3Context } from "web3-react/hooks"; 6 | 7 | import ERC20 from "../../constants/ERC20.js" 8 | 9 | import "./BalanceTable.css"; 10 | 11 | let app; 12 | let web3; 13 | 14 | function OnEnableTokenClicked (row) { 15 | var tokenAddress = row.original.address; 16 | 17 | var tokenContract = new web3.web3js.eth.Contract(ERC20.ABI, tokenAddress); 18 | 19 | // approve maximum amount of tokens 20 | tokenContract.methods.approve(app.state.LIQUIDATION_ADDRESS, new BigNumber(2**(256) - 1).toFixed() ).send( 21 | { from: web3.account } 22 | ).on('transactionHash', (txHash) => { 23 | console.log("Transaction submitted: " + app.state.ETHERSCAN_PREFIX + "tx/" + txHash); 24 | 25 | // add token address to pending allowances so it shows up a fetching spinner below 26 | app.state.pending_allowances[tokenAddress] = true; 27 | 28 | app.setState({}); 29 | }).on("confirmation", (err, receipt) => { 30 | // remove key from pending allowances 31 | delete app.state.pending_allowances[tokenAddress]; 32 | 33 | app.state.allowance_states[tokenAddress] = true; 34 | 35 | app.setState({}); 36 | }); 37 | } 38 | 39 | function BalanceTable(props) { 40 | app = props.app; 41 | 42 | var balanceType = props.balanceType; 43 | var stateProperty = props.stateProperty; 44 | var borrowerAccount = app.state.inspected_address; 45 | 46 | var data = []; 47 | 48 | web3 = useWeb3Context(); 49 | 50 | var compoundContract = new web3.web3js.eth.Contract( 51 | app.state.MONEY_MARKET_ABI, 52 | app.state.MONEY_MARKET_ADDRESS 53 | ); 54 | 55 | app.state.TOKENS.forEach(tokenData => { 56 | var isAllowed = false; 57 | 58 | if (tokenData.address in app.state.allowance_states) { 59 | isAllowed = app.state.allowance_states[tokenData.address]; 60 | } 61 | 62 | var rowData = { 63 | symbol: tokenData.symbol, 64 | address: tokenData.address, 65 | liquidateAsset: tokenData.symbol, 66 | clickable : false, 67 | disabled : true, 68 | fetching : false, 69 | allowed : isAllowed 70 | }; 71 | 72 | var asset = tokenData.address; 73 | var assetFetchKey = asset + balanceType; 74 | 75 | var tokenDecimals = Math.pow(10, tokenData.decimals); 76 | 77 | rowData[balanceType] = ""; 78 | 79 | if (asset in app.state.pending_allowances) { 80 | rowData.fetching = true; 81 | } 82 | 83 | if (balanceType === "Borrowed" && asset in app.state.borrow_balances) { 84 | rowData["Borrowed"] = app.state.borrow_balances[asset]; 85 | } else if ( 86 | balanceType === "Supplied" && 87 | asset in app.state.supply_balances 88 | ) { 89 | rowData["Supplied"] = app.state.supply_balances[asset]; 90 | } else if (Object.keys(app.state.pending_balances).length > 0) { 91 | // don't re-fetch an asset if we're already fetching one 92 | } else { 93 | app.state.pending_balances[assetFetchKey] = 0; 94 | 95 | rowData.fetching = true; 96 | 97 | if (balanceType === "Borrowed") { 98 | compoundContract.methods 99 | .getBorrowBalance(borrowerAccount, asset) 100 | .call(function(error, result) { 101 | delete app.state.pending_balances[assetFetchKey]; 102 | 103 | if (error === null) { 104 | var newBalances = app.state.borrow_balances; 105 | 106 | var amount = Number((result / tokenDecimals).toFixed(4)); 107 | if (amount === 0) { 108 | amount = "0"; 109 | } 110 | 111 | newBalances[asset] = amount; 112 | 113 | app.setState({ 114 | borrow_balances: newBalances 115 | }); 116 | } else { 117 | app.setState({}); 118 | } 119 | }); 120 | } else { 121 | compoundContract.methods 122 | .getSupplyBalance(borrowerAccount, asset) 123 | .call(function(error, result) { 124 | delete app.state.pending_balances[assetFetchKey]; 125 | 126 | if (error === null) { 127 | var newBalances = app.state.supply_balances; 128 | 129 | var amount = Number((result / tokenDecimals).toFixed(4)); 130 | if (amount === 0) { 131 | amount = "0"; 132 | } 133 | 134 | newBalances[asset] = amount; 135 | 136 | app.setState({ 137 | supply_balances: newBalances 138 | }); 139 | } else { 140 | app.setState({}); 141 | } 142 | }); 143 | } 144 | } 145 | 146 | if (("Supplied" in rowData && Number(rowData.Supplied) === 0) || 147 | ("Borrowed" in rowData && Number(rowData.Borrowed) === 0) ) { 148 | rowData.clickable = false; 149 | } else { 150 | rowData.clickable = true; 151 | } 152 | 153 | rowData.disabled = app.state.liquidateBlocked; 154 | 155 | data.push(rowData); 156 | }); 157 | 158 | // console.log(data); 159 | 160 | var etherScanPrefix = app.state.ETHERSCAN_PREFIX; 161 | 162 | var columns = [ 163 | { 164 | Header: "Symbol", 165 | accessor: "symbol", 166 | maxWidth: 200 167 | }, 168 | { 169 | Header: "Address", 170 | accessor: "address", 171 | Cell: row => ( 172 | 173 | {row.value} 174 | 175 | ) 176 | }, 177 | { 178 | Header: balanceType, 179 | accessor: balanceType, 180 | maxWidth: 200, 181 | className: "right" 182 | }, 183 | { 184 | Header: "", 185 | accessor: "liquidateAsset", 186 | maxWidth: 75, 187 | className: "center", 188 | Cell: row => { 189 | if (row.original.fetching) { 190 | return
loading
191 | } else if (row.original.allowed === false) { 192 | return () 193 | } else if (row.original.clickable) { 194 | return ( 195 | { 201 | // reset the repayment slider position 202 | var repaySlider = document.getElementById('repaySlider'); 203 | repaySlider.value = repaySlider.min; 204 | 205 | if (stateProperty === "asset_repay") { 206 | app.setState({ 207 | asset_repay: row.value 208 | }); 209 | 210 | // reset the liquidate button text 211 | document.getElementById('LiquidateButton').innerText = "Repay 0 " + row.value; 212 | } else { 213 | app.setState({ 214 | asset_collect: row.value 215 | }); 216 | 217 | // reset the liquidate button text if we have an asset to repay set 218 | if (app.state.asset_repay.length > 0) { 219 | document.getElementById('LiquidateButton').innerText = "Repay 0 " + app.state.asset_repay; 220 | } 221 | } 222 | }} 223 | onChange={() => {}} 224 | /> 225 | ) 226 | } else { 227 | return
228 | } 229 | } 230 | } 231 | ]; 232 | 233 | return ( 234 | 243 | ); 244 | } 245 | 246 | export default BalanceTable; 247 | -------------------------------------------------------------------------------- /src/constants/Compound.js: -------------------------------------------------------------------------------- 1 | export default { 2 | tokens : [ 3 | { 4 | "symbol" : "WETH", 5 | "address" : "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", 6 | "decimals" : 18 7 | }, 8 | { 9 | "symbol" : "DAI", 10 | "address" : "0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359", 11 | "decimals" : 18 12 | }, 13 | { 14 | "symbol" : "BAT", 15 | "address" : "0x0D8775F648430679A709E98d2b0Cb6250d2887EF", 16 | "decimals" : 18 17 | }, 18 | { 19 | "symbol" : "ZRX", 20 | "address" : "0xE41d2489571d322189246DaFA5ebDe1F4699F498", 21 | "decimals" : 18 22 | }, 23 | { 24 | "symbol" : "REP", 25 | "address" : "0x1985365e9f78359a9B6AD760e32412f4a445E862", 26 | "decimals" : 18 27 | } 28 | ], 29 | 30 | minCollateralRatio : 1.5, 31 | safeCollateralRatio : 2, 32 | 33 | liquidationAddress: "0x1055be4bf7338c7606d9efdcf80593f180ba043e", 34 | liquidationABI : [{"constant":true,"inputs":[],"name":"moneyMarket","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"targetAccount","type":"address"},{"name":"assetBorrow","type":"address"},{"name":"assetCollateral","type":"address"},{"name":"requestedAmountClose","type":"uint256"}],"name":"liquidateBorrow","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"moneyMarket_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"name":"targetAccount","type":"address","indexed":false},{"name":"assetBorrow","type":"address","indexed":false},{"name":"borrowBalanceBefore","type":"uint256","indexed":false},{"indexed":false,"name":"borrowBalanceAccumulated","type":"uint256"},{"indexed":false,"name":"amountRepaid","type":"uint256"},{"indexed":false,"name":"borrowBalanceAfter","type":"uint256"},{"indexed":false,"name":"liquidator","type":"address"},{"indexed":false,"name":"assetCollateral","type":"address"},{"indexed":false,"name":"collateralBalanceBefore","type":"uint256"},{"indexed":false,"name":"collateralBalanceAccumulated","type":"uint256"},{"indexed":false,"name":"amountSeized","type":"uint256"},{"indexed":false,"name":"collateralBalanceAfter","type":"uint256"}],"name":"BorrowLiquidated","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}], 35 | 36 | moneyMarketAddress : "0x3FDA67f7583380E67ef93072294a7fAc882FD7E7", 37 | moneyMarketABI : [{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"pendingAdmin","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":""}],"name":"paused","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"oracle","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"mantissa"}],"name":"liquidationDiscount","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":"isSupported"},{"type":"uint256","name":"blockNumber"},{"type":"address","name":"interestRateModel"},{"type":"uint256","name":"totalSupply"},{"type":"uint256","name":"supplyRateMantissa"},{"type":"uint256","name":"supplyIndex"},{"type":"uint256","name":"totalBorrows"},{"type":"uint256","name":"borrowRateMantissa"},{"type":"uint256","name":"borrowIndex"}],"name":"markets","inputs":[{"type":"address","name":""}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"mantissa"}],"name":"collateralRatio","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"principal"},{"type":"uint256","name":"interestIndex"}],"name":"supplyBalances","inputs":[{"type":"address","name":""},{"type":"address","name":""}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"mantissa"}],"name":"originationFee","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"collateralMarkets","inputs":[{"type":"uint256","name":""}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"admin","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"principal"},{"type":"uint256","name":"interestIndex"}],"name":"borrowBalances","inputs":[{"type":"address","name":""},{"type":"address","name":""}],"constant":true},{"type":"constructor","stateMutability":"nonpayable","payable":false,"inputs":[]},{"type":"fallback","stateMutability":"payable","payable":true},{"type":"event","name":"SupplyReceived","inputs":[{"type":"address","name":"account","indexed":false},{"type":"address","name":"asset","indexed":false},{"type":"uint256","name":"amount","indexed":false},{"type":"uint256","name":"startingBalance","indexed":false},{"type":"uint256","name":"newBalance","indexed":false}],"anonymous":false},{"type":"event","name":"SupplyWithdrawn","inputs":[{"type":"address","name":"account","indexed":false},{"type":"address","name":"asset","indexed":false},{"type":"uint256","name":"amount","indexed":false},{"type":"uint256","name":"startingBalance","indexed":false},{"type":"uint256","name":"newBalance","indexed":false}],"anonymous":false},{"type":"event","name":"BorrowTaken","inputs":[{"type":"address","name":"account","indexed":false},{"type":"address","name":"asset","indexed":false},{"type":"uint256","name":"amount","indexed":false},{"type":"uint256","name":"startingBalance","indexed":false},{"type":"uint256","name":"borrowAmountWithFee","indexed":false},{"type":"uint256","name":"newBalance","indexed":false}],"anonymous":false},{"type":"event","name":"BorrowRepaid","inputs":[{"type":"address","name":"account","indexed":false},{"type":"address","name":"asset","indexed":false},{"type":"uint256","name":"amount","indexed":false},{"type":"uint256","name":"startingBalance","indexed":false},{"type":"uint256","name":"newBalance","indexed":false}],"anonymous":false},{"type":"event","name":"BorrowLiquidated","inputs":[{"type":"address","name":"targetAccount","indexed":false},{"type":"address","name":"assetBorrow","indexed":false},{"type":"uint256","name":"borrowBalanceBefore","indexed":false},{"type":"uint256","name":"borrowBalanceAccumulated","indexed":false},{"type":"uint256","name":"amountRepaid","indexed":false},{"type":"uint256","name":"borrowBalanceAfter","indexed":false},{"type":"address","name":"liquidator","indexed":false},{"type":"address","name":"assetCollateral","indexed":false},{"type":"uint256","name":"collateralBalanceBefore","indexed":false},{"type":"uint256","name":"collateralBalanceAccumulated","indexed":false},{"type":"uint256","name":"amountSeized","indexed":false},{"type":"uint256","name":"collateralBalanceAfter","indexed":false}],"anonymous":false},{"type":"event","name":"NewPendingAdmin","inputs":[{"type":"address","name":"oldPendingAdmin","indexed":false},{"type":"address","name":"newPendingAdmin","indexed":false}],"anonymous":false},{"type":"event","name":"NewAdmin","inputs":[{"type":"address","name":"oldAdmin","indexed":false},{"type":"address","name":"newAdmin","indexed":false}],"anonymous":false},{"type":"event","name":"NewOracle","inputs":[{"type":"address","name":"oldOracle","indexed":false},{"type":"address","name":"newOracle","indexed":false}],"anonymous":false},{"type":"event","name":"SupportedMarket","inputs":[{"type":"address","name":"asset","indexed":false},{"type":"address","name":"interestRateModel","indexed":false}],"anonymous":false},{"type":"event","name":"NewRiskParameters","inputs":[{"type":"uint256","name":"oldCollateralRatioMantissa","indexed":false},{"type":"uint256","name":"newCollateralRatioMantissa","indexed":false},{"type":"uint256","name":"oldLiquidationDiscountMantissa","indexed":false},{"type":"uint256","name":"newLiquidationDiscountMantissa","indexed":false}],"anonymous":false},{"type":"event","name":"NewOriginationFee","inputs":[{"type":"uint256","name":"oldOriginationFeeMantissa","indexed":false},{"type":"uint256","name":"newOriginationFeeMantissa","indexed":false}],"anonymous":false},{"type":"event","name":"SetMarketInterestRateModel","inputs":[{"type":"address","name":"asset","indexed":false},{"type":"address","name":"interestRateModel","indexed":false}],"anonymous":false},{"type":"event","name":"EquityWithdrawn","inputs":[{"type":"address","name":"asset","indexed":false},{"type":"uint256","name":"equityAvailableBefore","indexed":false},{"type":"uint256","name":"amount","indexed":false},{"type":"address","name":"owner","indexed":false}],"anonymous":false},{"type":"event","name":"SuspendedMarket","inputs":[{"type":"address","name":"asset","indexed":false}],"anonymous":false},{"type":"event","name":"SetPaused","inputs":[{"type":"bool","name":"newState","indexed":false}],"anonymous":false},{"type":"event","name":"Failure","inputs":[{"type":"uint256","name":"error","indexed":false},{"type":"uint256","name":"info","indexed":false},{"type":"uint256","name":"detail","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getCollateralMarketsLength","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"assetPrices","inputs":[{"type":"address","name":"asset"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"_setPendingAdmin","inputs":[{"type":"address","name":"newPendingAdmin"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"_acceptAdmin","inputs":[],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"_setOracle","inputs":[{"type":"address","name":"newOracle"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"_setPaused","inputs":[{"type":"bool","name":"requestedState"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"int256","name":""}],"name":"getAccountLiquidity","inputs":[{"type":"address","name":"account"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getSupplyBalance","inputs":[{"type":"address","name":"account"},{"type":"address","name":"asset"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getBorrowBalance","inputs":[{"type":"address","name":"account"},{"type":"address","name":"asset"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"_supportMarket","inputs":[{"type":"address","name":"asset"},{"type":"address","name":"interestRateModel"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"_suspendMarket","inputs":[{"type":"address","name":"asset"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"_setRiskParameters","inputs":[{"type":"uint256","name":"collateralRatioMantissa"},{"type":"uint256","name":"liquidationDiscountMantissa"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"_setOriginationFee","inputs":[{"type":"uint256","name":"originationFeeMantissa"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"_setMarketInterestRateModel","inputs":[{"type":"address","name":"asset"},{"type":"address","name":"interestRateModel"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"_withdrawEquity","inputs":[{"type":"address","name":"asset"},{"type":"uint256","name":"amount"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"supply","inputs":[{"type":"address","name":"asset"},{"type":"uint256","name":"amount"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"withdraw","inputs":[{"type":"address","name":"asset"},{"type":"uint256","name":"requestedAmount"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""},{"type":"uint256","name":""},{"type":"uint256","name":""}],"name":"calculateAccountValues","inputs":[{"type":"address","name":"userAddress"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"repayBorrow","inputs":[{"type":"address","name":"asset"},{"type":"uint256","name":"amount"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"liquidateBorrow","inputs":[{"type":"address","name":"targetAccount"},{"type":"address","name":"assetBorrow"},{"type":"address","name":"assetCollateral"},{"type":"uint256","name":"requestedAmountClose"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"borrow","inputs":[{"type":"address","name":"asset"},{"type":"uint256","name":"amount"}],"constant":false}], 38 | 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"},{"inputs":[{"name":"_owner","type":"address"}],"payable":false,"stateMutability":"view","type":"function","constant":true,"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}]},{"payable":false,"stateMutability":"view","type":"function","constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}]},{"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","type":"function","constant":false,"outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable"},{"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","type":"function","constant":true,"outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view"},{"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"}] 39 | } -------------------------------------------------------------------------------- /src/constants/CompoundStaging.js: -------------------------------------------------------------------------------- 1 | export default { 2 | tokens : [ 3 | { 4 | "symbol" : "WETH", 5 | "address" : "0xc778417e063141139fce010982780140aa0cd5ab", 6 | "decimals" : 18 7 | }, 8 | { 9 | "symbol" : "DAI", 10 | "address" : "0x4e17c87c52d0e9a0cad3fbc53b77d9514f003807", 11 | "decimals" : 18 12 | }, 13 | { 14 | "symbol" : "BAT", 15 | "address" : "0xbf7bbeef6c56e53f79de37ee9ef5b111335bd2ab", 16 | "decimals" : 18 17 | }, 18 | { 19 | "symbol" : "ZRX", 20 | "address" : "0x8de2f821bc97979b7171e7a6fe065b9e17f73b87", 21 | "decimals" : 18 22 | }, 23 | { 24 | "symbol" : "REP", 25 | "address" : "0x930b647320f738d92f5647b2e5c4458497ce3c95", 26 | "decimals" : 18 27 | } 28 | ], 29 | 30 | minCollateralRatio : 2.0, 31 | safeCollateralRatio : 2.5, 32 | 33 | liquidationAddress: "0xD9aF65A30A0DdB041EcC24b0Ac1779e517A249EE", 34 | liquidationABI : [{"constant":true,"inputs":[],"name":"moneyMarket","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"targetAccount","type":"address"},{"name":"assetBorrow","type":"address"},{"name":"assetCollateral","type":"address"},{"name":"requestedAmountClose","type":"uint256"}],"name":"liquidateBorrow","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"moneyMarket_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"name":"targetAccount","type":"address","indexed":false},{"name":"assetBorrow","type":"address","indexed":false},{"name":"borrowBalanceBefore","type":"uint256","indexed":false},{"indexed":false,"name":"borrowBalanceAccumulated","type":"uint256"},{"indexed":false,"name":"amountRepaid","type":"uint256"},{"indexed":false,"name":"borrowBalanceAfter","type":"uint256"},{"indexed":false,"name":"liquidator","type":"address"},{"indexed":false,"name":"assetCollateral","type":"address"},{"indexed":false,"name":"collateralBalanceBefore","type":"uint256"},{"indexed":false,"name":"collateralBalanceAccumulated","type":"uint256"},{"indexed":false,"name":"amountSeized","type":"uint256"},{"indexed":false,"name":"collateralBalanceAfter","type":"uint256"}],"name":"BorrowLiquidated","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}], 35 | 36 | moneyMarketAddress : "0x61bbd7bd5ee2a202d7e62519750170a52a8dfd45", 37 | moneyMarketABI : [{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"pendingAdmin","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":""}],"name":"paused","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"oracle","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"mantissa"}],"name":"liquidationDiscount","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":"isSupported"},{"type":"uint256","name":"blockNumber"},{"type":"address","name":"interestRateModel"},{"type":"uint256","name":"totalSupply"},{"type":"uint256","name":"supplyRateMantissa"},{"type":"uint256","name":"supplyIndex"},{"type":"uint256","name":"totalBorrows"},{"type":"uint256","name":"borrowRateMantissa"},{"type":"uint256","name":"borrowIndex"}],"name":"markets","inputs":[{"type":"address","name":""}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"mantissa"}],"name":"collateralRatio","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"principal"},{"type":"uint256","name":"interestIndex"}],"name":"supplyBalances","inputs":[{"type":"address","name":""},{"type":"address","name":""}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"mantissa"}],"name":"originationFee","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"collateralMarkets","inputs":[{"type":"uint256","name":""}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"admin","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"principal"},{"type":"uint256","name":"interestIndex"}],"name":"borrowBalances","inputs":[{"type":"address","name":""},{"type":"address","name":""}],"constant":true},{"type":"constructor","stateMutability":"nonpayable","payable":false,"inputs":[]},{"type":"fallback","stateMutability":"payable","payable":true},{"type":"event","name":"SupplyReceived","inputs":[{"type":"address","name":"account","indexed":false},{"type":"address","name":"asset","indexed":false},{"type":"uint256","name":"amount","indexed":false},{"type":"uint256","name":"startingBalance","indexed":false},{"type":"uint256","name":"newBalance","indexed":false}],"anonymous":false},{"type":"event","name":"SupplyWithdrawn","inputs":[{"type":"address","name":"account","indexed":false},{"type":"address","name":"asset","indexed":false},{"type":"uint256","name":"amount","indexed":false},{"type":"uint256","name":"startingBalance","indexed":false},{"type":"uint256","name":"newBalance","indexed":false}],"anonymous":false},{"type":"event","name":"BorrowTaken","inputs":[{"type":"address","name":"account","indexed":false},{"type":"address","name":"asset","indexed":false},{"type":"uint256","name":"amount","indexed":false},{"type":"uint256","name":"startingBalance","indexed":false},{"type":"uint256","name":"borrowAmountWithFee","indexed":false},{"type":"uint256","name":"newBalance","indexed":false}],"anonymous":false},{"type":"event","name":"BorrowRepaid","inputs":[{"type":"address","name":"account","indexed":false},{"type":"address","name":"asset","indexed":false},{"type":"uint256","name":"amount","indexed":false},{"type":"uint256","name":"startingBalance","indexed":false},{"type":"uint256","name":"newBalance","indexed":false}],"anonymous":false},{"type":"event","name":"BorrowLiquidated","inputs":[{"type":"address","name":"targetAccount","indexed":false},{"type":"address","name":"assetBorrow","indexed":false},{"type":"uint256","name":"borrowBalanceBefore","indexed":false},{"type":"uint256","name":"borrowBalanceAccumulated","indexed":false},{"type":"uint256","name":"amountRepaid","indexed":false},{"type":"uint256","name":"borrowBalanceAfter","indexed":false},{"type":"address","name":"liquidator","indexed":false},{"type":"address","name":"assetCollateral","indexed":false},{"type":"uint256","name":"collateralBalanceBefore","indexed":false},{"type":"uint256","name":"collateralBalanceAccumulated","indexed":false},{"type":"uint256","name":"amountSeized","indexed":false},{"type":"uint256","name":"collateralBalanceAfter","indexed":false}],"anonymous":false},{"type":"event","name":"NewPendingAdmin","inputs":[{"type":"address","name":"oldPendingAdmin","indexed":false},{"type":"address","name":"newPendingAdmin","indexed":false}],"anonymous":false},{"type":"event","name":"NewAdmin","inputs":[{"type":"address","name":"oldAdmin","indexed":false},{"type":"address","name":"newAdmin","indexed":false}],"anonymous":false},{"type":"event","name":"NewOracle","inputs":[{"type":"address","name":"oldOracle","indexed":false},{"type":"address","name":"newOracle","indexed":false}],"anonymous":false},{"type":"event","name":"SupportedMarket","inputs":[{"type":"address","name":"asset","indexed":false},{"type":"address","name":"interestRateModel","indexed":false}],"anonymous":false},{"type":"event","name":"NewRiskParameters","inputs":[{"type":"uint256","name":"oldCollateralRatioMantissa","indexed":false},{"type":"uint256","name":"newCollateralRatioMantissa","indexed":false},{"type":"uint256","name":"oldLiquidationDiscountMantissa","indexed":false},{"type":"uint256","name":"newLiquidationDiscountMantissa","indexed":false}],"anonymous":false},{"type":"event","name":"NewOriginationFee","inputs":[{"type":"uint256","name":"oldOriginationFeeMantissa","indexed":false},{"type":"uint256","name":"newOriginationFeeMantissa","indexed":false}],"anonymous":false},{"type":"event","name":"SetMarketInterestRateModel","inputs":[{"type":"address","name":"asset","indexed":false},{"type":"address","name":"interestRateModel","indexed":false}],"anonymous":false},{"type":"event","name":"EquityWithdrawn","inputs":[{"type":"address","name":"asset","indexed":false},{"type":"uint256","name":"equityAvailableBefore","indexed":false},{"type":"uint256","name":"amount","indexed":false},{"type":"address","name":"owner","indexed":false}],"anonymous":false},{"type":"event","name":"SuspendedMarket","inputs":[{"type":"address","name":"asset","indexed":false}],"anonymous":false},{"type":"event","name":"SetPaused","inputs":[{"type":"bool","name":"newState","indexed":false}],"anonymous":false},{"type":"event","name":"Failure","inputs":[{"type":"uint256","name":"error","indexed":false},{"type":"uint256","name":"info","indexed":false},{"type":"uint256","name":"detail","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getCollateralMarketsLength","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"assetPrices","inputs":[{"type":"address","name":"asset"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"_setPendingAdmin","inputs":[{"type":"address","name":"newPendingAdmin"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"_acceptAdmin","inputs":[],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"_setOracle","inputs":[{"type":"address","name":"newOracle"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"_setPaused","inputs":[{"type":"bool","name":"requestedState"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"int256","name":""}],"name":"getAccountLiquidity","inputs":[{"type":"address","name":"account"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getSupplyBalance","inputs":[{"type":"address","name":"account"},{"type":"address","name":"asset"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getBorrowBalance","inputs":[{"type":"address","name":"account"},{"type":"address","name":"asset"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"_supportMarket","inputs":[{"type":"address","name":"asset"},{"type":"address","name":"interestRateModel"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"_suspendMarket","inputs":[{"type":"address","name":"asset"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"_setRiskParameters","inputs":[{"type":"uint256","name":"collateralRatioMantissa"},{"type":"uint256","name":"liquidationDiscountMantissa"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"_setOriginationFee","inputs":[{"type":"uint256","name":"originationFeeMantissa"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"_setMarketInterestRateModel","inputs":[{"type":"address","name":"asset"},{"type":"address","name":"interestRateModel"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"_withdrawEquity","inputs":[{"type":"address","name":"asset"},{"type":"uint256","name":"amount"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"supply","inputs":[{"type":"address","name":"asset"},{"type":"uint256","name":"amount"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"withdraw","inputs":[{"type":"address","name":"asset"},{"type":"uint256","name":"requestedAmount"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""},{"type":"uint256","name":""},{"type":"uint256","name":""}],"name":"calculateAccountValues","inputs":[{"type":"address","name":"userAddress"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"repayBorrow","inputs":[{"type":"address","name":"asset"},{"type":"uint256","name":"amount"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"liquidateBorrow","inputs":[{"type":"address","name":"targetAccount"},{"type":"address","name":"assetBorrow"},{"type":"address","name":"assetCollateral"},{"type":"uint256","name":"requestedAmountClose"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"borrow","inputs":[{"type":"address","name":"asset"},{"type":"uint256","name":"amount"}],"constant":false}], 38 | 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"},{"inputs":[{"name":"_owner","type":"address"}],"payable":false,"stateMutability":"view","type":"function","constant":true,"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}]},{"payable":false,"stateMutability":"view","type":"function","constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}]},{"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","type":"function","constant":false,"outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable"},{"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","type":"function","constant":true,"outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view"},{"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"}] 39 | } -------------------------------------------------------------------------------- /src/constants/ERC20.js: -------------------------------------------------------------------------------- 1 | export default { 2 | ABI : [{"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"},{"inputs":[{"name":"_owner","type":"address"}],"payable":false,"stateMutability":"view","type":"function","constant":true,"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}]},{"payable":false,"stateMutability":"view","type":"function","constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}]},{"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","type":"function","constant":false,"outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable"},{"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","type":"function","constant":true,"outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view"},{"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"}] 3 | } -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; 3 | 4 | font-size: 14px; 5 | 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import Web3Provider from 'web3-react' 4 | 5 | import './index.css'; 6 | import App from './App'; 7 | import * as serviceWorker from './serviceWorker'; 8 | 9 | function AppWrapper () { 10 | return ( 11 | 12 | 13 | 14 | ) 15 | } 16 | 17 | const rootElement = document.getElementById("root") 18 | ReactDOM.render(, rootElement); 19 | 20 | // If you want your app to work offline and load faster, you can change 21 | // unregister() to register() below. Note this comes with some pitfalls. 22 | // Learn more about service workers: http://bit.ly/CRA-PWA 23 | serviceWorker.unregister(); 24 | -------------------------------------------------------------------------------- /src/samplejson.json: -------------------------------------------------------------------------------- 1 | 2 | {"request":{"page_size":100,"page_number":1,"min_borrow_value_in_eth":{"value":"10000000000000000"},"max_collateral_ratio":{"value":"50"}},"pagination_summary":{"total_pages":1,"total_entries":27,"page_size":100,"page_number":1},"error":null,"account_values":[{"total_supply_value_in_eth":{"value":"100208241726887461.0000000000"},"total_borrow_value_in_eth":{"value":"54630940400959740.85656468000"},"block_updated":3457815,"address":"0xe4aa4aa4d768aeed2a6c84d698fecad89d24773b"},{"total_supply_value_in_eth":{"value":"100194427264004689.0000000000"},"total_borrow_value_in_eth":{"value":"53876859950636485.33804118000"},"block_updated":3457818,"address":"0x101445367f48e43c47c7baa6927564ef2e00d223"},{"total_supply_value_in_eth":{"value":"50097153622493126.00000000000"},"total_borrow_value_in_eth":{"value":"26938298486911046.10940200000"},"block_updated":3457815,"address":"0x8739bd1c2fca06bb220af051e249d7b3d990781c"},{"total_supply_value_in_eth":{"value":"100195482261302811.0000000000"},"total_borrow_value_in_eth":{"value":"53283810192545369.50099228000"},"block_updated":3457818,"address":"0x535432535c3fe8fd07902989e33bbacf72cf0814"},{"total_supply_value_in_eth":{"value":"409788072063739574.6570650493"},"total_borrow_value_in_eth":{"value":"214400189185339045.0913788207"},"block_updated":3457819,"address":"0xfd72981e24356f3b2b01e1d0c18cc7fead37f432"},{"total_supply_value_in_eth":{"value":"292167046631508052.0000000000"},"total_borrow_value_in_eth":{"value":"150885386377251664.0995372959"},"block_updated":3457818,"address":"0x2bc8a15c1f261a484d69fe093f15fa0646e5b3c3"},{"total_supply_value_in_eth":{"value":"20866968170587381302.22465688"},"total_borrow_value_in_eth":{"value":"10711281861950702778.81723024"},"block_updated":3457819,"address":"0x0006e4548aed4502ec8c844567840ce6ef1013f5"},{"total_supply_value_in_eth":{"value":"29439313978196062009.32754032"},"total_borrow_value_in_eth":{"value":"15029152139281420827.70211988"},"block_updated":3457819,"address":"0xb50eb12ecbe6371401f9b56c2aa12fe75c129d85"},{"total_supply_value_in_eth":{"value":"400990134023290053.0000000000"},"total_borrow_value_in_eth":{"value":"201966196533810874.6046310600"},"block_updated":3457819,"address":"0xf34171f8a56f36b72564897b05fca68efc28009b"},{"total_supply_value_in_eth":{"value":"100251559356883239.0000000000"},"total_borrow_value_in_eth":{"value":"50320836012431668.01917034000"},"block_updated":3457815,"address":"0x223374626583c65599c8c9dc1e23151224e77055"},{"total_supply_value_in_eth":{"value":"137153557860300391004.0000000"},"total_borrow_value_in_eth":{"value":"67901926197631531396.00000000"},"block_updated":3457815,"address":"0x53de1a33faf57e9341b532581847d13a2eb47814"},{"total_supply_value_in_eth":{"value":"8363838288617173972.389177394"},"total_borrow_value_in_eth":{"value":"4109130015616893259.398679842"},"block_updated":3457819,"address":"0x3e78b6e6066544c1483f0603ca8996c4892cd418"},{"total_supply_value_in_eth":{"value":"486711877865839783.0000000000"},"total_borrow_value_in_eth":{"value":"235803845691211419.8752037200"},"block_updated":3457819,"address":"0x0bcb7179e5a76aeacc1d362b4425509383e9e5b0"},{"total_supply_value_in_eth":{"value":"1007096597366537941.000000000"},"total_borrow_value_in_eth":{"value":"479408845229923823.4421940922"},"block_updated":3457819,"address":"0xd840d02bb3a715027343fc8428b61a7f83dcb6e7"},{"total_supply_value_in_eth":{"value":"1545455444667480929.000000000"},"total_borrow_value_in_eth":{"value":"687539006921800941.2449997440"},"block_updated":3457819,"address":"0x96a58046bd99208aa5025d25cd951ca5b7f1a449"},{"total_supply_value_in_eth":{"value":"200140677880708297.0000000000"},"total_borrow_value_in_eth":{"value":"89036672481619391.31684839662"},"block_updated":3457815,"address":"0x32720c334845503aec2eb0ef4e85b5bc5118dba1"},{"total_supply_value_in_eth":{"value":"100069649020150235.0000000000"},"total_borrow_value_in_eth":{"value":"44517639851578864.10399918545"},"block_updated":3457815,"address":"0x7b3baacf28a58b9c22a744ae31b0cfebd8379865"},{"total_supply_value_in_eth":{"value":"3007224060547574993.000000000"},"total_borrow_value_in_eth":{"value":"1021958872530231310.000000000"},"block_updated":3457815,"address":"0xe41086f6e4801bf49c0f9e24fb147cdfaf952a89"},{"total_supply_value_in_eth":{"value":"7591582995616502100.055486520"},"total_borrow_value_in_eth":{"value":"1750550732665539838.576027819"},"block_updated":3457819,"address":"0x4ff00ab850d02f36975ac90fc4377734694f5b81"},{"total_supply_value_in_eth":{"value":"238041707349444783.7610773908"},"total_borrow_value_in_eth":{"value":"52120213177019600.36178623549"},"block_updated":3457818,"address":"0x13a9a2ae288b5ee9de55d5355b3344fb1a601934"},{"total_supply_value_in_eth":{"value":"563363117424643177.3429691200"},"total_borrow_value_in_eth":{"value":"100978460174682087.0000000000"},"block_updated":3457815,"address":"0x7fe8562b325d44ce396d95ab1c3f449730db4779"},{"total_supply_value_in_eth":{"value":"208204191289425396.0000000000"},"total_borrow_value_in_eth":{"value":"30964764416905250.32477247857"},"block_updated":3457815,"address":"0x878368019cec9498d22e28b41fe59ab3deb0673c"},{"total_supply_value_in_eth":{"value":"1525629616414402781.350488036"},"total_borrow_value_in_eth":{"value":"215185913939927343.8513594000"},"block_updated":3457819,"address":"0xb819706e897eacf235cdb5048962bd65873202c4"},{"total_supply_value_in_eth":{"value":"577817181523042989.1672382804"},"total_borrow_value_in_eth":{"value":"74899321160195019.86430098000"},"block_updated":3457815,"address":"0xe3308cc77d42669cc60a9df8e53252002c46414b"},{"total_supply_value_in_eth":{"value":"174322549922183862.2600052336"},"total_borrow_value_in_eth":{"value":"20523385633932424.00000000000"},"block_updated":3457815,"address":"0x94da43c587c515ad30ea86a208603a7586d2c25f"},{"total_supply_value_in_eth":{"value":"146442730034780888.6028209600"},"total_borrow_value_in_eth":{"value":"14902199903355031.59506532000"},"block_updated":3457815,"address":"0xd523c9fd5ded98bd6abc05ee3392dd750ea0059a"},{"total_supply_value_in_eth":{"value":"7453132591192674831.834897600"},"total_borrow_value_in_eth":{"value":"176452418711310452.4989525625"},"block_updated":3457819,"address":"0xa978ac296565724475edbe0c7f71f964ed59c02d"}]} 3 | -------------------------------------------------------------------------------- /src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read http://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.1/8 is considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit http://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See http://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl) 104 | .then(response => { 105 | // Ensure service worker exists, and that we really are getting a JS file. 106 | const contentType = response.headers.get('content-type'); 107 | if ( 108 | response.status === 404 || 109 | (contentType != null && contentType.indexOf('javascript') === -1) 110 | ) { 111 | // No service worker found. Probably a different app. Reload the page. 112 | navigator.serviceWorker.ready.then(registration => { 113 | registration.unregister().then(() => { 114 | window.location.reload(); 115 | }); 116 | }); 117 | } else { 118 | // Service worker found. Proceed as normal. 119 | registerValidSW(swUrl, config); 120 | } 121 | }) 122 | .catch(() => { 123 | console.log( 124 | 'No internet connection found. App is running in offline mode.' 125 | ); 126 | }); 127 | } 128 | 129 | export function unregister() { 130 | if ('serviceWorker' in navigator) { 131 | navigator.serviceWorker.ready.then(registration => { 132 | registration.unregister(); 133 | }); 134 | } 135 | } 136 | --------------------------------------------------------------------------------