├── .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 |
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 |
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 |
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 |
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
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 |
--------------------------------------------------------------------------------