diff --git a/app/[server]/page.tsx b/app/[server]/page.tsx index 7b1b49c..7bfb344 100644 --- a/app/[server]/page.tsx +++ b/app/[server]/page.tsx @@ -12,10 +12,13 @@ import { Suspense, useState } from "react"; import { FlexibleStakingCard } from "@/components/overview/FlexibleStakingCard"; import { FixedStakingCard } from "@/components/overview/FixedStakingCard"; import { NFTCard } from "@/components/overview/NFTCard"; +import { useFlexibleStaking } from "@/store/flexibleStaking"; +import { utils } from "@consolelabs/mochi-formatter"; const Overview = () => { const { isLoggedIn } = useLoginWidget(); - const [showInfo, setShowInfo] = useState(false); + const [showInfo, setShowInfo] = useState(true); + const { balance } = useFlexibleStaking(); return (
@@ -25,7 +28,7 @@ const Overview = () => { You have{" "} - {showInfo ? 513.24 : "*****"} + {showInfo ? utils.formatTokenDigit(balance) : "*****"} {" "} ICY and{" "} diff --git a/app/layout.tsx b/app/layout.tsx index 71151e8..6f5cdab 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -5,6 +5,7 @@ import "./globals.css"; import { LoginWidgetProvider } from "@mochi-web3/login-widget"; import { Platform } from "@consolelabs/mochi-formatter"; import { AUTH_TELEGRAM_ID, MOCHI_PROFILE_API } from "@/envs"; +import { Toaster } from "@mochi-ui/core"; const inter = Inter({ subsets: ["latin"] }); @@ -28,6 +29,9 @@ export default function RootLayout({ > {children as any} +
+ +
); diff --git a/components/overview/FixedStakingCard.tsx b/components/overview/FixedStakingCard.tsx index 8f505b7..d6f3acf 100644 --- a/components/overview/FixedStakingCard.tsx +++ b/components/overview/FixedStakingCard.tsx @@ -13,7 +13,7 @@ import { utils } from "@consolelabs/mochi-formatter"; import { useDisclosure } from "@dwarvesf/react-hooks"; import { MinusLine, PlusCircleSolid, PlusLine } from "@mochi-ui/icons"; import { FixedStakeModal } from "../stake/fixed/fixed-stake-modal"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { useCountdown } from "@/hooks/useCountdown"; import { useLoginWidget } from "@mochi-web3/login-widget"; @@ -56,6 +56,15 @@ export const FixedStakingCard = (props: Props) => { claimableRewards: 0, }; + const [isClient, setIsClient] = useState(false); + useEffect(() => { + setIsClient(true); + }, []); + + if (!isClient) { + return null; + } + return ( <> { const { hidden } = props; const { isLoggedIn } = useLoginWidget(); + const { + apr, + balance, + stakedAmount, + poolStakedAmount, + earnedRewards, + tokenPrice, + autoStaking, + setValues, + } = useFlexibleStaking(); const { isOpen: isBoosting, onOpen: onBoost } = useDisclosure(); - const { isOpen: isAutoStaking, onOpenChange: onAutoStakingChange } = - useDisclosure({ - defaultIsOpen: true, - }); const { isOpen: isOpenFlexibleStakeModal, onOpenChange: onOpenChangeFlexibleStakeModal, @@ -36,27 +43,19 @@ export const FlexibleStakingCard = (props: Props) => { } = useDisclosure(); // FIXME: mock data + const nftBoost = 0; const { countDown, hours, minutes, seconds } = useCountdown( isLoggedIn ? 10 : 0 ); - const [claimableRewards, setClaimableRewards] = useState(356.7891); - const data = isLoggedIn - ? { - walletBalance: 0, - walletBalanceInUsd: 0, - totalStaked: 514.24, - totalStakedInUsd: 769.86, - nftBoost: isBoosting ? 35 : 0, - claimableRewards, - } - : { - walletBalance: 0, - walletBalanceInUsd: 0, - totalStaked: 0, - totalStakedInUsd: 0, - nftBoost: 0, - claimableRewards: 0, - }; + + const [isClient, setIsClient] = useState(false); + useEffect(() => { + setIsClient(true); + }, []); + + if (!isClient) { + return null; + } return ( <> @@ -75,8 +74,8 @@ export const FlexibleStakingCard = (props: Props) => { arrow="bottom-center" > setValues({ autoStaking })} /> , ]} @@ -84,8 +83,11 @@ export const FlexibleStakingCard = (props: Props) => { title="ICY" description="Earn competitive returns by staking ICY tokens and NFTs. Fixed yield is achieved at maturity, but you can exit anytime at its current market price." highlightItems={[ - { label: "Fixed APY", value: "28.7%" }, - { label: "TVL", value: "$2.02M" }, + { label: "Fixed APY", value: utils.formatPercentDigit(apr) }, + { + label: "TVL", + value: utils.formatUsdDigit(poolStakedAmount * tokenPrice), + }, ]} items={[ { @@ -93,16 +95,14 @@ export const FlexibleStakingCard = (props: Props) => { value: (
- {utils.formatDigit({ value: data.walletBalance.toFixed(2) })} + {utils.formatTokenDigit(balance)} ICY
), - convertedValue: utils.formatDigit({ - value: `$${data.walletBalanceInUsd.toFixed(2)}`, - }), + convertedValue: utils.formatUsdDigit(balance * tokenPrice), hidden, }, { @@ -110,35 +110,33 @@ export const FlexibleStakingCard = (props: Props) => { value: (
- {utils.formatDigit({ value: data.totalStaked.toFixed(2) })} + {utils.formatTokenDigit(stakedAmount)} ICY
), - convertedValue: data.totalStakedInUsd - ? utils.formatDigit({ - value: `$${data.totalStakedInUsd.toFixed(2)}`, - }) + convertedValue: stakedAmount + ? utils.formatUsdDigit(stakedAmount * tokenPrice) : undefined, hidden, }, { label: "My NFTs Boost", - value: data.nftBoost ? ( + value: nftBoost ? ( - {data.nftBoost < 0 ? ( + {nftBoost < 0 ? ( ) : ( )} - {Math.abs(data.nftBoost).toFixed(1)}% + {Math.abs(nftBoost).toFixed(1)}% ) : ( "0" @@ -163,7 +161,7 @@ export const FlexibleStakingCard = (props: Props) => { ), }, ] - : data.totalStaked + : stakedAmount ? [ { value: ( @@ -176,7 +174,7 @@ export const FlexibleStakingCard = (props: Props) => { : []), ]} actions={ - data.totalStaked ? ( + stakedAmount ? ( [ + ) ) : null } diff --git a/components/overview/NFTCard.tsx b/components/overview/NFTCard.tsx index 268c310..375a77e 100644 --- a/components/overview/NFTCard.tsx +++ b/components/overview/NFTCard.tsx @@ -4,6 +4,7 @@ import Image from "next/image"; import { PlusCircleSolid } from "@mochi-ui/icons"; import { useLoginWidget } from "@mochi-web3/login-widget"; import { utils } from "@consolelabs/mochi-formatter"; +import { useEffect, useState } from "react"; interface Props { hidden: boolean; @@ -32,6 +33,15 @@ export const NFTCard = (props: Props) => { claimableRewards: 0, }; + const [isClient, setIsClient] = useState(false); + useEffect(() => { + setIsClient(true); + }, []); + + if (!isClient) { + return null; + } + return ( void; + onStake: (amount: number) => Promise; + onApprove: (amount: number) => Promise; + loading: string | null; + connected: boolean; + container: HTMLDivElement | null; } export const FlexibleStakeContent = (props: Props) => { - const { onStake } = props; + const { onStake, onApprove, loading, connected, container } = props; + const { balance, allowance, apr } = useFlexibleStaking(); const [amount, setAmount] = useState({ value: 0, display: "", }); - const balance = 23667; return (
@@ -23,7 +36,7 @@ export const FlexibleStakeContent = (props: Props) => {
- {flexibleAPR}% + {apr}% Fixed ICY @@ -33,16 +46,44 @@ export const FlexibleStakeContent = (props: Props) => { Withdraw anytime at market prices
- +
- + {connected ? ( + + ) : ( + + + + + + + +
+ +
+
+
+
+ )}
); }; diff --git a/components/stake/flexible/flexible-stake-modal.tsx b/components/stake/flexible/flexible-stake-modal.tsx index 7131755..5c4d54f 100644 --- a/components/stake/flexible/flexible-stake-modal.tsx +++ b/components/stake/flexible/flexible-stake-modal.tsx @@ -6,11 +6,18 @@ import { ModalPortal, ModalTitle, Typography, + toast, } from "@mochi-ui/core"; import { CloseLgLine } from "@mochi-ui/icons"; -import { useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { FlexibleStakeContent } from "./flexible-stake-content"; import { FlexibleStakeResponse } from "./flexible-stake-response"; +import { useLoginWidget } from "@mochi-web3/login-widget"; +import { ERC20TokenInteraction } from "@/services/contracts/Token"; +import { PoolAddress, StakingPool } from "@/services/contracts/Pool"; +import { ChainProvider } from "@mochi-web3/connect-wallet-widget"; +import { useFlexibleStaking } from "@/store/flexibleStaking"; +import { retry } from "@/utils/retry"; interface Props { open: boolean; @@ -19,7 +26,180 @@ interface Props { export const FlexibleStakeModal = (props: Props) => { const { open, onOpenChange } = props; - const [state, setState] = useState<"init" | "success">("init"); + const { wallets, getProviderByAddress, isLoggingIn, isLoggedIn } = + useLoginWidget(); + const { poolContract, icyContract, allowance, stakedAmount, setValues } = + useFlexibleStaking(); + const ref = useRef(null); + const [state, setState] = useState<"init" | "approved" | "success">("init"); + const [loading, setLoading] = useState< + "Initializing" | "Approving" | "Staking" | null + >(null); + + const connected = wallets.find((w) => w.connectionStatus === "connected"); + const address = connected?.address || ""; + + const updateValues = useCallback(async () => { + if (!poolContract || !icyContract) return; + const [ + getBalance, + getAllowance, + getApr, + getStakedAmount, + getPoolStakedAmount, + getEarnedRewards, + ] = await Promise.allSettled([ + icyContract.getTokenBalance(), + icyContract.getAllowance(PoolAddress.POOL_ICY_ICY), + poolContract.calculateRealtimeAPR(), + poolContract.getSenderStakedAmount(), + poolContract.getPoolTotalStakedAmount(), + poolContract.getTotalRewardEarnedForAddress(), + ]); + const apr = getApr.status === "fulfilled" ? getApr.value || 0 : 0; + const balance = + getBalance.status === "fulfilled" ? getBalance.value?.value || 0 : 0; + const allowance = + getAllowance.status === "fulfilled" ? getAllowance.value?.value || 0 : 0; + const stakedAmount = + getStakedAmount.status === "fulfilled" + ? getStakedAmount.value?.value || 0 + : 0; + const poolStakedAmount = + getPoolStakedAmount.status === "fulfilled" + ? getPoolStakedAmount.value?.value || 0 + : 0; + const earnedRewards = + getEarnedRewards.status === "fulfilled" + ? getEarnedRewards.value?.value || 0 + : 0; + setValues({ + apr, + balance, + allowance, + stakedAmount, + poolStakedAmount, + earnedRewards, + }); + }, [icyContract, poolContract, setValues]); + + useEffect(() => { + if (!address) return; + const provider = getProviderByAddress(address) as ChainProvider | null; + if (!provider) { + toast({ + scheme: "danger", + title: "Error", + description: "No provider connected.", + }); + return; + } + + const initializeContractInteraction = async () => { + try { + setLoading("Initializing"); + const poolContract = StakingPool.getInstance("ICY_ICY", provider); + const icyContract = ERC20TokenInteraction.getInstance("ICY", provider); + poolContract.setSenderAddress(address); + icyContract.setSenderAddress(address); + setValues({ poolContract, icyContract }); + + await updateValues(); + } catch (err) { + toast({ + scheme: "danger", + title: "Error", + description: "Failed to initialize contract interaction", + }); + } finally { + setLoading(null); + } + }; + initializeContractInteraction(); + }, [address, getProviderByAddress, setValues, updateValues]); + + const onStake = async (amount: number) => { + if (!poolContract) return; + try { + setLoading("Staking"); + const tx = await poolContract.stake(amount); + if (!tx) { + throw new Error("Failed to stake"); + } + // FIXME: retry to get updated values + const newStakedAmount = await retry( + async () => { + const newStakedAmount = await poolContract.getSenderStakedAmount(); + if (newStakedAmount?.value !== stakedAmount) { + return newStakedAmount?.value; + } else { + throw new Error("Staked amount not updated"); + } + }, + 3000, + 100 + ); + if (!newStakedAmount) { + throw new Error("Failed to get updated values"); + } + await updateValues(); + setState("success"); + } catch (err) { + toast({ + scheme: "danger", + title: "Error", + description: "Failed to stake", + }); + } finally { + setLoading(null); + } + }; + + const onApprove = async (amount: number) => { + if (!icyContract) return; + try { + setLoading("Approving"); + const tx = await icyContract.approveTokenAmount( + PoolAddress.POOL_ICY_ICY, + amount + ); + if (!tx) { + throw new Error("Failed to approve allowance"); + } + // FIXME: retry to get updated values + const newAllowance = await retry( + async () => { + const newAllowance = await icyContract.getAllowance( + PoolAddress.POOL_ICY_ICY + ); + if (newAllowance?.value !== allowance) { + return newAllowance?.value; + } else { + throw new Error("Allowance not updated"); + } + }, + 3000, + 100 + ); + if (!newAllowance) { + throw new Error("Failed to get updated values"); + } + await updateValues(); + setState("approved"); + } catch (err) { + toast({ + scheme: "danger", + title: "Error", + description: "Failed to approve allowance", + }); + } finally { + setLoading(null); + } + }; + + if (isLoggingIn) { + return null; + } return ( { > - - {state === "init" && ( + + {(state === "init" || state === "approved") && ( Stake ICY @@ -44,11 +227,11 @@ export const FlexibleStakeModal = (props: Props) => { )} - {state === "init" && ( + {(state === "init" || state === "approved") && ( { - setState("success"); - }} + {...{ onApprove, onStake, loading }} + connected={isLoggedIn && !!connected} + container={ref.current} /> )} {state === "success" && ( diff --git a/components/stake/flexible/flexible-stake-response.tsx b/components/stake/flexible/flexible-stake-response.tsx index 7007417..37108fe 100644 --- a/components/stake/flexible/flexible-stake-response.tsx +++ b/components/stake/flexible/flexible-stake-response.tsx @@ -1,12 +1,10 @@ -import { - Button, - ModalClose, - Switch, - Tooltip, - Typography, -} from "@mochi-ui/core"; +import { Button, Switch, Tooltip, Typography } from "@mochi-ui/core"; import { CheckCircleHalfColoredLine, CheckLine } from "@mochi-ui/icons"; import Image from "next/image"; +import { useEffect, useMemo } from "react"; +import { formatUnixTimestampToDateTime } from "@/utils/datetime"; +import { useFlexibleStaking } from "@/store/flexibleStaking"; +import { utils } from "@consolelabs/mochi-formatter"; interface Props { onClose: () => void; @@ -14,6 +12,34 @@ interface Props { export const FlexibleStakeResponse = (props: Props) => { const { onClose } = props; + const { + poolContract, + stakedAmount, + startTime, + finishTime, + autoStaking, + setValues, + } = useFlexibleStaking(); + + const stakeDate = useMemo( + () => formatUnixTimestampToDateTime(Math.floor(Date.now() / 1000)), + [] + ); + + useEffect(() => { + if (!poolContract) return; + const getPoolContract = async () => { + const stakeDates = await Promise.allSettled([ + poolContract.getPeriodStartDate(), + poolContract.getPeriodFinishDate(), + ]); + const [startTime, finishTime] = stakeDates.map((r) => + r.status === "fulfilled" ? r.value : "" + ); + setValues({ stakeDate, startTime, finishTime }); + }; + getPoolContract(); + }, [poolContract, setValues, stakeDate]); return ( <> @@ -31,7 +57,7 @@ export const FlexibleStakeResponse = (props: Props) => {
icy - 2,000 ICY + {utils.formatTokenDigit(stakedAmount)} ICY
@@ -43,7 +69,7 @@ export const FlexibleStakeResponse = (props: Props) => { Stake date - 08/03/2024 17:05 + {stakeDate}
@@ -53,7 +79,7 @@ export const FlexibleStakeResponse = (props: Props) => {
Value date
- 08/03/2024 07:00 + {startTime}
@@ -63,7 +89,7 @@ export const FlexibleStakeResponse = (props: Props) => {
Interest distribution date
- 09/03/2025 07:00 + {finishTime}
@@ -72,7 +98,10 @@ export const FlexibleStakeResponse = (props: Props) => { className="max-w-xs text-center z-50" arrow="bottom-center" > - + setValues({ autoStaking })} + /> Auto-Staking diff --git a/components/stake/stake-input.tsx b/components/stake/stake-input.tsx index 1a2f05b..b385def 100644 --- a/components/stake/stake-input.tsx +++ b/components/stake/stake-input.tsx @@ -1,19 +1,19 @@ import { Avatar, Button, Switch, Tooltip, Typography } from "@mochi-ui/core"; -import { Dispatch, SetStateAction, useState } from "react"; +import { Dispatch, SetStateAction, useEffect, useState } from "react"; import * as Slider from "@radix-ui/react-slider"; import clsx from "clsx"; import { utils } from "@consolelabs/mochi-formatter"; import { TokenAmount, formatTokenAmount } from "@/utils/number"; interface Props { + balance?: number; amount: TokenAmount; setAmount: Dispatch>; } export const StakeInput = (props: Props) => { - const { amount, setAmount } = props; + const { balance = 0, amount, setAmount } = props; const [percent, setPercent] = useState(0); - const balance = 23667; const onMaxAmount = () => { setPercent(100); @@ -55,20 +55,18 @@ export const StakeInput = (props: Props) => { const formattedAmount = formatTokenAmount(e.target.value); formattedAmount.display = e.target.value; setAmount(formattedAmount); - const percent = Math.max( - 0, - Math.min(100, (formattedAmount.value / balance) * 100) - ); + const percent = balance + ? Math.max(0, Math.min(100, (formattedAmount.value / balance) * 100)) + : 0; setPercent(percent); }; const onBlur = (e: React.FocusEvent) => { const formattedAmount = formatTokenAmount(e.target.value); setAmount(formattedAmount); - const percent = Math.max( - 0, - Math.min(100, (formattedAmount.value / balance) * 100) - ); + const percent = balance + ? Math.max(0, Math.min(100, (formattedAmount.value / balance) * 100)) + : 0; setPercent(percent); }; @@ -118,17 +116,10 @@ export const StakeInput = (props: Props) => { className="relative flex w-full h-2 cursor-pointer items-center [&>span:last-child]:z-10" value={[percent]} onValueChange={(value) => { - console.log("Slider change"); const percent = value[0]; setPercent(percent); setAmount(formatTokenAmount((balance * percent) / 100)); }} - onValueCommit={() => { - console.log("Slider commit"); - }} - onClick={() => { - console.log("Slider click"); - }} max={100} step={1} > @@ -153,7 +144,6 @@ export const StakeInput = (props: Props) => { : "bg-background-level2" )} onClick={() => { - console.log("click"); setPercent(milestone); setAmount(formatTokenAmount((balance * milestone) / 100)); }} diff --git a/package.json b/package.json index eeffba2..beefe2d 100644 --- a/package.json +++ b/package.json @@ -6,24 +6,32 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "generate-types": "typechain --target ethers-v5 --out-dir contracts/types './contracts/*.json'" }, "dependencies": { "@consolelabs/mochi-formatter": "^20.0.5", "@dwarvesf/react-hooks": "^0.8.2", + "@ethersproject/abi": "^5.7.0", + "@ethersproject/providers": "^5.7.2", "@mochi-ui/core": "^0.13.4", "@mochi-ui/icons": "^0.7.4", "@mochi-ui/theme": "^0.17.0", - "@mochi-web3/login-widget": "^0.2.11", + "@mochi-web3/connect-wallet-widget": "^0.2.4", + "@mochi-web3/login-widget": "^0.2.17", "@radix-ui/react-slider": "^1.1.2", "@semantic-release/git": "^10.0.1", + "bignumber.js": "^9.1.2", "clsx": "^2.1.0", + "ethers": "5.7.2", "next": "14.1.3", "react": "^18", "react-dom": "^18", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "zustand": "^4.5.2" }, "devDependencies": { + "@typechain/ethers-v5": "^11.1.2", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a766d8a..fd24c5e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,12 @@ dependencies: '@dwarvesf/react-hooks': specifier: ^0.8.2 version: 0.8.2(react-dom@18.0.0)(react@18.0.0) + '@ethersproject/abi': + specifier: ^5.7.0 + version: 5.7.0 + '@ethersproject/providers': + specifier: ^5.7.2 + version: 5.7.2 '@mochi-ui/core': specifier: ^0.13.4 version: 0.13.4(@types/react-dom@18.0.0)(@types/react@18.0.0)(react-dom@18.0.0)(react@18.0.0)(tailwindcss@3.3.0) @@ -20,18 +26,27 @@ dependencies: '@mochi-ui/theme': specifier: ^0.17.0 version: 0.17.0(tailwindcss@3.3.0) + '@mochi-web3/connect-wallet-widget': + specifier: ^0.2.4 + version: 0.2.4(@types/react-dom@18.0.0)(@types/react@18.0.0)(ioredis@5.3.2)(react-dom@18.0.0)(react@18.0.0)(tailwindcss@3.3.0)(typescript@5.0.2) '@mochi-web3/login-widget': - specifier: ^0.2.11 - version: 0.2.11(@types/react-dom@18.0.0)(@types/react@18.0.0)(ioredis@5.3.2)(react-dom@18.0.0)(react@18.0.0)(tailwindcss@3.3.0)(typescript@5.0.2) + specifier: ^0.2.17 + version: 0.2.17(@types/react-dom@18.0.0)(@types/react@18.0.0)(ioredis@5.3.2)(react-dom@18.0.0)(react@18.0.0)(tailwindcss@3.3.0)(typescript@5.0.2) '@radix-ui/react-slider': specifier: ^1.1.2 version: 1.1.2(@types/react-dom@18.0.0)(@types/react@18.0.0)(react-dom@18.0.0)(react@18.0.0) '@semantic-release/git': specifier: ^10.0.1 version: 10.0.1(semantic-release@23.0.3) + bignumber.js: + specifier: ^9.1.2 + version: 9.1.2 clsx: specifier: ^2.1.0 version: 2.1.0 + ethers: + specifier: 5.7.2 + version: 5.7.2 next: specifier: 14.1.3 version: 14.1.3(react-dom@18.0.0)(react@18.0.0) @@ -44,8 +59,14 @@ dependencies: tailwindcss-animate: specifier: ^1.0.7 version: 1.0.7(tailwindcss@3.3.0) + zustand: + specifier: ^4.5.2 + version: 4.5.2(@types/react@18.0.0)(react@18.0.0) devDependencies: + '@typechain/ethers-v5': + specifier: ^11.1.2 + version: 11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typechain@8.3.2)(typescript@5.0.2) '@types/node': specifier: ^20 version: 20.0.0 @@ -320,7 +341,6 @@ packages: '@ethersproject/logger': 5.7.0 '@ethersproject/properties': 5.7.0 '@ethersproject/strings': 5.7.0 - dev: false /@ethersproject/abstract-provider@5.7.0: resolution: {integrity: sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==} @@ -332,7 +352,6 @@ packages: '@ethersproject/properties': 5.7.0 '@ethersproject/transactions': 5.7.0 '@ethersproject/web': 5.7.1 - dev: false /@ethersproject/abstract-signer@5.7.0: resolution: {integrity: sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==} @@ -342,7 +361,6 @@ packages: '@ethersproject/bytes': 5.7.0 '@ethersproject/logger': 5.7.0 '@ethersproject/properties': 5.7.0 - dev: false /@ethersproject/address@5.7.0: resolution: {integrity: sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==} @@ -352,20 +370,17 @@ packages: '@ethersproject/keccak256': 5.7.0 '@ethersproject/logger': 5.7.0 '@ethersproject/rlp': 5.7.0 - dev: false /@ethersproject/base64@5.7.0: resolution: {integrity: sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==} dependencies: '@ethersproject/bytes': 5.7.0 - dev: false /@ethersproject/basex@5.7.0: resolution: {integrity: sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==} dependencies: '@ethersproject/bytes': 5.7.0 '@ethersproject/properties': 5.7.0 - dev: false /@ethersproject/bignumber@5.7.0: resolution: {integrity: sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==} @@ -373,19 +388,16 @@ packages: '@ethersproject/bytes': 5.7.0 '@ethersproject/logger': 5.7.0 bn.js: 5.2.1 - dev: false /@ethersproject/bytes@5.7.0: resolution: {integrity: sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==} dependencies: '@ethersproject/logger': 5.7.0 - dev: false /@ethersproject/constants@5.7.0: resolution: {integrity: sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==} dependencies: '@ethersproject/bignumber': 5.7.0 - dev: false /@ethersproject/contracts@5.7.0: resolution: {integrity: sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==} @@ -400,7 +412,6 @@ packages: '@ethersproject/logger': 5.7.0 '@ethersproject/properties': 5.7.0 '@ethersproject/transactions': 5.7.0 - dev: false /@ethersproject/hash@5.7.0: resolution: {integrity: sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==} @@ -414,7 +425,6 @@ packages: '@ethersproject/logger': 5.7.0 '@ethersproject/properties': 5.7.0 '@ethersproject/strings': 5.7.0 - dev: false /@ethersproject/hdnode@5.7.0: resolution: {integrity: sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==} @@ -431,7 +441,6 @@ packages: '@ethersproject/strings': 5.7.0 '@ethersproject/transactions': 5.7.0 '@ethersproject/wordlists': 5.7.0 - dev: false /@ethersproject/json-wallets@5.7.0: resolution: {integrity: sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==} @@ -449,37 +458,31 @@ packages: '@ethersproject/transactions': 5.7.0 aes-js: 3.0.0 scrypt-js: 3.0.1 - dev: false /@ethersproject/keccak256@5.7.0: resolution: {integrity: sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==} dependencies: '@ethersproject/bytes': 5.7.0 js-sha3: 0.8.0 - dev: false /@ethersproject/logger@5.7.0: resolution: {integrity: sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==} - dev: false /@ethersproject/networks@5.7.1: resolution: {integrity: sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==} dependencies: '@ethersproject/logger': 5.7.0 - dev: false /@ethersproject/pbkdf2@5.7.0: resolution: {integrity: sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==} dependencies: '@ethersproject/bytes': 5.7.0 '@ethersproject/sha2': 5.7.0 - dev: false /@ethersproject/properties@5.7.0: resolution: {integrity: sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==} dependencies: '@ethersproject/logger': 5.7.0 - dev: false /@ethersproject/providers@5.7.2: resolution: {integrity: sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==} @@ -507,21 +510,18 @@ packages: transitivePeerDependencies: - bufferutil - utf-8-validate - dev: false /@ethersproject/random@5.7.0: resolution: {integrity: sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==} dependencies: '@ethersproject/bytes': 5.7.0 '@ethersproject/logger': 5.7.0 - dev: false /@ethersproject/rlp@5.7.0: resolution: {integrity: sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==} dependencies: '@ethersproject/bytes': 5.7.0 '@ethersproject/logger': 5.7.0 - dev: false /@ethersproject/sha2@5.7.0: resolution: {integrity: sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==} @@ -529,7 +529,6 @@ packages: '@ethersproject/bytes': 5.7.0 '@ethersproject/logger': 5.7.0 hash.js: 1.1.7 - dev: false /@ethersproject/signing-key@5.7.0: resolution: {integrity: sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==} @@ -540,7 +539,6 @@ packages: bn.js: 5.2.1 elliptic: 6.5.4 hash.js: 1.1.7 - dev: false /@ethersproject/solidity@5.7.0: resolution: {integrity: sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==} @@ -551,7 +549,6 @@ packages: '@ethersproject/logger': 5.7.0 '@ethersproject/sha2': 5.7.0 '@ethersproject/strings': 5.7.0 - dev: false /@ethersproject/strings@5.7.0: resolution: {integrity: sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==} @@ -559,7 +556,6 @@ packages: '@ethersproject/bytes': 5.7.0 '@ethersproject/constants': 5.7.0 '@ethersproject/logger': 5.7.0 - dev: false /@ethersproject/transactions@5.7.0: resolution: {integrity: sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==} @@ -573,7 +569,6 @@ packages: '@ethersproject/properties': 5.7.0 '@ethersproject/rlp': 5.7.0 '@ethersproject/signing-key': 5.7.0 - dev: false /@ethersproject/units@5.7.0: resolution: {integrity: sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==} @@ -581,7 +576,6 @@ packages: '@ethersproject/bignumber': 5.7.0 '@ethersproject/constants': 5.7.0 '@ethersproject/logger': 5.7.0 - dev: false /@ethersproject/wallet@5.7.0: resolution: {integrity: sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==} @@ -601,7 +595,6 @@ packages: '@ethersproject/signing-key': 5.7.0 '@ethersproject/transactions': 5.7.0 '@ethersproject/wordlists': 5.7.0 - dev: false /@ethersproject/web@5.7.1: resolution: {integrity: sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==} @@ -611,7 +604,6 @@ packages: '@ethersproject/logger': 5.7.0 '@ethersproject/properties': 5.7.0 '@ethersproject/strings': 5.7.0 - dev: false /@ethersproject/wordlists@5.7.0: resolution: {integrity: sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==} @@ -621,7 +613,6 @@ packages: '@ethersproject/logger': 5.7.0 '@ethersproject/properties': 5.7.0 '@ethersproject/strings': 5.7.0 - dev: false /@floating-ui/core@1.6.0: resolution: {integrity: sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==} @@ -1641,8 +1632,8 @@ packages: - tailwindcss dev: false - /@mochi-web3/connect-wallet-widget@0.0.15(@types/react-dom@18.0.0)(@types/react@18.0.0)(ioredis@5.3.2)(react-dom@18.0.0)(react@18.0.0)(tailwindcss@3.3.0)(typescript@5.0.2): - resolution: {integrity: sha512-EOrRDSWxTE+XPJ2muGjDVNLJx2HaYMc4U+SkHmvzrsVMr5Na+NgpuZIGWumgWRJoDFjAz7GimH6psRNwH0mu3w==} + /@mochi-web3/connect-wallet-widget@0.2.4(@types/react-dom@18.0.0)(@types/react@18.0.0)(ioredis@5.3.2)(react-dom@18.0.0)(react@18.0.0)(tailwindcss@3.3.0)(typescript@5.0.2): + resolution: {integrity: sha512-3jKSrXuTrTwX0vcG0FfJP3+d6Y/ew0VLQqPia2ygCDl+TPcZxqd3kGTqBwN8W2RZQK4JqHv5TslUgr1osC9/YQ==} peerDependencies: react: ^18.2.0 react-dom: ^18.2.0 @@ -1691,8 +1682,8 @@ packages: - zod dev: false - /@mochi-web3/login-widget@0.2.11(@types/react-dom@18.0.0)(@types/react@18.0.0)(ioredis@5.3.2)(react-dom@18.0.0)(react@18.0.0)(tailwindcss@3.3.0)(typescript@5.0.2): - resolution: {integrity: sha512-RN/jtJe3wZ5SNncJNy+thR+2p89kOvHFENZkSZdPF4fIDOgdpPWQHfPNDW5JPlNndDNFevAdWefJcqDIw3VkXQ==} + /@mochi-web3/login-widget@0.2.17(@types/react-dom@18.0.0)(@types/react@18.0.0)(ioredis@5.3.2)(react-dom@18.0.0)(react@18.0.0)(tailwindcss@3.3.0)(typescript@5.0.2): + resolution: {integrity: sha512-Y0RugvapMk5fPJ4FHsoja2UmfUZwxzgzsMY5F2FTP0LTdrMhkBpTRnVLr98cLlsTJA7dmqlzhglrteoiBrtOFw==} peerDependencies: react: ^18.2.0 react-dom: ^18.2.0 @@ -1707,7 +1698,7 @@ packages: '@mochi-ui/tabs': 0.2.8(@types/react-dom@18.0.0)(@types/react@18.0.0)(react-dom@18.0.0)(react@18.0.0)(tailwindcss@3.3.0) '@mochi-ui/theme': 0.18.0(tailwindcss@3.3.0) '@mochi-ui/typography': 0.1.1(react-dom@18.0.0)(react@18.0.0) - '@mochi-web3/connect-wallet-widget': 0.0.15(@types/react-dom@18.0.0)(@types/react@18.0.0)(ioredis@5.3.2)(react-dom@18.0.0)(react@18.0.0)(tailwindcss@3.3.0)(typescript@5.0.2) + '@mochi-web3/connect-wallet-widget': 0.2.4(@types/react-dom@18.0.0)(@types/react@18.0.0)(ioredis@5.3.2)(react-dom@18.0.0)(react@18.0.0)(tailwindcss@3.3.0)(typescript@5.0.2) '@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.0.0)(@types/react@18.0.0)(react-dom@18.0.0)(react@18.0.0) '@uidotdev/usehooks': 2.4.1(react-dom@18.0.0)(react@18.0.0) dlv: 1.1.3 @@ -3538,6 +3529,24 @@ packages: engines: {node: '>=12'} dev: false + /@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typechain@8.3.2)(typescript@5.0.2): + resolution: {integrity: sha512-ID6pqWkao54EuUQa0P5RgjvfA3MYqxUQKpbGKERbsjBW5Ra7EIXvbMlPp2pcP5IAdUkyMCFYsP2SN5q7mPdLDQ==} + peerDependencies: + '@ethersproject/abi': ^5.0.0 + '@ethersproject/providers': ^5.0.0 + ethers: ^5.1.3 + typechain: ^8.3.2 + typescript: '>=4.3.0' + dependencies: + '@ethersproject/abi': 5.7.0 + '@ethersproject/providers': 5.7.2 + ethers: 5.7.2 + lodash: 4.17.21 + ts-essentials: 7.0.3(typescript@5.0.2) + typechain: 8.3.2(typescript@5.0.2) + typescript: 5.0.2 + dev: true + /@types/connect@3.4.38: resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} dependencies: @@ -3601,6 +3610,10 @@ packages: resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} dev: false + /@types/prettier@2.7.3: + resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==} + dev: true + /@types/prop-types@15.7.11: resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} @@ -4008,7 +4021,6 @@ packages: /aes-js@3.0.0: resolution: {integrity: sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==} - dev: false /aes-js@4.0.0-beta.5: resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==} @@ -4088,7 +4100,6 @@ packages: engines: {node: '>=4'} dependencies: color-convert: 1.9.3 - dev: false /ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} @@ -4139,6 +4150,16 @@ packages: dequal: 2.0.3 dev: true + /array-back@3.1.0: + resolution: {integrity: sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==} + engines: {node: '>=6'} + dev: true + + /array-back@4.0.2: + resolution: {integrity: sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==} + engines: {node: '>=8'} + dev: true + /array-buffer-byte-length@1.0.1: resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} engines: {node: '>= 0.4'} @@ -4350,7 +4371,6 @@ packages: /bech32@1.1.4: resolution: {integrity: sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==} - dev: false /before-after-hook@3.0.2: resolution: {integrity: sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==} @@ -4384,11 +4404,9 @@ packages: /bn.js@4.12.0: resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} - dev: false /bn.js@5.2.1: resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} - dev: false /borsh@0.7.0: resolution: {integrity: sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==} @@ -4421,7 +4439,6 @@ packages: /brorand@1.1.0: resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} - dev: false /browser-string-hexer@1.0.0: resolution: {integrity: sha512-c/DIdftQT9dScnhQOVWIT+hcGPPhbZpgX4RXyPfLeg8aqrVQ3lwfH0JAioMhzQhye+C0zPjsQXtSm4jU1I999g==} @@ -4688,7 +4705,6 @@ packages: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 - dev: false /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -4877,7 +4893,6 @@ packages: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: color-name: 1.1.3 - dev: false /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} @@ -4887,7 +4902,6 @@ packages: /color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - dev: false /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} @@ -4910,6 +4924,26 @@ packages: /colorette@1.4.0: resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==} + /command-line-args@5.2.1: + resolution: {integrity: sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==} + engines: {node: '>=4.0.0'} + dependencies: + array-back: 3.1.0 + find-replace: 3.0.0 + lodash.camelcase: 4.3.0 + typical: 4.0.0 + dev: true + + /command-line-usage@6.1.3: + resolution: {integrity: sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw==} + engines: {node: '>=8.0.0'} + dependencies: + array-back: 4.0.2 + chalk: 2.4.2 + table-layout: 1.0.2 + typical: 5.2.0 + dev: true + /commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} dev: false @@ -5136,7 +5170,6 @@ packages: /deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} - dev: false /deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -5292,7 +5325,6 @@ packages: inherits: 2.0.4 minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 - dev: false /emittery@0.10.2: resolution: {integrity: sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==} @@ -5475,7 +5507,6 @@ packages: /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} - dev: false /escape-string-regexp@2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} @@ -5825,7 +5856,6 @@ packages: transitivePeerDependencies: - bufferutil - utf-8-validate - dev: false /ethers@6.11.1: resolution: {integrity: sha512-mxTAE6wqJQAbp5QAe/+o+rXOID7Nw91OZXvgpjDa1r4fAbq2Nu314oEZSbjoRLacuCzs7kUC3clEvkCQowffGg==} @@ -5975,6 +6005,13 @@ packages: engines: {node: '>=0.10.0'} dev: false + /find-replace@3.0.0: + resolution: {integrity: sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==} + engines: {node: '>=4.0.0'} + dependencies: + array-back: 3.1.0 + dev: true + /find-up-simple@1.0.0: resolution: {integrity: sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==} engines: {node: '>=18'} @@ -6068,6 +6105,15 @@ packages: universalify: 2.0.1 dev: false + /fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + dev: true + /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -6186,6 +6232,17 @@ packages: minipass: 7.0.4 path-scurry: 1.10.1 + /glob@7.1.7: + resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} dependencies: @@ -6295,7 +6352,6 @@ packages: /has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} - dev: false /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} @@ -6329,7 +6385,6 @@ packages: dependencies: inherits: 2.0.4 minimalistic-assert: 1.0.1 - dev: false /hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} @@ -6354,7 +6409,6 @@ packages: hash.js: 1.1.7 minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 - dev: false /hook-std@3.0.0: resolution: {integrity: sha512-jHRQzjSDzMtFy34AGj1DN+vq54WVuhSvKgrHf0OMiFQTwDD4L/qqofVEWjLOBMTn5+lCD3fPg32W9yOfnEJTTw==} @@ -6929,7 +6983,6 @@ packages: /js-sha3@0.8.0: resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} - dev: false /js-string-escape@1.0.1: resolution: {integrity: sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==} @@ -6988,6 +7041,12 @@ packages: resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==} dev: false + /jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + optionalDependencies: + graceful-fs: 4.2.11 + dev: true + /jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} dependencies: @@ -7118,6 +7177,10 @@ packages: resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} dev: false + /lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + dev: true + /lodash.capitalize@4.2.1: resolution: {integrity: sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==} dev: false @@ -7183,7 +7246,6 @@ packages: /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: false /longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -7671,11 +7733,9 @@ packages: /minimalistic-assert@1.0.1: resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} - dev: false /minimalistic-crypto-utils@1.0.1: resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} - dev: false /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -7711,6 +7771,12 @@ packages: - zod dev: false + /mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + dev: true + /mlly@1.6.1: resolution: {integrity: sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==} dependencies: @@ -8491,6 +8557,12 @@ packages: engines: {node: '>= 0.8.0'} dev: true + /prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + /pretty-ms@7.0.1: resolution: {integrity: sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==} engines: {node: '>=10'} @@ -8705,6 +8777,11 @@ packages: redis-errors: 1.2.0 dev: false + /reduce-flatten@2.0.0: + resolution: {integrity: sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==} + engines: {node: '>=6'} + dev: true + /reflect.getprototypeof@1.0.5: resolution: {integrity: sha512-62wgfC8dJWrmxv44CA36pLDnP6KKl3Vhxb7PL+8+qrrFMMoJij4vgiMP8zV4O8+CBMXY1mHxI5fITGHXFHVmQQ==} engines: {node: '>= 0.4'} @@ -8899,7 +8976,6 @@ packages: /scrypt-js@3.0.1: resolution: {integrity: sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==} - dev: false /search-params@3.0.0: resolution: {integrity: sha512-8CYNl/bjkEhXWbDTU/K7c2jQtrnqEffIPyOLMqygW/7/b+ym8UtQumcAZjOfMLjZKR6AxK5tOr9fChbQZCzPqg==} @@ -9194,6 +9270,10 @@ packages: engines: {node: '>=4'} dev: false + /string-format@2.0.0: + resolution: {integrity: sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==} + dev: true + /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -9351,7 +9431,6 @@ packages: engines: {node: '>=4'} dependencies: has-flag: 3.0.0 - dev: false /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} @@ -9376,6 +9455,16 @@ packages: engines: {node: '>=18'} dev: false + /table-layout@1.0.2: + resolution: {integrity: sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==} + engines: {node: '>=8.0.0'} + dependencies: + array-back: 4.0.2 + deep-extend: 0.6.0 + typical: 5.2.0 + wordwrapjs: 4.0.1 + dev: true + /tailwindcss-animate@1.0.7(tailwindcss@3.3.0): resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} peerDependencies: @@ -9525,6 +9614,24 @@ packages: typescript: 5.0.2 dev: true + /ts-command-line-args@2.5.1: + resolution: {integrity: sha512-H69ZwTw3rFHb5WYpQya40YAX2/w7Ut75uUECbgBIsLmM+BNuYnxsltfyyLMxy6sEeKxgijLTnQtLd0nKd6+IYw==} + hasBin: true + dependencies: + chalk: 4.1.2 + command-line-args: 5.2.1 + command-line-usage: 6.1.3 + string-format: 2.0.0 + dev: true + + /ts-essentials@7.0.3(typescript@5.0.2): + resolution: {integrity: sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==} + peerDependencies: + typescript: '>=3.7.0' + dependencies: + typescript: 5.0.2 + dev: true + /ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} @@ -9590,6 +9697,27 @@ packages: engines: {node: '>=16'} dev: false + /typechain@8.3.2(typescript@5.0.2): + resolution: {integrity: sha512-x/sQYr5w9K7yv3es7jo4KTX05CLxOf7TRWwoHlrjRh8H82G64g+k7VuWPJlgMo6qrjfCulOdfBjiaDtmhFYD/Q==} + hasBin: true + peerDependencies: + typescript: '>=4.3.0' + dependencies: + '@types/prettier': 2.7.3 + debug: 4.3.4 + fs-extra: 7.0.1 + glob: 7.1.7 + js-sha3: 0.8.0 + lodash: 4.17.21 + mkdirp: 1.0.4 + prettier: 2.8.8 + ts-command-line-args: 2.5.1 + ts-essentials: 7.0.3(typescript@5.0.2) + typescript: 5.0.2 + transitivePeerDependencies: + - supports-color + dev: true + /typed-array-buffer@1.0.2: resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} engines: {node: '>= 0.4'} @@ -9645,6 +9773,16 @@ packages: engines: {node: '>=12.20'} hasBin: true + /typical@4.0.0: + resolution: {integrity: sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==} + engines: {node: '>=8'} + dev: true + + /typical@5.2.0: + resolution: {integrity: sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==} + engines: {node: '>=8'} + dev: true + /ufo@1.4.0: resolution: {integrity: sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==} dev: false @@ -9746,6 +9884,11 @@ packages: resolution: {integrity: sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==} dev: false + /universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + dev: true + /universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -10058,6 +10201,14 @@ packages: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} dev: false + /wordwrapjs@4.0.1: + resolution: {integrity: sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==} + engines: {node: '>=8.0.0'} + dependencies: + reduce-flatten: 2.0.0 + typical: 5.2.0 + dev: true + /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -10102,7 +10253,6 @@ packages: optional: true utf-8-validate: optional: true - dev: false /ws@7.5.9: resolution: {integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==} diff --git a/services/contracts/Pool.ts b/services/contracts/Pool.ts new file mode 100644 index 0000000..c122972 --- /dev/null +++ b/services/contracts/Pool.ts @@ -0,0 +1,270 @@ +import { ChainProvider } from "@mochi-web3/connect-wallet-widget"; +import { ERC20TokenInfo } from ".."; +import { BigNumber, BigNumberish } from "ethers"; +import { TokenAmount, formatTokenAmount, getAmountWithDecimals } from "@/utils/number"; +import { formatUnixTimestampToDateTime } from "@/utils/datetime"; +import { formatUnits } from "ethers/lib/utils"; + + +const Abi = { + POOL_ICY_ICY: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_rewardsDistributor\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_rewardsToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_stakingToken\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"}],\"name\":\"AddressEmptyCode\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"AddressInsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EnforcedPause\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ExpectedPause\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedInnerCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrancyGuardReentrantCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"SafeERC20FailedOperation\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Deposited\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Recovered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"reward\",\"type\":\"uint256\"}],\"name\":\"RewardAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"reward\",\"type\":\"uint256\"}],\"name\":\"RewardPaid\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newDuration\",\"type\":\"uint256\"}],\"name\":\"RewardsDurationUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Withdrawn\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"deposit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"earned\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getReward\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRewardForDuration\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastTimeRewardApplicable\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastUpdateTime\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"reward\",\"type\":\"uint256\"}],\"name\":\"notifyRewardAmount\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"periodFinish\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenAmount\",\"type\":\"uint256\"}],\"name\":\"recoverERC20\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rewardPerToken\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rewardPerTokenStored\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rewardRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"rewards\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rewardsDistributor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rewardsDuration\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rewardsToken\",\"outputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_rewardsDuration\",\"type\":\"uint256\"}],\"name\":\"setRewardsDuration\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"stakingToken\",\"outputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unstake\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"userRewardPerTokenPaid\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + POOL_ICY_DFG: "", +} + +export const PoolAddress = { + POOL_ICY_ICY: "0x92598F02cE6D26Fa320d3fDE73f4EE1059448d66", + POOL_ICY_DFG: "", +} + +export type PoolType = "ICY_ICY" | "ICY_DFG"; + +interface TokenInfo { + address: string; + decimals: number; +} + +export class StakingPool { + private static instances: Map = new Map(); + private provider: ChainProvider; + private abi: string; + private address: string; + private stakingToken: TokenInfo; + private rewardToken: TokenInfo; + private sender: string = ""; + + private constructor(_abi: string, _address: string, _stakingToken: TokenInfo, _rewardToken: TokenInfo, _provider: ChainProvider) { + this.provider = _provider; + this.abi = _abi; + this.address = _address; + this.stakingToken = _stakingToken; + this.rewardToken = _rewardToken; + } + + public static getInstance(type: PoolType, _provider: ChainProvider): StakingPool { + if (!StakingPool.instances.has(type)) { + if (type === "ICY_ICY") { + StakingPool.instances.set(type, new StakingPool(Abi.POOL_ICY_ICY, PoolAddress.POOL_ICY_ICY, ERC20TokenInfo.ICY, ERC20TokenInfo.ICY, _provider)); + } else { + StakingPool.instances.set(type, new StakingPool(Abi.POOL_ICY_DFG, PoolAddress.POOL_ICY_DFG, ERC20TokenInfo.ICY, ERC20TokenInfo.DFG, _provider)); + } + } + return StakingPool.instances.get(type)!; + } + + setSenderAddress(address: string) { + this.sender = address; + } + + getAddress() { + return this.address; + } + + private getBigNumberValueByDecimals(value: BigNumberish, decimals: number): TokenAmount { + return formatTokenAmount(formatUnits(value, decimals)); + } + + async getPeriodFinishDate(): Promise { + try { + const response: BigNumber[] = await this.provider.read({ + abi: this.abi, + method: "periodFinish", + args: [], + to: this.address, + from: this.sender, + }); + + if (response?.length && BigNumber.isBigNumber(response[0])) { + console.log("getPeriodFinishDate", response[0].toNumber()); + return formatUnixTimestampToDateTime(response[0].toNumber()); + } + + console.error("cannot get periodFinish"); + return ""; + } catch (error) { + console.error(error); + return ""; + } + } + + async getPeriodStartDate(): Promise { + try { + const response: BigNumber[] = await this.provider.read({ + abi: this.abi, + method: "lastUpdateTime", + args: [], + to: this.address, + from: this.sender, + }); + + if (response?.length && BigNumber.isBigNumber(response[0])) { + console.log("getLastRewardUpdateDate", response[0].toBigInt()); + return formatUnixTimestampToDateTime(response[0].toNumber()); + } + + console.error("cannot get lastUpdateTime"); + return ""; + } catch (error) { + console.error(error); + return ""; + } + } + + async getRewardPerTokenStaked() { + try { + const response: BigNumber[] = await this.provider.read({ + abi: this.abi, + method: "rewardPerToken", + args: [], + to: this.address, + from: this.sender, + }); + + console.log(response); + if (response?.length && BigNumber.isBigNumber(response[0])) { + return this.getBigNumberValueByDecimals(response[0].toBigInt(), this.rewardToken.decimals); + } + } catch (error) { + console.error(error); + return; + } + } + + async getSenderStakedAmount() { + try { + const response: BigNumber[] = await this.provider.read({ + abi: this.abi, + method: "balanceOf", + args: [this.sender], + to: this.address, + from: this.sender, + }); + + console.log(response); + if (response?.length && BigNumber.isBigNumber(response[0])) { + return this.getBigNumberValueByDecimals(response[0].toBigInt(), this.rewardToken.decimals); + } + } catch (error) { + console.error(error); + return; + } + } + + async getPoolTotalStakedAmount() { + try { + const response: BigNumber[] = await this.provider.read({ + abi: this.abi, + method: "totalSupply", + args: [], + to: this.address, + from: this.sender, + }); + + console.log(response); + if (response?.length && BigNumber.isBigNumber(response[0])) { + return this.getBigNumberValueByDecimals(response[0].toBigInt(), this.rewardToken.decimals); + } + } catch (error) { + console.error(error); + return; + } + } + + async getTotalRewardEarnedForAddress() { + try { + const response: BigNumber[] = await this.provider.read({ + abi: this.abi, + method: "earned", + args: [this.sender], + to: this.address, + from: this.sender, + }); + console.log("getTotalRewardEarnedForAddress: ", response); + if (response?.length && BigNumber.isBigNumber(response[0])) { + return this.getBigNumberValueByDecimals(response[0].toBigInt(), this.rewardToken.decimals); + } + } catch (error) { + console.error(error); + return; + } + } + + async getCurrentRewardRate() { + try { + const response: BigNumber[] = await this.provider.read({ + abi: this.abi, + method: "rewardRate", + args: [], + to: this.address, + from: this.sender, + }); + console.log("getCurrentRewardRate: ", response); + if (response?.length && BigNumber.isBigNumber(response[0])) { + return this.getBigNumberValueByDecimals(response[0].toBigInt(), this.rewardToken.decimals); + } + } catch (error) { + console.error(error); + return; + } + } + + async calculateRealtimeAPR(): Promise { + await this.getCurrentRewardRate(); + return await Promise.resolve(28.7); + } + + /** + * WRITE METHODS + */ + async stake(amount: number) { + const { decimals } = this.stakingToken; + const amountWithDecimals = getAmountWithDecimals(amount, decimals).toString(); + try { + const txHash = await this.provider.write({ + abi: this.abi, + method: "deposit", + args: [amountWithDecimals], + to: this.address, + from: this.sender, + }); + if (txHash) return txHash; + console.log("stake tx_hash = ", txHash); + } catch (error) { + console.error(error); + return; + } + } + + async unstake() { + try { + const txHash = await this.provider.write({ + abi: this.abi, + method: "unstake", + args: [], + to: this.address, + from: this.sender, + }); + if (txHash) return txHash; + console.log("unstake tx_hash = ", txHash); + } catch (error) { + console.error(error); + return; + } + } + + async claimReward() { + try { + const txHash = await this.provider.write({ + abi: this.abi, + method: "getReward", + args: [], + to: this.address, + from: this.sender, + }); + if (txHash) return txHash; + console.log("getReward tx_hash = ", txHash); + } catch (error) { + console.error(error); + return; + } + } +} \ No newline at end of file diff --git a/services/contracts/Token.ts b/services/contracts/Token.ts new file mode 100644 index 0000000..2df42a9 --- /dev/null +++ b/services/contracts/Token.ts @@ -0,0 +1,114 @@ +import { TokenAmount, formatTokenAmount, getAmountWithDecimals } from "@/utils/number"; +import { ChainProvider } from "@mochi-web3/connect-wallet-widget"; +import { BigNumber, BigNumberish } from "ethers"; +import { formatUnits } from "ethers/lib/utils"; + +const Abi = { + ICY: "[{\"inputs\":[],\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"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\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burnFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"subtractedValue\",\"type\":\"uint256\"}],\"name\":\"decreaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"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\"}]", + DFG: "[]" +} + +export const ERC20TokenInfo = { + ICY: { + address: "0xf289e3b222dd42b185b7e335fa3c5bd6d132441d", + decimals: 18, + }, + DFG: { + address: "", + decimals: 18, + } +} + +export type TokenType = "ICY" | "DFG"; + +export class ERC20TokenInteraction { + private static instances: Map = new Map(); + private provider: ChainProvider; + private abi: string; + private address: string; + private decimals: number; + private sender: string; + + private constructor(_abi: string, _address: string, _provider: ChainProvider, _decimals: number = 18) { + this.provider = _provider; + this.abi = _abi; + this.address = _address; + this.decimals = _decimals; + this.sender = ""; // current connected wallet address + } + + public static getInstance(type: TokenType, _provider: ChainProvider): ERC20TokenInteraction { + if (!ERC20TokenInteraction.instances.has(type)) { + if (type === "ICY") { + ERC20TokenInteraction.instances.set(type, new ERC20TokenInteraction(Abi.ICY, ERC20TokenInfo.ICY.address, _provider)); + } else { + ERC20TokenInteraction.instances.set(type, new ERC20TokenInteraction(Abi.DFG, ERC20TokenInfo.DFG.address, _provider)); + } + } + return ERC20TokenInteraction.instances.get(type)!; + } + + setSenderAddress(address: string) { + this.sender = address; + } + + private getBigNumberValueByDecimals(value: BigNumberish): TokenAmount { + return formatTokenAmount(formatUnits(value, this.decimals)); + } + + async getTokenBalance(): Promise { + try { + const response: BigNumber[] = await this.provider.read({ + abi: this.abi, + method: "balanceOf", + args: [this.sender], + to: this.address, + from: this.sender, + }); + + if (response?.length && BigNumber.isBigNumber(response[0])) { + return this.getBigNumberValueByDecimals(response[0].toBigInt()); + } + } catch (error) { + console.error(error); + return; + } + } + + async getAllowance(spenderAddress: string): Promise { + try { + const response: [BigNumber] = await this.provider.read({ + abi: this.abi, + method: "allowance", + args: [this.sender, spenderAddress], + to: this.address, + from: this.sender, + }); + + if (response?.length && BigNumber.isBigNumber(response[0])) { + return this.getBigNumberValueByDecimals(response[0].toBigInt()); + } + } catch (error) { + console.error("error when trying to get allowance: ", error); + return; + } + } + + async approveTokenAmount(spenderAddress: string, amount: number) { + try { + const amountWithDecimals = getAmountWithDecimals(amount, this.decimals).toString(); + const txHash = await this.provider.write({ + abi: this.abi, + method: "approve", + args: [spenderAddress, amountWithDecimals], + to: this.address, + from: this.sender, + }); + if (txHash) return txHash; + console.log("approve tx_hash = ", txHash); + } catch (error) { + console.error(error); + return; + } + } +} diff --git a/services/index.ts b/services/index.ts new file mode 100644 index 0000000..8087c29 --- /dev/null +++ b/services/index.ts @@ -0,0 +1,2 @@ +export * from "./contracts/Token"; +export * from "./contracts/Pool"; \ No newline at end of file diff --git a/store/flexibleStaking.ts b/store/flexibleStaking.ts new file mode 100644 index 0000000..65da770 --- /dev/null +++ b/store/flexibleStaking.ts @@ -0,0 +1,47 @@ +import { ERC20TokenInteraction, StakingPool } from "@/services"; +import { create } from "zustand"; + +interface State { + poolContract: StakingPool | null; + icyContract: ERC20TokenInteraction | null; + apr: number; + balance: number; + balanceInUsd: number; + allowance: number; + stakedAmount: number; + poolStakedAmount: number; + earnedRewards: number; + tokenPrice: number; + stakeDate: string; + startTime: string; + finishTime: string; + autoStaking: boolean; +} + +interface Action { + reset: () => void; + setValues: (values: Partial) => void; +} + +const initialState: State = { + poolContract: null, + icyContract: null, + apr: 0, + balance: 0, + balanceInUsd: 0, + allowance: 0, + stakedAmount: 0, + poolStakedAmount: 0, + earnedRewards: 0, + tokenPrice: 1.5, + stakeDate: "", + startTime: "", + finishTime: "", + autoStaking: true, +}; + +export const useFlexibleStaking = create((set) => ({ + ...initialState, + reset: () => set(initialState), + setValues: (values) => set((state) => ({ ...state, ...values })), +})); diff --git a/utils/datetime.ts b/utils/datetime.ts new file mode 100644 index 0000000..0ae2cb5 --- /dev/null +++ b/utils/datetime.ts @@ -0,0 +1,15 @@ +export function formatUnixTimestampToDateTime(unixTimestamp: number): string { + const date = new Date(unixTimestamp * 1000); + + // Format the date-time string in the desired format + const options: Intl.DateTimeFormatOptions = { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + }; + + const formatter = new Intl.DateTimeFormat('vi-VN', options); + return formatter.format(date); +} \ No newline at end of file diff --git a/utils/number.ts b/utils/number.ts index 5ac6a6f..12a2357 100644 --- a/utils/number.ts +++ b/utils/number.ts @@ -1,4 +1,5 @@ import { utils } from "@consolelabs/mochi-formatter"; +import BigNumber from "bignumber.js"; export type TokenAmount = { value: number; @@ -18,3 +19,7 @@ export function formatTokenAmount(amount: string | number): TokenAmount { display: roundedAmount, }; } + +export function getAmountWithDecimals(amount: number, decimals: number): BigNumber { + return (new BigNumber(10)).pow(decimals).multipliedBy(amount); +} \ No newline at end of file diff --git a/utils/retry.ts b/utils/retry.ts new file mode 100644 index 0000000..81fed9a --- /dev/null +++ b/utils/retry.ts @@ -0,0 +1,23 @@ +const wait = (ms: number) => + new Promise((r) => { + setTimeout(r, ms); + }); + +export const retry = ( + operation: () => Promise, + delay: number, + retries: number +): Promise => + new Promise((resolve, reject) => { + operation() + .then(resolve) + .catch((reason) => { + if (retries > 0) { + return wait(delay) + .then(() => retry(operation, delay, retries - 1)) + .then(resolve) + .catch(reject); + } + return reject(reason); + }); + });