diff --git a/apps/defi/src/lang/en.json b/apps/defi/src/lang/en.json index d3709905e..0a340dede 100644 --- a/apps/defi/src/lang/en.json +++ b/apps/defi/src/lang/en.json @@ -247,12 +247,6 @@ "value": "Return to Home Page" } ], - "2/2yg+": [ - { - "type": 0, - "value": "Add" - } - ], "20KPDF": [ { "type": 0, @@ -871,12 +865,6 @@ "value": "Bridging activity" } ], - "BLAy25": [ - { - "type": 0, - "value": "Extend" - } - ], "BNFT1w": [ { "type": 0, @@ -1083,6 +1071,12 @@ "value": "0 months" } ], + "FIG7wh": [ + { + "type": 0, + "value": "Any unclaimed rewards will transferred to your wallet immediately when you extend your stake." + } + ], "FVYhKc": [ { "type": 0, @@ -1359,12 +1353,6 @@ "value": "symbol" } ], - "Kzhxqf": [ - { - "type": 0, - "value": "Select the additional OGN you would like to add to your lockup" - } - ], "L2dDDV": [ { "type": 0, @@ -2145,12 +2133,6 @@ "value": "Learn about governance" } ], - "YnTVWE": [ - { - "type": 0, - "value": "Adding to lockup" - } - ], "YnftYz": [ { "type": 0, @@ -2657,6 +2639,12 @@ "value": "Gas" } ], + "hsG1EM": [ + { + "type": 0, + "value": "Extend/Add" + } + ], "hsqHqR": [ { "type": 0, @@ -2687,20 +2675,6 @@ "value": "Error while estimating" } ], - "iRGhsn": [ - { - "type": 0, - "value": "Add unclaimed rewards (" - }, - { - "type": 1, - "value": "rewards" - }, - { - "type": 0, - "value": " OGN)" - } - ], "iaS0YY": [ { "type": 0, @@ -2779,12 +2753,6 @@ "value": "Your voting power" } ], - "kDaImL": [ - { - "type": 0, - "value": "Add to lockup" - } - ], "kH1R79": [ { "type": 0, @@ -2891,6 +2859,12 @@ "value": "." } ], + "mDjDir": [ + { + "type": 0, + "value": "Manage Stake" + } + ], "mTz9QI": [ { "type": 0, @@ -3403,12 +3377,6 @@ "value": "Bridge your ETH in a single transaction and use wOETH across Arbitrum to earn ARB tokens." } ], - "xiGfmr": [ - { - "type": 0, - "value": "Extend stake" - } - ], "xkKA0U": [ { "type": 0, @@ -3605,4 +3573,4 @@ "value": "Waiting for signature" } ] -} \ No newline at end of file +} diff --git a/apps/defi/src/lang/extracts/en.json b/apps/defi/src/lang/extracts/en.json index 916f18dc6..bad3efe2d 100644 --- a/apps/defi/src/lang/extracts/en.json +++ b/apps/defi/src/lang/extracts/en.json @@ -107,9 +107,6 @@ "1nxJrQ": { "defaultMessage": "Return to Home Page" }, - "2/2yg+": { - "defaultMessage": "Add" - }, "20KPDF": { "defaultMessage": "Registered Voters" }, @@ -350,9 +347,6 @@ "BB94QK": { "defaultMessage": "Bridging activity" }, - "BLAy25": { - "defaultMessage": "Extend" - }, "BNFT1w": { "defaultMessage": "Global stats" }, @@ -440,6 +434,9 @@ "F6lS59": { "defaultMessage": "0 months" }, + "FIG7wh": { + "defaultMessage": "Any unclaimed rewards will transferred to your wallet immediately when you extend your stake." + }, "FVYhKc": { "defaultMessage": "Balances" }, @@ -560,9 +557,6 @@ "Kw1jw2": { "defaultMessage": "Get {symbol}" }, - "Kzhxqf": { - "defaultMessage": "Select the additional OGN you would like to add to your lockup" - }, "L2dDDV": { "defaultMessage": "The percentage of total Origin DeFi DAO voting power represented by this lockup." }, @@ -845,9 +839,6 @@ "Yl2sc/": { "defaultMessage": "Learn about governance" }, - "YnTVWE": { - "defaultMessage": "Adding to lockup" - }, "YnftYz": { "defaultMessage": "Wait time:" }, @@ -1046,6 +1037,9 @@ "hjWYtP": { "defaultMessage": "Gas" }, + "hsG1EM": { + "defaultMessage": "Extend/Add" + }, "hsqHqR": { "defaultMessage": "Time remaining" }, @@ -1061,9 +1055,6 @@ "iLgjES": { "defaultMessage": "Error while estimating" }, - "iRGhsn": { - "defaultMessage": "Add unclaimed rewards ({rewards} OGN)" - }, "iaS0YY": { "defaultMessage": "I have read and agree to the above terms" }, @@ -1103,9 +1094,6 @@ "kBjaSb": { "defaultMessage": "Your voting power" }, - "kDaImL": { - "defaultMessage": "Add to lockup" - }, "kH1R79": { "defaultMessage": "Stake OGN" }, @@ -1145,6 +1133,9 @@ "m9XypM": { "defaultMessage": "Sending {amount} {symbolIn} to {chainOut}." }, + "mDjDir": { + "defaultMessage": "Manage Stake" + }, "mTz9QI": { "defaultMessage": "Casted vote" }, @@ -1367,9 +1358,6 @@ "xc6pWX": { "defaultMessage": "Bridge your ETH in a single transaction and use wOETH across Arbitrum to earn ARB tokens." }, - "xiGfmr": { - "defaultMessage": "Extend stake" - }, "xkKA0U": { "defaultMessage": "Delegate on-chain voting power to:" }, diff --git a/apps/oeth/src/lang/en.json b/apps/oeth/src/lang/en.json index 0bdb6568c..47686aab2 100644 --- a/apps/oeth/src/lang/en.json +++ b/apps/oeth/src/lang/en.json @@ -139,12 +139,6 @@ "value": " slippage:" } ], - "4D9M13": [ - { - "type": 0, - "value": "Swap with CurvePool" - } - ], "4LOwM/": [ { "type": 0, @@ -367,12 +361,6 @@ "value": "Theme:" } ], - "C73Nbd": [ - { - "type": 0, - "value": "Swap with Curve" - } - ], "CcqmCh": [ { "type": 0, @@ -869,6 +857,12 @@ "value": "Est. bridge fee" } ], + "eerX6M": [ + { + "type": 0, + "value": "Swap via CurvePool" + } + ], "fBhctx": [ { "type": 0, diff --git a/apps/oeth/src/lang/extracts/en.json b/apps/oeth/src/lang/extracts/en.json index 1d71832a5..8a727c520 100644 --- a/apps/oeth/src/lang/extracts/en.json +++ b/apps/oeth/src/lang/extracts/en.json @@ -59,9 +59,6 @@ "45EvZ3": { "defaultMessage": "Minimum received with {slippage} slippage:" }, - "4D9M13": { - "defaultMessage": "Swap with CurvePool" - }, "4LOwM/": { "defaultMessage": "OETH Transactions" }, @@ -149,9 +146,6 @@ "BuhEl+": { "defaultMessage": "Theme:" }, - "C73Nbd": { - "defaultMessage": "Swap with Curve" - }, "CcqmCh": { "defaultMessage": "OETH Balance" }, @@ -365,6 +359,9 @@ "eJNRiB": { "defaultMessage": "Est. bridge fee" }, + "eerX6M": { + "defaultMessage": "Swap via CurvePool" + }, "fBhctx": { "defaultMessage": "Download CSV" }, diff --git a/apps/ousd/src/lang/en.json b/apps/ousd/src/lang/en.json index 538d49fd7..3b19244fc 100644 --- a/apps/ousd/src/lang/en.json +++ b/apps/ousd/src/lang/en.json @@ -29,12 +29,6 @@ "value": "Opt in" } ], - "00AJDy": [ - { - "type": 0, - "value": "Origin Vault" - } - ], "024MfN": [ { "type": 0, @@ -219,12 +213,6 @@ "value": "lastPage" } ], - "BONcjO": [ - { - "type": 0, - "value": "Uniswap V3" - } - ], "BY343C": [ { "type": 0, @@ -311,6 +299,12 @@ "value": "Apply" } ], + "GdHGdR": [ + { + "type": 0, + "value": "Swap via Uniswap V2" + } + ], "H5+NAX": [ { "type": 0, @@ -341,12 +335,6 @@ "value": "Gas:" } ], - "KE0cl6": [ - { - "type": 0, - "value": "Curve" - } - ], "KN7zKn": [ { "type": 0, @@ -395,12 +383,6 @@ "value": "Bridge" } ], - "NNBdoh": [ - { - "type": 0, - "value": "Uniswap V2" - } - ], "NXFAgE": [ { "type": 0, @@ -431,16 +413,16 @@ "value": "No available route" } ], - "OhU4K0": [ + "OwO+Nr": [ { "type": 0, - "value": "SushiSwap" + "value": "Mint" } ], - "OwO+Nr": [ + "Ozw3aL": [ { "type": 0, - "value": "Mint" + "value": "Mint with Vault" } ], "PKgDoL": [ @@ -489,12 +471,6 @@ "value": "You affirm that you are not a subject of economic or trade sanctions administered or enforced by any governmental authority or otherwise designated on any list of prohibited or restricted parties, including the list maintained by the Office of Foreign Assets Control of the U.S. Department of the Treasury." } ], - "TrXNPz": [ - { - "type": 0, - "value": "Flipper" - } - ], "TwyMau": [ { "type": 0, @@ -569,6 +545,12 @@ "value": "Redeem" } ], + "Y4FfAH": [ + { + "type": 0, + "value": "Swap via Uniswap V3" + } + ], "ZmLuI/": [ { "type": 0, @@ -663,6 +645,12 @@ "value": "Lifetime Earnings (OUSD)" } ], + "f7AFus": [ + { + "type": 0, + "value": "Swap via SushiSwap" + } + ], "fBhctx": [ { "type": 0, @@ -737,6 +725,12 @@ "value": "30-Day Trailing APY" } ], + "lQmZjB": [ + { + "type": 0, + "value": "Swap via Curve" + } + ], "lb8m6m": [ { "type": 0, @@ -831,6 +825,12 @@ "value": "You confirm that you are not a resident of, citizen of, located in, incorporated in, or have a registered office in the United States or any country or region currently currently subject to sanctions by the United States." } ], + "tDcoCl": [ + { + "type": 0, + "value": "Swap via Flipper" + } + ], "tFriEi": [ { "type": 0, diff --git a/apps/ousd/src/lang/extracts/en.json b/apps/ousd/src/lang/extracts/en.json index d55fe3696..57625df7a 100644 --- a/apps/ousd/src/lang/extracts/en.json +++ b/apps/ousd/src/lang/extracts/en.json @@ -14,9 +14,6 @@ "/xvR4F": { "defaultMessage": "Opt in" }, - "00AJDy": { - "defaultMessage": "Origin Vault" - }, "024MfN": { "defaultMessage": "Processing Approval" }, @@ -95,9 +92,6 @@ "AdvVFR": { "defaultMessage": "{page} of {lastPage}" }, - "BONcjO": { - "defaultMessage": "Uniswap V3" - }, "BY343C": { "defaultMessage": "Change" }, @@ -125,6 +119,9 @@ "EWw/tK": { "defaultMessage": "Apply" }, + "GdHGdR": { + "defaultMessage": "Swap via Uniswap V2" + }, "H5+NAX": { "defaultMessage": "Balance" }, @@ -140,9 +137,6 @@ "JrepQU": { "defaultMessage": "Gas:" }, - "KE0cl6": { - "defaultMessage": "Curve" - }, "KN7zKn": { "defaultMessage": "Error" }, @@ -167,9 +161,6 @@ "MZCNjq": { "defaultMessage": "Bridge" }, - "NNBdoh": { - "defaultMessage": "Uniswap V2" - }, "NXFAgE": { "defaultMessage": "Ooops, something went wrong 😓" }, @@ -185,12 +176,12 @@ "OBfWz0": { "defaultMessage": "No available route" }, - "OhU4K0": { - "defaultMessage": "SushiSwap" - }, "OwO+Nr": { "defaultMessage": "Mint" }, + "Ozw3aL": { + "defaultMessage": "Mint with Vault" + }, "PKgDoL": { "defaultMessage": "Estimating..." }, @@ -212,9 +203,6 @@ "TrKxvg": { "defaultMessage": "You affirm that you are not a subject of economic or trade sanctions administered or enforced by any governmental authority or otherwise designated on any list of prohibited or restricted parties, including the list maintained by the Office of Foreign Assets Control of the U.S. Department of the Treasury." }, - "TrXNPz": { - "defaultMessage": "Flipper" - }, "TwyMau": { "defaultMessage": "Account" }, @@ -242,6 +230,9 @@ "XSdWHA": { "defaultMessage": "Redeem" }, + "Y4FfAH": { + "defaultMessage": "Swap via Uniswap V3" + }, "ZmLuI/": { "defaultMessage": "Rate:" }, @@ -284,6 +275,9 @@ "f0gqO4": { "defaultMessage": "Lifetime Earnings (OUSD)" }, + "f7AFus": { + "defaultMessage": "Swap via SushiSwap" + }, "fBhctx": { "defaultMessage": "Download CSV" }, @@ -317,6 +311,9 @@ "kF1zxQ": { "defaultMessage": "30-Day Trailing APY" }, + "lQmZjB": { + "defaultMessage": "Swap via Curve" + }, "lb8m6m": { "defaultMessage": "Mins" }, @@ -359,6 +356,9 @@ "stTYBM": { "defaultMessage": "You confirm that you are not a resident of, citizen of, located in, incorporated in, or have a registered office in the United States or any country or region currently currently subject to sanctions by the United States." }, + "tDcoCl": { + "defaultMessage": "Swap via Flipper" + }, "tFriEi": { "defaultMessage": "Transaction error" }, diff --git a/libs/defi/governance/src/overview/components/CurrentResultsCard.tsx b/libs/defi/governance/src/overview/components/CurrentResultsCard.tsx index 59ed5f781..cb11f81b0 100644 --- a/libs/defi/governance/src/overview/components/CurrentResultsCard.tsx +++ b/libs/defi/governance/src/overview/components/CurrentResultsCard.tsx @@ -75,7 +75,7 @@ export const CurrentResultsCard = (props: CardProps) => { proposal?.choices?.findIndex( (c) => c!.toLowerCase() === choice.toLowerCase(), ) ?? -1; - const score = idx > -1 ? (proposal?.scores?.at(idx) ?? 0) : 0; + const score = idx > -1 ? (proposal?.scores?.at?.(idx) ?? 0) : 0; return ( diff --git a/libs/defi/governance/src/overview/hooks.ts b/libs/defi/governance/src/overview/hooks.ts index 4846d3a1f..ddc608ba8 100644 --- a/libs/defi/governance/src/overview/hooks.ts +++ b/libs/defi/governance/src/overview/hooks.ts @@ -151,7 +151,8 @@ export const useUserVotes = () => { continue; } const idx = - Number(Array.isArray(v?.choice) ? v.choice.at(0) : v?.choice) - 1; + Number(Array.isArray(v?.choice) ? v.choice?.at?.(0) : v?.choice) - + 1; const choice = v?.proposal?.choices?.at?.(idx) ?? ''; offChainVotes.push({ diff --git a/libs/defi/oeth/src/redeem/actions.ts b/libs/defi/oeth/src/redeem/actions.ts new file mode 100644 index 000000000..1776447ce --- /dev/null +++ b/libs/defi/oeth/src/redeem/actions.ts @@ -0,0 +1,19 @@ +import { redeemArmOeth, redeemVaultOeth } from '@origin/shared/routes'; +import { defineMessage } from 'react-intl'; + +import type { SwapApi } from '@origin/shared/providers'; + +import type { OethRedeemAction } from './types'; + +export const redeemActions: Record = { + 'redeem-arm-oeth': { + ...redeemArmOeth, + routeLabel: defineMessage({ defaultMessage: 'Redeem via the ARM' }), + buttonLabel: defineMessage({ defaultMessage: 'Redeem' }), + }, + 'redeem-vault-async-oeth': { + ...redeemVaultOeth, + routeLabel: defineMessage({ defaultMessage: 'Redeem via OETH Vault' }), + buttonLabel: defineMessage({ defaultMessage: 'Request withdrawal' }), + }, +}; diff --git a/libs/defi/oeth/src/redeem/actions/index.ts b/libs/defi/oeth/src/redeem/actions/index.ts deleted file mode 100644 index cee1db918..000000000 --- a/libs/defi/oeth/src/redeem/actions/index.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { defineMessage } from 'react-intl'; - -import arm from './arm'; -import redeemVault from './redeemVault'; -import redeemVaultAsync from './redeemVaultAsync'; -import swapCurve from './swapCurve'; - -import type { SwapApi } from '@origin/shared/providers'; - -import type { OethRedeemAction } from '../types'; - -const defaultApi: SwapApi = { - isRouteAvailable: async () => true, - estimateAmount: async (config, { amountIn }) => { - console.log('Amount estimation not implemented'); - - return amountIn; - }, - estimateGas: async () => { - console.log('Gas estimation not implemented'); - - return 0n; - }, - estimateRoute: async (config, { amountIn, route }) => { - console.log('Route estimation not implemented'); - - return { - ...route, - estimatedAmount: amountIn, - allowanceAmount: 0n, - approvalGas: 0n, - gas: 0n, - rate: 0, - }; - }, - allowance: async () => { - console.log('Allowance not implemented'); - - return 0n; - }, - estimateApprovalGas: async () => { - console.log('Gas approval estimation not implemented'); - - return 0n; - }, - approve: async () => { - console.log('Approve operation not implemented'); - return null; - }, - swap: async () => { - console.log('Route swap operation not implemented'); - return null; - }, - buttonLabel: defineMessage({ defaultMessage: 'Redeem' }), - routeLabel: defineMessage({ defaultMessage: 'Redeem' }), -}; - -export const redeemActions: Record = { - 'swap-curve': { - ...defaultApi, - ...swapCurve, - routeLabel: defineMessage({ defaultMessage: 'Swap via Curve' }), - buttonLabel: defineMessage({ defaultMessage: 'Redeem' }), - }, - 'redeem-vault': { - ...defaultApi, - ...redeemVault, - routeLabel: defineMessage({ defaultMessage: 'Redeem via OETH Vault' }), - buttonLabel: defineMessage({ defaultMessage: 'Redeem' }), - }, - arm: { - ...defaultApi, - ...arm, - routeLabel: defineMessage({ defaultMessage: 'Redeem via the ARM' }), - buttonLabel: defineMessage({ defaultMessage: 'Redeem' }), - }, - 'redeem-vault-async': { - ...defaultApi, - ...redeemVaultAsync, - routeLabel: defineMessage({ defaultMessage: 'Redeem via OETH Vault' }), - buttonLabel: defineMessage({ defaultMessage: 'Request withdrawal' }), - }, -}; diff --git a/libs/defi/oeth/src/redeem/components/Swapper.tsx b/libs/defi/oeth/src/redeem/components/Swapper.tsx index 38e45aa3a..c24f1ecf0 100644 --- a/libs/defi/oeth/src/redeem/components/Swapper.tsx +++ b/libs/defi/oeth/src/redeem/components/Swapper.tsx @@ -377,8 +377,11 @@ function SwapperWrapped({ {intl.formatMessage({ defaultMessage: 'Duration' })} - - + + {intl.formatMessage({ defaultMessage: 'Receive amount' })} diff --git a/libs/defi/oeth/src/redeem/constants.ts b/libs/defi/oeth/src/redeem/constants.ts index 408a9fcc1..591aeb85f 100644 --- a/libs/defi/oeth/src/redeem/constants.ts +++ b/libs/defi/oeth/src/redeem/constants.ts @@ -8,23 +8,11 @@ import type { Meta, OethRedeemAction } from './types'; export const WITHDRAW_DELAY = 10; // minutes -export const GAS_BUFFER = 10n; // 10% - export const redeemRoutes: SwapRoute[] = [ - // { - // tokenIn: tokens.mainnet.OETH, - // tokenOut: tokens.mainnet.WETH, - // action: 'swap-curve', - // }, - // { - // tokenIn: tokens.mainnet.OETH, - // tokenOut: tokens.mainnet.WETH, - // action: 'redeem-vault', - // }, { tokenIn: tokens.mainnet.OETH, tokenOut: tokens.mainnet.WETH, - action: 'arm', + action: 'redeem-arm-oeth', meta: { icon: ARM, waitTime: defineMessage({ defaultMessage: '~1 min' }), @@ -34,7 +22,7 @@ export const redeemRoutes: SwapRoute[] = [ { tokenIn: tokens.mainnet.OETH, tokenOut: tokens.mainnet.WETH, - action: 'redeem-vault-async', + action: 'redeem-vault-async-oeth', meta: { icon: OETH, waitTime: defineMessage({ diff --git a/libs/defi/oeth/src/redeem/types.ts b/libs/defi/oeth/src/redeem/types.ts index ee1dca6db..64869ce69 100644 --- a/libs/defi/oeth/src/redeem/types.ts +++ b/libs/defi/oeth/src/redeem/types.ts @@ -1,4 +1,5 @@ import type { SvgIconProps } from '@mui/material'; +import type { OethRoute } from '@origin/shared/routes'; import type { ComponentType } from 'react'; import type { MessageDescriptor } from 'react-intl'; @@ -9,11 +10,10 @@ export type Meta = { comingSoon?: boolean; }; -export type OethRedeemAction = - | 'arm' - | 'redeem-vault' - | 'redeem-vault-async' - | 'swap-curve'; +export type OethRedeemAction = Extract< + OethRoute, + 'redeem-arm-oeth' | 'redeem-vault-async-oeth' +>; export type WithdrawalRequest = { id: string; diff --git a/libs/defi/oeth/src/swap/actions.ts b/libs/defi/oeth/src/swap/actions.ts new file mode 100644 index 000000000..e1e5a4634 --- /dev/null +++ b/libs/defi/oeth/src/swap/actions.ts @@ -0,0 +1,58 @@ +import { + mintVaultOeth, + SwapCurveOeth, + swapCurveOethEth, + swapCurveOethSfrxeth, + swapZapperOethEth, + swapZapperOethSfrxeth, + unwrapOethWoeth, + wrapOethWoeth, +} from '@origin/shared/routes'; +import { defineMessage } from 'react-intl'; + +import type { SwapApi } from '@origin/shared/providers'; + +import type { OethSwapAction } from './types'; + +export const oethSwapActions: Record = { + 'swap-curve-oeth': { + ...SwapCurveOeth, + routeLabel: defineMessage({ defaultMessage: 'Swap via Curve' }), + buttonLabel: defineMessage({ defaultMessage: 'Swap' }), + }, + 'swap-curve-oeth-eth': { + ...swapCurveOethEth, + routeLabel: defineMessage({ defaultMessage: 'Swap via CurvePool' }), + buttonLabel: defineMessage({ defaultMessage: 'Swap' }), + }, + 'swap-curve-oeth-sfrxeth': { + ...swapCurveOethSfrxeth, + routeLabel: defineMessage({ defaultMessage: 'Swap via Curve' }), + buttonLabel: defineMessage({ defaultMessage: 'Swap' }), + }, + 'swap-zapper-oeth-eth': { + ...swapZapperOethEth, + routeLabel: defineMessage({ defaultMessage: 'Mint with Vault' }), + buttonLabel: defineMessage({ defaultMessage: 'Mint' }), + }, + 'swap-zapper-oeth-sfrxeth': { + ...swapZapperOethSfrxeth, + routeLabel: defineMessage({ defaultMessage: 'Mint with Vault' }), + buttonLabel: defineMessage({ defaultMessage: 'Mint' }), + }, + 'mint-vault-oeth': { + ...mintVaultOeth, + routeLabel: defineMessage({ defaultMessage: 'Mint with Vault' }), + buttonLabel: defineMessage({ defaultMessage: 'Mint' }), + }, + 'wrap-oeth-oeth': { + ...wrapOethWoeth, + routeLabel: defineMessage({ defaultMessage: 'Wrap with Origin' }), + buttonLabel: defineMessage({ defaultMessage: 'Wrap' }), + }, + 'unwrap-oeth-woeth': { + ...unwrapOethWoeth, + routeLabel: defineMessage({ defaultMessage: 'Unwrap with Origin' }), + buttonLabel: defineMessage({ defaultMessage: 'Unwrap' }), + }, +}; diff --git a/libs/defi/oeth/src/swap/actions/index.ts b/libs/defi/oeth/src/swap/actions/index.ts deleted file mode 100644 index f4053e6ac..000000000 --- a/libs/defi/oeth/src/swap/actions/index.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { defineMessage } from 'react-intl'; - -import mintVault from './mintVault'; -import swapCurve from './swapCurve'; -import swapCurveEth from './swapCurveEth'; -import swapCurveSfrxeth from './swapCurveSfrxeth'; -import swapZapperEth from './swapZapperEth'; -import swapZapperSfrxeth from './swapZapperSfrxeth'; -import unwrapWOETH from './unwrapWOETH'; -import wrapOETH from './wrapOETH'; - -import type { SwapApi } from '@origin/shared/providers'; - -import type { OethSwapAction } from '../types'; - -const defaultApi: SwapApi = { - isRouteAvailable: async () => true, - estimateAmount: async (config, { amountIn }) => { - console.log('Amount estimation not implemented'); - - return amountIn; - }, - estimateGas: async () => { - console.log('Gas estimation not implemented'); - - return 0n; - }, - estimateRoute: async (config, { amountIn, route }) => { - console.log('Route estimation not implemented'); - - return { - ...route, - estimatedAmount: amountIn, - allowanceAmount: 0n, - approvalGas: 0n, - gas: 0n, - rate: 0, - }; - }, - allowance: async () => { - console.log('Allowance not implemented'); - - return 0n; - }, - estimateApprovalGas: async () => { - console.log('Gas approval estimation not implemented'); - - return 0n; - }, - approve: async () => { - console.log('Approve operation not implemented'); - return null; - }, - swap: async () => { - console.log('Route swap operation not implemented'); - return null; - }, - buttonLabel: defineMessage({ defaultMessage: 'Swap' }), - routeLabel: defineMessage({ defaultMessage: 'Swap' }), -}; - -export const oethSwapActions: Record = { - 'swap-curve': { - ...defaultApi, - ...swapCurve, - routeLabel: defineMessage({ defaultMessage: 'Swap via Curve' }), - buttonLabel: defineMessage({ defaultMessage: 'Swap' }), - }, - 'swap-curve-eth': { - ...defaultApi, - ...swapCurveEth, - routeLabel: defineMessage({ defaultMessage: 'Swap via CurvePool' }), - buttonLabel: defineMessage({ defaultMessage: 'Swap' }), - }, - 'swap-curve-sfrxeth': { - ...defaultApi, - ...swapCurveSfrxeth, - routeLabel: defineMessage({ defaultMessage: 'Swap via Curve' }), - buttonLabel: defineMessage({ defaultMessage: 'Swap' }), - }, - 'swap-zapper-eth': { - ...defaultApi, - ...swapZapperEth, - routeLabel: defineMessage({ defaultMessage: 'Mint with Vault' }), - buttonLabel: defineMessage({ defaultMessage: 'Mint' }), - }, - 'swap-zapper-sfrxeth': { - ...defaultApi, - ...swapZapperSfrxeth, - routeLabel: defineMessage({ defaultMessage: 'Mint with Vault' }), - buttonLabel: defineMessage({ defaultMessage: 'Mint' }), - }, - 'mint-vault': { - ...defaultApi, - ...mintVault, - routeLabel: defineMessage({ defaultMessage: 'Mint with Vault' }), - buttonLabel: defineMessage({ defaultMessage: 'Mint' }), - }, - 'wrap-oeth': { - ...defaultApi, - ...wrapOETH, - routeLabel: defineMessage({ defaultMessage: 'Wrap with Origin' }), - buttonLabel: defineMessage({ defaultMessage: 'Wrap' }), - }, - 'unwrap-woeth': { - ...defaultApi, - ...unwrapWOETH, - routeLabel: defineMessage({ defaultMessage: 'Unwrap with Origin' }), - buttonLabel: defineMessage({ defaultMessage: 'Unwrap' }), - }, -}; diff --git a/libs/defi/oeth/src/swap/actions/swapCurve/curveRoutes.ts b/libs/defi/oeth/src/swap/actions/swapCurve/curveRoutes.ts deleted file mode 100644 index a22eb6515..000000000 --- a/libs/defi/oeth/src/swap/actions/swapCurve/curveRoutes.ts +++ /dev/null @@ -1,265 +0,0 @@ -import { contracts, tokens } from '@origin/shared/contracts'; -import { ETH_ADDRESS_CURVE } from '@origin/shared/utils'; - -/* -- Mainnet Curve registry contract: https://etherscan.io/address/0x99a58482BD75cbab83b27EC03CA68fF489b5788f#code -- Vyper implementation for multiple amount exchanges - -``` - def get_exchange_multiple_amount - - @notice Get the current number the final output tokens received in an exchange - @dev Routing and swap params must be determined off-chain. This - functionality is designed for gas efficiency over ease-of-use. - @param _route Array of [initial token, pool, token, pool, token, ...] - The array is iterated until a pool address of 0x00, then the last - given token is transferred to `_receiver` - @param _swap_params Multidimensional array of [i, j, swap type] where i and j are the correct - values for the n'th pool in `_route`. The swap type should be - 1 for a stableswap `exchange`, - 2 for stableswap `exchange_underlying`, - 3 for a cryptoswap `exchange`, - 4 for a cryptoswap `exchange_underlying`, - 5 for factory metapools with lending base pool `exchange_underlying`, - 6 for factory crypto-meta pools underlying exchange (`exchange` method in zap), - 7-11 for wrapped coin (underlying for lending pool) -> LP token "exchange" (actually `add_liquidity`), - 12-14 for LP token -> wrapped coin (underlying for lending or fake pool) "exchange" (actually `remove_liquidity_one_coin`) - 15 for WETH -> ETH "exchange" (actually deposit/withdraw) - @param _amount The amount of `_route[0]` token to be sent. - @param _pools Array of pools for swaps via zap contracts. This parameter is only needed for - Polygon meta-factories underlying swaps. - @return Expected amount of the final output token -``` -*/ - -export const curveRoutes = { - // ETH -> OETH Mint - ETH: { - OETH: { - routes: [ - ETH_ADDRESS_CURVE, - contracts.mainnet.OETHCurvePool.address, - tokens.mainnet.OETH.address, - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - ], - swapParams: [ - [0n, 1n, 1n], - [0n, 0n, 0n], - [0n, 0n, 0n], - [0n, 0n, 0n], - ], - }, - }, - // stETH -> OETH Mint - stETH: { - OETH: { - routes: [ - tokens.mainnet.stETH.address, - '0x21E27a5E5513D6e65C4f830167390997aA84843a', - ETH_ADDRESS_CURVE, - contracts.mainnet.OETHCurvePool.address, - tokens.mainnet.OETH.address, - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - ], - swapParams: [ - // 1 for a stableswap `exchange`, - [1n, 0n, 1n], - // 1 for a stableswap `exchange`, - [0n, 1n, 1n], - [0n, 0n, 0n], - [0n, 0n, 0n], - ], - }, - }, - // WETH -> OETH Mint - WETH: { - OETH: { - routes: [ - tokens.mainnet.WETH.address, - tokens.mainnet.WETH.address, - ETH_ADDRESS_CURVE, - contracts.mainnet.OETHCurvePool.address, - tokens.mainnet.OETH.address, - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - ], - swapParams: [ - // 15 for WETH -> ETH "exchange" (actually deposit/withdraw) - [0n, 0n, 15n], - // 1 for a stableswap `exchange`, - [0n, 1n, 1n], - [0n, 0n, 0n], - [0n, 0n, 0n], - ], - }, - }, - // rETH -> OETH Mint - rETH: { - OETH: { - routes: [ - tokens.mainnet.rETH.address, - '0x0f3159811670c117c372428D4E69AC32325e4D0F', - ETH_ADDRESS_CURVE, - contracts.mainnet.OETHCurvePool.address, - tokens.mainnet.OETH.address, - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - ], - swapParams: [ - // 3 for a cryptoswap `exchange`, - [1n, 0n, 3n], - // 1 for a stableswap `exchange`, - [0n, 1n, 1n], - [0n, 0n, 0n], - [0n, 0n, 0n], - ], - }, - }, - // frxETH -> OETH Mint - frxETH: { - OETH: { - routes: [ - tokens.mainnet.frxETH.address, - '0xa1F8A6807c402E4A15ef4EBa36528A3FED24E577', - ETH_ADDRESS_CURVE, - contracts.mainnet.OETHCurvePool.address, - tokens.mainnet.OETH.address, - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - ], - swapParams: [ - // 1 for a stableswap `exchange`, - [1n, 0n, 1n], - // 1 for a stableswap `exchange`, - [0n, 1n, 1n], - [0n, 0n, 0n], - [0n, 0n, 0n], - ], - }, - }, - // OETH Redeem - OETH: { - // OETH -> ETH - ETH: { - routes: [ - tokens.mainnet.OETH.address, - contracts.mainnet.OETHCurvePool.address, - ETH_ADDRESS_CURVE, - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - ], - swapParams: [ - [1n, 0n, 1n], - [0n, 0n, 0n], - [0n, 0n, 0n], - [0n, 0n, 0n], - ], - }, - // OETH -> frxETH - frxETH: { - routes: [ - tokens.mainnet.OETH.address, - contracts.mainnet.OETHCurvePool.address, - ETH_ADDRESS_CURVE, - '0xa1F8A6807c402E4A15ef4EBa36528A3FED24E577', - tokens.mainnet.frxETH.address, - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - ], - swapParams: [ - // 1 for a stableswap `exchange`, - [1n, 0n, 1n], - // 1 for a stableswap `exchange`, - [0n, 1n, 1n], - [0n, 0n, 0n], - [0n, 0n, 0n], - ], - }, - // OETH -> WETH - WETH: { - routes: [ - tokens.mainnet.OETH.address, - contracts.mainnet.OETHCurvePool.address, - ETH_ADDRESS_CURVE, - tokens.mainnet.WETH.address, - tokens.mainnet.WETH.address, - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - ], - swapParams: [ - // 1 for a stableswap `exchange`, - [1n, 0n, 1n], - // 15 for WETH -> ETH "exchange" (actually deposit/withdraw) - [0n, 0n, 15n], - [0n, 0n, 0n], - [0n, 0n, 0n], - ], - }, - // OETH -> rETH - rETH: { - routes: [ - tokens.mainnet.OETH.address, - contracts.mainnet.OETHCurvePool.address, - ETH_ADDRESS_CURVE, - '0x0f3159811670c117c372428D4E69AC32325e4D0F', - tokens.mainnet.rETH.address, - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - ], - swapParams: [ - // 1 for a stableswap `exchange`, - [1n, 0n, 1n], - // 3 for a cryptoswap `exchange`, - [0n, 1n, 3n], - [0n, 0n, 0n], - [0n, 0n, 0n], - ], - }, - // OETH -> stETH - stETH: { - routes: [ - tokens.mainnet.OETH.address, - contracts.mainnet.OETHCurvePool.address, - ETH_ADDRESS_CURVE, - '0x21E27a5E5513D6e65C4f830167390997aA84843a', - tokens.mainnet.stETH.address, - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - ], - swapParams: [ - // 1 for a stableswap `exchange`, - [1n, 0n, 1n], - // 1 for a stableswap `exchange`, - [0n, 1n, 1n], - [0n, 0n, 0n], - [0n, 0n, 0n], - ], - }, - }, -} as const; diff --git a/libs/defi/oeth/src/swap/actions/swapCurve/index.ts b/libs/defi/oeth/src/swap/actions/swapCurve/index.ts deleted file mode 100644 index e372a148d..000000000 --- a/libs/defi/oeth/src/swap/actions/swapCurve/index.ts +++ /dev/null @@ -1,321 +0,0 @@ -import { queryClient } from '@origin/defi/shared'; -import { - isNativeCurrency, - simulateContractWithTxTracker, - useCurve, -} from '@origin/shared/providers'; -import { - ETH_ADDRESS_CURVE, - isNilOrEmpty, - subPercentage, - ZERO_ADDRESS, -} from '@origin/shared/utils'; -import { - getAccount, - getPublicClient, - readContract, - simulateContract, - writeContract, -} from '@wagmi/core'; -import { path } from 'ramda'; -import { erc20Abi, formatUnits, maxUint256 } from 'viem'; - -import { GAS_BUFFER } from '../../constants'; -import { curveRoutes } from './curveRoutes'; - -import type { - Allowance, - Approve, - EstimateAmount, - EstimateApprovalGas, - EstimateGas, - EstimateRoute, - Swap, -} from '@origin/shared/providers'; - -const estimateAmount: EstimateAmount = async ( - config, - { tokenIn, tokenOut, amountIn }, -) => { - const curve = await queryClient.fetchQuery({ - queryKey: useCurve.getKey(), - queryFn: useCurve.fetcher(config), - staleTime: Infinity, - }); - if (amountIn === 0n || isNilOrEmpty(curve?.CurveRegistryExchange)) { - return 0n; - } - - const curveConfig = path<{ routes: unknown[]; swapParams: unknown[] }>( - [tokenIn.symbol, tokenOut.symbol], - curveRoutes, - ); - - if (!curveConfig) { - throw new Error( - `No curve route found, verify exchange mapping ${tokenIn.symbol} -> ${tokenOut.symbol}`, - ); - } - - const amountOut = await readContract(config, { - address: curve.CurveRegistryExchange.address, - abi: curve.CurveRegistryExchange.abi, - functionName: 'get_exchange_multiple_amount', - args: [curveConfig.routes, curveConfig.swapParams, amountIn], - }); - - return amountOut as unknown as bigint; -}; - -const estimateGas: EstimateGas = async ( - config, - { tokenIn, tokenOut, amountIn, amountOut, slippage }, -) => { - let gasEstimate = 0n; - const publicClient = getPublicClient(config); - - if (amountIn === 0n || !publicClient) { - return gasEstimate; - } - - const { address } = getAccount(config); - - const minAmountOut = subPercentage( - [amountOut ?? 0n, tokenOut.decimals], - slippage, - ); - - const curveConfig = path<{ routes: unknown[]; swapParams: unknown[] }>( - [tokenIn.symbol, tokenOut.symbol], - curveRoutes, - ); - - if (!curveConfig) { - throw new Error( - `No curve route found, verify exchange mapping ${tokenIn.symbol} -> ${tokenOut.symbol}`, - ); - } - - const curve = await queryClient.fetchQuery({ - queryKey: useCurve.getKey(), - queryFn: useCurve.fetcher(config), - staleTime: Infinity, - }); - - const isTokenInNative = isNativeCurrency(tokenIn); - - try { - gasEstimate = await publicClient.estimateContractGas({ - address: curve.CurveRegistryExchange.address, - abi: curve.CurveRegistryExchange.abi, - functionName: 'exchange_multiple', - args: [ - curveConfig.routes, - curveConfig.swapParams, - amountIn, - minAmountOut[0], - ], - account: address ?? ETH_ADDRESS_CURVE, - ...(isTokenInNative && { value: amountIn }), - }); - } catch (e) { - gasEstimate = 350000n; - } - - return gasEstimate; -}; - -const allowance: Allowance = async (config, { tokenIn }) => { - const { address } = getAccount(config); - const curve = await queryClient.fetchQuery({ - queryKey: useCurve.getKey(), - queryFn: useCurve.fetcher(config), - staleTime: Infinity, - }); - - if (!address || isNilOrEmpty(curve?.CurveRegistryExchange)) { - return 0n; - } - - if (!tokenIn?.address) { - return maxUint256; - } - - const allowance = await readContract(config, { - address: tokenIn.address, - abi: erc20Abi, - functionName: 'allowance', - args: [address, curve.CurveRegistryExchange.address], - }); - - return allowance; -}; - -const estimateApprovalGas: EstimateApprovalGas = async ( - config, - { tokenIn, amountIn }, -) => { - let approvalEstimate = 0n; - const { address } = getAccount(config); - const publicClient = getPublicClient(config); - - if (amountIn === 0n || !address || !publicClient) { - return approvalEstimate; - } - - const curve = await queryClient.fetchQuery({ - queryKey: useCurve.getKey(), - queryFn: useCurve.fetcher(config), - staleTime: Infinity, - }); - - try { - approvalEstimate = await publicClient.estimateContractGas({ - address: tokenIn.address ?? ZERO_ADDRESS, - abi: erc20Abi, - functionName: 'approve', - args: [curve.CurveRegistryExchange.address, amountIn], - account: address, - }); - } catch { - approvalEstimate = 200000n; - } - - return approvalEstimate; -}; - -const estimateRoute: EstimateRoute = async ( - config, - { tokenIn, tokenOut, amountIn, route, slippage }, -) => { - if (amountIn === 0n) { - return { - ...route, - estimatedAmount: 0n, - gas: 0n, - rate: 0, - allowanceAmount: 0n, - approvalGas: 0n, - }; - } - - const [estimatedAmount, allowanceAmount, approvalGas] = await Promise.all([ - estimateAmount(config, { tokenIn, tokenOut, amountIn }), - allowance(config, { tokenIn, tokenOut }), - estimateApprovalGas(config, { amountIn, tokenIn, tokenOut }), - ]); - const gas = await estimateGas(config, { - tokenIn, - tokenOut, - amountIn, - amountOut: estimatedAmount, - slippage, - }); - - return { - ...route, - estimatedAmount, - gas, - approvalGas, - allowanceAmount, - rate: - +formatUnits(estimatedAmount, tokenOut.decimals) / - +formatUnits(amountIn, tokenIn.decimals), - }; -}; - -const approve: Approve = async (config, { tokenIn, amountIn }) => { - if (!tokenIn?.address) { - return null; - } - const curve = await queryClient.fetchQuery({ - queryKey: useCurve.getKey(), - queryFn: useCurve.fetcher(config), - staleTime: Infinity, - }); - const { request } = await simulateContract(config, { - address: tokenIn.address, - abi: erc20Abi, - functionName: 'approve', - args: [curve.CurveRegistryExchange.address, amountIn], - }); - const hash = await writeContract(config, request); - - return hash; -}; - -const swap: Swap = async ( - config, - { tokenIn, tokenOut, amountIn, amountOut, slippage }, -) => { - const { address } = getAccount(config); - - if (amountIn === 0n || isNilOrEmpty(address)) { - return null; - } - - const curve = await queryClient.fetchQuery({ - queryKey: useCurve.getKey(), - queryFn: useCurve.fetcher(config), - staleTime: Infinity, - }); - const approved = await allowance(config, { tokenIn, tokenOut }); - - if (approved < amountIn) { - throw new Error(`Swap curve is not approved`); - } - - const minAmountOut = subPercentage( - [amountOut ?? 0n, tokenOut.decimals], - slippage, - ); - - const curveConfig = path<{ routes: unknown[]; swapParams: unknown[] }>( - [tokenIn.symbol, tokenOut?.symbol], - curveRoutes, - ); - - if (!curveConfig) { - throw new Error( - `No curve route found, verify exchange mapping ${tokenIn.symbol} -> ${tokenOut.symbol}`, - ); - } - - const estimatedGas = await estimateGas(config, { - amountIn, - slippage, - tokenIn, - tokenOut, - amountOut, - }); - const gas = estimatedGas + (estimatedGas * GAS_BUFFER) / 100n; - - const isTokenInNative = isNativeCurrency(tokenIn); - - const { request } = await simulateContractWithTxTracker(config, { - address: curve.CurveRegistryExchange.address, - abi: curve.CurveRegistryExchange.abi, - functionName: 'exchange_multiple', - args: [ - curveConfig.routes, - curveConfig.swapParams, - amountIn, - minAmountOut[0], - ], - gas, - ...(isTokenInNative && { value: amountIn }), - }); - const hash = await writeContract(config, request); - - return hash; -}; - -export default { - estimateAmount, - estimateGas, - estimateRoute, - allowance, - estimateApprovalGas, - approve, - swap, -}; diff --git a/libs/defi/oeth/src/swap/actions/swapZapperEth.ts b/libs/defi/oeth/src/swap/actions/swapZapperEth.ts deleted file mode 100644 index 360835ca9..000000000 --- a/libs/defi/oeth/src/swap/actions/swapZapperEth.ts +++ /dev/null @@ -1,201 +0,0 @@ -import { contracts } from '@origin/shared/contracts'; -import { simulateContractWithTxTracker } from '@origin/shared/providers'; -import { isNilOrEmpty } from '@origin/shared/utils'; -import { - getAccount, - getPublicClient, - readContract, - simulateContract, - writeContract, -} from '@wagmi/core'; -import { erc20Abi, formatUnits, maxUint256 } from 'viem'; - -import { GAS_BUFFER } from '../constants'; - -import type { - Allowance, - Approve, - EstimateAmount, - EstimateApprovalGas, - EstimateGas, - EstimateRoute, - Swap, -} from '@origin/shared/providers'; - -const estimateAmount: EstimateAmount = async (config, { amountIn }) => { - return amountIn; -}; - -const estimateGas: EstimateGas = async (config, { amountIn }) => { - let gasEstimate = 200000n; - - const { address } = getAccount(config); - const publicClient = getPublicClient(config); - - if (amountIn === 0n || !address || !publicClient) { - return gasEstimate; - } - - try { - gasEstimate = await publicClient.estimateContractGas({ - address: contracts.mainnet.OETHZapper.address, - abi: contracts.mainnet.OETHZapper.abi, - functionName: 'deposit', - value: amountIn, - account: address, - }); - } catch {} - - return gasEstimate; -}; - -const allowance: Allowance = async (config, { tokenIn, tokenOut }) => { - const { address } = getAccount(config); - - if (!address) { - return 0n; - } - - if (!tokenIn?.address || !tokenOut?.address) { - return maxUint256; - } - - const allowance = await readContract(config, { - address: tokenIn.address, - abi: erc20Abi, - functionName: 'allowance', - args: [address, contracts.mainnet.OETHZapper.address], - }); - - return allowance; -}; - -const estimateApprovalGas: EstimateApprovalGas = async ( - config, - { tokenIn, tokenOut, amountIn }, -) => { - let approvalEstimate = 0n; - const { address } = getAccount(config); - const publicClient = getPublicClient(config); - - if ( - amountIn === 0n || - !address || - !tokenIn?.address || - !tokenOut?.address || - !publicClient - ) { - return approvalEstimate; - } - - try { - approvalEstimate = await publicClient.estimateContractGas({ - address: tokenIn.address, - abi: erc20Abi, - functionName: 'approve', - args: [contracts.mainnet.OETHZapper.address, amountIn], - account: address, - }); - } catch { - approvalEstimate = 200000n; - } - - return approvalEstimate; -}; - -const estimateRoute: EstimateRoute = async ( - config, - { tokenIn, tokenOut, amountIn, route, slippage }, -) => { - if (amountIn === 0n) { - return { - ...route, - estimatedAmount: 0n, - gas: 0n, - rate: 0, - allowanceAmount: 0n, - approvalGas: 0n, - }; - } - - const [estimatedAmount, gas, allowanceAmount, approvalGas] = - await Promise.all([ - estimateAmount(config, { tokenIn, tokenOut, amountIn }), - estimateGas(config, { tokenIn, tokenOut, amountIn, slippage }), - allowance(config, { tokenIn, tokenOut }), - estimateApprovalGas(config, { tokenIn, tokenOut, amountIn }), - ]); - - return { - ...route, - estimatedAmount, - gas, - approvalGas, - allowanceAmount, - rate: - +formatUnits(estimatedAmount, tokenOut.decimals) / - +formatUnits(amountIn, tokenIn.decimals), - }; -}; - -const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { - if (!tokenIn?.address || !tokenOut?.address) { - return null; - } - - const { request } = await simulateContract(config, { - address: tokenIn.address, - abi: erc20Abi, - functionName: 'approve', - args: [contracts.mainnet.OETHZapper.address, amountIn], - }); - const hash = await writeContract(config, request); - - return hash; -}; - -const swap: Swap = async ( - config, - { tokenIn, tokenOut, amountIn, slippage }, -) => { - const { address } = getAccount(config); - - if (amountIn === 0n || isNilOrEmpty(address)) { - return null; - } - - const approved = await allowance(config, { tokenIn, tokenOut }); - - if (approved < amountIn) { - throw new Error(`Swap zapper is not approved`); - } - - const estimatedGas = await estimateGas(config, { - tokenIn, - tokenOut, - amountIn, - slippage, - }); - const gas = estimatedGas + (estimatedGas * GAS_BUFFER) / 100n; - - const { request } = await simulateContractWithTxTracker(config, { - address: contracts.mainnet.OETHZapper.address, - abi: contracts.mainnet.OETHZapper.abi, - functionName: 'deposit', - value: amountIn, - gas, - }); - const hash = await writeContract(config, request); - - return hash; -}; - -export default { - estimateAmount, - estimateGas, - estimateRoute, - allowance, - estimateApprovalGas, - approve, - swap, -}; diff --git a/libs/defi/oeth/src/swap/constants.ts b/libs/defi/oeth/src/swap/constants.ts index dfdf6dc98..bf8c5c087 100644 --- a/libs/defi/oeth/src/swap/constants.ts +++ b/libs/defi/oeth/src/swap/constants.ts @@ -4,124 +4,53 @@ import type { SwapRoute } from '@origin/shared/providers'; import type { OethSwapAction } from './types'; -export const GAS_BUFFER = 10n; // 10% - export const oethSwapRoutes: SwapRoute[] = [ // Mint { tokenIn: tokens.mainnet.ETH, tokenOut: tokens.mainnet.OETH, - action: 'swap-curve', + action: 'swap-curve-oeth', }, - // { - // tokenIn: tokens.mainnet.ETH, - // tokenOut: tokens.mainnet.OETH, - // action: 'swap-curve-eth', - // }, { tokenIn: tokens.mainnet.ETH, tokenOut: tokens.mainnet.OETH, - action: 'swap-zapper-eth', + action: 'swap-zapper-oeth-eth', noSlippage: true, }, { tokenIn: tokens.mainnet.WETH, tokenOut: tokens.mainnet.OETH, - action: 'mint-vault', + action: 'mint-vault-oeth', noSlippage: true, }, { tokenIn: tokens.mainnet.WETH, tokenOut: tokens.mainnet.OETH, - action: 'swap-curve', + action: 'swap-curve-oeth', }, - // { - // tokenIn: tokens.mainnet.stETH, - // tokenOut: tokens.mainnet.OETH, - // action: 'mint-vault', - // noSlippage: true, - // }, - // { - // tokenIn: tokens.mainnet.stETH, - // tokenOut: tokens.mainnet.OETH, - // action: 'swap-curve', - // }, - // { - // tokenIn: tokens.mainnet.rETH, - // tokenOut: tokens.mainnet.OETH, - // action: 'swap-curve', - // }, - // { - // tokenIn: tokens.mainnet.rETH, - // tokenOut: tokens.mainnet.OETH, - // action: 'mint-vault', - // noSlippage: true, - // }, - // { - // tokenIn: tokens.mainnet.frxETH, - // tokenOut: tokens.mainnet.OETH, - // action: 'mint-vault', - // noSlippage: true, - // }, - // { - // tokenIn: tokens.mainnet.frxETH, - // tokenOut: tokens.mainnet.OETH, - // action: 'swap-curve', - // }, - // { - // tokenIn: tokens.mainnet.sfrxETH, - // tokenOut: tokens.mainnet.OETH, - // action: 'swap-zapper-sfrxeth', - // noSlippage: true, - // }, // Redeem { tokenIn: tokens.mainnet.OETH, tokenOut: tokens.mainnet.WETH, - action: 'swap-curve', + action: 'swap-curve-oeth', }, - // { - // tokenIn: tokens.mainnet.OETH, - // tokenOut: tokens.mainnet.stETH, - // action: 'swap-curve', - // }, - // { - // tokenIn: tokens.mainnet.OETH, - // tokenOut: tokens.mainnet.rETH, - // action: 'swap-curve', - // }, - // { - // tokenIn: tokens.mainnet.OETH, - // tokenOut: tokens.mainnet.frxETH, - // action: 'swap-curve', - // }, - // { - // tokenIn: tokens.mainnet.OETH, - // tokenOut: tokens.mainnet.sfrxETH, - // action: 'swap-curve-sfrxeth', - // }, { tokenIn: tokens.mainnet.OETH, tokenOut: tokens.mainnet.ETH, - action: 'swap-curve', + action: 'swap-curve-oeth', }, - // { - // tokenIn: tokens.mainnet.OETH, - // tokenOut: tokens.mainnet.ETH, - // action: 'swap-curve-eth', - // }, // Wrap { tokenIn: tokens.mainnet.OETH, tokenOut: tokens.mainnet.wOETH, - action: 'wrap-oeth', + action: 'wrap-oeth-oeth', noSlippage: true, }, // Unwrap { tokenIn: tokens.mainnet.wOETH, tokenOut: tokens.mainnet.OETH, - action: 'unwrap-woeth', + action: 'unwrap-oeth-woeth', noSlippage: true, }, ]; diff --git a/libs/defi/oeth/src/swap/types.ts b/libs/defi/oeth/src/swap/types.ts index bcde44cbc..a4f9e4027 100644 --- a/libs/defi/oeth/src/swap/types.ts +++ b/libs/defi/oeth/src/swap/types.ts @@ -1,9 +1,13 @@ -export type OethSwapAction = - | 'swap-curve' - | 'swap-curve-eth' - | 'swap-curve-sfrxeth' - | 'swap-zapper-eth' - | 'swap-zapper-sfrxeth' - | 'mint-vault' - | 'wrap-oeth' - | 'unwrap-woeth'; +import type { OethRoute } from '@origin/shared/routes'; + +export type OethSwapAction = Extract< + OethRoute, + | 'mint-vault-oeth' + | 'swap-curve-oeth' + | 'swap-curve-oeth-eth' + | 'swap-curve-oeth-sfrxeth' + | 'swap-zapper-oeth-eth' + | 'swap-zapper-oeth-sfrxeth' + | 'unwrap-oeth-woeth' + | 'wrap-oeth-oeth' +>; diff --git a/libs/defi/ogn/src/staking/components/AddToLockupModal.tsx b/libs/defi/ogn/src/staking/components/AddToLockupModal.tsx deleted file mode 100644 index 659829d91..000000000 --- a/libs/defi/ogn/src/staking/components/AddToLockupModal.tsx +++ /dev/null @@ -1,491 +0,0 @@ -import { useEffect, useState } from 'react'; - -import { - Accordion, - AccordionDetails, - AccordionSummary, - Button, - CardContent, - Dialog, - DialogActions, - DialogContent, - DialogTitle, - Divider, - FormControlLabel, - IconButton, - Stack, - Switch, - Typography, - useMediaQuery, - useTheme, -} from '@mui/material'; -import { - SectionCard, - TokenChip, - useOgnInfo, - useTxButton, - useXOgnStakingApy, -} from '@origin/defi/shared'; -import { - BigIntInput, - InfoTooltipLabel, - LoadingLabel, - TokenIcon, - ValueLabel, -} from '@origin/shared/components'; -import { tokens } from '@origin/shared/contracts'; -import { DefaultWallet, FaXmarkRegular } from '@origin/shared/icons'; -import { ConnectedButton, TxButton, useFormat } from '@origin/shared/providers'; -import { - getMonthDurationToSeconds, - isNilOrEmpty, - ZERO_ADDRESS, -} from '@origin/shared/utils'; -import { useDebouncedEffect, useMountEffect } from '@react-hookz/web'; -import { differenceInMonths, formatDistanceToNowStrict } from 'date-fns'; -import { useIntl } from 'react-intl'; -import { formatUnits } from 'viem'; -import { useAccount } from 'wagmi'; - -import { useStartLockupPolling } from '../hooks'; - -import type { DialogProps } from '@mui/material'; -import type { ConnectedButtonProps } from '@origin/shared/providers'; -import type { ChangeEvent, MouseEvent } from 'react'; - -import type { Lockup } from '../types'; - -export type AddToLockupModalProps = { - lockup: Lockup; -} & DialogProps; - -export const AddToLockupModal = ({ - lockup, - ...rest -}: AddToLockupModalProps) => { - const monthDuration = Math.max( - 0, - differenceInMonths(new Date(lockup.end), new Date()), - ); - - const intl = useIntl(); - const { formatQuantity, formatAmount } = useFormat(); - const theme = useTheme(); - const fullScreen = useMediaQuery(theme.breakpoints.down('md')); - const { isConnected, address } = useAccount(); - const startPolling = useStartLockupPolling(); - const { data: info, isLoading: isInfoLoading } = useOgnInfo(); - const [amount, setAmount] = useState(0n); - const [addRewards, setAddRewards] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const { - data: staking, - isLoading: isStakingApyLoading, - refetch, - } = useXOgnStakingApy( - amount + (addRewards && info?.xOgnRewards ? info.xOgnRewards : 0n), - monthDuration, - { - enabled: false, - }, - ); - const { params: writeParams, callbacks: writeCallbacks } = useTxButton({ - params: { - contract: tokens.mainnet.xOGN, - functionName: 'stake', - args: [ - amount, - getMonthDurationToSeconds(monthDuration), - address ?? ZERO_ADDRESS, - addRewards, - BigInt(lockup.lockupId), - ], - }, - activity: { - type: 'extend-stake', - status: 'idle', - amountIn: amount, - tokenIdIn: tokens.mainnet.OGN.id, - monthDuration, - lockupId: lockup.lockupId, - }, - callbacks: { - onWriteSuccess: () => { - startPolling(lockup.lockupId); - rest?.onClose?.({}, 'backdropClick'); - }, - }, - }); - - useMountEffect(() => { - refetch(); - }); - - useDebouncedEffect( - () => { - if (amount > 0n || (addRewards && (info?.xOgnRewards ?? 0n) > 0n)) { - refetch(); - } - }, - [amount, addRewards], - 800, - ); - - useEffect(() => { - if (isLoading && !isNilOrEmpty(staking?.xOgnApyPercentage)) { - setIsLoading(false); - } - }, [ - addRewards, - amount, - isLoading, - isStakingApyLoading, - staking?.xOgnApyPercentage, - ]); - - const handleAmountChange = (val: bigint) => { - setIsLoading(true); - setAmount(val); - }; - - const handleMaxClick = (evt: MouseEvent) => { - evt.stopPropagation(); - if (amount !== info?.ognBalance) { - setIsLoading(true); - setAmount(info?.ognBalance ?? 0n); - } - }; - - const handleToggleAddRewards = (evt: ChangeEvent) => { - evt.stopPropagation(); - if (info?.xOgnRewards) { - setIsLoading(true); - setAddRewards(evt.target.checked); - } - }; - - const xOgnReceived = amount === 0n ? 0 : staking?.xOgnPreview; - const votingPowerPercent = - (xOgnReceived ?? 0) / - +formatUnits( - (info?.xOgnTotalSupply as unknown as bigint) ?? 1n, - tokens.mainnet.OGN.decimals, - ); - const isStakeDisabled = - !isConnected || isInfoLoading || isLoading || amount === 0n; - - return ( - - - {intl.formatMessage({ defaultMessage: 'Add to lockup' })} - { - rest?.onClose?.(evt, 'backdropClick'); - }} - > - - - - - - - - {intl.formatMessage({ - defaultMessage: 'Amount to add', - })} - - - - - } - inputProps={{ sx: { p: 0, height: 40 } }} - sx={{ - p: 3, - borderRadius: 3, - backgroundColor: 'background.highlight', - border: '1px solid', - borderColor: 'divider', - ...theme.typography.h6, - }} - /> - - - - } - label={ - - {intl.formatMessage( - { - defaultMessage: 'Add unclaimed rewards ({rewards} OGN)', - }, - { - rewards: formatAmount( - info?.xOgnRewards ?? 0n, - tokens.mainnet.OGN.decimals, - ), - }, - )} - - } - /> - - - - - {formatAmount( - info?.xOgnRewards ?? 0n, - tokens.mainnet.OGN.decimals, - )} - - - - - - - - {intl.formatMessage({ defaultMessage: 'Adding to lockup' })} - - - - - {intl.formatMessage({ defaultMessage: 'OGN' })} - - - {intl.formatMessage({ defaultMessage: 'Time remaining' })} - - - {intl.formatMessage({ defaultMessage: 'Voting power' })} - - - - - - {intl.formatNumber( - +formatUnits( - BigInt(lockup.amount), - tokens.mainnet.OGN.decimals, - ), - { - minimumFractionDigits: 0, - maximumFractionDigits: 0, - }, - )} - - - {formatDistanceToNowStrict(new Date(lockup.end), { - unit: 'month', - roundingMethod: 'floor', - })} - - - {intl.formatNumber( - +formatUnits( - BigInt(lockup.points) ?? 0n, - tokens.mainnet.xOGN.decimals, - ) / - +formatUnits( - info?.xOgnTotalSupply ?? 1n, - tokens.mainnet.xOGN.decimals, - ), - { - style: 'percent', - minimumFractionDigits: 2, - maximumFractionDigits: 6, - }, - )} - - - - - - - - - {formatQuantity( - staking?.xOgnPreview, - tokens.mainnet.xOGN.decimals, - '0.00', - )} - - - - {tokens.mainnet.xOGN.symbol} - - - 0n} - value={intl.formatMessage( - { defaultMessage: '{tilt}{value}' }, - { - tilt: - votingPowerPercent <= 1e-6 && votingPowerPercent > 0 - ? `~ ` - : '', - value: - amount > 0n || addRewards - ? intl.formatNumber(votingPowerPercent, { - style: 'percent', - minimumFractionDigits: 2, - maximumFractionDigits: 5, - }) - : '-', - }, - )} - valueProps={{ variant: 'body3', fontWeight: 'medium' }} - sx={{ alignItems: 'flex-end' }} - /> - - - - - - - - - ); -}; - -export type AddButtonProps = { - lockup: Lockup; -} & ConnectedButtonProps; - -export const AddButton = ({ lockup, ...rest }: AddButtonProps) => { - const [open, setOpen] = useState(false); - - return ( - <> - { - setOpen(true); - rest?.onClick?.(e); - }} - /> - { - setOpen(false); - }} - /> - - ); -}; diff --git a/libs/defi/ogn/src/staking/components/ExtendLockupModal.tsx b/libs/defi/ogn/src/staking/components/ExtendAddLockupModal.tsx similarity index 64% rename from libs/defi/ogn/src/staking/components/ExtendLockupModal.tsx rename to libs/defi/ogn/src/staking/components/ExtendAddLockupModal.tsx index 6bc0f7c6c..1badcd5da 100644 --- a/libs/defi/ogn/src/staking/components/ExtendLockupModal.tsx +++ b/libs/defi/ogn/src/staking/components/ExtendAddLockupModal.tsx @@ -1,50 +1,66 @@ -import { useEffect, useState } from 'react'; +import { useState } from 'react'; import { Box, + Button, CardContent, + Collapse, Dialog, DialogActions, DialogContent, DialogTitle, Divider, + FormControlLabel, IconButton, Slider, Stack, + Switch, Typography, useMediaQuery, useTheme, } from '@mui/material'; import { SectionCard, + TokenButton, + useApprovalButton, useOgnInfo, useTxButton, useXOgnStakingApy, } from '@origin/defi/shared'; -import { LoadingLabel, TokenIcon, ValueLabel } from '@origin/shared/components'; +import { + BigIntInput, + LoadingLabel, + TokenIcon, + ValueLabel, +} from '@origin/shared/components'; import { tokens } from '@origin/shared/contracts'; import { FaCircleExclamationRegular, FaXmarkRegular, + WalletFilled, } from '@origin/shared/icons'; import { ConnectedButton, TxButton, useFormat } from '@origin/shared/providers'; -import { isNilOrEmpty, ZERO_ADDRESS } from '@origin/shared/utils'; -import { useDebouncedEffect, useMountEffect } from '@react-hookz/web'; +import { getFormatPrecision, ZERO_ADDRESS } from '@origin/shared/utils'; import { + addDays, addMonths, - differenceInMonths, - formatDistanceToNowStrict, + differenceInDays, formatDuration, isPast, } from 'date-fns'; +import { add, eq, format, from, toNumber } from 'dnum'; import { useIntl } from 'react-intl'; import { formatUnits } from 'viem'; import { useAccount } from 'wagmi'; import { useStartLockupPolling } from '../hooks'; +import { formatTimeRemaining } from '../utils'; import type { DialogProps } from '@mui/material'; +import type { SectionCardProps } from '@origin/defi/shared'; import type { ConnectedButtonProps } from '@origin/shared/providers'; +import type { Dnum } from 'dnum'; +import type { ChangeEvent } from 'react'; import type { Lockup } from '../types'; @@ -52,46 +68,77 @@ export type ExtendLockupModalProps = { lockup: Lockup; } & DialogProps; -export const ExtendLockupModal = ({ +const MIN_MONTH_DURATION = 1; + +export const ExtendAddLockupModal = ({ lockup, ...rest }: ExtendLockupModalProps) => { - const amount = BigInt(lockup.amount); - const initialMonthDuration = Math.max( - 0, - differenceInMonths(new Date(lockup.end), new Date()), + const initialAmount = [ + BigInt(lockup.amount), + tokens.mainnet.OGN.decimals, + ] as Dnum; + const minEndDate = isPast(new Date(lockup.end)) + ? addMonths(new Date(), MIN_MONTH_DURATION) + : differenceInDays(new Date(lockup.end), new Date()) < 365 / 12 + ? addDays( + new Date(lockup.end), + Math.floor( + 365 / 12 - differenceInDays(new Date(lockup.end), new Date()), + ), + ) + : new Date(lockup.end); + const initialMonthDuration = Math.floor( + differenceInDays(minEndDate, new Date()) / Math.floor(365 / 12), ); + const intl = useIntl(); - const { formatQuantity, formatAmount } = useFormat(); + const { formatAmount } = useFormat(); const theme = useTheme(); const fullScreen = useMediaQuery(theme.breakpoints.down('md')); const startPolling = useStartLockupPolling(); const { isConnected, address } = useAccount(); const { data: info, isLoading: isInfoLoading } = useOgnInfo(); + const [amountToAdd, setAmountToAdd] = useState( + from(0, tokens.mainnet.OGN.decimals), + ); const [duration, setDuration] = useState(initialMonthDuration); - const [isLoading, setIsLoading] = useState(false); - const { data: staking, refetch } = useXOgnStakingApy(amount, duration, { - enabled: false, - }); + const [addRewards, setAddRewards] = useState(true); + const totalAmount = add(initialAmount, amountToAdd); + const { data: staking, isLoading: isStakingLoading } = useXOgnStakingApy( + totalAmount[0], + duration, + ); const durationSeconds = BigInt( Math.min(duration * 60 * 60 * 24 * (365 / 12), 31_536_000), ); + const { + allowance, + params: approvalParams, + callbacks: approvalCallbacks, + label: approvalLabel, + } = useApprovalButton({ + token: tokens.mainnet.OGN, + spender: tokens.mainnet.xOGN.address, + amount: add(totalAmount, [1n, tokens.mainnet.OGN.decimals])[0], + enableAllowance: true, + }); const { params: writeParams, callbacks: writeCallbacks } = useTxButton({ params: { contract: tokens.mainnet.xOGN, functionName: 'stake', args: [ - 0n, + totalAmount[0], durationSeconds, address ?? ZERO_ADDRESS, - true, + addRewards, BigInt(lockup.lockupId), ], }, activity: { type: 'extend-stake', status: 'pending', - amountIn: amount, + amountIn: totalAmount[0], tokenIdIn: tokens.mainnet.xOGN.id, monthDuration: duration, lockupId: lockup.lockupId, @@ -104,60 +151,50 @@ export const ExtendLockupModal = ({ }, }); - useMountEffect(() => { - refetch(); - }); + const handleMaxClick = () => { + setAmountToAdd([info?.ognBalance ?? 0n, tokens.mainnet.OGN.decimals]); + }; - useDebouncedEffect( - () => { - if (duration > initialMonthDuration) { - refetch(); - } - }, - [duration], - 800, - ); + const handleAmountChange = (val: bigint) => { + setAmountToAdd([val, tokens.mainnet.OGN.decimals]); + }; - useEffect(() => { - if ( - isLoading && - (!isNilOrEmpty(staking?.xOgnApyPercentage) || - duration === initialMonthDuration) - ) { - setIsLoading(false); - } - }, [duration, initialMonthDuration, isLoading, staking?.xOgnApyPercentage]); + const handleToggleAddRewards = (event: ChangeEvent) => { + setAddRewards(event.target.checked); + }; const handleDurationChange = (_: Event, newValue: number | number[]) => { const val = newValue as number; - if (val >= (initialMonthDuration ?? 0)) { - setIsLoading(true); + if (val >= initialMonthDuration) { setDuration(val); } }; - const xOgnReceived = - duration === initialMonthDuration - ? +formatUnits(BigInt(lockup.points), tokens.mainnet.xOGN.decimals) - : staking?.xOgnPreview; + const bal = [info?.ognBalance ?? 0n, tokens.mainnet.OGN.decimals] as Dnum; + const xOgnReceived = from( + staking?.xOgnPreview ?? 0, + tokens.mainnet.xOGN.decimals, + ); const votingPowerPercent = - (xOgnReceived ?? 0) / + toNumber(from(staking?.xOgnPreview ?? 0, tokens.mainnet.xOGN.decimals)) / +formatUnits( (info?.xOgnTotalSupply as unknown as bigint) ?? 1n, tokens.mainnet.OGN.decimals, ); - const showRewardLabel = ((info?.xOgnRewards as unknown as bigint) ?? 0n) > 0n; + const extendLockupEnd = + duration === initialMonthDuration + ? minEndDate + : addMonths(minEndDate, duration - initialMonthDuration); + const showApprove = + isConnected && + !isInfoLoading && + amountToAdd[0] <= (info?.ognBalance ?? 0n) && + totalAmount[0] >= (allowance ?? 0n); const isStakeDisabled = !isConnected || isInfoLoading || - isLoading || - duration <= initialMonthDuration; - const extendLockupEnd = - duration === initialMonthDuration - ? isPast(new Date(lockup.end)) - ? new Date() - : new Date(lockup.end) - : addMonths(new Date(), duration); + showApprove || + (duration === initialMonthDuration && eq(amountToAdd, initialAmount)); return ( @@ -166,7 +203,7 @@ export const ExtendLockupModal = ({ justifyContent="space-between" alignItems="center" > - {intl.formatMessage({ defaultMessage: 'Extend Stake' })} + {intl.formatMessage({ defaultMessage: 'Manage Stake' })} { rest?.onClose?.(evt, 'backdropClick'); @@ -177,19 +214,89 @@ export const ExtendLockupModal = ({ - + + + {intl.formatMessage({ defaultMessage: 'Amount to add' })} + + + + } + sx={{ + px: 3, + py: 2, + borderRadius: 3, + backgroundColor: 'background.highlight', + border: '1px solid', + borderColor: 'divider', + ...theme.typography.h6, + }} + /> + + + } + label={intl.formatMessage( + { + defaultMessage: 'Add unclaimed rewards to stake ({rewards} OGN)', + }, + { + rewards: formatAmount( + info?.xOgnRewards ?? 0n, + tokens.mainnet.OGN.decimals, + ), + }, + )} + sx={{ mb: 3 }} + /> + + + + + {intl.formatMessage({ + defaultMessage: + 'Any unclaimed rewards will transferred to your wallet immediately when you extend your stake.', + })} + + + + - - {intl.formatMessage({ defaultMessage: 'Selected lockup' })} - - {intl.formatMessage({ defaultMessage: 'OGN' })} @@ -215,12 +322,7 @@ export const ExtendLockupModal = ({ }, )} - - {formatDistanceToNowStrict(new Date(lockup.end), { - unit: 'month', - roundingMethod: 'floor', - })} - + {formatTimeRemaining(lockup.end)} {intl.formatNumber( +formatUnits( @@ -239,17 +341,14 @@ export const ExtendLockupModal = ({ )} - + @@ -340,11 +439,11 @@ export const ExtendLockupModal = ({ ~ {intl.formatNumber(staking?.xOgnApyPercentage ?? 0, { @@ -369,9 +468,9 @@ export const ExtendLockupModal = ({ 0n} + isLoading={isStakingLoading || isInfoLoading} sWidth={60} > - {amount > 0n ? formatQuantity(staking?.xOgnPreview) : '0.00'} + {format(xOgnReceived, getFormatPrecision(xOgnReceived))} 0n} + isLoading={isStakingLoading} value={intl.formatMessage( { defaultMessage: '{tilt}{value}' }, { @@ -424,14 +523,11 @@ export const ExtendLockupModal = ({ votingPowerPercent <= 1e-6 && votingPowerPercent > 0 ? `~ ` : '', - value: - amount > 0n - ? intl.formatNumber(votingPowerPercent, { - style: 'percent', - minimumFractionDigits: 2, - maximumFractionDigits: 5, - }) - : '0.00%', + value: intl.formatNumber(votingPowerPercent, { + style: 'percent', + minimumFractionDigits: 2, + maximumFractionDigits: 5, + }), }, )} valueProps={{ variant: 'body3', fontWeight: 'medium' }} @@ -440,53 +536,23 @@ export const ExtendLockupModal = ({ - {showRewardLabel && ( - - - - - {intl.formatMessage({ - defaultMessage: 'OGN Rewards Will be Collected', - })} - - - {intl.formatMessage( - { - defaultMessage: - 'You have accrued {reward} OGN in staking rewards. This OGN will be transferred to your wallet immediately when you extend your stake.', - }, - { - reward: formatAmount( - info?.xOgnRewards, - tokens.mainnet.OGN.decimals, - undefined, - { notation: 'compact', maximumSignificantDigits: 4 }, - ), - }, - )} - - - - )} + + + = { + titleProps: { color: 'text.secondary', fontWeight: 'medium' }, + cardProps: { + sx: { backgroundColor: 'background.highlight' }, + }, +}; + export type ExtendButtonProps = { lockup: Lockup; } & ConnectedButtonProps; -export const ExtendButton = ({ lockup, ...rest }: ExtendButtonProps) => { +export const ExtendAddButton = ({ lockup, ...rest }: ExtendButtonProps) => { const [open, setOpen] = useState(false); return ( @@ -512,14 +585,16 @@ export const ExtendButton = ({ lockup, ...rest }: ExtendButtonProps) => { rest?.onClick?.(e); }} /> - { - setOpen(false); - }} - /> + {open && ( + { + setOpen(false); + }} + /> + )} ); }; diff --git a/libs/defi/ogn/src/staking/components/LockupsTable.tsx b/libs/defi/ogn/src/staking/components/LockupsTable.tsx index 8d7073818..d302e7129 100644 --- a/libs/defi/ogn/src/staking/components/LockupsTable.tsx +++ b/libs/defi/ogn/src/staking/components/LockupsTable.tsx @@ -26,15 +26,14 @@ import { getPaginationRowModel, useReactTable, } from '@tanstack/react-table'; -import { differenceInDays, formatDistanceToNowStrict } from 'date-fns'; import { useIntl } from 'react-intl'; import { formatUnits } from 'viem'; import { useAccount } from 'wagmi'; import { useOgnLockupsQuery } from '../queries.generated'; import { useLockupPolling } from '../state'; -import { AddButton } from './AddToLockupModal'; -import { ExtendButton } from './ExtendLockupModal'; +import { formatTimeRemaining } from '../utils'; +import { ExtendAddButton } from './ExtendAddLockupModal'; import { UnstakeLockupButton } from './UnstakeLockupModal'; import type { Lockup } from '../types'; @@ -85,11 +84,7 @@ export const LockupsTable = () => { columnHelper.display({ id: 'timeRemaining', header: intl.formatMessage({ defaultMessage: 'Time Remaining' }), - cell: (info) => - formatDistanceToNowStrict(new Date(info.row.original.end), { - unit: 'month', - roundingMethod: 'floor', - }), + cell: (info) => formatTimeRemaining(info.row.original.end), }), ...(isSm ? [] @@ -153,9 +148,6 @@ export const LockupsTable = () => { ); } - const addDisabled = - differenceInDays(new Date(info.row.original.end), new Date()) < 30; - return ( { alignItems="stretch" justifyContent="flex-end" > - - {intl.formatMessage({ defaultMessage: 'Extend' })} - + {intl.formatMessage({ defaultMessage: 'Extend/Add' })} + { > {intl.formatMessage({ defaultMessage: 'Unlock' })} - - {intl.formatMessage({ defaultMessage: 'Add' })} -