diff --git a/packages/dapp/components/lib/contracts-shortcuts.ts b/packages/dapp/components/lib/contracts-shortcuts.ts index c1078e25f..be4f208be 100644 --- a/packages/dapp/components/lib/contracts-shortcuts.ts +++ b/packages/dapp/components/lib/contracts-shortcuts.ts @@ -1,11 +1,10 @@ -import { BigNumber, ethers } from "ethers"; -import { ERC1155Ubiquity, ERC20 } from "types"; +import { BigNumber, ethers, Contract } from "ethers"; import { performTransaction } from "./utils"; export async function ensureERC20Allowance( logName: string, - contract: ERC20, + contract: Contract, amount: BigNumber, signer: ethers.providers.JsonRpcSigner, spender: string, @@ -25,7 +24,7 @@ export async function ensureERC20Allowance( return true; } -export async function ensureERC1155Allowance(logName: string, contract: ERC1155Ubiquity, signer: ethers.providers.JsonRpcSigner, spender: string): Promise { +export async function ensureERC1155Allowance(logName: string, contract: Contract, signer: ethers.providers.JsonRpcSigner, spender: string): Promise { const signerAddress = await signer.getAddress(); const isAllowed = await contract.isApprovedForAll(signerAddress, spender); console.log(`${logName} isAllowed: `, isAllowed); diff --git a/packages/dapp/components/lib/hooks/contracts/use-named-contracts.tsx b/packages/dapp/components/lib/hooks/contracts/use-named-contracts.tsx index 61723bd0f..d8ce7d7dd 100644 --- a/packages/dapp/components/lib/hooks/contracts/use-named-contracts.tsx +++ b/packages/dapp/components/lib/hooks/contracts/use-named-contracts.tsx @@ -1,16 +1,7 @@ import useWeb3, { PossibleProviders } from "../use-web-3"; -import Deployed_Contracts from "@ubiquity/contracts/deployments.json"; import NAMED_ACCOUNTS from "../../../config/named-accounts.json"; -import { getCurveFactoryContract, getDebtCouponManagerContract, getERC20Contract, getIJarContract, getYieldProxyContract } from "@/components/utils/contracts"; - -const getDebtCouponManagerAddress = () => { - const contractDeployments: Record = Deployed_Contracts; - const record = contractDeployments["1"] ?? {}; - const contract = record?.contracts ? record?.contracts["DebtCouponManager"] : undefined; - return contract ? contract.address : undefined; -}; -export const DEBT_COUPON_MANAGER_ADDRESS = getDebtCouponManagerAddress(); +import { getCurveFactoryContract, getERC20Contract, getIJarContract, getYieldProxyContract } from "@/components/utils/contracts"; export type NamedContracts = ReturnType | null; export function connectedContracts(provider: NonNullable) { @@ -20,7 +11,6 @@ export function connectedContracts(provider: NonNullable) { usdc: getERC20Contract(NAMED_ACCOUNTS.USDC, provider), dai: getERC20Contract(NAMED_ACCOUNTS.DAI, provider), usdt: getERC20Contract(NAMED_ACCOUNTS.USDT, provider), - debtCouponManager: getDebtCouponManagerContract(DEBT_COUPON_MANAGER_ADDRESS, provider), jarUsdc: getIJarContract(NAMED_ACCOUNTS.jarUSDCAddr, provider), }; } diff --git a/packages/dapp/components/lib/hooks/contracts/use-protocol-contracts.ts b/packages/dapp/components/lib/hooks/contracts/use-protocol-contracts.ts index a36d68409..8f65fb960 100644 --- a/packages/dapp/components/lib/hooks/contracts/use-protocol-contracts.ts +++ b/packages/dapp/components/lib/hooks/contracts/use-protocol-contracts.ts @@ -3,6 +3,7 @@ import { Contract, ethers } from "ethers"; import latestDeployment from "@ubiquity/contracts/broadcast/05_StakingShare.s.sol/31337/run-latest.json"; import useWeb3 from "../use-web-3"; +import { sushiSwapPoolAddress, dollar3poolMarketAddress } from "@/lib/utils"; // contract build artifacts // separately deployed contracts @@ -27,6 +28,10 @@ import StakingFacetArtifact from "@ubiquity/contracts/out/StakingFacet.sol/Staki import StakingFormulasFacetArtifact from "@ubiquity/contracts/out/StakingFormulasFacet.sol/StakingFormulasFacet.json"; import TWAPOracleDollar3poolFacetArtifact from "@ubiquity/contracts/out/TWAPOracleDollar3poolFacet.sol/TWAPOracleDollar3poolFacet.json"; import UbiquityPoolFacetArtifact from "@ubiquity/contracts/out/UbiquityPoolFacet.sol/UbiquityPoolFacet.json"; +// other related contracts +// import SushiSwapPoolArtifact from "@ubiquity/contracts/out/SushiSwapPool.sol/SushiSwapPool.json"; +import IMetaPoolArtifact from "@ubiquity/contracts/out/IMetaPool.sol/IMetaPool.json"; +import UniswapV2PairABI from "@/components/config/abis/uniswap-v-2-pair.json"; /** * Returns all of the available protocol contracts. @@ -52,7 +57,8 @@ import UbiquityPoolFacetArtifact from "@ubiquity/contracts/out/UbiquityPoolFacet * Contracts on hold (i.e. obsolete) until we find a better utility for them: * - https://github.com/ubiquity/ubiquity-dollar/tree/development/packages/contracts/src/ubiquistick */ -const useProtocolContracts = () => { +export type ProtocolContracts = ReturnType | null; +const useProtocolContracts = async () => { // get current web3 provider const { provider } = useWeb3(); @@ -80,6 +86,9 @@ const useProtocolContracts = () => { stakingFormulasFacet: Contract | null, twapOracleDollar3poolFacet: Contract | null, ubiquityPoolFacet: Contract | null, + // related contracts + sushiPoolGovernanceDollarLp: Contract | null, + curveMetaPoolDollarTriPoolLp: Contract | null, } = { // separately deployed contracts (i.e. not part of the diamond) creditNft: null, @@ -103,6 +112,9 @@ const useProtocolContracts = () => { stakingFormulasFacet: null, twapOracleDollar3poolFacet: null, ubiquityPoolFacet: null, + // related contracts + sushiPoolGovernanceDollarLp: null, + curveMetaPoolDollarTriPoolLp: null, }; let diamondAddress = ''; @@ -148,6 +160,18 @@ const useProtocolContracts = () => { protocolContracts.twapOracleDollar3poolFacet = new ethers.Contract(diamondAddress, TWAPOracleDollar3poolFacetArtifact.abi, provider); protocolContracts.ubiquityPoolFacet = new ethers.Contract(diamondAddress, UbiquityPoolFacetArtifact.abi, provider); + // other related contracts + // const sushiSwapPool = await protocolContracts.managerFacet.sushiSwapPoolAddress(); + // const sushiSwapPoolContract = new ethers.Contract(sushiSwapPool, SushiSwapPoolArtifact.abi, provider); + // const UniswapV2PairContract = new ethers.Contract(await sushiSwapPoolContract.pair(), UniswapV2PairABI, provider); + const UniswapV2PairContract = new ethers.Contract(sushiSwapPoolAddress, UniswapV2PairABI, provider); + protocolContracts.sushiPoolGovernanceDollarLp = UniswapV2PairContract; + + // const dollar3poolMarket = await protocolContracts.managerFacet.stableSwapMetaPoolAddress(); + // const metaPoolContract = new ethers.Contract(dollar3poolMarket, IMetaPoolArtifact.abi, provider); + const metaPoolContract = new ethers.Contract(dollar3poolMarketAddress, IMetaPoolArtifact.abi, provider); + protocolContracts.curveMetaPoolDollarTriPoolLp = metaPoolContract; + return protocolContracts; }; diff --git a/packages/dapp/components/lib/hooks/use-balances.tsx b/packages/dapp/components/lib/hooks/use-balances.tsx index 6eaf6c53a..0648a01ad 100644 --- a/packages/dapp/components/lib/hooks/use-balances.tsx +++ b/packages/dapp/components/lib/hooks/use-balances.tsx @@ -1,10 +1,12 @@ -import { erc1155BalanceOf } from "@/lib/utils"; -import { BigNumber, Contract } from "ethers"; +import { erc1155BalanceOf, _3crvTokenAddress } from "@/lib/utils"; +import { BigNumber } from "ethers"; import { createContext, useContext, useEffect, useState } from "react"; -import useManagerManaged from "./contracts/use-manager-managed"; import useNamedContracts from "./contracts/use-named-contracts"; import useWalletAddress from "./use-wallet-address"; import { ChildrenShim } from "./children-shim-d"; +import useProtocolContracts from "@/components/lib/hooks/contracts/use-protocol-contracts"; +import { getERC20Contract } from "@/components/utils/contracts"; +import useWeb3 from "@/components/lib/hooks/use-web-3"; export interface Balances { uad: BigNumber; @@ -26,24 +28,34 @@ export const BalancesContext = createContext<[Balances | null, RefreshBalances]> export const BalancesContextProvider: React.FC = ({ children }) => { const [balances, setBalances] = useState(null); const [walletAddress] = useWalletAddress(); - const managedContracts = useManagerManaged(); const namedContracts = useNamedContracts(); + const protocolContracts = useProtocolContracts(); + const { provider } = useWeb3(); async function refreshBalances() { - if (walletAddress && managedContracts && namedContracts) { + if (!walletAddress || !namedContracts || !protocolContracts || !provider) { + return; + } + + const contracts = await protocolContracts; + + if(contracts.creditNft && contracts.stakingShare) { + // const _3crvTokenAddress = await contracts.managerFacet?.curve3PoolTokenAddress(); + const _3crvTokenContract = getERC20Contract(_3crvTokenAddress, provider); + const [uad, _3crv, uad3crv, ucr, ubq, ucrNft, stakingShares, usdc, dai, usdt] = await Promise.all([ - managedContracts.dollarToken.balanceOf(walletAddress), - managedContracts._3crvToken.balanceOf(walletAddress), - managedContracts.dollarMetapool.balanceOf(walletAddress), - managedContracts.creditToken.balanceOf(walletAddress), - managedContracts.governanceToken.balanceOf(walletAddress), - erc1155BalanceOf(walletAddress, managedContracts.creditNft), - erc1155BalanceOf(walletAddress, managedContracts.stakingToken), + contracts.dollarToken?.balanceOf(walletAddress), + _3crvTokenContract.balanceOf(walletAddress), + contracts.curveMetaPoolDollarTriPoolLp?.balanceOf(walletAddress), + contracts.creditToken?.balanceOf(walletAddress), + contracts.governanceToken?.balanceOf(walletAddress), + erc1155BalanceOf(walletAddress, contracts.creditNft), + erc1155BalanceOf(walletAddress, contracts.stakingShare), namedContracts.usdc.balanceOf(walletAddress), namedContracts.dai.balanceOf(walletAddress), namedContracts.usdt.balanceOf(walletAddress), ]); - + setBalances({ uad, _3crv, @@ -61,7 +73,7 @@ export const BalancesContextProvider: React.FC = ({ children }) => useEffect(() => { refreshBalances(); - }, [walletAddress, managedContracts]); + }, [walletAddress, protocolContracts]); return {children}; }; diff --git a/packages/dapp/components/lib/utils.ts b/packages/dapp/components/lib/utils.ts index 2caf22cb1..87d747854 100644 --- a/packages/dapp/components/lib/utils.ts +++ b/packages/dapp/components/lib/utils.ts @@ -122,6 +122,10 @@ export const uCR_USDT_ADDRESS = "0x9d498aB38Aa889AE0f4A865d30da2116ee9716bC"; // Uniswap Router address export const V3_ROUTER_ADDRESS = "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45"; +export const sushiSwapPoolAddress = "0x41e087485f47538752A1195D984109cB8Dc0E429"; +export const dollar3poolMarketAddress = "0x20955CB69Ae1515962177D164dfC9522feef567E"; +export const _3crvTokenAddress = "0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490"; + export const safeParseEther = (val: string) => { try { return ethers.utils.parseEther(val); diff --git a/packages/dapp/components/lib/with-loaded-context.tsx b/packages/dapp/components/lib/with-loaded-context.tsx index 53ce24cd2..690575c4f 100644 --- a/packages/dapp/components/lib/with-loaded-context.tsx +++ b/packages/dapp/components/lib/with-loaded-context.tsx @@ -1,13 +1,10 @@ import { ethers } from "ethers"; -import useDeployedContracts, { DeployedContracts } from "./hooks/contracts/use-deployed-contracts"; -import { ManagedContracts } from "./hooks/contracts/use-manager-managed"; +import useProtocolContracts, { ProtocolContracts } from "./hooks/contracts/use-protocol-contracts"; import useNamedContracts, { NamedContracts } from "./hooks/contracts/use-named-contracts"; import useWeb3, { PossibleProviders } from "./hooks/use-web-3"; -import useManagerManaged from "@/components/lib/hooks/contracts/use-manager-managed"; export type LoadedContext = { - managedContracts: NonNullable; - deployedContracts: NonNullable; + protocolContracts: NonNullable; namedContracts: NonNullable; web3Provider: NonNullable; walletAddress: string; @@ -17,19 +14,17 @@ export type LoadedContext = { export default function withLoadedContext(El: (params: LoadedContext & T) => JSX.Element, ElNull?: () => JSX.Element) { return (otherParams: T) => { const { walletAddress, signer, provider } = useWeb3(); - const managedContracts = useManagerManaged(); - const deployedContracts = useDeployedContracts(); const namedContracts = useNamedContracts(); + const protocolContracts = useProtocolContracts(); - if (provider && walletAddress && signer && managedContracts && deployedContracts && namedContracts) { + if (provider && walletAddress && signer && protocolContracts && namedContracts) { return ( ); diff --git a/packages/dapp/components/redeem/debt-coupon-deposit.tsx b/packages/dapp/components/redeem/debt-coupon-deposit.tsx index df692e6a5..a95767a33 100644 --- a/packages/dapp/components/redeem/debt-coupon-deposit.tsx +++ b/packages/dapp/components/redeem/debt-coupon-deposit.tsx @@ -4,8 +4,7 @@ import { useState } from "react"; import { ensureERC20Allowance } from "@/lib/contracts-shortcuts"; import { formatEther } from "@/lib/format"; import { safeParseEther } from "@/lib/utils"; -import useDeployedContracts from "../lib/hooks/contracts/use-deployed-contracts"; -import useManagerManaged from "../lib/hooks/contracts/use-manager-managed"; +import useProtocolContracts from "@/components/lib/hooks/contracts/use-protocol-contracts"; import useBalances from "../lib/hooks/use-balances"; import useSigner from "../lib/hooks/use-signer"; import useTransactionLogger from "../lib/hooks/use-transaction-logger"; @@ -18,8 +17,7 @@ const UcrNftGenerator = () => { const signer = useSigner(); const [balances, refreshBalances] = useBalances(); const [, doTransaction, doingTransaction] = useTransactionLogger(); - const deployedContracts = useDeployedContracts(); - const managedContracts = useManagerManaged(); + const protocolContracts = useProtocolContracts(); const [inputVal, setInputVal] = useState(""); const [expectedDebtCoupon, setExpectedDebtCoupon] = useState(null); @@ -28,16 +26,18 @@ const UcrNftGenerator = () => { return Connect wallet; } - if (!balances || !managedContracts || !deployedContracts) { + if (!balances || !protocolContracts) { return · · ·; } const depositDollarForDebtCoupons = async (amount: BigNumber) => { - const { debtCouponManager } = deployedContracts; - // cspell: disable-next-line - await ensureERC20Allowance("uAD -> DebtCouponManager", managedContracts.dollarToken, amount, signer, debtCouponManager.address); - await (await debtCouponManager.connect(signer).exchangeDollarsForCreditNft(amount)).wait(); - refreshBalances(); + const contracts = await protocolContracts; + if (contracts.dollarToken && contracts.creditNftManagerFacet) { + // cspell: disable-next-line + await ensureERC20Allowance("uCR -> CreditNftManagerFacet", contracts.dollarToken, amount, signer, contracts.creditNftManagerFacet.address); + await (await contracts.creditNftManagerFacet.connect(signer).exchangeDollarsForCreditNft(amount)).wait(); + refreshBalances(); + } }; const handleBurn = async () => { @@ -52,11 +52,12 @@ const UcrNftGenerator = () => { }; const handleInput = async (val: string) => { + const contracts = await protocolContracts; setInputVal(val); const amount = extractValidAmount(val); - if (amount) { + if (amount && contracts.creditNftRedemptionCalculatorFacet) { setExpectedDebtCoupon(null); - setExpectedDebtCoupon(await managedContracts.creditNftCalculator.connect(signer).getCreditNftAmount(amount)); + setExpectedDebtCoupon(await contracts.creditNftRedemptionCalculatorFacet.connect(signer).getCreditNftAmount(amount)); } }; diff --git a/packages/dapp/components/redeem/debt-coupon.tsx b/packages/dapp/components/redeem/debt-coupon.tsx index 73682758b..cf18ba7c9 100644 --- a/packages/dapp/components/redeem/debt-coupon.tsx +++ b/packages/dapp/components/redeem/debt-coupon.tsx @@ -33,7 +33,7 @@ const uDEBT = "uDEBT"; const uAR = "uAR"; // eslint-disable-next-line @typescript-eslint/no-unused-vars -export const DebtCouponContainer = ({ managedContracts, deployedContracts, web3Provider, walletAddress, signer }: LoadedContext) => { +export const DebtCouponContainer = ({ protocolContracts, web3Provider, walletAddress, signer }: LoadedContext) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const [balances, refreshBalances] = useBalances(); const [, doTransaction] = useTransactionLogger(); diff --git a/packages/dapp/components/redeem/lib/use-prices.ts b/packages/dapp/components/redeem/lib/use-prices.ts index 9b22d050e..062b8d5f4 100644 --- a/packages/dapp/components/redeem/lib/use-prices.ts +++ b/packages/dapp/components/redeem/lib/use-prices.ts @@ -1,4 +1,3 @@ -import { getIMetaPoolContract } from "@/components/utils/contracts"; import useProtocolContracts from "@/components/lib/hooks/contracts/use-protocol-contracts"; import useWeb3 from "@/components/lib/hooks/use-web-3"; import { BigNumber, utils } from "ethers"; @@ -17,12 +16,12 @@ const usePrices = (): [BigNumber | null, BigNumber | null, () => Promise] return; } - if(protocolContracts.managerFacet && protocolContracts.twapOracleDollar3poolFacet) { - const dollarTokenAddress = await protocolContracts.managerFacet.dollarTokenAddress(); - const newTwapPrice = await protocolContracts.twapOracleDollar3poolFacet.consult(dollarTokenAddress); - const dollar3poolMarket = await protocolContracts.managerFacet.stableSwapMetaPoolAddress(); - const dollarMetapool = getIMetaPoolContract(dollar3poolMarket, provider) - const newSpotPrice = await dollarMetapool["get_dy(int128,int128,uint256)"](0, 1, utils.parseEther("1")); + const contracts = await protocolContracts; + + if(contracts.curveMetaPoolDollarTriPoolLp) { + const dollarTokenAddress = await contracts.managerFacet?.dollarTokenAddress(); + const newTwapPrice = await contracts.twapOracleDollar3poolFacet?.consult(dollarTokenAddress); + const newSpotPrice = await contracts.curveMetaPoolDollarTriPoolLp["get_dy(int128,int128,uint256)"](0, 1, utils.parseEther("1")); setTwapPrice(newTwapPrice); setSpotPrice(newSpotPrice); } @@ -34,7 +33,7 @@ const usePrices = (): [BigNumber | null, BigNumber | null, () => Promise] useEffect(() => { refreshPrices(); - }, [protocolContracts, provider]); + }, [provider]); return [twapPrice, spotPrice, refreshPrices]; }; diff --git a/packages/dapp/components/redeem/ucr-nft-redeem.tsx b/packages/dapp/components/redeem/ucr-nft-redeem.tsx index 44d7c87e8..699bc6773 100644 --- a/packages/dapp/components/redeem/ucr-nft-redeem.tsx +++ b/packages/dapp/components/redeem/ucr-nft-redeem.tsx @@ -1,25 +1,24 @@ import { BigNumber, ethers, Contract } from "ethers"; -import { useEffect, useState } from "react"; +import { useState } from "react"; import { ensureERC1155Allowance } from "@/lib/contracts-shortcuts"; import { formatEther } from "@/lib/format"; import { safeParseEther } from "@/lib/utils"; -import useDeployedContracts from "../lib/hooks/contracts/use-deployed-contracts"; -import useManagerManaged from "../lib/hooks/contracts/use-manager-managed"; +import useProtocolContracts from "@/components/lib/hooks/contracts/use-protocol-contracts"; import useBalances from "../lib/hooks/use-balances"; import useSigner from "../lib/hooks/use-signer"; import useTransactionLogger from "../lib/hooks/use-transaction-logger"; import useWalletAddress from "../lib/hooks/use-wallet-address"; import Button from "../ui/button"; import PositiveNumberInput from "../ui/positive-number-input"; +import useEffectAsync from "../lib/hooks/use-effect-async"; const UcrNftRedeem = () => { const [walletAddress] = useWalletAddress(); const signer = useSigner(); const [, refreshBalances] = useBalances(); const [, doTransaction, doingTransaction] = useTransactionLogger(); - const deployedContracts = useDeployedContracts(); - const managedContracts = useManagerManaged(); + const protocolContracts = useProtocolContracts(); const [inputVal, setInputVal] = useState(""); const [debtIds, setDebtIds] = useState(null); @@ -32,14 +31,15 @@ const UcrNftRedeem = () => { } }; - useEffect(() => { - if (managedContracts && walletAddress) { - fetchDebts(walletAddress, managedContracts.creditNft); + useEffectAsync(async () => { + const contracts = await protocolContracts; + if (contracts.creditNft && walletAddress) { + fetchDebts(walletAddress, contracts.creditNft); } - }, [managedContracts, walletAddress]); + }, [walletAddress]); if (!walletAddress || !signer) return Connect wallet; - if (!deployedContracts || !managedContracts || !debtIds) return · · ·; + if (!protocolContracts || !debtIds) return · · ·; if (debtIds.length === 0) return No uCR-NFT coupons; async function fetchDebts(address: string, contract: Contract) { @@ -67,12 +67,14 @@ const UcrNftRedeem = () => { }; const redeemUcrNftForUad = async (amount: BigNumber) => { - const { debtCouponManager } = deployedContracts; + const contracts = await protocolContracts; const debtId = debtIds[selectedDebtId]; - if (debtId && (await ensureERC1155Allowance("uCR-NFT -> DebtCouponManager", managedContracts.creditNft, signer, debtCouponManager.address))) { - await (await debtCouponManager.connect(signer).redeemCreditNft(debtId, amount)).wait(); - refreshBalances(); - fetchDebts(walletAddress, managedContracts.creditNft); + if (contracts.creditNft && contracts.creditNftManagerFacet) { + if (debtId && (await ensureERC1155Allowance("uCR-NFT -> CreditNftManagerFacet", contracts.creditNft, signer, contracts.creditNftManagerFacet.address))) { + await (await contracts.creditNftManagerFacet.connect(signer).redeemCreditNft(debtId, amount)).wait(); + refreshBalances(); + fetchDebts(walletAddress, contracts.creditNft); + } } }; diff --git a/packages/dapp/components/redeem/ucr-redeem.tsx b/packages/dapp/components/redeem/ucr-redeem.tsx index 5e3097a75..5e113c642 100644 --- a/packages/dapp/components/redeem/ucr-redeem.tsx +++ b/packages/dapp/components/redeem/ucr-redeem.tsx @@ -1,10 +1,9 @@ -import { BigNumber, Contract, ethers } from "ethers"; +import { BigNumber, ethers } from "ethers"; import { useState } from "react"; import { SwapWidget } from "@uniswap/widgets"; import { ensureERC20Allowance } from "@/lib/contracts-shortcuts"; import { safeParseEther } from "@/lib/utils"; -import useDeployedContracts from "../lib/hooks/contracts/use-deployed-contracts"; -import useManagerManaged from "../lib/hooks/contracts/use-manager-managed"; +import useProtocolContracts from "@/components/lib/hooks/contracts/use-protocol-contracts"; import useBalances from "../lib/hooks/use-balances"; import useSigner from "../lib/hooks/use-signer"; import useTransactionLogger from "../lib/hooks/use-transaction-logger"; @@ -12,7 +11,7 @@ import Button from "../ui/button"; import PositiveNumberInput from "../ui/positive-number-input"; import useRouter from "../lib/hooks/use-router"; import useTrade from "../lib/hooks/use-trade"; -import { USDC_ADDRESS, SWAP_WIDGET_TOKEN_LIST, V3_ROUTER_ADDRESS } from "@/lib/utils"; +import { SWAP_WIDGET_TOKEN_LIST, V3_ROUTER_ADDRESS } from "@/lib/utils"; import { getUniswapV3RouterContract } from "../utils/contracts"; import useWeb3 from "@/components/lib/hooks/use-web-3"; @@ -21,8 +20,7 @@ const UcrRedeem = ({ twapInteger }: { twapInteger: number }) => { const signer = useSigner(); const [balances, refreshBalances] = useBalances(); const [, doTransaction, doingTransaction] = useTransactionLogger(); - const deployedContracts = useDeployedContracts(); - const managedContracts = useManagerManaged(); + const protocolContracts = useProtocolContracts(); const [inputVal, setInputVal] = useState("0"); // cspell: disable-next-line @@ -34,22 +32,24 @@ const UcrRedeem = ({ twapInteger }: { twapInteger: number }) => { return Connect wallet; } - if (!managedContracts || !deployedContracts || !balances) { + if (!protocolContracts || !balances) { return · · ·; } const redeemUcr = async (amount: BigNumber) => { - const { debtCouponManager } = deployedContracts; - // cspell: disable-next-line - await ensureERC20Allowance("uCR -> DebtCouponManager", managedContracts.creditToken, amount, signer, debtCouponManager.address); - await (await debtCouponManager.connect(signer).burnCreditTokensForDollars(amount)).wait(); - refreshBalances(); - // cspell: disable-next-line - if (provider && quoteAmount && selectedRedeemToken !== "uAD") { - const routerContract = getUniswapV3RouterContract(V3_ROUTER_ADDRESS, provider); - await (await routerContract.connect(signer).approveMax(managedContracts.dollarToken.address)).wait(); - await useTrade(selectedRedeemToken, quoteAmount); + const contracts = await protocolContracts; + if (contracts.creditToken && contracts.creditNftManagerFacet && contracts.dollarToken) { + // cspell: disable-next-line + await ensureERC20Allowance("uCR -> CreditNftManagerFacet", contracts.creditToken, amount, signer, contracts.creditNftManagerFacet.address); + await (await contracts.creditNftManagerFacet.connect(signer).burnCreditTokensForDollars(amount)).wait(); refreshBalances(); + // cspell: disable-next-line + if (provider && quoteAmount && selectedRedeemToken !== "uAD") { + const routerContract = getUniswapV3RouterContract(V3_ROUTER_ADDRESS, provider); + await (await routerContract.connect(signer).approveMax(contracts.dollarToken.address)).wait(); + await useTrade(selectedRedeemToken, quoteAmount); + refreshBalances(); + } } }; diff --git a/packages/dapp/components/staking/deposit-share.tsx b/packages/dapp/components/staking/deposit-share.tsx index 7f8463280..7025bc992 100644 --- a/packages/dapp/components/staking/deposit-share.tsx +++ b/packages/dapp/components/staking/deposit-share.tsx @@ -1,7 +1,7 @@ import { BigNumber, ethers } from "ethers"; import { useEffect, useState } from "react"; -import { ManagedContracts } from "@/lib/hooks/contracts/use-manager-managed"; +import { ProtocolContracts } from "@/components/lib/hooks/contracts/use-protocol-contracts"; import { constrainNumber } from "@/lib/utils"; import withLoadedContext, { LoadedContext } from "@/lib/with-loaded-context"; import Button from "../ui/button"; @@ -15,24 +15,25 @@ const MAX_WEEKS = 208; // cspell: disable-next-line type PrefetchedConstants = { totalShares: number; usdPerWeek: number; bondingDiscountMultiplier: BigNumber }; -async function prefetchConstants(contracts: NonNullable): Promise { - const reserves = await contracts.governanceMarket.getReserves(); - +async function prefetchConstants(contracts: NonNullable): Promise { + const contract = await contracts; + const reserves = await contract.sushiPoolGovernanceDollarLp?.getReserves(); const ubqPrice = +reserves[0].toString() / +reserves[1].toString(); - const ubqPerBlock = await contracts.masterChef.governancePerBlock(); - const ubqMultiplier = await contracts.masterChef.governanceMultiplier(); + const ubqPerBlock = await contract.chefFacet?.governancePerBlock(); + const ubqMultiplier = await contract.chefFacet?.governanceMultiplier(); const actualUbqPerBlock = toEtherNum(ubqPerBlock.mul(ubqMultiplier).div(`${1e18}`)); - const blockCountInAWeek = toNum(await contracts.staking.blockCountInAWeek()); + const blockCountInAWeek = toNum(await contract.stakingFacet?.blockCountInAWeek()); const ubqPerWeek = actualUbqPerBlock * blockCountInAWeek; - const totalShares = toEtherNum(await contracts.masterChef.totalShares()); + const totalShares = toEtherNum(await contract.chefFacet?.totalShares()); const usdPerWeek = ubqPerWeek * ubqPrice; // cspell: disable-next-line - const bondingDiscountMultiplier = await contracts.staking.stakingDiscountMultiplier(); + const bondingDiscountMultiplier = await contract.stakingFacet?.stakingDiscountMultiplier(); // cspell: disable-next-line return { totalShares, usdPerWeek, bondingDiscountMultiplier }; } -async function calculateApyForWeeks(contracts: NonNullable, prefetch: PrefetchedConstants, weeksNum: number): Promise { +async function calculateApyForWeeks(contracts: NonNullable, prefetch: PrefetchedConstants, weeksNum: number): Promise { + const contract = await contracts; // cspell: disable-next-line const { totalShares, usdPerWeek, bondingDiscountMultiplier } = prefetch; const DAYS_IN_A_YEAR = 365.2422; @@ -40,7 +41,7 @@ async function calculateApyForWeeks(contracts: NonNullable, pr const bigNumberOneUsdAsLp = ethers.utils.parseEther(usdAsLp.toString()); const weeks = BigNumber.from(weeksNum.toString()); // cspell: disable-next-line - const shares = toEtherNum(await contracts.ubiquityFormulas.durationMultiply(bigNumberOneUsdAsLp, weeks, bondingDiscountMultiplier)); + const shares = toEtherNum(contract.stakingFormulasFacet?.durationMultiply(bigNumberOneUsdAsLp, weeks, bondingDiscountMultiplier)); const rewardsPerWeek = (shares / totalShares) * usdPerWeek; const yearlyYield = (rewardsPerWeek / 7) * DAYS_IN_A_YEAR * 100; return Math.round(yearlyYield * 100) / 100; @@ -52,7 +53,7 @@ type DepositShareProps = { maxLp: BigNumber; } & LoadedContext; -const DepositShare = ({ onStake, disabled, maxLp, managedContracts: contracts }: DepositShareProps) => { +const DepositShare = ({ onStake, disabled, maxLp, protocolContracts: contracts }: DepositShareProps) => { const [amount, setAmount] = useState(""); const [weeks, setWeeks] = useState(""); const [currentApy, setCurrentApy] = useState(null); diff --git a/packages/dapp/components/staking/bonding-shares-explorer.tsx b/packages/dapp/components/staking/staking-shares-explorer.tsx similarity index 60% rename from packages/dapp/components/staking/bonding-shares-explorer.tsx rename to packages/dapp/components/staking/staking-shares-explorer.tsx index 014420123..bc373a4bb 100644 --- a/packages/dapp/components/staking/bonding-shares-explorer.tsx +++ b/packages/dapp/components/staking/staking-shares-explorer.tsx @@ -15,8 +15,8 @@ import Loading from "../ui/loading"; type ShareData = { id: number; // cspell: disable-next-line - ugov: BigNumber; - bond: { + governanceToken: BigNumber; + stake: { minter: string; lpFirstDeposited: BigNumber; creationBlock: BigNumber; @@ -44,65 +44,68 @@ type Actions = { const USD_TO_LP = 0.7460387929; const LP_TO_USD = 1 / USD_TO_LP; -export const BondingSharesExplorerContainer = ({ managedContracts, web3Provider, walletAddress, signer }: LoadedContext) => { +export const StakingSharesExplorerContainer = ({ protocolContracts, web3Provider, walletAddress, signer }: LoadedContext) => { const [model, setModel] = useState(null); const [, doTransaction] = useTransactionLogger(); const [, refreshBalances] = useBalances(); - // cspell: disable-next-line - const { staking: bonding, masterChef, stakingToken: bondingToken, dollarMetapool: metaPool } = managedContracts; useAsyncInit(fetchSharesInformation); async function fetchSharesInformation(processedShareId?: ShareData["id"]) { - console.time("BondingShareExplorerContainer contract loading"); - const currentBlock = await web3Provider.getBlockNumber(); - // cspell: disable-next-line - const blockCountInAWeek = +(await bonding.blockCountInAWeek()).toString(); - const totalShares = await masterChef.totalShares(); // cspell: disable-next-line - const bondingShareIds = await bondingToken.holderTokens(walletAddress); - const walletLpBalance = await metaPool.balanceOf(walletAddress); - - const shares: ShareData[] = []; - await Promise.all( + const { stakingFacet, chefFacet, stakingShare: stakingToken, curveMetaPoolDollarTriPoolLp } = await protocolContracts; + if (stakingFacet) { + console.time("StakingShareExplorerContainer contract loading"); + const currentBlock = await web3Provider.getBlockNumber(); // cspell: disable-next-line - bondingShareIds.map(async (id: BigNumber) => { + const blockCountInAWeek = +(await stakingFacet.blockCountInAWeek()).toString(); + const totalShares = await chefFacet?.totalShares(); + // cspell: disable-next-line + const stakingShareIds = await stakingToken?.holderTokens(walletAddress); + + const walletLpBalance = await curveMetaPoolDollarTriPoolLp?.balanceOf(walletAddress); + + const shares: ShareData[] = []; + await Promise.all( // cspell: disable-next-line - const [ugov, bond, bondingShareInfo, tokenBalance] = await Promise.all([ - // cspell: disable-next-line - masterChef.pendingGovernance(id), + stakingShareIds.map(async (id: BigNumber) => { // cspell: disable-next-line - bondingToken.getStake(id), - masterChef.getStakingShareInfo(id), - // cspell: disable-next-line - bondingToken.balanceOf(walletAddress, id), - ]); + const [governanceToken, stake, stakingShareInfo, tokenBalance] = await Promise.all([ + // cspell: disable-next-line + chefFacet?.pendingGovernance(id), + // cspell: disable-next-line + stakingToken?.getStake(id), + chefFacet?.getStakingShareInfo(id), + // cspell: disable-next-line + stakingToken?.balanceOf(walletAddress, id), + ]); - const endBlock = +bond.endBlock.toString(); - const blocksLeft = endBlock - currentBlock; - const weeksLeft = Math.round((blocksLeft / blockCountInAWeek) * 100) / 100; + const endBlock = +stake.endBlock.toString(); + const blocksLeft = endBlock - currentBlock; + const weeksLeft = Math.round((blocksLeft / blockCountInAWeek) * 100) / 100; - // If this is 0 it means the share ERC1155 token was transferred to another account - if (+tokenBalance.toString() > 0) { - // cspell: disable-next-line - shares.push({ id: +id.toString(), ugov, bond, sharesBalance: bondingShareInfo[0], weeksLeft }); - } - }) - ); - - const sortedShares = shares.sort((a, b) => a.id - b.id); - - console.timeEnd("BondingShareExplorerContainer contract loading"); - setModel((model) => ({ - processing: model ? model.processing.filter((id) => id !== processedShareId) : [], - shares: sortedShares, - totalShares, - walletLpBalance, - })); + // If this is 0 it means the share ERC1155 token was transferred to another account + if (+tokenBalance.toString() > 0) { + // cspell: disable-next-line + shares.push({ id: +id.toString(), governanceToken, stake, sharesBalance: stakingShareInfo[0], weeksLeft }); + } + }) + ); + + const sortedShares = shares.sort((a, b) => a.id - b.id); + + console.timeEnd("StakingShareExplorerContainer contract loading"); + setModel((model) => ({ + processing: model ? model.processing.filter((id) => id !== processedShareId) : [], + shares: sortedShares, + totalShares, + walletLpBalance, + })); + } } function allLpAmount(id: number): BigNumber { if (!model) throw new Error("No model"); - const lpAmount = model.shares.find((s) => s.id === id)?.bond?.lpAmount; + const lpAmount = model.shares.find((s) => s.id === id)?.stake?.lpAmount; if (!lpAmount) throw new Error("Could not find share in model"); return lpAmount; } @@ -110,25 +113,26 @@ export const BondingSharesExplorerContainer = ({ managedContracts, web3Provider, const actions: Actions = { onWithdrawLp: useCallback( async ({ id, amount }) => { + const { stakingFacet: staking, stakingShare: stakingToken } = await protocolContracts; if (!model || model.processing.includes(id)) return; console.log(`Withdrawing ${amount ? amount : "ALL"} LP from ${id}`); setModel((prevModel) => (prevModel ? { ...prevModel, processing: [...prevModel.processing, id] } : null)); doTransaction("Withdrawing LP...", async () => { try { // cspell: disable-next-line - const isAllowed = await bondingToken.isApprovedForAll(walletAddress, bonding.address); + const isAllowed = await stakingToken?.isApprovedForAll(walletAddress, staking?.address); if (!isAllowed) { // cspell: disable-next-line - // Allow bonding contract to control account share + // Allow staking contract to control account share // cspell: disable-next-line - if (!(await performTransaction(bondingToken.connect(signer).setApprovalForAll(bonding.address, true)))) { + if (!(await performTransaction(stakingToken?.connect(signer).setApprovalForAll(staking?.address, true)))) { return; // TODO: Show transaction errors to user } } const bigNumberAmount = amount ? ethers.utils.parseEther(amount.toString()) : allLpAmount(id); // cspell: disable-next-line - await performTransaction(bonding.connect(signer).removeLiquidity(bigNumberAmount, BigNumber.from(id))); + await performTransaction(staking?.connect(signer).removeLiquidity(bigNumberAmount, BigNumber.from(id))); } catch (error) { console.log(`Withdrawing LP from ${id} failed:`, error); // throws exception to update the transaction log @@ -144,12 +148,13 @@ export const BondingSharesExplorerContainer = ({ managedContracts, web3Provider, onClaimUbq: useCallback( async (id) => { + const { chefFacet } = await protocolContracts; if (!model) return; console.log(`Claiming Ubiquity Governance token rewards from ${id}`); setModel((prevModel) => (prevModel ? { ...prevModel, processing: [...prevModel.processing, id] } : null)); doTransaction("Claiming Ubiquity Governance tokens...", async () => { try { - await performTransaction(masterChef.connect(signer).getRewards(BigNumber.from(id))); + await performTransaction(chefFacet?.connect(signer).getRewards(BigNumber.from(id))); } catch (error) { console.log(`Claiming Ubiquity Governance token rewards from ${id} failed:`, error); // throws exception to update the transaction log @@ -165,22 +170,24 @@ export const BondingSharesExplorerContainer = ({ managedContracts, web3Provider, onStake: useCallback( async ({ amount, weeks }) => { + const { stakingFacet: staking, curveMetaPoolDollarTriPoolLp } = await protocolContracts; if (!model || model.processing.length) return; console.log(`Staking ${amount} for ${weeks} weeks`); doTransaction("Staking...", async () => {}); + // cspell: disable-next-line - const allowance = await metaPool.allowance(walletAddress, bonding.address); + const allowance = await curveMetaPoolDollarTriPoolLp?.allowance(walletAddress, staking?.address); console.log("allowance", ethers.utils.formatEther(allowance)); console.log("lpsAmount", ethers.utils.formatEther(amount)); if (allowance.lt(amount)) { // cspell: disable-next-line - await performTransaction(metaPool.connect(signer).approve(bonding.address, amount)); + await performTransaction(curveMetaPoolDollarTriPoolLp?.connect(signer).approve(staking?.address, amount)); // cspell: disable-next-line - const allowance2 = await metaPool.allowance(walletAddress, bonding.address); + const allowance2 = await curveMetaPoolDollarTriPoolLp?.allowance(walletAddress, staking?.address); console.log("allowance2", ethers.utils.formatEther(allowance2)); } // cspell: disable-next-line - await performTransaction(bonding.connect(signer).deposit(amount, weeks)); + await performTransaction(staking?.connect(signer).deposit(amount, weeks)); fetchSharesInformation(); refreshBalances(); @@ -189,27 +196,27 @@ export const BondingSharesExplorerContainer = ({ managedContracts, web3Provider, ), }; - return ; + return ; }; -export const BondingSharesExplorer = memo(({ model, actions }: { model: Model | null; actions: Actions }) => { - return <>{model ? : }; +export const StakingSharesExplorer = memo(({ model, actions }: { model: Model | null; actions: Actions }) => { + return <>{model ? : }; }); -export const BondingSharesInformation = ({ shares, totalShares, onWithdrawLp, onClaimUbq, onStake, processing, walletLpBalance }: Model & Actions) => { +export const StakingSharesInformation = ({ shares, totalShares, onWithdrawLp, onClaimUbq, onStake, processing, walletLpBalance }: Model & Actions) => { const totalUserShares = shares.reduce((sum, val) => { return sum.add(val.sharesBalance); }, BigNumber.from(0)); const totalLpBalance = shares.reduce((sum, val) => { - return sum.add(val.bond.lpAmount); + return sum.add(val.stake.lpAmount); }, BigNumber.from(0)); // cspell: disable-next-line - const totalPendingUgov = shares.reduce((sum, val) => sum.add(val.ugov), BigNumber.from(0)); + const totalPendingGovernanceToken = shares.reduce((sum, val) => sum.add(val.governanceToken), BigNumber.from(0)); - const poolPercentage = formatEther(totalUserShares.mul(ethers.utils.parseEther("100")).div(totalShares)); + const poolPercentage = totalShares.isZero() ? 0 : formatEther(totalUserShares.mul(ethers.utils.parseEther("100")).div(totalShares)); // cspell: disable-next-line - const filteredShares = shares.filter(({ bond: { lpAmount }, ugov }) => lpAmount.gt(0) || ugov.gt(0)); + const filteredShares = shares.filter(({ stake: { lpAmount }, governanceToken }) => lpAmount.gt(0) || governanceToken.gt(0)); return (
@@ -226,7 +233,7 @@ export const BondingSharesInformation = ({ shares, totalShares, onWithdrawLp, on {filteredShares.length > 0 ? ( {filteredShares.map((share) => ( - + ))} ) : ( @@ -249,7 +256,7 @@ export const BondingSharesInformation = ({ shares, totalShares, onWithdrawLp, on {/* cspell: disable-next-line */} - {formatEther(totalPendingUgov)} + {formatEther(totalPendingGovernanceToken)} @@ -272,12 +279,12 @@ export const BondingSharesInformation = ({ shares, totalShares, onWithdrawLp, on ); }; -type BondingShareRowProps = ShareData & { disabled: boolean; onWithdrawLp: Actions["onWithdrawLp"]; onClaimUbq: Actions["onClaimUbq"] }; +type StakingShareRowProps = ShareData & { disabled: boolean; onWithdrawLp: Actions["onWithdrawLp"]; onClaimUbq: Actions["onClaimUbq"] }; // cspell: disable-next-line -const BondingShareRow = ({ id, ugov, sharesBalance, bond, weeksLeft, disabled, onWithdrawLp, onClaimUbq }: BondingShareRowProps) => { +const StakingShareRow = ({ id, governanceToken, stake, weeksLeft, disabled, onWithdrawLp, onClaimUbq }: StakingShareRowProps) => { const [withdrawAmount] = useState(""); - const numLpAmount = +formatEther(bond.lpAmount); + const numLpAmount = +formatEther(stake.lpAmount); const usdAmount = numLpAmount * LP_TO_USD; function onClickWithdraw() { @@ -290,14 +297,14 @@ const BondingShareRow = ({ id, ugov, sharesBalance, bond, weeksLeft, disabled, o } return ( - + - {weeksLeft <= 0 && bond.lpAmount.gt(0) ? ( + {weeksLeft <= 0 && stake.lpAmount.gt(0) ? ( ) : // cspell: disable-next-line - ugov.gt(0) ? ( + governanceToken.gt(0) ? ( @@ -306,7 +313,7 @@ const BondingShareRow = ({ id, ugov, sharesBalance, bond, weeksLeft, disabled, o
{/* cspell: disable-next-line */} - {formatEther(ugov)} + {formatEther(governanceToken)}
{weeksLeft <= 0 ? "Ready" : {weeksLeft}w} @@ -316,4 +323,4 @@ const BondingShareRow = ({ id, ugov, sharesBalance, bond, weeksLeft, disabled, o ); }; -export default withLoadedContext(BondingSharesExplorerContainer); +export default withLoadedContext(StakingSharesExplorerContainer); diff --git a/packages/dapp/pages/credits.tsx b/packages/dapp/pages/credits.tsx index 80320e0d2..c5cf19de5 100644 --- a/packages/dapp/pages/credits.tsx +++ b/packages/dapp/pages/credits.tsx @@ -3,7 +3,7 @@ import DollarPrice from "@/components/redeem/dollar-price"; import UcrRedeem from "@/components/redeem/ucr-redeem"; import UcrNftGenerator from "@/components/redeem/debt-coupon-deposit"; import UcrNftRedeem from "@/components/redeem/ucr-nft-redeem"; -import useManagerManaged from "@/components/lib/hooks/contracts/use-manager-managed"; +import useProtocolContracts from "@/components/lib/hooks/contracts/use-protocol-contracts"; import useEffectAsync from "@/components/lib/hooks/use-effect-async"; // import DisabledBlurredMessage from "@/components/ui/DisabledBlurredMessage"; import dynamic from "next/dynamic"; @@ -11,17 +11,19 @@ const WalletConnectionWall = dynamic(() => import("@/components/ui/wallet-connec const PriceStabilization: FC = (): JSX.Element => { const [twapInteger, setTwapInteger] = useState(0); - const managedContracts = useManagerManaged(); + const protocolContracts = useProtocolContracts(); useEffectAsync(async () => { - if (managedContracts) { - const twapPrice = await managedContracts.dollarTwapOracle.consult(managedContracts.dollarToken.address); + const contracts = await protocolContracts; + if (contracts) { + const dollarTokenAddress = await contracts.managerFacet?.dollarTokenAddress(); + const twapPrice = await contracts.twapOracleDollar3poolFacet?.consult(dollarTokenAddress); if (twapPrice) { const twapPriceInteger = (twapPrice as unknown as number) / 1e18; setTwapInteger(twapPriceInteger); } } - }, [managedContracts]); + }, []); return ( diff --git a/packages/dapp/pages/index.tsx b/packages/dapp/pages/index.tsx index 6003cdfbc..a0a0777e0 100644 --- a/packages/dapp/pages/index.tsx +++ b/packages/dapp/pages/index.tsx @@ -26,7 +26,7 @@ const index: FC = (): JSX.Element => { console.log("protocolContracts is null"); setTwapPrice(null); } - }, [protocolContracts]); + }, []); if (process.env.DEBUG === "true") { fetchData(); diff --git a/packages/dapp/pages/staking.tsx b/packages/dapp/pages/staking.tsx index e26d6cd9e..44d2c372b 100644 --- a/packages/dapp/pages/staking.tsx +++ b/packages/dapp/pages/staking.tsx @@ -1,12 +1,12 @@ import { FC } from "react"; -import BondingSharesExplorer from "@/components/staking/bonding-shares-explorer"; +import StakingSharesExplorer from "@/components/staking/staking-shares-explorer"; import dynamic from "next/dynamic"; const WalletConnectionWall = dynamic(() => import("@/components/ui/wallet-connection-wall"), { ssr: false }); //@note Fix: (Hydration Error) const Staking: FC = (): JSX.Element => { return ( - + ); };