diff --git a/README.md b/README.md index 41298753c3..b33dfce1d0 100644 --- a/README.md +++ b/README.md @@ -15,4 +15,4 @@ Create a `.env` file. See `.env.example` for environment variables which may be For Omen builds, use `yarn build`. -The `build` directory in the `app` directory will now contain the build to be served. +The `build` directory in the `app` directory will now contain the build to be served. \ No newline at end of file diff --git a/app/.eslintignore b/app/.eslintignore index 5e2fd58f63..59d842427d 100644 --- a/app/.eslintignore +++ b/app/.eslintignore @@ -1,2 +1,3 @@ src/serviceWorker.ts +src/abi/compound_abi.ts *.d.ts \ No newline at end of file diff --git a/app/package.json b/app/package.json index db0bb95bad..0d652c2e0d 100644 --- a/app/package.json +++ b/app/package.json @@ -51,7 +51,7 @@ "axios": "^0.21.1", "babel-loader": "^8.1.0", "babel-plugin-named-asset-import": "^0.3.6", - "big.js": "^5.2.2", + "big.js": "^6.0.3", "circular-dependency-plugin": "^5.2.0", "common-tags": "^1.8.0", "contract-proxy-kit": "2.2.0-alpha.1", diff --git a/app/src/abi/compound_abi.ts b/app/src/abi/compound_abi.ts new file mode 100644 index 0000000000..43d9a54cb7 --- /dev/null +++ b/app/src/abi/compound_abi.ts @@ -0,0 +1,15 @@ +/* eslint no-use-before-define: 0 */ // --> OFF + +export const cBATAbi = [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x06fdde03"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"amount","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x095ea7b3"},{"constant":false,"inputs":[{"name":"repayAmount","type":"uint256"}],"name":"repayBorrow","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x0e752702"},{"constant":true,"inputs":[],"name":"reserveFactorMantissa","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x173b9904"},{"constant":false,"inputs":[{"name":"account","type":"address"}],"name":"borrowBalanceCurrent","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x17bfdfbc"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x18160ddd"},{"constant":true,"inputs":[],"name":"exchangeRateStored","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x182df0f5"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x23b872dd"},{"constant":false,"inputs":[{"name":"borrower","type":"address"},{"name":"repayAmount","type":"uint256"}],"name":"repayBorrowBehalf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x2608f818"},{"constant":true,"inputs":[],"name":"pendingAdmin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x26782247"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x313ce567"},{"constant":false,"inputs":[{"name":"owner","type":"address"}],"name":"balanceOfUnderlying","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x3af9e669"},{"constant":true,"inputs":[],"name":"getCash","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x3b1d21a2"},{"constant":false,"inputs":[{"name":"newComptroller","type":"address"}],"name":"_setComptroller","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x4576b5db"},{"constant":true,"inputs":[],"name":"totalBorrows","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x47bd3718"},{"constant":true,"inputs":[],"name":"comptroller","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x5fe3b567"},{"constant":false,"inputs":[{"name":"reduceAmount","type":"uint256"}],"name":"_reduceReserves","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x601a0bf1"},{"constant":true,"inputs":[],"name":"initialExchangeRateMantissa","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x675d972c"},{"constant":true,"inputs":[],"name":"accrualBlockNumber","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x6c540baf"},{"constant":true,"inputs":[],"name":"underlying","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x6f307dc3"},{"constant":true,"inputs":[{"name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x70a08231"},{"constant":false,"inputs":[],"name":"totalBorrowsCurrent","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x73acee98"},{"constant":false,"inputs":[{"name":"redeemAmount","type":"uint256"}],"name":"redeemUnderlying","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x852a12e3"},{"constant":true,"inputs":[],"name":"totalReserves","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x8f840ddd"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x95d89b41"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"borrowBalanceStored","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x95dd9193"},{"constant":false,"inputs":[{"name":"mintAmount","type":"uint256"}],"name":"mint","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa0712d68"},{"constant":false,"inputs":[],"name":"accrueInterest","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa6afed95"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa9059cbb"},{"constant":true,"inputs":[],"name":"borrowIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xaa5af0fd"},{"constant":true,"inputs":[],"name":"supplyRatePerBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xae9d70b0"},{"constant":false,"inputs":[{"name":"liquidator","type":"address"},{"name":"borrower","type":"address"},{"name":"seizeTokens","type":"uint256"}],"name":"seize","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xb2a02ff1"},{"constant":false,"inputs":[{"name":"newPendingAdmin","type":"address"}],"name":"_setPendingAdmin","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xb71d1a0c"},{"constant":false,"inputs":[],"name":"exchangeRateCurrent","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xbd6d894d"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"getAccountSnapshot","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xc37f68e2"},{"constant":false,"inputs":[{"name":"borrowAmount","type":"uint256"}],"name":"borrow","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xc5ebeaec"},{"constant":false,"inputs":[{"name":"redeemTokens","type":"uint256"}],"name":"redeem","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xdb006a75"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xdd62ed3e"},{"constant":false,"inputs":[],"name":"_acceptAdmin","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xe9c714f2"},{"constant":false,"inputs":[{"name":"newInterestRateModel","type":"address"}],"name":"_setInterestRateModel","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xf2b3abbd"},{"constant":true,"inputs":[],"name":"interestRateModel","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf3fdb15a"},{"constant":false,"inputs":[{"name":"borrower","type":"address"},{"name":"repayAmount","type":"uint256"},{"name":"cTokenCollateral","type":"address"}],"name":"liquidateBorrow","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xf5e3c462"},{"constant":true,"inputs":[],"name":"admin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf851a440"},{"constant":true,"inputs":[],"name":"borrowRatePerBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf8f9da28"},{"constant":false,"inputs":[{"name":"newReserveFactorMantissa","type":"uint256"}],"name":"_setReserveFactor","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xfca7820b"},{"constant":true,"inputs":[],"name":"isCToken","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xfe9c44ae"},{"inputs":[{"name":"underlying_","type":"address"},{"name":"comptroller_","type":"address"},{"name":"interestRateModel_","type":"address"},{"name":"initialExchangeRateMantissa_","type":"uint256"},{"name":"name_","type":"string"},{"name":"symbol_","type":"string"},{"name":"decimals_","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"interestAccumulated","type":"uint256"},{"indexed":false,"name":"borrowIndex","type":"uint256"},{"indexed":false,"name":"totalBorrows","type":"uint256"}],"name":"AccrueInterest","type":"event","signature":"0x875352fb3fadeb8c0be7cbbe8ff761b308fa7033470cd0287f02f3436fd76cb9"},{"anonymous":false,"inputs":[{"indexed":false,"name":"minter","type":"address"},{"indexed":false,"name":"mintAmount","type":"uint256"},{"indexed":false,"name":"mintTokens","type":"uint256"}],"name":"Mint","type":"event","signature":"0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f"},{"anonymous":false,"inputs":[{"indexed":false,"name":"redeemer","type":"address"},{"indexed":false,"name":"redeemAmount","type":"uint256"},{"indexed":false,"name":"redeemTokens","type":"uint256"}],"name":"Redeem","type":"event","signature":"0xe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929"},{"anonymous":false,"inputs":[{"indexed":false,"name":"borrower","type":"address"},{"indexed":false,"name":"borrowAmount","type":"uint256"},{"indexed":false,"name":"accountBorrows","type":"uint256"},{"indexed":false,"name":"totalBorrows","type":"uint256"}],"name":"Borrow","type":"event","signature":"0x13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab80"},{"anonymous":false,"inputs":[{"indexed":false,"name":"payer","type":"address"},{"indexed":false,"name":"borrower","type":"address"},{"indexed":false,"name":"repayAmount","type":"uint256"},{"indexed":false,"name":"accountBorrows","type":"uint256"},{"indexed":false,"name":"totalBorrows","type":"uint256"}],"name":"RepayBorrow","type":"event","signature":"0x1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1"},{"anonymous":false,"inputs":[{"indexed":false,"name":"liquidator","type":"address"},{"indexed":false,"name":"borrower","type":"address"},{"indexed":false,"name":"repayAmount","type":"uint256"},{"indexed":false,"name":"cTokenCollateral","type":"address"},{"indexed":false,"name":"seizeTokens","type":"uint256"}],"name":"LiquidateBorrow","type":"event","signature":"0x298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldPendingAdmin","type":"address"},{"indexed":false,"name":"newPendingAdmin","type":"address"}],"name":"NewPendingAdmin","type":"event","signature":"0xca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldAdmin","type":"address"},{"indexed":false,"name":"newAdmin","type":"address"}],"name":"NewAdmin","type":"event","signature":"0xf9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldComptroller","type":"address"},{"indexed":false,"name":"newComptroller","type":"address"}],"name":"NewComptroller","type":"event","signature":"0x7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldInterestRateModel","type":"address"},{"indexed":false,"name":"newInterestRateModel","type":"address"}],"name":"NewMarketInterestRateModel","type":"event","signature":"0xedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldReserveFactorMantissa","type":"uint256"},{"indexed":false,"name":"newReserveFactorMantissa","type":"uint256"}],"name":"NewReserveFactor","type":"event","signature":"0xaaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f821460"},{"anonymous":false,"inputs":[{"indexed":false,"name":"admin","type":"address"},{"indexed":false,"name":"reduceAmount","type":"uint256"},{"indexed":false,"name":"newTotalReserves","type":"uint256"}],"name":"ReservesReduced","type":"event","signature":"0x3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e"},{"anonymous":false,"inputs":[{"indexed":false,"name":"error","type":"uint256"},{"indexed":false,"name":"info","type":"uint256"},{"indexed":false,"name":"detail","type":"uint256"}],"name":"Failure","type":"event","signature":"0x45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Transfer","type":"event","signature":"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Approval","type":"event","signature":"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"}] + +export const cDaiAbi = [{"inputs":[{"internalType":"address","name":"underlying_","type":"address"},{"internalType":"contract ComptrollerInterface","name":"comptroller_","type":"address"},{"internalType":"contract InterestRateModel","name":"interestRateModel_","type":"address"},{"internalType":"uint256","name":"initialExchangeRateMantissa_","type":"uint256"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"},{"internalType":"address payable","name":"admin_","type":"address"},{"internalType":"address","name":"implementation_","type":"address"},{"internalType":"bytes","name":"becomeImplementationData","type":"bytes"}],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"cashPrior","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestAccumulated","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"AccrueInterest","type":"event","signature":"0x4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc04"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event","signature":"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accountBorrows","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"Borrow","type":"event","signature":"0x13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab80"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"error","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"info","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"detail","type":"uint256"}],"name":"Failure","type":"event","signature":"0x45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liquidator","type":"address"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"cTokenCollateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"LiquidateBorrow","type":"event","signature":"0x298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint256","name":"mintAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mintTokens","type":"uint256"}],"name":"Mint","type":"event","signature":"0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"NewAdmin","type":"event","signature":"0xf9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract ComptrollerInterface","name":"oldComptroller","type":"address"},{"indexed":false,"internalType":"contract ComptrollerInterface","name":"newComptroller","type":"address"}],"name":"NewComptroller","type":"event","signature":"0x7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldImplementation","type":"address"},{"indexed":false,"internalType":"address","name":"newImplementation","type":"address"}],"name":"NewImplementation","type":"event","signature":"0xd604de94d45953f9138079ec1b82d533cb2160c906d1076d1f7ed54befbca97a"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract InterestRateModel","name":"oldInterestRateModel","type":"address"},{"indexed":false,"internalType":"contract InterestRateModel","name":"newInterestRateModel","type":"address"}],"name":"NewMarketInterestRateModel","type":"event","signature":"0xedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldPendingAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newPendingAdmin","type":"address"}],"name":"NewPendingAdmin","type":"event","signature":"0xca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldReserveFactorMantissa","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newReserveFactorMantissa","type":"uint256"}],"name":"NewReserveFactor","type":"event","signature":"0xaaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f821460"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"redeemer","type":"address"},{"indexed":false,"internalType":"uint256","name":"redeemAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"redeemTokens","type":"uint256"}],"name":"Redeem","type":"event","signature":"0xe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"payer","type":"address"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accountBorrows","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"RepayBorrow","type":"event","signature":"0x1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"benefactor","type":"address"},{"indexed":false,"internalType":"uint256","name":"addAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTotalReserves","type":"uint256"}],"name":"ReservesAdded","type":"event","signature":"0xa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc5"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"uint256","name":"reduceAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTotalReserves","type":"uint256"}],"name":"ReservesReduced","type":"event","signature":"0x3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event","signature":"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":false,"inputs":[],"name":"_acceptAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xe9c714f2"},{"constant":false,"inputs":[{"internalType":"uint256","name":"addAmount","type":"uint256"}],"name":"_addReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x3e941010"},{"constant":false,"inputs":[{"internalType":"uint256","name":"reduceAmount","type":"uint256"}],"name":"_reduceReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x601a0bf1"},{"constant":false,"inputs":[{"internalType":"contract ComptrollerInterface","name":"newComptroller","type":"address"}],"name":"_setComptroller","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x4576b5db"},{"constant":false,"inputs":[{"internalType":"address","name":"implementation_","type":"address"},{"internalType":"bool","name":"allowResign","type":"bool"},{"internalType":"bytes","name":"becomeImplementationData","type":"bytes"}],"name":"_setImplementation","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x555bcc40"},{"constant":false,"inputs":[{"internalType":"contract InterestRateModel","name":"newInterestRateModel","type":"address"}],"name":"_setInterestRateModel","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xf2b3abbd"},{"constant":false,"inputs":[{"internalType":"address payable","name":"newPendingAdmin","type":"address"}],"name":"_setPendingAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xb71d1a0c"},{"constant":false,"inputs":[{"internalType":"uint256","name":"newReserveFactorMantissa","type":"uint256"}],"name":"_setReserveFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xfca7820b"},{"constant":true,"inputs":[],"name":"accrualBlockNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x6c540baf"},{"constant":false,"inputs":[],"name":"accrueInterest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa6afed95"},{"constant":true,"inputs":[],"name":"admin","outputs":[{"internalType":"address payable","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf851a440"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xdd62ed3e"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x095ea7b3"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x70a08231"},{"constant":false,"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOfUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x3af9e669"},{"constant":false,"inputs":[{"internalType":"uint256","name":"borrowAmount","type":"uint256"}],"name":"borrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xc5ebeaec"},{"constant":false,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"borrowBalanceCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x17bfdfbc"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"borrowBalanceStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x95dd9193"},{"constant":true,"inputs":[],"name":"borrowIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xaa5af0fd"},{"constant":true,"inputs":[],"name":"borrowRatePerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf8f9da28"},{"constant":true,"inputs":[],"name":"comptroller","outputs":[{"internalType":"contract ComptrollerInterface","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x5fe3b567"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x313ce567"},{"constant":false,"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"delegateToImplementation","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x0933c1ed"},{"constant":true,"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"delegateToViewImplementation","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x4487152f"},{"constant":false,"inputs":[],"name":"exchangeRateCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xbd6d894d"},{"constant":true,"inputs":[],"name":"exchangeRateStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x182df0f5"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAccountSnapshot","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xc37f68e2"},{"constant":true,"inputs":[],"name":"getCash","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x3b1d21a2"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x5c60da1b"},{"constant":true,"inputs":[],"name":"interestRateModel","outputs":[{"internalType":"contract InterestRateModel","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf3fdb15a"},{"constant":true,"inputs":[],"name":"isCToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xfe9c44ae"},{"constant":false,"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"contract CTokenInterface","name":"cTokenCollateral","type":"address"}],"name":"liquidateBorrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xf5e3c462"},{"constant":false,"inputs":[{"internalType":"uint256","name":"mintAmount","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa0712d68"},{"constant":true,"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x06fdde03"},{"constant":true,"inputs":[],"name":"pendingAdmin","outputs":[{"internalType":"address payable","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x26782247"},{"constant":false,"inputs":[{"internalType":"uint256","name":"redeemTokens","type":"uint256"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xdb006a75"},{"constant":false,"inputs":[{"internalType":"uint256","name":"redeemAmount","type":"uint256"}],"name":"redeemUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x852a12e3"},{"constant":false,"inputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repayBorrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x0e752702"},{"constant":false,"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repayBorrowBehalf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x2608f818"},{"constant":true,"inputs":[],"name":"reserveFactorMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x173b9904"},{"constant":false,"inputs":[{"internalType":"address","name":"liquidator","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"seize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xb2a02ff1"},{"constant":true,"inputs":[],"name":"supplyRatePerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xae9d70b0"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x95d89b41"},{"constant":true,"inputs":[],"name":"totalBorrows","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x47bd3718"},{"constant":false,"inputs":[],"name":"totalBorrowsCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x73acee98"},{"constant":true,"inputs":[],"name":"totalReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x8f840ddd"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x18160ddd"},{"constant":false,"inputs":[{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa9059cbb"},{"constant":false,"inputs":[{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x23b872dd"},{"constant":true,"inputs":[],"name":"underlying","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x6f307dc3"}] + +export const cETHAbi = [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x06fdde03"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"amount","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x095ea7b3"},{"constant":false,"inputs":[],"name":"mint","outputs":[],"payable":true,"stateMutability":"payable","type":"function","signature":"0x1249c58b"},{"constant":true,"inputs":[],"name":"reserveFactorMantissa","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x173b9904"},{"constant":false,"inputs":[{"name":"account","type":"address"}],"name":"borrowBalanceCurrent","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x17bfdfbc"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x18160ddd"},{"constant":true,"inputs":[],"name":"exchangeRateStored","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x182df0f5"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x23b872dd"},{"constant":true,"inputs":[],"name":"pendingAdmin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x26782247"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x313ce567"},{"constant":false,"inputs":[{"name":"owner","type":"address"}],"name":"balanceOfUnderlying","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x3af9e669"},{"constant":true,"inputs":[],"name":"getCash","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x3b1d21a2"},{"constant":false,"inputs":[{"name":"newComptroller","type":"address"}],"name":"_setComptroller","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x4576b5db"},{"constant":true,"inputs":[],"name":"totalBorrows","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x47bd3718"},{"constant":false,"inputs":[],"name":"repayBorrow","outputs":[],"payable":true,"stateMutability":"payable","type":"function","signature":"0x4e4d9fea"},{"constant":true,"inputs":[],"name":"comptroller","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x5fe3b567"},{"constant":false,"inputs":[{"name":"reduceAmount","type":"uint256"}],"name":"_reduceReserves","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x601a0bf1"},{"constant":true,"inputs":[],"name":"initialExchangeRateMantissa","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x675d972c"},{"constant":true,"inputs":[],"name":"accrualBlockNumber","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x6c540baf"},{"constant":true,"inputs":[{"name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x70a08231"},{"constant":false,"inputs":[],"name":"totalBorrowsCurrent","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x73acee98"},{"constant":false,"inputs":[{"name":"redeemAmount","type":"uint256"}],"name":"redeemUnderlying","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x852a12e3"},{"constant":true,"inputs":[],"name":"totalReserves","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x8f840ddd"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x95d89b41"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"borrowBalanceStored","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x95dd9193"},{"constant":false,"inputs":[],"name":"accrueInterest","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa6afed95"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa9059cbb"},{"constant":true,"inputs":[],"name":"borrowIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xaa5af0fd"},{"constant":false,"inputs":[{"name":"borrower","type":"address"},{"name":"cTokenCollateral","type":"address"}],"name":"liquidateBorrow","outputs":[],"payable":true,"stateMutability":"payable","type":"function","signature":"0xaae40a2a"},{"constant":true,"inputs":[],"name":"supplyRatePerBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xae9d70b0"},{"constant":false,"inputs":[{"name":"liquidator","type":"address"},{"name":"borrower","type":"address"},{"name":"seizeTokens","type":"uint256"}],"name":"seize","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xb2a02ff1"},{"constant":false,"inputs":[{"name":"newPendingAdmin","type":"address"}],"name":"_setPendingAdmin","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xb71d1a0c"},{"constant":false,"inputs":[],"name":"exchangeRateCurrent","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xbd6d894d"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"getAccountSnapshot","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xc37f68e2"},{"constant":false,"inputs":[{"name":"borrowAmount","type":"uint256"}],"name":"borrow","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xc5ebeaec"},{"constant":false,"inputs":[{"name":"redeemTokens","type":"uint256"}],"name":"redeem","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xdb006a75"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xdd62ed3e"},{"constant":false,"inputs":[{"name":"borrower","type":"address"}],"name":"repayBorrowBehalf","outputs":[],"payable":true,"stateMutability":"payable","type":"function","signature":"0xe5974619"},{"constant":false,"inputs":[],"name":"_acceptAdmin","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xe9c714f2"},{"constant":false,"inputs":[{"name":"newInterestRateModel","type":"address"}],"name":"_setInterestRateModel","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xf2b3abbd"},{"constant":true,"inputs":[],"name":"interestRateModel","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf3fdb15a"},{"constant":true,"inputs":[],"name":"admin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf851a440"},{"constant":true,"inputs":[],"name":"borrowRatePerBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf8f9da28"},{"constant":false,"inputs":[{"name":"newReserveFactorMantissa","type":"uint256"}],"name":"_setReserveFactor","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xfca7820b"},{"constant":true,"inputs":[],"name":"isCToken","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xfe9c44ae"},{"inputs":[{"name":"comptroller_","type":"address"},{"name":"interestRateModel_","type":"address"},{"name":"initialExchangeRateMantissa_","type":"uint256"},{"name":"name_","type":"string"},{"name":"symbol_","type":"string"},{"name":"decimals_","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"interestAccumulated","type":"uint256"},{"indexed":false,"name":"borrowIndex","type":"uint256"},{"indexed":false,"name":"totalBorrows","type":"uint256"}],"name":"AccrueInterest","type":"event","signature":"0x875352fb3fadeb8c0be7cbbe8ff761b308fa7033470cd0287f02f3436fd76cb9"},{"anonymous":false,"inputs":[{"indexed":false,"name":"minter","type":"address"},{"indexed":false,"name":"mintAmount","type":"uint256"},{"indexed":false,"name":"mintTokens","type":"uint256"}],"name":"Mint","type":"event","signature":"0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f"},{"anonymous":false,"inputs":[{"indexed":false,"name":"redeemer","type":"address"},{"indexed":false,"name":"redeemAmount","type":"uint256"},{"indexed":false,"name":"redeemTokens","type":"uint256"}],"name":"Redeem","type":"event","signature":"0xe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929"},{"anonymous":false,"inputs":[{"indexed":false,"name":"borrower","type":"address"},{"indexed":false,"name":"borrowAmount","type":"uint256"},{"indexed":false,"name":"accountBorrows","type":"uint256"},{"indexed":false,"name":"totalBorrows","type":"uint256"}],"name":"Borrow","type":"event","signature":"0x13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab80"},{"anonymous":false,"inputs":[{"indexed":false,"name":"payer","type":"address"},{"indexed":false,"name":"borrower","type":"address"},{"indexed":false,"name":"repayAmount","type":"uint256"},{"indexed":false,"name":"accountBorrows","type":"uint256"},{"indexed":false,"name":"totalBorrows","type":"uint256"}],"name":"RepayBorrow","type":"event","signature":"0x1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1"},{"anonymous":false,"inputs":[{"indexed":false,"name":"liquidator","type":"address"},{"indexed":false,"name":"borrower","type":"address"},{"indexed":false,"name":"repayAmount","type":"uint256"},{"indexed":false,"name":"cTokenCollateral","type":"address"},{"indexed":false,"name":"seizeTokens","type":"uint256"}],"name":"LiquidateBorrow","type":"event","signature":"0x298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldPendingAdmin","type":"address"},{"indexed":false,"name":"newPendingAdmin","type":"address"}],"name":"NewPendingAdmin","type":"event","signature":"0xca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldAdmin","type":"address"},{"indexed":false,"name":"newAdmin","type":"address"}],"name":"NewAdmin","type":"event","signature":"0xf9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldComptroller","type":"address"},{"indexed":false,"name":"newComptroller","type":"address"}],"name":"NewComptroller","type":"event","signature":"0x7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldInterestRateModel","type":"address"},{"indexed":false,"name":"newInterestRateModel","type":"address"}],"name":"NewMarketInterestRateModel","type":"event","signature":"0xedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldReserveFactorMantissa","type":"uint256"},{"indexed":false,"name":"newReserveFactorMantissa","type":"uint256"}],"name":"NewReserveFactor","type":"event","signature":"0xaaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f821460"},{"anonymous":false,"inputs":[{"indexed":false,"name":"admin","type":"address"},{"indexed":false,"name":"reduceAmount","type":"uint256"},{"indexed":false,"name":"newTotalReserves","type":"uint256"}],"name":"ReservesReduced","type":"event","signature":"0x3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e"},{"anonymous":false,"inputs":[{"indexed":false,"name":"error","type":"uint256"},{"indexed":false,"name":"info","type":"uint256"},{"indexed":false,"name":"detail","type":"uint256"}],"name":"Failure","type":"event","signature":"0x45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Transfer","type":"event","signature":"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Approval","type":"event","signature":"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"}] + +export const cUSDCAbi = [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x06fdde03"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"amount","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x095ea7b3"},{"constant":false,"inputs":[{"name":"repayAmount","type":"uint256"}],"name":"repayBorrow","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x0e752702"},{"constant":true,"inputs":[],"name":"reserveFactorMantissa","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x173b9904"},{"constant":false,"inputs":[{"name":"account","type":"address"}],"name":"borrowBalanceCurrent","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x17bfdfbc"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x18160ddd"},{"constant":true,"inputs":[],"name":"exchangeRateStored","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x182df0f5"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x23b872dd"},{"constant":false,"inputs":[{"name":"borrower","type":"address"},{"name":"repayAmount","type":"uint256"}],"name":"repayBorrowBehalf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x2608f818"},{"constant":true,"inputs":[],"name":"pendingAdmin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x26782247"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x313ce567"},{"constant":false,"inputs":[{"name":"owner","type":"address"}],"name":"balanceOfUnderlying","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x3af9e669"},{"constant":true,"inputs":[],"name":"getCash","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x3b1d21a2"},{"constant":false,"inputs":[{"name":"newComptroller","type":"address"}],"name":"_setComptroller","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x4576b5db"},{"constant":true,"inputs":[],"name":"totalBorrows","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x47bd3718"},{"constant":true,"inputs":[],"name":"comptroller","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x5fe3b567"},{"constant":false,"inputs":[{"name":"reduceAmount","type":"uint256"}],"name":"_reduceReserves","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x601a0bf1"},{"constant":true,"inputs":[],"name":"initialExchangeRateMantissa","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x675d972c"},{"constant":true,"inputs":[],"name":"accrualBlockNumber","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x6c540baf"},{"constant":true,"inputs":[],"name":"underlying","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x6f307dc3"},{"constant":true,"inputs":[{"name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x70a08231"},{"constant":false,"inputs":[],"name":"totalBorrowsCurrent","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x73acee98"},{"constant":false,"inputs":[{"name":"redeemAmount","type":"uint256"}],"name":"redeemUnderlying","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x852a12e3"},{"constant":true,"inputs":[],"name":"totalReserves","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x8f840ddd"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x95d89b41"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"borrowBalanceStored","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x95dd9193"},{"constant":false,"inputs":[{"name":"mintAmount","type":"uint256"}],"name":"mint","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa0712d68"},{"constant":false,"inputs":[],"name":"accrueInterest","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa6afed95"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa9059cbb"},{"constant":true,"inputs":[],"name":"borrowIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xaa5af0fd"},{"constant":true,"inputs":[],"name":"supplyRatePerBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xae9d70b0"},{"constant":false,"inputs":[{"name":"liquidator","type":"address"},{"name":"borrower","type":"address"},{"name":"seizeTokens","type":"uint256"}],"name":"seize","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xb2a02ff1"},{"constant":false,"inputs":[{"name":"newPendingAdmin","type":"address"}],"name":"_setPendingAdmin","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xb71d1a0c"},{"constant":false,"inputs":[],"name":"exchangeRateCurrent","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xbd6d894d"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"getAccountSnapshot","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xc37f68e2"},{"constant":false,"inputs":[{"name":"borrowAmount","type":"uint256"}],"name":"borrow","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xc5ebeaec"},{"constant":false,"inputs":[{"name":"redeemTokens","type":"uint256"}],"name":"redeem","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xdb006a75"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xdd62ed3e"},{"constant":false,"inputs":[],"name":"_acceptAdmin","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xe9c714f2"},{"constant":false,"inputs":[{"name":"newInterestRateModel","type":"address"}],"name":"_setInterestRateModel","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xf2b3abbd"},{"constant":true,"inputs":[],"name":"interestRateModel","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf3fdb15a"},{"constant":false,"inputs":[{"name":"borrower","type":"address"},{"name":"repayAmount","type":"uint256"},{"name":"cTokenCollateral","type":"address"}],"name":"liquidateBorrow","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xf5e3c462"},{"constant":true,"inputs":[],"name":"admin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf851a440"},{"constant":true,"inputs":[],"name":"borrowRatePerBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf8f9da28"},{"constant":false,"inputs":[{"name":"newReserveFactorMantissa","type":"uint256"}],"name":"_setReserveFactor","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xfca7820b"},{"constant":true,"inputs":[],"name":"isCToken","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xfe9c44ae"},{"inputs":[{"name":"underlying_","type":"address"},{"name":"comptroller_","type":"address"},{"name":"interestRateModel_","type":"address"},{"name":"initialExchangeRateMantissa_","type":"uint256"},{"name":"name_","type":"string"},{"name":"symbol_","type":"string"},{"name":"decimals_","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"interestAccumulated","type":"uint256"},{"indexed":false,"name":"borrowIndex","type":"uint256"},{"indexed":false,"name":"totalBorrows","type":"uint256"}],"name":"AccrueInterest","type":"event","signature":"0x875352fb3fadeb8c0be7cbbe8ff761b308fa7033470cd0287f02f3436fd76cb9"},{"anonymous":false,"inputs":[{"indexed":false,"name":"minter","type":"address"},{"indexed":false,"name":"mintAmount","type":"uint256"},{"indexed":false,"name":"mintTokens","type":"uint256"}],"name":"Mint","type":"event","signature":"0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f"},{"anonymous":false,"inputs":[{"indexed":false,"name":"redeemer","type":"address"},{"indexed":false,"name":"redeemAmount","type":"uint256"},{"indexed":false,"name":"redeemTokens","type":"uint256"}],"name":"Redeem","type":"event","signature":"0xe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929"},{"anonymous":false,"inputs":[{"indexed":false,"name":"borrower","type":"address"},{"indexed":false,"name":"borrowAmount","type":"uint256"},{"indexed":false,"name":"accountBorrows","type":"uint256"},{"indexed":false,"name":"totalBorrows","type":"uint256"}],"name":"Borrow","type":"event","signature":"0x13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab80"},{"anonymous":false,"inputs":[{"indexed":false,"name":"payer","type":"address"},{"indexed":false,"name":"borrower","type":"address"},{"indexed":false,"name":"repayAmount","type":"uint256"},{"indexed":false,"name":"accountBorrows","type":"uint256"},{"indexed":false,"name":"totalBorrows","type":"uint256"}],"name":"RepayBorrow","type":"event","signature":"0x1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1"},{"anonymous":false,"inputs":[{"indexed":false,"name":"liquidator","type":"address"},{"indexed":false,"name":"borrower","type":"address"},{"indexed":false,"name":"repayAmount","type":"uint256"},{"indexed":false,"name":"cTokenCollateral","type":"address"},{"indexed":false,"name":"seizeTokens","type":"uint256"}],"name":"LiquidateBorrow","type":"event","signature":"0x298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldPendingAdmin","type":"address"},{"indexed":false,"name":"newPendingAdmin","type":"address"}],"name":"NewPendingAdmin","type":"event","signature":"0xca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldAdmin","type":"address"},{"indexed":false,"name":"newAdmin","type":"address"}],"name":"NewAdmin","type":"event","signature":"0xf9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldComptroller","type":"address"},{"indexed":false,"name":"newComptroller","type":"address"}],"name":"NewComptroller","type":"event","signature":"0x7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldInterestRateModel","type":"address"},{"indexed":false,"name":"newInterestRateModel","type":"address"}],"name":"NewMarketInterestRateModel","type":"event","signature":"0xedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldReserveFactorMantissa","type":"uint256"},{"indexed":false,"name":"newReserveFactorMantissa","type":"uint256"}],"name":"NewReserveFactor","type":"event","signature":"0xaaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f821460"},{"anonymous":false,"inputs":[{"indexed":false,"name":"admin","type":"address"},{"indexed":false,"name":"reduceAmount","type":"uint256"},{"indexed":false,"name":"newTotalReserves","type":"uint256"}],"name":"ReservesReduced","type":"event","signature":"0x3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e"},{"anonymous":false,"inputs":[{"indexed":false,"name":"error","type":"uint256"},{"indexed":false,"name":"info","type":"uint256"},{"indexed":false,"name":"detail","type":"uint256"}],"name":"Failure","type":"event","signature":"0x45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Transfer","type":"event","signature":"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Approval","type":"event","signature":"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"}] + +export const cUSDTAbi = [{"inputs":[{"internalType":"address","name":"underlying_","type":"address"},{"internalType":"contract ComptrollerInterface","name":"comptroller_","type":"address"},{"internalType":"contract InterestRateModel","name":"interestRateModel_","type":"address"},{"internalType":"uint256","name":"initialExchangeRateMantissa_","type":"uint256"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"},{"internalType":"address payable","name":"admin_","type":"address"},{"internalType":"address","name":"implementation_","type":"address"},{"internalType":"bytes","name":"becomeImplementationData","type":"bytes"}],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"cashPrior","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestAccumulated","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"AccrueInterest","type":"event","signature":"0x4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc04"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event","signature":"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accountBorrows","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"Borrow","type":"event","signature":"0x13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab80"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"error","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"info","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"detail","type":"uint256"}],"name":"Failure","type":"event","signature":"0x45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liquidator","type":"address"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"cTokenCollateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"LiquidateBorrow","type":"event","signature":"0x298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint256","name":"mintAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mintTokens","type":"uint256"}],"name":"Mint","type":"event","signature":"0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"NewAdmin","type":"event","signature":"0xf9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract ComptrollerInterface","name":"oldComptroller","type":"address"},{"indexed":false,"internalType":"contract ComptrollerInterface","name":"newComptroller","type":"address"}],"name":"NewComptroller","type":"event","signature":"0x7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldImplementation","type":"address"},{"indexed":false,"internalType":"address","name":"newImplementation","type":"address"}],"name":"NewImplementation","type":"event","signature":"0xd604de94d45953f9138079ec1b82d533cb2160c906d1076d1f7ed54befbca97a"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract InterestRateModel","name":"oldInterestRateModel","type":"address"},{"indexed":false,"internalType":"contract InterestRateModel","name":"newInterestRateModel","type":"address"}],"name":"NewMarketInterestRateModel","type":"event","signature":"0xedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldPendingAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newPendingAdmin","type":"address"}],"name":"NewPendingAdmin","type":"event","signature":"0xca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldReserveFactorMantissa","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newReserveFactorMantissa","type":"uint256"}],"name":"NewReserveFactor","type":"event","signature":"0xaaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f821460"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"redeemer","type":"address"},{"indexed":false,"internalType":"uint256","name":"redeemAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"redeemTokens","type":"uint256"}],"name":"Redeem","type":"event","signature":"0xe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"payer","type":"address"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accountBorrows","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"RepayBorrow","type":"event","signature":"0x1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"benefactor","type":"address"},{"indexed":false,"internalType":"uint256","name":"addAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTotalReserves","type":"uint256"}],"name":"ReservesAdded","type":"event","signature":"0xa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc5"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"uint256","name":"reduceAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTotalReserves","type":"uint256"}],"name":"ReservesReduced","type":"event","signature":"0x3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event","signature":"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":false,"inputs":[],"name":"_acceptAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xe9c714f2"},{"constant":false,"inputs":[{"internalType":"uint256","name":"addAmount","type":"uint256"}],"name":"_addReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x3e941010"},{"constant":false,"inputs":[{"internalType":"uint256","name":"reduceAmount","type":"uint256"}],"name":"_reduceReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x601a0bf1"},{"constant":false,"inputs":[{"internalType":"contract ComptrollerInterface","name":"newComptroller","type":"address"}],"name":"_setComptroller","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x4576b5db"},{"constant":false,"inputs":[{"internalType":"address","name":"implementation_","type":"address"},{"internalType":"bool","name":"allowResign","type":"bool"},{"internalType":"bytes","name":"becomeImplementationData","type":"bytes"}],"name":"_setImplementation","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x555bcc40"},{"constant":false,"inputs":[{"internalType":"contract InterestRateModel","name":"newInterestRateModel","type":"address"}],"name":"_setInterestRateModel","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xf2b3abbd"},{"constant":false,"inputs":[{"internalType":"address payable","name":"newPendingAdmin","type":"address"}],"name":"_setPendingAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xb71d1a0c"},{"constant":false,"inputs":[{"internalType":"uint256","name":"newReserveFactorMantissa","type":"uint256"}],"name":"_setReserveFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xfca7820b"},{"constant":true,"inputs":[],"name":"accrualBlockNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x6c540baf"},{"constant":false,"inputs":[],"name":"accrueInterest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa6afed95"},{"constant":true,"inputs":[],"name":"admin","outputs":[{"internalType":"address payable","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf851a440"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xdd62ed3e"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x095ea7b3"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x70a08231"},{"constant":false,"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOfUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x3af9e669"},{"constant":false,"inputs":[{"internalType":"uint256","name":"borrowAmount","type":"uint256"}],"name":"borrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xc5ebeaec"},{"constant":false,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"borrowBalanceCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x17bfdfbc"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"borrowBalanceStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x95dd9193"},{"constant":true,"inputs":[],"name":"borrowIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xaa5af0fd"},{"constant":true,"inputs":[],"name":"borrowRatePerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf8f9da28"},{"constant":true,"inputs":[],"name":"comptroller","outputs":[{"internalType":"contract ComptrollerInterface","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x5fe3b567"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x313ce567"},{"constant":false,"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"delegateToImplementation","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x0933c1ed"},{"constant":true,"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"delegateToViewImplementation","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x4487152f"},{"constant":false,"inputs":[],"name":"exchangeRateCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xbd6d894d"},{"constant":true,"inputs":[],"name":"exchangeRateStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x182df0f5"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAccountSnapshot","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xc37f68e2"},{"constant":true,"inputs":[],"name":"getCash","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x3b1d21a2"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x5c60da1b"},{"constant":true,"inputs":[],"name":"interestRateModel","outputs":[{"internalType":"contract InterestRateModel","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf3fdb15a"},{"constant":true,"inputs":[],"name":"isCToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xfe9c44ae"},{"constant":false,"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"contract CTokenInterface","name":"cTokenCollateral","type":"address"}],"name":"liquidateBorrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xf5e3c462"},{"constant":false,"inputs":[{"internalType":"uint256","name":"mintAmount","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa0712d68"},{"constant":true,"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x06fdde03"},{"constant":true,"inputs":[],"name":"pendingAdmin","outputs":[{"internalType":"address payable","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x26782247"},{"constant":false,"inputs":[{"internalType":"uint256","name":"redeemTokens","type":"uint256"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xdb006a75"},{"constant":false,"inputs":[{"internalType":"uint256","name":"redeemAmount","type":"uint256"}],"name":"redeemUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x852a12e3"},{"constant":false,"inputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repayBorrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x0e752702"},{"constant":false,"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repayBorrowBehalf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x2608f818"},{"constant":true,"inputs":[],"name":"reserveFactorMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x173b9904"},{"constant":false,"inputs":[{"internalType":"address","name":"liquidator","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"seize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xb2a02ff1"},{"constant":true,"inputs":[],"name":"supplyRatePerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xae9d70b0"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x95d89b41"},{"constant":true,"inputs":[],"name":"totalBorrows","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x47bd3718"},{"constant":false,"inputs":[],"name":"totalBorrowsCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x73acee98"},{"constant":true,"inputs":[],"name":"totalReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x8f840ddd"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x18160ddd"},{"constant":false,"inputs":[{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa9059cbb"},{"constant":false,"inputs":[{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x23b872dd"},{"constant":true,"inputs":[],"name":"underlying","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x6f307dc3"}] + +export const cWBTCAbi = [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x06fdde03"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"amount","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x095ea7b3"},{"constant":false,"inputs":[{"name":"repayAmount","type":"uint256"}],"name":"repayBorrow","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x0e752702"},{"constant":true,"inputs":[],"name":"reserveFactorMantissa","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x173b9904"},{"constant":false,"inputs":[{"name":"account","type":"address"}],"name":"borrowBalanceCurrent","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x17bfdfbc"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x18160ddd"},{"constant":true,"inputs":[],"name":"exchangeRateStored","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x182df0f5"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x23b872dd"},{"constant":false,"inputs":[{"name":"borrower","type":"address"},{"name":"repayAmount","type":"uint256"}],"name":"repayBorrowBehalf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x2608f818"},{"constant":true,"inputs":[],"name":"pendingAdmin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x26782247"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x313ce567"},{"constant":false,"inputs":[{"name":"owner","type":"address"}],"name":"balanceOfUnderlying","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x3af9e669"},{"constant":true,"inputs":[],"name":"getCash","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x3b1d21a2"},{"constant":false,"inputs":[{"name":"newComptroller","type":"address"}],"name":"_setComptroller","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x4576b5db"},{"constant":true,"inputs":[],"name":"totalBorrows","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x47bd3718"},{"constant":true,"inputs":[],"name":"comptroller","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x5fe3b567"},{"constant":false,"inputs":[{"name":"reduceAmount","type":"uint256"}],"name":"_reduceReserves","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x601a0bf1"},{"constant":true,"inputs":[],"name":"initialExchangeRateMantissa","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x675d972c"},{"constant":true,"inputs":[],"name":"accrualBlockNumber","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x6c540baf"},{"constant":true,"inputs":[],"name":"underlying","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x6f307dc3"},{"constant":true,"inputs":[{"name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x70a08231"},{"constant":false,"inputs":[],"name":"totalBorrowsCurrent","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x73acee98"},{"constant":false,"inputs":[{"name":"redeemAmount","type":"uint256"}],"name":"redeemUnderlying","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x852a12e3"},{"constant":true,"inputs":[],"name":"totalReserves","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x8f840ddd"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x95d89b41"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"borrowBalanceStored","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x95dd9193"},{"constant":false,"inputs":[{"name":"mintAmount","type":"uint256"}],"name":"mint","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa0712d68"},{"constant":false,"inputs":[],"name":"accrueInterest","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa6afed95"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa9059cbb"},{"constant":true,"inputs":[],"name":"borrowIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xaa5af0fd"},{"constant":true,"inputs":[],"name":"supplyRatePerBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xae9d70b0"},{"constant":false,"inputs":[{"name":"liquidator","type":"address"},{"name":"borrower","type":"address"},{"name":"seizeTokens","type":"uint256"}],"name":"seize","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xb2a02ff1"},{"constant":false,"inputs":[{"name":"newPendingAdmin","type":"address"}],"name":"_setPendingAdmin","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xb71d1a0c"},{"constant":false,"inputs":[],"name":"exchangeRateCurrent","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xbd6d894d"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"getAccountSnapshot","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xc37f68e2"},{"constant":false,"inputs":[{"name":"borrowAmount","type":"uint256"}],"name":"borrow","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xc5ebeaec"},{"constant":false,"inputs":[{"name":"redeemTokens","type":"uint256"}],"name":"redeem","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xdb006a75"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xdd62ed3e"},{"constant":false,"inputs":[],"name":"_acceptAdmin","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xe9c714f2"},{"constant":false,"inputs":[{"name":"newInterestRateModel","type":"address"}],"name":"_setInterestRateModel","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xf2b3abbd"},{"constant":true,"inputs":[],"name":"interestRateModel","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf3fdb15a"},{"constant":false,"inputs":[{"name":"borrower","type":"address"},{"name":"repayAmount","type":"uint256"},{"name":"cTokenCollateral","type":"address"}],"name":"liquidateBorrow","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xf5e3c462"},{"constant":true,"inputs":[],"name":"admin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf851a440"},{"constant":true,"inputs":[],"name":"borrowRatePerBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf8f9da28"},{"constant":false,"inputs":[{"name":"newReserveFactorMantissa","type":"uint256"}],"name":"_setReserveFactor","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xfca7820b"},{"constant":true,"inputs":[],"name":"isCToken","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xfe9c44ae"},{"inputs":[{"name":"underlying_","type":"address"},{"name":"comptroller_","type":"address"},{"name":"interestRateModel_","type":"address"},{"name":"initialExchangeRateMantissa_","type":"uint256"},{"name":"name_","type":"string"},{"name":"symbol_","type":"string"},{"name":"decimals_","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"interestAccumulated","type":"uint256"},{"indexed":false,"name":"borrowIndex","type":"uint256"},{"indexed":false,"name":"totalBorrows","type":"uint256"}],"name":"AccrueInterest","type":"event","signature":"0x875352fb3fadeb8c0be7cbbe8ff761b308fa7033470cd0287f02f3436fd76cb9"},{"anonymous":false,"inputs":[{"indexed":false,"name":"minter","type":"address"},{"indexed":false,"name":"mintAmount","type":"uint256"},{"indexed":false,"name":"mintTokens","type":"uint256"}],"name":"Mint","type":"event","signature":"0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f"},{"anonymous":false,"inputs":[{"indexed":false,"name":"redeemer","type":"address"},{"indexed":false,"name":"redeemAmount","type":"uint256"},{"indexed":false,"name":"redeemTokens","type":"uint256"}],"name":"Redeem","type":"event","signature":"0xe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929"},{"anonymous":false,"inputs":[{"indexed":false,"name":"borrower","type":"address"},{"indexed":false,"name":"borrowAmount","type":"uint256"},{"indexed":false,"name":"accountBorrows","type":"uint256"},{"indexed":false,"name":"totalBorrows","type":"uint256"}],"name":"Borrow","type":"event","signature":"0x13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab80"},{"anonymous":false,"inputs":[{"indexed":false,"name":"payer","type":"address"},{"indexed":false,"name":"borrower","type":"address"},{"indexed":false,"name":"repayAmount","type":"uint256"},{"indexed":false,"name":"accountBorrows","type":"uint256"},{"indexed":false,"name":"totalBorrows","type":"uint256"}],"name":"RepayBorrow","type":"event","signature":"0x1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1"},{"anonymous":false,"inputs":[{"indexed":false,"name":"liquidator","type":"address"},{"indexed":false,"name":"borrower","type":"address"},{"indexed":false,"name":"repayAmount","type":"uint256"},{"indexed":false,"name":"cTokenCollateral","type":"address"},{"indexed":false,"name":"seizeTokens","type":"uint256"}],"name":"LiquidateBorrow","type":"event","signature":"0x298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldPendingAdmin","type":"address"},{"indexed":false,"name":"newPendingAdmin","type":"address"}],"name":"NewPendingAdmin","type":"event","signature":"0xca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldAdmin","type":"address"},{"indexed":false,"name":"newAdmin","type":"address"}],"name":"NewAdmin","type":"event","signature":"0xf9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldComptroller","type":"address"},{"indexed":false,"name":"newComptroller","type":"address"}],"name":"NewComptroller","type":"event","signature":"0x7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldInterestRateModel","type":"address"},{"indexed":false,"name":"newInterestRateModel","type":"address"}],"name":"NewMarketInterestRateModel","type":"event","signature":"0xedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldReserveFactorMantissa","type":"uint256"},{"indexed":false,"name":"newReserveFactorMantissa","type":"uint256"}],"name":"NewReserveFactor","type":"event","signature":"0xaaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f821460"},{"anonymous":false,"inputs":[{"indexed":false,"name":"admin","type":"address"},{"indexed":false,"name":"reduceAmount","type":"uint256"},{"indexed":false,"name":"newTotalReserves","type":"uint256"}],"name":"ReservesReduced","type":"event","signature":"0x3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e"},{"anonymous":false,"inputs":[{"indexed":false,"name":"error","type":"uint256"},{"indexed":false,"name":"info","type":"uint256"},{"indexed":false,"name":"detail","type":"uint256"}],"name":"Failure","type":"event","signature":"0x45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Transfer","type":"event","signature":"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Approval","type":"event","signature":"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"}] + +export const cUNIAbi = [{"inputs":[{"internalType":"address","name":"underlying_","type":"address"},{"internalType":"contract ComptrollerInterface","name":"comptroller_","type":"address"},{"internalType":"contract InterestRateModel","name":"interestRateModel_","type":"address"},{"internalType":"uint256","name":"initialExchangeRateMantissa_","type":"uint256"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"},{"internalType":"address payable","name":"admin_","type":"address"},{"internalType":"address","name":"implementation_","type":"address"},{"internalType":"bytes","name":"becomeImplementationData","type":"bytes"}],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"cashPrior","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestAccumulated","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"AccrueInterest","type":"event","signature":"0x4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc04"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event","signature":"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accountBorrows","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"Borrow","type":"event","signature":"0x13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab80"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"error","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"info","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"detail","type":"uint256"}],"name":"Failure","type":"event","signature":"0x45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liquidator","type":"address"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"cTokenCollateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"LiquidateBorrow","type":"event","signature":"0x298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint256","name":"mintAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mintTokens","type":"uint256"}],"name":"Mint","type":"event","signature":"0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"NewAdmin","type":"event","signature":"0xf9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract ComptrollerInterface","name":"oldComptroller","type":"address"},{"indexed":false,"internalType":"contract ComptrollerInterface","name":"newComptroller","type":"address"}],"name":"NewComptroller","type":"event","signature":"0x7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldImplementation","type":"address"},{"indexed":false,"internalType":"address","name":"newImplementation","type":"address"}],"name":"NewImplementation","type":"event","signature":"0xd604de94d45953f9138079ec1b82d533cb2160c906d1076d1f7ed54befbca97a"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract InterestRateModel","name":"oldInterestRateModel","type":"address"},{"indexed":false,"internalType":"contract InterestRateModel","name":"newInterestRateModel","type":"address"}],"name":"NewMarketInterestRateModel","type":"event","signature":"0xedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldPendingAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newPendingAdmin","type":"address"}],"name":"NewPendingAdmin","type":"event","signature":"0xca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldReserveFactorMantissa","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newReserveFactorMantissa","type":"uint256"}],"name":"NewReserveFactor","type":"event","signature":"0xaaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f821460"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"redeemer","type":"address"},{"indexed":false,"internalType":"uint256","name":"redeemAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"redeemTokens","type":"uint256"}],"name":"Redeem","type":"event","signature":"0xe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"payer","type":"address"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accountBorrows","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"RepayBorrow","type":"event","signature":"0x1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"benefactor","type":"address"},{"indexed":false,"internalType":"uint256","name":"addAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTotalReserves","type":"uint256"}],"name":"ReservesAdded","type":"event","signature":"0xa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc5"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"uint256","name":"reduceAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTotalReserves","type":"uint256"}],"name":"ReservesReduced","type":"event","signature":"0x3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event","signature":"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":false,"inputs":[],"name":"_acceptAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xe9c714f2"},{"constant":false,"inputs":[{"internalType":"uint256","name":"addAmount","type":"uint256"}],"name":"_addReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x3e941010"},{"constant":false,"inputs":[{"internalType":"uint256","name":"reduceAmount","type":"uint256"}],"name":"_reduceReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x601a0bf1"},{"constant":false,"inputs":[{"internalType":"contract ComptrollerInterface","name":"newComptroller","type":"address"}],"name":"_setComptroller","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x4576b5db"},{"constant":false,"inputs":[{"internalType":"address","name":"implementation_","type":"address"},{"internalType":"bool","name":"allowResign","type":"bool"},{"internalType":"bytes","name":"becomeImplementationData","type":"bytes"}],"name":"_setImplementation","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x555bcc40"},{"constant":false,"inputs":[{"internalType":"contract InterestRateModel","name":"newInterestRateModel","type":"address"}],"name":"_setInterestRateModel","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xf2b3abbd"},{"constant":false,"inputs":[{"internalType":"address payable","name":"newPendingAdmin","type":"address"}],"name":"_setPendingAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xb71d1a0c"},{"constant":false,"inputs":[{"internalType":"uint256","name":"newReserveFactorMantissa","type":"uint256"}],"name":"_setReserveFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xfca7820b"},{"constant":true,"inputs":[],"name":"accrualBlockNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x6c540baf"},{"constant":false,"inputs":[],"name":"accrueInterest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa6afed95"},{"constant":true,"inputs":[],"name":"admin","outputs":[{"internalType":"address payable","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf851a440"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xdd62ed3e"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x095ea7b3"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x70a08231"},{"constant":false,"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOfUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x3af9e669"},{"constant":false,"inputs":[{"internalType":"uint256","name":"borrowAmount","type":"uint256"}],"name":"borrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xc5ebeaec"},{"constant":false,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"borrowBalanceCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x17bfdfbc"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"borrowBalanceStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x95dd9193"},{"constant":true,"inputs":[],"name":"borrowIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xaa5af0fd"},{"constant":true,"inputs":[],"name":"borrowRatePerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf8f9da28"},{"constant":true,"inputs":[],"name":"comptroller","outputs":[{"internalType":"contract ComptrollerInterface","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x5fe3b567"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x313ce567"},{"constant":false,"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"delegateToImplementation","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x0933c1ed"},{"constant":true,"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"delegateToViewImplementation","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x4487152f"},{"constant":false,"inputs":[],"name":"exchangeRateCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xbd6d894d"},{"constant":true,"inputs":[],"name":"exchangeRateStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x182df0f5"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAccountSnapshot","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xc37f68e2"},{"constant":true,"inputs":[],"name":"getCash","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x3b1d21a2"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x5c60da1b"},{"constant":true,"inputs":[],"name":"interestRateModel","outputs":[{"internalType":"contract InterestRateModel","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf3fdb15a"},{"constant":true,"inputs":[],"name":"isCToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xfe9c44ae"},{"constant":false,"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"contract CTokenInterface","name":"cTokenCollateral","type":"address"}],"name":"liquidateBorrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xf5e3c462"},{"constant":false,"inputs":[{"internalType":"uint256","name":"mintAmount","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa0712d68"},{"constant":true,"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x06fdde03"},{"constant":true,"inputs":[],"name":"pendingAdmin","outputs":[{"internalType":"address payable","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x26782247"},{"constant":false,"inputs":[{"internalType":"uint256","name":"redeemTokens","type":"uint256"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xdb006a75"},{"constant":false,"inputs":[{"internalType":"uint256","name":"redeemAmount","type":"uint256"}],"name":"redeemUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x852a12e3"},{"constant":false,"inputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repayBorrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x0e752702"},{"constant":false,"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repayBorrowBehalf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x2608f818"},{"constant":true,"inputs":[],"name":"reserveFactorMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x173b9904"},{"constant":false,"inputs":[{"internalType":"address","name":"liquidator","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"seize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xb2a02ff1"},{"constant":true,"inputs":[],"name":"supplyRatePerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xae9d70b0"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x95d89b41"},{"constant":true,"inputs":[],"name":"totalBorrows","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x47bd3718"},{"constant":false,"inputs":[],"name":"totalBorrowsCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x73acee98"},{"constant":true,"inputs":[],"name":"totalReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x8f840ddd"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x18160ddd"},{"constant":false,"inputs":[{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa9059cbb"},{"constant":false,"inputs":[{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x23b872dd"},{"constant":true,"inputs":[],"name":"underlying","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x6f307dc3"}] diff --git a/app/src/components/common/icons/IconTick.tsx b/app/src/components/common/icons/IconTick.tsx index 9e4d5e5107..5feacf73d5 100644 --- a/app/src/components/common/icons/IconTick.tsx +++ b/app/src/components/common/icons/IconTick.tsx @@ -3,6 +3,8 @@ import styled from 'styled-components' interface Props { disabled?: boolean selected?: boolean + fill?: string + stroke?: string } const StyledSvg = styled.svg<{ disabled?: boolean; selected?: boolean }>` filter: ${props => (props.selected ? 'saturate(0) brightness(2)' : props.disabled ? 'saturate(0)' : '')}; @@ -10,7 +12,7 @@ const StyledSvg = styled.svg<{ disabled?: boolean; selected?: boolean }>` ` export const IconTick = (props: Props) => { - const { disabled = false, selected = false } = props + const { disabled = false, fill = '#7986CB', selected = false, stroke = '#7986CB' } = props return ( { > diff --git a/app/src/components/common/icons/currencies/CompoundIcon.tsx b/app/src/components/common/icons/currencies/CompoundIcon.tsx new file mode 100644 index 0000000000..2b68da1261 --- /dev/null +++ b/app/src/components/common/icons/currencies/CompoundIcon.tsx @@ -0,0 +1,13 @@ +import React from 'react' + +export const CompoundIcon = () => ( + + + + +) diff --git a/app/src/components/common/icons/currencies/CompoundIconNoBorder.tsx b/app/src/components/common/icons/currencies/CompoundIconNoBorder.tsx new file mode 100644 index 0000000000..d89cd15c2b --- /dev/null +++ b/app/src/components/common/icons/currencies/CompoundIconNoBorder.tsx @@ -0,0 +1,12 @@ +import React from 'react' + +export const CompoundIconNoBorder = () => ( + + + +) diff --git a/app/src/components/common/icons/currencies/currencies_data.tsx b/app/src/components/common/icons/currencies/currencies_data.tsx index e027f3b633..ec86a8d575 100644 --- a/app/src/components/common/icons/currencies/currencies_data.tsx +++ b/app/src/components/common/icons/currencies/currencies_data.tsx @@ -68,4 +68,9 @@ export const currenciesData = [ text: 'USDT', token: 'USDT', }, + { + icon: , + text: 'COMP', + token: 'COMP', + }, ] diff --git a/app/src/components/common/icons/currencies/index.ts b/app/src/components/common/icons/currencies/index.ts index be8a857a05..60bccc1ac8 100644 --- a/app/src/components/common/icons/currencies/index.ts +++ b/app/src/components/common/icons/currencies/index.ts @@ -11,3 +11,5 @@ export { UsdcIcon } from './UsdcIcon' export { WEthIcon } from './WEthIcon' export { ZrxIcon } from './ZrxIcon' export { UsdtIcon } from './UsdtIcon' +export { CompoundIconNoBorder } from './CompoundIconNoBorder' +export { CompoundIcon } from './CompoundIcon' diff --git a/app/src/components/market/common/add_compound_service/index.tsx b/app/src/components/market/common/add_compound_service/index.tsx new file mode 100644 index 0000000000..fbdb6dde76 --- /dev/null +++ b/app/src/components/market/common/add_compound_service/index.tsx @@ -0,0 +1,165 @@ +import React from 'react' +import styled from 'styled-components' + +import { getCTokenForToken } from '../../../../util/tools' +import { IconTick } from '../../../common/icons' +import { CompoundIcon } from '../../../common/icons/currencies/CompoundIcon' + +const Wrapper = styled.div` + border-radius: 4px; + border: ${({ theme }) => theme.borders.borderLineDisabled}; + padding: 18px 25px; + margin-bottom: 20px; +` + +const Title = styled.h2` + color: ${props => props.theme.colors.textColorDark}; + font-size: 16px; + letter-spacing: 0.4px; + line-height: 1.2; + margin: 0 0 20px; + font-weight: 400; +` + +const DescriptionWrapper = styled.div` + align-items: center; + display: flex; +` + +const CheckService = styled.div<{ isServiceChecked: boolean }>` + width: 40px; + height: 40px; + border-radius: 50%; + text-align: center; + border: 1px solid ${props => (props.isServiceChecked ? props.theme.colors.transparent : props.theme.colors.tertiary)}; + background-color: ${props => + props.isServiceChecked ? props.theme.colors.clickable : props.theme.colors.mainBodyBackground}; + display: flex; + align-items: center; + justify-content: center; + &:hover { + border: 1px solid ${props => (props.isServiceChecked ? 'none' : props.theme.colors.tertiaryDark)}; + cursor: pointer; + } + &:active { + border: none; + } + path { + fill: ${props => + props.isServiceChecked ? props.theme.colors.mainBodyBackground : props.theme.textfield.textColorDark}; + } +` + +const ServiceWrapper = styled.div` + color: ${props => props.theme.colors.textColorLightish}; + font-size: ${props => props.theme.textfield.fontSize}; + letter-spacing: 0.2px; + line-height: 1.4; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: justify; +` + +const ServiceIconWrapper = styled.div` + display: flex; + padding-right: 16px; + text-align: center; + -webkit-box-align: center; +` + +const ServiceTextWrapper = styled.div` + width: 90%; +` + +const ServiceCheckWrapper = styled.div` + justify-content: space-between; + color: transparent; +` + +const CompoundServiceDescription = styled.div` + color: ${props => props.theme.colors.textColorLightish}; + font-size: ${props => props.theme.textfield.fontSize}; + letter-spacing: 0.2px; + line-height: 1.4; + margin: 0; + width: 100%; +` + +const ServiceTokenDetails = styled.div` + width: 100%; + display: flex; +` + +const TextHeading = styled.div` + color: ${props => props.theme.colors.textColorDark}; + width: 71px; + height: 16px; + left: 54px; + top: calc(50% - 16px / 2 - 11px); + font-weight: 500; + font-size: 14px; + line-height: 16px; + display: flex; + align-items: center; + letter-spacing: 0.2px; + margin: 0px 6px 0px 0px; +` + +const TextBody = styled.div` + line-height: 16px; + font-size: 14px; + height: 16px; + margin: 6px 6px 0px 0px; +` + +const TextBodyMarker = styled.span` + color: ${props => props.theme.colors.green}; + font-weight: 500; +` + +export interface AddCompoundServiceProps { + isServiceChecked: boolean + toggleServiceCheck?: any + compoundInterestRate: string + currentToken: string +} + +export const AddCompoundService: React.FC = (props: AddCompoundServiceProps) => { + const { compoundInterestRate, currentToken, isServiceChecked, toggleServiceCheck } = props + let serviceChecked = + if (!isServiceChecked) { + serviceChecked = + } + const cTokenSymbol = getCTokenForToken(currentToken) + const currentTokenDisplay = currentToken.charAt(0).toUpperCase() + currentToken.slice(1).toLowerCase() + const cTokenDisplay = + cTokenSymbol.charAt(0).toLowerCase() + cTokenSymbol.charAt(1).toUpperCase() + cTokenSymbol.slice(2).toLowerCase() + return ( + + Recommended Service + + + + + + + + + Compound + + Convert {currentTokenDisplay} to {cTokenDisplay} and + earn {compoundInterestRate}% APY + + + + {serviceChecked} + + + + + + + ) +} diff --git a/app/src/components/market/common/additional_market_data/index.tsx b/app/src/components/market/common/additional_market_data/index.tsx index b413d39334..47f2c6e416 100644 --- a/app/src/components/market/common/additional_market_data/index.tsx +++ b/app/src/components/market/common/additional_market_data/index.tsx @@ -1,11 +1,13 @@ -import React, { DOMAttributes } from 'react' +import React, { DOMAttributes, useEffect, useState } from 'react' import ReactTooltip from 'react-tooltip' import styled from 'styled-components' import { useConnectedWeb3Context, useRealityLink } from '../../../../hooks' +import { CompoundService } from '../../../../services/compound_service' import { networkIds } from '../../../../util/networks' -import { Arbitrator, KlerosItemStatus, KlerosSubmission } from '../../../../util/types' +import { Arbitrator, CompoundTokenType, KlerosItemStatus, KlerosSubmission, Token } from '../../../../util/types' import { IconAlert, IconArbitrator, IconCategory, IconOracle, IconVerified } from '../../../common/icons' +import { CompoundIconNoBorder } from '../../../common/icons/currencies/CompoundIconNoBorder' const AdditionalMarketDataWrapper = styled.div` border-top: ${({ theme }) => theme.borders.borderLineDisabled}; @@ -43,9 +45,17 @@ const AdditionalMarketDataLeft = styled.div` } ` -const AdditionalMarketDataSectionTitle = styled.p<{ isError?: boolean }>` +const CompoundInterestWrapper = styled.div<{ customColor: string }>` + color: ${props => props.theme.colors.green}; + margin-left: 2px !important; + &:hover { + color: ${props => props.customColor}; + } +` + +const AdditionalMarketDataSectionTitle = styled.p<{ isError?: boolean; noLeftMargin?: boolean }>` margin: 0; - margin-left: 8px; + margin-left: ${props => (props.noLeftMargin ? '0px' : '8px')}; font-size: ${props => props.theme.textfield.fontSize}; line-height: 16px; white-space: nowrap; @@ -55,7 +65,12 @@ const AdditionalMarketDataSectionTitle = styled.p<{ isError?: boolean }>` } ` -const AdditionalMarketDataSectionWrapper = styled.a<{ noColorChange?: boolean; isError?: boolean }>` +const AdditionalMarketDataSectionWrapper = styled.a<{ + noColorChange?: boolean + isError?: boolean + customColorChange?: boolean + customColor?: string +}>` display: flex; align-items: center; cursor: pointer; @@ -70,7 +85,13 @@ const AdditionalMarketDataSectionWrapper = styled.a<{ noColorChange?: boolean; i } path { fill: ${props => - props.noColorChange ? '' : props.isError ? props.theme.colors.alertHover : props.theme.colors.primaryLight}; + props.customColorChange + ? props.customColor + : props.noColorChange + ? '' + : props.isError + ? props.theme.colors.alertHover + : props.theme.colors.primaryLight}; } path:nth-child(even) { @@ -89,6 +110,7 @@ const AdditionalMarketDataSectionWrapper = styled.a<{ noColorChange?: boolean; i interface Props extends DOMAttributes { category: string arbitrator: Arbitrator + collateral: Token oracle: string id: string curatedByDxDaoOrKleros: boolean @@ -100,14 +122,13 @@ interface Props extends DOMAttributes { } export const AdditionalMarketData: React.FC = props => { - const { address, arbitrator, category, curatedByDxDaoOrKleros, id, oracle, submissionIDs, title } = props + const { address, arbitrator, category, collateral, curatedByDxDaoOrKleros, id, oracle, submissionIDs, title } = props const context = useConnectedWeb3Context() const realitioBaseUrl = useRealityLink() - - const realitioUrl = id ? `${realitioBaseUrl}/#!/question/${id}` : `${realitioBaseUrl}/` - + const realitioUrl = id ? `${realitioBaseUrl}/app/#!/question/${id}` : `${realitioBaseUrl}/` + const { account, library: provider } = context submissionIDs.sort((s1, s2) => { if (s1.status === KlerosItemStatus.Registered) return -1 if (s2.status === KlerosItemStatus.Registered) return 1 @@ -122,6 +143,18 @@ export const AdditionalMarketData: React.FC = props => { queryParams.append('col1', title) queryParams.append('col2', `https://omen.eth.link/#/${address}`) + const [compoundInterestRate, setCompoundInterestRate] = useState('-') + + useEffect(() => { + const getAPY = async () => { + const compoundServiceObject = new CompoundService(collateral.address, collateral.symbol, provider, account) + const supplyRate = await compoundServiceObject.calculateSupplyRateAPY() + setCompoundInterestRate(supplyRate.toFixed(2)) + } + if (collateral.symbol.toLowerCase() in CompoundTokenType) { + getAPY() + } + }, []) // eslint-disable-line react-hooks/exhaustive-deps return ( @@ -168,6 +201,22 @@ export const AdditionalMarketData: React.FC = props => { )} + {collateral.symbol.toLowerCase() in CompoundTokenType ? ( + + + + {compoundInterestRate}% APY + + + ) : ( + + )} = props => { resolutionTimestamp, runningDailyVolumeByHour, } = props - + const [displayCurrency, setDisplayCurrency] = useState(currency) const context = useConnectedWeb3Context() const { tokens } = useTokens(context) const [currencyIcon, setCurrencyIcon] = useState('') const [showUTC, setShowUTC] = useState(true) const [show24H, setShow24H] = useState(false) + const [totalVolume, setTotalVolume] = useState('') + const [dailyVolume, setDailyVolume] = useState('') + const [displayLiquidity, setDisplayLiquidity] = useState('') + const { account, library: provider, networkId } = context + + const setDisplayCurrencyValues = async () => { + if (currency.symbol.toLowerCase() in CompoundTokenType) { + const compoundService = new CompoundService(currency.address, currency.symbol, provider, account) + await compoundService.init() + const totalVolumeRaw: BigNumber = compoundService.calculateCTokenToBaseExchange(displayCurrency, collateralVolume) + const totalVolume = formatBigNumber(totalVolumeRaw, displayCurrency.decimals) + setTotalVolume(totalVolume) + const dailyVolume = + Math.floor(Date.now() / 86400000) === lastActiveDay && runningDailyVolumeByHour && currency.decimals + ? runningDailyVolumeByHour[Math.floor(Date.now() / (1000 * 60 * 60)) % 24] + : bigNumberify('0') + const dailyVolumeRaw: BigNumber = await compoundService.calculateCTokenToBaseExchange( + displayCurrency, + dailyVolume, + ) + const dailyVolumeDisplay = formatBigNumber(dailyVolumeRaw, displayCurrency.decimals) + setDailyVolume(dailyVolumeDisplay) + const liquidityRaw = parseUnits(liquidity, currency.decimals) + const baseTokenLiquidityRaw = await compoundService.calculateCTokenToBaseExchange(displayCurrency, liquidityRaw) + const displayLiquidity = formatBigNumber(baseTokenLiquidityRaw, displayCurrency.decimals) + setDisplayLiquidity(displayLiquidity) + } else { + const dailyVolume = + Math.floor(Date.now() / 86400000) === lastActiveDay && runningDailyVolumeByHour && currency.decimals + ? formatBigNumber(runningDailyVolumeByHour[Math.floor(Date.now() / (1000 * 60 * 60)) % 24], currency.decimals) + : '0' + setDailyVolume(dailyVolume) + const totalVolume = formatBigNumber(collateralVolume, currency.decimals) + setTotalVolume(totalVolume) + setDisplayLiquidity(liquidity) + } + } + + useEffect(() => { + const currencySymbol = currency.symbol.toLowerCase() + let baseToken: Token = getToken(networkId, currencySymbol as KnownToken) + if (currencySymbol in CompoundTokenType) { + const baseTokenSymbol = currencySymbol.substring(1, currencySymbol.length) as KnownToken + baseToken = getToken(networkId, baseTokenSymbol) + } + setDisplayCurrency(baseToken) + }, [currency.symbol, networkId]) + + useEffect(() => { + setDisplayCurrencyValues() + }, [displayCurrency.symbol]) // eslint-disable-line react-hooks/exhaustive-deps useEffect(() => { const matchingAddress = (token: Token) => token.address.toLowerCase() === currency.address.toLowerCase() const tokenIndex = tokens.findIndex(matchingAddress) tokenIndex !== -1 && setCurrencyIcon(tokens[tokenIndex].image) - }, [tokens, currency.address]) + }, [tokens, currency.address, displayCurrency]) const timezoneAbbr = momentTZ.tz(momentTZ.tz.guess()).zoneAbbr() - const dailyVolume = - Math.floor(Date.now() / 86400000) === lastActiveDay && runningDailyVolumeByHour && currency.decimals - ? formatBigNumber(runningDailyVolumeByHour[Math.floor(Date.now() / (1000 * 60 * 60)) % 24], currency.decimals) - : '0' - - const totalVolume = formatBigNumber(collateralVolume, currency.decimals) - return ( - {formatToShortNumber(liquidity)} {currency.symbol} + {formatToShortNumber(displayLiquidity)} {displayCurrency.symbol} Liquidity - {show24H ? formatToShortNumber(dailyVolume) : formatToShortNumber(totalVolume)} {currency.symbol} + {show24H ? formatToShortNumber(dailyVolume) : formatToShortNumber(totalVolume)} {displayCurrency.symbol} = (props: Props) => { address={address} arbitrator={arbitrator} category={question.category} + collateral={collateralToken} curatedByDxDao={curatedByDxDao} curatedByDxDaoOrKleros={curatedByDxDaoOrKleros} id={question.id} diff --git a/app/src/components/market/common/market_top_details_open/index.tsx b/app/src/components/market/common/market_top_details_open/index.tsx index 799d880c3f..e448a4b592 100644 --- a/app/src/components/market/common/market_top_details_open/index.tsx +++ b/app/src/components/market/common/market_top_details_open/index.tsx @@ -170,6 +170,7 @@ const MarketTopDetailsOpen: React.FC = (props: Props) => { address={address} arbitrator={arbitrator} category={question.category} + collateral={collateral} curatedByDxDao={curatedByDxDao} curatedByDxDaoOrKleros={curatedByDxDaoOrKleros} id={question.id} diff --git a/app/src/components/market/common/outcome_table/index.tsx b/app/src/components/market/common/outcome_table/index.tsx index 8e13aac848..b700927af5 100644 --- a/app/src/components/market/common/outcome_table/index.tsx +++ b/app/src/components/market/common/outcome_table/index.tsx @@ -5,9 +5,16 @@ import styled from 'styled-components' import { useConnectedWeb3Context } from '../../../../hooks' import { getOutcomeColor } from '../../../../theme/utils' -import { getWrapToken, pseudoNativeAssetAddress } from '../../../../util/networks' -import { formatBigNumber, formatNumber, mulBN } from '../../../../util/tools' -import { BalanceItem, BondItem, OutcomeTableValue, Token, TokenEthereum } from '../../../../util/types' +import { getToken, getWrapToken, pseudoNativeAssetAddress } from '../../../../util/networks' +import { formatBigNumber, formatNumber, getBaseTokenForCToken, mulBN } from '../../../../util/tools' +import { + BalanceItem, + BondItem, + CompoundTokenType, + OutcomeTableValue, + Token, + TokenEthereum, +} from '../../../../util/types' import { RadioInput, TD, THead, TR, Table } from '../../../common' import { BarDiagram } from '../bar_diagram_probabilities' import { @@ -24,9 +31,13 @@ import { WinningBadge } from '../winning_badge' interface Props { balances: BalanceItem[] collateral: Token + displayBalances?: BalanceItem[] + displayCollateral?: Token disabledColumns?: OutcomeTableValue[] displayRadioSelection?: boolean + displayProbabilities?: number[] outcomeHandleChange?: (e: number) => void + getPriceInBaseToken?: (e: number) => number outcomeSelected?: number payouts?: Maybe probabilities: number[] @@ -86,12 +97,15 @@ export const OutcomeTable = (props: Props) => { balances, collateral, disabledColumns = [], + displayCollateral = collateral, + displayBalances = balances, displayRadioSelection = true, newShares = null, outcomeHandleChange, outcomeSelected, payouts = [], probabilities, + displayProbabilities = probabilities, withWinningOutcome = false, showPriceChange = false, showSharesChange = false, @@ -120,8 +134,10 @@ export const OutcomeTable = (props: Props) => { const context = useConnectedWeb3Context() const wrapSymbol = getWrapToken(context.networkId).symbol - const symbol = collateral.address === pseudoNativeAssetAddress ? wrapSymbol : collateral.symbol - + let symbol = collateral.address === pseudoNativeAssetAddress ? wrapSymbol : collateral.symbol + if (displayCollateral.symbol) { + symbol = displayCollateral.symbol + } const renderTableHeader = () => { return ( @@ -212,14 +228,30 @@ export const OutcomeTable = (props: Props) => { } const renderTableRow = (balanceItem: BalanceItem, outcomeIndex: number) => { + const currentCollateral = displayCollateral ? displayCollateral : collateral + let baseCollateral = collateral + const { networkId } = context + if (collateral.symbol.toLowerCase() in CompoundTokenType) { + const baseCollateralSymbol = getBaseTokenForCToken(collateral.symbol.toLowerCase()) as KnownToken + baseCollateral = getToken(networkId, baseCollateralSymbol) + } const { currentPrice, outcomeName, payout, shares } = balanceItem - const currentPriceFormatted = withWinningOutcome ? payout.toFixed(2) : Number(currentPrice).toFixed(2) + const currentPriceValue = Number(currentPrice) + let currentPriceDisplay = currentPriceValue.toFixed(2) + if (currentPriceValue < 0.1) { + currentPriceDisplay = currentPriceValue.toFixed(4) + } + const currentPriceFormatted = withWinningOutcome ? payout.toFixed(2) : currentPriceDisplay const probability = withWinningOutcome ? Number(payout.mul(100).toString()) : probabilities[outcomeIndex] - const newPrice = (probabilities[outcomeIndex] / 100).toFixed(2) - const formattedPayout = formatBigNumber(mulBN(shares, Number(payout.toString())), collateral.decimals) - const formattedShares = formatBigNumber(shares, collateral.decimals) + const newPriceValue = displayProbabilities[outcomeIndex] / 100 + let newPrice = newPriceValue.toFixed(2) + if (newPriceValue < 0.01) { + newPrice = newPriceValue.toFixed(4) + } + const formattedPayout = formatBigNumber(mulBN(shares, Number(payout.toString())), currentCollateral.decimals) + const formattedShares = formatBigNumber(shares, displayCollateral.decimals) const isWinningOutcome = payouts && payouts[outcomeIndex] && payouts[outcomeIndex].gt(0) - const formattedNewShares = newShares ? formatBigNumber(newShares[outcomeIndex], collateral.decimals) : null + const formattedNewShares = newShares ? formatBigNumber(newShares[outcomeIndex], baseCollateral.decimals) : null const showBondBadge = isBond && withWinningOutcome && outcomeIndex === winningBondIndex const formattedBondedEth = bonds && bonds[outcomeIndex] && bonds[outcomeIndex].bondedEth @@ -328,13 +360,13 @@ export const OutcomeTable = (props: Props) => { ) } - const renderTable = () => balances.map((balanceItem: BalanceItem, index) => renderTableRow(balanceItem, index)) + const renderTable = () => displayBalances.map((balanceItem: BalanceItem, index) => renderTableRow(balanceItem, index)) return ( {renderTable()} - {isBond && renderInvalidRow(balances.length)} + {isBond && renderInvalidRow(displayBalances.length)}
) diff --git a/app/src/components/market/sections/market_buy/market_buy.tsx b/app/src/components/market/sections/market_buy/market_buy.tsx index 6f735a3bd0..9689c8a923 100644 --- a/app/src/components/market/sections/market_buy/market_buy.tsx +++ b/app/src/components/market/sections/market_buy/market_buy.tsx @@ -16,12 +16,27 @@ import { useCpkAllowance, useCpkProxy, } from '../../../../hooks' -import { MarketMakerService } from '../../../../services' +import { CompoundService, MarketMakerService } from '../../../../services' import { getLogger } from '../../../../util/logger' -import { getNativeAsset, getWrapToken, pseudoNativeAssetAddress } from '../../../../util/networks' +import { getNativeAsset, getToken, getWrapToken, pseudoNativeAssetAddress } from '../../../../util/networks' import { RemoteData } from '../../../../util/remote_data' -import { computeBalanceAfterTrade, formatBigNumber, formatNumber, mulBN } from '../../../../util/tools' -import { MarketDetailsTab, MarketMakerData, OutcomeTableValue, Status, Ternary, Token } from '../../../../util/types' +import { + computeBalanceAfterTrade, + formatBigNumber, + formatNumber, + getBaseTokenForCToken, + getSharesInBaseToken, + mulBN, +} from '../../../../util/tools' +import { + CompoundTokenType, + MarketDetailsTab, + MarketMakerData, + OutcomeTableValue, + Status, + Ternary, + Token, +} from '../../../../util/types' import { Button, ButtonContainer } from '../../../button' import { ButtonType } from '../../../button/button_styling_types' import { BigNumberInput, TextfieldCustomPlaceholder } from '../../../common' @@ -54,6 +69,7 @@ const StyledButtonContainer = styled(ButtonContainer)` const logger = getLogger('Market::Buy') interface Props extends RouteComponentProps { + compoundService: CompoundService marketMakerData: MarketMakerData switchMarketTab: (arg0: MarketDetailsTab) => void fetchGraphMarketMakerData: () => Promise @@ -67,7 +83,7 @@ const MarketBuyWrapper: React.FC = (props: Props) => { const signer = useMemo(() => provider.getSigner(), [provider]) const { buildMarketMaker } = useContracts(context) - const { fetchGraphMarketMakerData, marketMakerData, switchMarketTab } = props + const { compoundService, fetchGraphMarketMakerData, marketMakerData, switchMarketTab } = props const { address: marketMakerAddress, balances, fee, question } = marketMakerData const marketMaker = useMemo(() => buildMarketMaker(marketMakerAddress), [buildMarketMaker, marketMakerAddress]) @@ -78,6 +94,27 @@ const MarketBuyWrapper: React.FC = (props: Props) => { ? nativeAsset : marketMakerData.collateral const [collateral, setCollateral] = useState(initialCollateral) + const collateralSymbol = collateral.symbol.toLowerCase() + + let baseCollateral = collateral + if (collateral.symbol.toLowerCase() in CompoundTokenType) { + const baseTokenSymbol = getBaseTokenForCToken(collateral.symbol.toLowerCase()) as KnownToken + baseCollateral = getToken(networkId, baseTokenSymbol) + } + + let displayBalances = balances + if (baseCollateral.address !== collateral.address && collateral.symbol.toLowerCase() in CompoundTokenType) { + displayBalances = getSharesInBaseToken(balances, compoundService, baseCollateral) + } + const getInitialDisplayCollateral = (): Token => { + const collateralSymbol = collateral.symbol.toLowerCase() + if (collateralSymbol in CompoundTokenType) { + return baseCollateral + } else { + return collateral + } + } + const [displayCollateral, setDisplayCollateral] = useState(getInitialDisplayCollateral()) const [status, setStatus] = useState(Status.Ready) const [outcomeIndex, setOutcomeIndex] = useState(0) @@ -88,7 +125,7 @@ const MarketBuyWrapper: React.FC = (props: Props) => { const [isModalTransactionResultOpen, setIsModalTransactionResultOpen] = useState(false) const [tweet, setTweet] = useState('') const [newShares, setNewShares] = useState>(null) - + const [displayFundAmount, setDisplayFundAmount] = useState>(new BigNumber(0)) const [allowanceFinished, setAllowanceFinished] = useState(false) const { allowance, unlock } = useCpkAllowance(signer, collateral.address) @@ -145,7 +182,7 @@ const MarketBuyWrapper: React.FC = (props: Props) => { ) const { collateralBalance: maybeCollateralBalance, fetchCollateralBalance } = useCollateralBalance( - collateral, + displayCollateral, context, ) const collateralBalance = maybeCollateralBalance || Zero @@ -179,8 +216,13 @@ const MarketBuyWrapper: React.FC = (props: Props) => { if (!cpk) { return } - - const sharesAmount = formatBigNumber(tradedShares, collateral.decimals) + let displayTradedShares = tradedShares + let displayCollateralToken = collateral + if (collateralSymbol in CompoundTokenType) { + displayTradedShares = compoundService.calculateCTokenToBaseExchange(baseCollateral, tradedShares) + displayCollateralToken = baseCollateral + } + const sharesAmount = formatBigNumber(displayTradedShares, displayCollateralToken.decimals) setStatus(Status.Loading) setMessage(`Buying ${sharesAmount} shares ...`) @@ -205,6 +247,7 @@ const MarketBuyWrapper: React.FC = (props: Props) => { setAmount(null) setAmountToDisplay('') + setDisplayAmountToFund(new BigNumber(0)) setStatus(Status.Ready) setMessage(`Successfully bought ${sharesAmount} '${balances[outcomeIndex].outcomeName}' shares.`) } catch (err) { @@ -225,16 +268,33 @@ const MarketBuyWrapper: React.FC = (props: Props) => { const baseCost = debouncedAmount?.sub(feePaid) const potentialProfit = tradedShares.isZero() ? new BigNumber(0) : tradedShares.sub(amount || Zero) - + let displayFeePaid = feePaid + let displayBaseCost = baseCost + let displayPotentialProfit = potentialProfit + let displayTradedShares = tradedShares + if (collateralSymbol in CompoundTokenType) { + if (collateralSymbol !== displayCollateral.symbol.toLowerCase()) { + displayFeePaid = compoundService.calculateCTokenToBaseExchange(displayCollateral, feePaid) + if (baseCost && baseCost.gt(0)) { + displayBaseCost = compoundService.calculateCTokenToBaseExchange(displayCollateral, baseCost) + } + if (potentialProfit && potentialProfit.gt(0)) { + displayPotentialProfit = compoundService.calculateCTokenToBaseExchange(displayCollateral, potentialProfit) + } + } + displayTradedShares = compoundService.calculateCTokenToBaseExchange(baseCollateral, tradedShares) + } const currentBalance = `${formatBigNumber(collateralBalance, collateral.decimals, 5)}` - const feeFormatted = `${formatNumber(formatBigNumber(feePaid.mul(-1), collateral.decimals))} ${collateral.symbol}` - const baseCostFormatted = `${formatNumber(formatBigNumber(baseCost || Zero, collateral.decimals))} ${ - collateral.symbol + const feeFormatted = `${formatNumber(formatBigNumber(displayFeePaid.mul(-1), displayCollateral.decimals))} ${ + displayCollateral.symbol }` - const potentialProfitFormatted = `${formatNumber(formatBigNumber(potentialProfit, collateral.decimals))} ${ - collateral.symbol + const baseCostFormatted = `${formatNumber(formatBigNumber(displayBaseCost || Zero, displayCollateral.decimals))} ${ + displayCollateral.symbol }` - const sharesTotal = formatNumber(formatBigNumber(tradedShares, collateral.decimals)) + const potentialProfitFormatted = `${formatNumber( + formatBigNumber(displayPotentialProfit, displayCollateral.decimals), + )} ${displayCollateral.symbol}` + const sharesTotal = formatNumber(formatBigNumber(displayTradedShares, baseCollateral.decimals)) const total = `${sharesTotal} Shares` const amountError = @@ -255,16 +315,50 @@ const MarketBuyWrapper: React.FC = (props: Props) => { isNegativeAmount || (!isUpdated && collateral.address === pseudoNativeAssetAddress) - const currencyFilters = + let currencyFilters = collateral.address === wrapToken.address || collateral.address === pseudoNativeAssetAddress ? [wrapToken.address.toLowerCase(), pseudoNativeAssetAddress.toLowerCase()] : [] + if (collateralSymbol in CompoundTokenType) { + if (baseCollateral.symbol.toLowerCase() === 'eth') { + currencyFilters = [collateral.address, pseudoNativeAssetAddress.toLowerCase()] + } else { + currencyFilters = [collateral.address, baseCollateral.address] + } + } + const switchOutcome = (value: number) => { setNewShares(balances.map((balance, i) => (i === outcomeIndex ? balance.shares.add(tradedShares) : balance.shares))) setOutcomeIndex(value) } + const setBuyCollateral = (token: Token) => { + if (token.address === pseudoNativeAssetAddress && !(collateral.symbol.toLowerCase() in CompoundTokenType)) { + setCollateral(token) + setDisplayCollateral(token) + } else { + setDisplayCollateral(token) + } + } + let displayNewShares = newShares + if (newShares && collateralSymbol in CompoundTokenType) { + displayNewShares = newShares.map(function(ns) { + return compoundService.calculateCTokenToBaseExchange(baseCollateral, ns) + }) + } + + const setDisplayAmountToFund = (value: BigNumber) => { + const collateralSymbol = collateral.symbol.toLowerCase() + if (collateral.address !== displayCollateral.address && collateralSymbol in CompoundTokenType) { + const baseAmount = compoundService.calculateBaseToCTokenExchange(displayCollateral, value) + setAmount(baseAmount) + } else { + setAmount(value) + } + setDisplayFundAmount(value) + } + return ( <> = (props: Props) => { OutcomeTableValue.Probability, OutcomeTableValue.Bonded, ]} - newShares={newShares} + displayBalances={displayBalances} + displayCollateral={baseCollateral} + newShares={displayNewShares} outcomeHandleChange={(value: number) => switchOutcome(value)} outcomeSelected={outcomeIndex} probabilities={probabilities} @@ -297,16 +393,17 @@ const MarketBuyWrapper: React.FC = (props: Props) => { { if (token) { - setCollateral(token) + setBuyCollateral(token) setAmount(new BigNumber(0)) setAmountToDisplay('') + setDisplayAmountToFund(new BigNumber(0)) } }} /> @@ -316,23 +413,23 @@ const MarketBuyWrapper: React.FC = (props: Props) => { { - setAmount(e.value) + setDisplayAmountToFund(e.value) setAmountToDisplay('') }} style={{ width: 0 }} - value={amount} + value={displayFundAmount} valueToDisplay={amountToDisplay} /> } onClickMaxButton={() => { setAmount(collateralBalance) - setAmountToDisplay(formatBigNumber(collateralBalance, collateral.decimals, 5)) + setAmountToDisplay(formatBigNumber(collateralBalance, displayCollateral.decimals, 5)) }} shouldDisplayMaxButton={shouldDisplayMaxButton} - symbol={collateral.symbol} + symbol={displayCollateral.symbol} /> {amountError && {amountError}} diff --git a/app/src/components/market/sections/market_buy/market_buy_container.tsx b/app/src/components/market/sections/market_buy/market_buy_container.tsx index 8e7e878c18..613662e9aa 100644 --- a/app/src/components/market/sections/market_buy/market_buy_container.tsx +++ b/app/src/components/market/sections/market_buy/market_buy_container.tsx @@ -1,11 +1,13 @@ import React from 'react' +import { CompoundService } from '../../../../services' import { MarketDetailsTab, MarketMakerData } from '../../../../util/types' import { MarketBuy } from './market_buy' import { ScalarMarketBuy } from './scalar_market_buy' interface Props { + compoundService: CompoundService isScalar: boolean marketMakerData: MarketMakerData switchMarketTab: (arg0: MarketDetailsTab) => void diff --git a/app/src/components/market/sections/market_create/market_wizard_creator.tsx b/app/src/components/market/sections/market_create/market_wizard_creator.tsx index adae277ec3..2497fbcd01 100644 --- a/app/src/components/market/sections/market_create/market_wizard_creator.tsx +++ b/app/src/components/market/sections/market_create/market_wizard_creator.tsx @@ -6,7 +6,7 @@ import { IMPORT_QUESTION_ID_KEY, MARKET_FEE } from '../../../../common/constants import { useConnectedWeb3Context } from '../../../../hooks/connectedWeb3' import { queryTopCategories } from '../../../../queries/markets_home' import { MarketCreationStatus } from '../../../../util/market_creation_status_data' -import { getArbitrator, getDefaultArbitrator, getDefaultToken } from '../../../../util/networks' +import { getArbitrator, getDefaultArbitrator, getDefaultToken, getToken } from '../../../../util/networks' import { limitDecimalPlaces } from '../../../../util/tools' import { Arbitrator, GraphResponseTopCategories, MarketData, Question, Token } from '../../../../util/types' import { BigNumberInputReturn } from '../../../common/form/big_number_input' @@ -18,13 +18,14 @@ interface Props { callback: (param: MarketData, isScalar: boolean) => void marketCreationStatus: MarketCreationStatus marketMakerAddress: string | null + getCompoundInterestRate: (symbol: string) => Promise } export const MarketWizardCreator = (props: Props) => { const context = useConnectedWeb3Context() const { networkId } = context - const { callback, marketCreationStatus } = props + const { callback, getCompoundInterestRate, marketCreationStatus } = props const defaultCollateral = getDefaultToken(networkId) const defaultArbitrator = getDefaultArbitrator(networkId) @@ -46,8 +47,12 @@ export const MarketWizardCreator = (props: Props) => { categoriesCustom: [], category: '', collateral: defaultCollateral, + userInputCollateral: defaultCollateral, + compoundInterestRate: '', + userInputToken: defaultCollateral, funding: new BigNumber('0'), loadedQuestionId: getImportQuestionId(), + useCompoundReserve: false, outcomes: [ { name: '', @@ -214,11 +219,36 @@ export const MarketWizardCreator = (props: Props) => { })) } + const handleUseCompoundReserveChange = (useCompoundReserve: boolean) => { + let collateral = marketData.userInputCollateral + if (useCompoundReserve) { + collateral = getCompoundCollateral(collateral) + } + const newMarketData = { + ...marketData, + collateral, + useCompoundReserve: useCompoundReserve, + } + setMarketdata(newMarketData) + } + + const getCompoundCollateral = (collateral: Token): Token => { + const collateralSymbol = collateral.symbol.toLowerCase() + let compoundCollateralSymbol = `c${collateralSymbol}` + if (collateralSymbol === 'eth' || collateralSymbol === 'weth') { + compoundCollateralSymbol = 'ceth' + } + const compoundCollateralToken = compoundCollateralSymbol as KnownToken + const compoundTokenDetails = getToken(context.networkId, compoundCollateralToken) + return compoundTokenDetails + } + const handleCollateralChange = (collateral: Token, amount: BigNumber) => { const newMarketData = { ...marketData, funding: amount, collateral, + userInputCollateral: collateral, } setMarketdata(newMarketData) } @@ -280,9 +310,11 @@ export const MarketWizardCreator = (props: Props) => { return ( { const [marketCreationStatus, setMarketCreationStatus] = useState(MarketCreationStatus.ready()) const [marketMakerAddress, setMarketMakerAddress] = useState(null) + const getCompoundInterestRate = async (symbol: string): Promise => { + const tokenSymbol = symbol.toLowerCase() + let cToken = `c${tokenSymbol}` + if (tokenSymbol === 'weth') { + cToken = 'ceth' + } + const compoundCollateralToken = cToken as KnownToken + const compoundTokenDetails = getToken(context.networkId, compoundCollateralToken) + const compoundService = new CompoundService(compoundTokenDetails.address, cToken, provider, account) + await compoundService.init() + const supplyRate = compoundService.calculateSupplyRateAPY() + return supplyRate + } + const handleSubmit = async (marketData: MarketData, isScalar: boolean) => { try { if (!account) { @@ -37,19 +52,19 @@ const MarketWizardCreatorContainer: FC = () => { if (!cpk) { return } - setMarketCreationStatus(MarketCreationStatus.creatingAMarket()) - - if (!cpk.cpk.isSafeApp() && marketData.collateral.address !== pseudoNativeAssetAddress) { + if ( + !cpk.cpk.isSafeApp() && + marketData.collateral.address !== pseudoNativeAssetAddress && + marketData.userInputCollateral.address !== pseudoNativeAssetAddress + ) { // Approve collateral to the proxy contract - const collateralService = new ERC20Service(provider, account, marketData.collateral.address) + const collateralService = new ERC20Service(provider, account, marketData.userInputCollateral.address) const hasEnoughAlowance = await collateralService.hasEnoughAllowance(account, cpk.address, marketData.funding) - if (!hasEnoughAlowance) { await collateralService.approveUnlimited(cpk.address) } } - if (isScalar) { const { marketMakerAddress } = await cpk.createScalarMarket({ marketData, @@ -62,14 +77,28 @@ const MarketWizardCreatorContainer: FC = () => { setMarketCreationStatus(MarketCreationStatus.done()) history.replace(`/${marketMakerAddress}`) } else { + let compoundTokenDetails = marketData.userInputCollateral + let compoundService = null + const userInputCollateralSymbol = marketData.userInputCollateral.symbol.toLowerCase() + const useCompoundReserve = marketData.useCompoundReserve + if (useCompoundReserve) { + const cToken = `c${userInputCollateralSymbol}` + const compoundCollateralToken = cToken as KnownToken + compoundTokenDetails = getToken(context.networkId, compoundCollateralToken) + marketData.userInputToken = marketData.userInputCollateral + compoundService = new CompoundService(compoundTokenDetails.address, cToken, provider, account) + await compoundService.init() + } const { marketMakerAddress } = await cpk.createMarket({ + compoundService, + compoundTokenDetails, marketData, conditionalTokens, realitio, marketMakerFactory, + useCompoundReserve, }) setMarketMakerAddress(marketMakerAddress) - setMarketCreationStatus(MarketCreationStatus.done()) history.replace(`/${marketMakerAddress}`) } @@ -84,6 +113,7 @@ const MarketWizardCreatorContainer: FC = () => { <> diff --git a/app/src/components/market/sections/market_create/steps/items/funding_and_fee_step.tsx b/app/src/components/market/sections/market_create/steps/items/funding_and_fee_step.tsx index 634863ea1a..c6f66c3ae4 100644 --- a/app/src/components/market/sections/market_create/steps/items/funding_and_fee_step.tsx +++ b/app/src/components/market/sections/market_create/steps/items/funding_and_fee_step.tsx @@ -24,13 +24,14 @@ import { MarketCreationStatus } from '../../../../../../util/market_creation_sta import { getNativeAsset, pseudoNativeAssetAddress } from '../../../../../../util/networks' import { RemoteData } from '../../../../../../util/remote_data' import { formatBigNumber, formatDate, formatNumber } from '../../../../../../util/tools' -import { Arbitrator, Ternary, Token } from '../../../../../../util/types' +import { Arbitrator, CompoundEnabledTokenType, Ternary, Token } from '../../../../../../util/types' import { Button } from '../../../../../button' import { ButtonType } from '../../../../../button/button_styling_types' import { BigNumberInput, SubsectionTitle, TextfieldCustomPlaceholder } from '../../../../../common' import { BigNumberInputReturn } from '../../../../../common/form/big_number_input' import { TitleValue } from '../../../../../common/text/title_value' import { FullLoading } from '../../../../../loading' +import { AddCompoundService } from '../../../../common/add_compound_service' import { ButtonContainerFullWidth, CurrenciesWrapper, @@ -178,14 +179,17 @@ interface Props { outcomes: Outcome[] loadedQuestionId: Maybe verifyLabel?: string + userInputCollateral: Token lowerBound?: Maybe upperBound?: Maybe startingPoint?: Maybe unit?: string } marketCreationStatus: MarketCreationStatus + getCompoundInterestRate: (userInputCollateral: string) => Promise handleCollateralChange: (collateral: Token, amount: BigNumber) => void handleTradingFeeChange: (fee: string) => void + handleUseCompoundReserveChange: (useCompoundReserve: boolean) => void handleChange: (event: ChangeEvent | ChangeEvent | BigNumberInputReturn) => any resetTradingFee: () => void state: string @@ -198,18 +202,22 @@ const FundingAndFeeStep: React.FC = (props: Props) => { const dispatch = useDispatch() const { account, library: provider } = context const signer = useMemo(() => provider.getSigner(), [provider]) - + const [isServiceChecked, setServiceCheck] = useState(false) + const [compoundInterestRate, setCompoundInterestRate] = useState('-') const { back, + getCompoundInterestRate, handleChange, handleCollateralChange, handleTradingFeeChange, + handleUseCompoundReserveChange, marketCreationStatus, resetTradingFee, state, submit, values, } = props + const { arbitrator, category, @@ -224,11 +232,20 @@ const FundingAndFeeStep: React.FC = (props: Props) => { startingPoint, unit, upperBound, + userInputCollateral, } = values const { markets } = useGraphMarketsFromQuestion(loadedQuestionId || '') const [currentToken, setCurrentToken] = useState({ tokenExists: false, symbol: '', marketAddress: '' }) + let showAddCompundService = false + const currentTokenSymbol = currentToken.symbol.toLowerCase() + const isETHNetwork = () => { + return context.networkId === 1 || context.networkId === 4 + } + if (currentTokenSymbol in CompoundEnabledTokenType && isETHNetwork()) { + showAddCompundService = true + } useEffect(() => { const selectedToken = markets.find( ({ collateralToken }) => @@ -239,15 +256,14 @@ const FundingAndFeeStep: React.FC = (props: Props) => { symbol: 'DAI', marketAddress: selectedToken ? selectedToken.id : '', }) - // eslint-disable-next-line react-hooks/exhaustive-deps }, []) const [allowanceFinished, setAllowanceFinished] = useState(false) - const { allowance, unlock } = useCpkAllowance(signer, collateral.address) + const { allowance, unlock } = useCpkAllowance(signer, userInputCollateral.address) const [amount, setAmount] = useState(funding) const [formattedAmount, setFormattedAmount] = useState(Zero.toString()) - const [amountToDispaly, setAmountToDisplay] = useState('') + const [amountToDisplay, setAmountToDisplay] = useState('') const hasEnoughAllowance = RemoteData.mapToTernary(allowance, allowance => allowance.gte(funding)) const hasZeroAllowance = RemoteData.mapToTernary(allowance, allowance => allowance.isZero()) @@ -256,32 +272,47 @@ const FundingAndFeeStep: React.FC = (props: Props) => { const isUpdated = RemoteData.hasData(proxyIsUpToDate) ? proxyIsUpToDate.data : true useEffect(() => { - dispatch(fetchAccountBalance(account, provider, collateral)) - }, [dispatch, account, provider, collateral]) + dispatch(fetchAccountBalance(account, provider, userInputCollateral)) + }, [dispatch, account, provider, userInputCollateral]) const [collateralBalance, setCollateralBalance] = useState(Zero) const [collateralBalanceFormatted, setCollateralBalanceFormatted] = useState( - formatBigNumber(collateralBalance, collateral.decimals, 5), + formatBigNumber(collateralBalance, userInputCollateral.decimals, 5), ) - const { collateralBalance: maybeCollateralBalance } = useCollateralBalance(collateral, context) + const { collateralBalance: maybeCollateralBalance } = useCollateralBalance(userInputCollateral, context) const [isNegativeDepositAmount, setIsNegativeDepositAmount] = useState(false) useEffect(() => { setCollateralBalance(maybeCollateralBalance || Zero) - setCollateralBalanceFormatted(formatBigNumber(maybeCollateralBalance || Zero, collateral.decimals, 5)) + setCollateralBalanceFormatted(formatBigNumber(maybeCollateralBalance || Zero, userInputCollateral.decimals, 5)) // eslint-disable-next-line }, [maybeCollateralBalance]) useEffect(() => { - setIsNegativeDepositAmount(formatBigNumber(funding, collateral.decimals).includes('-')) - }, [funding, collateral.decimals]) + setIsNegativeDepositAmount(formatBigNumber(funding, userInputCollateral.decimals).includes('-')) + }, [funding, userInputCollateral.decimals]) const resolutionDate = resolution && formatDate(resolution, false) const [customFee, setCustomFee] = useState(false) const [exceedsMaxFee, setExceedsMaxFee] = useState(false) + const toggleServiceCheck = () => { + const newCheckValue = !isServiceChecked + setServiceCheck(newCheckValue) + handleUseCompoundReserveChange(newCheckValue) + } + useEffect(() => { + const getInterestRate = async (userInputCollateral: Token) => { + const interestRate = await getCompoundInterestRate(userInputCollateral.symbol.toLowerCase()) + setCompoundInterestRate(interestRate.toFixed(2)) + } + if (userInputCollateral.symbol.toLowerCase() in CompoundEnabledTokenType) { + getInterestRate(userInputCollateral) + } + }, [isServiceChecked, userInputCollateral, collateral]) // eslint-disable-line react-hooks/exhaustive-deps + const amountError = maybeCollateralBalance === null ? null @@ -290,7 +321,7 @@ const FundingAndFeeStep: React.FC = (props: Props) => { : maybeCollateralBalance.isZero() && funding.gt(maybeCollateralBalance) ? `Insufficient balance` : funding.gt(maybeCollateralBalance) - ? `Value must be less than or equal to ${collateralBalanceFormatted} ${collateral.symbol}` + ? `Value must be less than or equal to ${collateralBalanceFormatted} ${userInputCollateral.symbol}` : null const isCreateMarketbuttonDisabled = @@ -304,7 +335,6 @@ const FundingAndFeeStep: React.FC = (props: Props) => { exceedsMaxFee || isNegativeDepositAmount || (!isUpdated && collateral.address === pseudoNativeAssetAddress) - const showSetAllowance = collateral.address !== pseudoNativeAssetAddress && !cpk?.cpk.isSafeApp() && @@ -353,7 +383,9 @@ const FundingAndFeeStep: React.FC = (props: Props) => { setAmount(newAmount) handleCollateralChange(token, newAmount) + setServiceCheck(false) setAllowanceFinished(false) + setAmount(new BigNumber('0')) } const toggleCustomFee = () => { @@ -382,7 +414,7 @@ const FundingAndFeeStep: React.FC = (props: Props) => { name: 'funding', value: collateralBalance, }) - const formatted = formatBigNumber(collateralBalance, collateral.decimals, 5) + const formatted = formatBigNumber(collateralBalance, userInputCollateral.decimals, 5) setAmountToDisplay(formatted) setFormattedAmount(formatted) } @@ -469,7 +501,7 @@ const FundingAndFeeStep: React.FC = (props: Props) => { addNativeAsset balance={formatNumber(collateralBalanceFormatted, 5)} context={context} - currency={collateral.address} + currency={userInputCollateral.address} disabled={false} onSelect={onCollateralChange} /> @@ -477,17 +509,17 @@ const FundingAndFeeStep: React.FC = (props: Props) => { } onClickMaxButton={onClickMaxButton} shouldDisplayMaxButton={shouldDisplayMaxButton} - symbol={collateral.symbol} + symbol={userInputCollateral.symbol} /> {customFee && ( @@ -507,7 +539,7 @@ const FundingAndFeeStep: React.FC = (props: Props) => { @@ -531,9 +563,17 @@ const FundingAndFeeStep: React.FC = (props: Props) => { hyperlinkDescription={''} /> )} + {showAddCompundService && ( + + )} {showSetAllowance && ( { + compoundService: CompoundService marketMakerData: MarketMakerData theme?: any switchMarketTab: (arg0: MarketDetailsTab) => void @@ -111,11 +124,37 @@ const UserData = styled.div` flex-direction: column; } ` +const CurrencyDropdown = styled(Dropdown)` + min-width: 80px; + display: inline-flex; + float: right; +` +const CustomDropdownItem = styled.div` + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: 100%; + .dropdownItems & .sortBy { + display: none; + } +` +const CurrencyDropdownLabelContainer = styled.div` + margin-top: 20px; +` +const CurrencyDropdownLabel = styled.div` + display: inline-flex; + padding-left: 14px; + padding-top: 14px; + color: ${({ theme }) => theme.colors.textColorDark}; + font-size: ${({ theme }) => theme.textfield.fontSize}; + font-weight: 400; + line-height: 16px; +` const logger = getLogger('Market::Fund') const MarketPoolLiquidityWrapper: React.FC = (props: Props) => { - const { fetchGraphMarketMakerData, marketMakerData } = props + const { compoundService, fetchGraphMarketMakerData, marketMakerData } = props const { address: marketMakerAddress, balances, fee, totalEarnings, totalPoolShares, userEarnings } = marketMakerData const history = useHistory() const context = useConnectedWeb3Context() @@ -138,15 +177,31 @@ const MarketPoolLiquidityWrapper: React.FC = (props: Props) => { const { allowance, unlock } = useCpkAllowance(signer, collateral.address) + const getInitialCollateral = (): Token => { + const collateralSymbol = collateral.symbol.toLowerCase() + if (collateralSymbol in CompoundTokenType) { + const baseCollateralSymbol = getBaseTokenForCToken(collateralSymbol) as KnownToken + const baseToken = getToken(networkId, baseCollateralSymbol) + return baseToken + } else { + return collateral + } + } + const [displayCollateral, setDisplayCollateral] = useState(getInitialCollateral()) const [amountToFund, setAmountToFund] = useState>(new BigNumber(0)) const [amountToFundDisplay, setAmountToFundDisplay] = useState('') const [isNegativeAmountToFund, setIsNegativeAmountToFund] = useState(false) const [amountToRemove, setAmountToRemove] = useState>(new BigNumber(0)) const [amountToRemoveDisplay, setAmountToRemoveDisplay] = useState('') const [isNegativeAmountToRemove, setIsNegativeAmountToRemove] = useState(false) + const [amountToFundNormalized, setAmountToFundNormalized] = useState>(new BigNumber(0)) + const [amountToRemoveNormalized, setAmountToRemoveNormalized] = useState>(new BigNumber(0)) const [status, setStatus] = useState(Status.Ready) const [modalTitle, setModalTitle] = useState('') const [message, setMessage] = useState('') + + const collateralSymbol = collateral.symbol.toLowerCase() + const [isModalTransactionResultOpen, setIsModalTransactionResultOpen] = useState(false) const [upgradeFinished, setUpgradeFinished] = useState(false) @@ -179,12 +234,20 @@ const MarketPoolLiquidityWrapper: React.FC = (props: Props) => { const hasEnoughAllowance = RemoteData.mapToTernary(allowance, allowance => allowance.gte(amountToFund || Zero)) const hasZeroAllowance = RemoteData.mapToTernary(allowance, allowance => allowance.isZero()) - const poolTokens = calcPoolTokens( amountToFund || Zero, balances.map(b => b.holdings), totalPoolShares, ) + let displayPoolTokens = poolTokens + let displayTotalPoolShares = totalPoolShares + let baseCollateral = collateral + if (collateral.symbol.toLowerCase() in CompoundTokenType) { + const baseCollateralSymbol = getBaseTokenForCToken(collateral.symbol.toLowerCase()) as KnownToken + baseCollateral = getToken(networkId, baseCollateralSymbol) + displayPoolTokens = compoundService.calculateCTokenToBaseExchange(baseCollateral, displayPoolTokens) + displayTotalPoolShares = compoundService.calculateCTokenToBaseExchange(baseCollateral, displayTotalPoolShares) + } const sendAmountsAfterAddingFunding = calcAddFundingSendAmounts( amountToFund || Zero, balances.map(b => b.holdings), @@ -193,7 +256,12 @@ const MarketPoolLiquidityWrapper: React.FC = (props: Props) => { const sharesAfterAddingFunding = sendAmountsAfterAddingFunding ? balances.map((balance, i) => balance.shares.add(sendAmountsAfterAddingFunding[i])) : balances.map(balance => balance.shares) - + let displaySharesAfterAddingFunding = sharesAfterAddingFunding + if (collateralSymbol in CompoundTokenType) { + displaySharesAfterAddingFunding = sharesAfterAddingFunding.map(function(saf) { + return compoundService.calculateCTokenToBaseExchange(baseCollateral, saf) + }) + } const sendAmountsAfterRemovingFunding = calcRemoveFundingSendAmounts( amountToRemove || Zero, balances.map(b => b.holdings), @@ -207,11 +275,18 @@ const MarketPoolLiquidityWrapper: React.FC = (props: Props) => { const sharesAfterRemovingFunding = balances.map((balance, i) => { return balance.shares.add(sendAmountsAfterRemovingFunding[i]).sub(depositedTokens) }) - + let displaySharesAfterRemovingFunding = sharesAfterRemovingFunding + if (collateralSymbol in CompoundTokenType) { + displaySharesAfterRemovingFunding = sharesAfterRemovingFunding.map(function(srf) { + return compoundService.calculateCTokenToBaseExchange(baseCollateral, srf) + }) + } + //console.log(sharesAfterRemovingFunding.toString()) + //console.log(displaySharesAfterRemovingFunding.toString()) const showSharesChange = activeTab === Tabs.deposit ? amountToFund?.gt(0) : amountToRemove?.gt(0) const { collateralBalance: maybeCollateralBalance, fetchCollateralBalance } = useCollateralBalance( - collateral, + displayCollateral, context, ) const collateralBalance = maybeCollateralBalance || Zero @@ -223,24 +298,40 @@ const MarketPoolLiquidityWrapper: React.FC = (props: Props) => { const depositedTokensTotal = depositedTokens.add(userEarnings) const { fetchFundingBalance, fundingBalance: maybeFundingBalance } = useFundingBalance(marketMakerAddress, context) const fundingBalance = maybeFundingBalance || Zero - - const walletBalance = formatNumber(formatBigNumber(collateralBalance, collateral.decimals, 5), 5) - const sharesBalance = formatBigNumber(fundingBalance, collateral.decimals) - + let displayFundingBalance = fundingBalance + if (collateral.symbol.toLowerCase() in CompoundTokenType) { + displayFundingBalance = compoundService.calculateCTokenToBaseExchange(baseCollateral, fundingBalance) + } + const walletBalance = formatNumber(formatBigNumber(collateralBalance, displayCollateral.decimals, 5), 5) + let sharesBalance = formatBigNumber(fundingBalance, collateral.decimals) + if (collateral.symbol.toLowerCase() in CompoundTokenType) { + const sharesBalanceBN = compoundService.calculateCTokenToBaseExchange(baseCollateral, fundingBalance) + sharesBalance = formatBigNumber(sharesBalanceBN, baseCollateral.decimals) + } const totalUserShareAmounts = calcRemoveFundingSendAmounts( fundingBalance, balances.map(b => b.holdings), totalPoolShares, ) - const totalDepositedTokens = totalUserShareAmounts.reduce((min: BigNumber, amount: BigNumber) => amount.lt(min) ? amount : min, ) - - const totalUserLiquidity = totalDepositedTokens.add(userEarnings) - - const symbol = collateral.address === pseudoNativeAssetAddress ? wrapToken.symbol : collateral.symbol - + let totalUserLiquidity = totalDepositedTokens.add(userEarnings) + let displayUserEarnings = userEarnings + let displayDepositedTokens = depositedTokens + let displayDepositedTokensTotal = depositedTokensTotal + let displayTotalEarnings = totalEarnings + if (collateral.address !== displayCollateral.address && collateral.symbol.toLowerCase() in CompoundTokenType) { + displayDepositedTokens = compoundService.calculateCTokenToBaseExchange(displayCollateral, depositedTokens) + totalUserLiquidity = compoundService.calculateCTokenToBaseExchange(displayCollateral, totalUserLiquidity) + displayUserEarnings = compoundService.calculateCTokenToBaseExchange(displayCollateral, userEarnings) + displayTotalEarnings = compoundService.calculateCTokenToBaseExchange(displayCollateral, totalEarnings) + displayDepositedTokensTotal = compoundService.calculateCTokenToBaseExchange(displayCollateral, depositedTokensTotal) + } + let symbol = collateral.address === pseudoNativeAssetAddress ? wrapToken.symbol : collateral.symbol + if (displayCollateral && displayCollateral.symbol) { + symbol = displayCollateral.symbol + } const addFunding = async () => { setModalTitle('Deposit Funds') @@ -259,15 +350,20 @@ const MarketPoolLiquidityWrapper: React.FC = (props: Props) => { throw new Error("This method shouldn't be called if 'hasEnoughAllowance' is unknown or false") } - const fundsAmount = formatBigNumber(amountToFund || Zero, collateral.decimals) - + const displayFundsAmount = formatBigNumber(amountToFundNormalized || Zero, displayCollateral.decimals) setStatus(Status.Loading) - setMessage(`Depositing funds: ${fundsAmount} ${collateral.symbol}...`) + setMessage(`Depositing funds: ${displayFundsAmount} ${displayCollateral.symbol}...`) + let useBaseToken = false + if (displayCollateral.address !== collateral.address && collateral.symbol.toLowerCase() in CompoundTokenType) { + useBaseToken = true + } await cpk.addFunding({ - amount: amountToFund || Zero, + amount: amountToFundNormalized || Zero, + compoundService, collateral, marketMaker, + useBaseToken, }) await fetchGraphMarketMakerData() @@ -277,7 +373,8 @@ const MarketPoolLiquidityWrapper: React.FC = (props: Props) => { setStatus(Status.Ready) setAmountToFund(null) setAmountToFundDisplay('') - setMessage(`Successfully deposited ${fundsAmount} ${collateral.symbol}`) + setDisplayCollateralAmountToFund(new BigNumber(0)) + setMessage(`Successfully deposited ${displayFundsAmount} ${displayCollateral.symbol}`) } catch (err) { setStatus(Status.Error) setMessage(`Error trying to deposit funds.`) @@ -293,23 +390,32 @@ const MarketPoolLiquidityWrapper: React.FC = (props: Props) => { return } setStatus(Status.Loading) - - const fundsAmount = formatBigNumber(depositedTokensTotal, collateral.decimals) - + const fundsAmount = formatBigNumber(displayDepositedTokensTotal, displayCollateral.decimals) setMessage(`Withdrawing funds: ${fundsAmount} ${symbol}...`) - const collateralAddress = await marketMaker.getCollateralToken() const conditionId = await marketMaker.getConditionId() - + let useBaseToken = false + let normalizedAmountToMerge = depositedTokens + let normalizedAmountToRemove = amountToRemove || Zero + if (displayCollateral.address !== collateral.address) { + useBaseToken = true + normalizedAmountToMerge = compoundService.calculateBaseToCTokenExchange(baseCollateral, depositedTokens) + normalizedAmountToRemove = compoundService.calculateBaseToCTokenExchange( + baseCollateral, + normalizedAmountToRemove, + ) + } await cpk.removeFunding({ - amountToMerge: depositedTokens, + amountToMerge: normalizedAmountToMerge, collateralAddress, + compoundService, conditionId, conditionalTokens, earnings: userEarnings, marketMaker, outcomesCount: balances.length, - sharesToBurn: amountToRemove || Zero, + sharesToBurn: normalizedAmountToRemove || Zero, + useBaseToken, }) await fetchGraphMarketMakerData() await fetchFundingBalance() @@ -332,9 +438,7 @@ const MarketPoolLiquidityWrapper: React.FC = (props: Props) => { if (!cpk) { return } - await unlock() - setAllowanceFinished(true) } @@ -365,10 +469,12 @@ const MarketPoolLiquidityWrapper: React.FC = (props: Props) => { ? null : maybeFundingBalance.isZero() && amountToRemove?.gt(maybeFundingBalance) ? `Insufficient balance` - : amountToRemove?.gt(maybeFundingBalance) + : amountToRemove?.gt(displayFundingBalance) ? `Value must be less than or equal to ${sharesBalance} pool shares` : null - + const setUserInputCollateral = (userInput: Token) => { + setDisplayCollateral(userInput) + } const disableDepositButton = !amountToFund || amountToFund?.isZero() || @@ -380,40 +486,123 @@ const MarketPoolLiquidityWrapper: React.FC = (props: Props) => { const disableWithdrawButton = !amountToRemove || amountToRemove?.isZero() || - amountToRemove?.gt(fundingBalance) || + amountToRemove?.gt(displayFundingBalance) || sharesAmountError !== null || isNegativeAmountToRemove - const currencyFilters = - collateral.address === wrapToken.address || collateral.address === pseudoNativeAssetAddress - ? [wrapToken.address.toLowerCase(), pseudoNativeAssetAddress.toLowerCase()] + let withdrawCurrencySelect = + let filterItems: Array = [] + if (collateralSymbol in CompoundTokenType) { + const baseTokenSymbol = getBaseTokenForCToken(collateralSymbol) + const baseToken = getToken(networkId, baseTokenSymbol as KnownToken) + const filters = [ + { + title: baseToken.symbol, + onClick: () => { + setAmountToRemoveNormalized(new BigNumber('0')) + setUserInputCollateral(baseToken) + }, + }, + { + title: collateral.symbol, + onClick: () => { + setAmountToRemoveNormalized(new BigNumber('0')) + setUserInputCollateral(collateral) + }, + }, + ] + filterItems = filters.map(item => { + return { + content: {item.title}, + onClick: item.onClick, + } + }) + withdrawCurrencySelect = ( + + Withdraw as + + + ) + } + + const setBuyCollateral = (token: Token) => { + const userInputCollateralSymbol = token.symbol.toLowerCase() + if (userInputCollateralSymbol in CompoundTokenType) { + setDisplayCollateral(token) + } else if (token.address === pseudoNativeAssetAddress && !(collateral.symbol.toLowerCase() in CompoundTokenType)) { + setCollateral(token) + setDisplayCollateral(token) + } else { + setDisplayCollateral(token) + } + } + + const setDisplayCollateralAmountToFund = (value: BigNumber) => { + if (collateral.address === displayCollateral.address) { + setAmountToFund(value) + } else { + const baseAmount = compoundService.calculateBaseToCTokenExchange(displayCollateral, value) + setAmountToFund(baseAmount) + } + setAmountToFundNormalized(value) + } + + const wrapAddress = wrapToken.address + + let currencyFilters = + collateral.address === wrapAddress || collateral.address === pseudoNativeAssetAddress + ? [wrapAddress, pseudoNativeAssetAddress.toLowerCase()] : [] const shouldDisplayMaxButton = collateral.address !== pseudoNativeAssetAddress + if (collateralSymbol in CompoundTokenType) { + const baseCollateralSymbol = getBaseTokenForCToken(collateralSymbol) as KnownToken + const baseCollateral = getToken(networkId, baseCollateralSymbol) + if (baseCollateral.symbol.toLowerCase() === 'eth') { + currencyFilters = [collateral.address, pseudoNativeAssetAddress.toLowerCase()] + } else { + currencyFilters = [collateral.address, baseCollateral.address] + } + } + let displayBalances = balances + if (displayCollateral.address !== collateral.address && collateral.symbol.toLowerCase() in CompoundTokenType) { + displayBalances = getSharesInBaseToken(balances, compoundService, displayCollateral) + } + + const setWithdrawAmountToRemove = (val: BigNumber) => { + let normalizedWithdrawAmount = val + if (collateral.symbol.toLowerCase() in CompoundTokenType && displayCollateral.address !== collateral.address) { + normalizedWithdrawAmount = compoundService.calculateBaseToCTokenExchange(baseCollateral, normalizedWithdrawAmount) + } + setAmountToRemove(normalizedWithdrawAmount) + setAmountToRemoveNormalized(val) + } return ( <> @@ -421,8 +610,10 @@ const MarketPoolLiquidityWrapper: React.FC = (props: Props) => { balances={balances} collateral={collateral} disabledColumns={[OutcomeTableValue.OutcomeProbability, OutcomeTableValue.Payout, OutcomeTableValue.Bonded]} + displayBalances={displayBalances} + displayCollateral={displayCollateral} displayRadioSelection={false} - newShares={activeTab === Tabs.deposit ? sharesAfterAddingFunding : sharesAfterRemovingFunding} + newShares={activeTab === Tabs.deposit ? displaySharesAfterAddingFunding : displaySharesAfterRemovingFunding} probabilities={probabilities} showSharesChange={showSharesChange} /> @@ -451,13 +642,14 @@ const MarketPoolLiquidityWrapper: React.FC = (props: Props) => { addNativeAsset balance={walletBalance} context={context} - currency={collateral.address} + currency={displayCollateral.address} disabled={currencyFilters.length ? false : true} filters={currencyFilters} onSelect={(token: Token | null) => { if (token) { - setCollateral(token) + setBuyCollateral(token) setAmountToFund(new BigNumber(0)) + setDisplayCollateralAmountToFund(new BigNumber(0)) } }} /> @@ -466,23 +658,24 @@ const MarketPoolLiquidityWrapper: React.FC = (props: Props) => { { - setAmountToFund(e.value) + setDisplayCollateralAmountToFund(e.value) setAmountToFundDisplay('') }} style={{ width: 0 }} - value={amountToFund} + value={amountToFundNormalized} valueToDisplay={amountToFundDisplay} /> } onClickMaxButton={() => { setAmountToFund(collateralBalance) - setAmountToFundDisplay(formatBigNumber(collateralBalance, collateral.decimals, 5)) + setDisplayCollateralAmountToFund(collateralBalance) + setAmountToFundDisplay(formatBigNumber(collateralBalance, displayCollateral.decimals, 5)) }} shouldDisplayMaxButton={shouldDisplayMaxButton} - symbol={collateral.symbol} + symbol={displayCollateral.symbol} /> {collateralAmountError && {collateralAmountError}} @@ -495,25 +688,25 @@ const MarketPoolLiquidityWrapper: React.FC = (props: Props) => { { - setAmountToRemove(e.value) + setWithdrawAmountToRemove(e.value) setAmountToRemoveDisplay('') }} style={{ width: 0 }} - value={amountToRemove} + value={amountToRemoveNormalized} valueToDisplay={amountToRemoveDisplay} /> } onClickMaxButton={() => { setAmountToRemove(fundingBalance) - setAmountToRemoveDisplay(formatBigNumber(fundingBalance, collateral.decimals, 5)) + setAmountToRemoveDisplay(formatBigNumber(displayFundingBalance, displayCollateral.decimals, 5)) }} shouldDisplayMaxButton symbol="" /> - + {withdrawCurrencySelect} {sharesAmountError && {sharesAmountError}} )} @@ -529,10 +722,10 @@ const MarketPoolLiquidityWrapper: React.FC = (props: Props) => { /> )} @@ -542,19 +735,21 @@ const MarketPoolLiquidityWrapper: React.FC = (props: Props) => { emphasizeValue={userEarnings.gt(0)} state={ValueStates.success} title="Earned" - value={`${formatNumber(formatBigNumber(userEarnings, collateral.decimals))} ${symbol}`} + value={`${formatNumber(formatBigNumber(displayUserEarnings, displayCollateral.decimals))} ${symbol}`} /> )} diff --git a/app/src/components/market/sections/market_pooling/market_pool_liquidity_container.tsx b/app/src/components/market/sections/market_pooling/market_pool_liquidity_container.tsx index 7cc3b9dd3c..b74a4753d6 100644 --- a/app/src/components/market/sections/market_pooling/market_pool_liquidity_container.tsx +++ b/app/src/components/market/sections/market_pooling/market_pool_liquidity_container.tsx @@ -1,11 +1,13 @@ import React from 'react' +import { CompoundService } from '../../../../services' import { MarketDetailsTab, MarketMakerData } from '../../../../util/types' import { MarketPoolLiquidity } from './market_pool_liquidity' import { ScalarMarketPoolLiquidity } from './scalar_market_pool_liquidity' interface Props { + compoundService: CompoundService marketMakerData: MarketMakerData fetchGraphMarketMakerData: () => Promise fetchGraphMarketTradeData: () => Promise diff --git a/app/src/components/market/sections/market_profile/market_status/closed.tsx b/app/src/components/market/sections/market_profile/market_status/closed.tsx index 83c3fb2f30..3d2b1bae37 100644 --- a/app/src/components/market/sections/market_profile/market_status/closed.tsx +++ b/app/src/components/market/sections/market_profile/market_status/closed.tsx @@ -8,9 +8,16 @@ import styled from 'styled-components' import { useConnectedCPKContext, useContracts, useGraphMarketTradeData } from '../../../../../hooks' import { WhenConnected, useConnectedWeb3Context } from '../../../../../hooks/connectedWeb3' import { ERC20Service } from '../../../../../services' +import { CompoundService } from '../../../../../services/compound_service' import { getLogger } from '../../../../../util/logger' import { formatBigNumber, getUnit } from '../../../../../util/tools' -import { MarketDetailsTab, MarketMakerData, OutcomeTableValue, Status } from '../../../../../util/types' +import { + CompoundTokenType, + MarketDetailsTab, + MarketMakerData, + OutcomeTableValue, + Status, +} from '../../../../../util/types' import { Button, ButtonContainer } from '../../../../button' import { ButtonType } from '../../../../button/button_styling_types' import { FullLoading } from '../../../../loading' @@ -135,10 +142,38 @@ const Wrapper = (props: Props) => { const [modalTitle, setModalTitle] = useState('') const [message, setMessage] = useState('') const [isModalTransactionResultOpen, setIsModalTransactionResultOpen] = useState(false) + const marketCollateralToken = collateralToken + const [compoundService, setCompoundService] = useState( + new CompoundService(collateralToken.address, collateralToken.symbol, provider, account), + ) const [collateral, setCollateral] = useState(new BigNumber(0)) const marketMaker = useMemo(() => buildMarketMaker(marketMakerAddress), [buildMarketMaker, marketMakerAddress]) + useMemo(() => { + const getResult = async () => { + const compoundServiceObject = new CompoundService( + marketCollateralToken.address, + marketCollateralToken.symbol, + provider, + account, + ) + await compoundServiceObject.init() + setCompoundService(compoundServiceObject) + } + if (marketCollateralToken.symbol.toLowerCase() in CompoundTokenType) { + getResult() + } + }, [marketCollateralToken.address, account, marketCollateralToken.symbol, provider]) + useEffect(() => { + const getResult = async () => { + await compoundService.init() + setCompoundService(compoundService) + } + if (marketCollateralToken.symbol.toLowerCase() in CompoundTokenType) { + getResult() + } + }, []) // eslint-disable-line react-hooks/exhaustive-deps const resolveCondition = async () => { setModalTitle('Resolve Condition') @@ -389,6 +424,7 @@ const Wrapper = (props: Props) => { )} {currentTab === MarketDetailsTab.pool && ( { {currentTab === MarketDetailsTab.history && } {currentTab === MarketDetailsTab.buy && ( { )} {currentTab === MarketDetailsTab.sell && ( { scalarLow, totalPoolShares, } = marketMakerData - + const [displayCollateral, setDisplayCollateral] = useState(collateral) + const { account, library: provider, networkId } = context + const [compoundService, setCompoundService] = useState( + new CompoundService(collateral.address, collateral.symbol, provider, account), + ) const isQuestionOpen = question.resolution.valueOf() < Date.now() + useMemo(() => { + const getResult = async () => { + const compoundServiceObject = new CompoundService(collateral.address, collateral.symbol, provider, account) + await compoundServiceObject.init() + setCompoundService(compoundServiceObject) + } + if (collateral.symbol.toLowerCase() in CompoundTokenType) { + getResult() + } + }, [collateral.address, account, collateral.symbol, provider]) + + useEffect(() => { + const getResult = async () => { + await compoundService.init() + setCompoundService(compoundService) + } + if (collateral.symbol.toLowerCase() in CompoundTokenType) { + getResult() + } + }, []) // eslint-disable-line react-hooks/exhaustive-deps + const setCurrentDisplayCollateral = () => { + // if collateral is a cToken then convert the collateral and balances to underlying token + const collateralSymbol = collateral.symbol.toLowerCase() + if (collateralSymbol in CompoundTokenType) { + const baseCollateralSymbol = collateralSymbol.substring(1, collateralSymbol.length) + const baseCollateralToken = getToken(networkId, baseCollateralSymbol as KnownToken) + setDisplayCollateral(baseCollateralToken) + } else { + setDisplayCollateral(collateral) + } + } useEffect(() => { const timeDifference = new Date(question.resolution).getTime() - new Date().getTime() const maxTimeDifference = 86400000 @@ -130,8 +174,17 @@ const Wrapper = (props: Props) => { fetchGraphMarketMakerData() setCurrentTab(MarketDetailsTab.finalize) } + setCurrentDisplayCollateral() // eslint-disable-next-line react-hooks/exhaustive-deps }, []) + + useEffect(() => { + setCurrentDisplayCollateral() + }, [collateral.symbol]) // eslint-disable-line react-hooks/exhaustive-deps + let displayBalances = balances + if (collateral.address !== displayCollateral.address && collateral.symbol.toLowerCase() in CompoundTokenType) { + displayBalances = getSharesInBaseToken(balances, compoundService, displayCollateral) + } const userHasShares = balances.some((balanceItem: BalanceItem) => { const { shares } = balanceItem return shares && !isDust(shares, collateral.decimals) @@ -147,16 +200,16 @@ const Wrapper = (props: Props) => { OutcomeTableValue.Probability, OutcomeTableValue.Bonded, ] - if (!userHasShares) { disabledColumns.push(OutcomeTableValue.Shares) } - return ( @@ -382,6 +435,7 @@ const Wrapper = (props: Props) => { )} {currentTab === MarketDetailsTab.pool && ( { {currentTab === MarketDetailsTab.history && } {currentTab === MarketDetailsTab.buy && ( { )} {currentTab === MarketDetailsTab.sell && ( theme.borders.borderLineDisabled}; ` +const CurrencyDropdown = styled(Dropdown)` + min-width: 80px; + display: inline-flex; + float: right; +` +const CustomDropdownItem = styled.div` + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: 100%; + .dropdownItems & .sortBy { + display: none; + } +` +const CurrencyDropdownLabelContainer = styled.div` + margin-top: 20px; +` +const CurrencyDropdownLabel = styled.div` + display: inline-flex; + padding-left: 14px; + padding-top: 14px; + color: ${({ theme }) => theme.colors.textColorDark}; + font-size: ${({ theme }) => theme.textfield.fontSize}; + font-weight: 400; + line-height: 16px; +` + const logger = getLogger('Market::Sell') interface Props extends RouteComponentProps { + compoundService: CompoundService fetchGraphMarketMakerData: () => Promise marketMakerData: MarketMakerData switchMarketTab: (arg0: MarketDetailsTab) => void @@ -50,8 +91,10 @@ const MarketSellWrapper: React.FC = (props: Props) => { const context = useConnectedWeb3Context() const cpk = useConnectedCPKContext() const { buildMarketMaker, conditionalTokens } = useContracts(context) - const { fetchGraphMarketMakerData, marketMakerData, switchMarketTab } = props + const { compoundService, fetchGraphMarketMakerData, marketMakerData, switchMarketTab } = props const { address: marketMakerAddress, balances, collateral, fee } = marketMakerData + const collateralSymbol = collateral.symbol.toLowerCase() + let currencySelect = let defaultOutcomeIndex = 0 for (let i = 0; i < balances.length; i++) { @@ -69,16 +112,31 @@ const MarketSellWrapper: React.FC = (props: Props) => { const [balanceItem, setBalanceItem] = useState(balances[outcomeIndex]) const [amountShares, setAmountShares] = useState>(new BigNumber(0)) const [amountSharesToDisplay, setAmountSharesToDisplay] = useState('') + const [displaySellShares, setDisplaySellShares] = useState>(new BigNumber(0)) const [isNegativeAmountShares, setIsNegativeAmountShares] = useState(false) const [message, setMessage] = useState('') const [isModalTransactionResultOpen, setIsModalTransactionResultOpen] = useState(false) - + const { networkId } = context + const getInitialCollateral = (): Token => { + if (collateralSymbol in CompoundTokenType) { + const baseCollateralSymbol = getBaseTokenForCToken(collateralSymbol) as KnownToken + const baseToken = getToken(networkId, baseCollateralSymbol) + return baseToken + } else { + return collateral + } + } + let baseCollateral = collateral + if (collateralSymbol in CompoundTokenType) { + const baseCollateralSymbol = getBaseTokenForCToken(collateralSymbol) as KnownToken + baseCollateral = getToken(networkId, baseCollateralSymbol) + } + const [displayCollateral, setDisplayCollateral] = useState(getInitialCollateral()) const marketFeeWithTwoDecimals = Number(formatBigNumber(fee, 18)) useEffect(() => { setIsNegativeAmountShares(formatBigNumber(amountShares || Zero, collateral.decimals).includes('-')) }, [amountShares, collateral.decimals]) - useEffect(() => { setBalanceItem(balances[outcomeIndex]) // eslint-disable-next-line @@ -92,10 +150,43 @@ const MarketSellWrapper: React.FC = (props: Props) => { // eslint-disable-next-line }, [collateral.address]) + let displayBalances = balances + if (displayCollateral.address !== collateral.address && collateral.symbol.toLowerCase() in CompoundTokenType) { + displayBalances = getSharesInBaseToken(balances, compoundService, displayCollateral) + } + const setUserInputCollateral = (symbol: string): void => { + const inputToken = getToken(networkId, symbol.toLowerCase() as KnownToken) + setDisplayCollateral(inputToken) + } + if (collateralSymbol in CompoundTokenType) { + const filters = [ + { + title: baseCollateral.symbol, + onClick: () => setUserInputCollateral(baseCollateral.symbol), + }, + { + title: collateral.symbol, + onClick: () => setUserInputCollateral(collateral.symbol), + }, + ] + const filterItems: Array = filters.map(item => { + return { + content: {item.title}, + onClick: item.onClick, + } + }) + currencySelect = ( + + Withdraw as + + + ) + } + const calcSellAmount = useMemo( () => async ( amountShares: BigNumber, - ): Promise<[number[], Maybe, Maybe, Maybe]> => { + ): Promise<[number[], number[], Maybe, Maybe, Maybe]> => { const holdings = balances.map(balance => balance.holdings) const holdingsOfSoldOutcome = holdings[outcomeIndex] const holdingsOfOtherOutcomes = holdings.filter((item, index) => { @@ -114,7 +205,7 @@ const MarketSellWrapper: React.FC = (props: Props) => { logger.warn( `Could not compute amount of collateral to sell for '${amountShares.toString()}' and '${holdingsOfSoldOutcome.toString()}'`, ) - return [[], null, null, null] + return [[], [], null, null, null] } const balanceAfterTrade = computeBalanceAfterTrade( @@ -129,19 +220,34 @@ const MarketSellWrapper: React.FC = (props: Props) => { const costFee = potentialValue.sub(amountToSell) const probabilities = pricesAfterTrade.map(priceAfterTrade => priceAfterTrade * 100) + const displayProbabilities = probabilities logger.log(`Amount to sell ${amountToSell}`) - return [probabilities, costFee, amountToSell, potentialValue] - }, + return [probabilities, displayProbabilities, costFee, amountToSell, potentialValue] + }, // eslint-disable-next-line [outcomeIndex, balances, marketFeeWithTwoDecimals], ) - const [probabilities, costFee, tradedCollateral, potentialValue] = useAsyncDerivedValue( + const [probabilities, displayProbabilities, costFee, tradedCollateral, potentialValue] = useAsyncDerivedValue( amountShares || Zero, - [balances.map(() => 0), null, null, null], + [balances.map(() => 0), balances.map(() => 0), null, null, null], calcSellAmount, ) + let potentialValueNormalized = potentialValue + let costFeeNormalized = costFee + let normalizedTradedCollateral = tradedCollateral + if (displayCollateral.address !== collateral.address) { + if (potentialValue && potentialValue.gt(0)) { + potentialValueNormalized = compoundService.calculateCTokenToBaseExchange(displayCollateral, potentialValue) + } + if (costFee && costFee.gt(0)) { + costFeeNormalized = compoundService.calculateCTokenToBaseExchange(displayCollateral, costFee) + } + if (tradedCollateral && tradedCollateral.gt(0)) { + normalizedTradedCollateral = compoundService.calculateCTokenToBaseExchange(displayCollateral, tradedCollateral) + } + } const finish = async () => { try { if (!tradedCollateral) { @@ -153,22 +259,31 @@ const MarketSellWrapper: React.FC = (props: Props) => { } const sharesAmount = formatBigNumber(amountShares || Zero, collateral.decimals) - + let displaySharesAmount = sharesAmount + if (collateral.symbol.toLowerCase() in CompoundTokenType && amountShares) { + const displaySharesAmountValue = compoundService.calculateCTokenToBaseExchange(baseCollateral, amountShares) + displaySharesAmount = formatBigNumber(displaySharesAmountValue || Zero, baseCollateral.decimals) + } setStatus(Status.Loading) - setMessage(`Selling ${sharesAmount} shares...`) - + setMessage(`Selling ${displaySharesAmount} shares...`) + let useBaseToken = false + if (displayCollateral.address !== collateral.address && collateral.symbol.toLowerCase() in CompoundTokenType) { + useBaseToken = true + } await cpk.sellOutcomes({ amount: tradedCollateral, + compoundService, outcomeIndex, marketMaker, conditionalTokens, + useBaseToken, }) await fetchGraphMarketMakerData() setAmountShares(null) setAmountSharesToDisplay('') setStatus(Status.Ready) - setMessage(`Successfully sold ${sharesAmount} '${balances[outcomeIndex].outcomeName}' shares.`) + setMessage(`Successfully sold ${displaySharesAmount} '${balances[outcomeIndex].outcomeName}' shares.`) } catch (err) { setStatus(Status.Error) setMessage(`Error trying to sell '${balances[outcomeIndex].outcomeName}' shares.`) @@ -177,8 +292,27 @@ const MarketSellWrapper: React.FC = (props: Props) => { setIsModalTransactionResultOpen(true) } + const newShares = balances.map((balance, i) => + i === outcomeIndex ? balance.shares.sub(amountShares || Zero) : balance.shares, + ) + let displayNewShares = newShares + if (collateralSymbol in CompoundTokenType) { + displayNewShares = newShares.map(function(ns) { + return compoundService.calculateCTokenToBaseExchange(baseCollateral, ns) + }) + } const selectedOutcomeBalance = formatNumber(formatBigNumber(balanceItem.shares, collateral.decimals)) - + let displaySelectedOutcomeBalance = selectedOutcomeBalance + let displaySelectedOutcomeBalanceValue = balanceItem.shares + if (collateralSymbol in CompoundTokenType) { + displaySelectedOutcomeBalanceValue = compoundService.calculateCTokenToBaseExchange( + baseCollateral, + balanceItem.shares, + ) + displaySelectedOutcomeBalance = formatNumber( + formatBigNumber(displaySelectedOutcomeBalanceValue, baseCollateral.decimals), + ) + } const amountError = balanceItem.shares === null ? null @@ -187,13 +321,29 @@ const MarketSellWrapper: React.FC = (props: Props) => { : amountShares?.gt(balanceItem.shares) ? `Value must be less than or equal to ${selectedOutcomeBalance} shares` : null - const isSellButtonDisabled = !amountShares || (status !== Status.Ready && status !== Status.Error) || amountShares?.isZero() || amountError !== null || isNegativeAmountShares + const setAmountSharesFromInput = (shares: BigNumber) => { + if (collateralSymbol in CompoundTokenType) { + const actualAmountOfShares = compoundService.calculateBaseToCTokenExchange(baseCollateral, shares) + setAmountShares(actualAmountOfShares) + } else { + setAmountShares(shares) + } + } + const setDisplayAmountToSell = (val: BigNumber) => { + if (displayCollateral.address !== collateral.address && collateral.symbol.toLowerCase() in CompoundTokenType) { + const actualSellAmount = compoundService.calculateBaseToCTokenExchange(displayCollateral, val) + setAmountShares(actualSellAmount) + } else { + setAmountShares(val) + } + setDisplaySellShares(val) + } return ( <> = (props: Props) => { OutcomeTableValue.Probability, OutcomeTableValue.Bonded, ]} - newShares={balances.map((balance, i) => - i === outcomeIndex ? balance.shares.sub(amountShares || Zero) : balance.shares, - )} + displayBalances={displayBalances} + displayCollateral={displayCollateral} + displayProbabilities={displayProbabilities} + newShares={displayNewShares} outcomeHandleChange={(value: number) => { setOutcomeIndex(value) setBalanceItem(balances[value]) @@ -219,30 +370,31 @@ const MarketSellWrapper: React.FC = (props: Props) => { />
- + { - setAmountShares(e.value) + setDisplayAmountToSell(e.value) setAmountSharesToDisplay('') }} style={{ width: 0 }} - value={amountShares} + value={displaySellShares} valueToDisplay={amountSharesToDisplay} /> } onClickMaxButton={() => { - setAmountShares(balanceItem.shares) - setAmountSharesToDisplay(formatBigNumber(balanceItem.shares, collateral.decimals, 5)) + setAmountSharesFromInput(displaySelectedOutcomeBalanceValue) + setAmountSharesToDisplay(formatBigNumber(displaySelectedOutcomeBalanceValue, baseCollateral.decimals, 5)) }} shouldDisplayMaxButton symbol={'Shares'} /> {amountError && {amountError}} + {currencySelect}
@@ -251,20 +403,24 @@ const MarketSellWrapper: React.FC = (props: Props) => { value={`${formatNumber(formatBigNumber(amountShares || Zero, collateral.decimals))} Shares`} /> = (props: Props) => { } title={'Total'} value={`${ - tradedCollateral ? formatNumber(formatBigNumber(tradedCollateral, collateral.decimals, 2)) : '0.00' - } ${collateral.symbol}`} + normalizedTradedCollateral + ? formatNumber(formatBigNumber(normalizedTradedCollateral, displayCollateral.decimals, 2)) + : '0.00' + } ${displayCollateral.symbol}`} />
diff --git a/app/src/components/market/sections/market_sell/market_sell_container.tsx b/app/src/components/market/sections/market_sell/market_sell_container.tsx index c9dafceb06..6206ab6438 100644 --- a/app/src/components/market/sections/market_sell/market_sell_container.tsx +++ b/app/src/components/market/sections/market_sell/market_sell_container.tsx @@ -1,11 +1,13 @@ import React from 'react' +import { CompoundService } from '../../../../services' import { MarketDetailsTab, MarketMakerData } from '../../../../util/types' import { MarketSell } from './market_sell' import { ScalarMarketSell } from './scalar_market_sell' interface Props { + compoundService: CompoundService isScalar: boolean marketMakerData: MarketMakerData switchMarketTab: (arg0: MarketDetailsTab) => void diff --git a/app/src/global.d.ts b/app/src/global.d.ts index 82e9089189..f068436dfd 100644 --- a/app/src/global.d.ts +++ b/app/src/global.d.ts @@ -10,6 +10,6 @@ declare module 'react-share' declare type Maybe = T | null -declare type KnownToken = 'cdai' | 'usdc' | 'dai' | 'weth' | 'owl' | 'chai' | 'gno' | 'pnk' | 'dxd' | 'wspoa' | 'wxdai' +declare type KnownToken = 'cdai' | 'cbat' | 'ceth' | 'cusdc' | 'cusdt' | 'cwbtc' | 'cuni' | 'eth' | 'usdc' | 'dai' | 'weth' | 'owl' | 'chai' | 'gno' | 'pnk' | 'dxd' | 'wspoa' | 'wxdai' declare type KnownArbitrator = 'kleros' | 'unknown' diff --git a/app/src/hooks/useBlockchainMarketMakerData.tsx b/app/src/hooks/useBlockchainMarketMakerData.tsx index df871cb9d4..26d5068dbd 100644 --- a/app/src/hooks/useBlockchainMarketMakerData.tsx +++ b/app/src/hooks/useBlockchainMarketMakerData.tsx @@ -172,6 +172,7 @@ export const useBlockchainMarketMakerData = (graphMarketMakerData: Maybe { let collateralBalance = new BigNumber(0) + setCollateralBalance(collateralBalance) if (account) { if (collateral.address === pseudoNativeAssetAddress) { collateralBalance = await provider.getBalance(account) @@ -28,7 +29,6 @@ export const useCollateralBalance = ( collateralBalance = await collateralService.getCollateral(account) } } - setCollateralBalance(collateralBalance) } diff --git a/app/src/services/compound_service.ts b/app/src/services/compound_service.ts new file mode 100644 index 0000000000..fa777dfe65 --- /dev/null +++ b/app/src/services/compound_service.ts @@ -0,0 +1,158 @@ +import Big from 'big.js' +import { Contract, Wallet, ethers, utils } from 'ethers' +import { BigNumber, formatUnits, parseUnits } from 'ethers/utils' + +import { cBATAbi, cDaiAbi, cETHAbi, cUNIAbi, cUSDCAbi, cUSDTAbi, cWBTCAbi } from '../abi/compound_abi' +import { roundNumberStringToSignificantDigits } from '../util/tools' +import { Token } from '../util/types' + +// use floor as rounding method +Big.RM = 0 +const RoundingFactor = 100000 + +class CompoundService { + contract: Contract + signerAddress: Maybe + provider: any + exchangeRate: number + constructor(address: string, symbol: string, provider: any, signerAddress: Maybe) { + const cTokenABI = CompoundService.getABI(symbol) + if (signerAddress) { + const signer: Wallet = provider.getSigner() + this.contract = new ethers.Contract(address, cTokenABI, provider).connect(signer) + } else { + this.contract = new ethers.Contract(address, cTokenABI, provider) + } + this.exchangeRate = 0 + this.signerAddress = signerAddress + this.provider = provider + } + + init = async () => { + this.exchangeRate = await this.calculateExchangeRate() + } + + calculateSupplyRateAPY = async (): Promise => { + const supplyRate: number = await this.contract.supplyRatePerBlock() + const supplyMantissa = 1e18 + const blocksPerDay = 4 * 60 * 24 + const daysPerYear = 365 + const supplyApy = (Math.pow((supplyRate / supplyMantissa) * blocksPerDay + 1, daysPerYear - 1) - 1) * 100 + return supplyApy + } + + calculateCTokenToBaseExchange = (baseToken: Token, cTokenFunding: BigNumber): BigNumber => { + const cTokenDecimals = 8 + const bigTen = new Big(10) + const userCTokenAmount = new Big(formatUnits(cTokenFunding, cTokenDecimals)) + const exchangeRate = new Big(this.exchangeRate) + const baseTokenDecimals = Number(baseToken.decimals) + const mantissa = 18 + baseTokenDecimals - cTokenDecimals + if (exchangeRate.eq(0)) { + return new BigNumber('0') + } + const exp = bigTen.pow(mantissa) + const oneCTokenInUnderlying = exchangeRate.div(exp) + const amountUnderlyingTokens = userCTokenAmount.mul(oneCTokenInUnderlying) + let amountUnderlyingTokensBoundToPrecision = roundNumberStringToSignificantDigits( + amountUnderlyingTokens.toString(), + 4, + ) + try { + const underlyingBigNumber = parseUnits(amountUnderlyingTokensBoundToPrecision, baseTokenDecimals) + return underlyingBigNumber + } catch (e) { + const amountUnderlyingTokensNumber = new Big(amountUnderlyingTokensBoundToPrecision).mul(RoundingFactor) + amountUnderlyingTokensBoundToPrecision = roundNumberStringToSignificantDigits( + amountUnderlyingTokensNumber.toString(), + 4, + ) + let underlyingBigNumber = parseUnits(amountUnderlyingTokensBoundToPrecision.toString(), baseTokenDecimals) + underlyingBigNumber = underlyingBigNumber.div(RoundingFactor) + return underlyingBigNumber + } + } + + calculateBaseToCTokenExchange = (userInputToken: Token, userInputTokenFunding: BigNumber): BigNumber => { + const cTokenDecimals = 8 + const bigTen = new Big(10) + const userInputAmount = formatUnits(userInputTokenFunding, userInputToken.decimals) + const userInputTokenAmount = new Big(userInputAmount).round(cTokenDecimals, 0) + const underlyingDecimals = Number(userInputToken.decimals) + const exchangeRate = new Big(this.exchangeRate) + const mantissa = 18 + underlyingDecimals - cTokenDecimals + const divisor = bigTen.pow(mantissa) + if (exchangeRate.eq(0)) { + return new BigNumber('0') + } + const oneUnderlyingInCToken = divisor.div(exchangeRate) + const amountCTokens = userInputTokenAmount.times(oneUnderlyingInCToken) + let amountCTokensBoundToPrecision = roundNumberStringToSignificantDigits(amountCTokens.toString(), 4) + try { + const amountCTokenBigNumber = parseUnits(amountCTokensBoundToPrecision, cTokenDecimals) + return amountCTokenBigNumber + } catch (e) { + const amountCTokenNumber = new Big(amountCTokensBoundToPrecision).mul(RoundingFactor) + amountCTokensBoundToPrecision = roundNumberStringToSignificantDigits(amountCTokenNumber.toString(), 4) + if (amountCTokensBoundToPrecision === '0') { + return new BigNumber('0') + } + let amountCTokenBigNumber = parseUnits(amountCTokensBoundToPrecision.toString(), cTokenDecimals) + amountCTokenBigNumber = amountCTokenBigNumber.div(RoundingFactor) + return amountCTokenBigNumber + } + } + + calculateExchangeRate = async (): Promise => { + const exchangeRate = Number(await this.contract.functions.exchangeRateStored()) + return exchangeRate + } + + static encodeMintTokens = (tokenSymbol: string, amountWei: string): string => { + if (tokenSymbol.toLowerCase() === 'ceth') { + const tokenABI = CompoundService.getABI(tokenSymbol) + const mintInterface = new utils.Interface(tokenABI) + return mintInterface.functions.mint.encode([]) + } else { + const tokenABI = CompoundService.getABI(tokenSymbol) + const mintInterface = new utils.Interface(tokenABI) + return mintInterface.functions.mint.encode([amountWei]) + } + } + + static encodeRedeemTokens = (tokenSymbol: string, amountRedeem: string): string => { + const tokenABI = CompoundService.getABI(tokenSymbol) + const mintInterface = new utils.Interface(tokenABI) + return mintInterface.functions.redeem.encode([amountRedeem]) + } + + static encodeApproveUnlimited = (tokenSymbol: string, spenderAccount: string): string => { + const tokenABI = CompoundService.getABI(tokenSymbol) + const approveInterface = new utils.Interface(tokenABI) + return approveInterface.functions.approve.encode([spenderAccount, ethers.constants.MaxUint256]) + } + + static getABI = (symbol: string) => { + const symbolLowerCase = symbol.toLowerCase() + switch (symbolLowerCase) { + case 'cdai': + return cDaiAbi + case 'cwbtc': + return cWBTCAbi + case 'ceth': + return cETHAbi + case 'cbat': + return cBATAbi + case 'cuni': + return cUNIAbi + case 'cusdt': + return cUSDTAbi + case 'cusdc': + return cUSDCAbi + default: + return [] + } + } +} + +export { CompoundService } diff --git a/app/src/services/cpk.ts b/app/src/services/cpk.ts index 6766e9305f..e4f58c4e95 100644 --- a/app/src/services/cpk.ts +++ b/app/src/services/cpk.ts @@ -10,6 +10,8 @@ import { getLogger } from '../util/logger' import { getContractAddress, getTargetSafeImplementation, + getToken, + getTokenFromAddress, getWrapToken, pseudoNativeAssetAddress, waitForBlockToSync, @@ -17,6 +19,7 @@ import { import { calcDistributionHint, clampBigNumber, waitABit } from '../util/tools' import { MarketData, Question, Token } from '../util/types' +import { CompoundService } from './compound_service' import { ConditionalTokenService } from './conditional_token' import { ERC20Service } from './erc20' import { MarketMakerService } from './market_maker' @@ -30,39 +33,50 @@ const logger = getLogger('Services::CPKService') interface CPKBuyOutcomesParams { amount: BigNumber collateral: Token + compoundService?: CompoundService | null outcomeIndex: number + useBaseToken?: boolean marketMaker: MarketMakerService } interface CPKSellOutcomesParams { amount: BigNumber + compoundService?: CompoundService | null outcomeIndex: number marketMaker: MarketMakerService + useBaseToken?: boolean conditionalTokens: ConditionalTokenService } interface CPKCreateMarketParams { + compoundService?: CompoundService | null + compoundTokenDetails?: Token marketData: MarketData conditionalTokens: ConditionalTokenService realitio: RealitioService marketMakerFactory: MarketMakerFactoryService + useCompoundReserve?: boolean } interface CPKAddFundingParams { amount: BigNumber collateral: Token + compoundService?: CompoundService | null marketMaker: MarketMakerService + useBaseToken?: boolean } interface CPKRemoveFundingParams { amountToMerge: BigNumber collateralAddress: string + compoundService?: CompoundService | null conditionId: string conditionalTokens: ConditionalTokenService earnings: BigNumber marketMaker: MarketMakerService outcomesCount: number sharesToBurn: BigNumber + useBaseToken?: boolean } interface CPKRedeemParams { @@ -172,8 +186,10 @@ class CPKService { buyOutcomes = async ({ amount, collateral, + compoundService, marketMaker, outcomeIndex, + useBaseToken = false, }: CPKBuyOutcomesParams): Promise => { try { const signer = this.provider.getSigner() @@ -185,11 +201,17 @@ class CPKService { const txOptions: TxOptions = {} - if (this.cpk.isSafeApp() || collateral.address === pseudoNativeAssetAddress) { - txOptions.gas = 500000 - } - let collateralAddress + let collateralSymbol = '' + let userInputCollateralSymbol: KnownToken + let userInputCollateral: Token = collateral + let minCollateralAmount = amount + if (useBaseToken && compoundService != null) { + collateralSymbol = collateral.symbol.toLowerCase() + userInputCollateralSymbol = collateralSymbol.substring(1, collateralSymbol.length) as KnownToken + userInputCollateral = getToken(networkId, userInputCollateralSymbol) + minCollateralAmount = compoundService.calculateBaseToCTokenExchange(userInputCollateral, amount) + } if (collateral.address === pseudoNativeAssetAddress) { // ultimately WETH will be the collateral if we fund with native ether collateralAddress = getWrapToken(networkId).address @@ -198,30 +220,63 @@ class CPKService { if (!this.cpk.isSafeApp()) { txOptions.value = amount } - + if (this.cpk.isSafeApp()) { + txOptions.gas = 500000 + } // Step 0: Wrap ether transactions.push({ to: collateralAddress, value: amount, }) + } else if (useBaseToken) { + if (userInputCollateral.address === pseudoNativeAssetAddress) { + // If base token is ETH then we don't need to transfer to cpk + if (!this.cpk.isSafeApp()) { + txOptions.value = amount + } + if (this.cpk.isSafeApp()) { + txOptions.gas = 500000 + } + const encodedMintFunction = CompoundService.encodeMintTokens(collateralSymbol, amount.toString()) + transactions.push({ + to: collateral.address, + data: encodedMintFunction, + value: amount.toString(), + }) + } else { + // Transfer the base token to cpk + // Mint cTokens in the cpk + transactions.push({ + to: userInputCollateral.address, + data: ERC20Service.encodeTransferFrom(account, this.cpk.address, amount), + }) + const encodedMintFunction = CompoundService.encodeMintTokens(collateralSymbol, amount.toString()) + // Approve cToken for the cpk contract + transactions.push({ + to: userInputCollateral.address, + data: ERC20Service.encodeApproveUnlimited(collateral.address), + }) + // Mint ctokens from the underlying token + transactions.push({ + to: collateral.address, + data: encodedMintFunction, + }) + } + collateralAddress = await marketMaker.getCollateralToken() } else { collateralAddress = await marketMaker.getCollateralToken() } - const marketMakerAddress = marketMaker.address - const collateralService = new ERC20Service(this.provider, account, collateralAddress) - logger.log(`CPK address: ${this.cpk.address}`) - - const outcomeTokensToBuy = await marketMaker.calcBuyAmount(amount, outcomeIndex) + const outcomeTokensToBuy = await marketMaker.calcBuyAmount(minCollateralAmount, outcomeIndex) logger.log(`Min outcome tokens to buy: ${outcomeTokensToBuy}`) // Check if the allowance of the CPK to the market maker is enough. const hasCPKEnoughAlowance = await collateralService.hasEnoughAllowance( this.cpk.address, marketMakerAddress, - amount, + minCollateralAmount, ) if (!hasCPKEnoughAlowance) { @@ -235,17 +290,17 @@ class CPKService { // Step 2: Transfer the amount of collateral being spent from the user to the CPK // If we are funding with native ether we can skip this step // If we are signed in as a safe we don't need to transfer - if (!this.cpk.isSafeApp() && collateral.address !== pseudoNativeAssetAddress) { + if (!this.cpk.isSafeApp() && collateral.address !== pseudoNativeAssetAddress && !useBaseToken) { + // Step 2: Transfer the amount of collateral being spent from the user to the CPK transactions.push({ to: collateralAddress, data: ERC20Service.encodeTransferFrom(account, this.cpk.address, amount), }) } - // Step 3: Buy outcome tokens with the CPK transactions.push({ to: marketMakerAddress, - data: MarketMakerService.encodeBuy(amount, outcomeIndex, outcomeTokensToBuy), + data: MarketMakerService.encodeBuy(minCollateralAmount, outcomeIndex, outcomeTokensToBuy), }) const txObject = await this.cpk.execTransactions(transactions, txOptions) @@ -257,13 +312,25 @@ class CPKService { } createMarket = async ({ + compoundService, + compoundTokenDetails, conditionalTokens, marketData, marketMakerFactory, realitio, + useCompoundReserve, }: CPKCreateMarketParams): Promise => { try { - const { arbitrator, category, loadedQuestionId, outcomes, question, resolution, spread } = marketData + const { + arbitrator, + category, + loadedQuestionId, + outcomes, + question, + resolution, + spread, + userInputCollateral, + } = marketData if (!resolution) { throw new Error('Resolution time was not specified') @@ -292,7 +359,6 @@ class CPKService { if (marketData.collateral.address === pseudoNativeAssetAddress) { // ultimately WETH will be the collateral if we fund with native ether collateral = getWrapToken(networkId) - // we need to send the funding amount in native ether if (!this.cpk.isSafeApp()) { txOptions.value = marketData.funding @@ -303,6 +369,45 @@ class CPKService { to: collateral.address, value: marketData.funding, }) + } else if (useCompoundReserve && compoundTokenDetails) { + if (userInputCollateral.address === pseudoNativeAssetAddress) { + // If user chosen collateral is ETH + collateral = marketData.collateral + if (!this.cpk.isSafeApp()) { + txOptions.value = marketData.funding + } + const encodedMintFunction = CompoundService.encodeMintTokens( + compoundTokenDetails.symbol, + marketData.funding.toString(), + ) + transactions.push({ + to: collateral.address, + data: encodedMintFunction, + value: marketData.funding, + }) + } else { + collateral = marketData.collateral + // For any other compound pair that is not ETH + const encodedMintFunction = CompoundService.encodeMintTokens( + compoundTokenDetails.symbol, + marketData.funding.toString(), + ) + // Transfer user input collateral to cpk + transactions.push({ + to: userInputCollateral.address, + data: ERC20Service.encodeTransferFrom(account, this.cpk.address, marketData.funding), + }) + // Approve cToken for the cpk contract + transactions.push({ + to: userInputCollateral.address, + data: ERC20Service.encodeApproveUnlimited(collateral.address), + }) + // Mint ctokens from the underlying token + transactions.push({ + to: collateral.address, + data: encodedMintFunction, + }) + } } else { collateral = marketData.collateral } @@ -360,17 +465,23 @@ class CPKService { to: collateral.address, data: ERC20Service.encodeApproveUnlimited(marketMakerFactory.address), }) - + let minCollateralAmount = marketData.funding + if (useCompoundReserve && compoundService) { + minCollateralAmount = compoundService.calculateBaseToCTokenExchange(userInputCollateral, marketData.funding) + } // Step 4: Transfer funding from user // If we are funding with native ether we can skip this step // If we are signed in as a safe we don't need to transfer if (!this.cpk.isSafeApp() && marketData.collateral.address !== pseudoNativeAssetAddress) { - transactions.push({ - to: collateral.address, - data: ERC20Service.encodeTransferFrom(account, this.cpk.address, marketData.funding), - }) + // If we are using compound reserve then we don't need to transfer + // since we have already transferred the userinput collateral and minted cTokens + if (!useCompoundReserve) { + transactions.push({ + to: collateral.address, + data: ERC20Service.encodeTransferFrom(account, this.cpk.address, marketData.funding), + }) + } } - // Step 5: Create market maker const saltNonce = Math.round(Math.random() * 1000000) const predictedMarketMakerAddress = await marketMakerFactory.predictMarketMakerAddress( @@ -391,7 +502,7 @@ class CPKService { collateral.address, conditionId, spread, - marketData.funding, + minCollateralAmount, distributionHint, ), }) @@ -608,9 +719,11 @@ class CPKService { sellOutcomes = async ({ amount, + compoundService, conditionalTokens, marketMaker, outcomeIndex, + useBaseToken, }: CPKSellOutcomesParams): Promise => { try { const signer = this.provider.getSigner() @@ -642,14 +755,48 @@ class CPKService { to: marketMaker.address, data: MarketMakerService.encodeSell(amount, outcomeIndex, outcomeTokensToSell), }) - // If we are signed in as a safe we don't need to transfer if (!this.cpk.isSafeApp()) { - // Step 4: Transfer funding to user - transactions.push({ - to: collateralAddress, - data: ERC20Service.encodeTransfer(account, amount), - }) + if (useBaseToken && compoundService != null) { + const network = await this.provider.getNetwork() + const networkId = network.chainId + const collateralToken = getTokenFromAddress(networkId, collateralAddress) + const collateralSymbol = collateralToken.symbol.toLowerCase() + const userInputCollateralSymbol = collateralSymbol.substring(1, collateralSymbol.length) as KnownToken + const userInputCollateral = getToken(networkId, userInputCollateralSymbol) + const minCollateralAmount = compoundService.calculateCTokenToBaseExchange(userInputCollateral, amount) + // Convert cpk token to base token if user wants to redeem in base + const encodedRedeemFunction = CompoundService.encodeRedeemTokens(collateralSymbol, amount.toString()) + // Approve cToken for the cpk contract + transactions.push({ + to: userInputCollateral.address, + data: ERC20Service.encodeApproveUnlimited(collateralToken.address), + }) + // Redeem underlying token from the ctoken + transactions.push({ + to: collateralToken.address, + data: encodedRedeemFunction, + }) + // Transfer base token to the user + if (userInputCollateral.address === pseudoNativeAssetAddress) { + // If user wants to withdraw ether then simply transfer the amount + transactions.push({ + to: account, + value: minCollateralAmount.toString(), + }) + } else { + transactions.push({ + to: userInputCollateral.address, + data: ERC20Service.encodeTransfer(account, minCollateralAmount), + }) + } + } else { + // Step 4: Transfer funding to user + transactions.push({ + to: collateralAddress, + data: ERC20Service.encodeTransfer(account, amount), + }) + } } const txObject = await this.cpk.execTransactions(transactions, txOptions) @@ -660,7 +807,13 @@ class CPKService { } } - addFunding = async ({ amount, collateral, marketMaker }: CPKAddFundingParams): Promise => { + addFunding = async ({ + amount, + collateral, + compoundService, + marketMaker, + useBaseToken, + }: CPKAddFundingParams): Promise => { try { const signer = this.provider.getSigner() const account = await signer.getAddress() @@ -672,6 +825,10 @@ class CPKService { const txOptions: TxOptions = {} + let collateralSymbol = '' + let userInputCollateralSymbol: KnownToken + let userInputCollateral: Token = collateral + if (this.cpk.isSafeApp() || collateral.address === pseudoNativeAssetAddress) { txOptions.gas = 500000 } @@ -694,16 +851,13 @@ class CPKService { } else { collateralAddress = collateral.address } - - // Check if the allowance of the CPK to the market maker is enough. const collateralService = new ERC20Service(this.provider, account, collateralAddress) - + // Check if the allowance of the CPK to the market maker is enough. const hasCPKEnoughAlowance = await collateralService.hasEnoughAllowance( this.cpk.address, marketMaker.address, amount, ) - if (!hasCPKEnoughAlowance) { // Step 1: Approve unlimited amount to be transferred to the market maker transactions.push({ @@ -711,21 +865,59 @@ class CPKService { data: ERC20Service.encodeApproveUnlimited(marketMaker.address), }) } - - // Step 2: Transfer funding from user - // If we are funding with native ether we can skip this step + let minCollateralAmount = amount + if (useBaseToken && compoundService != null) { + collateralSymbol = collateral.symbol.toLowerCase() + userInputCollateralSymbol = collateralSymbol.substring(1, collateralSymbol.length) as KnownToken + userInputCollateral = getToken(networkId, userInputCollateralSymbol) + minCollateralAmount = compoundService.calculateBaseToCTokenExchange(userInputCollateral, amount) + } // If we are signed in as a safe we don't need to transfer if (!this.cpk.isSafeApp() && collateral.address !== pseudoNativeAssetAddress) { - transactions.push({ - to: collateral.address, - data: ERC20Service.encodeTransferFrom(account, this.cpk.address, amount), - }) + // Step 4: Transfer funding from user + if (useBaseToken) { + // If use base token then transfer the base token amount from the user + if (collateral.address !== pseudoNativeAssetAddress) { + transactions.push({ + to: userInputCollateral.address, + data: ERC20Service.encodeTransferFrom(account, this.cpk.address, amount), + }) + } + } else { + // If use collateral token then transfer the collateral token amount from the user + transactions.push({ + to: collateral.address, + data: ERC20Service.encodeTransferFrom(account, this.cpk.address, minCollateralAmount), + }) + } + } + if (useBaseToken) { + // get base token + const encodedMintFunction = CompoundService.encodeMintTokens(collateralSymbol, amount.toString()) + // Approve cToken for the cpk contract + if (userInputCollateral.address === pseudoNativeAssetAddress) { + txOptions.value = amount + transactions.push({ + to: collateral.address, + data: encodedMintFunction, + value: amount, + }) + } else { + transactions.push({ + to: userInputCollateral.address, + data: ERC20Service.encodeApproveUnlimited(collateral.address), + }) + // Mint ctokens from the underlying token + transactions.push({ + to: collateral.address, + data: encodedMintFunction, + }) + } } - // Step 3: Add funding to market transactions.push({ to: marketMaker.address, - data: MarketMakerService.encodeAddFunding(amount), + data: MarketMakerService.encodeAddFunding(minCollateralAmount), }) const txObject = await this.cpk.execTransactions(transactions, txOptions) @@ -739,17 +931,19 @@ class CPKService { removeFunding = async ({ amountToMerge, collateralAddress, + compoundService, conditionId, conditionalTokens, earnings, marketMaker, outcomesCount, sharesToBurn, + useBaseToken, }: CPKRemoveFundingParams): Promise => { try { const signer = this.provider.getSigner() const account = await signer.getAddress() - + const transactions = [] const removeFundingTx = { to: marketMaker.address, data: MarketMakerService.encodeRemoveFunding(sharesToBurn), @@ -764,22 +958,64 @@ class CPKService { amountToMerge, ), } - - const transactions = [removeFundingTx, mergePositionsTx] + transactions.push(removeFundingTx) + transactions.push(mergePositionsTx) const txOptions: TxOptions = {} if (this.cpk.isSafeApp()) { txOptions.gas = 500000 } - + const network = await this.provider.getNetwork() + const networkId = network.chainId + const collateralToken = getTokenFromAddress(networkId, collateralAddress) + const collateralSymbol = collateralToken.symbol.toLowerCase() + let userInputCollateral = collateralToken + const totalAmountEarned = amountToMerge.add(earnings) + // transfer to the user the merged collateral plus the earned fees + if (useBaseToken && compoundService != null) { + const userInputCollateralSymbol = collateralSymbol.substring(1, collateralSymbol.length) as KnownToken + userInputCollateral = getToken(networkId, userInputCollateralSymbol) + // Convert cpk token to base token if user wants to redeem in base + const encodedRedeemFunction = CompoundService.encodeRedeemTokens(collateralSymbol, totalAmountEarned.toString()) + // Approve cToken for the cpk contract + transactions.push({ + to: userInputCollateral.address, + data: ERC20Service.encodeApproveUnlimited(collateralToken.address), + }) + // redeeem underlying token from the ctoken token + transactions.push({ + to: collateralToken.address, + data: encodedRedeemFunction, + }) + } // If we are signed in as a safe we don't need to transfer if (!this.cpk.isSafeApp()) { // transfer to the user the merged collateral plus the earned fees - transactions.push({ - to: collateralAddress, - data: ERC20Service.encodeTransfer(account, amountToMerge.add(earnings)), - }) + if (useBaseToken && compoundService != null) { + const minCollateralAmount = compoundService.calculateCTokenToBaseExchange( + userInputCollateral, + totalAmountEarned, + ) + if (userInputCollateral.address === pseudoNativeAssetAddress) { + // If user wants to redeem in ether simply transfer the funds back to user + transactions.push({ + to: account, + value: minCollateralAmount.toString(), + }) + } else { + // Transfer base token to the user + transactions.push({ + to: userInputCollateral.address, + data: ERC20Service.encodeTransfer(account, minCollateralAmount), + }) + } + } else { + transactions.push({ + to: collateralAddress, + data: ERC20Service.encodeTransfer(account, totalAmountEarned), + }) + } } const txObject = await this.cpk.execTransactions(transactions, txOptions) diff --git a/app/src/services/index.ts b/app/src/services/index.ts index 0ed6543fd1..c40b224034 100644 --- a/app/src/services/index.ts +++ b/app/src/services/index.ts @@ -1,3 +1,4 @@ +export { CompoundService } from './compound_service' export { ConditionalTokenService } from './conditional_token' export { RealitioService } from './realitio' export { ERC20Service } from './erc20' diff --git a/app/src/theme/index.js b/app/src/theme/index.js index d083225431..bb2e589bec 100644 --- a/app/src/theme/index.js +++ b/app/src/theme/index.js @@ -101,6 +101,7 @@ const theme = { textColor: '#757575', textColorDark: '#37474F', textColorDarker: '#333', + compound: '#00897B', textColorLight: '#999', textColorLighter: '#86909E', textColorLightish: '#7D8189', diff --git a/app/src/util/networks.ts b/app/src/util/networks.ts index 429f9c1fec..47a34831d5 100644 --- a/app/src/util/networks.ts +++ b/app/src/util/networks.ts @@ -260,13 +260,66 @@ export const getInfuraUrl = (networkId: number): string => { export const knownTokens: { [name in KnownToken]: KnownTokenData } = { cdai: { symbol: 'cDAI', - decimals: 18, + decimals: 8, addresses: { [networkIds.MAINNET]: '0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643', - [networkIds.RINKEBY]: '0x7a978b38d5af06ff929ca06647e025b759479318', + [networkIds.RINKEBY]: '0x6d7f0754ffeb405d23c51ce938289d4835be3b14', }, order: 2, }, + cbat: { + symbol: 'cBAT', + decimals: 8, + addresses: { + [networkIds.MAINNET]: '0x6c8c6b02e7b2be14d4fa6022dfd6d75921d90e4e', + [networkIds.RINKEBY]: '0xebf1a11532b93a529b5bc942b4baa98647913002', + }, + order: 10, + }, + ceth: { + symbol: 'cETH', + decimals: 8, + addresses: { + [networkIds.MAINNET]: '0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5', + [networkIds.RINKEBY]: '0xd6801a1dffcd0a410336ef88def4320d6df1883e', + }, + order: 11, + }, + cusdc: { + symbol: 'cUSDC', + decimals: 8, + addresses: { + [networkIds.MAINNET]: '0x39aa39c021dfbae8fac545936693ac917d5e7563', + [networkIds.RINKEBY]: '0x5b281a6dda0b271e91ae35de655ad301c976edb1', + }, + order: 12, + }, + cusdt: { + symbol: 'cUSDT', + decimals: 8, + addresses: { + [networkIds.MAINNET]: '0xf650c3d88d12db855b8bf7d11be6c55a4e07dcc9', + [networkIds.RINKEBY]: '0x2fb298bdbef468638ad6653ff8376575ea41e768', + }, + order: 13, + }, + cwbtc: { + symbol: 'cWBTC', + decimals: 8, + addresses: { + [networkIds.MAINNET]: '0xc11b1268c1a384e55c48c2391d8d480264a3a7f4', + [networkIds.RINKEBY]: '0x0014f450b8ae7708593f4a46f8fa6e5d50620f96', + }, + order: 14, + }, + cuni: { + symbol: 'cUNI', + decimals: 8, + addresses: { + [networkIds.MAINNET]: '0x35a18000230da775cac24873d00ff85bccded550', + }, + order: 15, + }, dai: { symbol: 'DAI', decimals: 18, @@ -352,6 +405,15 @@ export const knownTokens: { [name in KnownToken]: KnownTokenData } = { }, order: 9, }, + eth: { + symbol: 'ETH', + decimals: 18, + addresses: { + [networkIds.MAINNET]: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + [networkIds.RINKEBY]: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + }, + order: 18, + }, } const validNetworkId = (networkId: number): networkId is NetworkId => { diff --git a/app/src/util/tools.ts b/app/src/util/tools.ts index 35568d2e84..d3240d8252 100644 --- a/app/src/util/tools.ts +++ b/app/src/util/tools.ts @@ -9,8 +9,10 @@ import { REALITIO_SCALAR_ADAPTER_ADDRESS_SOKOL, REALITIO_SCALAR_ADAPTER_ADDRESS_XDAI, } from '../common/constants' +import { CompoundService } from '../services/compound_service' import { getLogger } from './logger' +import { BalanceItem, CompoundEnabledTokenType, Token } from './types' const logger = getLogger('Tools') @@ -228,6 +230,86 @@ export const calcPoolTokens = ( } } +/** + * Round a given number string to a fixed number of significant digits + */ +export const roundNumberStringToSignificantDigits = (value: string, sd: number): string => { + const r = new Big(value) + const preciseValue = (r as any).prec(sd, 0) + if (preciseValue.gt(0)) { + return preciseValue.toString() + } else { + return '0' + } +} + +/** + * Gets the corresponding cToken for a given token symbol. + * Empty string if corresponding cToken doesn't exist + */ +export const getCTokenForToken = (token: string): string => { + const tokenSymbol = token.toLowerCase() + if (tokenSymbol in CompoundEnabledTokenType) { + if (tokenSymbol === 'eth' || tokenSymbol === 'weth') { + return 'ceth' + } else { + return `c${tokenSymbol}` + } + } else { + return '' + } +} + +/** + * Gets base token symbol for a given ctoken + */ +export const getBaseTokenForCToken = (token: string): string => { + const tokenSymbol = token.toLowerCase() + if (tokenSymbol.startsWith('c')) { + return tokenSymbol.substring(1, tokenSymbol.length) + } + return '' +} +/** + * Calculates balances in base token for a given c token + */ +export const getBalancesInBaseToken = ( + balances: BalanceItem[], + compoundService: CompoundService, + displayCollateral: Token, +): BalanceItem[] => { + const displayBalances = balances.map(function(bal) { + const cTokenPrecision = 8 + const cTokenWithPrecision = roundNumberStringToSignificantDigits(bal.currentPrice.toString(), 4) + let basePrice = '0' + try { + const cTokenPriceAmount = parseUnits(cTokenWithPrecision, cTokenPrecision) + const baseTokenPrice = compoundService.calculateCTokenToBaseExchange(displayCollateral, cTokenPriceAmount) + basePrice = formatBigNumber(baseTokenPrice, displayCollateral.decimals) + } catch (e) { + basePrice = '0' + } + return Object.assign({}, bal, { + currentPrice: basePrice, + }) + }) + return displayBalances +} + +export const getSharesInBaseToken = ( + balances: BalanceItem[], + compoundService: CompoundService, + displayCollateral: Token, +): BalanceItem[] => { + const displayBalances = balances.map(function(bal) { + const baseTokenPrice = compoundService.calculateCTokenToBaseExchange(displayCollateral, bal.shares) + return Object.assign({}, bal, { + shares: baseTokenPrice, + }) + }) + return displayBalances +} + /** * Compute the number of outcomes that will be sent to the user by the Market Maker * after funding it for the first time with `addedFunds` of collateral. diff --git a/app/src/util/types.ts b/app/src/util/types.ts index 88cc4418bb..145ebeae73 100644 --- a/app/src/util/types.ts +++ b/app/src/util/types.ts @@ -195,8 +195,11 @@ export enum Wallet { export interface MarketData { collateral: Token + userInputCollateral: Token + userInputToken: Token arbitratorsCustom: Arbitrator[] categoriesCustom: string[] + compoundInterestRate: string question: string category: string resolution: Date | null @@ -205,6 +208,7 @@ export interface MarketData { funding: BigNumber outcomes: Outcome[] loadedQuestionId: Maybe + useCompoundReserve: boolean verifiedLabel?: string lowerBound: Maybe upperBound: Maybe @@ -284,6 +288,7 @@ export interface MarketMakerData { balances: BalanceItem[] creationTimestamp: string collateral: Token + userInputCollateral: Token | null fee: BigNumber isConditionResolved: boolean isQuestionFinalized: boolean @@ -446,6 +451,24 @@ export enum MarketState { none = '', } +export enum CompoundTokenType { + cdai = 'cdai', + cusdc = 'cusdc', + cusdt = 'cusdt', + cuni = 'cuni', + cbat = 'cbat', + ceth = 'ceth', +} + +export enum CompoundEnabledTokenType { + dai = 'dai', + usdc = 'usdc', + usdt = 'usdt', + uni = 'uni', + bat = 'bat', + eth = 'eth', +} + export const INVALID_ANSWER_ID = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' export enum FormState { diff --git a/app/yarn.lock b/app/yarn.lock index 64f2d5e7ba..29a90aeb48 100644 --- a/app/yarn.lock +++ b/app/yarn.lock @@ -6044,6 +6044,11 @@ big.js@^5.1.2, big.js@^5.2.2: resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== +big.js@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-6.0.3.tgz#8b4d99ac7023668e0e465d3f78c23b8ac29ad381" + integrity sha512-n6yn1FyVL1EW2DBAr4jlU/kObhRzmr+NNRESl65VIOT8WBJj/Kezpx2zFdhJUqYI6qrtTW7moCStYL5VxeVdPA== + bignumber.js@*, bignumber.js@9.0.0, bignumber.js@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.0.tgz#805880f84a329b5eac6e7cb6f8274b6d82bdf075" diff --git a/omen-exchange b/omen-exchange new file mode 160000 index 0000000000..3671ab6a5c --- /dev/null +++ b/omen-exchange @@ -0,0 +1 @@ +Subproject commit 3671ab6a5c2ab05016df84ed2f8297101b8c9e02