diff --git a/.gitignore b/.gitignore index 98542e40..a6bccda9 100755 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,6 @@ /.nyc_output /reports - /src/static/contracts/compiled/*.ts /src/static/compiled/*.json @@ -35,10 +34,13 @@ cypress/screenshots cypress/videos cypress.env.json +tsconfig.tsbuildinfo + # vercel .now .vercel .env +.env.local .eslintcache diff --git a/README.md b/README.md index 266bd655..aab7aea1 100755 --- a/README.md +++ b/README.md @@ -6,7 +6,13 @@ Rari Capital's Web3 Portal. - Run `npm run dev` to start the development server. - Run `npm run build` to build a production bundle -- Run `npm run bump-rari-components` to automatically install the latest version (i.e. most recent commit hash) of rari-components and update `package.json`. +- Run `npm run bump-rari-components` to automatically install the latest version (i.e. most recent commit hash) of [rari-components](https://github.com/Rari-Capital/rari-components) and update `package.json`. +- Run `npm run typecheck` to typecheck + +## Environment + +- Set `NEXT_PUBLIC_USE_MOCKS` to `true` to test the dapp with mock objects (e.g. `NEXT_PUBLIC_USE_MOCKS=true npm run dev`). + - Currently, this env var is only used in the Turbo code — Ctrl+F for `process.env.NEXT_PUBLIC_USE_MOCKS` to see the specific use sites. ## Requirements diff --git a/next.config.js b/next.config.js index 21679762..045944ab 100644 --- a/next.config.js +++ b/next.config.js @@ -24,11 +24,11 @@ module.exports = withBundleAnalyzer({ config.module.rules.push({ test: /\.tsx?/, - // Transpile rari-components, even though it is in node_modules - include: [/node_modules\/rari-components/], + // Transpile rari-components + include: [/rari-components/], use: "next-swc-loader", }); return config; }, -}); +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1ca38336..bcd2f141 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@0xsequence/multicall": "^0.35.5", "@aave/protocol-v2": "^1.0.1", "@chakra-ui/icons": "^1.0.0", - "@chakra-ui/react": "^1.8.1", + "@chakra-ui/react": "1.8.1", "@emotion/react": "^11.0.0", "@emotion/styled": "^11.0.0", "@walletconnect/web3-provider": "^1.4.1", @@ -41,7 +41,7 @@ "node-fetch": "^2.6.1", "node-vibrant": "^3.1.6", "polished": "^4.1.3", - "rari-components": "github:Rari-Capital/rari-components#1131c95058f26b36048d9cfe507abb15ae5adaa8", + "rari-components": "github:Rari-Capital/rari-components#144451807e8673b4586274adfb8e78346ab32fce", "rari-tokens-generator": "^2.0.0", "react": "^17.0.2", "react-apexcharts": "1.3.7", @@ -51,6 +51,7 @@ "react-error-boundary": "^3.0.2", "react-fast-marquee": "^1.1.3", "react-infinite-scroll-hook": "^4.0.1", + "react-intersection-observer": "^8.34.0", "react-jazzicon": "^0.1.3", "react-query": "^3.13.10", "react-responsive-carousel": "^3.2.18", @@ -15788,8 +15789,8 @@ }, "node_modules/rari-components": { "version": "0.0.1", - "resolved": "git+https://git@github.com/Rari-Capital/rari-components.git#1131c95058f26b36048d9cfe507abb15ae5adaa8", - "integrity": "sha512-7i2PwHSGEiRR56oIYF0jsGhSG+Io36C9p32g56KVM0Qzb+bxtoSSXTBJ+otLr1Y8Rs9Dd42Yp7lY3zxX8NBVVQ==", + "resolved": "git+ssh://git@github.com/Rari-Capital/rari-components.git#144451807e8673b4586274adfb8e78346ab32fce", + "integrity": "sha512-on74amrYNTfdogsVUr55D9xicaOH/lGXkR6jRn6GPHxcsqwo6OZ8ix2AkPY5hbQdn6DW7q08UU1QTKOO8x7KFw==", "dependencies": { "lodash": "^4.17.21" }, @@ -16030,9 +16031,9 @@ } }, "node_modules/react-intersection-observer": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-8.33.1.tgz", - "integrity": "sha512-3v+qaJvp3D1MlGHyM+KISVg/CMhPiOlO6FgPHcluqHkx4YFCLuyXNlQ/LE6UkbODXlQcLOppfX6UMxCEkUhDLw==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-8.34.0.tgz", + "integrity": "sha512-TYKh52Zc0Uptp5/b4N91XydfSGKubEhgZRtcg1rhTKABXijc4Sdr1uTp5lJ8TN27jwUsdXxjHXtHa0kPj704sw==", "peerDependencies": { "react": "^15.0.0 || ^16.0.0 || ^17.0.0|| ^18.0.0" } @@ -32595,9 +32596,9 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "rari-components": { - "version": "git+https://git@github.com/Rari-Capital/rari-components.git#1131c95058f26b36048d9cfe507abb15ae5adaa8", - "integrity": "sha512-7i2PwHSGEiRR56oIYF0jsGhSG+Io36C9p32g56KVM0Qzb+bxtoSSXTBJ+otLr1Y8Rs9Dd42Yp7lY3zxX8NBVVQ==", - "from": "rari-components@git+https://github.com/Rari-Capital/rari-components#1131c95058f26b36048d9cfe507abb15ae5adaa8", + "version": "git+ssh://git@github.com/Rari-Capital/rari-components.git#144451807e8673b4586274adfb8e78346ab32fce", + "integrity": "sha512-on74amrYNTfdogsVUr55D9xicaOH/lGXkR6jRn6GPHxcsqwo6OZ8ix2AkPY5hbQdn6DW7q08UU1QTKOO8x7KFw==", + "from": "rari-components@git+https://github.com/Rari-Capital/rari-components#144451807e8673b4586274adfb8e78346ab32fce", "requires": { "lodash": "^4.17.21" } @@ -32767,9 +32768,9 @@ } }, "react-intersection-observer": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-8.33.1.tgz", - "integrity": "sha512-3v+qaJvp3D1MlGHyM+KISVg/CMhPiOlO6FgPHcluqHkx4YFCLuyXNlQ/LE6UkbODXlQcLOppfX6UMxCEkUhDLw==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-8.34.0.tgz", + "integrity": "sha512-TYKh52Zc0Uptp5/b4N91XydfSGKubEhgZRtcg1rhTKABXijc4Sdr1uTp5lJ8TN27jwUsdXxjHXtHa0kPj704sw==", "requires": {} }, "react-intersection-observer-hook": { diff --git a/package.json b/package.json index a1dc3a04..ac81e2f6 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "@0xsequence/multicall": "^0.35.5", "@aave/protocol-v2": "^1.0.1", "@chakra-ui/icons": "^1.0.0", - "@chakra-ui/react": "^1.8.1", + "@chakra-ui/react": "1.8.1", "@emotion/react": "^11.0.0", "@emotion/styled": "^11.0.0", "@walletconnect/web3-provider": "^1.4.1", @@ -35,7 +35,7 @@ "node-fetch": "^2.6.1", "node-vibrant": "^3.1.6", "polished": "^4.1.3", - "rari-components": "github:Rari-Capital/rari-components#1131c95058f26b36048d9cfe507abb15ae5adaa8", + "rari-components": "github:Rari-Capital/rari-components#144451807e8673b4586274adfb8e78346ab32fce", "rari-tokens-generator": "^2.0.0", "react": "^17.0.2", "react-apexcharts": "1.3.7", @@ -45,6 +45,7 @@ "react-error-boundary": "^3.0.2", "react-fast-marquee": "^1.1.3", "react-infinite-scroll-hook": "^4.0.1", + "react-intersection-observer": "^8.34.0", "react-jazzicon": "^0.1.3", "react-query": "^3.13.10", "react-responsive-carousel": "^3.2.18", @@ -61,7 +62,7 @@ }, "scripts": { "bump-rari-components": "rm -rf .next && npm i \"git+https://github.com/Rari-Capital/rari-components#$(git ls-remote https://github.com/Rari-Capital/rari-components.git HEAD | awk '{ print $1}')\"", - "typecheck": "tsc --skipLibCheck -p ./tsconfig.json", + "typecheck": "tsc --noEmit", "postinstall": "rari-tokens-generator ./src/static/compiled/tokens.json", "start": "next start", "build": "next build", diff --git a/public/static/turbo/action-icons/claim-interest.png b/public/static/turbo/action-icons/claim-interest.png new file mode 100644 index 00000000..6e167ca7 Binary files /dev/null and b/public/static/turbo/action-icons/claim-interest.png differ diff --git a/public/static/turbo/action-icons/deposit-collateral.png b/public/static/turbo/action-icons/deposit-collateral.png new file mode 100644 index 00000000..7fd070e7 Binary files /dev/null and b/public/static/turbo/action-icons/deposit-collateral.png differ diff --git a/public/static/turbo/action-icons/withdraw-collateral.png b/public/static/turbo/action-icons/withdraw-collateral.png new file mode 100644 index 00000000..87d8b889 Binary files /dev/null and b/public/static/turbo/action-icons/withdraw-collateral.png differ diff --git a/public/static/turbo/isolated-actions.png b/public/static/turbo/isolated-actions.png new file mode 100644 index 00000000..431c366d Binary files /dev/null and b/public/static/turbo/isolated-actions.png differ diff --git a/public/static/turbo/noseke.png b/public/static/turbo/noseke.png new file mode 100644 index 00000000..ff0ddc02 Binary files /dev/null and b/public/static/turbo/noseke.png differ diff --git a/public/static/turbo/one-collateral-type.png b/public/static/turbo/one-collateral-type.png new file mode 100644 index 00000000..4cdd3889 Binary files /dev/null and b/public/static/turbo/one-collateral-type.png differ diff --git a/public/static/turbo/treasury-assets.png b/public/static/turbo/treasury-assets.png new file mode 100644 index 00000000..ca095ea5 Binary files /dev/null and b/public/static/turbo/treasury-assets.png differ diff --git a/public/static/turbo/turbo-engine-3d-trimmed.png b/public/static/turbo/turbo-engine-3d-trimmed.png new file mode 100644 index 00000000..eeb855bd Binary files /dev/null and b/public/static/turbo/turbo-engine-3d-trimmed.png differ diff --git a/public/static/turbo/turbo-engine-3d.png b/public/static/turbo/turbo-engine-3d.png new file mode 100644 index 00000000..8a6cbfa1 Binary files /dev/null and b/public/static/turbo/turbo-engine-3d.png differ diff --git a/public/static/turbo/turbo-engine-green.svg b/public/static/turbo/turbo-engine-green.svg new file mode 100644 index 00000000..05da9109 --- /dev/null +++ b/public/static/turbo/turbo-engine-green.svg @@ -0,0 +1,22 @@ + + + + + + \ No newline at end of file diff --git a/public/static/turbo/turbo-engine-red.svg b/public/static/turbo/turbo-engine-red.svg new file mode 100644 index 00000000..d80de2ad --- /dev/null +++ b/public/static/turbo/turbo-engine-red.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/static/turbo/turbo-no-bg.png b/public/static/turbo/turbo-no-bg.png new file mode 100644 index 00000000..2bda7f39 Binary files /dev/null and b/public/static/turbo/turbo-no-bg.png differ diff --git a/public/static/turbo/turbo.png b/public/static/turbo/turbo.png new file mode 100644 index 00000000..cec294a2 Binary files /dev/null and b/public/static/turbo/turbo.png differ diff --git a/public/static/turbo/turbo2.png b/public/static/turbo/turbo2.png new file mode 100644 index 00000000..f6dee3e3 Binary files /dev/null and b/public/static/turbo/turbo2.png differ diff --git a/public/static/turbo/user-icons/daos.png b/public/static/turbo/user-icons/daos.png new file mode 100644 index 00000000..c4c8cd5f Binary files /dev/null and b/public/static/turbo/user-icons/daos.png differ diff --git a/public/static/turbo/user-icons/individuals.png b/public/static/turbo/user-icons/individuals.png new file mode 100644 index 00000000..52e8fa79 Binary files /dev/null and b/public/static/turbo/user-icons/individuals.png differ diff --git a/public/static/turbo/user-icons/more.png b/public/static/turbo/user-icons/more.png new file mode 100644 index 00000000..d115cb5e Binary files /dev/null and b/public/static/turbo/user-icons/more.png differ diff --git a/public/static/turbo/user-icons/protocols.png b/public/static/turbo/user-icons/protocols.png new file mode 100644 index 00000000..6d528ec2 Binary files /dev/null and b/public/static/turbo/user-icons/protocols.png differ diff --git a/src/components/pages/Fuse/FusePoolsPage/PoolRow.tsx b/src/components/pages/Fuse/FusePoolsPage/PoolRow.tsx index 0e74cb6d..19a378cd 100644 --- a/src/components/pages/Fuse/FusePoolsPage/PoolRow.tsx +++ b/src/components/pages/Fuse/FusePoolsPage/PoolRow.tsx @@ -115,12 +115,12 @@ export const PoolRow = ({
{poolNumber}
-
- {smallUsdFormatter(tvl)} -
-
- {smallUsdFormatter(borrowed)} -
+
+ {smallUsdFormatter(tvl)} +
+
+ {smallUsdFormatter(borrowed)} +
)} diff --git a/src/components/pages/Fuse/Modals/PluginModal/PluginRewardsModal.tsx b/src/components/pages/Fuse/Modals/PluginModal/PluginRewardsModal.tsx index 3911d78d..2d02965e 100644 --- a/src/components/pages/Fuse/Modals/PluginModal/PluginRewardsModal.tsx +++ b/src/components/pages/Fuse/Modals/PluginModal/PluginRewardsModal.tsx @@ -1,120 +1,150 @@ import { - Modal, - ModalOverlay, - ModalContent, - ModalHeader, - ModalFooter, - ModalBody, - ModalCloseButton, - Text, - Avatar, - Spinner, - Flex, - Link, - Button, - Heading, - VStack, - HStack, -} from "@chakra-ui/react" -import { TokenData } from "hooks/useTokenData" -import { USDPricedFuseAsset } from "utils/fetchFusePoolData" + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + ModalFooter, + ModalBody, + ModalCloseButton, + Text, + Avatar, + Spinner, + Flex, + Link, + Button, + Heading, + VStack, + HStack, +} from "@chakra-ui/react"; +import { TokenData } from "hooks/useTokenData"; +import { USDPricedFuseAsset } from "utils/fetchFusePoolData"; -import { InfoIcon } from "@chakra-ui/icons" -import AppLink from "components/shared/AppLink" -import { CTokenAvatarGroup } from "components/shared/Icons/CTokenIcon" -import { CONVEX_CTOKEN_INFO, eligibleTokens } from "constants/convex" +import { InfoIcon } from "@chakra-ui/icons"; +import AppLink from "components/shared/AppLink"; +import { CTokenAvatarGroup } from "components/shared/Icons/CTokenIcon"; +import { CONVEX_CTOKEN_INFO, eligibleTokens } from "constants/convex"; export const PluginRewardsModal = ({ - market, - isOpen, - onClose, - tokenData, - rewardTokens + market, + isOpen, + onClose, + tokenData, + rewardTokens, }: { - market: USDPricedFuseAsset, - isOpen: boolean, - onClose: () => void, - tokenData: TokenData | undefined, - rewardTokens: string[] + market: USDPricedFuseAsset; + isOpen: boolean; + onClose: () => void; + tokenData: TokenData | undefined; + rewardTokens: string[]; }) => { + const index = eligibleTokens.indexOf(market.underlyingSymbol); + const symbol = tokenData?.symbol ?? market.underlyingSymbol; - const index = eligibleTokens.indexOf(market.underlyingSymbol) - const symbol = tokenData?.symbol ?? market.underlyingSymbol + const pluginTokenInfo = CONVEX_CTOKEN_INFO?.[market.underlyingSymbol] ?? {}; - const pluginTokenInfo = CONVEX_CTOKEN_INFO?.[market.underlyingSymbol] ?? {} + return ( + <> + + + + + {tokenData ? ( + + ) : ( + + )} + + {" "} + 🔌 {symbol} + + + Rewards + + + + + {index === -1 ? null : ( + + {/* */} + + + + This market streams + {" "} + rewards from the {pluginTokenInfo?.convexPoolName}{" "} + Convex pool to suppliers of{" "} + {pluginTokenInfo?.curvePoolName} Curve LPs.{" "} + + {/* Deposit your {pluginTokenInfo?.curvePoolName} Curve LP tokens into Fuse to borrow against it while earning all the same rewards from Convex. */} + {/* View reward rates for {pluginTokenInfo?.convexPoolName} on Convex */} + + {/* */} - return ( - <> - - - - - {tokenData ? : } - 🔌 {symbol} - - Rewards - - - - - {index === -1 ? null : - - {/* */} - - - - This market streams rewards from the - - from the {pluginTokenInfo?.convexPoolName} Convex pool - to suppliers of {pluginTokenInfo?.curvePoolName} Curve LPs. - {/* Deposit your {pluginTokenInfo?.curvePoolName} Curve LP tokens into Fuse to borrow against it while earning all the same rewards from Convex. */} - {/* View reward rates for {pluginTokenInfo?.convexPoolName} on Convex */} - - {/* */} + + Info + + + + + + )} + + + + + + + + + + ); +}; - - Info - - - - - - - - } - - - - - - - - - - ) -} - -const InfoPairs = ({ - title, - address, - link +export const InfoPairs = ({ + title, + address, + link, }: { - title: string, - address: string, - link?: string, + title: string; + address: string; + link?: string; }) => { - return ( - - {title} - - {link ?? address} - - - ) -} - + return ( + + {title} + + + {link ?? address} + + + + ); +}; -export default PluginRewardsModal \ No newline at end of file +export default PluginRewardsModal; diff --git a/src/components/pages/Home/Home.tsx b/src/components/pages/Home/Home.tsx index daf69335..e1449abb 100644 --- a/src/components/pages/Home/Home.tsx +++ b/src/components/pages/Home/Home.tsx @@ -276,7 +276,7 @@ const Home = () => { - + Discover infinite possibilities across the Rari Capital Ecosystem diff --git a/src/components/pages/Turbo/TurboIndexPage/CreateSafeModal/CreateSafeModal.tsx b/src/components/pages/Turbo/TurboIndexPage/CreateSafeModal/CreateSafeModal.tsx new file mode 100644 index 00000000..f779b2ea --- /dev/null +++ b/src/components/pages/Turbo/TurboIndexPage/CreateSafeModal/CreateSafeModal.tsx @@ -0,0 +1,258 @@ +import { useRari } from "context/RariContext"; +import { formatEther, parseEther } from "ethers/lib/utils"; +import { useBalanceOf } from "hooks/useBalanceOf"; +import useHasApproval from "hooks/useHasApproval"; +import { createSafeAndDeposit } from "lib/turbo/transactions/createSafeAndDeposit"; +import { createSafe } from "lib/turbo/transactions/safe"; +import { TRIBE, TurboAddresses } from "lib/turbo/utils/constants"; +import { useRouter } from "next/router"; +import { Heading, Modal, Text } from "rari-components"; +import { useState } from "react"; +import { approve } from "utils/erc20Utils"; +import { handleGenericError } from "utils/errorHandling"; +import { Flex, useToast } from "@chakra-ui/react"; +import { CreateSafeCtx, MODAL_STEPS } from "./modalSteps"; +import { MAX_APPROVAL_AMOUNT } from "utils/tokenUtils"; +import { createTurboMaster } from "lib/turbo/utils/turboContracts"; +import { getRecentEventDecoded } from "lib/turbo/utils/decodeEvents"; +import { useQuery } from "react-query"; +import { getEthUsdPriceBN } from "esm/utils/getUSDPriceBN"; +import { getPriceFromOracles } from "hooks/rewards/useRewardAPY"; +import { BigNumber, constants } from "ethers"; +import { calculateMaxBoost } from "lib/turbo/fetchers/safes/getSafeInfo"; +import { getMarketCf } from "lib/turbo/utils/getMarketCF"; +import { USDPricedTurboSafe } from "lib/turbo/fetchers/safes/getUSDPricedSafeInfo"; +import useApprovedCollateral from "hooks/turbo/useApprovedCollateral"; + +type CreateSafeModalProps = Pick< + React.ComponentProps, + "isOpen" | "onClose" +>; + +export type SimulatedSafe = { + collateralUSD: BigNumber; + maxBoost: BigNumber; +}; + +export const CreateSafeModal: React.FC = ({ + isOpen, + onClose, +}) => { + // Rari and NextJs + const router = useRouter(); + const { address, provider, chainId, fuse, isAuthed } = useRari(); + const toast = useToast(); + + // Modal State + const [stepIndex, setStepIndex] = useState(0); + function incrementStepIndex() { + if (stepIndex + 1 !== MODAL_STEPS.length) { + setStepIndex(stepIndex + 1); + } + } + // Modal Buttons + const [approving, setApproving] = useState(false); + const [creatingSafe, setCreatingSafe] = useState(false); + const [navigating, setNavigating] = useState(false); + + // Safe's chosen underlying asset + const [createdSafe, setCreatedSafe] = useState(undefined); + + const underlyingTokenAddresses = useApprovedCollateral(); + const [underlyingTokenAddress, setUnderlyingTokenAddress] = useState(TRIBE); + const collateralBalance = useBalanceOf(address, underlyingTokenAddress); + + // Used only if user will deposit after creating. + const [depositAmount, setDepositAmount] = useState(""); + + // Router State + const hasApproval = useHasApproval( + underlyingTokenAddress, + TurboAddresses[chainId ?? 1]?.ROUTER, + depositAmount, + address + ); + + const { data: safeSimulation } = useQuery( + "Safe creation and deposit simulation for deposit amount:" + depositAmount, + async () => { + if (depositAmount === "0" || !depositAmount || !chainId) return; + + // 1. Get eth price and collateral price. + // @note - collateral price is denominated in ether. collateralPrice * ethPrice = collateralPriceToUSD + const ethUSDBN = (await getEthUsdPriceBN()).div(constants.WeiPerEther); + const collateralPriceBN = await getPriceFromOracles( + TRIBE, + TurboAddresses[1].COMPTROLLER, + fuse, + isAuthed + ); + + // Get collateral factor + const collateralFactor = await getMarketCf( + provider, + chainId, + underlyingTokenAddress + ); + + // Calculations + const amountBN = BigNumber.from(depositAmount); + const collateralUSD = collateralPriceBN.mul(ethUSDBN).mul(amountBN); + const maxBoost = calculateMaxBoost(collateralUSD, collateralFactor); + + const safe: SimulatedSafe = { + collateralUSD, + maxBoost, + }; + return safe; + } + ); + + // Modal Logic + const onClickCreateSafe = async () => { + if (!address || !provider || !chainId) return; + + setCreatingSafe(true); + + const amountBN = parseEther(depositAmount === "" ? "0" : depositAmount); + + let receipt; + if (!amountBN.isZero()) { + try { + const tx = await createSafe(underlyingTokenAddress, provider, 1); + + receipt = await tx.wait(2); + const turboMasterContract = createTurboMaster(provider, chainId); + const event = await getRecentEventDecoded( + turboMasterContract, + turboMasterContract.filters.TurboSafeCreated + ); + setCreatedSafe(event.safe); + incrementStepIndex(); + } catch (err) { + handleGenericError(err, toast); + console.log({ err }); + throw err; + } finally { + setCreatingSafe(false); + } + } else { + try { + const tx = await createSafe(underlyingTokenAddress, provider, chainId); + const receipt = await tx.wait(1); + const turboMasterContract = createTurboMaster(provider, chainId); + const event = await getRecentEventDecoded( + turboMasterContract, + turboMasterContract.filters.TurboSafeCreated + ); + setCreatedSafe(event.safe); + incrementStepIndex(); + } catch (err) { + handleGenericError(err, toast); + console.log({ err }); + throw err; + } finally { + setCreatingSafe(false); + } + } + + return receipt; + }; + + async function onClickApprove() { + if (!depositAmount || !chainId) return; + + setApproving(true); + + try { + await approve( + provider.getSigner(), + TurboAddresses[chainId].ROUTER, + underlyingTokenAddress, + MAX_APPROVAL_AMOUNT + ); + } finally { + setApproving(false); + } + } + + const balance = formatEther(collateralBalance); + + const onClickMax = async () => { + if (!chainId) return; + try { + setDepositAmount(balance.slice(0, balance.indexOf("."))); + } catch (err) { + handleGenericError(err, toast); + } + }; + + // Modal Context + const createSafeCtx: CreateSafeCtx = { + incrementStepIndex, + underlyingTokenAddresses, + underlyingTokenAddress, + setUnderlyingTokenAddress, + depositAmount, + setDepositAmount, + hasApproval, + approving, + onClickApprove, + onClickCreateSafe, + creatingSafe, + safeSimulation, + navigating, + collateralBalance: balance, + onClickMax, + createdSafe, + onClose() { + // Only allow close if a transaction isn't in progress. + if (!approving && !creatingSafe) { + setStepIndex(0); + onClose(); + } + }, + async navigateToCreatedSafe() { + if (!createdSafe) return; + setNavigating(true); + try { + await router.push(`/turbo/safe/${createdSafe}`); + } finally { + setNavigating(false); + } + }, + }; + + return ( + { + if (!approving && !creatingSafe) { + setStepIndex(0); + onClose(); + } + }} + progressValue={((stepIndex + 1) / MODAL_STEPS.length) * 100} + {...MODAL_STEPS[stepIndex]} + footerChildren={ + <> + Common questions: + + + What are safes? + + + What is boosting? + + + How do liquidations work? + + + + } + /> + ); +}; + +export default CreateSafeModal; diff --git a/src/components/pages/Turbo/TurboIndexPage/CreateSafeModal/index.ts b/src/components/pages/Turbo/TurboIndexPage/CreateSafeModal/index.ts new file mode 100644 index 00000000..049b9239 --- /dev/null +++ b/src/components/pages/Turbo/TurboIndexPage/CreateSafeModal/index.ts @@ -0,0 +1 @@ +export { default } from "./CreateSafeModal"; diff --git a/src/components/pages/Turbo/TurboIndexPage/CreateSafeModal/modalSteps.tsx b/src/components/pages/Turbo/TurboIndexPage/CreateSafeModal/modalSteps.tsx new file mode 100644 index 00000000..ba3a3998 --- /dev/null +++ b/src/components/pages/Turbo/TurboIndexPage/CreateSafeModal/modalSteps.tsx @@ -0,0 +1,448 @@ +import { BigNumber, constants, utils } from "ethers"; +import { commify, formatEther } from "ethers/lib/utils"; +import { + Card, + Heading, + HoverableCard, + ModalProps, + StatisticsTable, + Text, + TokenAmountInput, + TokenIcon, + TokenSymbol, +} from "rari-components"; +import { CheckCircleIcon, ChevronRightIcon } from "@chakra-ui/icons"; +import { + Box, + Flex, + HStack, + Image, + Spacer, + Spinner, + Stack, + VStack, +} from "@chakra-ui/react"; +import { SimulatedSafe } from "./CreateSafeModal"; + +type CreateSafeCtx = { + /** + * Function to increment the current step by 1 (i.e. go to the next step in + * the safe creation process). + */ + incrementStepIndex(): void; + /** List of addresses of possible underlying tokens. */ + underlyingTokenAddresses: string[]; + /** The address of the currently selected underlying token for the safe. */ + underlyingTokenAddress: string; + /** Function to set the address of the underlying token of the safe. */ + setUnderlyingTokenAddress(underlyingTokenAddress: string): void; + /** + * Amount to deposit into the safe. Stored as a string that we'll convert to + * a `BigNumber` later. + */ + depositAmount: string; + /** Set a new amount to deposit into the safe. */ + setDepositAmount(newDepositAmount: string): void; + /** + * Whether the currently selected `underlyingTokenAddress` has approved + * the router. + */ + hasApproval: boolean; + /** Function to approve the underlying token of the safe. */ + onClickApprove: () => Promise; + /** Whether the approval is currently pending. */ + approving: boolean; + /** Function which creates a safe. */ + onClickCreateSafe: () => Promise; + /** Whether the safe is currently being created. */ + creatingSafe: boolean; + collateralBalance: string; + safeSimulation: SimulatedSafe | undefined; + onClickMax(): Promise; + /** Function which closes the modal. */ + onClose(): void; + /** Whether the navigation to the created safe is pending. */ + navigating: boolean; + /** + * Function which navigates to the safe that was just created in this modal. + */ + navigateToCreatedSafe(): void; + createdSafe: string | undefined; +}; + +type ModalStep = Omit, "ctx" | "isOpen" | "onClose">; + +const MODAL_STEP_1: ModalStep = { + title: "Create a Safe", + subtitle: "Safes isolate manage and your collateral.", + children: ({ + underlyingTokenAddresses, + setUnderlyingTokenAddress, + incrementStepIndex, + }) => ( + + {!underlyingTokenAddresses.length && } + {underlyingTokenAddresses.map((tokenAddress) => ( + { + setUnderlyingTokenAddress(tokenAddress); + incrementStepIndex(); + }} + key={tokenAddress} + p={4} + > + {(hovered) => ( + + + + + + + + {" "} + Safe + + + + + Boost FEI against{" "} + {" "} + collateral + + + + + + + )} + + ))} + + ), +}; + +const MODAL_STEP_2: ModalStep = { + children: ({ underlyingTokenAddress }) => ( + + + + {" "} + You are creating a + + + + + Safe + + + + + + + + + + Collateral + + + Boost FEI against{" "} + {" "} + Collateral. + + + + + + + Isolated + + Safes isolate collateral, FEI boosting, and liquidations. + + + + + + + ), + buttons: ({ + hasApproval, + approving, + depositAmount, + creatingSafe, + onClickCreateSafe, + onClickApprove, + incrementStepIndex, + }) => [ + { + children: approving + ? "Approving..." + : creatingSafe + ? "Creating Safe..." + : !hasApproval && + BigNumber.from(!!depositAmount ? depositAmount : "0").gt(0) + ? "Approve Router" + : BigNumber.from(!!depositAmount ? depositAmount : "0").gt(0) + ? "Create Safe & Deposit" + : "Create Safe", + variant: "success", + loading: approving || creatingSafe, + async onClick() { + try { + if (!hasApproval) { + await onClickApprove(); + } + await onClickCreateSafe(); + } catch (err) { + throw err; + } + }, + }, + ], +}; + +// const MODAL_STEP_3: ModalStep = { +// title: "Deposit collateral", +// subtitle: +// "Collateralizing is required before boosting pools. This step is optional.", +// children: ({ +// underlyingTokenAddress, +// onClickMax, +// depositAmount, +// collateralBalance, +// setDepositAmount, +// safeSimulation, +// }) => ( +// +// +// +// +// +// Balance: {commify(collateralBalance)}{" "} +// +// +// +// +// +// +// ), +// buttons: ({ +// incrementStepIndex, +// setDepositAmount, +// depositAmount, +// collateralBalance, +// }) => [ +// { +// children: "Skip", +// variant: "cardmatte", +// onClick() { +// setDepositAmount("0"); +// incrementStepIndex(); +// }, +// }, +// { +// disabled: +// parseInt(depositAmount) > parseInt(collateralBalance) ? true : false, +// children: +// parseInt(depositAmount) > parseInt(collateralBalance) +// ? "Invalid amount" +// : "Review", +// variant: "neutral", +// onClick() { +// incrementStepIndex(); +// }, +// }, +// ], +// }; + +// const MODAL_STEP_4: ModalStep = { +// children: ({ underlyingTokenAddress, depositAmount, safeSimulation }) => ( +// +// +// You are creating +// +// {" "} +// Safe +// +// +// {!depositAmount || depositAmount === "0" ? null : ( +// +// )} +// +// ), +// stepBubbles: ({ approving, creatingSafe, hasApproval }) => ({ +// steps: hasApproval ? 1 : 2, +// loading: approving || creatingSafe, +// activeIndex: creatingSafe ? 1 : 0, +// background: "neutral", +// }), +// buttons: ({ +// hasApproval, +// approving, +// depositAmount, +// creatingSafe, +// onClickCreateSafe, +// onClickApprove, +// incrementStepIndex, +// }) => [ +// { +// children: approving +// ? "Approving..." +// : creatingSafe +// ? "Creating Safe..." +// : !hasApproval && +// BigNumber.from(!!depositAmount ? depositAmount : "0").gt(0) +// ? "Approve Router" +// : BigNumber.from(!!depositAmount ? depositAmount : "0").gt(0) +// ? "Create Safe & Deposit" +// : "Create Safe", +// variant: "neutral", +// loading: approving || creatingSafe, +// async onClick() { +// try { +// if (!hasApproval) { +// await onClickApprove(); +// } +// await onClickCreateSafe(); +// incrementStepIndex(); +// } catch (err) { +// throw err; +// } +// }, +// }, +// ], +// }; + +const MODAL_STEP_5: ModalStep = { + children: ({ underlyingTokenAddress }) => ( + + + + + {" "} + Safe + + + Successfully created + + + + ), + buttons: ({ onClose, navigateToCreatedSafe, createdSafe, navigating }) => [ + { + children: navigating ? "Loading..." : "View Safe", + loading: navigating, + disabled: !createdSafe, + variant: "success", + async onClick() { + await navigateToCreatedSafe(); + onClose(); + }, + }, + ], +}; + +const MODAL_STEPS: ModalStep[] = [ + MODAL_STEP_1, + MODAL_STEP_2, + // MODAL_STEP_3, + // MODAL_STEP_4, + MODAL_STEP_5, +]; + +export { MODAL_STEPS }; +export type { CreateSafeCtx }; diff --git a/src/components/pages/Turbo/TurboIndexPage/SafeCard.tsx b/src/components/pages/Turbo/TurboIndexPage/SafeCard.tsx new file mode 100644 index 00000000..faddf6ac --- /dev/null +++ b/src/components/pages/Turbo/TurboIndexPage/SafeCard.tsx @@ -0,0 +1,138 @@ +import { commify, formatEther, formatUnits } from "ethers/lib/utils"; +import { SafeInfo } from "lib/turbo/fetchers/safes/getSafeInfo"; +import { + Badge, + Heading, + HoverableCard, + Link, + Progress, + Text, + TokenIcon, + TokenSymbol, + Tooltip, +} from "rari-components"; +import { abbreviateAmount } from "utils/bigUtils"; +import { Box, Flex, HStack, Spacer, Stack } from "@chakra-ui/react"; +import { + StrategyInfosMap, + useERC4626StrategiesDataAsMap, +} from "hooks/turbo/useStrategyInfo"; +import useSafeAvgAPY from "hooks/turbo/useSafeAvgAPY"; +import { filterUsedStrategies } from "lib/turbo/fetchers/strategies/formatStrategyInfo"; +import { useSafeInfo } from "hooks/turbo/useSafeInfo"; +import { useTokenData } from "hooks/useTokenData"; +import useShouldBoostSafe from "hooks/turbo/useShouldBoostSafe"; +import { getSafeColor } from "context/TurboSafeContext"; +import { motion } from "framer-motion"; +import { USDPricedTurboSafe } from "lib/turbo/fetchers/safes/getUSDPricedSafeInfo"; + +type SafeCardProps = { + safe: USDPricedTurboSafe | SafeInfo; + previewMode: boolean; +}; + +const SafeCard: React.FC = ({ safe, previewMode = false }) => { + const usdPricedSafeInfo = useSafeInfo(safe.safeAddress); + + const getERC4626StrategyData = useERC4626StrategiesDataAsMap( + safe.strategies.map((s) => s.strategy) + ); + + const avgAPY = useSafeAvgAPY( + filterUsedStrategies(safe.strategies), + getERC4626StrategyData, + parseFloat(formatEther(safe?.tribeDAOFee ?? 0)) + ); + + const tokenData = useTokenData(safe.collateralAsset); + + const boostMe = useShouldBoostSafe(safe); + const isAtLiquidationRisk = + usdPricedSafeInfo?.safeUtilization.gt(80) ?? false; + + const color = getSafeColor(safe?.safeUtilization); + + return ( + + + + {(hovered) => ( + + + + + Safe + + + + {!previewMode ? ( + <> + {isAtLiquidationRisk && ( + At Risk + )} + {boostMe && Boost Me} + + ) : null} + + + + + <> + + {abbreviateAmount( + usdPricedSafeInfo?.collateralValueUSD + )} + + + deposited + + + + + + {avgAPY.toFixed(2)}% + + APY + + + + + + Active boost + + + + + )} + + + + ); +}; + +export default SafeCard; diff --git a/src/components/pages/Turbo/TurboIndexPage/SafeGrid.tsx b/src/components/pages/Turbo/TurboIndexPage/SafeGrid.tsx new file mode 100644 index 00000000..91fb8d4f --- /dev/null +++ b/src/components/pages/Turbo/TurboIndexPage/SafeGrid.tsx @@ -0,0 +1,57 @@ +import { SafeInfo } from "lib/turbo/fetchers/safes/getSafeInfo"; +import { Heading, HoverableCard, Text } from "rari-components"; +import { Box, Flex, SimpleGrid } from "@chakra-ui/react"; +import SafeCard from "./SafeCard"; +import { useIsUserAuthorizedToCreateSafes } from "hooks/turbo/useIsUserAuthorizedToCreateSafes"; + +type SafeGridProps = { + safes: SafeInfo[]; + onClickCreateSafe?: () => void; +}; + +const SafeGrid: React.FC = ({ safes, onClickCreateSafe }) => { + let isAuthed = useIsUserAuthorizedToCreateSafes(); + const previewMode = !onClickCreateSafe || !isAuthed; + console.log({ previewMode }); + return ( + + + {!previewMode && ( + + {(hovered) => ( + + + Add safe{" "} + + + + + + + + + Every safe isolates and manages your collateral. + + + )} + + )} + {safes.map((safe) => ( + + ))} + + + ); +}; + +export default SafeGrid; diff --git a/src/components/pages/Turbo/TurboIndexPage/TurboFAQ.tsx b/src/components/pages/Turbo/TurboIndexPage/TurboFAQ.tsx new file mode 100644 index 00000000..c69181a3 --- /dev/null +++ b/src/components/pages/Turbo/TurboIndexPage/TurboFAQ.tsx @@ -0,0 +1,351 @@ +import { Variants, motion, useAnimation } from "framer-motion"; +import usePreviewSafes from "hooks/turbo/usePreviewSafes"; +import { Button, Heading, Text } from "rari-components"; +import React, { useEffect, useRef } from "react"; +import { useInView } from "react-intersection-observer"; +import { + Box, + BoxProps, + Center, + Flex, + HStack, + Image, + SimpleGrid, + Stack, +} from "@chakra-ui/react"; +import AppLink from "components/shared/AppLink"; + +type TurboFAQProps = BoxProps; + +const TurboFAQ: React.FC = (props) => { + const safes = usePreviewSafes(); + return ( + + + {/* Section 1 */} + + + {/* Section 2 */} + + + {/* Section 3 */} + + + {/* Section 4*/} + + + + ); +}; + +const Section1 = () => ( + + + How does Turbo work? + {/* `Stack` adds a little bit of space between each child, even if + * `spacing` is set to 0, which we don't want because we want the bottom + * borders of each child to flow together, giving the impression of a + * single, connected arrow. `Flex` does not add this space, so we use + * `Flex` instead. */} + + + +
+ 1 +
+ Create Safe +
+ + + Take your first step by creating a Turbo Safe. Manage your safe to + boost against a collateral type, earn yield, bootstrap Fuse Pools, + and more. + +
+ + +
+ 2 +
+ Deposit Collateral +
+ + + Deposit whitelisted collateral (gOHM, TRIBE, BAL) into your safe to + enable boosting FEI. + +
+ + +
+ 3 +
+ Boost FEI +
+ + + {/* Arrow tip */} + {/* https://css-tricks.com/snippets/css/css-triangle/ */} + + + + Take a 0% interest FEI loan, and boost it into the yield bearing + strategy of your choice. Claim interest whenever you like. + +
+
+
+
+); + +const Section2 = () => ( + + + + + + Turbo is for everyone + + Turbo can be used by individuals, treasuries, DAOs, protocols, or any + on-chain entity. + + + + + + + Individuals + + + + + + + + DAOs + + + + + + + + Protocols + + + + + + + + And more... + + + + + + + +); + +const Section3 = () => ( + + + + Utilize your treasury assets + + Deposit BAL, TRIBE, and gOHM today. Support for more protocol tokens + coming soon. + + + + + +); + +const Section4 = () => ( + + +
+ A completely new financial mechanism + + Turbo changes the relationship between users in an ecosystem and the + stablecoin issuer. It’s a powerful transformation in the thinking of + stablecoins and their provision throughout the ecosystem. + +
+ + + + +
+
+); + +/** Animations */ +type AnimateInViewDirection = "left" | "right" | "top" | "bottom"; +type AnimateInViewProps = { + children: any; + from: AnimateInViewDirection; +}; + +// TODO (@sharad-s) Move into Component Library and fix the map key type +const VARIANT_DEFAULTS = { + transition: { + duration: 0.6, + staggerChildren: 0.3, + delayChildren: 0.3, + }, +}; + +const variants: { [from: string]: Variants } = { + top: { + visible: { opacity: 1, y: 0, ...VARIANT_DEFAULTS }, + hidden: { opacity: 0, y: -40 }, + }, + bottom: { + visible: { opacity: 1, y: 0, ...VARIANT_DEFAULTS }, + hidden: { opacity: 0, y: 40 }, + }, + left: { + visible: { opacity: 1, x: 0, ...VARIANT_DEFAULTS }, + hidden: { opacity: 0, x: -40 }, + }, + right: { + visible: { opacity: 1, x: 0, ...VARIANT_DEFAULTS }, + hidden: { opacity: 0, x: 40 }, + }, +}; + +const AnimateInView: React.FC = ({ + children, + from = "top", +}) => { + const controls = useAnimation(); + const [ref, inView] = useInView(); + useEffect(() => { + if (inView) { + controls.start("visible"); + } + }, [controls, inView]); + return ( + + {children} + + ); +}; + +export default TurboFAQ; diff --git a/src/components/pages/Turbo/TurboIndexPage/TurboIndexPage.tsx b/src/components/pages/Turbo/TurboIndexPage/TurboIndexPage.tsx new file mode 100644 index 00000000..9ad82c7d --- /dev/null +++ b/src/components/pages/Turbo/TurboIndexPage/TurboIndexPage.tsx @@ -0,0 +1,131 @@ +import { useIsUserAuthorizedToCreateSafes } from "hooks/turbo/useIsUserAuthorizedToCreateSafes"; +import { useAllUserSafes } from "hooks/turbo/useUserSafes"; +import { TRIBE } from "lib/turbo/utils/constants"; +import { Button, Divider, Heading, Text } from "rari-components"; +import { useRariTokenData } from "rari-components/hooks"; +import { WarningIcon } from "@chakra-ui/icons"; +import { + Box, + HStack, + Image, + Stack, + VStack, + useDisclosure, + SlideFade, + Slide, +} from "@chakra-ui/react"; +import TurboLayout from "../TurboLayout"; +import CreateSafeModal from "./CreateSafeModal/"; +import TurboFAQ from "./TurboFAQ"; +import UserSafes from "./UserSafes"; +import useApprovedCollateral from "hooks/turbo/useApprovedCollateral"; +import { useTokensDataAsMap } from "hooks/useTokenData"; +import { useRef } from "react"; +import TurboEngineIcon from "components/shared/Icons/TurboEngineIcon"; + +const TurboIndexPage: React.FC = () => { + const scrollRef = useRef(null); + const { isOpen, onOpen, onClose } = useDisclosure(); + + const safes = useAllUserSafes() ?? []; + const hasSafes = safes.length > 0; + + const isAuthorized = useIsUserAuthorizedToCreateSafes(); + + const approvedCollateral = useApprovedCollateral(); + + // // Prefetch Tribe data so it's in the `TokenIcon`/`TokenSymbol` cache. + // // This allows collateral types to load instantly when the "Create Safe" + // // modal is initially opened. + // const _ = useRariTokenData(TRIBE); + // const tokenData = useTokensDataAsMap(approvedCollateral); + + const handleClick = () => { + // @ts-ignore + isAuthorized && !hasSafes ? onOpen() : scrollRef.current.scrollIntoView(); + }; + + return ( + // Hide overflow of main Turbo screenshot image + + + + + {/* Copy */} + + + + + Introducing Turbo + + + + Boost FEI liquidity and earn interest against any protocol + token. + + {/* Buttons */} + + + {!!isAuthorized && ( + + )} + {hasSafes ? ( + + ) : ( + + )} + + + + + + {/* Image */} + + + + + + + + + + {hasSafes ? ( + + ) : ( + + )} + + + + ); +}; + +export default TurboIndexPage; diff --git a/src/components/pages/Turbo/TurboIndexPage/UserSafes.tsx b/src/components/pages/Turbo/TurboIndexPage/UserSafes.tsx new file mode 100644 index 00000000..f4af3a87 --- /dev/null +++ b/src/components/pages/Turbo/TurboIndexPage/UserSafes.tsx @@ -0,0 +1,76 @@ +import { Box } from "@chakra-ui/react"; +import { commify, formatEther } from "ethers/lib/utils"; +import { useERC4626StrategiesDataAsMap } from "hooks/turbo/useStrategyInfo"; +import { useTrustedStrategies } from "hooks/turbo/useTrustedStrategies"; +import { SafeInfo } from "lib/turbo/fetchers/safes/getSafeInfo"; +import { Heading, Statistic } from "rari-components"; +import { smallUsdFormatter } from "utils/bigUtils"; +import { TriangleDownIcon, TriangleUpIcon } from "@chakra-ui/icons"; +import { Flex, HStack } from "@chakra-ui/react"; +import useAggregateSafeData from "hooks/turbo/useAggregateSafeData"; +import { useState } from "react"; +import SafeGrid from "./SafeGrid"; + +type SafeGridProps = { + safes: SafeInfo[]; + onClickCreateSafe(): void; +}; +const UserSafes: React.FC = ({ safes, onClickCreateSafe }) => { + const allStrategies = useTrustedStrategies(); + const getERC4626StrategyData = useERC4626StrategiesDataAsMap(allStrategies); + + const { totalBoosted, totalClaimableUSD, netAPY } = useAggregateSafeData( + safes, + getERC4626StrategyData + ); + + // TODO(sharad-s) write APY triangle implementation + const [apyIncreasing, setApyIncreasing] = useState(true); + + return ( + + + Your Safes + + + + + setApyIncreasing(!apyIncreasing)} + > + + {netAPY.toFixed(2)}% + + {/* {apyIncreasing ? ( + + ) : ( + + )} */} + + } + /> + + + + ); +}; + +export default UserSafes; diff --git a/src/components/pages/Turbo/TurboIndexPage/index.ts b/src/components/pages/Turbo/TurboIndexPage/index.ts new file mode 100644 index 00000000..bf3d099f --- /dev/null +++ b/src/components/pages/Turbo/TurboIndexPage/index.ts @@ -0,0 +1 @@ +export { default } from "./TurboIndexPage"; diff --git a/src/components/pages/Turbo/TurboLayout.tsx b/src/components/pages/Turbo/TurboLayout.tsx new file mode 100644 index 00000000..cf67a719 --- /dev/null +++ b/src/components/pages/Turbo/TurboLayout.tsx @@ -0,0 +1,37 @@ +import Head from "next/head"; +import theme from "rari-components/theme"; +import { Box, BoxProps, ChakraProvider, Fade } from "@chakra-ui/react"; +import { useRouter } from "next/router"; + +type TurboLayoutProps = BoxProps; + +const TurboLayout: React.FC = (props) => { + const router = useRouter() + return ( + <> + + {/* Default title (can be overridden with another `title` tag). */} + Tribe Turbo + + {/** + * Provide rari-components theme on all Turbo pages so we can import from + * the main `rari-components` entry point within Turbo pages (Turbo is the + * first project to use rari-components from the start — elsewhere on the + * app, we are replacing components incrementally and import from + * `rari-components/standalone` instead, which is less performant). + */} + + + + + + + + + ); +}; + +export default TurboLayout; diff --git a/src/components/pages/Turbo/TurboSafePage/BoostBar.tsx b/src/components/pages/Turbo/TurboSafePage/BoostBar.tsx new file mode 100644 index 00000000..87a45ae4 --- /dev/null +++ b/src/components/pages/Turbo/TurboSafePage/BoostBar.tsx @@ -0,0 +1,113 @@ +// Components +import { + Heading, + Progress, + Text, + TokenIcon, + TokenSymbol, +} from "rari-components"; +import { Avatar, Box, Flex, HStack, Image } from "@chakra-ui/react"; +import { useTurboSafe } from "context/TurboSafeContext"; +import { shortUsdFormatter } from "utils/bigUtils"; +import { toInt } from "utils/ethersUtils"; +import TurboEngineIcon from "components/shared/Icons/TurboEngineIcon"; +import { SimpleTooltip } from "components/shared/SimpleTooltip"; +import AppLink from "components/shared/AppLink"; + +export const BoostBar: React.FC = () => { + const { + usdPricedSafe, + collateralTokenData: tokenData, + colorScheme, + isAtLiquidationRisk, + } = useTurboSafe(); + const { boostedUSD, safeUtilization, maxBoostUSD, liquidationPriceUSD } = + usdPricedSafe ?? {}; + + return ( + + + + 0} + /> + {/* */} + + {shortUsdFormatter(boostedUSD ?? 0)} boost + + + / {shortUsdFormatter(maxBoostUSD ?? 0)} ({toInt(safeUtilization)} + %) + + + + Liquidated when + + + e.stopPropagation()} + isExternal + > + + + + = ${liquidationPriceUSD?.toFixed(2)} + + + + + + + + ); +}; diff --git a/src/components/pages/Turbo/TurboSafePage/OnboardingCard.tsx b/src/components/pages/Turbo/TurboSafePage/OnboardingCard.tsx new file mode 100644 index 00000000..e24d44b9 --- /dev/null +++ b/src/components/pages/Turbo/TurboSafePage/OnboardingCard.tsx @@ -0,0 +1,91 @@ +import { motion } from "framer-motion"; +import { Badge, Card, Heading, Text } from "rari-components"; +import { Box, HStack, ScaleFade, SlideFade, Stack } from "@chakra-ui/react"; +import { useTurboSafe } from "context/TurboSafeContext"; +import { ArrowLeftIcon, CheckIcon } from "@chakra-ui/icons"; + +type OnboardingCardProps = { + openDepositModal: () => void; + onClickBoost: (strategyAddress: string) => void; +}; + +export const OnboardingCard: React.FC = ({ + openDepositModal, + onClickBoost, +}) => { + const { safe, collateralTokenData } = useTurboSafe(); + const { collateralAmount, boostedAmount } = safe ?? {}; + + const hasCollateral = collateralAmount?.isZero() ? false : true; + const hasBoosted = boostedAmount?.isZero() ? false : true; + + const _handleClickBoost = () => { + const pool8Fei = safe?.strategies[0].strategy ?? ""; + if (!pool8Fei) { + return; + } + onClickBoost(pool8Fei); + }; + + return ( + + + Getting Started + + (hasCollateral ? null : openDepositModal())} + opacity={hasCollateral ? 0.25 : 1} + > + + {hasCollateral ? : 1} + + + + Deposit {collateralTokenData?.symbol} collateral{hasCollateral ? "" : " →"} + + + Deposit collateral into your safe to enable boosting FEI + + + + + + {hasBoosted ? : 2} + + + Boost a pool {hasCollateral ? " →" : ""} + + Click to boost your first pool, or scroll below for options + + + + + + + ); +}; diff --git a/src/components/pages/Turbo/TurboSafePage/SafeStats.tsx b/src/components/pages/Turbo/TurboSafePage/SafeStats.tsx new file mode 100644 index 00000000..f15248ee --- /dev/null +++ b/src/components/pages/Turbo/TurboSafePage/SafeStats.tsx @@ -0,0 +1,68 @@ +import { commify, formatEther, formatUnits } from "ethers/lib/utils"; +import { Statistic } from "rari-components"; +// Hooks +import { smallStringUsdFormatter, smallUsdFormatter } from "utils/bigUtils"; +import { Stack } from "@chakra-ui/react"; +import { useUserFeiOwed } from "hooks/turbo/useUserFeiOwed"; +import { useTurboSafe } from "context/TurboSafeContext"; + +export const SafeStats: React.FC = () => { + const { usdPricedSafe, netAPY, collateralTokenData, loading } = + useTurboSafe(); + + const [userFeiOwed] = useUserFeiOwed(usdPricedSafe); + + return ( + + + + + + + + {/* + */} + + ); +}; diff --git a/src/components/pages/Turbo/TurboSafePage/SafeStrategies.tsx b/src/components/pages/Turbo/TurboSafePage/SafeStrategies.tsx new file mode 100644 index 00000000..112b429c --- /dev/null +++ b/src/components/pages/Turbo/TurboSafePage/SafeStrategies.tsx @@ -0,0 +1,174 @@ +import { Box, Button, Flex, HStack, Image } from "@chakra-ui/react"; +import { + Heading, + Link, + Table, + Text, + TokenIcon, + Tooltip, +} from "rari-components"; + +// Turbo +import { USDPricedStrategy } from "lib/turbo/fetchers/safes/getUSDPricedSafeInfo"; + +// Utils +import { smallUsdFormatter } from "utils/bigUtils"; +import { formatEther, formatUnits } from "ethers/lib/utils"; +import { convertMantissaToAPY } from "utils/apyUtils"; +import { FEI } from "lib/turbo/utils/constants"; +import { useTurboSafe } from "context/TurboSafeContext"; +import { getStrategyFusePoolId } from "lib/turbo/fetchers/strategies/formatStrategyInfo"; +import TurboEngineIcon from "components/shared/Icons/TurboEngineIcon"; + +export const SafeStrategies: React.FC<{ + onClickBoost: (strategyAddress: string) => void; + onClickLess: (strategyAddress: string) => void; +}> = ({ onClickBoost, onClickLess }) => { + const { + usdPricedSafe, + getERC4626StrategyData, + isAtLiquidationRisk, + colorScheme, + isUserAdmin + } = useTurboSafe(); + + const safeStrategies: USDPricedStrategy[] = + usdPricedSafe?.usdPricedStrategies ?? []; + + const userPercent = usdPricedSafe?.tribeDAOFee + ? 1 - parseFloat(formatEther(usdPricedSafe?.tribeDAOFee)) + : 1; + + console.log({ safeStrategies }); + + // TODO (@sharad-s) Need to find a way to merge "active" and "inactive" strategies elegantly. Inactive Strategies have no strat address + return ( + <> + { + const strategyData = getERC4626StrategyData[strat.strategy]; + const poolId: string | undefined = getStrategyFusePoolId( + strategyData?.symbol + ); + const grossApy = convertMantissaToAPY( + strategyData?.supplyRatePerBlock ?? 0, + 365 + ); + const netAPY = grossApy * userPercent; + + const canLess = strat.boostAmountUSD > 0; + + return { + key: strat.strategy, + items: [ + + + + {/* */} + {strategyData?.name} + + , + + + + {strat.feiClaimableUSD > 0 || strat.boostAmountUSD > 0 + ? smallUsdFormatter(strat.feiClaimableUSD) + : "-"} + + + , + + + {netAPY.toFixed(2) + "%"} + + , + + + + + {strat.boostAmountUSD > 0 + ? smallUsdFormatter(strat.boostAmountUSD) + : "-"} + + + , + + + onClickBoost(strat.strategy)} + > + + + + + + + + + canLess ? onClickLess(strat.strategy) : null + } + > + + — + + + + , + ], + }; + })} + /> + + ); +}; diff --git a/src/components/pages/Turbo/TurboSafePage/TurboSafePage.tsx b/src/components/pages/Turbo/TurboSafePage/TurboSafePage.tsx new file mode 100644 index 00000000..e5148e06 --- /dev/null +++ b/src/components/pages/Turbo/TurboSafePage/TurboSafePage.tsx @@ -0,0 +1,292 @@ +import Head from "next/head"; + +// Components +import { + Button, + Divider, + Heading, + Link, + Text, + TokenIcon, + TokenSymbol, +} from "rari-components"; +import { + Box, + Flex, + HStack, + Image, + Skeleton, + Stack, + StackProps, + useDisclosure, + VStack, +} from "@chakra-ui/react"; +import { ChevronLeftIcon, InfoIcon, WarningTwoIcon } from "@chakra-ui/icons"; +import TurboLayout from "../TurboLayout"; +import { SafeStats } from "./SafeStats"; +import { SafeStrategies } from "./SafeStrategies"; +import ClaimInterestModal from "./modals/ClaimInterestModal"; +import DepositSafeCollateralModal from "./modals/DepositSafeCollateralModal/DepositSafeCollateralModal"; +import SafeInfoModal from "./modals/TurboInfoModal"; +import WithdrawSafeCollateralModal from "./modals/WithdrawSafeCollateralModal"; +import AtRiskOfLiquidationAlert from "../alerts/AtRiskOfLiquidationAlert"; +import { OnboardingCard } from "./OnboardingCard"; +import { BoostBar } from "./BoostBar"; + +// Hooks +import { useRouter } from "next/router"; +import { useState } from "react"; + +// Context +import { TurboSafeProvider, useTurboSafe } from "context/TurboSafeContext"; +import BoostMeAlert from "../alerts/BoostMeAlert"; +import { AdminAlert } from "components/shared/AdminAlert"; +import { SafeInteractionMode } from "hooks/turbo/useUpdatedSafeInfo"; +import BoostModal from "./modals/BoostModal"; + +const TurboSafePage: React.FC = () => { + const { + safe, + usdPricedSafe, + collateralTokenData, + loading, + isAtLiquidationRisk, + shouldBoost, + isUserAdmin, + } = useTurboSafe(); + + const safeHealth = safe?.safeUtilization; + + const { + isOpen: isDepositModalOpen, + onOpen: openDepositModal, + onClose: closeDepositModal, + } = useDisclosure(); + + const { + isOpen: isSafeModalOpen, + onOpen: openSafeModal, + onClose: closeSafeModal, + } = useDisclosure(); + + const { + isOpen: isWithdrawModalOpen, + onOpen: openWithdrawModal, + onClose: closeWithdrawModal, + } = useDisclosure(); + + const { + isOpen: isClaimInterestModalOpen, + onOpen: openClaimInterestModal, + onClose: closeClaimInterestModal, + } = useDisclosure(); + + const [hovered, setHovered] = useState(false); + + /* Strategy + Boost Modal State */ + const [activeStrategyAddress, setActiveStrategyAddress] = useState(); + + const { + isOpen: isBoostModalOpen, + onOpen: onBoostModalOpen, + onClose: onBoostModalClose, + } = useDisclosure(); + + const [boostMode, setBoostMode] = useState< + SafeInteractionMode.BOOST | SafeInteractionMode.LESS | undefined + >(); + + const onClickBoost = (strategyAddress: string) => { + setActiveStrategyAddress(strategyAddress); + setBoostMode(SafeInteractionMode.BOOST); + onBoostModalOpen(); + }; + + const onClickLess = (strategyAddress: string) => { + setActiveStrategyAddress(strategyAddress); + setBoostMode(SafeInteractionMode.LESS); + onBoostModalOpen(); + }; + + return ( + <> + + {collateralTokenData?.symbol} Safe | Tribe Turbo + + + {/* Modals */} + + + + + {!!activeStrategyAddress && !!boostMode && ( + + )} + + setHovered(true)} + onMouseLeave={() => setHovered(false)} + mb={4} + > + + + {" "} + + All Safes + + + + + + {/* Alerts */} + {isAtLiquidationRisk && ( + + )} + + + + + + + + Safe + + + + {!isUserAdmin && ( + + + You are not the admin of this safe + + )} + + + + + + {safe?.boostedAmount.isZero() && !loading && ( + + )} + + + + + + + + + ); +}; + +type ButtonsProps = StackProps & { + openDepositModal: any; + openWithdrawModal: any; + openClaimInterestModal: any; +}; + +export const Buttons: React.FC = ({ + openDepositModal, + openWithdrawModal, + openClaimInterestModal, + ...restProps +}) => { + const { loading, safe, totalFeiOwed, isUserAdmin } = useTurboSafe(); + const hasDeposits = !!safe?.collateralAmount.gt(0); + const hasClaimable = totalFeiOwed.gt(0); + + return ( + + + + + + ); +}; + +export default () => { + const router = useRouter(); + const { id } = router.query; + const safeAddress = id as string; + + return ( + + + + + + ); +}; diff --git a/src/components/pages/Turbo/TurboSafePage/UpdatingStatisticsTable.tsx b/src/components/pages/Turbo/TurboSafePage/UpdatingStatisticsTable.tsx new file mode 100644 index 00000000..b51dcfca --- /dev/null +++ b/src/components/pages/Turbo/TurboSafePage/UpdatingStatisticsTable.tsx @@ -0,0 +1,86 @@ +import { commify } from "ethers/lib/utils"; +import { + StatisticsTable, + StatisticsTableProps, + Tooltip, +} from "rari-components"; +import { abbreviateAmount } from "utils/bigUtils"; +import { Box, Text } from "@chakra-ui/react"; + +/** + * An `UpdatingStatistic` can either be a statistic with an initial value and + * a new value, or a regular statistic (i.e. one that would be passed to a bare + * ``). + */ +type UpdatingStatistic = + | { + title: string; + tooltip: string; + initialValue: number; + newValue?: number | undefined; + } + | StatisticsTableProps["statistics"][number]; + +type UpdatingStatisticsTableProps = Omit & { + statistics: UpdatingStatistic[]; + colorScheme: string; +}; + +/** + * Composition of `` specifically for displaying large + * numeric statistics that update (use cases include displaying data related to + * depositing, withdrawing, etc.). + * + * The component handles abbreviating long values and setting up tooltips which + * display the full value on hover. + * + * Based on https://reactjs.org/docs/composition-vs-inheritance.html + */ +const UpdatingStatisticsTable: React.FC = ({ + statistics, + colorScheme, + ...restProps +}) => { + const updatingStatistics = statistics.map((it) => { + // If this item is a regular statistic, skip processing. + if (Array.isArray(it) || it === StatisticsTable.DIVIDER) { + return it; + } + + const { title, tooltip, initialValue, newValue } = it; + + const value = ( + + {!newValue ? ( + + {abbreviateAmount(initialValue)} + + ) : ( + <> + + {abbreviateAmount(initialValue)} + {" "} + + →{" "} + + {abbreviateAmount(newValue)} + + + + )} + + ); + + const statistic: StatisticsTableProps["statistics"][number] = [ + it.title, + value, + it.tooltip, + ]; + return statistic; + }); + + return ; +}; + +export default UpdatingStatisticsTable; +export type { UpdatingStatisticsTableProps }; diff --git a/src/components/pages/Turbo/TurboSafePage/index.tsx b/src/components/pages/Turbo/TurboSafePage/index.tsx new file mode 100644 index 00000000..be31c1f1 --- /dev/null +++ b/src/components/pages/Turbo/TurboSafePage/index.tsx @@ -0,0 +1 @@ +export { default } from "./TurboSafePage"; diff --git a/src/components/pages/Turbo/TurboSafePage/modals/BoostModal/BoostModal.tsx b/src/components/pages/Turbo/TurboSafePage/modals/BoostModal/BoostModal.tsx new file mode 100644 index 00000000..38a2dbdb --- /dev/null +++ b/src/components/pages/Turbo/TurboSafePage/modals/BoostModal/BoostModal.tsx @@ -0,0 +1,261 @@ +// Hooks +import { useRari } from "context/RariContext"; +// Utils +import { formatEther, parseEther, parseUnits } from "ethers/lib/utils"; +import useSafeMaxAmount from "hooks/turbo/useSafeMaxAmount"; +import { FuseERC4626Strategy } from "hooks/turbo/useStrategyInfo"; +import { + SafeInteractionMode, + useUpdatedSafeInfo, +} from "hooks/turbo/useUpdatedSafeInfo"; +import { + USDPricedStrategy, + USDPricedTurboSafe, +} from "lib/turbo/fetchers/safes/getUSDPricedSafeInfo"; +import { safeBoost, safeLess } from "lib/turbo/transactions/safe"; +// Turbo +import { Modal } from "rari-components"; +import { useMemo, useState } from "react"; +import { handleGenericError } from "utils/errorHandling"; +import { useToast } from "@chakra-ui/react"; +import { MODAL_STEPS } from "./modalSteps"; +import { useQuery, useQueryClient } from "react-query"; +import { useBoostCapForStrategy } from "hooks/turbo/useBoostCapsForStrategies"; +import { useTurboSafe } from "context/TurboSafeContext"; + +// Utils +import { keyBy } from "lodash"; +import useUpdatedStrategyRate from "hooks/turbo/useUpdatedStrategyRate"; +import { getEthUsdPriceBN } from "esm/utils/getUSDPriceBN"; +import { constants } from "ethers"; + +type BoostStrategyModalProps = { + isOpen: boolean; + onClose: () => void; + activeStrategyAddress: string; + mode: SafeInteractionMode.BOOST | SafeInteractionMode.LESS; +}; + +export const BoostStrategyModal: React.FC = ({ + isOpen, + onClose, + activeStrategyAddress, + mode, +}) => { + const { provider, chainId } = useRari(); + const toast = useToast(); + const queryClient = useQueryClient(); + + /** Strategy ? **/ + const { usdPricedSafe, getERC4626StrategyData } = useTurboSafe(); + const safeStrategies: USDPricedStrategy[] = + usdPricedSafe?.usdPricedStrategies ?? []; + + const { data: ethUSDPriceBN } = useQuery("ETH USD", async () => + getEthUsdPriceBN() + ); + + // Construct a new object where safe strategies are indexed by address + // for O(1) access by address (order in the table is not necessarily + // stable). + const safeStrategiesByAddress = useMemo( + () => keyBy(safeStrategies, (strategy) => strategy.strategy), + [safeStrategies] + ); + + const strategy = !!activeStrategyAddress + ? safeStrategiesByAddress[activeStrategyAddress] + : undefined; + + // For now, a bunch of other components rely on the assumption that the + // order of the strategies *stored in the source `safe.usdPricedStrategies` + // array* is stable. Since the order of items in the table is not + // necessarily stable, we need to translate from a table index to a source + // array index using `Array.prototype.findIndex`. + const activeStrategyIndex = useMemo( + () => + safeStrategies.findIndex( + (strategy) => strategy.strategy === activeStrategyAddress + ), + [safeStrategies, activeStrategyAddress] + ); + + const erc4626Strategy = getERC4626StrategyData[activeStrategyAddress]; + + const [stepIndex, setStepIndex] = useState(0); + function incrementStepIndex() { + if (stepIndex + 1 !== MODAL_STEPS.length) { + setStepIndex(stepIndex + 1); + } + } + function resetStepIndex() { + setStepIndex(0); + } + + const [amount, setAmount] = useState("0"); + const [transacting, setTransacting] = useState(false); + + const updatedSafe = useUpdatedSafeInfo({ + mode, + safe: usdPricedSafe, + amount: parseUnits(!!amount ? amount : "0", 18), + strategyIndex: activeStrategyIndex, + }); + + const updatedRate = useUpdatedStrategyRate( + mode, + usdPricedSafe, + activeStrategyIndex, + parseUnits(!!amount ? amount : "0", 18) + ); + + const maxAmount = useSafeMaxAmount(usdPricedSafe, mode, activeStrategyIndex); + + const isRiskyBoost = + mode === SafeInteractionMode.BOOST && + !!updatedSafe?.safeUtilization?.gt(75); + + // Boost cap for a vault + const [boostCap, totalBoosted] = + useBoostCapForStrategy(strategy?.strategy) ?? []; + const percentTotalBoosted = + boostCap && totalBoosted + ? (parseFloat(formatEther(totalBoosted)) / + parseFloat(formatEther(boostCap))) * + 100 + : undefined; + + // Form validation + const inputError: string | undefined = useMemo(() => { + const _amount = amount ? amount : "0"; + + switch (mode) { + case SafeInteractionMode.BOOST: + if (parseEther(_amount).gt(maxAmount)) { + return "You can't boost this much!"; + } + if (!!boostCap && !!totalBoosted) { + if ( + parseFloat(formatEther(totalBoosted)) + parseFloat(_amount) > + parseFloat(formatEther(boostCap)) + ) { + return "Boost amount exceeds Cap for Vault"; + } + } + if ( + amount !== "0" && + ethUSDPriceBN?.div(constants.WeiPerEther).gt(parseInt(_amount)) + ) { + return "Minimum Boost must be > 1 ETH"; + } + + break; + case SafeInteractionMode.LESS: + if (parseEther(_amount).gt(maxAmount)) { + return "You can't less this much!"; + } + break; + default: + return undefined; + } + }, [amount, maxAmount, strategy, mode, updatedSafe, ethUSDPriceBN]); + + // Boost a strategy + const onClickBoost = async () => { + if (!usdPricedSafe?.safeAddress || !provider || !amount) return; + const amountBN = parseEther(amount); + const strategyAddress = strategy!.strategy; + + try { + setTransacting(true); + const tx = await safeBoost( + usdPricedSafe.safeAddress, + strategyAddress, + amountBN, + //@ts-ignore + await provider.getSigner() + ); + await tx.wait(1); + incrementStepIndex(); + } catch (err) { + handleGenericError(err, toast); + } finally { + setTransacting(false); + + + setTimeout(() => { + onClose(); + resetStepIndex(); + }, 1500); + await queryClient.refetchQueries(); + } + }; + + // Less a strategy + const onClickLess = async () => { + if (!usdPricedSafe?.safeAddress || !provider || !amount || !strategy) + return; + + let amountBN = parseEther(amount); + amountBN = amountBN.gte(strategy.boostedAmount) + ? strategy.boostedAmount + : amountBN; + + const lessingMax = strategy.boostedAmount.eq(amountBN); + + try { + setTransacting(true); + const tx = await safeLess( + usdPricedSafe.safeAddress, + strategy.strategy, + amountBN, + lessingMax, + await provider.getSigner(), + chainId ?? 1 + ); + await tx.wait(1); + incrementStepIndex(); + } catch (err) { + handleGenericError(err, toast); + } finally { + setTransacting(false); + await queryClient.refetchQueries(); + } + }; + + const onClickMax = () => setAmount(formatEther(maxAmount)); + + return ( + + ); +}; + +export default BoostStrategyModal; diff --git a/src/components/pages/Turbo/TurboSafePage/modals/BoostModal/index.ts b/src/components/pages/Turbo/TurboSafePage/modals/BoostModal/index.ts new file mode 100644 index 00000000..957c3c8a --- /dev/null +++ b/src/components/pages/Turbo/TurboSafePage/modals/BoostModal/index.ts @@ -0,0 +1 @@ +export { default } from "./BoostModal"; diff --git a/src/components/pages/Turbo/TurboSafePage/modals/BoostModal/modalSteps.tsx b/src/components/pages/Turbo/TurboSafePage/modals/BoostModal/modalSteps.tsx new file mode 100644 index 00000000..068d15b1 --- /dev/null +++ b/src/components/pages/Turbo/TurboSafePage/modals/BoostModal/modalSteps.tsx @@ -0,0 +1,306 @@ +import TurboEngineIcon from "components/shared/Icons/TurboEngineIcon"; +import { BigNumber, constants } from "ethers"; +import { commify, formatEther, parseEther } from "ethers/lib/utils"; +import { FuseERC4626Strategy } from "hooks/turbo/useStrategyInfo"; +import { SafeInteractionMode } from "hooks/turbo/useUpdatedSafeInfo"; +import { + USDPricedStrategy, + USDPricedTurboSafe, +} from "lib/turbo/fetchers/safes/getUSDPricedSafeInfo"; +import { FEI } from "lib/turbo/utils/constants"; +import { + Heading, + ModalProps, + StatisticsTable, + StatisticsTableProps, + Text, + TokenAmountInput, +} from "rari-components"; +import { abbreviateAmount } from "utils/bigUtils"; +import { CheckCircleIcon } from "@chakra-ui/icons"; +import { Box, HStack, Image, Spinner, Stack, VStack } from "@chakra-ui/react"; +import { getSafeColor } from "context/TurboSafeContext"; +import { SimpleTooltip } from "components/shared/SimpleTooltip"; + +type BoostModalCtx = { + incrementStepIndex(): void; + resetStepIndex(): void; + safe?: USDPricedTurboSafe; + updatedSafe?: USDPricedTurboSafe; + amount: string; + setAmount(newAmount: string): void; + transacting: boolean; + onClickBoost(): Promise; + onClickLess(): Promise; + onClose(): void; + onClickMax(snip?: boolean): void; + maxAmount: BigNumber; + mode: SafeInteractionMode.BOOST | SafeInteractionMode.LESS; + strategy: USDPricedStrategy | undefined; + erc4626Strategy: FuseERC4626Strategy | undefined; + inputError: string | undefined; + // Boost caps + boostCap: BigNumber | undefined; + totalBoosted: BigNumber | undefined; + percentTotalBoosted: number | undefined; + // Risk + isRiskyBoost: boolean; +}; + +type ModalStep = Omit, "ctx" | "isOpen" | "onClose">; + +const MODAL_STEP_1: ModalStep = { + title: ({ mode, erc4626Strategy }) => `${mode} strategy`, + children: ({ + onClickMax, + setAmount, + amount, + safe, + updatedSafe, + mode, + maxAmount, + strategy, + boostCap, + totalBoosted, + erc4626Strategy, + }) => { + if (!safe) { + return null; + } + + const statisticsLoading = !updatedSafe; + + const totalBoostBalanceStatistic = statisticsLoading ? ( + + ) : ( + + {abbreviateAmount(safe?.boostedUSD)}{" "} + + → {abbreviateAmount(updatedSafe?.boostedUSD)} + + + ); + + const safeUtilizationStatistic = statisticsLoading ? ( + + ) : ( + + {parseFloat(safe?.safeUtilization.toString() ?? "0").toFixed(2)}%{" "} + + →{" "} + {parseFloat(updatedSafe?.safeUtilization.toString() ?? "0").toFixed( + 2 + )} + % + + + ); + + const liquidationPriceStatistic = statisticsLoading ? ( + + ) : ( + + {abbreviateAmount(safe?.liquidationPriceUSD)}{" "} + + → {abbreviateAmount(updatedSafe?.liquidationPriceUSD)} + + + ); + + const newTotalBoost = + totalBoosted?.add(parseEther(!!amount ? amount : "0")) ?? constants.Zero; + + const boostCapStatistic = statisticsLoading ? ( + + ) : ( + + + + {abbreviateAmount(formatEther(newTotalBoost), false)} + + + / + + {abbreviateAmount(formatEther(boostCap ?? constants.Zero), false)} + + + ); + + const statistics: StatisticsTableProps["statistics"] = [ + [ + "Total Boost Balance", + totalBoostBalanceStatistic, + "The maximum amount you can boost.", + ], + [ + "Safe Utilization", + safeUtilizationStatistic, + "The maximum amount you can boost.", + ], + [ + "Liquidation Price", + liquidationPriceStatistic, + "The liquidation price of your collateral", + ], + "DIVIDER", + [ + "Strategy", + {erc4626Strategy?.name}, + "The strategy you are boosting", + ], + ]; + + if (mode === SafeInteractionMode.BOOST) { + statistics.push([ + "Boost Cap", + boostCapStatistic, + "Every strategy has its boost cap to as a safety measure. This the amount of FEI in total that the strategy can be boosted with.", + ]); + } + + return ( + <> + + setAmount(amount ?? "0")} + tokenAddress={FEI} + onClickMax={() => onClickMax(true)} + /> + onClickMax()} + // _hover={{ cursor: "pointer", opacity: 0.9 }} + // transition="opacity 0.2s ease" + > + {mode === SafeInteractionMode.BOOST + ? `You can boost ${commify( + parseFloat(formatEther(maxAmount)).toFixed(2) + )} FEI` + : `You can less ${commify( + parseFloat(formatEther(strategy!.boostedAmount)).toFixed(2) + )} FEI`} + + + + {/* Boost cap for Strategy - show if amount is 50% over boost */} + {mode === SafeInteractionMode.BOOST && + !!boostCap + ?.div(2) + ?.lt( + totalBoosted?.add(parseEther(amount || "0")) ?? constants.Zero + ) && ( + + {/* */} + {/* + Total Boost{" "} + {abbreviateAmount( + formatEther( + totalBoosted?.add(parseEther(amount || "0")) ?? + constants.Zero + ) + )} + + · + Approaching {abbreviateAmount(formatEther(boostCap))} */} + + )} + {/* Claim Fees on Less All */} + {mode === SafeInteractionMode.LESS && + strategy?.boostedAmount?.eq(parseEther(amount ? amount : "0")) && ( + + + + Lessing the entire strategy will also accrue{" "} + + {parseFloat(formatEther(strategy.feiClaimable)) < 0.01 + ? "<.01" + : parseFloat(formatEther(strategy.feiClaimable)).toFixed( + 2 + )}{" "} + FEI + {" "} + to the Safe. + + + )} + + ); + }, + buttons: ({ + mode, + amount, + strategy, + transacting, + onClickBoost, + onClickLess, + incrementStepIndex, + inputError, + isRiskyBoost, + }) => [ + { + children: !!inputError + ? inputError + : mode === SafeInteractionMode.LESS + ? strategy?.boostedAmount?.eq(parseEther(amount || "0")) + ? "Less and Accrue Rewards" + : "Less" + : isRiskyBoost + ? "Risky Boost!" + : "Boost", + variant: isRiskyBoost ? "danger" : "neutral", + loading: transacting, + disabled: !amount || !!inputError || transacting, + async onClick() { + try { + if (mode === "Boost") { + await onClickBoost(); + } else { + await onClickLess(); + } + } catch (err) { + throw err; + } + }, + }, + ], +}; + +const MODAL_STEP_2: ModalStep = { + children: ({ amount, mode }) => ( + + + + {commify(parseFloat(amount).toFixed(2))} FEI + + Successfully {mode}ed + + + + ), + buttons: ({ onClose, resetStepIndex }) => [ + { + children: "Back to Safe", + variant: "neutral", + async onClick() { + resetStepIndex(); + onClose(); + }, + }, + ], +}; + +const MODAL_STEPS: ModalStep[] = [MODAL_STEP_1, MODAL_STEP_2]; + +export { MODAL_STEPS }; +export type { BoostModalCtx }; diff --git a/src/components/pages/Turbo/TurboSafePage/modals/ClaimInterestModal/ClaimInterestModal.tsx b/src/components/pages/Turbo/TurboSafePage/modals/ClaimInterestModal/ClaimInterestModal.tsx new file mode 100644 index 00000000..0fee59b1 --- /dev/null +++ b/src/components/pages/Turbo/TurboSafePage/modals/ClaimInterestModal/ClaimInterestModal.tsx @@ -0,0 +1,115 @@ +// Hooks +import { useRari } from "context/RariContext"; +import { useERC4626StrategiesDataAsMap } from "hooks/turbo/useStrategyInfo"; +import { useUserFeiOwed } from "hooks/turbo/useUserFeiOwed"; +import { + USDPricedStrategy, + USDPricedTurboSafe, +} from "lib/turbo/fetchers/safes/getUSDPricedSafeInfo"; +import { filterUsedStrategies } from "lib/turbo/fetchers/strategies/formatStrategyInfo"; +import { safeClaimAll } from "lib/turbo/transactions/claim"; +import { safeSweep } from "lib/turbo/transactions/safe"; +import { FEI } from "lib/turbo/utils/constants"; +// Turbo +import { Modal } from "rari-components"; +import { useState } from "react"; +import { handleGenericError } from "utils/errorHandling"; +import { useToast } from "@chakra-ui/react"; +import { MODAL_STEPS } from "./modalSteps"; + +// Todo - reuse Modal Prop Types +type ClaimInterestModalProps = { + isOpen: boolean; + onClose: () => void; + safe?: USDPricedTurboSafe; +}; + +export const ClaimInterestModal: React.FC = ({ + isOpen, + onClose, + safe, +}) => { + const { address, chainId, provider } = useRari(); + + const toast = useToast(); + + const [claiming, setClaiming] = useState(false); + + const [stepIndex, setStepIndex] = useState(0); + function incrementStepIndex() { + if (stepIndex + 1 !== MODAL_STEPS.length) { + setStepIndex(stepIndex + 1); + } + } + function resetStepIndex() { + setStepIndex(0); + } + + const activeStrategies: USDPricedStrategy[] = filterUsedStrategies( + safe?.usdPricedStrategies ?? [] + ) as USDPricedStrategy[]; + const strategyData = useERC4626StrategiesDataAsMap( + activeStrategies.map((strat) => strat.strategy) + ); + + const [totalClaimable, claimableFromStrategies, safeFeiBalance] = + useUserFeiOwed(safe); + + const onClickClaimInterest = async () => { + if (!safe || !chainId || !activeStrategies) return; + try { + setClaiming(true); + + // If There is nothing claimable from strats, just sweep the safe. Else slurp all + sweep + if (claimableFromStrategies.isZero()) { + const tx = await safeSweep( + safe.safeAddress, + address, + FEI, + safeFeiBalance, + chainId, + await provider.getSigner() + ); + await tx.wait(1); + incrementStepIndex(); + } else { + const tx = await safeClaimAll({ + safeAddress: safe.safeAddress, + strategies: activeStrategies.map((s) => s.strategy), + recipient: address, + signer: await provider.getSigner(), + chainID: chainId, + }); + await tx.wait(1); + incrementStepIndex(); + } + } catch (err) { + handleGenericError(err, toast); + } finally { + setClaiming(false); + } + }; + + return ( + + ); +}; + +export default ClaimInterestModal; diff --git a/src/components/pages/Turbo/TurboSafePage/modals/ClaimInterestModal/index.ts b/src/components/pages/Turbo/TurboSafePage/modals/ClaimInterestModal/index.ts new file mode 100644 index 00000000..5d555dc2 --- /dev/null +++ b/src/components/pages/Turbo/TurboSafePage/modals/ClaimInterestModal/index.ts @@ -0,0 +1 @@ +export { default } from "./ClaimInterestModal"; diff --git a/src/components/pages/Turbo/TurboSafePage/modals/ClaimInterestModal/modalSteps.tsx b/src/components/pages/Turbo/TurboSafePage/modals/ClaimInterestModal/modalSteps.tsx new file mode 100644 index 00000000..e999b608 --- /dev/null +++ b/src/components/pages/Turbo/TurboSafePage/modals/ClaimInterestModal/modalSteps.tsx @@ -0,0 +1,115 @@ +import { BigNumber } from "ethers"; +import { commify, formatEther } from "ethers/lib/utils"; +import { StrategyInfosMap } from "hooks/turbo/useStrategyInfo"; +import { + USDPricedStrategy, + USDPricedTurboSafe, +} from "lib/turbo/fetchers/safes/getUSDPricedSafeInfo"; +import { FEI } from "lib/turbo/utils/constants"; +import { + Heading, + ModalProps, + StatisticsTable, + Text, + TokenIcon, +} from "rari-components"; +import { CheckCircleIcon } from "@chakra-ui/icons"; +import { Box, HStack, Stack, VStack } from "@chakra-ui/react"; + +type ClaimInterestCtx = { + incrementStepIndex(): void; + resetStepIndex(): void; + claiming: boolean; + onClickClaimInterest(): void; + onClose(): void; + totalClaimable: BigNumber; + claimableFromStrategies: BigNumber; + safeFeiBalance: BigNumber; + activeStrategies: USDPricedStrategy[]; + strategyData: StrategyInfosMap; + safe?: USDPricedTurboSafe; +}; + +type ModalStep = Omit< + ModalProps, + "ctx" | "isOpen" | "onClose" +>; + +const MODAL_STEP_1: ModalStep = { + children: ({ + totalClaimable, + claimableFromStrategies, + safeFeiBalance, + activeStrategies, + strategyData, + safe, + }) => ( + + Claim Interest + + + + {commify(parseFloat(formatEther(totalClaimable)).toFixed(2))} FEI + + + + + ), + buttons: ({ claiming, onClickClaimInterest }) => [ + { + children: claiming ? "Claiming..." : "Claim rewards", + variant: "neutral", + loading: claiming, + async onClick() { + await onClickClaimInterest(); + }, + }, + ], +}; + +const MODAL_STEP_2: ModalStep = { + children: ({ totalClaimable }) => ( + + + + + {commify(parseFloat(formatEther(totalClaimable)).toFixed(2))} FEI + + + Successfully claimed + + + + ), + buttons: ({ resetStepIndex, onClose }) => [ + { + children: "Back to Safe", + variant: "neutral", + async onClick() { + resetStepIndex(); + onClose(); + }, + }, + ], +}; + +const MODAL_STEPS: ModalStep[] = [MODAL_STEP_1, MODAL_STEP_2]; + +export { MODAL_STEPS }; +export type { ClaimInterestCtx }; diff --git a/src/components/pages/Turbo/TurboSafePage/modals/DepositSafeCollateralModal/DepositSafeCollateralModal.tsx b/src/components/pages/Turbo/TurboSafePage/modals/DepositSafeCollateralModal/DepositSafeCollateralModal.tsx new file mode 100644 index 00000000..1680d259 --- /dev/null +++ b/src/components/pages/Turbo/TurboSafePage/modals/DepositSafeCollateralModal/DepositSafeCollateralModal.tsx @@ -0,0 +1,168 @@ +// Hooks +import { useRari } from "context/RariContext"; +// Utils +import { formatEther, parseEther, parseUnits } from "ethers/lib/utils"; +import { useBalanceOf } from "hooks/useBalanceOf"; +// Turbo +import { Modal } from "rari-components"; +import { useMemo, useState } from "react"; +import { handleGenericError } from "utils/errorHandling"; +import { useToast } from "@chakra-ui/react"; +import { MODAL_STEPS } from "./modalSteps"; +import { safeDeposit } from "lib/turbo/transactions/safe"; +import { checkAllowanceAndApprove } from "utils/erc20Utils"; + +// import useHasApproval from "hooks/useHasApproval"; +import { + SafeInteractionMode, + useUpdatedSafeInfo, +} from "hooks/turbo/useUpdatedSafeInfo"; +import { USDPricedTurboSafe } from "lib/turbo/fetchers/safes/getUSDPricedSafeInfo"; +import { fetchMaxSafeAmount } from "lib/turbo/utils/fetchMaxSafeAmount"; +import { MAX_APPROVAL_AMOUNT } from "utils/tokenUtils"; +import useHasApproval from "hooks/useHasApproval"; +import { useQueryClient } from "react-query"; +import useSafeMaxAmount from "hooks/turbo/useSafeMaxAmount"; +import { constants } from "ethers"; +import { useTurboSafe } from "context/TurboSafeContext"; + +// Todo - reuse Modal Prop Types +type DepositSafeCollateralModalProps = { + isOpen: boolean; + onClose: () => void; + safe: USDPricedTurboSafe | undefined; +}; + +export const DepositSafeCollateralModal: React.FC< + DepositSafeCollateralModalProps +> = ({ isOpen, onClose, safe }) => { + const { address, provider, chainId } = useRari(); + const { collateralTokenData } = useTurboSafe(); + const toast = useToast(); + const queryClient = useQueryClient(); + + const [stepIndex, setStepIndex] = useState(0); + function incrementStepIndex() { + if (stepIndex + 1 !== MODAL_STEPS.length) { + setStepIndex(stepIndex + 1); + } + } + function resetStepIndex() { + setStepIndex(0); + } + + const [depositAmount, setDepositAmount] = useState(""); + const [depositing, setDepositing] = useState(false); + + const collateralBalance = useBalanceOf(address, safe?.collateralAsset); + const [approving, setApproving] = useState(false); + + const hasApproval = useHasApproval( + safe?.collateralAsset, + safe?.safeAddress, + depositAmount + ); + + const updatedSafe = useUpdatedSafeInfo({ + mode: SafeInteractionMode.DEPOSIT, + safe, + amount: parseUnits(!!depositAmount ? depositAmount : "0", 18), + }); + + const handleApproveAndDeposit = async () => { + if (!hasApproval) { + await approve(); + } + await onClickDeposit(); + }; + + const approve = async () => { + setApproving(true); + const safeAddress = safe?.safeAddress; + if (!safeAddress) return; + try { + setApproving(true); + const tx = await checkAllowanceAndApprove( + provider.getSigner(), + address, + safeAddress, + safe.collateralAsset, + MAX_APPROVAL_AMOUNT + ); + await tx.wait(1); + } catch (err) { + throw err; + } finally { + setApproving(false); + } + }; + + const onClickDeposit = async () => { + if (!depositAmount || !safe) return; + const depositAmountBN = parseEther(depositAmount); + const { safeAddress, collateralAsset } = safe; + + try { + setDepositing(true); + const tx = await safeDeposit( + safeAddress, + address, + depositAmountBN, + provider.getSigner() + ); + await tx.wait(1); + incrementStepIndex(); + } catch (err) { + throw err; + } finally { + setDepositing(false); + await queryClient.refetchQueries(); + } + }; + + const maxAmount = useSafeMaxAmount(safe, SafeInteractionMode.DEPOSIT); + + const onClickMax = async () => { + try { + setDepositAmount(formatEther(maxAmount)); + } catch (err) { + handleGenericError(err, toast); + } + }; + + // Form validation + const inputError: string | undefined = useMemo(() => { + const _amount = !!depositAmount ? depositAmount : "0"; + const _amountBN = parseUnits(_amount, collateralTokenData?.decimals ?? 18); + // + if (_amountBN.gt(maxAmount)) { + return "Can't deposit this much!"; + } + }, [depositAmount, maxAmount]); + + return ( + + ); +}; + +export default DepositSafeCollateralModal; diff --git a/src/components/pages/Turbo/TurboSafePage/modals/DepositSafeCollateralModal/index.ts b/src/components/pages/Turbo/TurboSafePage/modals/DepositSafeCollateralModal/index.ts new file mode 100644 index 00000000..1ae3e984 --- /dev/null +++ b/src/components/pages/Turbo/TurboSafePage/modals/DepositSafeCollateralModal/index.ts @@ -0,0 +1 @@ +export { default } from "./DepositSafeCollateralModal"; diff --git a/src/components/pages/Turbo/TurboSafePage/modals/DepositSafeCollateralModal/modalSteps.tsx b/src/components/pages/Turbo/TurboSafePage/modals/DepositSafeCollateralModal/modalSteps.tsx new file mode 100644 index 00000000..42f3fe6c --- /dev/null +++ b/src/components/pages/Turbo/TurboSafePage/modals/DepositSafeCollateralModal/modalSteps.tsx @@ -0,0 +1,181 @@ +import { BigNumber } from "ethers"; +import { commify, formatEther } from "ethers/lib/utils"; +import { USDPricedTurboSafe } from "lib/turbo/fetchers/safes/getUSDPricedSafeInfo"; +import { + Heading, + ModalProps, + Text, + TokenAmountInput, + TokenSymbol, +} from "rari-components"; +import { Box, Stack, VStack } from "@chakra-ui/react"; +import { CheckCircleIcon } from "@chakra-ui/icons"; +import UpdatingStatisticsTable from "../../UpdatingStatisticsTable"; +import { getSafeColor } from "context/TurboSafeContext"; + +type DepositSafeCollateralCtx = { + incrementStepIndex(): void; + resetStepIndex(): void; + safe?: USDPricedTurboSafe; + updatedSafe?: USDPricedTurboSafe; + hasApproval: boolean; + approving: boolean; + depositAmount: string; + setDepositAmount(newDepositAmount: string): void; + depositing: boolean; + collateralBalance: BigNumber; + onClickMax(): Promise; + onClose(): void; + inputError: string | undefined; + handleApproveAndDeposit(): Promise; +}; + +type ModalStep = Omit< + ModalProps, + "ctx" | "isOpen" | "onClose" +>; + +const MODAL_STEP_1: ModalStep = { + title: "Deposit Collateral", + subtitle: "Collateralizing is required before boosting pools.", + children: ({ + onClickMax, + setDepositAmount, + safe, + updatedSafe, + collateralBalance, + depositAmount, + }) => { + if (!safe) { + return null; + } + + const safeUtilizationValue = (colorScheme: string) => + depositAmount === "" || parseInt(depositAmount) == 0 ? ( + parseFloat(safe?.safeUtilization.toString() ?? "0").toFixed(2) + "%" + ) : ( + + {parseFloat(safe?.safeUtilization.toString() ?? "0").toFixed(2) + "%"}{" "} + + →{" "} + + {parseFloat( + updatedSafe?.safeUtilization.toString() ?? "0" + ).toFixed(2) + "%"} + + + + ); + + return ( + <> + + setDepositAmount(amount ?? "0")} + tokenAddress={safe.collateralAsset} + onClickMax={onClickMax} + /> + + Balance: {commify(formatEther(collateralBalance))}{" "} + + + + + + ); + }, + buttons: ({ + approving, + depositing, + hasApproval, + depositAmount, + inputError, + handleApproveAndDeposit, + }) => [ + { + children: !!inputError + ? inputError + : approving + ? "Approving..." + : depositing + ? "Depositing..." + : !hasApproval + ? "Approve Safe" + : "Deposit", + variant: "neutral", + loading: approving || depositing, + disabled: + approving || + depositing || + !depositAmount || + parseFloat(depositAmount) == 0 || + !!inputError, + async onClick() { + await handleApproveAndDeposit(); + }, + }, + ], +}; + +const MODAL_STEP_2: ModalStep = { + children: ({ depositAmount, updatedSafe }) => ( + + + + + {commify(depositAmount)}{" "} + {updatedSafe ? ( + + ) : null} + + + Successfully deposited + + + + ), + buttons: ({ onClose, resetStepIndex }) => [ + { + children: "Back to Safe", + variant: "neutral", + async onClick() { + resetStepIndex(); + onClose(); + }, + }, + ], +}; + +const MODAL_STEPS: ModalStep[] = [MODAL_STEP_1, MODAL_STEP_2]; + +export { MODAL_STEPS }; +export type { DepositSafeCollateralCtx }; diff --git a/src/components/pages/Turbo/TurboSafePage/modals/TurboInfoModal/TurboInfoModal.tsx b/src/components/pages/Turbo/TurboSafePage/modals/TurboInfoModal/TurboInfoModal.tsx new file mode 100644 index 00000000..729e3067 --- /dev/null +++ b/src/components/pages/Turbo/TurboSafePage/modals/TurboInfoModal/TurboInfoModal.tsx @@ -0,0 +1,66 @@ +import { SimpleGrid } from "@chakra-ui/react"; +import { InfoPairs } from "components/pages/Fuse/Modals/PluginModal/PluginRewardsModal"; +import { useRari } from "context/RariContext"; +import { constants } from "ethers"; +import { commify, formatEther } from "ethers/lib/utils"; +import useCollateralBoostCap from "hooks/turbo/useCollateralBoostCap"; +import { useSafeOwner } from "hooks/turbo/useSafeOwner"; +import { useBalanceOf } from "hooks/useBalanceOf"; +import { SafeInfo } from "lib/turbo/fetchers/safes/getSafeInfo"; +import { FEI } from "lib/turbo/utils/constants"; +import { Modal, Statistic } from "rari-components"; + +export const SafeInfoModal: React.FC<{ + isOpen: any; + onClose: any; + safe: SafeInfo | undefined; +}> = ({ isOpen, onClose, safe }) => { + const { address } = useRari(); + const safeBalanceOfFei = useBalanceOf(safe?.safeAddress, FEI); + const userBalanceOfFei = useBalanceOf(address, FEI); + + const collateralBoostCap = useCollateralBoostCap(safe?.collateralAsset); + const owner = useSafeOwner(safe?.safeAddress); + + return ( + + + + {/* */} + + + + + + + + ); +}; + +export default SafeInfoModal; diff --git a/src/components/pages/Turbo/TurboSafePage/modals/TurboInfoModal/index.ts b/src/components/pages/Turbo/TurboSafePage/modals/TurboInfoModal/index.ts new file mode 100644 index 00000000..004b1cfb --- /dev/null +++ b/src/components/pages/Turbo/TurboSafePage/modals/TurboInfoModal/index.ts @@ -0,0 +1,2 @@ +import TurboInfoModal from './TurboInfoModal' +export default TurboInfoModal diff --git a/src/components/pages/Turbo/TurboSafePage/modals/WithdrawSafeCollateralModal/WithdrawSafeCollateralModal.tsx b/src/components/pages/Turbo/TurboSafePage/modals/WithdrawSafeCollateralModal/WithdrawSafeCollateralModal.tsx new file mode 100644 index 00000000..ca4cfb32 --- /dev/null +++ b/src/components/pages/Turbo/TurboSafePage/modals/WithdrawSafeCollateralModal/WithdrawSafeCollateralModal.tsx @@ -0,0 +1,112 @@ +// Hooks +import { useRari } from "context/RariContext"; +// Turbo +import { Modal } from "rari-components"; +import { useMemo, useState } from "react"; +import { handleGenericError } from "utils/errorHandling"; +import { useToast } from "@chakra-ui/react"; +import { MODAL_STEPS } from "./modalSteps"; +import { fetchMaxSafeAmount } from "lib/turbo/utils/fetchMaxSafeAmount"; +import { SafeInteractionMode, useUpdatedSafeInfo } from "hooks/turbo/useUpdatedSafeInfo"; +import { formatEther, parseEther, parseUnits } from "ethers/lib/utils"; +import { USDPricedTurboSafe } from "lib/turbo/fetchers/safes/getUSDPricedSafeInfo"; +import { constants } from "ethers"; +import useSafeMaxAmount from "hooks/turbo/useSafeMaxAmount"; +import { safeWithdraw } from "lib/turbo/transactions/safe"; + +// Todo - reuse Modal Prop Types +type WithdrawSafeCollateralModalProps = { + isOpen: boolean; + onClose: () => void; + safe: USDPricedTurboSafe | undefined; +}; + +export const WithdrawSafeCollateralModal: React.FC< + WithdrawSafeCollateralModalProps +> = ({ isOpen, onClose, safe }) => { + const { address, provider, chainId } = useRari(); + const toast = useToast(); + + const [withdrawalAmount, setWithdrawalAmount] = useState("10"); + const [withdrawing, setWithdrawing] = useState(false); + + const [stepIndex, setStepIndex] = useState(0); + function incrementStepIndex() { + if (stepIndex + 1 !== MODAL_STEPS.length) { + setStepIndex(stepIndex + 1); + } + } + + const updatedSafe = useUpdatedSafeInfo( + { + mode: SafeInteractionMode.WITHDRAW, + safe, + amount: parseUnits(!!withdrawalAmount ? withdrawalAmount : "0", 18) + } + ) + + const maxAmount = useSafeMaxAmount(safe, SafeInteractionMode.WITHDRAW) + + const onClickWithdraw = async () => { + if (!safe) return + try { + setWithdrawing(true); + const tx = await safeWithdraw(safe.safeAddress, address, parseEther(withdrawalAmount), await provider.getSigner()) + await tx.wait(1) + } catch (err) { + handleGenericError(err, toast); + } finally { + setWithdrawing(false); + } + }; + + const onClickMax = async () => { + try { + const maxAmount = await fetchMaxSafeAmount( + provider, + SafeInteractionMode.WITHDRAW, + address, + safe, + chainId ?? 1 + ) + setWithdrawalAmount(formatEther(maxAmount ?? 0)) + } catch (err) { + handleGenericError(err, toast) + } + } + + // Form validation + const inputError: string | undefined = useMemo(() => { + const amount = withdrawalAmount ? withdrawalAmount : '0' + if (parseUnits(amount, 18).gt(maxAmount)) { + return "Cannot withdraw this much!" + } + if (updatedSafe && updatedSafe.collateralAmount.lt(0)) { + return "Cannot withdraw this much!" + } + }, [maxAmount, updatedSafe, withdrawalAmount]) + + + return ( + + ); +}; + +export default WithdrawSafeCollateralModal; diff --git a/src/components/pages/Turbo/TurboSafePage/modals/WithdrawSafeCollateralModal/index.ts b/src/components/pages/Turbo/TurboSafePage/modals/WithdrawSafeCollateralModal/index.ts new file mode 100644 index 00000000..e1b71841 --- /dev/null +++ b/src/components/pages/Turbo/TurboSafePage/modals/WithdrawSafeCollateralModal/index.ts @@ -0,0 +1 @@ +export { default } from "./WithdrawSafeCollateralModal"; diff --git a/src/components/pages/Turbo/TurboSafePage/modals/WithdrawSafeCollateralModal/modalSteps.tsx b/src/components/pages/Turbo/TurboSafePage/modals/WithdrawSafeCollateralModal/modalSteps.tsx new file mode 100644 index 00000000..2e4230cf --- /dev/null +++ b/src/components/pages/Turbo/TurboSafePage/modals/WithdrawSafeCollateralModal/modalSteps.tsx @@ -0,0 +1,138 @@ +import { Box, VStack } from "@chakra-ui/react"; +import { getSafeColor } from "context/TurboSafeContext"; +import { BigNumber } from "ethers"; +import { formatEther } from "ethers/lib/utils"; +import { USDPricedTurboSafe } from "lib/turbo/fetchers/safes/getUSDPricedSafeInfo"; +import { + ModalProps, + Text, + TokenAmountInput, + TokenSymbol, +} from "rari-components"; +import UpdatingStatisticsTable from "../../UpdatingStatisticsTable"; + +type WithdrawSafeCollateralCtx = { + incrementStepIndex(): void; + withdrawalAmount: string; + setWithdrawalAmount(newWithdrawalAmount: string): void; + withdrawing: boolean; + onClickWithdraw(): Promise; + onClose(): void; + onClickMax(): Promise; + maxAmount: BigNumber; + safe?: USDPricedTurboSafe; + updatedSafe?: USDPricedTurboSafe; + inputError?: string | undefined; +}; + +type ModalStep = Omit< + ModalProps, + "ctx" | "isOpen" | "onClose" +>; + +const MODAL_STEP_1: ModalStep = { + title: "Withdraw Collateral", + subtitle: "Withdraw collateral from this safe.", + children: ({ + setWithdrawalAmount, + withdrawalAmount, + onClickMax, + safe, + updatedSafe, + maxAmount, + }) => { + if (!safe) { + return null; + } + + const safeUtilizationValue = (colorScheme: string) => + withdrawalAmount === "" || parseInt(withdrawalAmount) == 0 ? ( + parseFloat(safe?.safeUtilization.toString() ?? "0").toFixed(2) + "%" + ) : ( + + {parseFloat(safe?.safeUtilization.toString() ?? "0").toFixed(2) + "%"}{" "} + + →{" "} + + {parseFloat( + updatedSafe?.safeUtilization.toString() ?? "0" + ).toFixed(2) + "%"} + + + + ); + + return ( + <> + + setWithdrawalAmount(amount || "0")} + tokenAddress={safe.collateralAsset} + onClickMax={onClickMax} + /> + + You can withdraw {formatEther(maxAmount)} {" "} + + + + + + ); + }, + buttons: ({ + withdrawing, + onClickWithdraw, + incrementStepIndex, + onClose, + inputError, + }) => [ + { + children: inputError + ? inputError + : withdrawing + ? "Withdrawing..." + : "Withdraw", + variant: "neutral", + loading: withdrawing, + disabled: !!inputError || withdrawing, + async onClick() { + await onClickWithdraw(); + onClose(); + }, + }, + ], +}; + +const MODAL_STEPS: ModalStep[] = [MODAL_STEP_1]; + +export { MODAL_STEPS }; +export type { WithdrawSafeCollateralCtx }; diff --git a/src/components/pages/Turbo/alerts/AtRiskOfLiquidationAlert.tsx b/src/components/pages/Turbo/alerts/AtRiskOfLiquidationAlert.tsx new file mode 100644 index 00000000..ead40eb5 --- /dev/null +++ b/src/components/pages/Turbo/alerts/AtRiskOfLiquidationAlert.tsx @@ -0,0 +1,35 @@ +import { BigNumber } from 'ethers'; +import { motion } from "framer-motion"; +import React from 'react' +import { + Alert, + AlertIcon, + Box, +} from "@chakra-ui/react" +import { Text } from 'rari-components'; +import theme from 'rari-components/theme'; + +const AtRiskOfLiquidationAlert: React.FC<{ safeHealth: BigNumber | undefined }> = ({ + safeHealth, +}) => { + return ( + + + + + With a {safeHealth?.toNumber()}% utilization, you are at + liquidation risk. Please deposit more collateral, or unboost. + + + + + ); +}; + + +export default AtRiskOfLiquidationAlert + diff --git a/src/components/pages/Turbo/alerts/BoostMeAlert.tsx b/src/components/pages/Turbo/alerts/BoostMeAlert.tsx new file mode 100644 index 00000000..1a4b20ff --- /dev/null +++ b/src/components/pages/Turbo/alerts/BoostMeAlert.tsx @@ -0,0 +1,34 @@ +import { BigNumber } from 'ethers'; +import { motion } from "framer-motion"; +import React from 'react' +import { + Alert, + AlertIcon, + Box, +} from "@chakra-ui/react" +import { Text } from 'rari-components'; +import theme from 'rari-components/theme'; + +const BoostMeAlert: React.FC<{ safeHealth: BigNumber | undefined }> = ({ + safeHealth, +}) => { + return ( + + + + + Looks like you have collateral deposited but a {safeHealth?.toNumber()}% utilization. Boost some strategies! + + + + + ); +}; + + +export default BoostMeAlert + diff --git a/src/components/shared/AdminAlert.tsx b/src/components/shared/AdminAlert.tsx index 0f7c536c..02f6461e 100644 --- a/src/components/shared/AdminAlert.tsx +++ b/src/components/shared/AdminAlert.tsx @@ -29,7 +29,7 @@ export const AdminAlert = ({ return ( {name} @@ -84,7 +85,6 @@ export const HeaderLink = ({ href={route} whiteSpace="nowrap" className={noUnderline ? "no-underline" : ""} - borderRadius="sm" {...getHeaderLinkStyleProps(isOnThisRoute)} > {name} @@ -95,7 +95,8 @@ export const HeaderLink = ({ const splitHairs = (path: string) => path .replace(/\/+$/, "") - .split("/") + // Split on `/` or `?` + .split(/\/|\?/) .filter((str) => !!str); // Dropdown Header Link @@ -134,7 +135,6 @@ export const DropDownLink = ({ as={Button} rightIcon={} {...getHeaderLinkStyleProps(isOnThisRoute)} - borderRadius="sm" > {name} @@ -159,7 +159,7 @@ const DropdownItem = ({ link }: { link: DropDownLinkInterface }) => { return ( - {name} + {name} ); @@ -169,7 +169,13 @@ const DropdownMenuGroup = ({ menuItem }: { menuItem: MenuItemInterface }) => { return ( <> - + {menuItem.links?.map((link, i) => ( ))} diff --git a/src/components/shared/Header2/NewHeader.tsx b/src/components/shared/Header2/NewHeader.tsx index 94b01973..e94c9dfb 100644 --- a/src/components/shared/Header2/NewHeader.tsx +++ b/src/components/shared/Header2/NewHeader.tsx @@ -1,5 +1,6 @@ import { PixelSize, Row } from "lib/chakraUtils"; import { Box, Flex, Icon, Spacer, useDisclosure } from "@chakra-ui/react"; +import { Link } from "rari-components/standalone"; // Components import DashboardBox, { DASHBOARD_BOX_SPACING } from "../DashboardBox"; @@ -54,9 +55,9 @@ export const NewHeader = () => { mb={5} // bg="pink" > - + - + {isMobile ? null : ( { + const prefersReducedMotion = usePrefersReducedMotion(); + return ( + <> + + + + + + + + + + + ); +}; + +export default TurboEngineIcon; diff --git a/src/components/shared/Layout/Layout.tsx b/src/components/shared/Layout/Layout.tsx index 9d2be4b8..eb58f033 100644 --- a/src/components/shared/Layout/Layout.tsx +++ b/src/components/shared/Layout/Layout.tsx @@ -3,23 +3,34 @@ import { useRari } from "context/RariContext"; import { ChainID } from "esm/utils/networks"; import { Column } from "lib/chakraUtils"; +import { useRouter } from "next/router"; +import theme from "rari-components/theme"; import { useMemo, useState, useEffect } from "react"; import NewHeader from "../Header2/NewHeader"; import Footer from "./Footer"; +const { darkmatte } = theme.colors; const Layout = ({ children }) => { const { chainId } = useRari() + const { pathname } = useRouter(); const bg = useMemo(() => { + // Use the rari-components theme background color on `/turbo` pages to match + // with the other rari-components colors (Turbo is the first project to use + // rari-components from the start — elsewhere on the app, we are replacing + // components incrementally and the original `black` background is more + // appropriate). + const baseBg = pathname.startsWith("/turbo") ? darkmatte : "black"; + switch (chainId) { case ChainID.ARBITRUM: - return "linear-gradient(45deg, hsla(0, 0%, 0%, 1) 76%, hsla(220, 47%, 36%, 0.9) 100%);" + return `linear-gradient(45deg, ${baseBg} 76%, hsla(220, 47%, 36%, 0.9) 100%)`; default: - return 'black' + return baseBg } - }, [chainId]) + }, [chainId, pathname]) const [showShader, setShowShader] = useState(false); diff --git a/src/constants/homepage.ts b/src/constants/homepage.ts index 19c82f76..0ed0a328 100644 --- a/src/constants/homepage.ts +++ b/src/constants/homepage.ts @@ -95,6 +95,7 @@ export enum HomepageOpportunityType { Arbitrum, Connext, PegExchanger, + Turbo, } export interface HomepageOpportunity { @@ -127,12 +128,13 @@ export const HOMEPAGE_OPPORTUNIES: HomepageOpportunity[] = [ icon: "/static/icons/arbitrum_icon_glow.png", }, { - type: HomepageOpportunityType.PegExchanger, - title: "Swap RGT for TRIBE", - subtitle: "Swap your RGT for TRIBE post-merge.", - bgColor: "#DD6829", - icon: "/static/icons/tribe_swap.svg", + type: HomepageOpportunityType.Turbo, + title: "Tribe Turbo", + subtitle: "Earn costless yield on unutilized assets.", + bgColor: "#EFEFEF", + icon: "/static/turbo/turbo-engine-green.svg", vaultType: Pool.USDC, + textColor: "black" }, { type: HomepageOpportunityType.FusePool, diff --git a/src/constants/nav.ts b/src/constants/nav.ts index 6b8f61f6..fd58babe 100644 --- a/src/constants/nav.ts +++ b/src/constants/nav.ts @@ -6,6 +6,10 @@ import { ChainID } from "esm/utils/networks"; export const PRODUCTS_DROPDOWN_ITEMS: MenuItemInterface[] = [ { type: MenuItemType.LINK, link: { name: "Fuse", route: "/fuse" } }, + { + type: MenuItemType.LINK, + link: { name: "Turbo", route: "/turbo" }, + }, // { type: MenuItemType.LINK, link: { name: "Vaults", route: "/vaults" } }, { type: MenuItemType.LINK, @@ -43,11 +47,18 @@ export const UTILS_DROPDOWN_ITEMS: MenuItemInterface[] = [ }, { type: MenuItemType.LINK, - link: { name: "Metrics (Mainnet)", route: "https://rari.grafana.net/goto/61kctV_Gk" }, + link: { + name: "Metrics (Mainnet)", + route: "https://rari.grafana.net/goto/61kctV_Gk", + }, }, { type: MenuItemType.LINK, - link: { name: "Metrics (Arbitrum)", route: "https://metrics.rari.capital/d/BOdF7Hbnk/fuse-overview-arbitrum?orgId=1&refresh=5m&from=now-5m&to=now" }, + link: { + name: "Metrics (Arbitrum)", + route: + "https://metrics.rari.capital/d/BOdF7Hbnk/fuse-overview-arbitrum?orgId=1&refresh=5m&from=now-5m&to=now", + }, }, { type: MenuItemType.LINK, @@ -69,7 +80,11 @@ export const UTILS_DROPDOWN_ITEMS_ARBITRUM: MenuItemInterface[] = [ }, { type: MenuItemType.LINK, - link: { name: "Metrics", route: "https://metrics.rari.capital/d/BOdF7Hbnk/fuse-overview-arbitrum?orgId=1&refresh=5m&from=now-5m&to=now" }, + link: { + name: "Metrics", + route: + "https://metrics.rari.capital/d/BOdF7Hbnk/fuse-overview-arbitrum?orgId=1&refresh=5m&from=now-5m&to=now", + }, }, { type: MenuItemType.LINK, @@ -80,7 +95,8 @@ export const UTILS_DROPDOWN_ITEMS_ARBITRUM: MenuItemInterface[] = [ }, ]; - export const UtilLinks = (chainId: number) => { - return chainId === ChainID.ETHEREUM ? UTILS_DROPDOWN_ITEMS : UTILS_DROPDOWN_ITEMS_ARBITRUM -} \ No newline at end of file + return chainId === ChainID.ETHEREUM + ? UTILS_DROPDOWN_ITEMS + : UTILS_DROPDOWN_ITEMS_ARBITRUM; +}; diff --git a/src/context/BalancesContext.tsx b/src/context/BalancesContext.tsx index 1d06711c..42a176ab 100644 --- a/src/context/BalancesContext.tsx +++ b/src/context/BalancesContext.tsx @@ -28,19 +28,22 @@ export const BalancesContextProvider = ({ revalidateOnReconnect: false, }); - const tokenAddresses = useMemo( - () => - underlyingAssets?.length ? underlyingAssets.map((asset) => asset.id) : [], - [underlyingAssets] - ); + const tokenAddresses = !!underlyingAssets + ? underlyingAssets.map((asset) => asset.id) + : []; const tokenBalances = useTokenBalances(tokenAddresses); - const balances: { + type BalancesMap = { [address: string]: number; - } = useMemo(() => { - let ret: { [address: string]: number } = {}; - if (!isAuthed) return ret; + }; + + const balances: BalancesMap = useMemo(() => { + let ret: BalancesMap = {}; + + if (!isAuthed) { + return ret; + } for (let i = 0; i < tokenBalances.length; i++) { const asset = underlyingAssets![i]; diff --git a/src/context/RariContext.tsx b/src/context/RariContext.tsx index f4716c65..aa221974 100644 --- a/src/context/RariContext.tsx +++ b/src/context/RariContext.tsx @@ -25,7 +25,7 @@ import { initFuseWithProviders, } from "../utils/web3Providers"; -import { Web3Provider } from "@ethersproject/providers"; +import { Web3Provider, JsonRpcProvider } from "@ethersproject/providers"; import { ChainID, isSupportedChainId, @@ -96,6 +96,7 @@ export interface RariContextData { login: (cacheProvider?: boolean) => Promise; logout: () => any; address: string; + provider: JsonRpcProvider | Web3Provider isAttemptingLogin: boolean; chainId: number | undefined; switchNetwork: (newChainId: number, router: any) => void; @@ -251,11 +252,32 @@ export const RariProvider = ({ children }: { children: ReactNode }) => { [setWeb3ModalProvider, setRariAndAddressFromModal, setIsAttemptingLogin, t] ); - const refetchAccountData = useCallback(() => { + const refetchAccountData = useCallback(async () => { + setSwitchingNetwork(true); console.log("New account, clearing the queryClient! ChainId: ", chainId); + + // Get Web3 Provider const provider = chooseBestWeb3Provider(); - setProvider(provider); + + // Initiate fuse and set global account/address form provider const fuseInstance = initFuseWithProviders(provider, chainId); + fuseInstance.provider.listAccounts().then((addresses: string[]) => { + if (addresses.length === 0) { + console.log("Address array was empty. Reloading!"); + router.reload(); + } + + const address = addresses[0]; + const requestedAddress = router.query.address as string; + + console.log("Setting Logrocket user to new address: " + address); + LogRocket.identify(address); + + console.log("Requested address: ", requestedAddress); + setAddress(requestedAddress ?? address); + }); + + // Set Fuse setFuse(fuseInstance); setSwitchingNetwork(false); }, [setRariAndAddressFromModal, web3ModalProvider, queryClient, chainId]); @@ -359,6 +381,7 @@ export const RariProvider = ({ children }: { children: ReactNode }) => { isAuthed: address !== EmptyAddress, login, logout, + provider, address, isAttemptingLogin, chainId, diff --git a/src/context/TurboSafeContext.tsx b/src/context/TurboSafeContext.tsx new file mode 100644 index 00000000..728dbe86 --- /dev/null +++ b/src/context/TurboSafeContext.tsx @@ -0,0 +1,155 @@ +import { BigNumber } from "ethers"; +import { formatEther, formatUnits } from "ethers/lib/utils"; +import useSafeAvgAPY from "hooks/turbo/useSafeAvgAPY"; +import { useSafeInfo } from "hooks/turbo/useSafeInfo"; +import { useUserIsSafeOwner } from "hooks/turbo/useSafeOwner"; +import useShouldBoostSafe from "hooks/turbo/useShouldBoostSafe"; +import { + StrategyInfosMap, + useERC4626StrategiesDataAsMap, +} from "hooks/turbo/useStrategyInfo"; +import { useUserFeiOwed } from "hooks/turbo/useUserFeiOwed"; +import { TokenData, useTokenData } from "hooks/useTokenData"; +import { SafeInfo } from "lib/turbo/fetchers/safes/getSafeInfo"; +import { USDPricedTurboSafe } from "lib/turbo/fetchers/safes/getUSDPricedSafeInfo"; +import { + filterUsedStrategies, + StrategyInfo, +} from "lib/turbo/fetchers/strategies/formatStrategyInfo"; +import { createContext, useContext, ReactNode, useMemo } from "react"; + +export const getSafeColor = (safeHealth: BigNumber | undefined) => { + console.log({safeHealth}) + return safeHealth?.lte(40) + ? "#4DD691" + : safeHealth?.lte(60) + ? "#4DD691" + : safeHealth?.lte(80) + ? "orange" + : safeHealth?.isZero() + ? "grey" + : "#DB6464"; +}; + +type TurboSafeContextData = { + // Raw safe data from Lens + safe: SafeInfo | undefined; + + // USD Priced safe Data from lens + usdPricedSafe: USDPricedTurboSafe | undefined; + + // Fetched token data about collateral asset + collateralTokenData: TokenData | undefined; + + // Loading state + loading: boolean; + + // List of Active Strats + activeStrategies: StrategyInfo[]; + + // Fuse ERC 4626 Data about strategies + getERC4626StrategyData: StrategyInfosMap; + + // Net APY of safe + netAPY: number; + + // bools + isAtLiquidationRisk: boolean; + shouldBoost: boolean; + isUserAdmin: boolean; + + // Colors to display based on safe Health + colorScheme: string; + + totalFeiOwed: BigNumber; +}; + +export const TurboSafeContext = createContext( + undefined +); + +export const TurboSafeProvider = ({ + safeAddress, + children, +}: { + safeAddress: string; + children: ReactNode; +}) => { + /** General Safe Data **/ + const safe = useSafeInfo(safeAddress); + const collateralTokenData = useTokenData(safe?.collateralAsset); + const isUserAdmin = useUserIsSafeOwner(safeAddress); + const loading = !collateralTokenData || !safe; + + // Strategies + const activeStrategies = filterUsedStrategies(safe?.strategies); + const getERC4626StrategyData = useERC4626StrategiesDataAsMap( + safe?.strategies.map((strat) => strat.strategy) ?? [] + ); + + // Average APY across all active Fuse strategies + const netAPY = useSafeAvgAPY( + activeStrategies, + getERC4626StrategyData, + parseFloat(formatEther(safe?.tribeDAOFee ?? 0)) + ); + + /** Safe metadata **/ + const isAtLiquidationRisk = safe?.safeUtilization?.gt(80) ?? false; + const shouldBoost = useShouldBoostSafe(safe); + const safeHealth = safe?.safeUtilization; + const colorScheme = useMemo( + () => getSafeColor(safe?.safeUtilization), + [safeHealth] + ); + + const [totalFeiOwed] = useUserFeiOwed(safe); + + /** Boost / Less **/ + + const value = useMemo( + () => ({ + safe, + usdPricedSafe: safe, + collateralTokenData, + loading, + activeStrategies, + getERC4626StrategyData, + netAPY, + isAtLiquidationRisk, + colorScheme, + shouldBoost, + isUserAdmin, + totalFeiOwed, + }), + [ + safe, + collateralTokenData, + loading, + activeStrategies, + getERC4626StrategyData, + netAPY, + isAtLiquidationRisk, + colorScheme, + shouldBoost, + isUserAdmin, + totalFeiOwed, + ] + ); + + return ( + + {children} + + ); +}; + +export const useTurboSafe = () => { + const turboSafeData = useContext(TurboSafeContext); + + if (turboSafeData === undefined) { + throw new Error(`useTurboSafe must be used within a TurboSafeProvider`); + } + + return turboSafeData; +}; diff --git a/src/esm/utils/networks.js b/src/esm/utils/networks.js index 4929a457..bfe6ead8 100644 --- a/src/esm/utils/networks.js +++ b/src/esm/utils/networks.js @@ -81,15 +81,15 @@ export const chainMetadata = { blockExplorerURL: "https://optimistic.etherscan.io", color: "#FE0521", }, - // [ChainID.HARDHAT]: { - // chainId: ChainID.HARDHAT, - // name: "Hardhat", - // imageUrl: "/static/networks/optimism.svg", // no logo - // supported: true, - // rpcUrl: "http://localhost:8545", - // blockExplorerURL: "", - // color: "#BC6C6C" - // } + [ChainID.HARDHAT]: { + chainId: ChainID.HARDHAT, + name: "Hardhat", + imageUrl: "/static/networks/optimism.svg", // no logo + supported: true, + rpcUrl: "http://localhost:8545", + blockExplorerURL: "", + color: "#BC6C6C" + } }; export const isSupportedChainId = (chainId) => Object.values(ChainID).includes(chainId); export function getSupportedChains() { diff --git a/src/hooks/homepage/useOpportunitySubtitle.ts b/src/hooks/homepage/useOpportunitySubtitle.ts index 914d99be..88436825 100644 --- a/src/hooks/homepage/useOpportunitySubtitle.ts +++ b/src/hooks/homepage/useOpportunitySubtitle.ts @@ -15,7 +15,7 @@ import { useRari } from "context/RariContext"; export const useOpportunitySubtitle = ( opportunity: HomepageOpportunity ): string | null => { - const { fuse, chainId } = useRari() + const { fuse, chainId } = useRari(); // // Earn // const earnPoolAPY = usePoolAPY(opportunity.vaultType); // const poolInfos = usePoolInfos(); @@ -24,63 +24,68 @@ export const useOpportunitySubtitle = ( // // Fuse // const fusePoolData = useFusePoolData(opportunity.fusePoolId?.toString()); // const { data: fuseTVL } = useFuseTVL(); - switch (opportunity.type) { - case HomepageOpportunityType.EarnVault: - { - const earnPoolAPY = usePoolAPY(opportunity.vaultType); - return earnPoolAPY ? `${earnPoolAPY}% APY` : null; - } - case HomepageOpportunityType.FusePool: { - const fusePoolData = useFusePoolData(opportunity.fusePoolId?.toString()); - - switch (opportunity.fuseMetric) { - case FusePoolMetric.TotalBorrowedUSD: - return fusePoolData?.totalBorrowedUSD - ? `${shortUsdFormatter( - fusePoolData.totalBorrowedUSD.toNumber() - )} borrowed` - : null; - case FusePoolMetric.TotalSuppliedUSD: - return fusePoolData?.totalSuppliedUSD - ? `${shortUsdFormatter( - fusePoolData.totalSuppliedUSD.toNumber() - )} supplied` - : null; - case FusePoolMetric.TotalLiquidityUSD: - return fusePoolData?.totalLiquidityUSD - ? `${shortUsdFormatter( - fusePoolData.totalLiquidityUSD.toNumber() - )} liquidity` - : null; - default: - return fusePoolData?.totalSuppliedUSD - ? `${shortUsdFormatter( - fusePoolData.totalSuppliedUSD.toNumber() - )} supplied` - : null; - } - } - case HomepageOpportunityType.EarnPage: { - const poolInfos = usePoolInfos(); - const poolsAPY = usePoolsAPY(poolInfos); - // @ts-ignore - const apys = poolsAPY.filter((obj) => obj).map(parseFloat); - const maxAPY = !!apys.length ? Math.max.apply(null, apys) : null; - return maxAPY ? `${maxAPY}% APY` : null; - } - case HomepageOpportunityType.FusePage: { - const { data: fuseTVL } = useFuseTVL(); - return (fuseTVL !== undefined || fuseTVL !== null) ? `${shortUsdFormatter(fuseTVL!)} TVL` : null; + switch (opportunity.type) { + case HomepageOpportunityType.EarnVault: { + const earnPoolAPY = usePoolAPY(opportunity.vaultType); + return earnPoolAPY ? `${earnPoolAPY}% APY` : null; + } + case HomepageOpportunityType.FusePool: { + const fusePoolData = useFusePoolData(opportunity.fusePoolId?.toString()); + + switch (opportunity.fuseMetric) { + case FusePoolMetric.TotalBorrowedUSD: + return fusePoolData?.totalBorrowedUSD + ? `${shortUsdFormatter( + fusePoolData.totalBorrowedUSD.toNumber() + )} borrowed` + : null; + case FusePoolMetric.TotalSuppliedUSD: + return fusePoolData?.totalSuppliedUSD + ? `${shortUsdFormatter( + fusePoolData.totalSuppliedUSD.toNumber() + )} supplied` + : null; + case FusePoolMetric.TotalLiquidityUSD: + return fusePoolData?.totalLiquidityUSD + ? `${shortUsdFormatter( + fusePoolData.totalLiquidityUSD.toNumber() + )} liquidity` + : null; + default: + return fusePoolData?.totalSuppliedUSD + ? `${shortUsdFormatter( + fusePoolData.totalSuppliedUSD.toNumber() + )} supplied` + : null; } - case HomepageOpportunityType.Arbitrum: - return "Now live!"; + } + case HomepageOpportunityType.EarnPage: { + const poolInfos = usePoolInfos(); + const poolsAPY = usePoolsAPY(poolInfos); + // @ts-ignore + const apys = poolsAPY.filter((obj) => obj).map(parseFloat); + const maxAPY = !!apys.length ? Math.max.apply(null, apys) : null; + return maxAPY ? `${maxAPY}% APY` : null; + } + case HomepageOpportunityType.FusePage: { + const { data: fuseTVL } = useFuseTVL(); + return fuseTVL !== undefined || fuseTVL !== null + ? `${shortUsdFormatter(fuseTVL!)} TVL` + : null; + } + case HomepageOpportunityType.Arbitrum: + return "Now live!"; + + case HomepageOpportunityType.Connext: + return "via Connext"; - case HomepageOpportunityType.Connext: - return "via Connext"; + case HomepageOpportunityType.PegExchanger: + return "Join the Tribe"; - case HomepageOpportunityType.PegExchanger: - return "Join the Tribe"; + case HomepageOpportunityType.Turbo: + return 'Tribe x Rari' - default: - return null; -}}; + default: + return null; + } +}; diff --git a/src/hooks/rewards/useRewardAPY.ts b/src/hooks/rewards/useRewardAPY.ts index dc170400..196106a5 100644 --- a/src/hooks/rewards/useRewardAPY.ts +++ b/src/hooks/rewards/useRewardAPY.ts @@ -25,6 +25,7 @@ import { import { convertMantissaToAPR, convertMantissaToAPY } from "utils/apyUtils"; import { constants, BigNumber } from "ethers"; import { getEthUsdPriceBN } from "esm/utils/getUSDPriceBN"; +import { formatEther } from "ethers/lib/utils"; // ( ( rewardSupplySpeed * rewardEthPrice ) / ( underlyingTotalSupply * underlyingEthPrice / 1e18 / 1e18 ) ) // ( @@ -315,14 +316,13 @@ export const getPriceFromOracles = async ( comptroller: string, fuse: Fuse, isAuthed: boolean -) => { +): Promise => { // Rari MPO const masterPriceOracle = createMasterPriceOracle(fuse, true); // Pool's MPO const comptrollerInstance = useCreateComptroller(comptroller, fuse, isAuthed); const oracleAddress: string = await comptrollerInstance.callStatic.oracle(); - // const oracleModel: string | undefined = await fuse.getPriceOracle(oracle); const oracleContract = createOracle( oracleAddress, fuse, @@ -377,7 +377,7 @@ export const useAssetPricesInEth = ( // Calc usd prices for (let i = 0; i < tokenAddresses.length; i++) { - const priceInEth = parseFloat(tokenPricesInEth[i]); + const priceInEth = parseFloat(tokenPricesInEth[i].toString()); const tokenData = tokensData[tokenAddresses[i]]; const decimals = tokenData?.decimals ?? 18; @@ -397,7 +397,7 @@ export const useAssetPricesInEth = ( for (let i = 0; i < tokenAddresses.length; i++) { const tokenAddress = tokenAddresses[i]; const usdPrice = tokenUSDPrices[i]; - const ethPrice = parseFloat(tokenPricesInEth[i]); + const ethPrice = parseFloat(tokenPricesInEth[i].toString()); tokenPrices[tokenAddress] = { ethPrice, usdPrice, diff --git a/src/hooks/turbo/test/mocks.ts b/src/hooks/turbo/test/mocks.ts new file mode 100644 index 00000000..566e825a --- /dev/null +++ b/src/hooks/turbo/test/mocks.ts @@ -0,0 +1,123 @@ +import { BigNumber } from "ethers"; +import { + USDPricedStrategy, + USDPricedTurboSafe, +} from "lib/turbo/fetchers/safes/getUSDPricedSafeInfo"; +import { StrategyInfo } from "lib/turbo/fetchers/strategies/formatStrategyInfo"; +import { FuseERC4626Strategy } from "../useStrategyInfo"; + +const MOCK_STRATEGY_1: StrategyInfo = { + strategy: "0xaC4c093c777581DC9c4DC935394Ff11e6c58CD45", + boostedAmount: BigNumber.from("0x01a784379d99db42000000"), + feiAmount: BigNumber.from("0x01a784384483c3a3ebd483"), + feiEarned: BigNumber.from("0x0001a784384483c3a3ebd4"), + feiClaimable: BigNumber.from("0x0001a784384483c3a3ebd4"), +}; + +const MOCK_STRATEGY_2: StrategyInfo = { + // Randomly generated address + strategy: "0x3E556610757D238c7c806bBE04536a05828f474b", + boostedAmount: BigNumber.from("0x01a784379d99db42000000"), + feiAmount: BigNumber.from("0x01a784384483c3a3ebd483"), + feiEarned: BigNumber.from("0x0001a784384483c3a3ebd4"), + feiClaimable: BigNumber.from("0x0001a784384483c3a3ebd4"), +}; + +const MOCK_USD_PRICED_STRATEGY_1: USDPricedStrategy = { + ...MOCK_STRATEGY_1, + boostAmountUSD: 50, + feiAmountUSD: 50, + feiEarnedUSD: 10, + feiClaimableUSD: 10, + +}; + +const MOCK_USD_PRICED_STRATEGY_2: USDPricedStrategy = { + ...MOCK_STRATEGY_2, + boostAmountUSD: 50, + feiAmountUSD: 50, + feiEarnedUSD: 10, + feiClaimableUSD: 10, +}; + +const MOCK_ERC4626_STRATEGY_1: FuseERC4626Strategy = { + underlying: "0xc7283b66Eb1EB5FB86327f08e1B5816b0720212B", + name: "Fei USD", + symbol: "FEI", + // Randomly generated address + fToken: "0xc7283b66Eb1EB5FB86327f08e1B5816b0720212B", + // Randomly generated address + comptroller: "0x517F73a1329330c469BA1446BA248aEAa3b3a883", + supplyRatePerBlock: 100, +}; + +const MOCK_SAFE_1: USDPricedTurboSafe = { + safeAddress: "0xCd6442eB75f676671FBFe003A6A6F022CbbB8d38", + collateralAsset: "0xc7283b66Eb1EB5FB86327f08e1B5816b0720212B", + collateralAmount: BigNumber.from("5000001000000000000000000"), + collateralValue: BigNumber.from("1158926336594045961765"), + collateralPrice: BigNumber.from("231785220961765"), + debtAmount: BigNumber.from("2000000000000000000000000"), + debtValue: BigNumber.from("651700000000000000000"), + boostedAmount: BigNumber.from("2000000000000000000000000"), + feiPrice: BigNumber.from("325850000000000"), + feiAmount: BigNumber.from("2000000046982030418498691"), + tribeDAOFee: BigNumber.from("750000000000000000"), + strategies: [MOCK_STRATEGY_1, MOCK_STRATEGY_2], + safeUtilization: BigNumber.from(50), + collateralValueUSD: 100, + collateralPriceUSD: 1, + debtUSD: 50, + boostedUSD: 50, + feiAmountUSD: 50, + feiPriceUSD: 1, + usdPricedStrategies: [MOCK_USD_PRICED_STRATEGY_1, MOCK_USD_PRICED_STRATEGY_2], + maxBoost: BigNumber.from("20000000000000000000000000"), + maxBoostUSD: 20000000000000000000000000, + collateralFactor: BigNumber.from("100"), + liquidationPrice: .01, + liquidationPriceUSD: .32 +}; + +const MOCK_SAFE_2: USDPricedTurboSafe = { + // Randomly generated address + safeAddress: "0x87F5A53A5FBB4085AA4111B531044B4350788E2F", + collateralAsset: "0xc7283b66Eb1EB5FB86327f08e1B5816b0720212B", + collateralAmount: BigNumber.from("5000001000000000000000000"), + collateralValue: BigNumber.from("1158926336594045961765"), + collateralPrice: BigNumber.from("231785220961765"), + debtAmount: BigNumber.from("2000000000000000000000000"), + debtValue: BigNumber.from("651700000000000000000"), + boostedAmount: BigNumber.from("2000000000000000000000000"), + feiPrice: BigNumber.from("325850000000000"), + feiAmount: BigNumber.from("2000000046982030418498691"), + tribeDAOFee: BigNumber.from("750000000000000000"), + strategies: [MOCK_STRATEGY_1], + safeUtilization: BigNumber.from(50), + collateralValueUSD: 100, + collateralPriceUSD: 1, + debtUSD: 50, + boostedUSD: 50, + feiAmountUSD: 50, + feiPriceUSD: 1, + usdPricedStrategies: [ + { + strategy: "0xaC4c093c777581DC9c4DC935394Ff11e6c58CD45", + boostedAmount: BigNumber.from("0x01a784379d99db42000000"), + feiAmount: BigNumber.from("0x01a784384483c3a3ebd483"), + feiEarned: BigNumber.from("0x0001a784384483c3a3ebd4"), + feiClaimable: BigNumber.from("0x0001a784384483c3a3ebd4"), + boostAmountUSD: 50, + feiAmountUSD: 50, + feiEarnedUSD: 10, + feiClaimableUSD: 1 + }, + ], + maxBoost: BigNumber.from("20000000000000000000000000"), + maxBoostUSD: 20000000000000000000000000, + collateralFactor: BigNumber.from("100"), + liquidationPrice: .01, + liquidationPriceUSD: .32 +}; + +export { MOCK_SAFE_1, MOCK_SAFE_2, MOCK_ERC4626_STRATEGY_1 }; diff --git a/src/hooks/turbo/useAggregateSafeData.ts b/src/hooks/turbo/useAggregateSafeData.ts new file mode 100644 index 00000000..805b84a9 --- /dev/null +++ b/src/hooks/turbo/useAggregateSafeData.ts @@ -0,0 +1,82 @@ +import { getEthUsdPriceBN } from 'esm/utils/getUSDPriceBN'; +import { BigNumber, constants } from 'ethers' +import { formatEther } from 'ethers/lib/utils'; +import { useBalancesOfMultipleAddresses } from 'hooks/useBalanceOf'; +import { SafeInfo } from 'lib/turbo/fetchers/safes/getSafeInfo'; +import { filterUsedStrategies } from 'lib/turbo/fetchers/strategies/formatStrategyInfo'; +import { FEI } from 'lib/turbo/utils/constants'; +import { getUserFeiOwed, getUserFeiOwedWithBalance } from 'lib/turbo/utils/getUserFeiOwed'; +import { calculateETHValueUSD, calculateFEIValueUSD } from 'lib/turbo/utils/usdUtils'; +import { useQuery } from 'react-query'; +import { convertMantissaToAPY } from 'utils/apyUtils'; +import { getStrategiesAvgAPY } from './useSafeAvgAPY'; +import { StrategyInfosMap } from './useStrategyInfo'; + +type AggregateSafeData = { + totalBoosted: BigNumber; + totalClaimable: BigNumber; + totalClaimableUSD: number; + netAPY: number; +} + +const EMPTY_SAFE_DATA = { + totalBoosted: constants.Zero, + totalClaimable: constants.Zero, + totalClaimableUSD: 0, + netAPY: 0 +} + +const useAggregateSafeData = ( + safes: SafeInfo[], + getERC4626StrategyData: StrategyInfosMap +): AggregateSafeData => { + + const balancesFEI = useBalancesOfMultipleAddresses(safes.map(safe => safe.safeAddress), FEI) + console.log({ balancesFEI }) + + const { data } = useQuery( + `user aggregate safes ${safes.map(safe => safe.safeAddress).join(', ')}`, + async () => { + const ethUSD = await getEthUsdPriceBN() + const feiPrice = await safes[0]?.feiPrice ?? constants.WeiPerEther + + const numActiveSafes = safes.reduce((acc, safe) => { + return safe.boostedAmount.isZero() ? acc : acc + 1 + }, 0) + + const totalBoosted = safes.reduce((acc, safe) => { + return acc.add(safe.boostedAmount); + }, constants.Zero) + + let totalClaimable = safes.reduce((acc, safe) => { + return acc.add(getUserFeiOwedWithBalance(safe, balancesFEI[safe.safeAddress])); + }, constants.Zero) + + let totalClaimableUSD = calculateFEIValueUSD(totalClaimable, feiPrice, ethUSD) + + let netAPY = !numActiveSafes ? 0 : safes.reduce((acc, safe) => { + const apy = getStrategiesAvgAPY( + filterUsedStrategies(safe.strategies), + getERC4626StrategyData, + parseFloat(formatEther(safe?.tribeDAOFee ?? 0)) + ) + return (acc + apy) / numActiveSafes; + }, 0) + + const obj: AggregateSafeData = { + totalBoosted, + totalClaimable, + totalClaimableUSD, + netAPY + } + + console.log({ obj }) + + return obj + + }) + + return data ?? EMPTY_SAFE_DATA +} + +export default useAggregateSafeData \ No newline at end of file diff --git a/src/hooks/turbo/useAllSafes.ts b/src/hooks/turbo/useAllSafes.ts new file mode 100644 index 00000000..3ed787d6 --- /dev/null +++ b/src/hooks/turbo/useAllSafes.ts @@ -0,0 +1,21 @@ +import { useQuery } from "react-query"; +import { useRari } from "context/RariContext"; +import { createTurboSafe } from "lib/turbo/utils/turboContracts"; +import { getAllSafes } from "lib/turbo/fetchers/safes/getAllSafes"; + +// Trusted Strategies will be independent of any Safe and whitelisted by TRIBE Governance +export const useAllSafes = (): any => { + const { provider, chainId } = useRari(); + + const { data: safeOwner } = useQuery( + `All safes`, + async () => { + if (!provider || !chainId) return; + const answer = await getAllSafes(provider, chainId) + console.log(answer) + return answer + } + ); + + return safeOwner +}; \ No newline at end of file diff --git a/src/hooks/turbo/useApprovedCollateral.ts b/src/hooks/turbo/useApprovedCollateral.ts new file mode 100644 index 00000000..8fc47ce6 --- /dev/null +++ b/src/hooks/turbo/useApprovedCollateral.ts @@ -0,0 +1,13 @@ +import { useRari } from "context/RariContext"; +import { getTurboApprovedCollateral } from "lib/turbo/fetchers/getApprovedCollaterals"; +import { useQuery } from "react-query"; + +const useApprovedCollateral = () => { + const { provider } = useRari(); + const { data } = useQuery("Approved Turbo Collateral", async () => { + return await getTurboApprovedCollateral(provider); + }); + return data ?? []; +}; + +export default useApprovedCollateral; diff --git a/src/hooks/turbo/useBoostCapsForStrategies.ts b/src/hooks/turbo/useBoostCapsForStrategies.ts new file mode 100644 index 00000000..9e6156b0 --- /dev/null +++ b/src/hooks/turbo/useBoostCapsForStrategies.ts @@ -0,0 +1,31 @@ +import { useRari } from "context/RariContext" +import { getBoostCapForStrategy, getBoostCapsForStrategies } from "lib/turbo/fetchers/strategies/getBoostCapsForStrategies" +import { useQuery } from "react-query" + +const useBoostCapsForStrategies = (strategies: string[]) => { + const { provider } = useRari(); + + const { data } = useQuery( + `Boost caps for strategies ${strategies.join(', ')}`, + async () => await getBoostCapsForStrategies(provider, strategies) + + ) + + return data +} + +export const useBoostCapForStrategy = (strategy: string | undefined) => { + const { provider } = useRari(); + + const { data } = useQuery( + `Boost caps for strategy ${strategy}`, + async () => { + if (!strategy) return + return await getBoostCapForStrategy(provider, strategy) + } + ) + + return data +} + +export default useBoostCapsForStrategies \ No newline at end of file diff --git a/src/hooks/turbo/useCollateralBoostCap.ts b/src/hooks/turbo/useCollateralBoostCap.ts new file mode 100644 index 00000000..504106d2 --- /dev/null +++ b/src/hooks/turbo/useCollateralBoostCap.ts @@ -0,0 +1,26 @@ +import { useRari } from "context/RariContext"; +import { BigNumber } from "ethers"; +import { createTurboBooster } from "lib/turbo/utils/turboContracts"; +import { useQuery } from "react-query"; + +const useCollateralBoostCap = (token: string | undefined) => { + const { provider } = useRari(); + const { data: boostCap } = useQuery( + "Turbo collateral boost cap for asset " + token, + async () => { + if (!token) return; + return await getBoostCapForCollateral(provider, token); + } + ); + return boostCap +}; + +const getBoostCapForCollateral = async ( + provider: any, + token: string +): Promise => { + const Booster = createTurboBooster(provider, 1); + return await Booster.callStatic.getBoostCapForCollateral(token); +}; + +export default useCollateralBoostCap; diff --git a/src/hooks/turbo/useIsUserAuthorizedToCreateSafes.ts b/src/hooks/turbo/useIsUserAuthorizedToCreateSafes.ts new file mode 100644 index 00000000..7ae4e5d2 --- /dev/null +++ b/src/hooks/turbo/useIsUserAuthorizedToCreateSafes.ts @@ -0,0 +1,30 @@ +import { useRari } from "context/RariContext"; +import { isUserAuthorizedToCreateSafes } from "lib/turbo/fetchers/getIsUserAuthorizedToCreateSafes"; +import { TurboAddresses } from "lib/turbo/utils/constants"; +import { useQuery } from "react-query"; + +export const useIsUserAuthorizedToCreateSafes = () => { + if (process.env.NEXT_PUBLIC_USE_MOCKS === "true") { + return true; + } + + const { address, provider, chainId } = useRari(); + + const { data: isAuthorized } = useQuery( + `Is ${address} authorized to create safes`, + async () => { + if (!address || !chainId || !provider) return; + + const isAuthorized = await isUserAuthorizedToCreateSafes( + provider, + TurboAddresses[chainId].TURBO_AUTHORITY, + address, + TurboAddresses[chainId].MASTER + ); + + return isAuthorized; + } + ); + + return isAuthorized; +}; diff --git a/src/hooks/turbo/usePreviewSafes.ts b/src/hooks/turbo/usePreviewSafes.ts new file mode 100644 index 00000000..3603e76a --- /dev/null +++ b/src/hooks/turbo/usePreviewSafes.ts @@ -0,0 +1,36 @@ +import { useQuery } from "react-query"; +import { createTurboMaster } from "lib/turbo/utils/turboContracts"; +import { useRari } from "context/RariContext"; +import { providers } from "@0xsequence/multicall"; +import { useSafesInfo } from "./useSafeInfo"; + +const SAFE_IDS = [1, 2, 1]; + +const usePreviewSafes = () => { + const { provider } = useRari(); + + const { data: safeAddresses } = useQuery( + "Preview safe addresses for indices" + SAFE_IDS.join(","), + async () => { + const multicallProvider = new providers.MulticallProvider(provider); + const TurboMaster = createTurboMaster(multicallProvider); + + const settledPromises = await Promise.allSettled( + SAFE_IDS.map((id) => TurboMaster.callStatic.safes(id)) + ); + + const result: string[] = settledPromises.reduce((acc: string[], obj) => { + if (obj.status === "fulfilled") return [...acc, obj.value]; + return [...acc]; + }, []); + + return result; + } + ); + const safes = useSafesInfo(safeAddresses ?? []); + + console.log({ safes }); + return safes ?? []; +}; + +export default usePreviewSafes; diff --git a/src/hooks/turbo/useSafeAvgAPY.ts b/src/hooks/turbo/useSafeAvgAPY.ts new file mode 100644 index 00000000..cf2f44b7 --- /dev/null +++ b/src/hooks/turbo/useSafeAvgAPY.ts @@ -0,0 +1,49 @@ +import { StrategyInfo } from "lib/turbo/fetchers/strategies/formatStrategyInfo"; +import { useMemo } from "react"; +import { convertMantissaToAPY } from "utils/apyUtils"; +import { StrategyInfosMap } from "./useStrategyInfo"; + +// Returns avg Apy for a safe +const useSafeAvgAPY = ( + activeStrategies: StrategyInfo[], + getERC4626StrategyData: StrategyInfosMap, + tribeDaoFeeShare: number +) => { + const apy = useMemo( + () => + getStrategiesAvgAPY( + activeStrategies, + getERC4626StrategyData, + tribeDaoFeeShare + ), + [activeStrategies, getERC4626StrategyData, tribeDaoFeeShare] + ); + + return apy; +}; + +export const getStrategiesAvgAPY = ( + strategies: StrategyInfo[], + getERC4626StrategyData: StrategyInfosMap, + tribeDaoFeeShare: number +) => { + const numActiveStrategies = strategies.reduce((acc, strategy) => { + return strategy.boostedAmount.isZero() ? acc : acc + 1; + }, 0); + + console.log({ strategies, getERC4626StrategyData, convertMantissaToAPY, numActiveStrategies, tribeDaoFeeShare }); + + return strategies.reduce((num, { strategy }) => { + const erc4626Strategy = getERC4626StrategyData[strategy]; + if (erc4626Strategy && erc4626Strategy.supplyRatePerBlock) { + let apy = convertMantissaToAPY(erc4626Strategy.supplyRatePerBlock, 365); + let userShare = 1 - tribeDaoFeeShare; + let userAPY = apy * userShare; + num += userAPY / numActiveStrategies; + } + + return num; + }, 0); +}; + +export default useSafeAvgAPY; diff --git a/src/hooks/turbo/useSafeInfo.ts b/src/hooks/turbo/useSafeInfo.ts new file mode 100644 index 00000000..0ca0108a --- /dev/null +++ b/src/hooks/turbo/useSafeInfo.ts @@ -0,0 +1,62 @@ +import { useQueries, useQuery, UseQueryResult } from "react-query"; +import { useRari } from "context/RariContext"; +import { + getUSDPricedSafeInfo, + USDPricedTurboSafe, +} from "lib/turbo/fetchers/safes/getUSDPricedSafeInfo"; +import { MOCK_SAFE_1, MOCK_SAFE_2 } from "./test/mocks"; +import { isSupportedChainId } from "esm/utils/networks"; + +export const useSafeInfo = (safe: string): USDPricedTurboSafe | undefined => { + if (process.env.NEXT_PUBLIC_USE_MOCKS === "true") { + return MOCK_SAFE_1; + } + + const { provider, chainId } = useRari(); + + const { data: safeInfo } = useQuery( + `Safe info for: ${safe} chain ${chainId}`, + async () => { + if (!safe || !provider || !chainId || !isSupportedChainId(chainId)) + return; + return await getUSDPricedSafeInfo(provider, safe, chainId); + } + ); + return safeInfo; +}; + +export const useSafesInfo = ( + safes: string[] +): USDPricedTurboSafe[] | undefined => { + const { provider, chainId } = useRari(); + + if (process.env.NEXT_PUBLIC_USE_MOCKS === "true") { + return [MOCK_SAFE_1, MOCK_SAFE_2]; + } + + const safesQueryResult: UseQueryResult[] = + useQueries( + safes.map((safe) => { + return { + queryKey: `SafeInfo for safe ${safe}`, + queryFn: async () => { + if (!safe || !provider || !chainId || !isSupportedChainId(chainId)) + return; + return await getUSDPricedSafeInfo(provider, safe, chainId); + }, + }; + }) + ); + + const result: USDPricedTurboSafe[] = safesQueryResult.reduce( + (obj: USDPricedTurboSafe[], result, i) => { + if (result.data) { + return [...obj, result.data]; + } + return [...obj]; + }, + [] + ); + + return result; +}; diff --git a/src/hooks/turbo/useSafeMaxAmount.ts b/src/hooks/turbo/useSafeMaxAmount.ts new file mode 100644 index 00000000..9d629aaa --- /dev/null +++ b/src/hooks/turbo/useSafeMaxAmount.ts @@ -0,0 +1,37 @@ +import { useRari } from "context/RariContext"; +import { BigNumber, constants } from "ethers"; +import { USDPricedTurboSafe } from "lib/turbo/fetchers/safes/getUSDPricedSafeInfo"; +import { fetchMaxSafeAmount } from "lib/turbo/utils/fetchMaxSafeAmount"; +import { useQuery } from "react-query"; +import { SafeInteractionMode } from "./useUpdatedSafeInfo"; + +const useSafeMaxAmount = ( + safe: USDPricedTurboSafe | undefined, + mode: SafeInteractionMode, + strategyIndex?: number //only used for LESS +) => { + const { provider, address, chainId } = useRari(); + + const { data: maxAmount } = useQuery( + `Safe ${safe?.safeAddress} Max ${mode} amount ${ + !!strategyIndex ? "Strategy " + strategyIndex : null + }`, + async () => { + if (!chainId) return constants.Zero; + + return fetchMaxSafeAmount( + provider, + mode, + address, + safe, + chainId, + strategyIndex, + true, + ); + } + ); + + return maxAmount ?? constants.Zero; +}; + +export default useSafeMaxAmount; diff --git a/src/hooks/turbo/useSafeOwner.ts b/src/hooks/turbo/useSafeOwner.ts new file mode 100644 index 00000000..6b09440c --- /dev/null +++ b/src/hooks/turbo/useSafeOwner.ts @@ -0,0 +1,25 @@ +import { useQuery } from "react-query"; +import { useRari } from "context/RariContext"; +import { createTurboSafe } from "lib/turbo/utils/turboContracts"; + +// Trusted Strategies will be independent of any Safe and whitelisted by TRIBE Governance +export const useSafeOwner = (safeAddress: string | undefined): string => { + const { provider, chainId } = useRari(); + + const { data: safeOwner } = useQuery( + `Safe owner for safe ${safeAddress}`, + async () => { + if (!provider || !chainId || !safeAddress) return; + const safe = createTurboSafe(provider, safeAddress); + return await safe.callStatic.owner(); + } + ); + + return safeOwner; +}; + +export const useUserIsSafeOwner = (safeAddress: string): boolean => { + const { address } = useRari(); + const owner = useSafeOwner(safeAddress); + return !!address && address === owner; +}; diff --git a/src/hooks/turbo/useShouldBoostSafe.ts b/src/hooks/turbo/useShouldBoostSafe.ts new file mode 100644 index 00000000..e807c6ac --- /dev/null +++ b/src/hooks/turbo/useShouldBoostSafe.ts @@ -0,0 +1,11 @@ +import { SafeInfo } from 'lib/turbo/fetchers/safes/getSafeInfo' +import { constants } from "ethers"; + +const useShouldBoostSafe = (safe: SafeInfo | undefined) => { + if (!safe) return false + const boostMe = safe.maxBoost.gt(constants.WeiPerEther.mul(3)) && safe.safeUtilization.lt(10) + return boostMe + +} + +export default useShouldBoostSafe \ No newline at end of file diff --git a/src/hooks/turbo/useStrategyInfo.ts b/src/hooks/turbo/useStrategyInfo.ts new file mode 100644 index 00000000..06cecbe9 --- /dev/null +++ b/src/hooks/turbo/useStrategyInfo.ts @@ -0,0 +1,114 @@ +import { useQueries, useQuery } from "react-query"; +import { useRari } from "context/RariContext"; +import { + createFusePoolDirectory, + createFusePoolLens, + createFusePoolLensSecondary, + ICERC20Delegate, + IFuseERC4626, +} from "lib/turbo/utils/turboContracts"; +import { callInterfaceWithMulticall } from "utils/multicall"; +import { MOCK_ERC4626_STRATEGY_1 } from "./test/mocks"; +import { getStrategyFusePoolId } from "lib/turbo/fetchers/strategies/formatStrategyInfo"; +import { filterPoolName } from "utils/fetchFusePoolData"; + +export interface FuseERC4626Strategy { + underlying: string; + name: string; + symbol: string; + fToken: string; + comptroller: string; + supplyRatePerBlock: number; +} + +// Data Required to render FuseERC4626 strategies +export type StrategyInfosMap = { + [strategyAddress: string]: FuseERC4626Strategy | undefined; +}; + +// Gets ERC4626 strategy data +export const useERC4626StrategyData = (strategy: string) => { + const { provider } = useRari(); + + const { data: strategyInfo } = useQuery( + `Strategy info for: ${strategy}`, + async () => { + if (!strategy) return; + return await fetchStrategyData(provider, strategy); + } + ); + return strategyInfo; +}; + +// Gets ERC4626 strategy info for multiple strategies +export const useERC4626StrategiesDataAsMap = (strategies: string[]) => { + const { provider } = useRari(); + + const strategiesQueryResult = useQueries( + strategies.map((strategy: string, i) => { + return { + queryKey: `Strategy info for: ${strategy}`, + queryFn: () => { + return fetchStrategyData(provider, strategy); + }, + }; + }) + ); + + const result: StrategyInfosMap = strategiesQueryResult.reduce( + (obj: StrategyInfosMap, result, i) => { + obj[strategies[i]] = result.data; + return { ...obj }; + }, + {} + ); + + return result ?? {}; +}; + +export const fetchStrategyData = async ( + provider: any, + strategy: string +): Promise => { + if (process.env.NEXT_PUBLIC_USE_MOCKS === "true") { + return MOCK_ERC4626_STRATEGY_1; + } + + // Get stuff from FuseERC4626 + const [[symbol], [fToken], [underlying], [comptroller]] = + await callInterfaceWithMulticall( + provider, + IFuseERC4626, + strategy, + ["symbol", "cToken", "cTokenUnderlying", "unitroller"], + [[], [], [], []] + ); + + // Get SupplyRate from CERC20 + const [[supplyRatePerBlock]] = await callInterfaceWithMulticall( + provider, + ICERC20Delegate, + fToken, + ["supplyRatePerBlock"], + [[]] + ); + + const poolId = getStrategyFusePoolId(symbol); + + console.log({ poolId , symbol}); + + const dir = createFusePoolDirectory(provider); + const { name } = await dir.callStatic.pools(poolId); + + // Get pool name + let result: FuseERC4626Strategy = { + name: filterPoolName(name), + symbol, + fToken, + underlying, + comptroller, + supplyRatePerBlock: supplyRatePerBlock.toNumber(), + }; + + return result; +}; diff --git a/src/hooks/turbo/useTrustedStrategies.ts b/src/hooks/turbo/useTrustedStrategies.ts new file mode 100644 index 00000000..6991691d --- /dev/null +++ b/src/hooks/turbo/useTrustedStrategies.ts @@ -0,0 +1,18 @@ +import { useQuery } from "react-query"; +import { getBoostableStrategies } from "lib/turbo/fetchers/strategies/getBoostableStrategies"; +import { useRari } from "context/RariContext"; + +// Trusted Strategies will be independent of any Safe and whitelisted by TRIBE Governance +export const useTrustedStrategies = (): string[] => { + const { provider, chainId } = useRari(); + + const { data: trustedStrategies } = useQuery( + `Boostable strategies`, + async () => { + if (!provider || !chainId) return; + return await getBoostableStrategies(provider, chainId); + } + ); + + return trustedStrategies ?? []; +}; diff --git a/src/hooks/turbo/useUpdatedSafeInfo.ts b/src/hooks/turbo/useUpdatedSafeInfo.ts new file mode 100644 index 00000000..8b59485b --- /dev/null +++ b/src/hooks/turbo/useUpdatedSafeInfo.ts @@ -0,0 +1,302 @@ +import { useQuery } from "react-query"; +import { useRari } from "context/RariContext"; +import { BigNumber, constants } from "ethers"; +import { + calculateLiquidationPriceUSD, + USDPricedStrategy, + USDPricedTurboSafe, +} from "lib/turbo/fetchers/safes/getUSDPricedSafeInfo"; +import { + calculateETHValueUSD, + calculateFEIValueUSD, +} from "lib/turbo/utils/usdUtils"; +import { getEthUsdPriceBN } from "esm/utils/getUSDPriceBN"; +import { + calcuateLiquidationPrice, + calculateMaxBoost, + calculateSafeUtilization, +} from "lib/turbo/fetchers/safes/getSafeInfo"; + +export enum SafeInteractionMode { + DEPOSIT = "Deposit", + WITHDRAW = "Withdraw", + BOOST = "Boost", + LESS = "Less", +} + +// Preivews a safe position based on action taken and amount +// TODO(@nathanhleung): Possibly find ways to optimize this query +export const useUpdatedSafeInfo = ({ + mode, + safe, + amount, + strategyIndex, +}: { + mode: SafeInteractionMode; + safe: USDPricedTurboSafe | undefined; + amount: BigNumber; + strategyIndex?: number; +}): USDPricedTurboSafe | undefined => { + const { provider, chainId, fuse } = useRari(); + + const { data: updatedSafeInfo } = useQuery( + `Updated safe info for ${ + safe?.safeAddress + } for mode ${mode} and amount ${amount.toString()}`, + async () => { + if (!provider || !chainId || !safe) return; + + const ethUSDBN = await getEthUsdPriceBN(); + + let updatedSafe: USDPricedTurboSafe; + + /** DEPOSIT **/ + if (mode === SafeInteractionMode.DEPOSIT) { + const collateralAmount = safe.collateralAmount.add(amount); + const collateralValue = collateralAmount + .mul(safe.collateralPrice) + .div(constants.WeiPerEther); + const collateralValueUSD = calculateETHValueUSD( + collateralValue, + ethUSDBN + ); + const safeUtilization = calculateSafeUtilization( + safe.debtValue, + collateralValue, + safe.collateralFactor + ); + const maxBoost = calculateMaxBoost( + collateralValue, + safe.collateralFactor + ); + const maxBoostUSD = calculateETHValueUSD(maxBoost, ethUSDBN); + + const liquidationPrice = calcuateLiquidationPrice( + safe.debtValue, + collateralValue, + safe.collateralFactor, + safe.collateralPrice + ); + + const liquidationPriceUSD = calculateLiquidationPriceUSD( + liquidationPrice, + ethUSDBN + ); + + updatedSafe = { + ...safe, + collateralAmount, + collateralValue, + collateralValueUSD, + safeUtilization, + maxBoost, + maxBoostUSD, + liquidationPrice, + liquidationPriceUSD, + }; + + return updatedSafe; + } + + /** WITHDRAW **/ + if (mode === SafeInteractionMode.WITHDRAW) { + const collateralAmount = safe.collateralAmount.sub(amount); + const collateralValue = collateralAmount + .mul(safe.collateralPrice) + .div(constants.WeiPerEther); + const collateralValueUSD = calculateETHValueUSD( + collateralValue, + ethUSDBN + ); + const safeUtilization = calculateSafeUtilization( + safe.debtValue, + collateralValue, + safe.collateralFactor + ); + + const liquidationPrice = calcuateLiquidationPrice( + safe.debtValue, + collateralValue, + safe.collateralFactor, + safe.collateralPrice + ); + + const liquidationPriceUSD = calculateLiquidationPriceUSD( + liquidationPrice, + ethUSDBN + ); + + const maxBoost = calculateMaxBoost( + collateralValue, + safe.collateralFactor + ); + const maxBoostUSD = calculateETHValueUSD(maxBoost, ethUSDBN); + + updatedSafe = { + ...safe, + collateralAmount, + collateralValue, + collateralValueUSD, + safeUtilization, + liquidationPrice, + liquidationPriceUSD, + maxBoost, + maxBoostUSD, + }; + + return updatedSafe; + } + + /** BOOST **/ + if (mode === SafeInteractionMode.BOOST) { + console.log({ strategyIndex }); + + if (strategyIndex === undefined || strategyIndex < 0) return undefined; + const strategyToUpdate = safe.usdPricedStrategies[strategyIndex]; + + const boostedAmount = safe.boostedAmount.add(amount); // boosted FEI + const boostedUSD = calculateFEIValueUSD( + boostedAmount, + safe.feiPrice, + ethUSDBN + ); + const debtAmount = safe.debtAmount.add(amount); + const debtValue = debtAmount + .mul(safe.feiPrice) + .div(constants.WeiPerEther); + + const safeUtilization = calculateSafeUtilization( + debtValue, + safe.collateralValue, + safe.collateralFactor + ); + + const stratBoostedAmount = strategyToUpdate.boostedAmount.add(amount); + const stratBoostedUSD = calculateFEIValueUSD( + stratBoostedAmount, + safe.feiPrice, + ethUSDBN + ); + const stratFeiAmount = strategyToUpdate.feiAmount.add(amount); + const stratFeiUSD = calculateFEIValueUSD( + stratFeiAmount, + safe.feiPrice, + ethUSDBN + ); + + const updatedStrategy: USDPricedStrategy = { + ...strategyToUpdate, + boostedAmount: stratBoostedAmount, + boostAmountUSD: stratBoostedUSD, + feiAmount: stratFeiAmount, + feiAmountUSD: stratFeiUSD, + }; + + const strategies = [...safe.usdPricedStrategies]; + strategies[strategyIndex] = updatedStrategy; + + const liquidationPrice = calcuateLiquidationPrice( + debtValue, + safe.collateralValue, + safe.collateralFactor, + safe.collateralPrice + ); + const liquidationPriceUSD = calculateLiquidationPriceUSD( + liquidationPrice, + ethUSDBN + ); + + updatedSafe = { + ...safe, + boostedAmount, + boostedUSD, + debtAmount, + debtValue, + safeUtilization, + strategies, + liquidationPrice, + liquidationPriceUSD, + }; + + return updatedSafe; + } + + /** LESS **/ + if (mode === SafeInteractionMode.LESS) { + if (strategyIndex === undefined || strategyIndex < 0) return undefined; + + const boostedAmount = safe.boostedAmount.sub(amount); // boosted FEI + const boostedUSD = calculateFEIValueUSD( + boostedAmount, + safe.feiPrice, + ethUSDBN + ); + const debtAmount = safe.debtAmount.sub(amount); + const debtValue = debtAmount + .mul(safe.feiPrice) + .div(constants.WeiPerEther); + + console.log({ boostedAmount, boostedUSD, debtAmount, debtValue }); + + const safeUtilization = calculateSafeUtilization( + debtValue, + safe.collateralValue, + safe.collateralFactor + ); + + const strategyToUpdate = safe.usdPricedStrategies[strategyIndex]; + + const stratBoostedAmount = strategyToUpdate.boostedAmount.sub(amount); + const stratBoostedUSD = calculateFEIValueUSD( + stratBoostedAmount, + safe.feiPrice, + ethUSDBN + ); + const stratFeiAmount = strategyToUpdate.feiAmount.sub(amount); + const stratFeiUSD = calculateFEIValueUSD( + stratFeiAmount, + safe.feiPrice, + ethUSDBN + ); + + const updatedStrategy: USDPricedStrategy = { + ...strategyToUpdate, + boostedAmount: stratBoostedAmount, + boostAmountUSD: stratBoostedUSD, + feiAmount: stratFeiAmount, + feiAmountUSD: stratFeiUSD, + }; + + const strategies = safe.usdPricedStrategies; + strategies[strategyIndex] = updatedStrategy; + + const liquidationPrice = calcuateLiquidationPrice( + debtValue, + safe.collateralValue, + safe.collateralFactor, + safe.collateralPrice + ); + const liquidationPriceUSD = calculateLiquidationPriceUSD( + liquidationPrice, + ethUSDBN + ); + + updatedSafe = { + ...safe, + boostedAmount, + boostedUSD, + debtAmount, + debtValue, + safeUtilization, + strategies, + liquidationPrice, + liquidationPriceUSD, + }; + + return updatedSafe; + } + } + ); + + return updatedSafeInfo; +}; diff --git a/src/hooks/turbo/useUpdatedStrategyRate.ts b/src/hooks/turbo/useUpdatedStrategyRate.ts new file mode 100644 index 00000000..f816f4f1 --- /dev/null +++ b/src/hooks/turbo/useUpdatedStrategyRate.ts @@ -0,0 +1,56 @@ +import { useRari } from "context/RariContext"; +import { BigNumber, constants } from "ethers"; +import { parseEther } from "ethers/lib/utils"; +import { SafeInfo } from "lib/turbo/fetchers/safes/getSafeInfo"; +import { useQuery } from "react-query"; +import { fetchStrategyData } from "./useStrategyInfo"; +import { SafeInteractionMode } from "./useUpdatedSafeInfo"; + +const useUpdatedStrategyRate = ( + mode: SafeInteractionMode.BOOST | SafeInteractionMode.LESS, + safe: SafeInfo | undefined, + strategyIndex: number, + amountBN: BigNumber +) => { + const { fuse, provider } = useRari(); + + const { data: updatedStrategyRates } = useQuery( + `Updated strategy APY for safe ${ + safe?.safeAddress + } strategy ${strategyIndex} mode ${mode} and amount ${amountBN.toString()}`, + async () => { + const strategy = safe?.strategies[strategyIndex]; + if (!safe || !strategy) return; + + // Get CToken from strategy + const fuseStrategy = await fetchStrategyData(provider, strategy.strategy); + const irm = fuse.getInterestRateModel(fuseStrategy.fToken); + let supplyRatePerBlock = fuseStrategy.supplyRatePerBlock; + + const collateralValue = safe.collateralValue; + + const debtAmount = + mode === SafeInteractionMode.BOOST + ? safe.debtAmount.add(amountBN) + : safe.debtAmount.sub(amountBN); + + const debtValue = debtAmount + .mul(safe.feiPrice) + .div(constants.WeiPerEther); + + // console.log({ amountBN, collateralValue, debtAmount, debtValue }); + + // let newSupplyRatePerBlock = await irm.getSupplyRate( + // totalSupply.gt(0) + // ? assetToBeUpdated.totalBorrow + // .mul(constants.WeiPerEther) + // .div(totalSupply) + // : constants.Zero + // ); + } + ); + + return updatedStrategyRates; +}; + +export default useUpdatedStrategyRate; diff --git a/src/hooks/turbo/useUserFeiOwed.ts b/src/hooks/turbo/useUserFeiOwed.ts new file mode 100644 index 00000000..76ea5f86 --- /dev/null +++ b/src/hooks/turbo/useUserFeiOwed.ts @@ -0,0 +1,14 @@ +import { SafeInfo } from "lib/turbo/fetchers/safes/getSafeInfo"; +import { useBalanceOf } from "hooks/useBalanceOf"; +import { FEI } from "lib/turbo/utils/constants"; +import { getUserFeiOwed } from "lib/turbo/utils/getUserFeiOwed"; + +// Safe Balance of FEI is external to SafeInfo right now. Need to use this hook to add the safeFeiBalance +export const useUserFeiOwed = (safe: SafeInfo | undefined) => { + const safeBalance = useBalanceOf(safe?.safeAddress, FEI) + const claimableFromStrategies = getUserFeiOwed(safe) + const total = claimableFromStrategies.add(safeBalance) + return [total, claimableFromStrategies, safeBalance] +}; + + diff --git a/src/hooks/turbo/useUserSafes.ts b/src/hooks/turbo/useUserSafes.ts new file mode 100644 index 00000000..a14c8687 --- /dev/null +++ b/src/hooks/turbo/useUserSafes.ts @@ -0,0 +1,27 @@ +import { useQuery } from "react-query"; + +import { useRari } from "context/RariContext"; +import { SafeInfo } from "lib/turbo/fetchers/safes/getSafeInfo"; +import { getAllUserSafes } from "lib/turbo/fetchers/safes/getAllUserSafes"; +import { MOCK_SAFE_1, MOCK_SAFE_2 } from "./test/mocks"; + +export const useAllUserSafes = (providedAddress?: string): SafeInfo[] | undefined => { + if (process.env.NEXT_PUBLIC_USE_MOCKS === "true") { + return [MOCK_SAFE_1, MOCK_SAFE_2]; + } + + const { address, provider, chainId } = useRari() + + let addressToUse = providedAddress ?? address + + const { data: safes } = useQuery( + `User: ${addressToUse} safes`, + async () => { + if(!addressToUse || !chainId) return + return await getAllUserSafes(provider, addressToUse, chainId) + } + ); + return safes; +}; + + diff --git a/src/hooks/useBalanceOf.ts b/src/hooks/useBalanceOf.ts new file mode 100644 index 00000000..b8742d1b --- /dev/null +++ b/src/hooks/useBalanceOf.ts @@ -0,0 +1,53 @@ +import { useQueries, useQuery, UseQueryResult } from "react-query"; +import { balanceOf } from "utils/erc20Utils"; +import { BigNumber, constants } from "ethers"; +import { useRari } from "context/RariContext"; + + +const fetchBalanceOf = async (provider: any, holder: string | undefined, tokenAddress: string | undefined) => { + if (!holder || !tokenAddress) return constants.Zero; + const balance = await balanceOf(holder, tokenAddress, provider); + return balance; +} + +export const useBalanceOf = ( + holder: string | undefined, + tokenAddress: string | undefined +) => { + const { provider } = useRari(); + const { data: balance } = useQuery( + `${holder} balance of ${tokenAddress}`, + async () => await fetchBalanceOf(provider, holder, tokenAddress) + ); + + return balance ?? constants.Zero; +}; + +export type BalancesMap = { + [account: string]: BigNumber +} +export const useBalancesOfMultipleAddresses = ( + holders: string[], + tokenAddress: string, +): BalancesMap => { + const { provider } = useRari(); + const result: UseQueryResult[] = useQueries( + holders.map(holder => { + return { + queryKey: `${holder} balance of ${tokenAddress}`, + queryFn: async () => await fetchBalanceOf(provider, holder, tokenAddress) + } + + }) + ) + + const balancesMap: BalancesMap = result.reduce((acc: BalancesMap, r, i) => { + const { data } = r + return { + ...acc, + [holders[i]]: data as BigNumber + } + }, {}) + + return balancesMap +}; diff --git a/src/hooks/useETHUSDBN.ts b/src/hooks/useETHUSDBN.ts index 6808319b..beea1397 100644 --- a/src/hooks/useETHUSDBN.ts +++ b/src/hooks/useETHUSDBN.ts @@ -1,9 +1,11 @@ import { getEthUsdPriceBN } from "esm/utils/getUSDPriceBN"; +import React from "react"; import { useQuery } from "react-query"; const useETHUSDBN = () => { - const { data: ethUSDPriceBN } = useQuery("ETHUSD", async () => - getEthUsdPriceBN() + const { data: ethUSDPriceBN } = useQuery( + "ETHUSDBN", + async () => await getEthUsdPriceBN() ); return ethUSDPriceBN; }; diff --git a/src/hooks/useHasApproval.ts b/src/hooks/useHasApproval.ts new file mode 100644 index 00000000..7b266408 --- /dev/null +++ b/src/hooks/useHasApproval.ts @@ -0,0 +1,41 @@ +import { useQuery } from "react-query"; +import { checkAllowance } from "utils/erc20Utils"; +import { useRari } from "context/RariContext"; +import { parseEther } from "ethers/lib/utils"; +import { isSupportedChainId } from "esm/utils/networks"; + +const useHasApproval = ( + underlyingToken: string | undefined, + spender: string | undefined, + amount: string, + userAddress?: string +) => { + const { address, chainId, provider } = useRari(); + const addressToUse = userAddress ?? address; + + const { data } = useQuery( + ` ${spender} has approval to spend ${underlyingToken} ${amount} on behalf of ${addressToUse} on chain ${chainId}`, + async () => { + if ( + !addressToUse || + !chainId || + !spender || + !underlyingToken || + !isSupportedChainId(chainId) + ) + return false; + if (amount === "" || amount === "0") return true; + + return await checkAllowance( + provider, + addressToUse, + spender, + underlyingToken, + parseEther(amount) + ); + } + ); + return data ?? false; +}; + +export default useHasApproval; diff --git a/src/hooks/usePriceUSD.ts b/src/hooks/usePriceUSD.ts new file mode 100644 index 00000000..dd290961 --- /dev/null +++ b/src/hooks/usePriceUSD.ts @@ -0,0 +1,18 @@ +import { useQuery } from "react-query"; +import { BigNumber } from "ethers"; +import { getEthUsdPriceBN } from "esm/utils/getUSDPriceBN"; + +export const usePriceUSD = ( + priceETH: BigNumber, +): number | undefined => { + const { data: priceUSD } = useQuery( + `price ${priceETH} in USD`, + async () => { + if (!priceETH) return + const ethUSD = await getEthUsdPriceBN(); + const priceUSD = parseFloat(ethUSD.toString()) * parseFloat(priceETH.toString()) / 1e36 + return priceUSD + } + ); + return priceUSD; +}; diff --git a/src/hooks/useTokenData.ts b/src/hooks/useTokenData.ts index 35269137..6ad52f26 100644 --- a/src/hooks/useTokenData.ts +++ b/src/hooks/useTokenData.ts @@ -65,8 +65,6 @@ export const fetchTokenData = async ( _chainId = 1; } - // console.log('fetchTokenData',{address, chainId, _chainid}) - if (address !== ETH_TOKEN_DATA.address) { try { // Since running the vercel functions requires a Vercel account and is super slow, diff --git a/src/index.css b/src/index.css index 0e884b6e..6bc858a5 100644 --- a/src/index.css +++ b/src/index.css @@ -10,6 +10,7 @@ html { height: 100% !important; /* background-color: #000000 !important; */ overflow-y: scroll; + scroll-behavior: smooth; } * { diff --git a/src/lib/turbo/abi/Authority.json b/src/lib/turbo/abi/Authority.json new file mode 100644 index 00000000..2d722898 --- /dev/null +++ b/src/lib/turbo/abi/Authority.json @@ -0,0 +1,33 @@ +{ + "abi": [ + { + "type": "function", + "name": "canCall", + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "functionSig", + "type": "bytes4" + } + ], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view" + } + ] +} \ No newline at end of file diff --git a/src/lib/turbo/abi/CERC20.json b/src/lib/turbo/abi/CERC20.json new file mode 100644 index 00000000..4731e475 --- /dev/null +++ b/src/lib/turbo/abi/CERC20.json @@ -0,0 +1,446 @@ +{ + "abi": [ + { + "type": "function", + "name": "DOMAIN_SEPARATOR", + "inputs": [], + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "allowance", + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "approve", + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "balanceOf", + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "balanceOfUnderlying", + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "borrow", + "inputs": [ + { + "internalType": "uint256", + "name": "underlyingAmount", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "borrowBalanceCurrent", + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "decimals", + "inputs": [], + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "exchangeRateStored", + "inputs": [], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "mint", + "inputs": [ + { + "internalType": "uint256", + "name": "underlyingAmount", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "name", + "inputs": [], + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "nonces", + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "permit", + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "redeemUnderlying", + "inputs": [ + { + "internalType": "uint256", + "name": "underlyingAmount", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "repayBorrow", + "inputs": [ + { + "internalType": "uint256", + "name": "underlyingAmount", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "repayBorrowBehalf", + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "uint256", + "name": "underlyingAmount", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "symbol", + "inputs": [], + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "totalSupply", + "inputs": [], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transfer", + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "transferFrom", + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "Approval", + "inputs": [ + { + "name": "owner", + "type": "address", + "indexed": true + }, + { + "name": "spender", + "type": "address", + "indexed": true + }, + { + "name": "amount", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Transfer", + "inputs": [ + { + "name": "from", + "type": "address", + "indexed": true + }, + { + "name": "to", + "type": "address", + "indexed": true + }, + { + "name": "amount", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + } + ] +} \ No newline at end of file diff --git a/src/lib/turbo/abi/CERC20Delegate.json b/src/lib/turbo/abi/CERC20Delegate.json new file mode 100644 index 00000000..e6b1248b --- /dev/null +++ b/src/lib/turbo/abi/CERC20Delegate.json @@ -0,0 +1,1637 @@ +[ + { + "inputs": [ + + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "cashPrior", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "interestAccumulated", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "borrowIndex", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + } + ], + "name": "AccrueInterest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "accountBorrows", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + } + ], + "name": "Borrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "error", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "info", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "detail", + "type": "uint256" + } + ], + "name": "Failure", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "cTokenCollateral", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "LiquidateBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "mintTokens", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldAdminFeeMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newAdminFeeMantissa", + "type": "uint256" + } + ], + "name": "NewAdminFee", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contractComptrollerInterface", + "name": "oldComptroller", + "type": "address" + }, + { + "indexed": false, + "internalType": "contractComptrollerInterface", + "name": "newComptroller", + "type": "address" + } + ], + "name": "NewComptroller", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldFuseFeeMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newFuseFeeMantissa", + "type": "uint256" + } + ], + "name": "NewFuseFee", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldImplementation", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "NewImplementation", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contractInterestRateModel", + "name": "oldInterestRateModel", + "type": "address" + }, + { + "indexed": false, + "internalType": "contractInterestRateModel", + "name": "newInterestRateModel", + "type": "address" + } + ], + "name": "NewMarketInterestRateModel", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldReserveFactorMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newReserveFactorMantissa", + "type": "uint256" + } + ], + "name": "NewReserveFactor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "redeemer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "redeemAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "Redeem", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "payer", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "accountBorrows", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + } + ], + "name": "RepayBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "benefactor", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "addAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newTotalReserves", + "type": "uint256" + } + ], + "name": "ReservesAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "reduceAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newTotalReserves", + "type": "uint256" + } + ], + "name": "ReservesReduced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "_becomeImplementation", + "outputs": [ + + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "compLikeDelegatee", + "type": "address" + } + ], + "name": "_delegateCompLikeTo", + "outputs": [ + + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + + ], + "name": "_prepare", + "outputs": [ + + ], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "reduceAmount", + "type": "uint256" + } + ], + "name": "_reduceReserves", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newAdminFeeMantissa", + "type": "uint256" + } + ], + "name": "_setAdminFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "implementation_", + "type": "address" + }, + { + "internalType": "bool", + "name": "allowResign", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "becomeImplementationData", + "type": "bytes" + } + ], + "name": "_setImplementationSafe", + "outputs": [ + + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "contractInterestRateModel", + "name": "newInterestRateModel", + "type": "address" + } + ], + "name": "_setInterestRateModel", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "string", + "name": "_symbol", + "type": "string" + } + ], + "name": "_setNameAndSymbol", + "outputs": [ + + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newReserveFactorMantissa", + "type": "uint256" + } + ], + "name": "_setReserveFactor", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "withdrawAmount", + "type": "uint256" + } + ], + "name": "_withdrawAdminFees", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "withdrawAmount", + "type": "uint256" + } + ], + "name": "_withdrawFuseFees", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "accrualBlockNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + + ], + "name": "accrueInterest", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "adminFeeMantissa", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOfUnderlying", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "borrow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "borrowBalanceCurrent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "borrowBalanceStored", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "borrowIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "borrowRatePerBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "comptroller", + "outputs": [ + { + "internalType": "contractComptrollerInterface", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + + ], + "name": "exchangeRateCurrent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "exchangeRateStored", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "fuseFeeMantissa", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getAccountSnapshot", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "getCash", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "contractComptrollerInterface", + "name": "comptroller_", + "type": "address" + }, + { + "internalType": "contractInterestRateModel", + "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": "uint256", + "name": "reserveFactorMantissa_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "adminFeeMantissa_", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [ + + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "underlying_", + "type": "address" + }, + { + "internalType": "contractComptrollerInterface", + "name": "comptroller_", + "type": "address" + }, + { + "internalType": "contractInterestRateModel", + "name": "interestRateModel_", + "type": "address" + }, + { + "internalType": "string", + "name": "name_", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol_", + "type": "string" + }, + { + "internalType": "uint256", + "name": "reserveFactorMantissa_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "adminFeeMantissa_", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [ + + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "interestRateModel", + "outputs": [ + { + "internalType": "contractInterestRateModel", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "isCEther", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "isCToken", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + }, + { + "internalType": "contractCTokenInterface", + "name": "cTokenCollateral", + "type": "address" + } + ], + "name": "liquidateBorrow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "protocolSeizeShareMantissa", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "redeem", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "redeemAmount", + "type": "uint256" + } + ], + "name": "redeemUnderlying", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "repayBorrow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "repayBorrowBehalf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "reserveFactorMantissa", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "seize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "supplyRatePerBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "totalAdminFees", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "totalBorrows", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + + ], + "name": "totalBorrowsCurrent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "totalFuseFees", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "totalReserves", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "dst", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "src", + "type": "address" + }, + { + "internalType": "address", + "name": "dst", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + + ], + "name": "underlying", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ] \ No newline at end of file diff --git a/src/lib/turbo/abi/ERC20.json b/src/lib/turbo/abi/ERC20.json new file mode 100644 index 00000000..e7b0bd7a --- /dev/null +++ b/src/lib/turbo/abi/ERC20.json @@ -0,0 +1,295 @@ +{ + "abi": [ + { + "type": "function", + "name": "DOMAIN_SEPARATOR", + "inputs": [], + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "allowance", + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "approve", + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "balanceOf", + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "decimals", + "inputs": [], + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "name", + "inputs": [], + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "nonces", + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "permit", + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "symbol", + "inputs": [], + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "totalSupply", + "inputs": [], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transfer", + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "transferFrom", + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "Approval", + "inputs": [ + { + "name": "owner", + "type": "address", + "indexed": true + }, + { + "name": "spender", + "type": "address", + "indexed": true + }, + { + "name": "amount", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Transfer", + "inputs": [ + { + "name": "from", + "type": "address", + "indexed": true + }, + { + "name": "to", + "type": "address", + "indexed": true + }, + { + "name": "amount", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + } + ] +} \ No newline at end of file diff --git a/src/lib/turbo/abi/FuseERC4626.json b/src/lib/turbo/abi/FuseERC4626.json new file mode 100644 index 00000000..8556e713 --- /dev/null +++ b/src/lib/turbo/abi/FuseERC4626.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_cToken","type":"address"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cToken","outputs":[{"internalType":"contract CToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cTokenUnderlying","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unitroller","outputs":[{"internalType":"contract Unitroller","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/src/lib/turbo/abi/TurboAdmin.json b/src/lib/turbo/abi/TurboAdmin.json new file mode 100644 index 00000000..26e081fb --- /dev/null +++ b/src/lib/turbo/abi/TurboAdmin.json @@ -0,0 +1,762 @@ +{ + "abi": [ + { + "type": "constructor", + "inputs": [ + { + "internalType": "contract Comptroller", + "name": "_comptroller", + "type": "address" + }, + { + "internalType": "contract TimelockController", + "name": "_timelock", + "type": "address" + }, + { + "internalType": "contract Authority", + "name": "_authority", + "type": "address" + } + ] + }, + { + "type": "function", + "name": "CTOKEN_IMPL", + "inputs": [], + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "REVERSE_REGISTRAR", + "inputs": [], + "outputs": [ + { + "internalType": "contract IReverseRegistrar", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "ZERO_IRM", + "inputs": [], + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "_acceptAdmin", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_addRewardsDistributor", + "inputs": [ + { + "internalType": "address", + "name": "distributor", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_deployMarket", + "inputs": [ + { + "internalType": "address", + "name": "underlying", + "type": "address" + }, + { + "internalType": "address", + "name": "irm", + "type": "address" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "address", + "name": "impl", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "reserveFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "adminFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "collateralFactorMantissa", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_setBorrowCapGuardian", + "inputs": [ + { + "internalType": "address", + "name": "newBorrowCapGuardian", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_setBorrowPaused", + "inputs": [ + { + "internalType": "contract CERC20", + "name": "cToken", + "type": "address" + }, + { + "internalType": "bool", + "name": "state", + "type": "bool" + } + ], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_setBorrowPausedByUnderlying", + "inputs": [ + { + "internalType": "contract ERC20", + "name": "underlying", + "type": "address" + }, + { + "internalType": "bool", + "name": "state", + "type": "bool" + } + ], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_setCloseFactor", + "inputs": [ + { + "internalType": "uint256", + "name": "newCloseFactorMantissa", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_setCollateralFactor", + "inputs": [ + { + "internalType": "contract CERC20", + "name": "cToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "newCollateralFactorMantissa", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_setLiquidationIncentive", + "inputs": [ + { + "internalType": "uint256", + "name": "newLiquidationIncentiveMantissa", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_setMarketBorrowCaps", + "inputs": [ + { + "internalType": "contract CERC20[]", + "name": "cTokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "newBorrowCaps", + "type": "uint256[]" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_setMarketBorrowCapsByUnderlying", + "inputs": [ + { + "internalType": "address[]", + "name": "underlyings", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "newBorrowCaps", + "type": "uint256[]" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_setMarketSupplyCaps", + "inputs": [ + { + "internalType": "contract CERC20[]", + "name": "cTokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "newSupplyCaps", + "type": "uint256[]" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_setMarketSupplyCapsByUnderlying", + "inputs": [ + { + "internalType": "address[]", + "name": "underlyings", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "newSupplyCaps", + "type": "uint256[]" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_setMintPaused", + "inputs": [ + { + "internalType": "contract CERC20", + "name": "cToken", + "type": "address" + }, + { + "internalType": "bool", + "name": "state", + "type": "bool" + } + ], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_setMintPausedByUnderlying", + "inputs": [ + { + "internalType": "contract ERC20", + "name": "underlying", + "type": "address" + }, + { + "internalType": "bool", + "name": "state", + "type": "bool" + } + ], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_setPauseGuardian", + "inputs": [ + { + "internalType": "address", + "name": "newPauseGuardian", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_setPendingAdmin", + "inputs": [ + { + "internalType": "address", + "name": "newPendingAdmin", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_setPriceOracle", + "inputs": [ + { + "internalType": "address", + "name": "newOracle", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_setSeizePaused", + "inputs": [ + { + "internalType": "bool", + "name": "state", + "type": "bool" + } + ], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_setTransferPaused", + "inputs": [ + { + "internalType": "bool", + "name": "state", + "type": "bool" + } + ], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_setWhitelistEnforcement", + "inputs": [ + { + "internalType": "bool", + "name": "enforce", + "type": "bool" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_setWhitelistStatuses", + "inputs": [ + { + "internalType": "address[]", + "name": "suppliers", + "type": "address[]" + }, + { + "internalType": "bool[]", + "name": "statuses", + "type": "bool[]" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_toggleAutoImplementations", + "inputs": [ + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "_unsupportMarket", + "inputs": [ + { + "internalType": "contract CERC20", + "name": "cToken", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "addCollateral", + "inputs": [ + { + "internalType": "address", + "name": "underlying", + "type": "address" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "uint256", + "name": "collateralFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyCap", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "authority", + "inputs": [], + "outputs": [ + { + "internalType": "contract Authority", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "cancel", + "inputs": [ + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "comptroller", + "inputs": [], + "outputs": [ + { + "internalType": "contract Comptroller", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "execute", + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "oracleAdd", + "inputs": [ + { + "internalType": "address[]", + "name": "underlyings", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "_oracles", + "type": "address[]" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "oracleChangeAdmin", + "inputs": [ + { + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "schedule", + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "scheduleSetPendingAdmin", + "inputs": [ + { + "internalType": "address", + "name": "newPendingAdmin", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setAuthority", + "inputs": [ + { + "internalType": "contract Authority", + "name": "newAuthority", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setENSName", + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setOwner", + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "timelock", + "inputs": [], + "outputs": [ + { + "internalType": "contract TimelockController", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "event", + "name": "AuthorityUpdated", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "newAuthority", + "type": "address", + "indexed": true + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnerUpdated", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "newOwner", + "type": "address", + "indexed": true + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "ComptrollerError", + "inputs": [] + } + ] +} \ No newline at end of file diff --git a/src/lib/turbo/abi/TurboBooster.json b/src/lib/turbo/abi/TurboBooster.json new file mode 100644 index 00000000..fcb0d457 --- /dev/null +++ b/src/lib/turbo/abi/TurboBooster.json @@ -0,0 +1,368 @@ +{ + "abi": [ + { + "type": "constructor", + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "contract Authority", + "name": "_authority", + "type": "address" + } + ] + }, + { + "type": "function", + "name": "REVERSE_REGISTRAR", + "inputs": [], + "outputs": [ + { + "internalType": "contract IReverseRegistrar", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "authority", + "inputs": [], + "outputs": [ + { + "internalType": "contract Authority", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "boostableVaults", + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "contract ERC4626", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "canSafeBoostVault", + "inputs": [ + { + "internalType": "contract TurboSafe", + "name": "safe", + "type": "address" + }, + { + "internalType": "contract ERC20", + "name": "collateral", + "type": "address" + }, + { + "internalType": "contract ERC4626", + "name": "vault", + "type": "address" + }, + { + "internalType": "uint256", + "name": "feiAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "newTotalBoostedForVault", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "newTotalBoostedAgainstCollateral", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "frozen", + "inputs": [], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getBoostCapForCollateral", + "inputs": [ + { + "internalType": "contract ERC20", + "name": "", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getBoostCapForVault", + "inputs": [ + { + "internalType": "contract ERC4626", + "name": "", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getBoostableVaults", + "inputs": [], + "outputs": [ + { + "internalType": "contract ERC4626[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "setAuthority", + "inputs": [ + { + "internalType": "contract Authority", + "name": "newAuthority", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setBoostCapForCollateral", + "inputs": [ + { + "internalType": "contract ERC20", + "name": "collateral", + "type": "address" + }, + { + "internalType": "uint256", + "name": "newBoostCap", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setBoostCapForVault", + "inputs": [ + { + "internalType": "contract ERC4626", + "name": "vault", + "type": "address" + }, + { + "internalType": "uint256", + "name": "newBoostCap", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setENSName", + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setFreezeStatus", + "inputs": [ + { + "internalType": "bool", + "name": "freeze", + "type": "bool" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setOwner", + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "AuthorityUpdated", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "newAuthority", + "type": "address", + "indexed": true + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BoostCapUpdatedForCollateral", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "collateral", + "type": "address", + "indexed": true + }, + { + "name": "newBoostCap", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BoostCapUpdatedForVault", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "vault", + "type": "address", + "indexed": true + }, + { + "name": "newBoostCap", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "FreezeStatusUpdated", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "frozen", + "type": "bool", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnerUpdated", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "newOwner", + "type": "address", + "indexed": true + } + ], + "anonymous": false + } + ] +} \ No newline at end of file diff --git a/src/lib/turbo/abi/TurboClerk.json b/src/lib/turbo/abi/TurboClerk.json new file mode 100644 index 00000000..9338d898 --- /dev/null +++ b/src/lib/turbo/abi/TurboClerk.json @@ -0,0 +1,316 @@ +{ + "abi": [ + { + "type": "constructor", + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "contract Authority", + "name": "_authority", + "type": "address" + } + ] + }, + { + "type": "function", + "name": "REVERSE_REGISTRAR", + "inputs": [], + "outputs": [ + { + "internalType": "contract IReverseRegistrar", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "authority", + "inputs": [], + "outputs": [ + { + "internalType": "contract Authority", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "defaultFeePercentage", + "inputs": [], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getCustomFeePercentageForCollateral", + "inputs": [ + { + "internalType": "contract ERC20", + "name": "", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getCustomFeePercentageForSafe", + "inputs": [ + { + "internalType": "contract TurboSafe", + "name": "", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getFeePercentageForSafe", + "inputs": [ + { + "internalType": "contract TurboSafe", + "name": "safe", + "type": "address" + }, + { + "internalType": "contract ERC20", + "name": "collateral", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "setAuthority", + "inputs": [ + { + "internalType": "contract Authority", + "name": "newAuthority", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setCustomFeePercentageForCollateral", + "inputs": [ + { + "internalType": "contract ERC20", + "name": "collateral", + "type": "address" + }, + { + "internalType": "uint256", + "name": "newFeePercentage", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setCustomFeePercentageForSafe", + "inputs": [ + { + "internalType": "contract TurboSafe", + "name": "safe", + "type": "address" + }, + { + "internalType": "uint256", + "name": "newFeePercentage", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setDefaultFeePercentage", + "inputs": [ + { + "internalType": "uint256", + "name": "newDefaultFeePercentage", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setENSName", + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setOwner", + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "AuthorityUpdated", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "newAuthority", + "type": "address", + "indexed": true + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "CustomFeePercentageUpdatedForCollateral", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "collateral", + "type": "address", + "indexed": true + }, + { + "name": "newFeePercentage", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "CustomFeePercentageUpdatedForSafe", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "safe", + "type": "address", + "indexed": true + }, + { + "name": "newFeePercentage", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "DefaultFeePercentageUpdated", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "newDefaultFeePercentage", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnerUpdated", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "newOwner", + "type": "address", + "indexed": true + } + ], + "anonymous": false + } + ] +} \ No newline at end of file diff --git a/src/lib/turbo/abi/TurboLens.json b/src/lib/turbo/abi/TurboLens.json new file mode 100644 index 00000000..7fac8817 --- /dev/null +++ b/src/lib/turbo/abi/TurboLens.json @@ -0,0 +1,238 @@ +{ + "abi": [ + { + "type": "constructor", + "inputs": [ + { + "internalType": "contract TurboMaster", + "name": "_master", + "type": "address" + } + ] + }, + { + "type": "function", + "name": "getAllUserSafes", + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "struct TurboLens.SafeInfo[]", + "name": "", + "type": "tuple[]", + "components": [ + { + "type": "address" + }, + { + "type": "address" + }, + { + "type": "uint256" + }, + { + "type": "uint256" + }, + { + "type": "uint256" + }, + { + "type": "uint256" + }, + { + "type": "uint256" + }, + { + "type": "uint256" + }, + { + "type": "uint256" + }, + { + "type": "uint256" + }, + { + "type": "uint256" + }, + { + "type": "uint256" + }, + { + "type": "tuple[]", + "components": [ + { + "type": "address" + }, + { + "type": "uint256" + }, + { + "type": "uint256" + } + ] + } + ] + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "getSafeInfo", + "inputs": [ + { + "internalType": "contract TurboSafe", + "name": "safe", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "struct TurboLens.SafeInfo", + "name": "", + "type": "tuple", + "components": [ + { + "type": "address" + }, + { + "type": "address" + }, + { + "type": "uint256" + }, + { + "type": "uint256" + }, + { + "type": "uint256" + }, + { + "type": "uint256" + }, + { + "type": "uint256" + }, + { + "type": "uint256" + }, + { + "type": "uint256" + }, + { + "type": "uint256" + }, + { + "type": "uint256" + }, + { + "type": "uint256" + }, + { + "type": "tuple[]", + "components": [ + { + "type": "address" + }, + { + "type": "uint256" + }, + { + "type": "uint256" + } + ] + } + ] + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "master", + "inputs": [], + "outputs": [ + { + "internalType": "contract TurboMaster", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "pool", + "inputs": [], + "outputs": [ + { + "internalType": "contract Comptroller", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + } + ], + "bytecode": { + "object": "0x60c06040523480156200001157600080fd5b50604051620016de380380620016de8339810160408190526200003491620000d1565b6001600160a01b03811660a0819052604080516316f0115b60e01b815290516316f0115b916004808201926020929091908290030181865afa1580156200007f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000a59190620000d1565b6001600160a01b031660805250620000f8565b6001600160a01b0381168114620000ce57600080fd5b50565b600060208284031215620000e457600080fd5b8151620000f181620000b8565b9392505050565b60805160a05161158a620001546000396000818160da015281816101020152818161024f015281816102d5015281816105bd01526107320152600081816056015281816103c5015281816106b00152610e60015261158a6000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806316f0115b1461005157806369ff32d814610095578063d027f464146100b5578063ee97f7f3146100d5575b600080fd5b6100787f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b6100a86100a3366004611104565b6100fc565b60405161008c919061123d565b6100c86100c3366004611104565b6105b1565b60405161008c919061129f565b6100787f000000000000000000000000000000000000000000000000000000000000000081565b606060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638cbf46ae6040518163ffffffff1660e01b8152600401600060405180830381865afa15801561015e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610186919081019061131d565b9050600060015b825181101561024a57846001600160a01b03168382815181106101b2576101b26113bc565b60200260200101516001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061021b91906113d2565b6001600160a01b0316141561023857610235600183611405565b91505b806102428161141d565b91505061018d565b5060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c6def0766040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102cf91906113d2565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b87ee7af6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610331573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061035591906113d2565b90506000826001600160a01b0316630225c09e6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610397573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526103bf9190810190611438565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316637dc0d1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015610421573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061044591906113d2565b905060008567ffffffffffffffff811115610462576104626112b2565b60405190808252806020026020018201604052801561049b57816020015b610488611072565b8152602001906001900390816104805790505b509050600060015b88518110156105a2578a6001600160a01b03168982815181106104c8576104c86113bc565b60200260200101516001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561050d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061053191906113d2565b6001600160a01b0316141561059057610565898281518110610555576105556113bc565b60200260200101518686896107bf565b838381518110610577576105776113bc565b602090810291909101015261058d600183611405565b91505b8061059a8161141d565b9150506104a3565b50909998505050505050505050565b6105b9611072565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c6def0766040518163ffffffff1660e01b8152600401602060405180830381865afa158015610619573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061063d91906113d2565b90506000816001600160a01b0316630225c09e6040518163ffffffff1660e01b8152600401600060405180830381865afa15801561067f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526106a79190810190611438565b90506107b784827f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316637dc0d1d06040518163ffffffff1660e01b8152600401602060405180830381865afa15801561070c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061073091906113d2565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b87ee7af6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561078e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107b291906113d2565b6107bf565b949350505050565b6107c7611072565b6000845167ffffffffffffffff8111156107e3576107e36112b2565b60405190808252806020026020018201604052801561084157816020015b61082e604051806060016040528060006001600160a01b0316815260200160008152602001600081525090565b8152602001906001900390816108015790505b5090506000805b8651811015610a1c576000878281518110610865576108656113bc565b6020908102919091010151604051635fd35eb960e01b81526001600160a01b0380831660048301529192506000918b1690635fd35eb990602401602060405180830381865afa1580156108bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108e091906114c7565b6040516370a0823160e01b81526001600160a01b038c81166004830152919250600091841690634cdad5069082906370a0823190602401602060405180830381865afa158015610934573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061095891906114c7565b6040518263ffffffff1660e01b815260040161097691815260200190565b602060405180830381865afa158015610993573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109b791906114c7565b90506109c38186611405565b94506040518060600160405280846001600160a01b03168152602001838152602001828152508685815181106109fb576109fb6113bc565b60200260200101819052505050508080610a149061141d565b915050610848565b506000876001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a5d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a8191906113d2565b60405162ede82760e41b81526001600160a01b038a811660048301528083166024830152919250600091871690630ede827090604401602060405180830381865afa158015610ad4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610af891906114c7565b90506000896001600160a01b031663a1ea7d6a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b5e91906113d2565b6040516305eff7ef60e21b81526001600160a01b038c8116600483015291909116906317bfdfbc906024016020604051808303816000875af1158015610ba8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bcc91906114c7565b90506000886001600160a01b031663fc57d4df8c6001600160a01b031663a1ea7d6a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4191906113d2565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015610c85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca991906114c7565b905060008b6001600160a01b0316634cdad5068d6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cfa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d1e91906114c7565b6040518263ffffffff1660e01b8152600401610d3c91815260200190565b602060405180830381865afa158015610d59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7d91906114c7565b905060008a6001600160a01b031663fc57d4df8e6001600160a01b031663ec11e1676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610dce573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610df291906113d2565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015610e36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5a91906114c7565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638e8f294b8f6001600160a01b031663ec11e1676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ecb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eef91906113d2565b6040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024016040805180830381865afa158015610f32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5691906114e0565b915050604051806101a001604052808f6001600160a01b03168152602001886001600160a01b03168152602001848152602001838152602001828152602001858152602001670de0b6b3a76400008486610fb09190611513565b610fba9190611532565b815260208101879052604001670de0b6b3a7640000610fd98789611513565b610fe39190611532565b81526020018f6001600160a01b03166321fcf7506040518163ffffffff1660e01b8152600401602060405180830381865afa158015611026573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061104a91906114c7565b81526020018981526020018781526020018a8152509950505050505050505050949350505050565b604051806101a0016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001606081525090565b6001600160a01b038116811461110157600080fd5b50565b60006020828403121561111657600080fd5b8135611121816110ec565b9392505050565b600081518084526020808501945080840160005b8381101561117757815180516001600160a01b031688528381015184890152604090810151908801526060909601959082019060010161113c565b509495945050505050565b80516001600160a01b0316825260006101a060208301516111ae60208601826001600160a01b03169052565b5060408301516040850152606083015160608501526080830151608085015260a083015160a085015260c083015160c085015260e083015160e085015261010080840151818601525061012080840151818601525061014080840151818601525061016080840151818601525061018080840151828287015261123383870182611128565b9695505050505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561129257603f19888603018452611280858351611182565b94509285019290850190600101611264565b5092979650505050505050565b6020815260006111216020830184611182565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156112f1576112f16112b2565b604052919050565b600067ffffffffffffffff821115611313576113136112b2565b5060051b60200190565b6000602080838503121561133057600080fd5b825167ffffffffffffffff81111561134757600080fd5b8301601f8101851361135857600080fd5b805161136b611366826112f9565b6112c8565b81815260059190911b8201830190838101908783111561138a57600080fd5b928401925b828410156113b15783516113a2816110ec565b8252928401929084019061138f565b979650505050505050565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156113e457600080fd5b8151611121816110ec565b634e487b7160e01b600052601160045260246000fd5b60008219821115611418576114186113ef565b500190565b6000600019821415611431576114316113ef565b5060010190565b6000602080838503121561144b57600080fd5b825167ffffffffffffffff81111561146257600080fd5b8301601f8101851361147357600080fd5b8051611481611366826112f9565b81815260059190911b820183019083810190878311156114a057600080fd5b928401925b828410156113b15783516114b8816110ec565b825292840192908401906114a5565b6000602082840312156114d957600080fd5b5051919050565b600080604083850312156114f357600080fd5b8251801515811461150357600080fd5b6020939093015192949293505050565b600081600019048311821515161561152d5761152d6113ef565b500290565b60008261154f57634e487b7160e01b600052601260045260246000fd5b50049056fea264697066735822122000633454327713610bfc1341afc7e508e5d7f441aaf1f3e4e2859aa081c52fa064736f6c634300080a0033", + "sourceMap": "415:4873:50:-:0;;;1939:98;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;1983:16:50;;;;;;2016:14;;;-1:-1:-1;;;2016:14:50;;;;:12;;:14;;;;;;;;;;;;;;;1983:16;2016:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;2009:21:50;;;-1:-1:-1;415:4873:50;;14:144:63;-1:-1:-1;;;;;102:31:63;;92:42;;82:70;;148:1;145;138:12;82:70;14:144;:::o;163:284::-;253:6;306:2;294:9;285:7;281:23;277:32;274:52;;;322:1;319;312:12;274:52;354:9;348:16;373:44;411:5;373:44;:::i;:::-;436:5;163:284;-1:-1:-1;;;163:284:63:o;452:285::-;415:4873:50;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;", + "linkReferences": {} + }, + "deployed_bytecode": { + "object": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c806316f0115b1461005157806369ff32d814610095578063d027f464146100b5578063ee97f7f3146100d5575b600080fd5b6100787f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b6100a86100a3366004611104565b6100fc565b60405161008c919061123d565b6100c86100c3366004611104565b6105b1565b60405161008c919061129f565b6100787f000000000000000000000000000000000000000000000000000000000000000081565b606060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638cbf46ae6040518163ffffffff1660e01b8152600401600060405180830381865afa15801561015e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610186919081019061131d565b9050600060015b825181101561024a57846001600160a01b03168382815181106101b2576101b26113bc565b60200260200101516001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061021b91906113d2565b6001600160a01b0316141561023857610235600183611405565b91505b806102428161141d565b91505061018d565b5060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c6def0766040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102cf91906113d2565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b87ee7af6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610331573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061035591906113d2565b90506000826001600160a01b0316630225c09e6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610397573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526103bf9190810190611438565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316637dc0d1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015610421573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061044591906113d2565b905060008567ffffffffffffffff811115610462576104626112b2565b60405190808252806020026020018201604052801561049b57816020015b610488611072565b8152602001906001900390816104805790505b509050600060015b88518110156105a2578a6001600160a01b03168982815181106104c8576104c86113bc565b60200260200101516001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561050d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061053191906113d2565b6001600160a01b0316141561059057610565898281518110610555576105556113bc565b60200260200101518686896107bf565b838381518110610577576105776113bc565b602090810291909101015261058d600183611405565b91505b8061059a8161141d565b9150506104a3565b50909998505050505050505050565b6105b9611072565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c6def0766040518163ffffffff1660e01b8152600401602060405180830381865afa158015610619573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061063d91906113d2565b90506000816001600160a01b0316630225c09e6040518163ffffffff1660e01b8152600401600060405180830381865afa15801561067f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526106a79190810190611438565b90506107b784827f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316637dc0d1d06040518163ffffffff1660e01b8152600401602060405180830381865afa15801561070c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061073091906113d2565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b87ee7af6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561078e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107b291906113d2565b6107bf565b949350505050565b6107c7611072565b6000845167ffffffffffffffff8111156107e3576107e36112b2565b60405190808252806020026020018201604052801561084157816020015b61082e604051806060016040528060006001600160a01b0316815260200160008152602001600081525090565b8152602001906001900390816108015790505b5090506000805b8651811015610a1c576000878281518110610865576108656113bc565b6020908102919091010151604051635fd35eb960e01b81526001600160a01b0380831660048301529192506000918b1690635fd35eb990602401602060405180830381865afa1580156108bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108e091906114c7565b6040516370a0823160e01b81526001600160a01b038c81166004830152919250600091841690634cdad5069082906370a0823190602401602060405180830381865afa158015610934573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061095891906114c7565b6040518263ffffffff1660e01b815260040161097691815260200190565b602060405180830381865afa158015610993573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109b791906114c7565b90506109c38186611405565b94506040518060600160405280846001600160a01b03168152602001838152602001828152508685815181106109fb576109fb6113bc565b60200260200101819052505050508080610a149061141d565b915050610848565b506000876001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a5d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a8191906113d2565b60405162ede82760e41b81526001600160a01b038a811660048301528083166024830152919250600091871690630ede827090604401602060405180830381865afa158015610ad4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610af891906114c7565b90506000896001600160a01b031663a1ea7d6a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b5e91906113d2565b6040516305eff7ef60e21b81526001600160a01b038c8116600483015291909116906317bfdfbc906024016020604051808303816000875af1158015610ba8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bcc91906114c7565b90506000886001600160a01b031663fc57d4df8c6001600160a01b031663a1ea7d6a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4191906113d2565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015610c85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca991906114c7565b905060008b6001600160a01b0316634cdad5068d6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cfa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d1e91906114c7565b6040518263ffffffff1660e01b8152600401610d3c91815260200190565b602060405180830381865afa158015610d59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7d91906114c7565b905060008a6001600160a01b031663fc57d4df8e6001600160a01b031663ec11e1676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610dce573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610df291906113d2565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015610e36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5a91906114c7565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638e8f294b8f6001600160a01b031663ec11e1676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ecb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eef91906113d2565b6040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024016040805180830381865afa158015610f32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5691906114e0565b915050604051806101a001604052808f6001600160a01b03168152602001886001600160a01b03168152602001848152602001838152602001828152602001858152602001670de0b6b3a76400008486610fb09190611513565b610fba9190611532565b815260208101879052604001670de0b6b3a7640000610fd98789611513565b610fe39190611532565b81526020018f6001600160a01b03166321fcf7506040518163ffffffff1660e01b8152600401602060405180830381865afa158015611026573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061104a91906114c7565b81526020018981526020018781526020018a8152509950505050505050505050949350505050565b604051806101a0016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001606081525090565b6001600160a01b038116811461110157600080fd5b50565b60006020828403121561111657600080fd5b8135611121816110ec565b9392505050565b600081518084526020808501945080840160005b8381101561117757815180516001600160a01b031688528381015184890152604090810151908801526060909601959082019060010161113c565b509495945050505050565b80516001600160a01b0316825260006101a060208301516111ae60208601826001600160a01b03169052565b5060408301516040850152606083015160608501526080830151608085015260a083015160a085015260c083015160c085015260e083015160e085015261010080840151818601525061012080840151818601525061014080840151818601525061016080840151818601525061018080840151828287015261123383870182611128565b9695505050505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561129257603f19888603018452611280858351611182565b94509285019290850190600101611264565b5092979650505050505050565b6020815260006111216020830184611182565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156112f1576112f16112b2565b604052919050565b600067ffffffffffffffff821115611313576113136112b2565b5060051b60200190565b6000602080838503121561133057600080fd5b825167ffffffffffffffff81111561134757600080fd5b8301601f8101851361135857600080fd5b805161136b611366826112f9565b6112c8565b81815260059190911b8201830190838101908783111561138a57600080fd5b928401925b828410156113b15783516113a2816110ec565b8252928401929084019061138f565b979650505050505050565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156113e457600080fd5b8151611121816110ec565b634e487b7160e01b600052601160045260246000fd5b60008219821115611418576114186113ef565b500190565b6000600019821415611431576114316113ef565b5060010190565b6000602080838503121561144b57600080fd5b825167ffffffffffffffff81111561146257600080fd5b8301601f8101851361147357600080fd5b8051611481611366826112f9565b81815260059190911b820183019083810190878311156114a057600080fd5b928401925b828410156113b15783516114b8816110ec565b825292840192908401906114a5565b6000602082840312156114d957600080fd5b5051919050565b600080604083850312156114f357600080fd5b8251801515811461150357600080fd5b6020939093015192949293505050565b600081600019048311821515161561152d5761152d6113ef565b500290565b60008261154f57634e487b7160e01b600052601260045260246000fd5b50049056fea264697066735822122000633454327713610bfc1341afc7e508e5d7f441aaf1f3e4e2859aa081c52fa064736f6c634300080a0033", + "sourceMap": "415:4873:50:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;440:33;;;;;;;;-1:-1:-1;;;;;321:32:63;;;303:51;;291:2;276:18;440:33:50;;;;;;;;2043:902;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;3156:273::-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;480:35::-;;;;;2043:902;2101:17;2130:24;2157:6;-1:-1:-1;;;;;2157:18:50;;:20;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;2157:20:50;;;;;;;;;;;;:::i;:::-;2130:47;-1:-1:-1;2187:21:50;2235:1;2218:117;2242:5;:12;2238:1;:16;2218:117;;;2299:5;-1:-1:-1;;;;;2279:25:50;:5;2285:1;2279:8;;;;;;;;:::i;:::-;;;;;;;-1:-1:-1;;;;;2279:14:50;;:16;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;2279:25:50;;2275:49;;;2306:18;2323:1;2306:18;;:::i;:::-;;;2275:49;2256:3;;;;:::i;:::-;;;;2218:117;;;;2353:20;2376:6;-1:-1:-1;;;;;2376:14:50;;:16;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;2353:39;;2402:16;2421:6;-1:-1:-1;;;;;2421:12:50;;:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;2402:33;;2446:27;2476:7;-1:-1:-1;;;;;2476:26:50;;:28;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;2476:28:50;;;;;;;;;;;;:::i;:::-;2446:58;;2514:16;2533:4;-1:-1:-1;;;;;2533:11:50;;:13;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;2514:32;;2565:27;2610:13;2595:29;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;-1:-1:-1;2565:59:50;-1:-1:-1;2634:22:50;2683:1;2666:246;2690:5;:12;2686:1;:16;2666:246;;;2747:5;-1:-1:-1;;;;;2727:25:50;:5;2733:1;2727:8;;;;;;;;:::i;:::-;;;;;;;-1:-1:-1;;;;;2727:14:50;;:16;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;2727:25:50;;2723:179;;;2800:49;2813:5;2819:1;2813:8;;;;;;;;:::i;:::-;;;;;;;2823:10;2835:6;2843:5;2800:12;:49::i;:::-;2772:9;2782:14;2772:25;;;;;;;;:::i;:::-;;;;;;;;;;:77;2867:19;2885:1;2867:19;;:::i;:::-;;;2723:179;2704:3;;;;:::i;:::-;;;;2666:246;;;-1:-1:-1;2929:9:50;;2043:902;-1:-1:-1;;;;;;;;;2043:902:50:o;3156:273::-;3210:15;;:::i;:::-;3237:20;3260:6;-1:-1:-1;;;;;3260:14:50;;:16;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3237:39;;3286:27;3316:7;-1:-1:-1;;;;;3316:26:50;;:28;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;3316:28:50;;;;;;;;;;;;:::i;:::-;3286:58;;3361:61;3374:4;3380:10;3392:4;-1:-1:-1;;;;;3392:11:50;;:13;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3407:6;-1:-1:-1;;;;;3407:12:50;;:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3361:12;:61::i;:::-;3354:68;3156:273;-1:-1:-1;;;;3156:273:50:o;3435:1851::-;3556:15;;:::i;:::-;3583:26;3631:10;:17;3612:37;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3612:37:50;;;;;;;;;;;;;;;;-1:-1:-1;3583:66:50;-1:-1:-1;3660:22:50;;3706:439;3730:10;:17;3726:1;:21;3706:439;;;3772:16;3791:10;3802:1;3791:13;;;;;;;;:::i;:::-;;;;;;;;;;;3840:41;;-1:-1:-1;;;3840:41:50;;-1:-1:-1;;;;;321:32:63;;;3840:41:50;;;303:51:63;3791:13:50;;-1:-1:-1;3822:15:50;;3840:31;;;;;276:18:63;;3840:41:50;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3942:33;;-1:-1:-1;;;3942:33:50;;-1:-1:-1;;;;;321:32:63;;;3942:33:50;;;303:51:63;3822:59:50;;-1:-1:-1;3899:17:50;;3919:22;;;;;;;3942:18;;276::63;;3942:33:50;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3919:57;;;;;;;;;;;;;9110:25:63;;9098:2;9083:18;;8964:177;3919:57:50;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3899:77;-1:-1:-1;3995:27:50;3899:77;3995:27;;:::i;:::-;;;4050:80;;;;;;;;4074:8;-1:-1:-1;;;;;4050:80:50;;;;;4099:7;4050:80;;;;4119:9;4050:80;;;4040:4;4045:1;4040:7;;;;;;;;:::i;:::-;;;;;;:90;;;;3754:391;;;3749:3;;;;;:::i;:::-;;;;3706:439;;;;4165:16;4184:4;-1:-1:-1;;;;;4184:10:50;;:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4220:47;;-1:-1:-1;;;4220:47:50;;-1:-1:-1;;;;;9678:15:63;;;4220:47:50;;;9660:34:63;9730:15;;;9710:18;;;9703:43;4165:31:50;;-1:-1:-1;4206:11:50;;4220:29;;;;;9595:18:63;;4220:47:50;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4206:61;;4277:18;4298:4;-1:-1:-1;;;;;4298:19:50;;:21;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:57;;-1:-1:-1;;;4298:57:50;;-1:-1:-1;;;;;321:32:63;;;4298:57:50;;;303:51:63;4298:42:50;;;;;;;276:18:63;;4298:57:50;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4277:78;;4365:16;4384:6;-1:-1:-1;;;;;4384:25:50;;4410:4;-1:-1:-1;;;;;4410:19:50;;:21;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4384:48;;-1:-1:-1;;;;;;4384:48:50;;;;;;;-1:-1:-1;;;;;321:32:63;;;4384:48:50;;;303:51:63;276:18;;4384:48:50;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4365:67;;4443:24;4470:4;-1:-1:-1;;;;;4470:18:50;;4489:4;-1:-1:-1;;;;;4489:16:50;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4470:38;;;;;;;;;;;;;9110:25:63;;9098:2;9083:18;;8964:177;4470:38:50;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4443:65;;4518:23;4544:6;-1:-1:-1;;;;;4544:25:50;;4570:4;-1:-1:-1;;;;;4570:21:50;;:23;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4544:50;;-1:-1:-1;;;;;;4544:50:50;;;;;;;-1:-1:-1;;;;;321:32:63;;;4544:50:50;;;303:51:63;276:18;;4544:50:50;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4518:76;;4607:24;4635:4;-1:-1:-1;;;;;4635:12:50;;4648:4;-1:-1:-1;;;;;4648:21:50;;:23;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4635:37;;-1:-1:-1;;;;;;4635:37:50;;;;;;;-1:-1:-1;;;;;321:32:63;;;4635:37:50;;;303:51:63;276:18;;4635:37:50;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4604:68;;;4690:589;;;;;;;;4734:4;-1:-1:-1;;;;;4690:589:50;;;;;4770:10;-1:-1:-1;;;;;4690:589:50;;;;;4939:16;4690:589;;;;4812:15;4690:589;;;;4859:16;4690:589;;;;4899:8;4690:589;;;;5023:4;5005:15;4986:16;:34;;;;:::i;:::-;:41;;;;:::i;:::-;4690:589;;;;;;;;;;5112:4;5088:21;5101:8;5053:10;5088:21;:::i;:::-;:28;;;;:::i;:::-;4690:589;;;;5145:4;-1:-1:-1;;;;;5145:20:50;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4690:589;;;;5192:14;4690:589;;;;5233:3;4690:589;;;;5264:4;4690:589;;;4683:596;;;;;;;;;;;3435:1851;;;;;;:::o;-1:-1:-1:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;365:131:63:-;-1:-1:-1;;;;;440:31:63;;430:42;;420:70;;486:1;483;476:12;420:70;365:131;:::o;501:247::-;560:6;613:2;601:9;592:7;588:23;584:32;581:52;;;629:1;626;619:12;581:52;668:9;655:23;687:31;712:5;687:31;:::i;:::-;737:5;501:247;-1:-1:-1;;;501:247:63:o;753:640::-;818:3;856:5;850:12;883:6;878:3;871:19;909:4;938:2;933:3;929:12;922:19;;975:2;968:5;964:14;996:1;1006:362;1020:6;1017:1;1014:13;1006:362;;;1079:13;;1121:9;;-1:-1:-1;;;;;1117:35:63;1105:48;;1193:11;;;1187:18;1173:12;;;1166:40;1229:4;1273:11;;;1267:18;1253:12;;;1246:40;1315:4;1306:14;;;;1343:15;;;;1149:1;1035:9;1006:362;;;-1:-1:-1;1384:3:63;;753:640;-1:-1:-1;;;;;753:640:63:o;1398:1112::-;1524:12;;-1:-1:-1;;;;;93:31:63;81:44;;1449:3;1477:6;1588:4;1581:5;1577:16;1571:23;1603:61;1658:4;1653:3;1649:14;1635:12;-1:-1:-1;;;;;93:31:63;81:44;;14:117;1603:61;;1713:4;1706:5;1702:16;1696:23;1689:4;1684:3;1680:14;1673:47;1769:4;1762:5;1758:16;1752:23;1745:4;1740:3;1736:14;1729:47;1825:4;1818:5;1814:16;1808:23;1801:4;1796:3;1792:14;1785:47;1881:4;1874:5;1870:16;1864:23;1857:4;1852:3;1848:14;1841:47;1937:4;1930:5;1926:16;1920:23;1913:4;1908:3;1904:14;1897:47;1993:4;1986:5;1982:16;1976:23;1969:4;1964:3;1960:14;1953:47;2019:6;2072:2;2065:5;2061:14;2055:21;2050:2;2045:3;2041:12;2034:43;;2096:6;2149:2;2142:5;2138:14;2132:21;2127:2;2122:3;2118:12;2111:43;;2173:6;2226:2;2219:5;2215:14;2209:21;2204:2;2199:3;2195:12;2188:43;;2250:6;2303:2;2296:5;2292:14;2286:21;2281:2;2276:3;2272:12;2265:43;;2327:6;2381:2;2374:5;2370:14;2364:21;2415:2;2410;2405:3;2401:12;2394:24;2434:70;2500:2;2495:3;2491:12;2475:14;2434:70;:::i;:::-;2427:77;1398:1112;-1:-1:-1;;;;;;1398:1112:63:o;2515:846::-;2711:4;2740:2;2780;2769:9;2765:18;2810:2;2799:9;2792:21;2833:6;2868;2862:13;2899:6;2891;2884:22;2937:2;2926:9;2922:18;2915:25;;2999:2;2989:6;2986:1;2982:14;2971:9;2967:30;2963:39;2949:53;;3037:2;3029:6;3025:15;3058:1;3068:264;3082:6;3079:1;3076:13;3068:264;;;3175:2;3171:7;3159:9;3151:6;3147:22;3143:36;3138:3;3131:49;3203;3245:6;3236;3230:13;3203:49;:::i;:::-;3193:59;-1:-1:-1;3310:12:63;;;;3275:15;;;;3104:1;3097:9;3068:264;;;-1:-1:-1;3349:6:63;;2515:846;-1:-1:-1;;;;;;;2515:846:63:o;3636:263::-;3819:2;3808:9;3801:21;3782:4;3839:54;3889:2;3878:9;3874:18;3866:6;3839:54;:::i;4132:127::-;4193:10;4188:3;4184:20;4181:1;4174:31;4224:4;4221:1;4214:15;4248:4;4245:1;4238:15;4264:275;4335:2;4329:9;4400:2;4381:13;;-1:-1:-1;;4377:27:63;4365:40;;4435:18;4420:34;;4456:22;;;4417:62;4414:88;;;4482:18;;:::i;:::-;4518:2;4511:22;4264:275;;-1:-1:-1;4264:275:63:o;4544:194::-;4615:4;4648:18;4640:6;4637:30;4634:56;;;4670:18;;:::i;:::-;-1:-1:-1;4715:1:63;4711:14;4727:4;4707:25;;4544:194::o;4743:985::-;4856:6;4887:2;4930;4918:9;4909:7;4905:23;4901:32;4898:52;;;4946:1;4943;4936:12;4898:52;4979:9;4973:16;5012:18;5004:6;5001:30;4998:50;;;5044:1;5041;5034:12;4998:50;5067:22;;5120:4;5112:13;;5108:27;-1:-1:-1;5098:55:63;;5149:1;5146;5139:12;5098:55;5178:2;5172:9;5201:71;5217:54;5268:2;5217:54;:::i;:::-;5201:71;:::i;:::-;5306:15;;;5388:1;5384:10;;;;5376:19;;5372:28;;;5337:12;;;;5412:19;;;5409:39;;;5444:1;5441;5434:12;5409:39;5468:11;;;;5488:210;5504:6;5499:3;5496:15;5488:210;;;5577:3;5571:10;5594:31;5619:5;5594:31;:::i;:::-;5638:18;;5521:12;;;;5676;;;;5488:210;;;5717:5;4743:985;-1:-1:-1;;;;;;;4743:985:63:o;5733:127::-;5794:10;5789:3;5785:20;5782:1;5775:31;5825:4;5822:1;5815:15;5849:4;5846:1;5839:15;5865:251;5935:6;5988:2;5976:9;5967:7;5963:23;5959:32;5956:52;;;6004:1;6001;5994:12;5956:52;6036:9;6030:16;6055:31;6080:5;6055:31;:::i;6121:127::-;6182:10;6177:3;6173:20;6170:1;6163:31;6213:4;6210:1;6203:15;6237:4;6234:1;6227:15;6253:128;6293:3;6324:1;6320:6;6317:1;6314:13;6311:39;;;6330:18;;:::i;:::-;-1:-1:-1;6366:9:63;;6253:128::o;6386:135::-;6425:3;-1:-1:-1;;6446:17:63;;6443:43;;;6466:18;;:::i;:::-;-1:-1:-1;6513:1:63;6502:13;;6386:135::o;7080:983::-;7191:6;7222:2;7265;7253:9;7244:7;7240:23;7236:32;7233:52;;;7281:1;7278;7271:12;7233:52;7314:9;7308:16;7347:18;7339:6;7336:30;7333:50;;;7379:1;7376;7369:12;7333:50;7402:22;;7455:4;7447:13;;7443:27;-1:-1:-1;7433:55:63;;7484:1;7481;7474:12;7433:55;7513:2;7507:9;7536:71;7552:54;7603:2;7552:54;:::i;7536:71::-;7641:15;;;7723:1;7719:10;;;;7711:19;;7707:28;;;7672:12;;;;7747:19;;;7744:39;;;7779:1;7776;7769:12;7744:39;7803:11;;;;7823:210;7839:6;7834:3;7831:15;7823:210;;;7912:3;7906:10;7929:31;7954:5;7929:31;:::i;:::-;7973:18;;7856:12;;;;8011;;;;7823:210;;8567:184;8637:6;8690:2;8678:9;8669:7;8665:23;8661:32;8658:52;;;8706:1;8703;8696:12;8658:52;-1:-1:-1;8729:16:63;;8567:184;-1:-1:-1;8567:184:63:o;10253:338::-;10329:6;10337;10390:2;10378:9;10369:7;10365:23;10361:32;10358:52;;;10406:1;10403;10396:12;10358:52;10438:9;10432:16;10491:5;10484:13;10477:21;10470:5;10467:32;10457:60;;10513:1;10510;10503:12;10457:60;10581:2;10566:18;;;;10560:25;10536:5;;10560:25;;-1:-1:-1;;;10253:338:63:o;10596:168::-;10636:7;10702:1;10698;10694:6;10690:14;10687:1;10684:21;10679:1;10672:9;10665:17;10661:45;10658:71;;;10709:18;;:::i;:::-;-1:-1:-1;10749:9:63;;10596:168::o;10769:217::-;10809:1;10835;10825:132;;10879:10;10874:3;10870:20;10867:1;10860:31;10914:4;10911:1;10904:15;10942:4;10939:1;10932:15;10825:132;-1:-1:-1;10971:9:63;;10769:217::o", + "linkReferences": {}, + "immutableReferences": { + "13165": [ + { + "start": 86, + "length": 32 + }, + { + "start": 965, + "length": 32 + }, + { + "start": 1712, + "length": 32 + }, + { + "start": 3680, + "length": 32 + } + ], + "13168": [ + { + "start": 218, + "length": 32 + }, + { + "start": 258, + "length": 32 + }, + { + "start": 591, + "length": 32 + }, + { + "start": 725, + "length": 32 + }, + { + "start": 1469, + "length": 32 + }, + { + "start": 1842, + "length": 32 + } + ] + } + } +} \ No newline at end of file diff --git a/src/lib/turbo/abi/TurboMaster.json b/src/lib/turbo/abi/TurboMaster.json new file mode 100644 index 00000000..a0de127f --- /dev/null +++ b/src/lib/turbo/abi/TurboMaster.json @@ -0,0 +1,545 @@ +{ + "abi": [ + { + "type": "constructor", + "inputs": [ + { + "internalType": "contract Comptroller", + "name": "_pool", + "type": "address" + }, + { + "internalType": "contract ERC20", + "name": "_fei", + "type": "address" + }, + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "contract Authority", + "name": "_authority", + "type": "address" + } + ] + }, + { + "type": "function", + "name": "REVERSE_REGISTRAR", + "inputs": [], + "outputs": [ + { + "internalType": "contract IReverseRegistrar", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "authority", + "inputs": [], + "outputs": [ + { + "internalType": "contract Authority", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "booster", + "inputs": [], + "outputs": [ + { + "internalType": "contract TurboBooster", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "clerk", + "inputs": [], + "outputs": [ + { + "internalType": "contract TurboClerk", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "createSafe", + "inputs": [ + { + "internalType": "contract ERC20", + "name": "asset", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "contract TurboSafe", + "name": "safe", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "defaultSafeAuthority", + "inputs": [], + "outputs": [ + { + "internalType": "contract Authority", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "fei", + "inputs": [], + "outputs": [ + { + "internalType": "contract ERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getAllSafes", + "inputs": [], + "outputs": [ + { + "internalType": "contract TurboSafe[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getSafeId", + "inputs": [ + { + "internalType": "contract TurboSafe", + "name": "", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getTotalBoostedAgainstCollateral", + "inputs": [ + { + "internalType": "contract ERC20", + "name": "", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getTotalBoostedForVault", + "inputs": [ + { + "internalType": "contract ERC4626", + "name": "", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "onSafeBoost", + "inputs": [ + { + "internalType": "contract ERC20", + "name": "asset", + "type": "address" + }, + { + "internalType": "contract ERC4626", + "name": "vault", + "type": "address" + }, + { + "internalType": "uint256", + "name": "feiAmount", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "onSafeLess", + "inputs": [ + { + "internalType": "contract ERC20", + "name": "asset", + "type": "address" + }, + { + "internalType": "contract ERC4626", + "name": "vault", + "type": "address" + }, + { + "internalType": "uint256", + "name": "feiAmount", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "pool", + "inputs": [], + "outputs": [ + { + "internalType": "contract Comptroller", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "safes", + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "contract TurboSafe", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "setAuthority", + "inputs": [ + { + "internalType": "contract Authority", + "name": "newAuthority", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setBooster", + "inputs": [ + { + "internalType": "contract TurboBooster", + "name": "newBooster", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setClerk", + "inputs": [ + { + "internalType": "contract TurboClerk", + "name": "newClerk", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setDefaultSafeAuthority", + "inputs": [ + { + "internalType": "contract Authority", + "name": "newDefaultSafeAuthority", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setENSName", + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setOwner", + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "sweep", + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "contract ERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "totalBoosted", + "inputs": [], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "event", + "name": "AuthorityUpdated", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "newAuthority", + "type": "address", + "indexed": true + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BoosterUpdated", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "newBooster", + "type": "address", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ClerkUpdated", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "newClerk", + "type": "address", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "DefaultSafeAuthorityUpdated", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "newDefaultSafeAuthority", + "type": "address", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnerUpdated", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "newOwner", + "type": "address", + "indexed": true + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "TokenSweeped", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "to", + "type": "address", + "indexed": true + }, + { + "name": "token", + "type": "address", + "indexed": true + }, + { + "name": "amount", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "TurboSafeCreated", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "asset", + "type": "address", + "indexed": true + }, + { + "name": "safe", + "type": "address", + "indexed": false + }, + { + "name": "id", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + } + ] +} \ No newline at end of file diff --git a/src/lib/turbo/abi/TurboRouter.json b/src/lib/turbo/abi/TurboRouter.json new file mode 100644 index 00000000..c2f0b9d2 --- /dev/null +++ b/src/lib/turbo/abi/TurboRouter.json @@ -0,0 +1,786 @@ +{ + "abi": [ + { + "type": "constructor", + "inputs": [ + { + "internalType": "contract TurboMaster", + "name": "_master", + "type": "address" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "contract IWETH9", + "name": "weth", + "type": "address" + } + ] + }, + { + "type": "function", + "name": "REVERSE_REGISTRAR", + "inputs": [], + "outputs": [ + { + "internalType": "contract IReverseRegistrar", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "WETH9", + "inputs": [], + "outputs": [ + { + "internalType": "contract IWETH9", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "approve", + "inputs": [ + { + "internalType": "contract ERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "boost", + "inputs": [ + { + "internalType": "contract TurboSafe", + "name": "safe", + "type": "address" + }, + { + "internalType": "contract ERC4626", + "name": "vault", + "type": "address" + }, + { + "internalType": "uint256", + "name": "feiAmount", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "createSafe", + "inputs": [ + { + "internalType": "contract ERC20", + "name": "underlying", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "contract TurboSafe", + "name": "safe", + "type": "address" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "createSafeAndDeposit", + "inputs": [ + { + "internalType": "contract ERC20", + "name": "underlying", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minSharesOut", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "contract TurboSafe", + "name": "safe", + "type": "address" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "createSafeAndDepositAndBoost", + "inputs": [ + { + "internalType": "contract ERC20", + "name": "underlying", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minSharesOut", + "type": "uint256" + }, + { + "internalType": "contract ERC4626", + "name": "boostedVault", + "type": "address" + }, + { + "internalType": "uint256", + "name": "boostedFeiAmount", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "contract TurboSafe", + "name": "safe", + "type": "address" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "createSafeAndDepositAndBoostMany", + "inputs": [ + { + "internalType": "contract ERC20", + "name": "underlying", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minSharesOut", + "type": "uint256" + }, + { + "internalType": "contract ERC4626[]", + "name": "boostedVaults", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "boostedFeiAmounts", + "type": "uint256[]" + } + ], + "outputs": [ + { + "internalType": "contract TurboSafe", + "name": "safe", + "type": "address" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "deposit", + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "safe", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minSharesOut", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "less", + "inputs": [ + { + "internalType": "contract TurboSafe", + "name": "safe", + "type": "address" + }, + { + "internalType": "contract ERC4626", + "name": "vault", + "type": "address" + }, + { + "internalType": "uint256", + "name": "feiAmount", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "lessAll", + "inputs": [ + { + "internalType": "contract TurboSafe", + "name": "safe", + "type": "address" + }, + { + "internalType": "contract ERC4626", + "name": "vault", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "master", + "inputs": [], + "outputs": [ + { + "internalType": "contract TurboMaster", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "mint", + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "safe", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxAmountIn", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "multicall", + "inputs": [ + { + "internalType": "bytes[]", + "name": "data", + "type": "bytes[]" + } + ], + "outputs": [ + { + "internalType": "bytes[]", + "name": "results", + "type": "bytes[]" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "pullToken", + "inputs": [ + { + "internalType": "contract ERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "redeem", + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "safe", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOut", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "refundETH", + "inputs": [], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "selfPermit", + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "selfPermitAllowed", + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expiry", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "selfPermitAllowedIfNecessary", + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expiry", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "selfPermitIfNecessary", + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "slurp", + "inputs": [ + { + "internalType": "contract TurboSafe", + "name": "safe", + "type": "address" + }, + { + "internalType": "contract ERC4626", + "name": "vault", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "slurpAndLessAll", + "inputs": [ + { + "internalType": "contract TurboSafe", + "name": "safe", + "type": "address" + }, + { + "internalType": "contract ERC4626", + "name": "vault", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "sweep", + "inputs": [ + { + "internalType": "contract TurboSafe", + "name": "safe", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "contract ERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "sweepAll", + "inputs": [ + { + "internalType": "contract TurboSafe", + "name": "safe", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "contract ERC20", + "name": "token", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "sweepToken", + "inputs": [ + { + "internalType": "contract ERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountMinimum", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "unwrapWETH9", + "inputs": [ + { + "internalType": "uint256", + "name": "amountMinimum", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "withdraw", + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "safe", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxSharesOut", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "wrapWETH9", + "inputs": [], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "error", + "name": "MaxAmountError", + "inputs": [] + }, + { + "type": "error", + "name": "MaxSharesError", + "inputs": [] + }, + { + "type": "error", + "name": "MinAmountError", + "inputs": [] + }, + { + "type": "error", + "name": "MinSharesError", + "inputs": [] + }, + { + "type": "receive" + } + ] +} \ No newline at end of file diff --git a/src/lib/turbo/abi/TurboSafe.json b/src/lib/turbo/abi/TurboSafe.json new file mode 100644 index 00000000..1b6ae68d --- /dev/null +++ b/src/lib/turbo/abi/TurboSafe.json @@ -0,0 +1,1095 @@ +{ + "abi": [ + { + "type": "constructor", + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "contract Authority", + "name": "_authority", + "type": "address" + }, + { + "internalType": "contract ERC20", + "name": "_asset", + "type": "address" + } + ] + }, + { + "type": "function", + "name": "DOMAIN_SEPARATOR", + "inputs": [], + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "allowance", + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "approve", + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "asset", + "inputs": [], + "outputs": [ + { + "internalType": "contract ERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "assetTurboCToken", + "inputs": [], + "outputs": [ + { + "internalType": "contract CERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "authority", + "inputs": [], + "outputs": [ + { + "internalType": "contract Authority", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "balanceOf", + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "boost", + "inputs": [ + { + "internalType": "contract ERC4626", + "name": "vault", + "type": "address" + }, + { + "internalType": "uint256", + "name": "feiAmount", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "convertToAssets", + "inputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "convertToShares", + "inputs": [ + { + "internalType": "uint256", + "name": "assets", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "decimals", + "inputs": [], + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "deposit", + "inputs": [ + { + "internalType": "uint256", + "name": "assets", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "fei", + "inputs": [], + "outputs": [ + { + "internalType": "contract ERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "feiTurboCToken", + "inputs": [], + "outputs": [ + { + "internalType": "contract CERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getTotalFeiBoostedForVault", + "inputs": [ + { + "internalType": "contract ERC4626", + "name": "", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "gib", + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "assetAmount", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "less", + "inputs": [ + { + "internalType": "contract ERC4626", + "name": "vault", + "type": "address" + }, + { + "internalType": "uint256", + "name": "feiAmount", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "master", + "inputs": [], + "outputs": [ + { + "internalType": "contract TurboMaster", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "maxDeposit", + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "maxMint", + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "maxRedeem", + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "maxWithdraw", + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "mint", + "inputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "assets", + "type": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "name", + "inputs": [], + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "nonces", + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "permit", + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "pool", + "inputs": [], + "outputs": [ + { + "internalType": "contract Comptroller", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "previewDeposit", + "inputs": [ + { + "internalType": "uint256", + "name": "assets", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "previewMint", + "inputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "previewRedeem", + "inputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "previewWithdraw", + "inputs": [ + { + "internalType": "uint256", + "name": "assets", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "redeem", + "inputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "assets", + "type": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setAuthority", + "inputs": [ + { + "internalType": "contract Authority", + "name": "newAuthority", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setOwner", + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "slurp", + "inputs": [ + { + "internalType": "contract ERC4626", + "name": "vault", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "safeInterestAmount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "sweep", + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "contract ERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "symbol", + "inputs": [], + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "totalAssets", + "inputs": [], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "totalFeiBoosted", + "inputs": [], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "totalSupply", + "inputs": [], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transfer", + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "transferFrom", + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "withdraw", + "inputs": [ + { + "internalType": "uint256", + "name": "assets", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "outputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "Approval", + "inputs": [ + { + "name": "owner", + "type": "address", + "indexed": true + }, + { + "name": "spender", + "type": "address", + "indexed": true + }, + { + "name": "amount", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "AuthorityUpdated", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "newAuthority", + "type": "address", + "indexed": true + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Deposit", + "inputs": [ + { + "name": "caller", + "type": "address", + "indexed": true + }, + { + "name": "owner", + "type": "address", + "indexed": true + }, + { + "name": "assets", + "type": "uint256", + "indexed": false + }, + { + "name": "shares", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnerUpdated", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "newOwner", + "type": "address", + "indexed": true + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SafeGibbed", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "to", + "type": "address", + "indexed": true + }, + { + "name": "assetAmount", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "TokenSweeped", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "to", + "type": "address", + "indexed": true + }, + { + "name": "token", + "type": "address", + "indexed": true + }, + { + "name": "amount", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Transfer", + "inputs": [ + { + "name": "from", + "type": "address", + "indexed": true + }, + { + "name": "to", + "type": "address", + "indexed": true + }, + { + "name": "amount", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "VaultBoosted", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "vault", + "type": "address", + "indexed": true + }, + { + "name": "feiAmount", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "VaultLessened", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "vault", + "type": "address", + "indexed": true + }, + { + "name": "feiAmount", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "VaultSlurped", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "vault", + "type": "address", + "indexed": true + }, + { + "name": "protocolFeeAmount", + "type": "uint256", + "indexed": false + }, + { + "name": "safeInterestAmount", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Withdraw", + "inputs": [ + { + "name": "caller", + "type": "address", + "indexed": true + }, + { + "name": "receiver", + "type": "address", + "indexed": true + }, + { + "name": "owner", + "type": "address", + "indexed": true + }, + { + "name": "assets", + "type": "uint256", + "indexed": false + }, + { + "name": "shares", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + } + ] +} \ No newline at end of file diff --git a/src/lib/turbo/abi/TurboSavior.json b/src/lib/turbo/abi/TurboSavior.json new file mode 100644 index 00000000..3bea9515 --- /dev/null +++ b/src/lib/turbo/abi/TurboSavior.json @@ -0,0 +1,281 @@ +{ + "abi": [ + { + "type": "constructor", + "inputs": [ + { + "internalType": "contract TurboMaster", + "name": "_master", + "type": "address" + }, + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "contract Authority", + "name": "_authority", + "type": "address" + } + ] + }, + { + "type": "function", + "name": "REVERSE_REGISTRAR", + "inputs": [], + "outputs": [ + { + "internalType": "contract IReverseRegistrar", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "authority", + "inputs": [], + "outputs": [ + { + "internalType": "contract Authority", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "fei", + "inputs": [], + "outputs": [ + { + "internalType": "contract Fei", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "feiTurboCToken", + "inputs": [], + "outputs": [ + { + "internalType": "contract CERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "master", + "inputs": [], + "outputs": [ + { + "internalType": "contract TurboMaster", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "minDebtPercentageForSaving", + "inputs": [], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "pool", + "inputs": [], + "outputs": [ + { + "internalType": "contract Comptroller", + "name": "", + "type": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "save", + "inputs": [ + { + "internalType": "contract TurboSafe", + "name": "safe", + "type": "address" + }, + { + "internalType": "contract ERC4626", + "name": "vault", + "type": "address" + }, + { + "internalType": "uint256", + "name": "feiAmount", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setAuthority", + "inputs": [ + { + "internalType": "contract Authority", + "name": "newAuthority", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setENSName", + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setMinDebtPercentageForSaving", + "inputs": [ + { + "internalType": "uint256", + "name": "newMinDebtPercentageForSaving", + "type": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setOwner", + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "AuthorityUpdated", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "newAuthority", + "type": "address", + "indexed": true + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "MinDebtPercentageForSavingUpdated", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "newDefaultFeePercentage", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnerUpdated", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "newOwner", + "type": "address", + "indexed": true + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SafeSaved", + "inputs": [ + { + "name": "user", + "type": "address", + "indexed": true + }, + { + "name": "safe", + "type": "address", + "indexed": true + }, + { + "name": "vault", + "type": "address", + "indexed": true + }, + { + "name": "feiAmount", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + } + ] +} \ No newline at end of file diff --git a/src/lib/turbo/abi/comptroller.json b/src/lib/turbo/abi/comptroller.json new file mode 100644 index 00000000..9a4d7dc2 --- /dev/null +++ b/src/lib/turbo/abi/comptroller.json @@ -0,0 +1,2144 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "action", + "type": "string" + }, + { + "indexed": false, + "internalType": "bool", + "name": "pauseState", + "type": "bool" + } + ], + "name": "ActionPaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract CToken", + "name": "cToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "action", + "type": "string" + }, + { + "indexed": false, + "internalType": "bool", + "name": "pauseState", + "type": "bool" + } + ], + "name": "ActionPaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "rewardsDistributor", + "type": "address" + } + ], + "name": "AddedRewardsDistributor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "AutoImplementationsToggled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "error", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "info", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "detail", + "type": "uint256" + } + ], + "name": "Failure", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract CToken", + "name": "cToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "MarketEntered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract CToken", + "name": "cToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "MarketExited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract CToken", + "name": "cToken", + "type": "address" + } + ], + "name": "MarketListed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract CToken", + "name": "cToken", + "type": "address" + } + ], + "name": "MarketUnlisted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract CToken", + "name": "cToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newBorrowCap", + "type": "uint256" + } + ], + "name": "NewBorrowCap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldBorrowCapGuardian", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newBorrowCapGuardian", + "type": "address" + } + ], + "name": "NewBorrowCapGuardian", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldCloseFactorMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newCloseFactorMantissa", + "type": "uint256" + } + ], + "name": "NewCloseFactor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract CToken", + "name": "cToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldCollateralFactorMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newCollateralFactorMantissa", + "type": "uint256" + } + ], + "name": "NewCollateralFactor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newLiquidationIncentiveMantissa", + "type": "uint256" + } + ], + "name": "NewLiquidationIncentive", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldPauseGuardian", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newPauseGuardian", + "type": "address" + } + ], + "name": "NewPauseGuardian", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract PriceOracle", + "name": "oldPriceOracle", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract PriceOracle", + "name": "newPriceOracle", + "type": "address" + } + ], + "name": "NewPriceOracle", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract CToken", + "name": "cToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newSupplyCap", + "type": "uint256" + } + ], + "name": "NewSupplyCap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "enforce", + "type": "bool" + } + ], + "name": "WhitelistEnforcementChanged", + "type": "event" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "distributor", + "type": "address" + } + ], + "name": "_addRewardsDistributor", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "_afterNonReentrant", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "contract Unitroller", + "name": "unitroller", + "type": "address" + } + ], + "name": "_become", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "_becomeImplementation", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "_beforeNonReentrant", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "_borrowGuardianPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bool", + "name": "isCEther", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "constructorData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "collateralFactorMantissa", + "type": "uint256" + } + ], + "name": "_deployMarket", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "_mintGuardianPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newBorrowCapGuardian", + "type": "address" + } + ], + "name": "_setBorrowCapGuardian", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "contract CToken", + "name": "cToken", + "type": "address" + }, + { + "internalType": "bool", + "name": "state", + "type": "bool" + } + ], + "name": "_setBorrowPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newCloseFactorMantissa", + "type": "uint256" + } + ], + "name": "_setCloseFactor", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "contract CToken", + "name": "cToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "newCollateralFactorMantissa", + "type": "uint256" + } + ], + "name": "_setCollateralFactor", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newLiquidationIncentiveMantissa", + "type": "uint256" + } + ], + "name": "_setLiquidationIncentive", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "contract CToken[]", + "name": "cTokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "newBorrowCaps", + "type": "uint256[]" + } + ], + "name": "_setMarketBorrowCaps", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "contract CToken[]", + "name": "cTokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "newSupplyCaps", + "type": "uint256[]" + } + ], + "name": "_setMarketSupplyCaps", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "contract CToken", + "name": "cToken", + "type": "address" + }, + { + "internalType": "bool", + "name": "state", + "type": "bool" + } + ], + "name": "_setMintPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newPauseGuardian", + "type": "address" + } + ], + "name": "_setPauseGuardian", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "contract PriceOracle", + "name": "newOracle", + "type": "address" + } + ], + "name": "_setPriceOracle", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bool", + "name": "state", + "type": "bool" + } + ], + "name": "_setSeizePaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bool", + "name": "state", + "type": "bool" + } + ], + "name": "_setTransferPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bool", + "name": "enforce", + "type": "bool" + } + ], + "name": "_setWhitelistEnforcement", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address[]", + "name": "suppliers", + "type": "address[]" + }, + { + "internalType": "bool[]", + "name": "statuses", + "type": "bool[]" + } + ], + "name": "_setWhitelistStatuses", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "_toggleAutoImplementations", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "contract CToken", + "name": "cToken", + "type": "address" + } + ], + "name": "_unsupportMarket", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "accountAssets", + "outputs": [ + { + "internalType": "contract CToken", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "adminHasRights", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "allBorrowers", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "allMarkets", + "outputs": [ + { + "internalType": "contract CToken", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "autoImplementation", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "cToken", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "borrowAllowed", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "borrowCapGuardian", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "borrowCaps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "borrowGuardianPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "cToken", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "borrowVerify", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "cToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "accountBorrowsNew", + "type": "uint256" + } + ], + "name": "borrowWithinLimits", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "cTokensByUnderlying", + "outputs": [ + { + "internalType": "contract CToken", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "contract CToken", + "name": "cToken", + "type": "address" + } + ], + "name": "checkMembership", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "closeFactorMantissa", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "comptrollerImplementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "enforceWhitelist", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address[]", + "name": "cTokens", + "type": "address[]" + } + ], + "name": "enterMarkets", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "cTokenAddress", + "type": "address" + } + ], + "name": "exitMarket", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "fuseAdminHasRights", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getAccountLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getAllBorrowers", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getAllMarkets", + "outputs": [ + { + "internalType": "contract CToken[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getAssetsIn", + "outputs": [ + { + "internalType": "contract CToken[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "cTokenModify", + "type": "address" + }, + { + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "getHypotheticalAccountLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getRewardsDistributors", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getWhitelist", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isComptroller", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "contract CToken", + "name": "cToken", + "type": "address" + } + ], + "name": "isDeprecated", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "cTokenBorrowed", + "type": "address" + }, + { + "internalType": "address", + "name": "cTokenCollateral", + "type": "address" + }, + { + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "liquidateBorrowAllowed", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "cTokenBorrowed", + "type": "address" + }, + { + "internalType": "address", + "name": "cTokenCollateral", + "type": "address" + }, + { + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "actualRepayAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "liquidateBorrowVerify", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "cTokenBorrowed", + "type": "address" + }, + { + "internalType": "address", + "name": "cTokenCollateral", + "type": "address" + }, + { + "internalType": "uint256", + "name": "actualRepayAmount", + "type": "uint256" + } + ], + "name": "liquidateCalculateSeizeTokens", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "liquidationIncentiveMantissa", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "markets", + "outputs": [ + { + "internalType": "bool", + "name": "isListed", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "collateralFactorMantissa", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "cToken", + "type": "address" + }, + { + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + } + ], + "name": "mintAllowed", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "mintGuardianPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "cToken", + "type": "address" + }, + { + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "internalType": "uint256", + "name": "actualMintAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "mintTokens", + "type": "uint256" + } + ], + "name": "mintVerify", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "cToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "exchangeRateMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "accountTokens", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + } + ], + "name": "mintWithinLimits", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "oracle", + "outputs": [ + { + "internalType": "contract PriceOracle", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "pauseGuardian", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "pendingAdmin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "pendingComptrollerImplementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "cToken", + "type": "address" + }, + { + "internalType": "address", + "name": "redeemer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "redeemAllowed", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "cToken", + "type": "address" + }, + { + "internalType": "address", + "name": "redeemer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "redeemAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "redeemVerify", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "cToken", + "type": "address" + }, + { + "internalType": "address", + "name": "payer", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "repayBorrowAllowed", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "cToken", + "type": "address" + }, + { + "internalType": "address", + "name": "payer", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "actualRepayAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowerIndex", + "type": "uint256" + } + ], + "name": "repayBorrowVerify", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "rewardsDistributors", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "cTokenCollateral", + "type": "address" + }, + { + "internalType": "address", + "name": "cTokenBorrowed", + "type": "address" + }, + { + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "seizeAllowed", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "seizeGuardianPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "cTokenCollateral", + "type": "address" + }, + { + "internalType": "address", + "name": "cTokenBorrowed", + "type": "address" + }, + { + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "seizeVerify", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "suppliers", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "supplyCaps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "cToken", + "type": "address" + }, + { + "internalType": "address", + "name": "src", + "type": "address" + }, + { + "internalType": "address", + "name": "dst", + "type": "address" + }, + { + "internalType": "uint256", + "name": "transferTokens", + "type": "uint256" + } + ], + "name": "transferAllowed", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "transferGuardianPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "cToken", + "type": "address" + }, + { + "internalType": "address", + "name": "src", + "type": "address" + }, + { + "internalType": "address", + "name": "dst", + "type": "address" + }, + { + "internalType": "uint256", + "name": "transferTokens", + "type": "uint256" + } + ], + "name": "transferVerify", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "whitelist", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "whitelistArray", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/src/lib/turbo/fetchers/getApprovedCollaterals.tsx b/src/lib/turbo/fetchers/getApprovedCollaterals.tsx new file mode 100644 index 00000000..15f16d25 --- /dev/null +++ b/src/lib/turbo/fetchers/getApprovedCollaterals.tsx @@ -0,0 +1,34 @@ +import { providers } from "@0xsequence/multicall"; +import { JsonRpcProvider, Web3Provider } from "@ethersproject/providers"; +import { FEI } from "../utils/constants"; +import { + createCERC20, + createCERC20Delegate, + createTurboComptroller, +} from "../utils/turboContracts"; + +export const getTurboApprovedCollateral = async ( + provider: JsonRpcProvider | Web3Provider +): Promise => { + const multicallProvider = new providers.MulticallProvider(provider); + + const TurboPool = createTurboComptroller(multicallProvider, 1); + console.log({ TurboPool }); + const markets: string[] = await TurboPool.callStatic.getAllMarkets(); + + console.log({ markets }); + + const underlyings: string[] = ( + await Promise.all( + markets.map(async (market) => { + const CERC20 = createCERC20Delegate(multicallProvider, market); + const underlying: string = await CERC20.callStatic.underlying(); + return underlying; + }) + ) + ).filter((token) => token.toLowerCase() !== FEI.toLowerCase()); + + console.log({ underlyings, markets }); + + return underlyings; +}; diff --git a/src/lib/turbo/fetchers/getIsUserAuthorizedToCreateSafes.tsx b/src/lib/turbo/fetchers/getIsUserAuthorizedToCreateSafes.tsx new file mode 100644 index 00000000..bbcd747f --- /dev/null +++ b/src/lib/turbo/fetchers/getIsUserAuthorizedToCreateSafes.tsx @@ -0,0 +1,21 @@ +import { providers } from "ethers" +import { createTurboAuthority, ITurboMaster } from "lib/turbo/utils/turboContracts" + +export const isUserAuthorizedToCreateSafes = async ( + provider: providers.BaseProvider, + authority: string, + user: string, + target: string +) => { + const turboBoosterContract = await createTurboAuthority(provider, authority) + + const functionSig = ITurboMaster.getSighash('createSafe') + + const authorized = await turboBoosterContract.canCall( + user, + target, + functionSig + ) + + return authorized +} \ No newline at end of file diff --git a/src/lib/turbo/fetchers/safes/getAllSafes.ts b/src/lib/turbo/fetchers/safes/getAllSafes.ts new file mode 100644 index 00000000..56c90823 --- /dev/null +++ b/src/lib/turbo/fetchers/safes/getAllSafes.ts @@ -0,0 +1,12 @@ +import { providers } from "ethers" +import { createTurboMaster } from "lib/turbo/utils/turboContracts" + +export const getAllSafes = async (provider: providers.Provider, chainID: number) => { + let master = createTurboMaster(provider, chainID) + try { + let result: any[] = await master.callStatic.getAllSafes() + return result + } catch (err) { + console.log(err) + } +} \ No newline at end of file diff --git a/src/lib/turbo/fetchers/safes/getAllUserSafes.ts b/src/lib/turbo/fetchers/safes/getAllUserSafes.ts new file mode 100644 index 00000000..c51e7445 --- /dev/null +++ b/src/lib/turbo/fetchers/safes/getAllUserSafes.ts @@ -0,0 +1,18 @@ +import { providers } from "ethers"; +import { createTurboLens } from "lib/turbo/utils/turboContracts"; +import { formatSafeInfo } from "./getSafeInfo"; + +export const getAllUserSafes = async ( + provider: providers.Provider, + user: string, + chainID: number +) => { + let lens = createTurboLens(provider, chainID); + try { + let result: any[] = await lens.callStatic.getAllUserSafes(user); + const formattedResult = result.map(formatSafeInfo); + return formattedResult; + } catch (err) { + console.log(err); + } +}; diff --git a/src/lib/turbo/fetchers/safes/getSafeInfo.ts b/src/lib/turbo/fetchers/safes/getSafeInfo.ts new file mode 100644 index 00000000..35f69bb4 --- /dev/null +++ b/src/lib/turbo/fetchers/safes/getSafeInfo.ts @@ -0,0 +1,127 @@ +import { BigNumber, constants } from "ethers"; +import { formatEther, formatUnits } from "ethers/lib/utils"; +import { createTurboLens } from "../../utils/turboContracts"; +import { + formatStrategiesInfo, + LensStrategyInfo, + StrategyInfo, +} from "../strategies/formatStrategyInfo"; + +// Data directly from the TurboLens +export type LensSafeInfo = [ + safeAddress: string, + collateralAsset: string, + collateralAmount: BigNumber, + collateralPrice: BigNumber, + collateralFactor: BigNumber, + feiPrice: BigNumber, + collateralValue: BigNumber, + debtAmount: BigNumber, + debtValue: BigNumber, + boostedAmount: BigNumber, + feiAmount: BigNumber, + tribeDAOFee: BigNumber, + strategyInfo: LensStrategyInfo[] +]; + +// Formatted Safe Data +export type SafeInfo = { + safeAddress: string; + collateralAsset: string; + collateralAmount: BigNumber; + collateralPrice: BigNumber; + collateralFactor: BigNumber; + feiPrice: BigNumber; + collateralValue: BigNumber; + debtAmount: BigNumber; + debtValue: BigNumber; + boostedAmount: BigNumber; + feiAmount: BigNumber; + tribeDAOFee: BigNumber; + strategies: StrategyInfo[]; + safeUtilization: BigNumber; + maxBoost: BigNumber; + liquidationPrice: number; +}; + +export const formatSafeInfo = (safe: LensSafeInfo): SafeInfo => ({ + safeAddress: safe[0], + collateralAsset: safe[1], + collateralAmount: safe[2], + collateralPrice: safe[3], + collateralFactor: safe[4], + feiPrice: safe[5], + collateralValue: safe[6], + debtAmount: safe[7], + debtValue: safe[8], + boostedAmount: safe[9], + feiAmount: safe[10], + tribeDAOFee: safe[11], + strategies: formatStrategiesInfo(safe[12], safe[11]), + safeUtilization: calculateSafeUtilization(safe[8], safe[6], safe[4]), + maxBoost: calculateMaxBoost(safe[6], safe[4]), + liquidationPrice: calcuateLiquidationPrice( + safe[8], + safe[6], + safe[4], + safe[3] + ), +}); + +// debtValue * 100 / collateralValue +export const calculateSafeUtilization = ( + debtValue: BigNumber, + collateralValue: BigNumber, + collateralFactor: BigNumber +) => { + return collateralValue.isZero() + ? constants.Zero + : debtValue + .mul(100) + .div(collateralValue.mul(collateralFactor).div(constants.WeiPerEther)); +}; + +export const calculateMaxBoost = ( + collateralValue: BigNumber, + collateralFactor: BigNumber +) => { + const maxBoost = collateralValue + .mul(collateralFactor) + .div(constants.WeiPerEther); + return maxBoost; +}; + +export const calcuateLiquidationPrice = ( + debtValue: BigNumber, + collateralValue: BigNumber, + collateralFactor: BigNumber, + collateralPrice: BigNumber +): number => { + const util = calculateSafeUtilization( + debtValue, + collateralValue, + collateralFactor + ).toNumber(); + const liqPriceETH = parseFloat(formatUnits(collateralPrice.mul(util), 20)); + console.log({ liqPriceETH }); + return liqPriceETH; +}; + +export const getSafeInfo = async ( + provider: any, + safe: string, + chainID: number +) => { + let lens = createTurboLens(provider, chainID); + + try { + const result: SafeInfo = formatSafeInfo( + await lens.callStatic.getSafeInfo(safe, { gasLimit: 12000000 }) + ); + + return result; + } catch (err) { + console.log("LENS ERR", { err, lens }); + throw err; + } +}; diff --git a/src/lib/turbo/fetchers/safes/getUSDPricedSafeInfo.ts b/src/lib/turbo/fetchers/safes/getUSDPricedSafeInfo.ts new file mode 100644 index 00000000..19c0b467 --- /dev/null +++ b/src/lib/turbo/fetchers/safes/getUSDPricedSafeInfo.ts @@ -0,0 +1,162 @@ +import { getEthUsdPriceBN } from "esm/utils/getUSDPriceBN"; +import { BigNumber } from "ethers"; +import { parseEther } from "ethers/lib/utils"; +import { EMPTY_ADDRESS } from "lib/turbo/utils/constants"; +import { + calculateETHValueUSD, + calculateFEIValueUSD, +} from "lib/turbo/utils/usdUtils"; +import { StrategyInfo } from "../strategies/formatStrategyInfo"; +import { getSafeInfo, SafeInfo } from "./getSafeInfo"; + +export interface USDPricedTurboSafe extends SafeInfo { + collateralValueUSD: number; + collateralPriceUSD: number; + debtUSD: number; + boostedUSD: number; + feiAmountUSD: number; + feiPriceUSD: number; + usdPricedStrategies: USDPricedStrategy[]; + maxBoostUSD: number; + liquidationPriceUSD: number; +} + +export interface USDPricedStrategy extends StrategyInfo { + boostAmountUSD: number; + feiAmountUSD: number; + feiEarnedUSD: number; + feiClaimableUSD: number; +} + +export const getUSDPricedSafeInfo = async ( + provider: any, + safe: string, + chainID: number +): Promise => { + try { + const [ethUSDBN, safeInfo]: [BigNumber, SafeInfo] = await Promise.all([ + getEthUsdPriceBN(), + getSafeInfo(provider, safe, chainID), + ]); + + const collateralValueUSD = calculateETHValueUSD( + safeInfo.collateralValue, + ethUSDBN + ); + const collateralPriceUSD = calculateETHValueUSD( + safeInfo.collateralPrice, + ethUSDBN + ); + const debtUSD = calculateETHValueUSD(safeInfo.debtValue, ethUSDBN); + const boostedUSD = calculateFEIValueUSD( + safeInfo.boostedAmount, + safeInfo.feiPrice, + ethUSDBN + ); + const feiAmountUSD = calculateFEIValueUSD( + safeInfo.feiAmount, + safeInfo.feiPrice, + ethUSDBN + ); + const feiPriceUSD = calculateETHValueUSD(safeInfo.feiPrice, ethUSDBN); + const maxBoostUSD = calculateETHValueUSD(safeInfo.maxBoost, ethUSDBN); + + const liquidationPriceUSD = calculateLiquidationPriceUSD( + safeInfo.liquidationPrice, + ethUSDBN + ); + + // Add USD values to each strategyInfo + const usdPricedStrategies = getUSDPricedStrategies( + ethUSDBN, + safeInfo.feiPrice, + safeInfo.strategies + ); + + const usdPricedSafe: USDPricedTurboSafe = { + ...safeInfo, + collateralValueUSD, + collateralPriceUSD, + debtUSD, + feiAmountUSD, + boostedUSD, + feiPriceUSD, + usdPricedStrategies, + maxBoostUSD, + liquidationPriceUSD, + }; + + return usdPricedSafe; + } catch (err) { + console.log(err); + throw err; + } +}; + +export const getUSDPricedStrategies = ( + ethUSDBN: BigNumber, + feiPriceBN: BigNumber, + strategies: StrategyInfo[] +): USDPricedStrategy[] => { + const usdPricedStrategies: USDPricedStrategy[] = []; + + strategies.forEach((strategy) => { + let boostAmountUSD = + strategy.strategy === EMPTY_ADDRESS + ? 0 + : calculateFEIValueUSD(strategy.boostedAmount, feiPriceBN, ethUSDBN); + + let feiAmountUSD = + strategy.strategy === EMPTY_ADDRESS + ? 0 + : calculateFEIValueUSD(strategy.feiAmount, feiPriceBN, ethUSDBN); + + let feiEarnedUSD = feiAmountUSD - boostAmountUSD; + let feiClaimableUSD = + strategy.strategy === EMPTY_ADDRESS + ? 0 + : calculateFEIValueUSD(strategy.feiClaimable, feiPriceBN, ethUSDBN); + + let usdStrat: USDPricedStrategy = { + ...strategy, + boostAmountUSD, + feiAmountUSD, + feiEarnedUSD, + feiClaimableUSD, + }; + + usdPricedStrategies.push(usdStrat); + }); + + return usdPricedStrategies; + /* + const usdPricedStrategies = strategies + .reduce((arr, strategy) => { + + const usdStrat: USDPricedStrategy = { + ...strategy, + boostAmountUSD: parseFloat( + formatEther(strategy.boostedAmount + .mul(ethUSDBN) + .div(constants.WeiPerEther)) + ), + feiAmountUSD: parseFloat( + formatEther(strategy.boostedAmount + .mul(ethUSDBN) + .div(constants.WeiPerEther)) + ) + } + + return [...arr, usdStrat] + }, []) + */ +}; + +export const calculateLiquidationPriceUSD = ( + liquidationPriceETH: number, + ethUSDBN: BigNumber +) => + calculateETHValueUSD( + BigNumber.from((liquidationPriceETH * 1e20).toFixed()), // 1e18 * 100 + ethUSDBN + ) / 100; diff --git a/src/lib/turbo/fetchers/strategies/formatStrategyInfo.ts b/src/lib/turbo/fetchers/strategies/formatStrategyInfo.ts new file mode 100644 index 00000000..79cfbe4c --- /dev/null +++ b/src/lib/turbo/fetchers/strategies/formatStrategyInfo.ts @@ -0,0 +1,56 @@ +import { BigNumber, constants } from "ethers"; +import { FuseERC4626Strategy } from "hooks/turbo/useStrategyInfo"; +import { DELISTED_STRATEGIES, EMPTY_ADDRESS } from "lib/turbo/utils/constants"; + +// Data directly from the TurboLens +export type LensStrategyInfo = [ + strategy: string, + boostedAmount: BigNumber, + feiAmount: BigNumber +]; + +// Formatted strategy data +export type StrategyInfo = { + strategy: string; + /// @notice the amount of fei boosted by the safe to this strategy + boostedAmount: BigNumber; + /// @notice the amount of fei held by the safe in this strategy + feiAmount: BigNumber; + /// @notice the amount of fei earned by the strategy + feiEarned: BigNumber; + feiClaimable: BigNumber; +}; + +export const formatStrategiesInfo = ( + strategies: LensStrategyInfo[], + tribeDAOFeeShare: BigNumber, + shouldFilterStrategies: boolean = false +): StrategyInfo[] | [] => { + const formattedStrategies = strategies + .map((strategy) => { + return { + strategy: strategy[0], + boostedAmount: strategy[1], + feiAmount: strategy[2], + feiEarned: strategy[2].sub(strategy[1]), + feiClaimable: strategy[2] + .sub(strategy[1]) + .mul(constants.WeiPerEther.sub(tribeDAOFeeShare)) + .div(constants.WeiPerEther), + } as StrategyInfo; + }) + .filter((s) => !DELISTED_STRATEGIES[s.strategy.toLowerCase()]); + + return shouldFilterStrategies + ? filterUsedStrategies(formattedStrategies) + : formattedStrategies; +}; + +export const filterUsedStrategies = (strats: StrategyInfo[] = []) => + strats.filter((s) => s.boostedAmount._hex !== "0x00"); + +//IE wfFEI-8 +export const getStrategyFusePoolId = (fuseStrategyName: string | undefined) => { + const arr = fuseStrategyName?.split("-") ?? []; + return arr[arr.length - 1]; +}; diff --git a/src/lib/turbo/fetchers/strategies/getBoostCapsForStrategies.ts b/src/lib/turbo/fetchers/strategies/getBoostCapsForStrategies.ts new file mode 100644 index 00000000..ad887328 --- /dev/null +++ b/src/lib/turbo/fetchers/strategies/getBoostCapsForStrategies.ts @@ -0,0 +1,51 @@ +import { JsonRpcProvider, Web3Provider } from "@ethersproject/providers" +import { BigNumber } from "ethers" +import { TurboAddresses } from "lib/turbo/utils/constants" +import { createTurboBooster, createTurboMaster, ITurboBooster } from "lib/turbo/utils/turboContracts" +import { callStaticWithMultiCall, encodeCall, EncodedCall } from "utils/multicall" +import { providers } from "@0xsequence/multicall"; + +type BoostCapForStrategyMap = { + [strategy: string]: BigNumber +} + + +// Multicall Boost Caps for all strategies passed in +// Return a map of strategy Address to boost cap +export const getBoostCapForStrategy = async ( + provider: JsonRpcProvider | Web3Provider, + strategy: string +): Promise<[boostCap: BigNumber, totalBoosted: BigNumber, boostRemaining: BigNumber]> => { + const multicallProvider = new providers.MulticallProvider(provider) + const booster = createTurboBooster(multicallProvider, 1) + const master = createTurboMaster(multicallProvider, 1) + const cap = await booster.callStatic.getBoostCapForVault(strategy) + const totalBoosted = await master.callStatic.getTotalBoostedForVault(strategy) + const boostRemaining = cap.sub(totalBoosted) + return [cap, totalBoosted, boostRemaining] +} + +// Multicall Boost Caps for all strategies passed in +// Return a map of strategy Address to boost cap +export const getBoostCapsForStrategies = async ( + provider: JsonRpcProvider | Web3Provider, + strategies: string[] +): Promise => { + + + const encodedCalls: EncodedCall[] = strategies.map( + strategyAddr => encodeCall( + ITurboBooster, + TurboAddresses[1].BOOSTER, + "getBoostCapForVault", + [strategyAddr] + ) + ) + + const { returnData } = await callStaticWithMultiCall(provider, encodedCalls) + + return strategies.reduce((acc: BoostCapForStrategyMap, curr, i) => ({ + ...acc, + [curr]: BigNumber.from(returnData[i]) + }), {}) +} \ No newline at end of file diff --git a/src/lib/turbo/fetchers/strategies/getBoostableStrategies.tsx b/src/lib/turbo/fetchers/strategies/getBoostableStrategies.tsx new file mode 100644 index 00000000..78e2b229 --- /dev/null +++ b/src/lib/turbo/fetchers/strategies/getBoostableStrategies.tsx @@ -0,0 +1,8 @@ +import { providers } from "ethers" +import { createTurboBooster } from "lib/turbo/utils/turboContracts" + +export const getBoostableStrategies = async (provider: providers.Provider, chainID: number) => { + const turboBoosterContract = await createTurboBooster(provider, chainID) + const boostableStrategies: string[] = await turboBoosterContract.callStatic.getBoostableVaults() + return boostableStrategies +} \ No newline at end of file diff --git a/src/lib/turbo/transactions/claim.ts b/src/lib/turbo/transactions/claim.ts new file mode 100644 index 00000000..c7dd4290 --- /dev/null +++ b/src/lib/turbo/transactions/claim.ts @@ -0,0 +1,52 @@ +import { FEI } from "../utils/constants"; +import { sendRouterWithMultiCall } from "../utils/turboMulticall"; +import encodeCall from "./encodedCalls"; + +interface SafeClaimParams { + safeAddress: string, + strategies: string[], + recipient: string, + signer: any, + chainID: number, +} + +/* +/* Multicall +/* 1.) Slurp all strategies to safe +/* 2.) Sweep the safe to recipient +*/ +export const safeClaimAll = async ( + { + safeAddress, + strategies, + recipient, + signer, + chainID, + }: SafeClaimParams +) => { + + // 1.) Slurp all strategies + const encodedSlurps = strategies.map(strategy => + encodeCall.slurp(safeAddress, strategy) + ); + + // 2.) Sweep da safe + const encodedSweepAll = encodeCall.sweepAll(safeAddress, recipient, FEI); + + const encodedCalls = [ + ...encodedSlurps, + encodedSweepAll + ] + + try { + const tx = await sendRouterWithMultiCall( + signer, + encodedCalls, + chainID + ); + return tx + } catch (err) { + console.error(err); + throw err + } +} \ No newline at end of file diff --git a/src/lib/turbo/transactions/createSafeAndDeposit.ts b/src/lib/turbo/transactions/createSafeAndDeposit.ts new file mode 100644 index 00000000..3293c0a8 --- /dev/null +++ b/src/lib/turbo/transactions/createSafeAndDeposit.ts @@ -0,0 +1,50 @@ +import { + encodeRouterCall, + sendRouterWithMultiCall, +} from "lib/turbo/utils/turboMulticall"; +import { BigNumber, Signer } from "ethers"; +import { TurboAddresses } from "lib/turbo/utils/constants"; +import { ITurboRouter } from "lib/turbo/utils/turboContracts"; + +export const createSafeAndDeposit = async ( + signer: Signer, + amount: BigNumber, + chainID: number, + underlyingTokenAddress: string +) => { + const provider = signer.provider; + if (!provider) return; + + const userAddress = await signer.getAddress(); + + const pullTokensArgs = [underlyingTokenAddress, amount, TurboAddresses[chainID].ROUTER]; + + const createAndDepositArgs = [underlyingTokenAddress, userAddress, amount, amount]; + + const encodedCreateSafeAndDeposit = encodeRouterCall( + ITurboRouter, + "createSafeAndDeposit", + createAndDepositArgs + ); + + const encodedPullTokens = encodeRouterCall( + ITurboRouter, + "pullToken", + pullTokensArgs + ); + + const encodedCalls = [encodedPullTokens, encodedCreateSafeAndDeposit]; + + try { + const tx = await sendRouterWithMultiCall( + signer, + encodedCalls, + chainID + ); + + return tx; + } catch (e) { + console.log(e); + throw e + } +}; diff --git a/src/lib/turbo/transactions/encodedCalls.ts b/src/lib/turbo/transactions/encodedCalls.ts new file mode 100644 index 00000000..24209717 --- /dev/null +++ b/src/lib/turbo/transactions/encodedCalls.ts @@ -0,0 +1,115 @@ +import { BigNumber } from "ethers"; +import { ITurboRouter } from "lib/turbo/utils/turboContracts"; +import { encodeRouterCall } from "lib/turbo/utils/turboMulticall"; +// TODO: natspec on funcs + +/** Periphery Payments **/ +type PullTokenArgs = [token: string, amount: BigNumber, recipient: string]; +const pullTokens = ( + token: string, + amount: BigNumber, + recipient: string +) => { + const pullTokenArgs: PullTokenArgs = [token, amount, recipient]; + return encodeRouterCall(ITurboRouter, "pullToken", pullTokenArgs); +}; + +/** Create Safe **/ +type CreateSafeAndDepositArgs = [ + token: string, + userAddress: string, + amount: BigNumber, + sharesRecieved: BigNumber +]; +const createSafeAndDeposit = ( + token: string, + userAddress: string, + amount: BigNumber, + sharesRecieved: BigNumber +) => { + const createAndDepositArgs: CreateSafeAndDepositArgs = [ + token, + userAddress, + amount, + sharesRecieved, + ]; + return encodeRouterCall( + ITurboRouter, + "createSafeAndDeposit", + createAndDepositArgs + ); +}; + +type CreateSafeArgs = [ + token: string, +]; + +const createSafe = ( + underlyingToken: string, +) => { + const createSafe: CreateSafeArgs = [ + underlyingToken + ]; + return encodeRouterCall( + ITurboRouter, + "createSafe", + createSafe + ); +}; + +/** Boost / Less **/ +type BoostAndLessArgs = [safe: string, strategy: string, amount: BigNumber]; +const boost = (safe: string, strategy: string, amount: BigNumber) => { + const boostArgs: BoostAndLessArgs = [safe, strategy, amount]; + return encodeRouterCall(ITurboRouter, "boost", boostArgs); +}; + +const less = (safe: string, strategy: string, amount: BigNumber) => { + const boostArgs: BoostAndLessArgs = [safe, strategy, amount]; + return encodeRouterCall(ITurboRouter, "less", boostArgs); +}; + +/** Deposit / Withdraw **/ +type DepositAndWithdrawArgs = any[]; +const deposit = (safe: string, strategy: string, amount: BigNumber) => { + const boostArgs: DepositAndWithdrawArgs = [safe, strategy, amount]; + return encodeRouterCall(ITurboRouter, "boost", boostArgs); +}; + +const withdraw = (safe: string, strategy: string, amount: BigNumber) => { + const boostArgs: DepositAndWithdrawArgs = [safe, strategy, amount]; + return encodeRouterCall(ITurboRouter, "less", boostArgs); +}; + +type SlurpArgs = [safe: string, strategy: string]; +const slurp = (safe: string, strategy: string) => { + const slurpArgs: SlurpArgs = [safe, strategy]; + return encodeRouterCall(ITurboRouter, "slurp", slurpArgs); +}; + +type SweepArgs = [safe: string, recepient: string, tokenAddress: string, amount: BigNumber]; +const sweep = (safe: string, recepient: string, tokenAddress: string, amount: BigNumber) => { + const sweepArgs: SweepArgs = [safe, recepient, tokenAddress, amount]; + return encodeRouterCall(ITurboRouter, "sweep", sweepArgs); +}; + +type SweepAllArgs = [safe: string, recepient: string, tokenAddress: string]; +const sweepAll = (safe: string, recepient: string, tokenAddress: string) => { + const sweepAllArgs: SweepAllArgs = [safe, recepient, tokenAddress]; + return encodeRouterCall(ITurboRouter, "sweepAll", sweepAllArgs); +} + +const encodeCall = { + pullTokens, + createSafeAndDeposit, + createSafe, + boost, + less, + deposit, + withdraw, + slurp, + sweep, + sweepAll +} + +export default encodeCall \ No newline at end of file diff --git a/src/lib/turbo/transactions/safe.ts b/src/lib/turbo/transactions/safe.ts new file mode 100644 index 00000000..82a712b5 --- /dev/null +++ b/src/lib/turbo/transactions/safe.ts @@ -0,0 +1,121 @@ +import { BigNumber, providers } from "ethers"; +import encodeCall from "lib/turbo/transactions/encodedCalls"; +import { + createTurboMaster, + createTurboSafe, +} from "lib/turbo/utils/turboContracts"; +import { sendRouterWithMultiCall } from "lib/turbo/utils/turboMulticall"; + +export const createSafe = async ( + underlyingToken: string, + provider: providers.JsonRpcProvider | providers.Web3Provider, + chainID: number +) => { + const turboMaster = createTurboMaster(await provider.getSigner(), 1); + const tx = await turboMaster.createSafe(underlyingToken); + return tx; +}; + +export const safeBoost = async ( + safe: string, + strategy: string, + amount: BigNumber, + signer: providers.JsonRpcSigner +) => { + const turboSafeContract = await createTurboSafe(signer.provider, safe); + const connectedTurboSafe = turboSafeContract.connect(signer); + const receipt = await connectedTurboSafe.boost(strategy, amount); + return await receipt; +}; + +export const safeDeposit = async ( + safe: string, + recipient: string, + amount: BigNumber, + signer: providers.JsonRpcSigner | any +) => { + if (process.env.NEXT_PUBLIC_USE_MOCKS === "true") { + await new Promise((resolve) => setTimeout(resolve, 3000)); + return; + } + + const turboSafeContract = await createTurboSafe(signer, safe); + const tx = await turboSafeContract.deposit(amount, recipient); + return tx; +}; + +export const safeWithdraw = async ( + safe: string, + recipient: string, + amount: BigNumber, + signer: providers.JsonRpcSigner | any +) => { + const turboSafeContract = await createTurboSafe(signer, safe); + const tx = await turboSafeContract.withdraw(amount, recipient, recipient); + return tx; +}; + +export const safeLess = async ( + safe: string, + strategy: string, + amount: BigNumber, + lessingMax: boolean, + signer: any, + chainID: number +) => { + let encodedCalls = []; + + if (lessingMax) { + const encodedSlurp = encodeCall.slurp(safe, strategy); + encodedCalls.push(encodedSlurp); + } + + const encodedLess = encodeCall.less(safe, strategy, amount); + encodedCalls.push(encodedLess); + + try { + const tx = await sendRouterWithMultiCall(signer, encodedCalls, chainID); + return tx; + } catch (err) { + console.error(err); + } +}; + +export const safeSlurp = async ( + safe: string, + strategies: string[], + signer: any, + chainID: number +) => { + const encodedSlurps = strategies.map((vault) => + encodeCall.slurp(safe, vault) + ); + + try { + const result = await sendRouterWithMultiCall( + signer, + encodedSlurps, + chainID + ); + } catch (err) { + console.error(err); + } +}; + +export const safeSweep = async ( + safe: string, + recepient: string, + tokenAddress: string, + amount: BigNumber, + chainID: number, + signer: any +) => { + const encodedSweep = encodeCall.sweep(safe, recepient, tokenAddress, amount); + + try { + const tx = await sendRouterWithMultiCall(signer, [encodedSweep], chainID); + return tx; + } catch (err) { + console.error(err); + } +}; diff --git a/src/lib/turbo/utils/constants.ts b/src/lib/turbo/utils/constants.ts new file mode 100644 index 00000000..29e2f914 --- /dev/null +++ b/src/lib/turbo/utils/constants.ts @@ -0,0 +1,55 @@ +import { ChainID } from "esm/utils/networks"; + +export const TurboAddresses: TurboAddresses = { + 1: { + MASTER: "0xf2e513d3b4171bb115cb9ffc45555217fbbbd00c", + ADMIN: "0x18413D61b335D2F46235E9E1256Fd5ec8AD03757", + ROUTER: "0x550b756ad4fbef34db5ac84ad41f9cf3c8927d33", + LENS: "0x38e16f5c4556d3ddb938954a632a0b761701e9b3", + STRATEGY: "0xac4c093c777581dc9c4dc935394ff11e6c58cd45", + BOOSTER: "0xf6c7f4a90b10c9eaaf2a6676ce81fe8673453e72", + CLERK: "0x1F45Af9bfDb6ab4B95311e27BEcA59B33A7E17D7", + COMPTROLLER: "0x1d9EEE473CC1B3b6D316740F5677Ef36E8f0329e", + ORACLE: "0xFb5b08d17dc5Bc5C8627c10dBed11614b43dc0F1", + TURBO_AUTHORITY: "0x286c9724a0C1875233cf17A4ffE475A0BD8158dE", + }, + 31337: { + MASTER: "0xf2e513d3b4171bb115cb9ffc45555217fbbbd00c", + ADMIN: "0x18413D61b335D2F46235E9E1256Fd5ec8AD03757", + ROUTER: "0x550b756ad4fbef34db5ac84ad41f9cf3c8927d33", + LENS: "0x38e16f5c4556d3ddb938954a632a0b761701e9b3", + STRATEGY: "0xac4c093c777581dc9c4dc935394ff11e6c58cd45", + BOOSTER: "0xf6c7f4a90b10c9eaaf2a6676ce81fe8673453e72", + CLERK: "0x1F45Af9bfDb6ab4B95311e27BEcA59B33A7E17D7", + COMPTROLLER: "0x1d9EEE473CC1B3b6D316740F5677Ef36E8f0329e", + ORACLE: "0xFb5b08d17dc5Bc5C8627c10dBed11614b43dc0F1", + TURBO_AUTHORITY: "0x286c9724a0C1875233cf17A4ffE475A0BD8158dE", + }, +}; + +type TurboAddresses = { + [chainId: number]: { + MASTER: string; + ADMIN: string; + ROUTER: string; + LENS: string; + STRATEGY: string; + BOOSTER: string; + CLERK: string; + COMPTROLLER: string; + ORACLE: string; + TURBO_AUTHORITY: string; + }; +}; + +export const TRIBE = "0xc7283b66Eb1EB5FB86327f08e1B5816b0720212B"; +export const FEI = "0x956F47F50A910163D8BF957Cf5846D573E7f87CA"; +export const EMPTY_ADDRESS = "0x0000000000000000000000000000000000000000"; + +export const DELISTED_STRATEGIES: { [strat: string]: boolean } = { + "0xb734cc08a38f0b81e7d3ddd38dfbd66a66f1a6ba": true, + "0xac4c093c777581dc9c4dc935394ff11e6c58cd45": true, +}; + +const isTurboSupportedNetwork = (chainId: number) => + Object.keys(TurboAddresses).includes(chainId.toString()); diff --git a/src/lib/turbo/utils/decodeEvents.ts b/src/lib/turbo/utils/decodeEvents.ts new file mode 100644 index 00000000..48c89b68 --- /dev/null +++ b/src/lib/turbo/utils/decodeEvents.ts @@ -0,0 +1,32 @@ +import { Contract, EventFilter } from "ethers" + +export const getRecentEvent = async ( + contract: Contract, + filter: (...args: any[]) => EventFilter, +) => { + const blockNumber = await contract.provider.getBlockNumber() + const events: any[] = await contract.queryFilter(filter(), blockNumber - 20, blockNumber ) + + return events[0] +} + +export const decodeEvent = ( + decode: (data: string, topics?: Array) => any, + data: string, + topics: string[] +) => { + const decodedEvent = decode(data, topics) + + return decodedEvent +} + +export const getRecentEventDecoded = async ( + contract: Contract, + filter: (...args: any[]) => EventFilter, +) => { + const event = await getRecentEvent(contract, filter) + const decodedEvent = decodeEvent(event.decode, event.data, event.topics) + + + return decodedEvent +} \ No newline at end of file diff --git a/src/lib/turbo/utils/fetchMaxSafeAmount.ts b/src/lib/turbo/utils/fetchMaxSafeAmount.ts new file mode 100644 index 00000000..55a3e87d --- /dev/null +++ b/src/lib/turbo/utils/fetchMaxSafeAmount.ts @@ -0,0 +1,130 @@ +import { BigNumber, constants } from "ethers"; +import { formatEther, parseEther } from "ethers/lib/utils"; +import { SafeInteractionMode } from "hooks/turbo/useUpdatedSafeInfo"; +import { format } from "path"; +import { balanceOf } from "utils/erc20Utils"; +import { SafeInfo } from "../fetchers/safes/getSafeInfo"; +import { USDPricedTurboSafe } from "../fetchers/safes/getUSDPricedSafeInfo"; +import { getBoostCapForStrategy } from "../fetchers/strategies/getBoostCapsForStrategies"; +import { FEI } from "./constants"; +import { + createFusePoolLensSecondary, + createTurboComptroller, + createTurboSafe, +} from "./turboContracts"; + +export async function fetchMaxSafeAmount( + provider: any, + mode: SafeInteractionMode, + userAddress: string, + safe: USDPricedTurboSafe | undefined, + chainId: number, + strategyIndex?: number, + limitBorrow?: boolean // Whether we should limit to 75% +) { + if (!safe) return constants.Zero; + + if (mode === SafeInteractionMode.DEPOSIT) { + const balance = await balanceOf( + userAddress, + safe.collateralAsset, + provider + ); + return balance; + } + + // TODO(@sharad-s) implement after Lens func is in-place: https://github.com/fei-protocol/tribe-turbo/issues/86 + if (mode === SafeInteractionMode.WITHDRAW) { + let maxWithdraw; + + // If Safe Utilization is above 75%, they can't withdraw anything + if (safe.safeUtilization.gte(75)) return constants.Zero; + + // If safe has debt, calculate the amount you can withdraw to get utilization to 75%. + if (safe.debtAmount.gt(0)) { + // Utillization = maxBoost / activeBoost + // 1. Calculate maxBoost. Denominated in dollars. + // 74 - activeBoost + // 100 - x + // so... + // 100 * activeDebt / 74 = x + // where x is minimum maxBoost targeted. + const debt = parseEther(safe.debtUSD.toString()); + const percentage = parseEther("100"); + const utilization = parseEther("74"); + const targetMaxBoostInUSD = debt.mul(percentage).div(utilization); + + // 2. Get minimum collateral necessary to get the targetMaxBoost, in USD. + const minimumCollateralValueInUSD = parseEther( + targetMaxBoostInUSD.div(safe.collateralFactor).toString() + ); + + // 3. Calculate minimum collateral denominated in TRIBE + const minimumCollateralInTRIBE = minimumCollateralValueInUSD.div( + parseEther(safe.collateralPriceUSD.toString()) + ); + + // 4. From current deposited collateral, substract minimum collateral to get max withdrawable amount + maxWithdraw = safe.collateralAmount.sub( + parseEther(minimumCollateralInTRIBE.toString()) + ); + } else { + // If 0 safeUtilization then withdraw full balance of safe + const turboSafe = createTurboSafe(provider, safe.safeAddress); + maxWithdraw = await turboSafe.callStatic.maxWithdraw(userAddress); + } + + return maxWithdraw; + } + + if (mode === SafeInteractionMode.BOOST) { + if (strategyIndex === undefined || !safe.strategies) return constants.Zero; + + const TurboComptroller = createTurboComptroller(provider, chainId); + const FusePoolLensSecondary = createFusePoolLensSecondary(provider); + + const cToken = await TurboComptroller.callStatic.cTokensByUnderlying(FEI); + + // Safe's Max Borrow + const maxBorrow = await FusePoolLensSecondary.callStatic.getMaxBorrow( + safe.safeAddress, + cToken + ); + + // Strategy Boost Cap + const [boostCap, totalBoosted, boostRemaining] = + await getBoostCapForStrategy( + provider, + safe.strategies[strategyIndex].strategy + ); + + console.log({ boostCap, totalBoosted, boostRemaining }); + + // Prevent rekt + let amount: BigNumber; + if (!!limitBorrow) { + amount = maxBorrow; + } else { + amount = maxBorrow; + } + + // // Max Amount can't be higher than Boost Cap + // if (amount.gt(boostRemaining)) { + // amount = boostRemaining; + // } + + console.log({ boostCap, totalBoosted, boostRemaining }); + return amount; + } + + // This one is unique as it is applied to a specific strategy + if (mode === SafeInteractionMode.LESS) { + if (strategyIndex === undefined || !safe.strategies) return constants.Zero; + const strategy = safe.strategies[strategyIndex]; + if (!strategy) return constants.Zero; + const maxLess = strategy.boostedAmount; + return maxLess; + } + + return constants.Zero; +} diff --git a/src/lib/turbo/utils/formatters.ts b/src/lib/turbo/utils/formatters.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/lib/turbo/utils/formatters.ts @@ -0,0 +1 @@ +export {}; diff --git a/src/lib/turbo/utils/getMarketCF.ts b/src/lib/turbo/utils/getMarketCF.ts new file mode 100644 index 00000000..25b82d8d --- /dev/null +++ b/src/lib/turbo/utils/getMarketCF.ts @@ -0,0 +1,15 @@ +import { JsonRpcProvider } from "@ethersproject/providers" +import { BigNumber } from "ethers" +import { createTurboComptroller } from "./turboContracts" + +export async function getMarketCf( + provider: JsonRpcProvider, + chainId: number, + underlyingAddress: string +): Promise { + const turboComptrollerContract = createTurboComptroller(provider, chainId) + const marketAddress = await turboComptrollerContract.callStatic.cTokensByUnderlying(underlyingAddress) + const market = await turboComptrollerContract.markets(marketAddress) + return market.collateralFactorMantissa +} + diff --git a/src/lib/turbo/utils/getUserFeiOwed.ts b/src/lib/turbo/utils/getUserFeiOwed.ts new file mode 100644 index 00000000..8b87f337 --- /dev/null +++ b/src/lib/turbo/utils/getUserFeiOwed.ts @@ -0,0 +1,28 @@ +import { BigNumber, constants } from "ethers"; +import { SafeInfo } from "lib/turbo/fetchers/safes/getSafeInfo"; + +// (boostedAmount - debtAmount) + ((feiAmount - boostedAmount) * revShare) +export const getUserFeiOwed = (safe: SafeInfo | undefined): BigNumber => { + if (!safe) return constants.Zero; + const { boostedAmount, debtAmount, feiAmount, tribeDAOFee } = safe; + const boostedAmountAfterDebtRepaid = boostedAmount.sub(debtAmount); + const yieldAccruedBySafe = feiAmount.sub(boostedAmount); + + const userShare = 1 - parseFloat(tribeDAOFee.toString()) / 1e18; + const revShareUser = BigNumber.from(userShare * 100) + + const feiOwedForUser = boostedAmountAfterDebtRepaid.add( + yieldAccruedBySafe.mul(revShareUser) + ).div(100); + + return feiOwedForUser; +}; + +// (boostedAmount - debtAmount) + ((feiAmount - boostedAmount) * revShare) +export const getUserFeiOwedWithBalance = (safe: SafeInfo | undefined, safeBalanceOfFei: BigNumber): BigNumber => { + if (!safe) return constants.Zero; + const claimableFromStrategies = getUserFeiOwed(safe) + const total = claimableFromStrategies.add(safeBalanceOfFei) + return total; +}; + diff --git a/src/lib/turbo/utils/turboContracts.ts b/src/lib/turbo/utils/turboContracts.ts new file mode 100644 index 00000000..932ca2f2 --- /dev/null +++ b/src/lib/turbo/utils/turboContracts.ts @@ -0,0 +1,190 @@ +import { Contract } from "ethers"; + +// ABIS +import TurboRouter from "lib/turbo/abi/TurboRouter.json"; +import TurboMaster from "lib/turbo/abi/TurboMaster.json"; +import ERC20 from "lib/turbo/abi/ERC20.json"; +import CERC20 from "lib/turbo/abi/CERC20.json"; +import CERC20Delegate from "lib/turbo/abi/CERC20Delegate.json"; +import FuseERC4626 from "lib/turbo/abi/FuseERC4626.json"; +import TurboComptroller from "lib/turbo/abi/comptroller.json"; +import TurboLens from "lib/turbo/abi/TurboLens.json"; +import TurboBooster from "lib/turbo/abi/TurboBooster.json"; +import TurboSafe from "lib/turbo/abi/TurboSafe.json"; +import TurboAuthority from "lib/turbo/abi/Authority.json"; + +// Fuse +import FusePoolLensSecondary from "esm/Fuse/contracts/abi/FusePoolLensSecondary.json"; +import FusePoolLens from "esm/Fuse/contracts/abi/FusePoolLens.json"; +import FusePoolDirectory from "esm/Fuse/contracts/abi/FusepoolDirectory.json"; + +// Utils +import { Interface } from "ethers/lib/utils"; +import { TurboAddresses } from "../utils/constants"; +import { providers } from "ethers"; +import addresses from "esm/Fuse/addresses"; + +//** Contracts **/ +export const createTurboRouter = async ( + provider: providers.JsonRpcProvider, + id: number +) => { + const turboRouterContract = new Contract( + TurboAddresses[id].ROUTER, + TurboRouter.abi, + provider + ); + return turboRouterContract; +}; + +export const createTurboMaster = ( + provider: any, + id: number = 1 +) => { + const turboMasterContract = new Contract( + TurboAddresses[id].MASTER, + TurboMaster.abi, + provider + ); + + return turboMasterContract; +}; + +export const createTurboComptroller = ( + provider: providers.Provider, + id: number +) => { + const turboRouterContract = new Contract( + TurboAddresses[id].COMPTROLLER, + TurboComptroller, + provider + ); + + return turboRouterContract; +}; + +export const createTurboLens = ( + provider: providers.Provider, + chainID: number +) => { + const turboLens = new Contract( + TurboAddresses[chainID].LENS, + TurboLens.abi, + provider + ); + + return turboLens; +}; + +export const createTurboBooster = ( + provider: providers.Provider, + chainID: number +) => { + const turboBoosterContract = new Contract( + TurboAddresses[chainID].BOOSTER, + TurboBooster.abi, + provider + ); + + return turboBoosterContract; +}; + +export const createTurboSafe = ( + provider: providers.Provider, + turboSafe: string +) => { + const turboSafeContract = new Contract(turboSafe, TurboSafe.abi, provider); + return turboSafeContract; +}; + +export const createTurboAuthority = async ( + provider: providers.BaseProvider, + authorityAddress: string +) => { + const turboAuthorityContract = new Contract( + authorityAddress, + TurboAuthority.abi, + provider + ); + + return turboAuthorityContract; +}; + +/* Token Contracts */ + +export const createERC20 = (token: string, provider: providers.Provider) => + new Contract(token, ERC20.abi, provider); + +export const createCERC20 = ( + provider: providers.Provider, + tokenAddress: string +) => new Contract(tokenAddress, CERC20.abi, provider); + +export const createCERC20Delegate = ( + provider: providers.Provider, + tokenAddress: string +) => new Contract(tokenAddress, CERC20Delegate, provider); + +export const createFuseERC4626 = ( + provider: providers.JsonRpcProvider, + strategyAddress: string +) => { + const FuseERC4626Contract = new Contract( + strategyAddress, + FuseERC4626, + provider + ); + + return FuseERC4626Contract; +}; + +/** Lens **/ + +export const createFusePoolLensSecondary = ( + provider: providers.JsonRpcProvider, + chainId: number = 1 +) => { + const addr = addresses[chainId].FUSE_POOL_LENS_SECONDARY_CONTRACT_ADDRESS; + + const fusePoolLensSecondary = new Contract( + addr, + FusePoolLensSecondary, + provider + ); + return fusePoolLensSecondary; +}; + +export const createFusePoolLens = ( + provider: providers.JsonRpcProvider, + chainId: number = 1 +) => { + const addr = addresses[chainId].FUSE_POOL_LENS_CONTRACT_ADDRESS; + const fusePoolLens = new Contract(addr, FusePoolLens, provider); + return fusePoolLens; +}; + +export const createFusePoolDirectory = ( + provider: providers.JsonRpcProvider, + chainId: number = 1 +) => { + const addr = addresses[chainId].FUSE_POOL_DIRECTORY_CONTRACT_ADDRESS; + const fusePoolDir = new Contract(addr, FusePoolDirectory, provider); + return fusePoolDir; +}; + +/** Interfaces **/ + +// Turbo Ifaces +export const ITurboRouter = new Interface(TurboRouter.abi); +export const ITurboMaster = new Interface(TurboMaster.abi); +export const ITurboComptroller = new Interface(TurboComptroller); +export const ITurboSafe = new Interface(TurboSafe.abi); +export const ITurboBooster = new Interface(TurboBooster.abi); +export const ITurboLens = new Interface(TurboLens.abi); +export const ITurboAuthority = new Interface(TurboAuthority.abi); + +// Etc Ifaces +export const IERC20 = new Interface(ERC20.abi); +export const ICERC20 = new Interface(CERC20.abi); +export const ICERC20Delegate = new Interface(CERC20Delegate); +export const IFuseERC4626 = new Interface(FuseERC4626); diff --git a/src/lib/turbo/utils/turboMulticall.ts b/src/lib/turbo/utils/turboMulticall.ts new file mode 100644 index 00000000..9be704cc --- /dev/null +++ b/src/lib/turbo/utils/turboMulticall.ts @@ -0,0 +1,62 @@ +import { providers, Signer } from "ethers"; +import { Interface } from "ethers/lib/utils"; +import { createTurboRouter } from './turboContracts' + +// returns callData for a function call to be parsed through multicall +export const encodeRouterCall = ( + iface: Interface, + functionName: string, + params: any[] +): string => iface.encodeFunctionData(functionName, [...params]) + + +export const decodeRouterCall = ( + iface: Interface, + functionName: string, + txResult: any +): any => iface.decodeFunctionResult(functionName, txResult); + + +export const callRouterWithMultiCall = async ( + provider: providers.JsonRpcProvider, + encodedCalls: string[], + chainID: number, + simulatedAddress?: string, +) => { + const router = await createTurboRouter(provider, chainID); + + let options: any = {}; + if (!!simulatedAddress) options.address = simulatedAddress; + + const returnDatas = await router.callStatic.multicall( + encodedCalls, + options + ); + + return returnDatas; +}; + + +export const sendRouterWithMultiCall = async ( + signer: Signer, + encodedCalls: string[], + chainID: number, + simulatedAddress?: string +) => { + // @ts-ignore + const router = await createTurboRouter(signer.provider, chainID); + + const routerWithSigner = router.connect(signer) + + let options: any = {}; + if (!!simulatedAddress) options.address = simulatedAddress; + + const tx = await routerWithSigner.multicall( + encodedCalls, + options + ); + + + return tx; +}; + diff --git a/src/lib/turbo/utils/usdUtils.ts b/src/lib/turbo/utils/usdUtils.ts new file mode 100644 index 00000000..e0e4ce4d --- /dev/null +++ b/src/lib/turbo/utils/usdUtils.ts @@ -0,0 +1,25 @@ +import { BigNumber, constants } from "ethers" +import { formatEther } from "ethers/lib/utils" + + +export const calculateETHValueUSD = (ethValueBN: BigNumber, ethUSDBN: BigNumber) => parseFloat( + formatEther(ethValueBN + .mul(ethUSDBN) + .div(constants.WeiPerEther)) +) + +export const calculateFEIValueUSD = ( + feiAmountBN: BigNumber, + feiPriceBN: BigNumber, + ethUSDBN: BigNumber) => parseFloat( + formatEther( + feiAmountBN + .mul(feiPriceBN) + .mul(ethUSDBN) + .div(constants.WeiPerEther) + .div(constants.WeiPerEther) + ) + ) + + + diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index c5322518..706e6e93 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -61,7 +61,7 @@ function MyApp({ Component, pageProps }: AppProps) { - + diff --git a/src/pages/turbo/index.tsx b/src/pages/turbo/index.tsx new file mode 100644 index 00000000..3aa70a8b --- /dev/null +++ b/src/pages/turbo/index.tsx @@ -0,0 +1,17 @@ +import { NextPage } from "next"; +import TurboIndexPage from "components/pages/Turbo/TurboIndexPage"; +import { serverSideTranslations } from "next-i18next/serverSideTranslations"; + +export async function getStaticProps({ locale }: { locale: string }) { + return { + props: { + ...(await serverSideTranslations(locale)), + }, + }; +} + +const Page: NextPage = () => { + return ; +}; + +export default Page; diff --git a/src/pages/turbo/protocols/index.tsx b/src/pages/turbo/protocols/index.tsx new file mode 100644 index 00000000..54040cc0 --- /dev/null +++ b/src/pages/turbo/protocols/index.tsx @@ -0,0 +1,185 @@ +import { useAllSafes } from "hooks/turbo/useAllSafes"; +import { NextPage } from "next"; +import SafeCard from 'components/pages/Turbo/TurboIndexPage/SafeCard' +import { useSafeInfo } from "hooks/turbo/useSafeInfo"; +import { Avatar, Box, Flex, HStack, Image, SimpleGrid, Spinner, VStack } from "@chakra-ui/react"; +import { Heading, HoverableCard, Link, Statistic, Text, TokenIcon } from "rari-components"; +import { motion } from "framer-motion"; +import TurboLayout from 'components/pages/Turbo/TurboLayout'; +import { commify, formatEther } from "ethers/lib/utils"; +import { useTrustedStrategies } from "hooks/turbo/useTrustedStrategies"; +import { StrategyInfosMap, useERC4626StrategiesDataAsMap } from "hooks/turbo/useStrategyInfo"; +import { useAllUserSafes } from "hooks/turbo/useUserSafes"; +import useAggregateSafeData from "hooks/turbo/useAggregateSafeData"; +import { smallUsdFormatter } from "utils/bigUtils"; +import { SafeInfo } from "lib/turbo/fetchers/safes/getSafeInfo"; + +type AuthorizedUser = { + address: string, + protocolName: string, + logo: string +} + +const authorizedUsers: AuthorizedUser[] = [ + { + address: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", + protocolName: "Olympus DAO", + logo: "0x64aa3364F17a4D01c6f1751Fd97C2BD3D7e7f1D5" + }, + { + address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + protocolName: "Balancer DAO", + logo: "0xba100000625a3754423978a60c9317c58a424e3d" + } +] + +const Page: NextPage = () => { + return ( + + Protocols using Turbo + + + ) +}; + +const ProtocolGrid = () => { + const allStrategies = useTrustedStrategies(); + const getERC4626StrategyData = useERC4626StrategiesDataAsMap(allStrategies); + + return ( + + {authorizedUsers.map((user: AuthorizedUser) => { + return ( + + )}) + } + + ) +} + + +const ProtocolCard = ({ + user, + getERC4626StrategyData, +} : { + user: AuthorizedUser, + getERC4626StrategyData: StrategyInfosMap, +}) => { + const safes = useAllUserSafes(user.address) + + + return ( + + + + {(hovered) => ( + + + + +

{user.protocolName}

+
+
+ { safes ? : } +
+ )} +
+
+ + ) +} + +const AggregateSafeStatis = ({ + safes, + getERC4626StrategyData +}: { + safes: SafeInfo[], + getERC4626StrategyData: StrategyInfosMap +}) => { + const { totalBoosted, totalClaimableUSD, netAPY } = useAggregateSafeData( + safes, + getERC4626StrategyData + ); + + console.log({safes}) + return ( + + + {commify(parseFloat(formatEther(totalBoosted)).toFixed(2)) + " FEI"} + + } + /> + + {smallUsdFormatter(totalClaimableUSD)} + + } + /> + setApyIncreasing(!apyIncreasing)} + > + + {netAPY.toFixed(2)}% + + {/* {apyIncreasing ? ( + + ) : ( + + )} */} + + } + /> + + ) +} + + +{/* + + {safes.map((safe: string) => { + if (safe === EMPTY_ADDRESS) return + return + })} + + */} +const SafeWrapper = ({safeAddress}: {safeAddress: string}) => { + const safeWithInfo = useSafeInfo(safeAddress) + + if (!safeWithInfo) return + + return ( + + ) +} +export default Page \ No newline at end of file diff --git a/src/pages/turbo/safe/[id].tsx b/src/pages/turbo/safe/[id].tsx new file mode 100644 index 00000000..2043483e --- /dev/null +++ b/src/pages/turbo/safe/[id].tsx @@ -0,0 +1,24 @@ +import { NextPage } from "next"; +import TurboSafePage from "components/pages/Turbo/TurboSafePage/TurboSafePage"; +import { serverSideTranslations } from "next-i18next/serverSideTranslations"; + +export async function getStaticProps({ locale }: { locale: string }) { + return { + props: { + ...(await serverSideTranslations(locale)), + }, + }; +} + +export const getStaticPaths = async () => { + return { + paths: [], + fallback: "blocking", + }; +}; + +const Page: NextPage = () => { + return ; +}; + +export default Page; diff --git a/src/pages/turbo/safes/index.tsx b/src/pages/turbo/safes/index.tsx new file mode 100644 index 00000000..723b337e --- /dev/null +++ b/src/pages/turbo/safes/index.tsx @@ -0,0 +1,47 @@ +import { SimpleGrid, Spinner } from "@chakra-ui/react"; +import SafeCard from "components/pages/Turbo/TurboIndexPage/SafeCard"; +import TurboLayout from "components/pages/Turbo/TurboLayout"; +import { useAllSafes } from "hooks/turbo/useAllSafes"; +import { useSafeInfo, useSafesInfo } from "hooks/turbo/useSafeInfo"; +import { SafeInfo } from "lib/turbo/fetchers/safes/getSafeInfo"; +import { EMPTY_ADDRESS } from "lib/turbo/utils/constants"; +import { NextPage } from "next"; +import { Heading } from "rari-components"; + +const Page: NextPage = () => { + return ( + + All safes + + + ) +}; + +export default Page + + +const SafesGrid = () => { + const safes: string[] = useAllSafes() + + if (!safes) return + return ( + <> + + {safes.map((safe: string) => { + if (safe === EMPTY_ADDRESS) return null + return + })} + + + ) +} + +const SafeInfoFetcher = ({safe}: {safe: string}) => { + const safeInfo = useSafeInfo(safe) + + if (!safeInfo) return + + return ( + + ) +} \ No newline at end of file diff --git a/src/utils/apyUtils.ts b/src/utils/apyUtils.ts index 584dbf17..486efcff 100644 --- a/src/utils/apyUtils.ts +++ b/src/utils/apyUtils.ts @@ -1,11 +1,13 @@ import { toInt } from "./ethersUtils"; export const convertMantissaToAPY = (mantissa: any, dayRange: number = 35) => { + if (!mantissa) return 0 const parsedMantissa = toInt(mantissa) return (Math.pow((parsedMantissa / 1e18) * 6500 + 1, dayRange) - 1) * 100; }; export const convertMantissaToAPR = (mantissa: any) => { + if (!mantissa) return 0 const parsedMantissa = toInt(mantissa) return (parsedMantissa * 2372500) / 1e16; }; diff --git a/src/utils/bigUtils.ts b/src/utils/bigUtils.ts index 6d2e5cd5..ec097245 100644 --- a/src/utils/bigUtils.ts +++ b/src/utils/bigUtils.ts @@ -27,7 +27,8 @@ export function stringUsdFormatter(num: string) { return formatter.format(parseFloat(num)); } -export function smallUsdFormatter(num: number | string) { +export function smallUsdFormatter(_num: number | string | undefined, dollars: boolean = true) { + let num = _num ?? 0 if (typeof num === typeof "") { return smallFormatter.format(parseFloat(num as string)); } @@ -38,17 +39,20 @@ export function usdFormatter(num: number) { return formatter.format(num); } -export function shortUsdFormatter(num: number | string) { +export function shortUsdFormatter(_num: number | string | undefined, dollars: boolean = true) { + let num = _num ?? 0 + let str = dollars ? "$" : "" if (typeof num === typeof "") { - return "$" + shortFormatter.format(parseFloat(num as string)); + return str + shortFormatter.format(parseFloat(num as string)); } - return "$" + shortFormatter.format(num as number); + return str + shortFormatter.format(num as number); } -export const abbreviateAmount = (amount: number) => { +export const abbreviateAmount = (_amount: number | string | undefined, dollars: boolean = true) => { + const amount = _amount ? Number(_amount) : 0 return Math.abs(amount) > 100000 - ? shortUsdFormatter(amount) - : smallUsdFormatter(amount); + ? shortUsdFormatter(amount, dollars) + : smallUsdFormatter(amount, dollars); }; export const bnToString = (bn: BigNumber): string => bn.toString(); diff --git a/src/utils/createComptroller.ts b/src/utils/createComptroller.ts index 9b4b5131..18383481 100644 --- a/src/utils/createComptroller.ts +++ b/src/utils/createComptroller.ts @@ -94,3 +94,15 @@ export const createMasterPriceOracle = (fuse: Fuse, special?: boolean) => { ); return masterPriceOracle; }; + + +export const createFusePoolDirectory = (fuse: Fuse, special?: boolean) => { + const provider = special ? fuse.provider : fuse.provider.getSigner() + const masterPriceOracle = new Contract( + fuse.addresses.PUBLIC_PRICE_ORACLE_CONTRACT_ADDRESSES.MasterPriceOracle, + fuse.oracleContracts["MasterPriceOracle"].abi, + provider + ); + return masterPriceOracle; +}; + diff --git a/src/utils/erc20Utils.ts b/src/utils/erc20Utils.ts new file mode 100644 index 00000000..cc813fd9 --- /dev/null +++ b/src/utils/erc20Utils.ts @@ -0,0 +1,112 @@ +import { constants, providers } from "ethers"; +import { parseEther } from "ethers/lib/utils"; +import { Interface } from "@ethersproject/abi"; +import { BigNumber } from "@ethersproject/bignumber"; +import { Contract } from "@ethersproject/contracts"; +import { MAX_APPROVAL_AMOUNT } from "./tokenUtils"; + +export async function checkAllowance( + signer: any, + userAddress: string, + spender: string, + underlyingAddress: string, + amount?: BigNumber +) { + if (isAssetETH(underlyingAddress)) return true; + const erc20Interface = new Interface([ + "function allowance(address owner, address spender) public view returns (uint256 remaining)", + ]); + + const erc20Contract = new Contract(underlyingAddress, erc20Interface, signer); + const allowance = await erc20Contract.callStatic.allowance( + userAddress, + spender + ); + + console.log({ allowance, amount }, allowance.gte(amount)) + + const hasApproval = allowance.gte(amount); + + return hasApproval; +} + +export async function balanceOf( + userAddress: string | undefined, + underlyingAddress: string | undefined, + signer: any +): Promise { + if (!userAddress || !underlyingAddress) return constants.Zero; + + if (isAssetETH(underlyingAddress)) return parseEther("0"); + const erc20Interface = new Interface([ + "function balanceOf(address _owner) public view returns (uint256 balance)", + ]); + + const erc20Contract = new Contract(underlyingAddress, erc20Interface, signer); + + const balance: BigNumber = await erc20Contract.callStatic.balanceOf( + userAddress + ); + + return balance; +} + +export async function approve( + signer: providers.JsonRpcSigner | providers.Web3Provider | any, + spender: string, + underlyingAddress: string, + amount: BigNumber +) { + if (isAssetETH(underlyingAddress)) return; + + const erc20Interface = new Interface([ + "function allowance(address owner, address spender) public view returns (uint256 remaining)", + "function approve(address spender, uint256 value) public returns (bool success)", + ]); + + console.log({ spender, amount }) + const erc20Contract = new Contract(underlyingAddress, erc20Interface, signer); + + return await erc20Contract.approve(spender, amount); +} + +const isAssetETH = (address: string) => + address === "0x0000000000000000000000000000000000000000"; + +/** + * @param provider - An initiated ethers provider. + * @param userAddress - Address of user to check allowance for. + * @param spender - Address/User to give approval to. + * @param underlyingAddress - The token to approve. + * @param amount - Amount user is supplying. + */ +export async function checkAllowanceAndApprove( + signer: providers.JsonRpcSigner | providers.Web3Provider | any, + userAddress: string, + spender: string, + underlyingAddress: string, + amount?: BigNumber +) { + if (process.env.NEXT_PUBLIC_USE_MOCKS === "true") { + await new Promise((resolve) => setTimeout(resolve, 3000)); + return; + } + + console.log({ signer, spender, underlyingAddress, amount, MAX_APPROVAL_AMOUNT }) + + const hasApprovedEnough = await checkAllowance( + signer, + userAddress, + spender, + underlyingAddress, + amount + ); + + // alert("HAS APPROVED ENOUGH: " + String(hasApprovedEnough)) + + if (!hasApprovedEnough) { + console.log({ signer, spender, underlyingAddress, amount }) + const tx = await approve(signer, spender, underlyingAddress, amount ?? MAX_APPROVAL_AMOUNT); + return tx; + } +} diff --git a/src/utils/ethersUtils.ts b/src/utils/ethersUtils.ts index 035f474b..2e90e37d 100644 --- a/src/utils/ethersUtils.ts +++ b/src/utils/ethersUtils.ts @@ -9,7 +9,7 @@ export const toBN = (input: BigNumberish) => { else return BigNumber.from(input); }; -export const toInt = (input: BigNumber) => { +export const toInt = (input: BigNumber | undefined) => { if (!input) return 0 return parseInt(input.toString()) } diff --git a/src/utils/homepage.ts b/src/utils/homepage.ts index 67ab1920..6f2d03ef 100644 --- a/src/utils/homepage.ts +++ b/src/utils/homepage.ts @@ -23,6 +23,8 @@ export const getOpportunityLink = ( return "https://xpollinate.io/"; case HomepageOpportunityType.PegExchanger: return "https://jointhetribe.xyz/"; + case HomepageOpportunityType.Turbo: + return "/turbo"; } return "/"; }; diff --git a/src/utils/multicall.ts b/src/utils/multicall.ts index 8599c897..9904f27b 100644 --- a/src/utils/multicall.ts +++ b/src/utils/multicall.ts @@ -4,7 +4,7 @@ import { JsonRpcProvider, Web3Provider } from "@ethersproject/providers"; import { Interface } from "ethers/lib/utils"; import { filterOnlyObjectProperties } from "./fetchFusePoolData"; -type EncodedCall = [string, any] +export type EncodedCall = [string, any] export const createMultiCall = (provider: JsonRpcProvider | Web3Provider,) => { const multicallContract = new Contract( @@ -33,6 +33,11 @@ export const sendWithMultiCall = async ( }; +type MultiCallReturn = { + returnData: any[], + blockNum: BigNumber +} + export const callStaticWithMultiCall = async ( provider: JsonRpcProvider | Web3Provider, encodedCalls: EncodedCall[], @@ -42,8 +47,9 @@ export const callStaticWithMultiCall = async ( let options: any = {} if (!!address) options.address = address - const returnDatas = await multicall.callStatic.aggregate(encodedCalls, options) - + const returnDatas: MultiCallReturn = filterOnlyObjectProperties( + await multicall.callStatic.aggregate(encodedCalls, options) + ) return returnDatas; }; diff --git a/src/utils/multicall/multicall.ts b/src/utils/multicall/multicall.ts deleted file mode 100644 index b3013375..00000000 --- a/src/utils/multicall/multicall.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { JsonRpcProvider, Web3Provider } from "@ethersproject/providers"; -import { BigNumber, Contract } from "ethers"; -import { Interface } from "ethers/lib/utils"; -import { filterOnlyObjectProperties } from "utils/fetchFusePoolData"; - -export const createMultiCall = ( - ethersProvider: JsonRpcProvider | Web3Provider -) => new Contract(MULTICALL_ADDRESS, MULTICALL_ABI, ethersProvider); - -export const sendWithMultiCall = async ( - provider: JsonRpcProvider | Web3Provider, - encodedCalls: any, - address: string -) => { - const multicall = createMultiCall(provider); - - const returnDatas = await multicall.methods - .aggregate(encodedCalls) - .send({ from: address }); - - return returnDatas; -}; - -export const callInterfaceWithMulticall = async ( - provider: JsonRpcProvider | Web3Provider, - iface: Interface, - contractAddress: string, - functionNames: string[], - params: any[][] -) => { - const encodedCalls = functionNames.map((funcName, i) => - encodeCall(iface, contractAddress, funcName, params[i]) - ); - - let result: { blockNum: BigNumber; returnData: string[] } = - filterOnlyObjectProperties( - await callStaticWithMultiCall(provider, encodedCalls) - ); - const { returnData } = result; - - const decodedCalls = functionNames.map((funcName, i) => - decodeCall(iface, funcName, returnData[i]) - ); - - return decodedCalls; -}; - - -type EncodedCall = [string, any]; - -export const callStaticWithMultiCall = async ( - provider: JsonRpcProvider | Web3Provider, - encodedCalls: EncodedCall[], - address?: string -) => { - const multicall = createMultiCall(provider); - let options: any = {}; - if (!!address) options.address = address; - - const returnDatas = await multicall.callStatic.aggregate( - encodedCalls, - options - ); - - return returnDatas; -}; - -export const encodeCall = ( - iface: Interface, - contractAddress: string, - functionName: string, - params: any[] -): EncodedCall => [ - contractAddress, - iface.encodeFunctionData(functionName, [...params]), - ]; - -export const decodeCall = ( - iface: Interface, - functionName: string, - txResult: any -): any => iface.decodeFunctionResult(functionName, txResult); - - -const MULTICALL_ADDRESS = "0xeefba1e63905ef1d7acba5a8513c70307c1ce441"; - -const MULTICALL_ABI = [ - { - constant: true, - inputs: [], - name: "getCurrentBlockTimestamp", - outputs: [{ name: "timestamp", type: "uint256" }], - payable: false, - stateMutability: "view", - type: "function", - }, - { - constant: false, - inputs: [ - { - components: [ - { name: "target", type: "address" }, - { name: "callData", type: "bytes" }, - ], - name: "calls", - type: "tuple[]", - }, - ], - name: "aggregate", - outputs: [ - { name: "blockNumber", type: "uint256" }, - { name: "returnData", type: "bytes[]" }, - ], - payable: false, - stateMutability: "nonpayable", - type: "function", - }, - { - constant: true, - inputs: [], - name: "getLastBlockHash", - outputs: [{ name: "blockHash", type: "bytes32" }], - payable: false, - stateMutability: "view", - type: "function", - }, - { - constant: true, - inputs: [{ name: "addr", type: "address" }], - name: "getEthBalance", - outputs: [{ name: "balance", type: "uint256" }], - payable: false, - stateMutability: "view", - type: "function", - }, - { - constant: true, - inputs: [], - name: "getCurrentBlockDifficulty", - outputs: [{ name: "difficulty", type: "uint256" }], - payable: false, - stateMutability: "view", - type: "function", - }, - { - constant: true, - inputs: [], - name: "getCurrentBlockGasLimit", - outputs: [{ name: "gaslimit", type: "uint256" }], - payable: false, - stateMutability: "view", - type: "function", - }, - { - constant: true, - inputs: [], - name: "getCurrentBlockCoinbase", - outputs: [{ name: "coinbase", type: "address" }], - payable: false, - stateMutability: "view", - type: "function", - }, - { - constant: true, - inputs: [{ name: "blockNumber", type: "uint256" }], - name: "getBlockHash", - outputs: [{ name: "blockHash", type: "bytes32" }], - payable: false, - stateMutability: "view", - type: "function", - }, -]; diff --git a/src/utils/tokenUtils.ts b/src/utils/tokenUtils.ts index a3ed34ff..df791d65 100644 --- a/src/utils/tokenUtils.ts +++ b/src/utils/tokenUtils.ts @@ -9,7 +9,7 @@ import { Fuse } from "../esm/index" import { ETH_TOKEN_DATA, TokenData } from "hooks/useTokenData"; // Ethers -import { Contract, BigNumber } from 'ethers' +import { Contract, BigNumber, constants } from 'ethers' import Tokens from "../static/compiled/tokens.json"; @@ -114,10 +114,7 @@ export const checkHasApprovedEnough = async ({ .gte(approvedForAmount); }; -export const MAX_APPROVAL_AMOUNT = BigNumber.from(2) - .pow(256) - .sub(1); // big fucking # - +export const MAX_APPROVAL_AMOUNT = constants.MaxUint256 const ETH_AND_WETH = [ "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", diff --git a/src/utils/web3Providers.ts b/src/utils/web3Providers.ts index 0e6294e8..ed046db0 100644 --- a/src/utils/web3Providers.ts +++ b/src/utils/web3Providers.ts @@ -18,6 +18,7 @@ export function chooseBestWeb3Provider( */ vaults?: boolean ): JsonRpcProvider | Web3Provider { + let providerURL = getChainMetadata(chainId).rpcUrl ?? ""; console.log({ chainId, providerURL }); // return new JsonRpcProvider(providerURL); @@ -27,7 +28,7 @@ export function chooseBestWeb3Provider( return new JsonRpcProvider(providerURL); } - if (window.web3) { + if (window.web3) { return new Web3Provider(window.web3.currentProvider); } else { return new JsonRpcProvider(providerURL); @@ -38,7 +39,7 @@ export const initFuseWithProviders = ( provider = chooseBestWeb3Provider(), chainId: ChainID = 1 ): Fuse => { - const fuse = new Fuse(provider, chainId === 31337 ? 1 : chainId ); + const fuse = new Fuse(provider, chainId === 31337 ? 1 : chainId); let lensProvider = getChainMetadata(chainId).rpcUrl ?? ""; // @ts-ignore We have to do this to avoid Infura ratelimits on our large calls. fuse.contracts.FusePoolLens = fuse.contracts.FusePoolLens.connect(