diff --git a/apps/defi/src/lang/en.json b/apps/defi/src/lang/en.json index 34c78a23f..1e40f286a 100644 --- a/apps/defi/src/lang/en.json +++ b/apps/defi/src/lang/en.json @@ -1,10 +1,4 @@ { - "+0zAiT": [ - { - "type": 0, - "value": "The updated amount of xOGN you will receive by adding OGN to this lockup." - } - ], "+14VoL": [ { "type": 0, @@ -223,12 +217,6 @@ "value": "Return to Home Page" } ], - "2/2yg+": [ - { - "type": 0, - "value": "Add" - } - ], "20KPDF": [ { "type": 0, @@ -821,12 +809,6 @@ "value": "Bridging activity" } ], - "BLAy25": [ - { - "type": 0, - "value": "Extend" - } - ], "BNFT1w": [ { "type": 0, @@ -1021,6 +1003,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, @@ -1279,12 +1267,6 @@ "value": "symbol" } ], - "Kzhxqf": [ - { - "type": 0, - "value": "Select the additional OGN you would like to add to your lockup" - } - ], "L2dDDV": [ { "type": 0, @@ -2011,12 +1993,6 @@ "value": "Learn about governance" } ], - "YnTVWE": [ - { - "type": 0, - "value": "Adding to lockup" - } - ], "YnftYz": [ { "type": 0, @@ -2307,6 +2283,20 @@ "value": "Connect your wallet to see your balances" } ], + "e/f0yl": [ + { + "type": 0, + "value": "Add unclaimed rewards to stake (" + }, + { + "type": 1, + "value": "rewards" + }, + { + "type": 0, + "value": " OGN)" + } + ], "e6Ph5+": [ { "type": 0, @@ -2477,6 +2467,12 @@ "value": "Gas" } ], + "hsG1EM": [ + { + "type": 0, + "value": "Extend/Add" + } + ], "hsqHqR": [ { "type": 0, @@ -2507,20 +2503,6 @@ "value": "Error while estimating" } ], - "iRGhsn": [ - { - "type": 0, - "value": "Add unclaimed rewards (" - }, - { - "type": 1, - "value": "rewards" - }, - { - "type": 0, - "value": " OGN)" - } - ], "iaS0YY": [ { "type": 0, @@ -2599,12 +2581,6 @@ "value": "Your voting power" } ], - "kDaImL": [ - { - "type": 0, - "value": "Add to lockup" - } - ], "kH1R79": [ { "type": 0, @@ -2699,6 +2675,12 @@ "value": "." } ], + "mDjDir": [ + { + "type": 0, + "value": "Manage Stake" + } + ], "mTz9QI": [ { "type": 0, @@ -3181,12 +3163,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, diff --git a/apps/defi/src/lang/extracts/en.json b/apps/defi/src/lang/extracts/en.json index f6baec2f2..f93f889fa 100644 --- a/apps/defi/src/lang/extracts/en.json +++ b/apps/defi/src/lang/extracts/en.json @@ -1,7 +1,4 @@ { - "+0zAiT": { - "defaultMessage": "The updated amount of xOGN you will receive by adding OGN to this lockup." - }, "+14VoL": { "defaultMessage": "Staking" }, @@ -95,9 +92,6 @@ "1nxJrQ": { "defaultMessage": "Return to Home Page" }, - "2/2yg+": { - "defaultMessage": "Add" - }, "20KPDF": { "defaultMessage": "Registered Voters" }, @@ -329,9 +323,6 @@ "BB94QK": { "defaultMessage": "Bridging activity" }, - "BLAy25": { - "defaultMessage": "Extend" - }, "BNFT1w": { "defaultMessage": "Global stats" }, @@ -413,6 +404,9 @@ "F6lS59": { "defaultMessage": "0 months" }, + "FIG7wh": { + "defaultMessage": "Any unclaimed rewards will transferred to your wallet immediately when you extend your stake." + }, "FVYhKc": { "defaultMessage": "Balances" }, @@ -524,9 +518,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." }, @@ -782,9 +773,6 @@ "Yl2sc/": { "defaultMessage": "Learn about governance" }, - "YnTVWE": { - "defaultMessage": "Adding to lockup" - }, "YnftYz": { "defaultMessage": "Wait time:" }, @@ -905,6 +893,9 @@ "e+yfm6": { "defaultMessage": "Connect your wallet to see your balances" }, + "e/f0yl": { + "defaultMessage": "Add unclaimed rewards to stake ({rewards} OGN)" + }, "e6Ph5+": { "defaultMessage": "Address" }, @@ -974,6 +965,9 @@ "hjWYtP": { "defaultMessage": "Gas" }, + "hsG1EM": { + "defaultMessage": "Extend/Add" + }, "hsqHqR": { "defaultMessage": "Time remaining" }, @@ -989,9 +983,6 @@ "iLgjES": { "defaultMessage": "Error while estimating" }, - "iRGhsn": { - "defaultMessage": "Add unclaimed rewards ({rewards} OGN)" - }, "iaS0YY": { "defaultMessage": "I have read and agree to the above terms" }, @@ -1031,9 +1022,6 @@ "kBjaSb": { "defaultMessage": "Your voting power" }, - "kDaImL": { - "defaultMessage": "Add to lockup" - }, "kH1R79": { "defaultMessage": "Stake OGN" }, @@ -1067,6 +1055,9 @@ "m9XypM": { "defaultMessage": "Sending {amount} {symbolIn} to {chainOut}." }, + "mDjDir": { + "defaultMessage": "Manage Stake" + }, "mTz9QI": { "defaultMessage": "Casted vote" }, @@ -1280,9 +1271,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/oeth/src/redeem/actions.ts b/libs/defi/oeth/src/redeem/actions.ts new file mode 100644 index 000000000..be64853b2 --- /dev/null +++ b/libs/defi/oeth/src/redeem/actions.ts @@ -0,0 +1,19 @@ +import { redeemVaultOeth, SwapCurveOeth } 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 = { + 'swap-curve-oeth': { + ...SwapCurveOeth, + routeLabel: defineMessage({ defaultMessage: 'Swap via Curve' }), + buttonLabel: defineMessage({ defaultMessage: 'Redeem' }), + }, + 'redeem-vault-oeth': { + ...redeemVaultOeth, + routeLabel: defineMessage({ defaultMessage: 'Redeem via OETH Vault' }), + buttonLabel: defineMessage({ defaultMessage: 'Redeem' }), + }, +}; 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 68d0e5a0d..000000000 --- a/libs/defi/oeth/src/redeem/actions/index.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { defineMessage } from 'react-intl'; - -import redeemVault from './redeemVault'; -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' }), - }, -}; diff --git a/libs/defi/oeth/src/redeem/actions/swapCurve/index.ts b/libs/defi/oeth/src/redeem/actions/swapCurve/index.ts deleted file mode 100644 index e372a148d..000000000 --- a/libs/defi/oeth/src/redeem/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/redeem/components/RedeemActionCard.tsx b/libs/defi/oeth/src/redeem/components/RedeemActionCard.tsx index 224fb3fe9..bf36a85c4 100644 --- a/libs/defi/oeth/src/redeem/components/RedeemActionCard.tsx +++ b/libs/defi/oeth/src/redeem/components/RedeemActionCard.tsx @@ -128,7 +128,7 @@ export const RedeemActionCard = ({ {intl.formatMessage(routeLabel)} - {action === 'redeem-vault' ? ( + {action === 'redeem-vault-oeth' ? ( ) : ( diff --git a/libs/defi/oeth/src/redeem/components/Swapper.tsx b/libs/defi/oeth/src/redeem/components/Swapper.tsx index 411870550..c4603daaa 100644 --- a/libs/defi/oeth/src/redeem/components/Swapper.tsx +++ b/libs/defi/oeth/src/redeem/components/Swapper.tsx @@ -351,8 +351,8 @@ function SwapperWrapped({ {intl.formatMessage({ defaultMessage: 'Route' })} - - + + {intl.formatMessage({ defaultMessage: 'Receive amount' })} diff --git a/libs/defi/oeth/src/redeem/constants.ts b/libs/defi/oeth/src/redeem/constants.ts index 5414f83af..59cae9b81 100644 --- a/libs/defi/oeth/src/redeem/constants.ts +++ b/libs/defi/oeth/src/redeem/constants.ts @@ -4,17 +4,15 @@ import type { SwapRoute } from '@origin/shared/providers'; import type { OethRedeemAction } from './types'; -export const GAS_BUFFER = 10n; // 10% - export const redeemRoutes: SwapRoute[] = [ { tokenIn: tokens.mainnet.OETH, tokenOut: tokens.mainnet.WETH, - action: 'swap-curve', + action: 'swap-curve-oeth', }, { tokenIn: tokens.mainnet.OETH, tokenOut: tokens.mainnet.WETH, - action: 'redeem-vault', + action: 'redeem-vault-oeth', }, ]; diff --git a/libs/defi/oeth/src/redeem/types.ts b/libs/defi/oeth/src/redeem/types.ts index a9ad46da7..47c13bdfc 100644 --- a/libs/defi/oeth/src/redeem/types.ts +++ b/libs/defi/oeth/src/redeem/types.ts @@ -1 +1,6 @@ -export type OethRedeemAction = 'swap-curve' | 'redeem-vault'; +import type { OethRoute } from '@origin/shared/routes'; + +export type OethRedeemAction = Extract< + OethRoute, + 'swap-curve-oeth' | 'redeem-vault-oeth' +>; 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/mintVault.ts b/libs/defi/oeth/src/swap/actions/mintVault.ts deleted file mode 100644 index da2e0fb08..000000000 --- a/libs/defi/oeth/src/swap/actions/mintVault.ts +++ /dev/null @@ -1,282 +0,0 @@ -import { queryClient } from '@origin/defi/shared'; -import { contracts } from '@origin/shared/contracts'; -import { simulateContractWithTxTracker } from '@origin/shared/providers'; -import { isNilOrEmpty, subPercentage } from '@origin/shared/utils'; -import { - getAccount, - getPublicClient, - readContract, - readContracts, - simulateContract, - writeContract, -} from '@wagmi/core'; -import { erc20Abi, formatUnits, parseUnits } from 'viem'; - -import { GAS_BUFFER } from '../constants'; - -import type { - Allowance, - Approve, - EstimateApprovalGas, - EstimateGas, - EstimateRoute, - IsRouteAvailable, - Swap, -} from '@origin/shared/providers'; -import type { EstimateAmount } from '@origin/shared/providers'; - -const isRouteAvailable: IsRouteAvailable = async (config, { tokenIn }) => { - try { - if (tokenIn?.address) { - await readContract(config, { - address: contracts.mainnet.OETHVault.address, - abi: contracts.mainnet.OETHVault.abi, - functionName: 'priceUnitMint', - args: [tokenIn.address], - }); - - return true; - } - } catch {} - - return false; -}; - -const estimateAmount: EstimateAmount = async ( - config, - { tokenIn, tokenOut, amountIn }, -) => { - if (amountIn === 0n || !tokenIn?.address) { - return 0n; - } - - const priceUnitMint = await readContract(config, { - address: contracts.mainnet.OETHVault.address, - abi: contracts.mainnet.OETHVault.abi, - functionName: 'priceUnitMint', - args: [tokenIn.address], - }); - - return parseUnits( - ( - +formatUnits(amountIn, tokenIn.decimals) * - +formatUnits(priceUnitMint as unknown as bigint, 18) - ).toString(), - tokenOut.decimals, - ); -}; - -const estimateGas: EstimateGas = async ( - config, - { tokenIn, tokenOut, amountIn, slippage, amountOut }, -) => { - let gasEstimate = 0n; - const publicClient = getPublicClient(config); - - if (amountIn === 0n || !publicClient || !tokenIn?.address) { - return gasEstimate; - } - - const { address } = getAccount(config); - - const minAmountOut = subPercentage( - [amountOut ?? 0n, tokenOut.decimals], - slippage, - ); - - try { - gasEstimate = await publicClient.estimateContractGas({ - address: contracts.mainnet.OETHVault.address, - abi: contracts.mainnet.OETHVault.abi, - functionName: 'mint', - args: [tokenIn.address, amountIn, minAmountOut[0]], - account: address, - }); - - return gasEstimate; - } catch {} - - const [rebaseThreshold, autoAllocateThreshold] = await queryClient.fetchQuery( - { - queryKey: ['vault-info', tokenOut.address], - queryFn: () => - readContracts(config, { - contracts: [ - { - address: contracts.mainnet.OETHVault.address, - abi: contracts.mainnet.OETHVault.abi, - functionName: 'rebaseThreshold', - }, - { - address: contracts.mainnet.OETHVault.address, - abi: contracts.mainnet.OETHVault.abi, - functionName: 'autoAllocateThreshold', - }, - ], - }), - staleTime: Infinity, - }, - ); - - gasEstimate = 220000n; - if (amountIn > (autoAllocateThreshold?.result as bigint)) { - gasEstimate = 2900000n; - } else if (amountIn > (rebaseThreshold?.result as bigint)) { - gasEstimate = 510000n; - } - - return gasEstimate; -}; - -const allowance: Allowance = async (config, { tokenIn }) => { - const { address } = getAccount(config); - - if (!address || !tokenIn?.address) { - return 0n; - } - - const allowance = await readContract(config, { - address: tokenIn.address, - abi: erc20Abi, - functionName: 'allowance', - args: [address, contracts.mainnet.OETHVault.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 || !tokenIn?.address) { - return approvalEstimate; - } - - try { - approvalEstimate = await publicClient.estimateContractGas({ - address: tokenIn.address, - abi: erc20Abi, - functionName: 'approve', - args: [contracts.mainnet.OETHVault.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, - slippage, - amountOut: estimatedAmount, - }); - - 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) { - return null; - } - - const { request } = await simulateContract(config, { - address: tokenIn.address, - abi: erc20Abi, - functionName: 'approve', - args: [contracts.mainnet.OETHVault.address, amountIn], - }); - const hash = await writeContract(config, request); - - return hash; -}; - -const swap: Swap = async ( - config, - { tokenIn, tokenOut, amountIn, slippage, amountOut }, -) => { - const { address } = getAccount(config); - - if (amountIn === 0n || isNilOrEmpty(address)) { - return null; - } - - const approved = await allowance(config, { tokenIn, tokenOut }); - - if (approved < amountIn) { - throw new Error(`Mint vault is not approved`); - } - - const minAmountOut = subPercentage( - [amountOut ?? 0n, tokenOut.decimals], - slippage, - ); - - const estimatedGas = await estimateGas(config, { - amountIn, - slippage, - tokenIn, - tokenOut, - amountOut, - }); - const gas = estimatedGas + (estimatedGas * GAS_BUFFER) / 100n; - - const { request } = await simulateContractWithTxTracker(config, { - address: contracts.mainnet.OETHVault.address, - abi: contracts.mainnet.OETHVault.abi, - functionName: 'mint', - args: [tokenIn.address, amountIn, minAmountOut[0]], - gas, - }); - const hash = await writeContract(config, request); - - return hash; -}; - -export default { - isRouteAvailable, - estimateAmount, - estimateGas, - estimateRoute, - allowance, - estimateApprovalGas, - approve, - swap, -}; 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/swapCurveSfrxeth.ts b/libs/defi/oeth/src/swap/actions/swapCurveSfrxeth.ts deleted file mode 100644 index 6635d4ecb..000000000 --- a/libs/defi/oeth/src/swap/actions/swapCurveSfrxeth.ts +++ /dev/null @@ -1,250 +0,0 @@ -import { contracts, tokens } from '@origin/shared/contracts'; -import { simulateContractWithTxTracker } from '@origin/shared/providers'; -import { - ETH_ADDRESS_CURVE, - isNilOrEmpty, - subPercentage, -} from '@origin/shared/utils'; -import { - getAccount, - getPublicClient, - readContract, - simulateContract, - writeContract, -} from '@wagmi/core'; -import { erc20Abi, formatUnits } from 'viem'; - -import { GAS_BUFFER } from '../constants'; - -import type { - Allowance, - Approve, - EstimateAmount, - EstimateApprovalGas, - EstimateGas, - EstimateRoute, - Swap, -} from '@origin/shared/providers'; - -const curveConfig = { - routes: [ - tokens.mainnet.OETH.address, - contracts.mainnet.OETHCurvePool.address, - ETH_ADDRESS_CURVE, - '0xa1f8a6807c402e4a15ef4eba36528a3fed24e577', - tokens.mainnet.frxETH.address, - tokens.mainnet.sfrxETH.address, - tokens.mainnet.sfrxETH.address, - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - ], - params: [ - [1n, 0n, 1n, 1n, 2n], - [0n, 1n, 1n, 1n, 2n], - [0n, 0n, 8n, 0n, 0n], - [0n, 0n, 0n, 0n, 0n], - [0n, 0n, 0n, 0n, 0n], - ], -} as const; - -const estimateAmount: EstimateAmount = async (config, { amountIn }) => { - if (amountIn === 0n) { - return 0n; - } - - const amountOut = await readContract(config, { - address: contracts.mainnet.CurveRouter.address, - abi: contracts.mainnet.CurveRouter.abi, - functionName: 'get_dy', - args: [curveConfig.routes, curveConfig.params, amountIn], - }); - - return amountOut as unknown as bigint; -}; - -const estimateGas: EstimateGas = async ( - config, - { tokenOut, amountIn, amountOut, slippage }, -) => { - let gasEstimate = 0n; - const { address } = getAccount(config); - const publicClient = getPublicClient(config); - - if (amountIn === 0n || !publicClient) { - return gasEstimate; - } - - const minAmountOut = subPercentage( - [amountOut ?? 0n, tokenOut.decimals], - slippage, - ); - - try { - gasEstimate = await publicClient.estimateContractGas({ - address: contracts.mainnet.CurveRouter.address, - abi: contracts.mainnet.CurveRouter.abi, - functionName: 'exchange', - args: [curveConfig.routes, curveConfig.params, amountIn, minAmountOut[0]], - account: address ?? ETH_ADDRESS_CURVE, - }); - } catch (e) { - gasEstimate = 350000n; - } - - return gasEstimate; -}; - -const allowance: Allowance = async (config, { tokenIn }) => { - const { address } = getAccount(config); - - if (!address || !tokenIn?.address) { - return 0n; - } - - const allowance = await readContract(config, { - address: tokenIn.address, - abi: erc20Abi, - functionName: 'allowance', - args: [address, contracts.mainnet.CurveRouter.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 || !tokenIn?.address || !publicClient) { - return approvalEstimate; - } - - try { - approvalEstimate = await publicClient.estimateContractGas({ - address: tokenIn.address, - abi: erc20Abi, - functionName: 'approve', - args: [contracts.mainnet.CurveRouter.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 { request } = await simulateContract(config, { - address: tokenIn.address, - abi: erc20Abi, - functionName: 'approve', - args: [contracts.mainnet.CurveRouter.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 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 estimatedGas = await estimateGas(config, { - amountIn, - slippage, - tokenIn, - tokenOut, - amountOut, - }); - const gas = estimatedGas + (estimatedGas * GAS_BUFFER) / 100n; - - const { request } = await simulateContractWithTxTracker(config, { - address: contracts.mainnet.CurveRouter.address, - abi: contracts.mainnet.CurveRouter.abi, - functionName: 'exchange', - args: [curveConfig.routes, curveConfig.params, amountIn, minAmountOut[0]], - account: address, - 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/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/actions/swapZapperSfrxeth.ts b/libs/defi/oeth/src/swap/actions/swapZapperSfrxeth.ts deleted file mode 100644 index 4a32e5b45..000000000 --- a/libs/defi/oeth/src/swap/actions/swapZapperSfrxeth.ts +++ /dev/null @@ -1,225 +0,0 @@ -import { contracts, tokens } from '@origin/shared/contracts'; -import { simulateContractWithTxTracker } from '@origin/shared/providers'; -import { isNilOrEmpty, subPercentage } from '@origin/shared/utils'; -import { - getAccount, - getPublicClient, - readContract, - readContracts, - simulateContract, - writeContract, -} from '@wagmi/core'; -import { erc20Abi, formatUnits, maxUint256, parseUnits } 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, - { tokenOut, amountIn }, -) => { - if (amountIn === 0n) { - return 0n; - } - - const [previewRedeem, priceUnitMint] = await readContracts(config, { - contracts: [ - { - address: tokens.mainnet.sfrxETH.address, - abi: tokens.mainnet.sfrxETH.abi, - functionName: 'previewRedeem', - args: [amountIn], - }, - { - address: contracts.mainnet.OETHVault.address, - abi: contracts.mainnet.OETHVault.abi, - functionName: 'priceUnitMint', - args: [tokens.mainnet.frxETH.address], - }, - ], - }); - - return parseUnits( - ( - +formatUnits( - previewRedeem?.result as unknown as bigint, - tokens.mainnet.frxETH.decimals, - ) * - +formatUnits( - priceUnitMint?.result as unknown as bigint, - tokenOut.decimals, - ) - ).toString(), - tokenOut.decimals, - ); -}; - -const estimateGas: EstimateGas = async () => { - return 90000n; -}; - -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, amountOut }, -) => { - 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 sfrxETH is not approved`); - } - - const minAmountOut = subPercentage( - [amountOut ?? 0n, tokenOut.decimals], - slippage, - ); - - const estimatedGas = await estimateGas(config, { - amountIn, - slippage, - tokenIn, - tokenOut, - amountOut, - }); - const gas = estimatedGas + (estimatedGas * GAS_BUFFER) / 100n; - - const { request } = await simulateContractWithTxTracker(config, { - address: contracts.mainnet.OETHZapper.address, - abi: contracts.mainnet.OETHZapper.abi, - functionName: 'depositSFRXETH', - args: [amountIn, minAmountOut[0]], - 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/actions/unwrapWOETH.ts b/libs/defi/oeth/src/swap/actions/unwrapWOETH.ts deleted file mode 100644 index 59642c475..000000000 --- a/libs/defi/oeth/src/swap/actions/unwrapWOETH.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { tokens, whales } from '@origin/shared/contracts'; -import { simulateContractWithTxTracker } from '@origin/shared/providers'; -import { - getAccount, - getPublicClient, - readContract, - writeContract, -} from '@wagmi/core'; -import { formatUnits, maxUint256 } from 'viem'; - -import type { - Allowance, - Approve, - EstimateAmount, - EstimateApprovalGas, - EstimateGas, - EstimateRoute, - Swap, -} from '@origin/shared/providers'; - -const estimateAmount: EstimateAmount = async (config, { amountIn }) => { - if (amountIn === 0n) { - return 0n; - } - - const data = await readContract(config, { - address: tokens.mainnet.wOETH.address, - abi: tokens.mainnet.wOETH.abi, - functionName: 'convertToAssets', - args: [amountIn], - }); - - return data as unknown as bigint; -}; - -const estimateGas: EstimateGas = async (config, { amountIn }) => { - let gasEstimate = 0n; - - const publicClient = getPublicClient(config); - - if (amountIn === 0n) { - return gasEstimate; - } - - const { address } = getAccount(config); - - if (address) { - try { - gasEstimate = - (await publicClient?.estimateContractGas({ - address: tokens.mainnet.wOETH.address, - abi: tokens.mainnet.wOETH.abi, - functionName: 'redeem', - args: [amountIn, address, address], - account: address, - })) ?? 0n; - - return gasEstimate; - } catch {} - } - - try { - gasEstimate = - (await publicClient?.estimateContractGas({ - address: tokens.mainnet.wOETH.address, - abi: tokens.mainnet.wOETH.abi, - functionName: 'redeem', - args: [amountIn, whales.mainnet.wOETH, whales.mainnet.wOETH], - account: whales.mainnet.wOETH, - })) ?? 21000n; - } catch { - gasEstimate = 21000n; - } - - return gasEstimate; -}; - -const allowance: Allowance = async () => { - // Unwrap wOETH does not require approval - return maxUint256; -}; - -const estimateApprovalGas: EstimateApprovalGas = async () => { - // Unwrap wOETH does not require approval - return 0n; -}; - -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, { amountIn, tokenIn, tokenOut }), - ]); - - return { - ...route, - estimatedAmount, - gas, - approvalGas, - allowanceAmount, - rate: - +formatUnits(estimatedAmount, tokenOut.decimals) / - +formatUnits(amountIn, tokenIn.decimals), - }; -}; - -const approve: Approve = async () => { - // Unwrap wOETH does not require approval - return null; -}; - -const swap: Swap = async (config, { amountIn }) => { - const { address } = getAccount(config); - - if (amountIn === 0n || !address) { - return null; - } - - const { request } = await simulateContractWithTxTracker(config, { - address: tokens.mainnet.wOETH.address, - abi: tokens.mainnet.wOETH.abi, - functionName: 'redeem', - args: [amountIn, address, address], - }); - 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/wrapOETH.ts b/libs/defi/oeth/src/swap/actions/wrapOETH.ts deleted file mode 100644 index e4ae2b348..000000000 --- a/libs/defi/oeth/src/swap/actions/wrapOETH.ts +++ /dev/null @@ -1,211 +0,0 @@ -import { tokens, whales } 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 } from 'viem'; - -import type { - Allowance, - Approve, - EstimateAmount, - EstimateApprovalGas, - EstimateGas, - EstimateRoute, - Swap, -} from '@origin/shared/providers'; - -const estimateAmount: EstimateAmount = async (config, { amountIn }) => { - if (amountIn === 0n) { - return 0n; - } - - const data = await readContract(config, { - address: tokens.mainnet.wOETH.address, - abi: tokens.mainnet.wOETH.abi, - functionName: 'convertToShares', - args: [amountIn], - }); - - return data as unknown as bigint; -}; - -const estimateGas: EstimateGas = async (config, { amountIn }) => { - let gasEstimate = 0n; - - const publicClient = getPublicClient(config); - - if (amountIn === 0n) { - return gasEstimate; - } - - const { address } = getAccount(config); - - if (address) { - try { - gasEstimate = - (await publicClient?.estimateContractGas({ - address: tokens.mainnet.wOETH.address, - abi: tokens.mainnet.wOETH.abi, - functionName: 'deposit', - args: [amountIn, address], - account: address, - })) ?? 0n; - - return gasEstimate; - } catch {} - } - - try { - if (publicClient) { - gasEstimate = await publicClient.estimateContractGas({ - address: tokens.mainnet.wOETH.address, - abi: tokens.mainnet.wOETH.abi, - functionName: 'deposit', - args: [amountIn, whales.mainnet.OETH], - account: whales.mainnet.OETH, - }); - } - } catch { - gasEstimate = 21000n; - } - - return gasEstimate; -}; - -const allowance: Allowance = async (config, { tokenIn }) => { - const { address } = getAccount(config); - - if (!address || !tokenIn?.address) { - return 0n; - } - - const allowance = await readContract(config, { - address: tokenIn.address, - abi: erc20Abi, - functionName: 'allowance', - args: [address, tokens.mainnet.wOETH.address], - }); - - return allowance; -}; - -const estimateApprovalGas: EstimateApprovalGas = async ( - config, - { tokenIn, amountIn }, -) => { - let approvalEstimate = 0n; - const { address } = getAccount(config); - - if (amountIn === 0n || !address || !tokenIn?.address) { - return approvalEstimate; - } - - const publicClient = getPublicClient(config); - - try { - if (publicClient) { - approvalEstimate = await publicClient?.estimateContractGas({ - address: tokenIn.address, - abi: erc20Abi, - functionName: 'approve', - args: [tokens.mainnet.wOETH.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, amountIn }) => { - if (!tokenIn?.address) { - return null; - } - - const { request } = await simulateContract(config, { - address: tokenIn.address, - abi: erc20Abi, - functionName: 'approve', - args: [tokens.mainnet.wOETH.address, amountIn], - }); - const hash = await writeContract(config, request); - - return hash; -}; - -const swap: Swap = async (config, { tokenIn, tokenOut, amountIn }) => { - const { address } = getAccount(config); - - if (amountIn === 0n || isNilOrEmpty(address)) { - return null; - } - - const approved = await allowance(config, { tokenIn, tokenOut }); - - if (approved < amountIn) { - throw new Error(`Wrap OETH is not approved`); - } - - const { request } = await simulateContractWithTxTracker(config, { - address: tokens.mainnet.wOETH.address, - abi: tokens.mainnet.wOETH.abi, - functionName: 'deposit', - args: [amountIn, address], - }); - 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/ousd/src/swap/actions.ts b/libs/defi/ousd/src/swap/actions.ts new file mode 100644 index 000000000..9e9f466d4 --- /dev/null +++ b/libs/defi/ousd/src/swap/actions.ts @@ -0,0 +1,53 @@ +import { + mintVaultOusd, + swapCurveOusd, + swapFlipperOusd, + swapSushiswapOusd, + swapUniswapV2Ousd, + swapUniswapV3Ousd, + unwrapOusdWousd, + wrapOusdWousd, +} from '@origin/shared/routes'; +import { defineMessage } from 'react-intl'; + +import type { SwapApi } from '@origin/shared/providers'; + +import type { OusdSwapAction } from './types'; + +export const ousdSwapActions: Record = { + 'swap-flipper-ousd': { + ...swapFlipperOusd, + routeLabel: defineMessage({ defaultMessage: 'Swap via Flipper' }), + }, + 'mint-vault-ousd': { + ...mintVaultOusd, + routeLabel: defineMessage({ defaultMessage: 'Mint with Vault' }), + buttonLabel: defineMessage({ defaultMessage: 'Mint' }), + }, + 'swap-sushiswap-ousd': { + ...swapSushiswapOusd, + routeLabel: defineMessage({ defaultMessage: 'Swap via SushiSwap' }), + }, + 'swap-curve-ousd': { + ...swapCurveOusd, + routeLabel: defineMessage({ defaultMessage: 'Swap via Curve' }), + }, + 'swap-uniswap-v2-ousd': { + ...swapUniswapV2Ousd, + routeLabel: defineMessage({ defaultMessage: 'Swap via Uniswap V2' }), + }, + 'swap-uniswap-v3-ousd': { + ...swapUniswapV3Ousd, + routeLabel: defineMessage({ defaultMessage: 'Swap via Uniswap V3' }), + }, + 'wrap-ousd-wousd': { + ...wrapOusdWousd, + routeLabel: defineMessage({ defaultMessage: 'Wrap with Origin' }), + buttonLabel: defineMessage({ defaultMessage: 'Wrap' }), + }, + 'unwrap-ousd-wousd': { + ...unwrapOusdWousd, + routeLabel: defineMessage({ defaultMessage: 'Unwrap with Origin' }), + buttonLabel: defineMessage({ defaultMessage: 'Unwrap' }), + }, +}; diff --git a/libs/defi/ousd/src/swap/actions/index.ts b/libs/defi/ousd/src/swap/actions/index.ts deleted file mode 100644 index 75e2430d9..000000000 --- a/libs/defi/ousd/src/swap/actions/index.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { defineMessage } from 'react-intl'; - -import flipper from './flipper'; -import mintVault from './mintVault'; -import sushiswap from './sushiswap'; -import swapCurve from './swapCurve'; -import uniswapV2 from './uniswapV2'; -import uniswapV3 from './uniswapV3'; -import unwrapWOUSD from './unwrapWOUSD'; -import wrapOUSD from './wrapOUSD'; - -import type { SwapApi } from '@origin/shared/providers'; - -import type { OusdSwapAction } 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; - }, - routeLabel: defineMessage({ defaultMessage: 'Swap' }), - buttonLabel: defineMessage({ defaultMessage: 'Swap' }), -}; - -export const ousdSwapActions: Record = { - flipper: { - ...defaultApi, - ...flipper, - routeLabel: defineMessage({ defaultMessage: 'Swap via Flipper' }), - }, - 'mint-vault': { - ...defaultApi, - ...mintVault, - routeLabel: defineMessage({ defaultMessage: 'Mint with Vault' }), - buttonLabel: defineMessage({ defaultMessage: 'Mint' }), - }, - sushiswap: { - ...defaultApi, - ...sushiswap, - routeLabel: defineMessage({ defaultMessage: 'Swap via SushiSwap' }), - }, - 'swap-curve': { - ...defaultApi, - ...swapCurve, - routeLabel: defineMessage({ defaultMessage: 'Swap via Curve' }), - }, - 'uniswap-v2': { - ...defaultApi, - ...uniswapV2, - routeLabel: defineMessage({ defaultMessage: 'Swap via Uniswap V2' }), - }, - 'uniswap-v3': { - ...defaultApi, - ...uniswapV3, - routeLabel: defineMessage({ defaultMessage: 'Swap via Uniswap V3' }), - }, - 'wrap-ousd': { - ...defaultApi, - ...wrapOUSD, - routeLabel: defineMessage({ defaultMessage: 'Wrap with Origin' }), - buttonLabel: defineMessage({ defaultMessage: 'Wrap' }), - }, - 'unwrap-wousd': { - ...defaultApi, - ...unwrapWOUSD, - routeLabel: defineMessage({ defaultMessage: 'Unwrap with Origin' }), - buttonLabel: defineMessage({ defaultMessage: 'Unwrap' }), - }, -}; diff --git a/libs/defi/ousd/src/swap/actions/unwrapWOUSD.ts b/libs/defi/ousd/src/swap/actions/unwrapWOUSD.ts deleted file mode 100644 index 4257e2243..000000000 --- a/libs/defi/ousd/src/swap/actions/unwrapWOUSD.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { tokens, whales } from '@origin/shared/contracts'; -import { simulateContractWithTxTracker } from '@origin/shared/providers'; -import { - getAccount, - getPublicClient, - readContract, - writeContract, -} from '@wagmi/core'; -import { formatUnits, maxUint256 } from 'viem'; - -import type { - Allowance, - Approve, - EstimateAmount, - EstimateApprovalGas, - EstimateGas, - EstimateRoute, - Swap, -} from '@origin/shared/providers'; - -const estimateAmount: EstimateAmount = async (config, { amountIn }) => { - if (amountIn === 0n) { - return 0n; - } - - const data = await readContract(config, { - address: tokens.mainnet.wOUSD.address, - abi: tokens.mainnet.wOUSD.abi, - functionName: 'convertToAssets', - args: [amountIn], - }); - - return data; -}; - -const estimateGas: EstimateGas = async (config, { amountIn }) => { - let gasEstimate = 0n; - - const publicClient = getPublicClient(config); - - if (amountIn === 0n || !publicClient) { - return gasEstimate; - } - - const { address } = getAccount(config); - - if (address) { - try { - gasEstimate = await publicClient.estimateContractGas({ - address: tokens.mainnet.wOUSD.address, - abi: tokens.mainnet.wOUSD.abi, - functionName: 'redeem', - args: [amountIn, address, address], - account: address, - }); - - return gasEstimate; - } catch {} - } - - try { - gasEstimate = await publicClient.estimateContractGas({ - address: tokens.mainnet.wOUSD.address, - abi: tokens.mainnet.wOUSD.abi, - functionName: 'redeem', - args: [amountIn, whales.mainnet.wOUSD, whales.mainnet.wOUSD], - account: whales.mainnet.wOUSD, - }); - } catch { - gasEstimate = 21000n; - } - - return gasEstimate; -}; - -const allowance: Allowance = async () => { - // Unwrap wOUSD does not require approval - return maxUint256; -}; - -const estimateApprovalGas: EstimateApprovalGas = async () => { - // Unwrap wOUSD does not require approval - return 0n; -}; - -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 () => { - // Unwrap wOUSD does not require approval - return null; -}; - -const swap: Swap = async (config, { amountIn }) => { - const { address } = getAccount(config); - - if (amountIn === 0n || !address) { - return null; - } - - const { request } = await simulateContractWithTxTracker(config, { - address: tokens.mainnet.wOUSD.address, - abi: tokens.mainnet.wOUSD.abi, - functionName: 'redeem', - args: [amountIn, address, address], - }); - const hash = await writeContract(config, request); - - return hash; -}; - -export default { - estimateAmount, - estimateGas, - estimateRoute, - allowance, - estimateApprovalGas, - approve, - swap, -}; diff --git a/libs/defi/ousd/src/swap/actions/wrapOUSD.ts b/libs/defi/ousd/src/swap/actions/wrapOUSD.ts deleted file mode 100644 index e4709e20a..000000000 --- a/libs/defi/ousd/src/swap/actions/wrapOUSD.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { tokens, whales } from '@origin/shared/contracts'; -import { simulateContractWithTxTracker } from '@origin/shared/providers'; -import { isNilOrEmpty, ZERO_ADDRESS } from '@origin/shared/utils'; -import { - getAccount, - getPublicClient, - readContract, - simulateContract, - writeContract, -} from '@wagmi/core'; -import { erc20Abi, formatUnits } from 'viem'; - -import type { - Allowance, - Approve, - EstimateAmount, - EstimateApprovalGas, - EstimateGas, - EstimateRoute, - Swap, -} from '@origin/shared/providers'; - -const estimateAmount: EstimateAmount = async (config, { amountIn }) => { - if (amountIn === 0n) { - return 0n; - } - - const data = await readContract(config, { - address: tokens.mainnet.wOUSD.address, - abi: tokens.mainnet.wOUSD.abi, - functionName: 'convertToShares', - args: [amountIn], - }); - - return data; -}; - -const estimateGas: EstimateGas = async (config, { amountIn }) => { - let gasEstimate = 0n; - - const publicClient = getPublicClient(config); - - if (amountIn === 0n || !publicClient) { - return gasEstimate; - } - - const { address } = getAccount(config); - - if (address) { - try { - gasEstimate = await publicClient.estimateContractGas({ - address: tokens.mainnet.wOUSD.address, - abi: tokens.mainnet.wOUSD.abi, - functionName: 'deposit', - args: [amountIn, address], - account: address, - }); - - return gasEstimate; - } catch {} - } - - try { - gasEstimate = await publicClient.estimateContractGas({ - address: tokens.mainnet.wOUSD.address, - abi: tokens.mainnet.wOUSD.abi, - functionName: 'deposit', - args: [amountIn, whales.mainnet.OUSD], - account: whales.mainnet.OUSD, - }); - } catch { - gasEstimate = 21000n; - } - - return gasEstimate; -}; - -const allowance: Allowance = async (config, { tokenIn }) => { - const { address } = getAccount(config); - - if (!address || !tokenIn?.address) { - return 0n; - } - - const allowance = await readContract(config, { - address: tokenIn.address, - abi: tokenIn.abi, - functionName: 'allowance', - args: [address, tokens.mainnet.wOUSD.address], - }); - - return allowance as unknown as bigint; -}; - -const estimateApprovalGas: EstimateApprovalGas = async ( - config, - { tokenIn, amountIn }, -) => { - let approvalEstimate = 0n; - const { address } = getAccount(config); - const publicClient = getPublicClient(config); - - if (amountIn === 0n || !publicClient || !tokenIn?.address) { - return approvalEstimate; - } - - try { - approvalEstimate = await publicClient.estimateContractGas({ - address: tokenIn.address, - abi: erc20Abi, - functionName: 'approve', - args: [tokens.mainnet.wOUSD.address, amountIn], - account: address ?? ZERO_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, amountIn }) => { - if (!tokenIn?.address) { - return null; - } - - const { request } = await simulateContract(config, { - address: tokenIn.address, - abi: tokenIn.abi, - functionName: 'approve', - args: [tokens.mainnet.wOUSD.address, amountIn], - }); - const hash = await writeContract(config, request); - - return hash; -}; - -const swap: Swap = async (config, { tokenIn, tokenOut, amountIn }) => { - const { address } = getAccount(config); - - if (amountIn === 0n || isNilOrEmpty(address)) { - return null; - } - - const approved = await allowance(config, { tokenIn, tokenOut }); - - if (approved < amountIn) { - throw new Error(`wOUSD is not approved`); - } - - const { request } = await simulateContractWithTxTracker(config, { - address: tokens.mainnet.wOUSD.address, - abi: tokens.mainnet.wOUSD.abi, - functionName: 'deposit', - args: [amountIn, address], - }); - const hash = await writeContract(config, request); - - return hash; -}; - -export default { - estimateAmount, - estimateGas, - estimateRoute, - allowance, - estimateApprovalGas, - approve, - swap, -}; diff --git a/libs/defi/ousd/src/swap/constants.ts b/libs/defi/ousd/src/swap/constants.ts index a9a75a87e..96248fcf4 100644 --- a/libs/defi/ousd/src/swap/constants.ts +++ b/libs/defi/ousd/src/swap/constants.ts @@ -4,199 +4,196 @@ import type { SwapRoute } from '@origin/shared/providers'; import type { OusdSwapAction } from './types'; -export const GAS_BUFFER = 10n; // 10% -export const MAX_PRICE = 1.2; - export const ousdSwapRoutes: SwapRoute[] = [ // Mint DAI -> OUSD { tokenIn: tokens.mainnet.DAI, tokenOut: tokens.mainnet.OUSD, - action: 'flipper', + action: 'swap-flipper-ousd', noSlippage: true, }, { tokenIn: tokens.mainnet.DAI, tokenOut: tokens.mainnet.OUSD, - action: 'swap-curve', + action: 'swap-curve-ousd', }, { tokenIn: tokens.mainnet.DAI, tokenOut: tokens.mainnet.OUSD, - action: 'uniswap-v2', + action: 'swap-uniswap-v2-ousd', }, { tokenIn: tokens.mainnet.DAI, tokenOut: tokens.mainnet.OUSD, - action: 'uniswap-v3', + action: 'swap-uniswap-v3-ousd', }, { tokenIn: tokens.mainnet.DAI, tokenOut: tokens.mainnet.OUSD, - action: 'mint-vault', + action: 'mint-vault-ousd', }, { tokenIn: tokens.mainnet.DAI, tokenOut: tokens.mainnet.OUSD, - action: 'sushiswap', + action: 'swap-sushiswap-ousd', }, // Mint USDT -> OUSD { tokenIn: tokens.mainnet.USDT, tokenOut: tokens.mainnet.OUSD, - action: 'flipper', + action: 'swap-flipper-ousd', noSlippage: true, }, { tokenIn: tokens.mainnet.USDT, tokenOut: tokens.mainnet.OUSD, - action: 'swap-curve', + action: 'swap-curve-ousd', }, { tokenIn: tokens.mainnet.USDT, tokenOut: tokens.mainnet.OUSD, - action: 'uniswap-v2', + action: 'swap-uniswap-v2-ousd', }, { tokenIn: tokens.mainnet.USDT, tokenOut: tokens.mainnet.OUSD, - action: 'uniswap-v3', + action: 'swap-uniswap-v3-ousd', }, { tokenIn: tokens.mainnet.USDT, tokenOut: tokens.mainnet.OUSD, - action: 'mint-vault', + action: 'mint-vault-ousd', }, { tokenIn: tokens.mainnet.USDT, tokenOut: tokens.mainnet.OUSD, - action: 'sushiswap', + action: 'swap-sushiswap-ousd', }, // Mint USDC -> OUSD { tokenIn: tokens.mainnet.USDC, tokenOut: tokens.mainnet.OUSD, - action: 'flipper', + action: 'swap-flipper-ousd', noSlippage: true, }, { tokenIn: tokens.mainnet.USDC, tokenOut: tokens.mainnet.OUSD, - action: 'swap-curve', + action: 'swap-curve-ousd', }, { tokenIn: tokens.mainnet.USDC, tokenOut: tokens.mainnet.OUSD, - action: 'uniswap-v2', + action: 'swap-uniswap-v2-ousd', }, { tokenIn: tokens.mainnet.USDC, tokenOut: tokens.mainnet.OUSD, - action: 'uniswap-v3', + action: 'swap-uniswap-v3-ousd', }, { tokenIn: tokens.mainnet.USDC, tokenOut: tokens.mainnet.OUSD, - action: 'mint-vault', + action: 'mint-vault-ousd', }, { tokenIn: tokens.mainnet.USDC, tokenOut: tokens.mainnet.OUSD, - action: 'sushiswap', + action: 'swap-sushiswap-ousd', }, // Redeem OUSD -> DAI { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.DAI, - action: 'flipper', + action: 'swap-flipper-ousd', noSlippage: true, }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.DAI, - action: 'sushiswap', + action: 'swap-sushiswap-ousd', }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.DAI, - action: 'uniswap-v2', + action: 'swap-uniswap-v2-ousd', }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.DAI, - action: 'uniswap-v3', + action: 'swap-uniswap-v3-ousd', }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.DAI, - action: 'swap-curve', + action: 'swap-curve-ousd', }, // Redeem OUSD -> USDT { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.USDT, - action: 'flipper', + action: 'swap-flipper-ousd', noSlippage: true, }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.USDT, - action: 'sushiswap', + action: 'swap-sushiswap-ousd', }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.USDT, - action: 'uniswap-v2', + action: 'swap-uniswap-v2-ousd', }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.USDT, - action: 'uniswap-v3', + action: 'swap-uniswap-v3-ousd', }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.USDT, - action: 'swap-curve', + action: 'swap-curve-ousd', }, // Redeem OUSD -> USDC { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.USDC, - action: 'flipper', + action: 'swap-flipper-ousd', noSlippage: true, }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.USDC, - action: 'sushiswap', + action: 'swap-sushiswap-ousd', }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.USDC, - action: 'uniswap-v2', + action: 'swap-uniswap-v2-ousd', }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.USDC, - action: 'uniswap-v3', + action: 'swap-uniswap-v3-ousd', }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.USDC, - action: 'swap-curve', + action: 'swap-curve-ousd', }, // Wrap OUSD { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.wOUSD, - action: 'wrap-ousd', + action: 'wrap-ousd-wousd', noSlippage: true, }, // Unwrap wOUSD { tokenIn: tokens.mainnet.wOUSD, tokenOut: tokens.mainnet.OUSD, - action: 'unwrap-wousd', + action: 'unwrap-ousd-wousd', noSlippage: true, }, ]; diff --git a/libs/defi/ousd/src/swap/types.ts b/libs/defi/ousd/src/swap/types.ts index 8a3f003ff..5b4572001 100644 --- a/libs/defi/ousd/src/swap/types.ts +++ b/libs/defi/ousd/src/swap/types.ts @@ -1,9 +1,13 @@ -export type OusdSwapAction = - | 'flipper' - | 'uniswap-v2' - | 'uniswap-v3' - | 'sushiswap' - | 'mint-vault' - | 'swap-curve' - | 'wrap-ousd' - | 'unwrap-wousd'; +import type { OusdRoute } from '@origin/shared/routes'; + +export type OusdSwapAction = Extract< + OusdRoute, + | 'mint-vault-ousd' + | 'swap-flipper-ousd' + | 'swap-curve-ousd' + | 'swap-sushiswap-ousd' + | 'swap-uniswap-v2-ousd' + | 'swap-uniswap-v3-ousd' + | 'unwrap-ousd-wousd' + | 'wrap-ousd-wousd' +>; diff --git a/libs/oeth/redeem/src/actions.ts b/libs/oeth/redeem/src/actions.ts new file mode 100644 index 000000000..be64853b2 --- /dev/null +++ b/libs/oeth/redeem/src/actions.ts @@ -0,0 +1,19 @@ +import { redeemVaultOeth, SwapCurveOeth } 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 = { + 'swap-curve-oeth': { + ...SwapCurveOeth, + routeLabel: defineMessage({ defaultMessage: 'Swap via Curve' }), + buttonLabel: defineMessage({ defaultMessage: 'Redeem' }), + }, + 'redeem-vault-oeth': { + ...redeemVaultOeth, + routeLabel: defineMessage({ defaultMessage: 'Redeem via OETH Vault' }), + buttonLabel: defineMessage({ defaultMessage: 'Redeem' }), + }, +}; diff --git a/libs/oeth/redeem/src/actions/index.ts b/libs/oeth/redeem/src/actions/index.ts deleted file mode 100644 index 69f2c4671..000000000 --- a/libs/oeth/redeem/src/actions/index.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { defineMessage } from 'react-intl'; - -import redeemVault from './redeemVault'; -import swapCurve from './swapCurve'; - -import type { SwapApi } from '@origin/shared/providers'; - -import type { RedeemAction } 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: 'Swap' }), - }, - 'redeem-vault': { - ...defaultApi, - ...redeemVault, - routeLabel: defineMessage({ defaultMessage: 'Redeem via OETH Vault' }), - buttonLabel: defineMessage({ defaultMessage: 'Redeem' }), - }, -}; diff --git a/libs/oeth/redeem/src/actions/redeemVault.ts b/libs/oeth/redeem/src/actions/redeemVault.ts deleted file mode 100644 index 9671f1574..000000000 --- a/libs/oeth/redeem/src/actions/redeemVault.ts +++ /dev/null @@ -1,208 +0,0 @@ -import { contracts, tokens, whales } from '@origin/shared/contracts'; -import { simulateContractWithTxTracker } from '@origin/shared/providers'; -import { isNilOrEmpty, subtractSlippage } 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, - EstimateApprovalGas, - EstimateGas, - EstimateRoute, - IsRouteAvailable, - Swap, -} from '@origin/shared/providers'; -import type { EstimateAmount } from '@origin/shared/providers'; - -const isRouteAvailable: IsRouteAvailable = async ( - config, - { tokenIn, amountIn }, -) => { - try { - if (tokenIn?.address) { - const bal = await readContract(config, { - address: tokens.mainnet.WETH.address, - abi: tokens.mainnet.WETH.abi, - functionName: 'balanceOf', - args: [contracts.mainnet.OETHVault.address], - }); - - return amountIn <= bal; - } - } catch {} - - return false; -}; - -const estimateAmount: EstimateAmount = async (config, { amountIn }) => { - // 0.1% redeem fee - return amountIn - amountIn / 1000n; -}; - -const estimateGas: EstimateGas = async ( - config, - { tokenIn, tokenOut, amountIn, slippage, amountOut }, -) => { - let gasEstimate = 0n; - const publicClient = getPublicClient(config); - - if (amountIn === 0n || !publicClient || !tokenIn?.address) { - return gasEstimate; - } - - const minAmountOut = subtractSlippage(amountOut, tokenOut.decimals, slippage); - - try { - gasEstimate = await publicClient.estimateContractGas({ - address: contracts.mainnet.OETHVault.address, - abi: contracts.mainnet.OETHVault.abi, - functionName: 'redeem', - args: [amountIn, minAmountOut], - account: whales.mainnet.OETH, - }); - } catch { - gasEstimate = 1500000n; - } - - return gasEstimate; -}; - -const allowance: Allowance = async (config, { tokenIn }) => { - return maxUint256; -}; - -const estimateApprovalGas: EstimateApprovalGas = async ( - config, - { tokenIn, amountIn }, -) => { - let approvalEstimate = 0n; - const { address } = getAccount(config); - const publicClient = getPublicClient(config); - - if (amountIn === 0n || !address || !publicClient || !tokenIn?.address) { - return approvalEstimate; - } - - try { - approvalEstimate = await publicClient.estimateContractGas({ - address: tokenIn.address, - abi: erc20Abi, - functionName: 'approve', - args: [contracts.mainnet.OETHVault.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, - slippage, - amountOut: estimatedAmount, - }); - - 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) { - return null; - } - - const { request } = await simulateContract(config, { - address: tokenIn.address, - abi: erc20Abi, - functionName: 'approve', - args: [contracts.mainnet.OETHVault.address, amountIn], - }); - const hash = await writeContract(config, request); - - return hash; -}; - -const swap: Swap = async ( - config, - { tokenIn, tokenOut, amountIn, slippage, amountOut }, -) => { - const { address } = getAccount(config); - - if (amountIn === 0n || isNilOrEmpty(address)) { - return null; - } - - const minAmountOut = subtractSlippage(amountOut, tokenOut.decimals, slippage); - - const estimatedGas = await estimateGas(config, { - amountIn, - slippage, - tokenIn, - tokenOut, - amountOut, - }); - const gas = estimatedGas + (estimatedGas * GAS_BUFFER) / 100n; - - const { request } = await simulateContractWithTxTracker(config, { - address: contracts.mainnet.OETHVault.address, - abi: contracts.mainnet.OETHVault.abi, - functionName: 'redeem', - args: [amountIn, minAmountOut], - gas, - }); - const hash = await writeContract(config, request); - - return hash; -}; - -export default { - isRouteAvailable, - estimateAmount, - estimateGas, - estimateRoute, - allowance, - estimateApprovalGas, - approve, - swap, -}; diff --git a/libs/oeth/redeem/src/actions/swapCurve/curveRoutes.ts b/libs/oeth/redeem/src/actions/swapCurve/curveRoutes.ts deleted file mode 100644 index a22eb6515..000000000 --- a/libs/oeth/redeem/src/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/oeth/redeem/src/actions/swapCurve/index.ts b/libs/oeth/redeem/src/actions/swapCurve/index.ts deleted file mode 100644 index 1b5b615b1..000000000 --- a/libs/oeth/redeem/src/actions/swapCurve/index.ts +++ /dev/null @@ -1,310 +0,0 @@ -import { queryClient } from '@origin/oeth/shared'; -import { - isNativeCurrency, - simulateContractWithTxTracker, - useCurve, -} from '@origin/shared/providers'; -import { - ETH_ADDRESS_CURVE, - isNilOrEmpty, - subtractSlippage, - 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 = subtractSlippage(amountOut, 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, - ], - 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 = subtractSlippage(amountOut, 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], - 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/oeth/redeem/src/components/RedeemActionCard.tsx b/libs/oeth/redeem/src/components/RedeemActionCard.tsx index d7062274b..c93b712a0 100644 --- a/libs/oeth/redeem/src/components/RedeemActionCard.tsx +++ b/libs/oeth/redeem/src/components/RedeemActionCard.tsx @@ -15,10 +15,10 @@ import { useIntl } from 'react-intl'; import type { CardProps, StackProps, TypographyProps } from '@mui/material'; -import type { RedeemAction } from '../types'; +import type { OethRedeemAction } from '../types'; export type RedeemActionCardProps = { - action: RedeemAction; + action: OethRedeemAction; } & Omit; export const RedeemActionCard = ({ @@ -134,7 +134,7 @@ export const RedeemActionCard = ({ {intl.formatMessage(routeLabel)} - {action === 'redeem-vault' ? ( + {action === 'redeem-vault-oeth' ? ( ) : ( diff --git a/libs/oeth/redeem/src/components/Swapper.tsx b/libs/oeth/redeem/src/components/Swapper.tsx index 863bc32ea..f9be3af11 100644 --- a/libs/oeth/redeem/src/components/Swapper.tsx +++ b/libs/oeth/redeem/src/components/Swapper.tsx @@ -352,8 +352,8 @@ function SwapperWrapped({ {intl.formatMessage({ defaultMessage: 'Route' })} - - + + {intl.formatMessage({ defaultMessage: 'Receive amount' })} diff --git a/libs/oeth/redeem/src/constants.ts b/libs/oeth/redeem/src/constants.ts index 38784cab8..59cae9b81 100644 --- a/libs/oeth/redeem/src/constants.ts +++ b/libs/oeth/redeem/src/constants.ts @@ -2,19 +2,17 @@ import { tokens } from '@origin/shared/contracts'; import type { SwapRoute } from '@origin/shared/providers'; -import type { RedeemAction } from './types'; +import type { OethRedeemAction } from './types'; -export const GAS_BUFFER = 10n; // 10% - -export const redeemRoutes: SwapRoute[] = [ +export const redeemRoutes: SwapRoute[] = [ { tokenIn: tokens.mainnet.OETH, tokenOut: tokens.mainnet.WETH, - action: 'swap-curve', + action: 'swap-curve-oeth', }, { tokenIn: tokens.mainnet.OETH, tokenOut: tokens.mainnet.WETH, - action: 'redeem-vault', + action: 'redeem-vault-oeth', }, ]; diff --git a/libs/oeth/redeem/src/types.ts b/libs/oeth/redeem/src/types.ts index b6a371a88..47c13bdfc 100644 --- a/libs/oeth/redeem/src/types.ts +++ b/libs/oeth/redeem/src/types.ts @@ -1 +1,6 @@ -export type RedeemAction = 'swap-curve' | 'redeem-vault'; +import type { OethRoute } from '@origin/shared/routes'; + +export type OethRedeemAction = Extract< + OethRoute, + 'swap-curve-oeth' | 'redeem-vault-oeth' +>; diff --git a/libs/oeth/swap/src/actions.ts b/libs/oeth/swap/src/actions.ts new file mode 100644 index 000000000..7f6b0414d --- /dev/null +++ b/libs/oeth/swap/src/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 swapActions: 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/oeth/swap/src/actions/index.ts b/libs/oeth/swap/src/actions/index.ts deleted file mode 100644 index fb0dcfafd..000000000 --- a/libs/oeth/swap/src/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 { SwapAction } 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 swapActions: Record = { - 'swap-curve': { - ...defaultApi, - ...swapCurve, - routeLabel: defineMessage({ defaultMessage: 'Swap with Curve' }), - buttonLabel: defineMessage({ defaultMessage: 'Swap' }), - }, - 'swap-curve-eth': { - ...defaultApi, - ...swapCurveEth, - routeLabel: defineMessage({ defaultMessage: 'Swap with CurvePool' }), - buttonLabel: defineMessage({ defaultMessage: 'Swap' }), - }, - 'swap-curve-sfrxeth': { - ...defaultApi, - ...swapCurveSfrxeth, - routeLabel: defineMessage({ defaultMessage: 'Swap with 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/oeth/swap/src/actions/swapCurve/curveRoutes.ts b/libs/oeth/swap/src/actions/swapCurve/curveRoutes.ts deleted file mode 100644 index a22eb6515..000000000 --- a/libs/oeth/swap/src/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/oeth/swap/src/actions/swapCurveEth.ts b/libs/oeth/swap/src/actions/swapCurveEth.ts deleted file mode 100644 index ee9ad8ee6..000000000 --- a/libs/oeth/swap/src/actions/swapCurveEth.ts +++ /dev/null @@ -1,228 +0,0 @@ -import { queryClient } from '@origin/oeth/shared'; -import { contracts } from '@origin/shared/contracts'; -import { - isNativeCurrency, - simulateContractWithTxTracker, - useCurve, -} from '@origin/shared/providers'; -import { ETH_ADDRESS_CURVE, subtractSlippage } from '@origin/shared/utils'; -import { - getAccount, - getPublicClient, - readContract, - writeContract, -} from '@wagmi/core'; -import { formatUnits, isAddressEqual, 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, - { tokenIn, tokenOut, amountIn }, -) => { - if (amountIn === 0n) { - return 0n; - } - - const curve = await queryClient.fetchQuery({ - queryKey: useCurve.getKey(), - queryFn: useCurve.fetcher(config), - staleTime: Infinity, - }); - const amountOut = await readContract(config, { - address: curve.CurveRegistryExchange.address, - abi: curve.CurveRegistryExchange.abi, - functionName: 'get_exchange_amount', - args: [ - contracts.mainnet.OETHCurvePool.address, - tokenIn.address ?? ETH_ADDRESS_CURVE, - tokenOut.address ?? ETH_ADDRESS_CURVE, - amountIn, - ], - chainId: curve.CurveRegistryExchange.chainId, - }); - - return amountOut as unknown as bigint; -}; - -const estimateGas: EstimateGas = async ( - config, - { tokenIn, tokenOut, amountIn, amountOut, slippage }, -) => { - let gasEstimate = 0n; - const publicClient = getPublicClient(config, { - chainId: contracts.mainnet.OETHCurvePool.chainId, - }); - - if (amountIn === 0n || !publicClient) { - return gasEstimate; - } - - const { address } = getAccount(config); - - const minAmountOut = subtractSlippage(amountOut, tokenOut.decimals, slippage); - - const curve = await queryClient.fetchQuery({ - queryKey: useCurve.getKey(), - queryFn: useCurve.fetcher(config), - staleTime: Infinity, - }); - - const isTokenInNative = isNativeCurrency(tokenIn); - - try { - gasEstimate = await publicClient.estimateContractGas({ - address: contracts.mainnet.OETHCurvePool.address, - abi: contracts.mainnet.OETHCurvePool.abi, - functionName: 'exchange', - args: [ - BigInt( - curve.OethPoolUnderlyings.findIndex((t) => - isAddressEqual(t, tokenIn.address ?? ETH_ADDRESS_CURVE), - ), - ), - BigInt( - curve.OethPoolUnderlyings.findIndex((t) => - isAddressEqual(t, tokenOut.address ?? ETH_ADDRESS_CURVE), - ), - ), - amountIn, - minAmountOut, - ], - ...(isTokenInNative && { value: amountIn }), - account: address ?? ETH_ADDRESS_CURVE, - }); - } catch (e) { - gasEstimate = 180000n; - } - - return gasEstimate; -}; - -const allowance: Allowance = async () => { - // ETH doesn't need approval - return maxUint256; -}; - -const estimateApprovalGas: EstimateApprovalGas = async () => { - // ETH doesn't need approval - return 0n; -}; - -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, { tokenIn, tokenOut, amountIn }), - ]); - 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 () => { - // ETH doesn't need approval - return null; -}; - -const swap: Swap = async ( - config, - { tokenIn, tokenOut, amountIn, amountOut, slippage }, -) => { - if (amountIn === 0n) { - return null; - } - - const minAmountOut = subtractSlippage(amountOut, tokenOut.decimals, slippage); - - const curve = await queryClient.fetchQuery({ - queryKey: useCurve.getKey(), - queryFn: useCurve.fetcher(config), - staleTime: Infinity, - }); - 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: contracts.mainnet.OETHCurvePool.address, - abi: contracts.mainnet.OETHCurvePool.abi, - functionName: 'exchange', - args: [ - BigInt( - curve.OethPoolUnderlyings.findIndex((t) => - isAddressEqual(t, tokenIn.address ?? ETH_ADDRESS_CURVE), - ), - ), - BigInt( - curve.OethPoolUnderlyings.findIndex((t) => - isAddressEqual(t, tokenOut.address ?? ETH_ADDRESS_CURVE), - ), - ), - amountIn, - minAmountOut, - ], - gas, - chainId: contracts.mainnet.OETHCurvePool.chainId, - ...(isTokenInNative && { value: amountIn }), - }); - const hash = await writeContract(config, request); - - return hash; -}; - -export default { - estimateAmount, - estimateGas, - estimateRoute, - allowance, - estimateApprovalGas, - approve, - swap, -}; diff --git a/libs/oeth/swap/src/constants.ts b/libs/oeth/swap/src/constants.ts index 116563370..92d66b662 100644 --- a/libs/oeth/swap/src/constants.ts +++ b/libs/oeth/swap/src/constants.ts @@ -2,118 +2,51 @@ import { tokens } from '@origin/shared/contracts'; import type { SwapRoute } from '@origin/shared/providers'; -import type { SwapAction } from './types'; +import type { OethSwapAction } from './types'; -export const GAS_BUFFER = 10n; // 10% - -export const swapRoutes: SwapRoute[] = [ +export const swapRoutes: 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', }, { tokenIn: tokens.mainnet.WETH, tokenOut: tokens.mainnet.OETH, - action: 'mint-vault', + action: 'mint-vault-oeth', }, { 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', - // }, - // { - // 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', - // }, - // { - // tokenIn: tokens.mainnet.frxETH, - // tokenOut: tokens.mainnet.OETH, - // action: 'mint-vault', - // }, - // { - // tokenIn: tokens.mainnet.frxETH, - // tokenOut: tokens.mainnet.OETH, - // action: 'swap-curve', - // }, - // { - // tokenIn: tokens.mainnet.sfrxETH, - // tokenOut: tokens.mainnet.OETH, - // action: 'swap-zapper-sfrxeth', - // }, // 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', }, // Unwrap { tokenIn: tokens.mainnet.wOETH, tokenOut: tokens.mainnet.OETH, - action: 'unwrap-woeth', + action: 'unwrap-oeth-woeth', }, ]; diff --git a/libs/oeth/swap/src/types.ts b/libs/oeth/swap/src/types.ts index 6a8df56ac..a4f9e4027 100644 --- a/libs/oeth/swap/src/types.ts +++ b/libs/oeth/swap/src/types.ts @@ -1,9 +1,13 @@ -export type SwapAction = - | '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/ousd/swap/src/actions.ts b/libs/ousd/swap/src/actions.ts new file mode 100644 index 000000000..ffee36fb0 --- /dev/null +++ b/libs/ousd/swap/src/actions.ts @@ -0,0 +1,53 @@ +import { + mintVaultOusd, + swapCurveOusd, + swapFlipperOusd, + swapSushiswapOusd, + swapUniswapV2Ousd, + swapUniswapV3Ousd, + unwrapOusdWousd, + wrapOusdWousd, +} from '@origin/shared/routes'; +import { defineMessage } from 'react-intl'; + +import type { SwapApi } from '@origin/shared/providers'; + +import type { SwapAction } from './types'; + +export const swapActions: Record = { + 'swap-flipper-ousd': { + ...swapFlipperOusd, + routeLabel: defineMessage({ defaultMessage: 'Swap via Flipper' }), + }, + 'mint-vault-ousd': { + ...mintVaultOusd, + routeLabel: defineMessage({ defaultMessage: 'Mint with Vault' }), + buttonLabel: defineMessage({ defaultMessage: 'Mint' }), + }, + 'swap-sushiswap-ousd': { + ...swapSushiswapOusd, + routeLabel: defineMessage({ defaultMessage: 'Swap via SushiSwap' }), + }, + 'swap-curve-ousd': { + ...swapCurveOusd, + routeLabel: defineMessage({ defaultMessage: 'Swap via Curve' }), + }, + 'swap-uniswap-v2-ousd': { + ...swapUniswapV2Ousd, + routeLabel: defineMessage({ defaultMessage: 'Swap via Uniswap V2' }), + }, + 'swap-uniswap-v3-ousd': { + ...swapUniswapV3Ousd, + routeLabel: defineMessage({ defaultMessage: 'Swap via Uniswap V3' }), + }, + 'wrap-ousd-wousd': { + ...wrapOusdWousd, + routeLabel: defineMessage({ defaultMessage: 'Wrap with Origin' }), + buttonLabel: defineMessage({ defaultMessage: 'Wrap' }), + }, + 'unwrap-ousd-wousd': { + ...unwrapOusdWousd, + routeLabel: defineMessage({ defaultMessage: 'Unwrap with Origin' }), + buttonLabel: defineMessage({ defaultMessage: 'Unwrap' }), + }, +}; diff --git a/libs/ousd/swap/src/actions/flipper.ts b/libs/ousd/swap/src/actions/flipper.ts deleted file mode 100644 index c3c4704b7..000000000 --- a/libs/ousd/swap/src/actions/flipper.ts +++ /dev/null @@ -1,216 +0,0 @@ -import { contracts, tokens } from '@origin/shared/contracts'; -import { simulateContractWithTxTracker } from '@origin/shared/providers'; -import { scale, ZERO_ADDRESS } from '@origin/shared/utils'; -import { - getAccount, - getPublicClient, - readContract, - simulateContract, - writeContract, -} from '@wagmi/core'; -import { formatUnits } from 'viem'; - -import type { Token } from '@origin/shared/contracts'; -import type { - Allowance, - Approve, - EstimateAmount, - EstimateApprovalGas, - EstimateRoute, - IsRouteAvailable, - Swap, -} from '@origin/shared/providers'; - -const getFunctionName = (tokenIn: Token, tokenOut: Token) => { - if (tokenIn.symbol === tokens.mainnet.OUSD.symbol) { - return { - [tokens.mainnet.DAI.symbol]: 'sellOusdForDai' as const, - [tokens.mainnet.USDT.symbol]: 'sellOusdForUsdt' as const, - [tokens.mainnet.USDC.symbol]: 'sellOusdForUsdc' as const, - }[tokenOut.symbol]; - } else if (tokenOut.symbol === tokens.mainnet.OUSD.symbol) { - return { - [tokens.mainnet.DAI.symbol]: 'buyOusdWithDai' as const, - [tokens.mainnet.USDT.symbol]: 'buyOusdWithUsdt' as const, - [tokens.mainnet.USDC.symbol]: 'buyOusdWithUsdc' as const, - }[tokenIn.symbol]; - } -}; - -const isRouteAvailable: IsRouteAvailable = async ( - config, - { amountIn, tokenIn, tokenOut }, -) => { - const amtIn = +formatUnits(amountIn, tokenIn.decimals); - - if (amtIn > 25000 || !tokenOut?.address) { - return false; - } - - try { - const balance = await readContract(config, { - address: tokenOut.address, - abi: tokenOut.abi, - functionName: 'balanceOf', - args: [contracts.mainnet.OUSDFlipper.address], - }); - - const bal = +formatUnits(balance as unknown as bigint, tokenOut.decimals); - - return bal > amtIn; - } catch {} - - return false; -}; - -const estimateAmount: EstimateAmount = async ( - config, - { amountIn, tokenIn, tokenOut }, -) => { - const publicClient = getPublicClient(config); - const functionName = getFunctionName(tokenIn, tokenOut); - - try { - if (publicClient && functionName) { - const scaledAmount = scale(amountIn, tokenIn.decimals, 18); - const estimate = ( - await publicClient.simulateContract({ - address: contracts.mainnet.OUSDFlipper.address, - abi: contracts.mainnet.OUSDFlipper.abi, - functionName, - args: [scaledAmount], - }) - )?.result; - - return scale(estimate as unknown as bigint, 18, tokenIn.decimals); - } - } catch {} - - return scale(amountIn, tokenIn.decimals, tokenOut.decimals); -}; - -const estimateGas = async () => { - return 90000n; -}; - -const estimateRoute: EstimateRoute = async ( - config, - { tokenIn, tokenOut, amountIn, route, slippage }, -) => { - const [estimatedAmount, gas, allowanceAmount, approvalGas] = - await Promise.all([ - estimateAmount(config, { tokenIn, tokenOut, amountIn }), - estimateGas(), - allowance(config, { tokenIn, tokenOut }), - estimateApprovalGas(config, { amountIn, tokenIn, tokenOut }), - ]); - - return { - ...route, - estimatedAmount, - allowanceAmount, - approvalGas, - gas, - rate: - +formatUnits(estimatedAmount, tokenOut.decimals) / - +formatUnits(amountIn, tokenIn.decimals), - }; -}; - -const allowance: Allowance = async (config, { tokenIn }) => { - const { address } = getAccount(config); - - if (!address || !tokenIn?.address) { - return 0n; - } - - const allowance = await readContract(config, { - address: tokenIn.address, - abi: tokenIn.abi, - functionName: 'allowance', - args: [address, contracts.mainnet.OUSDFlipper.address], - }); - - return allowance as unknown as bigint; -}; - -const estimateApprovalGas: EstimateApprovalGas = async ( - config, - { tokenIn, amountIn }, -) => { - let approvalEstimate = 0n; - const { address } = getAccount(config); - const publicClient = getPublicClient(config); - - if (amountIn === 0n || !publicClient || !tokenIn?.address) { - return approvalEstimate; - } - - try { - approvalEstimate = await publicClient.estimateContractGas({ - address: tokenIn.address, - abi: tokenIn.abi, - functionName: 'approve', - args: [contracts.mainnet.OUSDFlipper.address, amountIn], - account: address ?? ZERO_ADDRESS, - }); - } catch { - approvalEstimate = 60000n; - } - - return approvalEstimate; -}; - -const approve: Approve = async (config, { tokenIn, amountIn }) => { - if (!tokenIn?.address) { - return null; - } - - const { request } = await simulateContract(config, { - address: tokenIn.address, - abi: tokenIn.abi, - functionName: 'approve', - args: [contracts.mainnet.OUSDFlipper.address, amountIn], - }); - const hash = await writeContract(config, request); - - return hash; -}; - -const swap: Swap = async (config, { tokenIn, tokenOut, amountIn }) => { - const { address } = getAccount(config); - const functionName = getFunctionName(tokenIn, tokenOut); - - if (amountIn === 0n || !address || !functionName) { - return null; - } - - const approved = await allowance(config, { tokenIn, tokenOut }); - - if (approved < amountIn) { - throw new Error(`Flipper is not approved`); - } - - const scaledAmount = scale(amountIn, tokenIn.decimals, 18); - - const { request } = await simulateContractWithTxTracker(config, { - address: contracts.mainnet.OUSDFlipper.address, - abi: contracts.mainnet.OUSDFlipper.abi, - functionName, - args: [scaledAmount], - }); - const hash = await writeContract(config, request); - - return hash; -}; - -export default { - isRouteAvailable, - estimateAmount, - estimateGas, - estimateRoute, - allowance, - estimateApprovalGas, - approve, - swap, -}; diff --git a/libs/ousd/swap/src/actions/index.ts b/libs/ousd/swap/src/actions/index.ts deleted file mode 100644 index da3b430ce..000000000 --- a/libs/ousd/swap/src/actions/index.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { defineMessage } from 'react-intl'; - -import flipper from './flipper'; -import mintVault from './mintVault'; -import sushiswap from './sushiswap'; -import swapCurve from './swapCurve'; -import uniswapV2 from './uniswapV2'; -import uniswapV3 from './uniswapV3'; -import unwrapWOUSD from './unwrapWOUSD'; -import wrapOUSD from './wrapOUSD'; - -import type { SwapApi } from '@origin/shared/providers'; - -import type { SwapAction } 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; - }, - routeLabel: defineMessage({ defaultMessage: 'Swap' }), - buttonLabel: defineMessage({ defaultMessage: 'Swap' }), -}; - -export const swapActions: Record = { - flipper: { - ...defaultApi, - ...flipper, - routeLabel: defineMessage({ defaultMessage: 'Flipper' }), - }, - 'mint-vault': { - ...defaultApi, - ...mintVault, - routeLabel: defineMessage({ defaultMessage: 'Origin Vault' }), - buttonLabel: defineMessage({ defaultMessage: 'Mint' }), - }, - sushiswap: { - ...defaultApi, - ...sushiswap, - routeLabel: defineMessage({ defaultMessage: 'SushiSwap' }), - }, - 'swap-curve': { - ...defaultApi, - ...swapCurve, - routeLabel: defineMessage({ defaultMessage: 'Curve' }), - }, - 'uniswap-v2': { - ...defaultApi, - ...uniswapV2, - routeLabel: defineMessage({ defaultMessage: 'Uniswap V2' }), - }, - 'uniswap-v3': { - ...defaultApi, - ...uniswapV3, - routeLabel: defineMessage({ defaultMessage: 'Uniswap V3' }), - }, - 'wrap-ousd': { - ...defaultApi, - ...wrapOUSD, - routeLabel: defineMessage({ defaultMessage: 'Wrap with Origin' }), - buttonLabel: defineMessage({ defaultMessage: 'Wrap' }), - }, - 'unwrap-wousd': { - ...defaultApi, - ...unwrapWOUSD, - routeLabel: defineMessage({ defaultMessage: 'Unwrap with Origin' }), - buttonLabel: defineMessage({ defaultMessage: 'Unwrap' }), - }, -}; diff --git a/libs/ousd/swap/src/actions/mintVault.ts b/libs/ousd/swap/src/actions/mintVault.ts deleted file mode 100644 index a8e097448..000000000 --- a/libs/ousd/swap/src/actions/mintVault.ts +++ /dev/null @@ -1,286 +0,0 @@ -import { queryClient } from '@origin/ousd/shared'; -import { contracts } from '@origin/shared/contracts'; -import { simulateContractWithTxTracker } from '@origin/shared/providers'; -import { - isNilOrEmpty, - subtractSlippage, - ZERO_ADDRESS, -} from '@origin/shared/utils'; -import { - getAccount, - getPublicClient, - readContract, - readContracts, - simulateContract, - writeContract, -} from '@wagmi/core'; -import { formatUnits, parseUnits } from 'viem'; - -import { GAS_BUFFER } from '../constants'; - -import type { - Allowance, - Approve, - EstimateAmount, - EstimateApprovalGas, - EstimateGas, - EstimateRoute, - IsRouteAvailable, - Swap, -} from '@origin/shared/providers'; - -const isRouteAvailable: IsRouteAvailable = async ( - config, - { amountIn, tokenIn }, -) => { - try { - if (tokenIn?.address) { - const priceUnitMint = await readContract(config, { - address: contracts.mainnet.OUSDVault.address, - abi: contracts.mainnet.OUSDVault.abi, - functionName: 'priceUnitMint', - args: [tokenIn.address], - }); - - return ( - +formatUnits(amountIn, tokenIn.decimals) * - +formatUnits(priceUnitMint, 18) > - +formatUnits(1n, tokenIn.decimals) - ); - } - } catch {} - - return false; -}; - -const estimateAmount: EstimateAmount = async ( - config, - { amountIn, tokenIn, tokenOut }, -) => { - if (amountIn === 0n || !tokenIn?.address) { - return 0n; - } - - const priceUnitMint = await readContract(config, { - address: contracts.mainnet.OUSDVault.address, - abi: contracts.mainnet.OUSDVault.abi, - functionName: 'priceUnitMint', - args: [tokenIn.address], - }); - - return parseUnits( - ( - +formatUnits(amountIn, tokenIn.decimals) * +formatUnits(priceUnitMint, 18) - ).toString(), - tokenOut.decimals, - ); -}; - -const estimateGas: EstimateGas = async ( - config, - { tokenIn, tokenOut, amountIn, slippage, amountOut }, -) => { - let gasEstimate = 0n; - const publicClient = getPublicClient(config); - - if (amountIn === 0n || !publicClient || !tokenIn?.address) { - return gasEstimate; - } - - const { address } = getAccount(config); - - const minAmountOut = subtractSlippage(amountOut, tokenOut.decimals, slippage); - - try { - gasEstimate = await publicClient.estimateContractGas({ - address: contracts.mainnet.OUSDVault.address, - abi: contracts.mainnet.OUSDVault.abi, - functionName: 'mint', - args: [tokenIn.address, amountIn, minAmountOut], - account: address ?? ZERO_ADDRESS, - }); - - return gasEstimate; - } catch {} - - const [rebaseThreshold, autoAllocateThreshold] = await queryClient.fetchQuery( - { - queryKey: ['vault-info', tokenOut.address], - queryFn: () => - readContracts(config, { - contracts: [ - { - address: contracts.mainnet.OUSDVault.address, - abi: contracts.mainnet.OUSDVault.abi, - functionName: 'rebaseThreshold', - }, - { - address: contracts.mainnet.OUSDVault.address, - abi: contracts.mainnet.OUSDVault.abi, - functionName: 'autoAllocateThreshold', - }, - ], - }), - staleTime: Infinity, - }, - ); - - gasEstimate = 220_000n; - if (amountIn > (autoAllocateThreshold?.result ?? 0n)) { - gasEstimate = 2_900_000n; - } else if (amountIn > (rebaseThreshold?.result ?? 0n)) { - gasEstimate = 510_000n; - } - - return gasEstimate; -}; - -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, - slippage, - amountOut: estimatedAmount, - }); - - return { - ...route, - estimatedAmount, - gas, - approvalGas, - allowanceAmount, - rate: - +formatUnits(estimatedAmount, tokenOut.decimals) / - +formatUnits(amountIn, tokenIn.decimals), - }; -}; - -const allowance: Allowance = async (config, { tokenIn }) => { - const { address } = getAccount(config); - - if (!address || !tokenIn?.address) { - return 0n; - } - - const allowance = await readContract(config, { - address: tokenIn.address, - abi: tokenIn.abi, - functionName: 'allowance', - args: [address, contracts.mainnet.OUSDVault.address], - }); - - return allowance as unknown as bigint; -}; - -const estimateApprovalGas: EstimateApprovalGas = async ( - config, - { tokenIn, amountIn }, -) => { - let approvalEstimate = 0n; - const { address } = getAccount(config); - const publicClient = getPublicClient(config); - - if (amountIn === 0n || !publicClient || !tokenIn?.address) { - return approvalEstimate; - } - - try { - approvalEstimate = await publicClient.estimateContractGas({ - address: tokenIn.address, - abi: tokenIn.abi, - functionName: 'approve', - args: [contracts.mainnet.OUSDVault.address, amountIn], - account: address ?? ZERO_ADDRESS, - }); - } catch { - approvalEstimate = 200000n; - } - - return approvalEstimate; -}; - -const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { - if (amountIn === 0n || !tokenIn?.address) { - return null; - } - - const { request } = await simulateContract(config, { - address: tokenIn.address, - abi: tokenIn.abi, - functionName: 'approve', - args: [contracts.mainnet.OUSDVault.address, amountIn], - }); - const hash = await writeContract(config, request); - - return hash; -}; - -const swap: Swap = async ( - config, - { tokenIn, tokenOut, amountIn, slippage, amountOut }, -) => { - const { address } = getAccount(config); - - if (amountIn === 0n || isNilOrEmpty(address)) { - return null; - } - - const approved = await allowance(config, { tokenIn, tokenOut }); - - if (approved < amountIn) { - throw new Error(`Mint vault is not approved`); - } - - const minAmountOut = subtractSlippage(amountOut, tokenOut.decimals, slippage); - - const estimatedGas = await estimateGas(config, { - amountIn, - slippage, - tokenIn, - tokenOut, - amountOut, - }); - const gas = estimatedGas + (estimatedGas * GAS_BUFFER) / 100n; - - const { request } = await simulateContractWithTxTracker(config, { - address: contracts.mainnet.OUSDVault.address, - abi: contracts.mainnet.OUSDVault.abi, - functionName: 'mint', - args: [tokenIn.address, amountIn, minAmountOut], - gas, - }); - const hash = await writeContract(config, request); - - return hash; -}; - -export default { - isRouteAvailable, - estimateAmount, - estimateGas, - estimateRoute, - allowance, - estimateApprovalGas, - approve, - swap, -}; diff --git a/libs/ousd/swap/src/actions/sushiswap.ts b/libs/ousd/swap/src/actions/sushiswap.ts deleted file mode 100644 index 3fd5a7554..000000000 --- a/libs/ousd/swap/src/actions/sushiswap.ts +++ /dev/null @@ -1,297 +0,0 @@ -import { contracts, tokens } from '@origin/shared/contracts'; -import { simulateContractWithTxTracker } from '@origin/shared/providers'; -import { subtractSlippage, ZERO_ADDRESS } from '@origin/shared/utils'; -import { - getAccount, - getPublicClient, - readContract, - simulateContract, - writeContract, -} from '@wagmi/core'; -import { last } from 'ramda'; -import { formatUnits } from 'viem'; - -import { GAS_BUFFER, MAX_PRICE } from '../constants'; - -import type { Token } from '@origin/shared/contracts'; -import type { - Allowance, - Approve, - EstimateAmount, - EstimateApprovalGas, - EstimateGas, - EstimateRoute, - IsRouteAvailable, - Swap, -} from '@origin/shared/providers'; - -const getPath = (tokenIn: Token, tokenOut: Token) => { - if (tokenIn.symbol === tokens.mainnet.OUSD.symbol) { - return { - [tokens.mainnet.DAI.symbol]: [ - tokens.mainnet.OUSD.address, - tokens.mainnet.USDT.address, - tokens.mainnet.DAI.address, - ] as const, - [tokens.mainnet.USDT.symbol]: [ - tokens.mainnet.OUSD.address, - tokens.mainnet.USDT.address, - ] as const, - [tokens.mainnet.USDC.symbol]: [ - tokens.mainnet.OUSD.address, - tokens.mainnet.USDT.address, - tokens.mainnet.USDC.address, - ] as const, - }[tokenOut.symbol]; - } else if (tokenOut.symbol === tokens.mainnet.OUSD.symbol) { - return { - [tokens.mainnet.DAI.symbol]: [ - tokens.mainnet.DAI.address, - tokens.mainnet.USDT.address, - tokens.mainnet.OUSD.address, - ] as const, - [tokens.mainnet.USDT.symbol]: [ - tokens.mainnet.USDT.address, - tokens.mainnet.OUSD.address, - ] as const, - [tokens.mainnet.USDC.symbol]: [ - tokens.mainnet.USDC.address, - tokens.mainnet.USDT.address, - tokens.mainnet.OUSD.address, - ] as const, - }[tokenIn.symbol]; - } -}; - -const isRouteAvailable: IsRouteAvailable = async ( - config, - { amountIn, tokenIn, tokenOut }, -) => { - const path = getPath(tokenIn, tokenOut); - try { - if (path) { - const estimate = await readContract(config, { - address: contracts.mainnet.sushiswapRouter.address, - abi: contracts.mainnet.sushiswapRouter.abi, - functionName: 'getAmountsOut', - args: [amountIn, path], - }); - - return ( - +formatUnits(amountIn, tokenIn.decimals) / - +formatUnits(last(estimate) ?? 1n, tokenOut.decimals) < - MAX_PRICE - ); - } - } catch {} - - return false; -}; - -const estimateAmount: EstimateAmount = async ( - config, - { amountIn, tokenIn, tokenOut }, -) => { - const path = getPath(tokenIn, tokenOut); - - if (amountIn === 0n || !path) { - return 0n; - } - - const estimate = await readContract(config, { - address: contracts.mainnet.sushiswapRouter.address, - abi: contracts.mainnet.sushiswapRouter.abi, - functionName: 'getAmountsOut', - args: [amountIn, path], - }); - - return last(estimate) ?? 0n; -}; - -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 = subtractSlippage(amountOut, tokenOut.decimals, slippage); - const path = getPath(tokenIn, tokenOut); - - gasEstimate = - tokenIn.symbol === tokens.mainnet.USDT.symbol || - tokenOut.symbol === tokens.mainnet.USDT.symbol - ? 175000n - : 230000n; - - try { - if (path) { - gasEstimate = await publicClient.estimateContractGas({ - address: contracts.mainnet.sushiswapRouter.address, - abi: contracts.mainnet.sushiswapRouter.abi, - functionName: 'swapExactTokensForTokens', - args: [ - amountIn, - minAmountOut, - path, - address ?? ZERO_ADDRESS, - BigInt(Date.now() + 2 * 60 * 1000), - ], - account: address, - }); - } - } catch {} - - return gasEstimate; -}; - -const estimateRoute: EstimateRoute = async ( - config, - { tokenIn, tokenOut, amountIn, slippage, route }, -) => { - 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, - allowanceAmount, - approvalGas, - gas, - rate: - +formatUnits(estimatedAmount, tokenOut.decimals) / - +formatUnits(amountIn, tokenIn.decimals), - }; -}; - -const allowance: Allowance = async (config, { tokenIn }) => { - const { address } = getAccount(config); - - if (!address || !tokenIn?.address) { - return 0n; - } - - const allowance = await readContract(config, { - address: tokenIn.address, - abi: tokenIn.abi, - functionName: 'allowance', - args: [address, contracts.mainnet.sushiswapRouter.address], - }); - - return allowance as unknown as bigint; -}; - -const estimateApprovalGas: EstimateApprovalGas = async ( - config, - { tokenIn, amountIn }, -) => { - let approvalEstimate = 0n; - const { address } = getAccount(config); - const publicClient = getPublicClient(config); - - if (amountIn === 0n || !publicClient || !tokenIn?.address) { - return approvalEstimate; - } - - try { - approvalEstimate = await publicClient.estimateContractGas({ - address: tokenIn.address, - abi: tokenIn.abi, - functionName: 'approve', - args: [contracts.mainnet.sushiswapRouter.address, amountIn], - account: address ?? ZERO_ADDRESS, - }); - } catch { - approvalEstimate = 60000n; - } - - return approvalEstimate; -}; - -const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { - if (amountIn === 0n || !tokenIn?.address) { - return null; - } - - const { request } = await simulateContract(config, { - address: tokenIn.address, - abi: tokenIn.abi, - functionName: 'approve', - args: [contracts.mainnet.sushiswapRouter.address, amountIn], - }); - const hash = await writeContract(config, request); - - return hash; -}; - -const swap: Swap = async ( - config, - { tokenIn, tokenOut, amountIn, slippage, amountOut }, -) => { - const { address } = getAccount(config); - - if (amountIn === 0n || !address) { - return null; - } - - const approved = await allowance(config, { tokenIn, tokenOut }); - - if (approved < amountIn) { - throw new Error(`SushiSwap is not approved`); - } - - const minAmountOut = subtractSlippage(amountOut, tokenOut.decimals, slippage); - - const estimatedGas = await estimateGas(config, { - tokenIn, - tokenOut, - amountIn, - amountOut, - slippage, - }); - const gas = estimatedGas + (estimatedGas * GAS_BUFFER) / 100n; - - const { request } = await simulateContractWithTxTracker(config, { - address: contracts.mainnet.sushiswapRouter.address, - abi: contracts.mainnet.sushiswapRouter.abi, - functionName: 'swapExactTokensForTokens', - args: [ - amountIn, - minAmountOut, - getPath(tokenIn, tokenOut), - address, - BigInt(Date.now() + 2 * 60 * 1000), - ], - account: address, - gas, - }); - const hash = await writeContract(config, request); - - return hash; -}; - -export default { - isRouteAvailable, - estimateAmount, - estimateGas, - estimateRoute, - allowance, - estimateApprovalGas, - approve, - swap, -}; diff --git a/libs/ousd/swap/src/actions/swapCurve.ts b/libs/ousd/swap/src/actions/swapCurve.ts deleted file mode 100644 index 6e9d873dd..000000000 --- a/libs/ousd/swap/src/actions/swapCurve.ts +++ /dev/null @@ -1,301 +0,0 @@ -import { queryClient } from '@origin/ousd/shared'; -import { contracts } from '@origin/shared/contracts'; -import { - simulateContractWithTxTracker, - useCurve, -} from '@origin/shared/providers'; -import { - isAddressEqual, - subtractSlippage, - ZERO_ADDRESS, -} from '@origin/shared/utils'; -import { - getAccount, - getPublicClient, - readContract, - simulateContract, - writeContract, -} from '@wagmi/core'; -import { formatUnits } from 'viem'; - -import { GAS_BUFFER, MAX_PRICE } from '../constants'; - -import type { - Allowance, - Approve, - EstimateAmount, - EstimateApprovalGas, - EstimateGas, - EstimateRoute, - IsRouteAvailable, - Swap, -} from '@origin/shared/providers'; - -const isRouteAvailable: IsRouteAvailable = async ( - config, - { amountIn, tokenIn, tokenOut }, -) => { - const curve = await queryClient.fetchQuery({ - queryKey: useCurve.getKey(), - queryFn: useCurve.fetcher(config), - staleTime: Infinity, - }); - try { - const estimate = await readContract(config, { - address: curve.CurveRegistryExchange.address, - abi: curve.CurveRegistryExchange.abi, - functionName: 'get_exchange_amount', - args: [ - contracts.mainnet.OUSDCurveMetaPool.address, - tokenIn.address, - tokenOut.address, - amountIn, - ], - }); - return ( - +formatUnits(amountIn, tokenIn.decimals) / - +formatUnits(estimate as unknown as bigint, tokenOut.decimals) < - MAX_PRICE - ); - } catch {} - - return false; -}; - -const estimateAmount: EstimateAmount = async ( - config, - { amountIn, tokenIn, tokenOut }, -) => { - if (amountIn === 0n) { - return 0n; - } - - const curve = await queryClient.fetchQuery({ - queryKey: useCurve.getKey(), - queryFn: useCurve.fetcher(config), - staleTime: Infinity, - }); - const estimate = await readContract(config, { - address: curve.CurveRegistryExchange.address, - abi: curve.CurveRegistryExchange.abi, - functionName: 'get_exchange_amount', - args: [ - contracts.mainnet.OUSDCurveMetaPool.address, - tokenIn.address, - tokenOut.address, - amountIn, - ], - }); - - return estimate 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 || - !tokenIn?.address || - !tokenOut?.address - ) { - return gasEstimate; - } - - const { address } = getAccount(config); - const minAmountOut = subtractSlippage(amountOut, tokenOut.decimals, slippage); - const curve = await queryClient.fetchQuery({ - queryKey: useCurve.getKey(), - queryFn: useCurve.fetcher(config), - staleTime: Infinity, - }); - - try { - gasEstimate = await publicClient.estimateContractGas({ - address: contracts.mainnet.OUSDCurveMetaPool.address, - abi: contracts.mainnet.OUSDCurveMetaPool.abi, - functionName: 'exchange_underlying', - args: [ - BigInt( - curve.OusdMetaPoolUnderlyings.findIndex((t) => - isAddressEqual(t, tokenIn.address ?? ZERO_ADDRESS), - ), - ), - BigInt( - curve.OusdMetaPoolUnderlyings.findIndex((t) => - isAddressEqual(t, tokenOut.address ?? ZERO_ADDRESS), - ), - ), - amountIn, - minAmountOut, - ], - account: address, - }); - } catch { - gasEstimate = 350000n; - } - - return gasEstimate; -}; - -const estimateRoute: EstimateRoute = async ( - config, - { tokenIn, tokenOut, amountIn, slippage, route }, -) => { - 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, - allowanceAmount, - approvalGas, - gas, - rate: - +formatUnits(estimatedAmount, tokenOut.decimals) / - +formatUnits(amountIn, tokenIn.decimals), - }; -}; - -const allowance: Allowance = async (config, { tokenIn }) => { - const { address } = getAccount(config); - - if (!address || !tokenIn?.address) { - return 0n; - } - - const allowance = await readContract(config, { - address: tokenIn.address, - abi: tokenIn.abi, - functionName: 'allowance', - args: [address, contracts.mainnet.OUSDCurveMetaPool.address], - }); - - return allowance as unknown as bigint; -}; - -const estimateApprovalGas: EstimateApprovalGas = async ( - config, - { tokenIn, amountIn }, -) => { - let approvalEstimate = 0n; - const { address } = getAccount(config); - const publicClient = getPublicClient(config); - - if (amountIn === 0n || !address || !publicClient || !tokenIn?.address) { - return approvalEstimate; - } - - try { - approvalEstimate = await publicClient.estimateContractGas({ - address: tokenIn.address, - abi: tokenIn.abi, - functionName: 'approve', - args: [contracts.mainnet.OUSDCurveMetaPool.address, amountIn], - account: address, - }); - } catch { - approvalEstimate = 60000n; - } - - return approvalEstimate; -}; - -const approve: Approve = async (config, { tokenIn, amountIn }) => { - if (amountIn === 0n || !tokenIn?.address) { - return null; - } - - const { request } = await simulateContract(config, { - address: tokenIn.address, - abi: tokenIn.abi, - functionName: 'approve', - args: [contracts.mainnet.OUSDCurveMetaPool.address, amountIn], - }); - const hash = await writeContract(config, request); - - return hash; -}; - -const swap: Swap = async ( - config, - { tokenIn, tokenOut, amountIn, slippage, amountOut }, -) => { - const { address } = getAccount(config); - - if (amountIn === 0n || !address || !tokenIn?.address || !tokenOut?.address) { - return null; - } - - const approved = await allowance(config, { tokenIn, tokenOut }); - - if (approved < amountIn) { - throw new Error(`Curve swap is not approved`); - } - - const minAmountOut = subtractSlippage(amountOut, tokenOut.decimals, slippage); - - const estimatedGas = await estimateGas(config, { - tokenIn, - tokenOut, - amountIn, - amountOut, - slippage, - }); - const gas = estimatedGas + (estimatedGas * GAS_BUFFER) / 100n; - const curve = await queryClient.fetchQuery({ - queryKey: useCurve.getKey(), - queryFn: useCurve.fetcher(config), - staleTime: Infinity, - }); - - const { request } = await simulateContractWithTxTracker(config, { - address: contracts.mainnet.OUSDCurveMetaPool.address, - abi: contracts.mainnet.OUSDCurveMetaPool.abi, - functionName: 'exchange_underlying', - args: [ - BigInt( - curve.OusdMetaPoolUnderlyings.findIndex((t) => - isAddressEqual(t, tokenIn.address ?? ZERO_ADDRESS), - ), - ), - BigInt( - curve.OusdMetaPoolUnderlyings.findIndex((t) => - isAddressEqual(t, tokenOut.address ?? ZERO_ADDRESS), - ), - ), - amountIn, - minAmountOut, - ], - account: address, - gas, - }); - const hash = await writeContract(config, request); - - return hash; -}; - -export default { - isRouteAvailable, - estimateAmount, - estimateGas, - estimateRoute, - allowance, - estimateApprovalGas, - approve, - swap, -}; diff --git a/libs/ousd/swap/src/actions/uniswapV2.ts b/libs/ousd/swap/src/actions/uniswapV2.ts deleted file mode 100644 index b417a1ebb..000000000 --- a/libs/ousd/swap/src/actions/uniswapV2.ts +++ /dev/null @@ -1,299 +0,0 @@ -import { contracts, tokens } from '@origin/shared/contracts'; -import { simulateContractWithTxTracker } from '@origin/shared/providers'; -import { - isNilOrEmpty, - subtractSlippage, - ZERO_ADDRESS, -} from '@origin/shared/utils'; -import { - getAccount, - getPublicClient, - readContract, - simulateContract, - writeContract, -} from '@wagmi/core'; -import { last } from 'ramda'; -import { formatUnits } from 'viem'; - -import { GAS_BUFFER, MAX_PRICE } from '../constants'; - -import type { Token } from '@origin/shared/contracts'; -import type { - Allowance, - Approve, - EstimateAmount, - EstimateApprovalGas, - EstimateGas, - EstimateRoute, - IsRouteAvailable, - Swap, -} from '@origin/shared/providers'; - -const getPath = (tokenIn: Token, tokenOut: Token) => { - if (tokenIn.symbol === tokens.mainnet.OUSD.symbol) { - return { - [tokens.mainnet.DAI.symbol]: [ - tokens.mainnet.OUSD.address, - tokens.mainnet.USDT.address, - tokens.mainnet.DAI.address, - ] as const, - [tokens.mainnet.USDT.symbol]: [ - tokens.mainnet.OUSD.address, - tokens.mainnet.USDT.address, - ] as const, - [tokens.mainnet.USDC.symbol]: [ - tokens.mainnet.OUSD.address, - tokens.mainnet.USDT.address, - tokens.mainnet.USDC.address, - ] as const, - }[tokenOut.symbol]; - } else if (tokenOut.symbol === tokens.mainnet.OUSD.symbol) { - return { - [tokens.mainnet.DAI.symbol]: [ - tokens.mainnet.DAI.address, - tokens.mainnet.USDT.address, - tokens.mainnet.OUSD.address, - ] as const, - [tokens.mainnet.USDT.symbol]: [ - tokens.mainnet.USDT.address, - tokens.mainnet.OUSD.address, - ] as const, - [tokens.mainnet.USDC.symbol]: [ - tokens.mainnet.USDC.address, - tokens.mainnet.USDT.address, - tokens.mainnet.OUSD.address, - ] as const, - }[tokenIn.symbol]; - } -}; - -const isRouteAvailable: IsRouteAvailable = async ( - config, - { amountIn, tokenIn, tokenOut }, -) => { - const path = getPath(tokenIn, tokenOut); - - try { - if (path) { - const estimate = await readContract(config, { - address: contracts.mainnet.uniswapV2Router.address, - abi: contracts.mainnet.uniswapV2Router.abi, - functionName: 'getAmountsOut', - args: [amountIn, path], - }); - - return ( - +formatUnits(amountIn, tokenIn.decimals) / - +formatUnits(last(estimate) ?? 0n, tokenOut.decimals) < - MAX_PRICE - ); - } - } catch {} - - return false; -}; - -const estimateAmount: EstimateAmount = async ( - config, - { amountIn, tokenIn, tokenOut }, -) => { - const path = getPath(tokenIn, tokenOut); - if (amountIn === 0n || !path) { - return 0n; - } - - const estimate = await readContract(config, { - address: contracts.mainnet.uniswapV2Router.address, - abi: contracts.mainnet.uniswapV2Router.abi, - functionName: 'getAmountsOut', - args: [amountIn, path], - }); - - return last(estimate) ?? 0n; -}; - -const estimateGas: EstimateGas = async ( - config, - { tokenIn, tokenOut, amountIn, amountOut, slippage }, -) => { - let gasEstimate = 0n; - const publicClient = getPublicClient(config); - const { address } = getAccount(config); - const path = getPath(tokenIn, tokenOut); - - if (amountIn === 0n || !publicClient || !path) { - return gasEstimate; - } - - const minAmountOut = subtractSlippage(amountOut, tokenOut.decimals, slippage); - - try { - gasEstimate = await publicClient.estimateContractGas({ - address: contracts.mainnet.uniswapV2Router.address, - abi: contracts.mainnet.uniswapV2Router.abi, - functionName: 'swapExactTokensForTokens', - args: [ - amountIn, - minAmountOut, - path, - address ?? ZERO_ADDRESS, - BigInt(Date.now() + 2 * 60 * 1000), - ], - account: address, - }); - } catch { - gasEstimate = - tokenIn.symbol === tokens.mainnet.USDT.symbol || - tokenOut.symbol === tokens.mainnet.USDT.symbol - ? 175000n - : 230000n; - } - - return gasEstimate; -}; - -const estimateRoute: EstimateRoute = async ( - config, - { tokenIn, tokenOut, amountIn, slippage, route }, -) => { - 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, - allowanceAmount, - approvalGas, - gas, - rate: - +formatUnits(estimatedAmount, tokenOut.decimals) / - +formatUnits(amountIn, tokenIn.decimals), - }; -}; - -const allowance: Allowance = async (config, { tokenIn }) => { - const { address } = getAccount(config); - - if (!address || !tokenIn?.address) { - return 0n; - } - - const allowance = await readContract(config, { - address: tokenIn.address, - abi: tokenIn.abi, - functionName: 'allowance', - args: [address, contracts.mainnet.uniswapV2Router.address], - }); - - return allowance as unknown as bigint; -}; - -const estimateApprovalGas: EstimateApprovalGas = async ( - config, - { tokenIn, amountIn }, -) => { - let approvalEstimate = 0n; - const { address } = getAccount(config); - const publicClient = getPublicClient(config); - - if (amountIn === 0n || !address || !publicClient || !tokenIn?.address) { - return approvalEstimate; - } - - try { - approvalEstimate = await publicClient.estimateContractGas({ - address: tokenIn.address, - abi: tokenIn.abi, - functionName: 'approve', - args: [contracts.mainnet.uniswapV2Router.address, amountIn], - account: address, - }); - } catch { - approvalEstimate = 60000n; - } - - return approvalEstimate; -}; - -const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { - if (!tokenIn?.address) { - return null; - } - - const { request } = await simulateContract(config, { - address: tokenIn.address, - abi: tokenIn.abi, - functionName: 'approve', - args: [contracts.mainnet.uniswapV2Router.address, amountIn], - }); - const hash = await writeContract(config, request); - - return hash; -}; - -const swap: Swap = async ( - config, - { tokenIn, tokenOut, amountIn, slippage, amountOut }, -) => { - const { address } = getAccount(config); - - if (amountIn === 0n || isNilOrEmpty(address)) { - return null; - } - - const approved = await allowance(config, { tokenIn, tokenOut }); - - if (approved < amountIn) { - throw new Error(`Uniswap V2 is not approved`); - } - - const minAmountOut = subtractSlippage(amountOut, tokenOut.decimals, slippage); - - const estimatedGas = await estimateGas(config, { - tokenIn, - tokenOut, - amountIn, - amountOut, - slippage, - }); - const gas = estimatedGas + (estimatedGas * GAS_BUFFER) / 100n; - - const { request } = await simulateContractWithTxTracker(config, { - address: contracts.mainnet.uniswapV2Router.address, - abi: contracts.mainnet.uniswapV2Router.abi, - functionName: 'swapExactTokensForTokens', - args: [ - amountIn, - minAmountOut, - getPath(tokenIn, tokenOut), - address, - BigInt(Date.now() + 2 * 60 * 1000), - ], - account: address, - gas, - }); - const hash = await writeContract(config, request); - - return hash; -}; - -export default { - isRouteAvailable, - estimateAmount, - estimateGas, - estimateRoute, - allowance, - estimateApprovalGas, - approve, - swap, -}; diff --git a/libs/ousd/swap/src/actions/uniswapV3.ts b/libs/ousd/swap/src/actions/uniswapV3.ts deleted file mode 100644 index d650ab526..000000000 --- a/libs/ousd/swap/src/actions/uniswapV3.ts +++ /dev/null @@ -1,349 +0,0 @@ -import { contracts, tokens } from '@origin/shared/contracts'; -import { simulateContractWithTxTracker } from '@origin/shared/providers'; -import { - isNilOrEmpty, - subtractSlippage, - ZERO_ADDRESS, -} from '@origin/shared/utils'; -import { - getAccount, - getPublicClient, - readContract, - simulateContract, - writeContract, -} from '@wagmi/core'; -import { encodePacked, formatUnits } from 'viem'; - -import type { Token } from '@origin/shared/contracts'; -import type { - Allowance, - Approve, - EstimateAmount, - EstimateApprovalGas, - EstimateGas, - EstimateRoute, - IsRouteAvailable, - Swap, -} from '@origin/shared/providers'; - -const getPath = (tokenIn: Token, tokenOut: Token) => { - if (tokenIn.symbol === tokens.mainnet.OUSD.symbol) { - return { - [tokens.mainnet.DAI.symbol]: encodePacked( - ['address', 'uint24', 'address', 'uint24', 'address'], - [ - tokens.mainnet.OUSD.address, - 500, - tokens.mainnet.USDT.address, - 500, - tokens.mainnet.DAI.address, - ], - ), - [tokens.mainnet.USDC.symbol]: encodePacked( - ['address', 'uint24', 'address', 'uint24', 'address'], - [ - tokens.mainnet.OUSD.address, - 500, - tokens.mainnet.USDT.address, - 500, - tokens.mainnet.USDC.address, - ], - ), - }[tokenOut.symbol]; - } else if (tokenOut.symbol === tokens.mainnet.OUSD.symbol) { - return { - [tokens.mainnet.DAI.symbol]: encodePacked( - ['address', 'uint24', 'address', 'uint24', 'address'], - [ - tokens.mainnet.DAI.address, - 500, - tokens.mainnet.USDT.address, - 500, - tokens.mainnet.OUSD.address, - ], - ), - [tokens.mainnet.USDC.symbol]: encodePacked( - ['address', 'uint24', 'address', 'uint24', 'address'], - [ - tokens.mainnet.USDC.address, - 500, - tokens.mainnet.USDT.address, - 500, - tokens.mainnet.OUSD.address, - ], - ), - }[tokenIn.symbol]; - } -}; - -const isRouteAvailable: IsRouteAvailable = async ( - config, - { amountIn, tokenIn }, -) => { - return +formatUnits(amountIn, tokenIn.decimals) > 0.000003; -}; - -const estimateAmount: EstimateAmount = async ( - config, - { amountIn, tokenIn, tokenOut }, -) => { - let estimate = 0n; - const publicClient = getPublicClient(config); - if ( - amountIn === 0n || - !publicClient || - !tokenIn?.address || - !tokenOut?.address - ) { - return estimate; - } - - const path = getPath(tokenIn, tokenOut); - - if ([tokenIn.symbol, tokenOut.symbol].includes(tokens.mainnet.USDT.symbol)) { - estimate = ( - await publicClient.simulateContract({ - address: contracts.mainnet.uniswapV3Quoter.address, - abi: contracts.mainnet.uniswapV3Quoter.abi, - functionName: 'quoteExactInputSingle', - args: [tokenIn.address, tokenOut.address, 500, amountIn, 0n], - }) - )?.result; - } else if (path) { - estimate = ( - await publicClient.simulateContract({ - address: contracts.mainnet.uniswapV3Quoter.address, - abi: contracts.mainnet.uniswapV3Quoter.abi, - functionName: 'quoteExactInput', - args: [path, amountIn], - }) - )?.result; - } - - return estimate; -}; - -const estimateGas: EstimateGas = async ( - config, - { tokenIn, tokenOut, amountIn, amountOut, slippage }, -) => { - let gasEstimate = 0n; - const publicClient = getPublicClient(config); - - if ( - amountIn === 0n || - !publicClient || - !tokenIn?.address || - !tokenOut?.address - ) { - return gasEstimate; - } - - const { address } = getAccount(config); - const minAmountOut = subtractSlippage(amountOut, tokenOut.decimals, slippage); - const path = getPath(tokenIn, tokenOut); - - try { - if ( - [tokenIn.symbol, tokenOut.symbol].includes(tokens.mainnet.USDT.symbol) - ) { - gasEstimate = await publicClient.estimateContractGas({ - address: contracts.mainnet.uniswapV3Router.address, - abi: contracts.mainnet.uniswapV3Router.abi, - functionName: 'exactInputSingle', - args: [ - { - tokenIn: tokenIn.address, - tokenOut: tokenOut.address, - amountIn, - amountOutMinimum: minAmountOut, - deadline: BigInt(Date.now() + 2 * 60 * 1000), - fee: 500, - recipient: address ?? ZERO_ADDRESS, - sqrtPriceLimitX96: 0n, - }, - ], - }); - } else if (path) { - gasEstimate = await publicClient.estimateContractGas({ - address: contracts.mainnet.uniswapV3Router.address, - abi: contracts.mainnet.uniswapV3Router.abi, - functionName: 'exactInput', - args: [ - { - path, - amountIn, - amountOutMinimum: minAmountOut, - deadline: BigInt(Date.now() + 2 * 60 * 1000), - recipient: address ?? ZERO_ADDRESS, - }, - ], - }); - } - } catch { - gasEstimate = 165000n; - } - - return gasEstimate; -}; - -const estimateRoute: EstimateRoute = async ( - config, - { tokenIn, tokenOut, amountIn, slippage, route }, -) => { - 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, - allowanceAmount, - approvalGas, - gas, - rate: - +formatUnits(estimatedAmount, tokenOut.decimals) / - +formatUnits(amountIn, tokenIn.decimals), - }; -}; - -const allowance: Allowance = async (config, { tokenIn }) => { - const { address } = getAccount(config); - - if (!address || !tokenIn?.address) { - return 0n; - } - - const allowance = await readContract(config, { - address: tokenIn.address, - abi: tokenIn.abi, - functionName: 'allowance', - args: [address, contracts.mainnet.uniswapV3Router.address], - }); - - return allowance as unknown as bigint; -}; - -const estimateApprovalGas: EstimateApprovalGas = async ( - config, - { tokenIn, amountIn }, -) => { - let approvalEstimate = 0n; - const { address } = getAccount(config); - const publicClient = getPublicClient(config); - - if (amountIn === 0n || !publicClient || !tokenIn?.address) { - return approvalEstimate; - } - - try { - approvalEstimate = await publicClient.estimateContractGas({ - address: tokenIn.address, - abi: tokenIn.abi, - functionName: 'approve', - args: [contracts.mainnet.uniswapV3Router.address, amountIn], - account: address ?? ZERO_ADDRESS, - }); - } catch { - approvalEstimate = 60000n; - } - - return approvalEstimate; -}; - -const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { - if (!tokenIn?.address) { - return null; - } - - const { request } = await simulateContract(config, { - address: tokenIn.address, - abi: tokenIn.abi, - functionName: 'approve', - args: [contracts.mainnet.uniswapV3Router.address, amountIn], - }); - const hash = await writeContract(config, request); - - return hash; -}; - -const swap: Swap = async ( - config, - { tokenIn, tokenOut, amountIn, slippage, amountOut }, -) => { - const { address } = getAccount(config); - - if (amountIn === 0n || isNilOrEmpty(address)) { - return null; - } - - const approved = await allowance(config, { tokenIn, tokenOut }); - - if (approved < amountIn) { - throw new Error(`Uniswap V3 is not approved`); - } - - const minAmountOut = subtractSlippage(amountOut, tokenOut.decimals, slippage); - - let txHash; - if ([tokenIn.symbol, tokenOut.symbol].includes(tokens.mainnet.USDT.symbol)) { - const { request } = await simulateContractWithTxTracker(config, { - address: contracts.mainnet.uniswapV3Router.address, - abi: contracts.mainnet.uniswapV3Router.abi, - functionName: 'exactInputSingle', - args: [ - { - tokenIn: tokenIn.address, - tokenOut: tokenOut.address, - amountIn: amountIn, - amountOutMinimum: minAmountOut, - deadline: BigInt(Date.now() + 2 * 60 * 1000), - fee: 500, - recipient: address, - sqrtPriceLimitX96: 0n, - }, - ], - }); - const hash = await writeContract(config, request); - txHash = hash; - } else { - const { request } = await simulateContractWithTxTracker(config, { - address: contracts.mainnet.uniswapV3Router.address, - abi: contracts.mainnet.uniswapV3Router.abi, - functionName: 'exactInput', - args: [ - { - path: getPath(tokenIn, tokenOut), - amountIn: amountIn, - amountOutMinimum: minAmountOut, - deadline: BigInt(Date.now() + 2 * 60 * 1000), - recipient: address, - }, - ], - }); - const hash = await writeContract(config, request); - txHash = hash; - } - - return txHash; -}; - -export default { - isRouteAvailable, - estimateAmount, - estimateGas, - estimateRoute, - allowance, - estimateApprovalGas, - approve, - swap, -}; diff --git a/libs/ousd/swap/src/constants.ts b/libs/ousd/swap/src/constants.ts index 23a8fea09..15744f09b 100644 --- a/libs/ousd/swap/src/constants.ts +++ b/libs/ousd/swap/src/constants.ts @@ -4,191 +4,188 @@ import type { SwapRoute } from '@origin/shared/providers'; import type { SwapAction } from './types'; -export const GAS_BUFFER = 10n; // 10% -export const MAX_PRICE = 1.2; - export const swapRoutes: SwapRoute[] = [ // Mint DAI -> OUSD { tokenIn: tokens.mainnet.DAI, tokenOut: tokens.mainnet.OUSD, - action: 'flipper', + action: 'swap-flipper-ousd', }, { tokenIn: tokens.mainnet.DAI, tokenOut: tokens.mainnet.OUSD, - action: 'swap-curve', + action: 'swap-curve-ousd', }, { tokenIn: tokens.mainnet.DAI, tokenOut: tokens.mainnet.OUSD, - action: 'uniswap-v2', + action: 'swap-uniswap-v2-ousd', }, { tokenIn: tokens.mainnet.DAI, tokenOut: tokens.mainnet.OUSD, - action: 'uniswap-v3', + action: 'swap-uniswap-v3-ousd', }, { tokenIn: tokens.mainnet.DAI, tokenOut: tokens.mainnet.OUSD, - action: 'mint-vault', + action: 'mint-vault-ousd', }, { tokenIn: tokens.mainnet.DAI, tokenOut: tokens.mainnet.OUSD, - action: 'sushiswap', + action: 'swap-sushiswap-ousd', }, // Mint USDT -> OUSD { tokenIn: tokens.mainnet.USDT, tokenOut: tokens.mainnet.OUSD, - action: 'flipper', + action: 'swap-flipper-ousd', }, { tokenIn: tokens.mainnet.USDT, tokenOut: tokens.mainnet.OUSD, - action: 'swap-curve', + action: 'swap-curve-ousd', }, { tokenIn: tokens.mainnet.USDT, tokenOut: tokens.mainnet.OUSD, - action: 'uniswap-v2', + action: 'swap-uniswap-v2-ousd', }, { tokenIn: tokens.mainnet.USDT, tokenOut: tokens.mainnet.OUSD, - action: 'uniswap-v3', + action: 'swap-uniswap-v3-ousd', }, { tokenIn: tokens.mainnet.USDT, tokenOut: tokens.mainnet.OUSD, - action: 'mint-vault', + action: 'mint-vault-ousd', }, { tokenIn: tokens.mainnet.USDT, tokenOut: tokens.mainnet.OUSD, - action: 'sushiswap', + action: 'swap-sushiswap-ousd', }, // Mint USDC -> OUSD { tokenIn: tokens.mainnet.USDC, tokenOut: tokens.mainnet.OUSD, - action: 'flipper', + action: 'swap-flipper-ousd', }, { tokenIn: tokens.mainnet.USDC, tokenOut: tokens.mainnet.OUSD, - action: 'swap-curve', + action: 'swap-curve-ousd', }, { tokenIn: tokens.mainnet.USDC, tokenOut: tokens.mainnet.OUSD, - action: 'uniswap-v2', + action: 'swap-uniswap-v2-ousd', }, { tokenIn: tokens.mainnet.USDC, tokenOut: tokens.mainnet.OUSD, - action: 'uniswap-v3', + action: 'swap-uniswap-v3-ousd', }, { tokenIn: tokens.mainnet.USDC, tokenOut: tokens.mainnet.OUSD, - action: 'mint-vault', + action: 'mint-vault-ousd', }, { tokenIn: tokens.mainnet.USDC, tokenOut: tokens.mainnet.OUSD, - action: 'sushiswap', + action: 'swap-sushiswap-ousd', }, // Redeem OUSD -> DAI { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.DAI, - action: 'flipper', + action: 'swap-flipper-ousd', }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.DAI, - action: 'sushiswap', + action: 'swap-sushiswap-ousd', }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.DAI, - action: 'uniswap-v2', + action: 'swap-uniswap-v2-ousd', }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.DAI, - action: 'uniswap-v3', + action: 'swap-uniswap-v3-ousd', }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.DAI, - action: 'swap-curve', + action: 'swap-curve-ousd', }, // Redeem OUSD -> USDT { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.USDT, - action: 'flipper', + action: 'swap-flipper-ousd', }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.USDT, - action: 'sushiswap', + action: 'swap-sushiswap-ousd', }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.USDT, - action: 'uniswap-v2', + action: 'swap-uniswap-v2-ousd', }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.USDT, - action: 'uniswap-v3', + action: 'swap-uniswap-v3-ousd', }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.USDT, - action: 'swap-curve', + action: 'swap-curve-ousd', }, // Redeem OUSD -> USDC { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.USDC, - action: 'flipper', + action: 'swap-flipper-ousd', }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.USDC, - action: 'sushiswap', + action: 'swap-sushiswap-ousd', }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.USDC, - action: 'uniswap-v2', + action: 'swap-uniswap-v2-ousd', }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.USDC, - action: 'uniswap-v3', + action: 'swap-uniswap-v3-ousd', }, { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.USDC, - action: 'swap-curve', + action: 'swap-curve-ousd', }, // Wrap OUSD { tokenIn: tokens.mainnet.OUSD, tokenOut: tokens.mainnet.wOUSD, - action: 'wrap-ousd', + action: 'wrap-ousd-wousd', }, // Unwrap wOUSD { tokenIn: tokens.mainnet.wOUSD, tokenOut: tokens.mainnet.OUSD, - action: 'unwrap-wousd', + action: 'unwrap-ousd-wousd', }, ]; diff --git a/libs/ousd/swap/src/types.ts b/libs/ousd/swap/src/types.ts index f895a5816..fa0b28937 100644 --- a/libs/ousd/swap/src/types.ts +++ b/libs/ousd/swap/src/types.ts @@ -1,9 +1,13 @@ -export type SwapAction = - | 'flipper' - | 'uniswap-v2' - | 'uniswap-v3' - | 'sushiswap' - | 'mint-vault' - | 'swap-curve' - | 'wrap-ousd' - | 'unwrap-wousd'; +import type { OusdRoute } from '@origin/shared/routes'; + +export type SwapAction = Extract< + OusdRoute, + | 'mint-vault-ousd' + | 'swap-flipper-ousd' + | 'swap-curve-ousd' + | 'swap-sushiswap-ousd' + | 'swap-uniswap-v2-ousd' + | 'swap-uniswap-v3-ousd' + | 'unwrap-ousd-wousd' + | 'wrap-ousd-wousd' +>; diff --git a/libs/prime/restake/src/actions.ts b/libs/prime/restake/src/actions.ts new file mode 100644 index 000000000..266969726 --- /dev/null +++ b/libs/prime/restake/src/actions.ts @@ -0,0 +1,28 @@ +import { + restakePrime, + swapUniswapPrime, + swapZapperPrime, +} from '@origin/shared/routes'; +import { defineMessage } from 'react-intl'; + +import type { SwapApi } from '@origin/shared/providers'; + +import type { RestakeAction } from './types'; + +export const restakeActions: Record = { + 'restake-prime': { + ...restakePrime, + buttonLabel: defineMessage({ defaultMessage: 'Stake' }), + routeLabel: defineMessage({ defaultMessage: 'PrimeStaked' }), + }, + 'swap-uniswap-prime': { + ...swapUniswapPrime, + buttonLabel: defineMessage({ defaultMessage: 'Swap with Uniswap' }), + routeLabel: defineMessage({ defaultMessage: 'Uniswap V3' }), + }, + 'swap-zapper-prime': { + ...swapZapperPrime, + buttonLabel: defineMessage({ defaultMessage: 'Stake' }), + routeLabel: defineMessage({ defaultMessage: 'PrimeStaked' }), + }, +}; diff --git a/libs/prime/restake/src/constants.ts b/libs/prime/restake/src/constants.ts index 76826a5aa..8491be384 100644 --- a/libs/prime/restake/src/constants.ts +++ b/libs/prime/restake/src/constants.ts @@ -4,64 +4,12 @@ import type { SwapRoute } from '@origin/shared/providers'; import type { Meta, RestakeAction } from './types'; -export const GAS_BUFFER = 10n; // 10% -export const MAX_PRICE = 1.2; export const WAITING_BLOCK_AMOUNT = 50400; // 7 days export const restakeRoutes: SwapRoute[] = [ - // { - // tokenIn: tokens.mainnet.ETH, - // tokenOut: tokens.mainnet.primeETH, - // action: 'zapper', - // }, - // { - // tokenIn: tokens.mainnet.ETH, - // tokenOut: tokens.mainnet.primeETH, - // action: 'uniswap', - // }, - // { - // tokenIn: tokens.mainnet.WETH, - // tokenOut: tokens.mainnet.primeETH, - // action: 'uniswap', - // }, - // { - // tokenIn: tokens.mainnet.WETH, - // tokenOut: tokens.mainnet.primeETH, - // action: 'restake', - // }, { tokenIn: tokens.mainnet.OETH, tokenOut: tokens.mainnet.primeETH, - action: 'restake', + action: 'restake-prime', }, - // { - // tokenIn: tokens.mainnet.stETH, - // tokenOut: tokens.mainnet.primeETH, - // action: 'restake', - // }, - // { - // tokenIn: tokens.mainnet.mETH, - // tokenOut: tokens.mainnet.primeETH, - // action: 'restake', - // }, - // { - // tokenIn: tokens.mainnet.ETHx, - // tokenOut: tokens.mainnet.primeETH, - // action: 'restake', - // }, - // { - // tokenIn: tokens.mainnet.sfrxETH, - // tokenOut: tokens.mainnet.primeETH, - // action: 'restake', - // }, - // { - // tokenIn: tokens.mainnet.swETH, - // tokenOut: tokens.mainnet.primeETH, - // action: 'restake', - // }, - // { - // tokenIn: tokens.mainnet.rETH, - // tokenOut: tokens.mainnet.primeETH, - // action: 'restake', - // }, ]; diff --git a/libs/prime/restake/src/hooks.ts b/libs/prime/restake/src/hooks.ts index affae2fb2..ed66ca9ba 100644 --- a/libs/prime/restake/src/hooks.ts +++ b/libs/prime/restake/src/hooks.ts @@ -1,11 +1,12 @@ import { contracts, tokens } from '@origin/shared/contracts'; import { useRoutingSwapState } from '@origin/shared/providers'; -import { useQuery } from '@tanstack/react-query'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; import { mul } from 'dnum'; import { formatUnits, parseUnits } from 'viem'; import { useConfig, useReadContract } from 'wagmi'; export const useExchangeRate = () => { + const queryClient = useQueryClient(); const config = useConfig(); const { action, route } = useRoutingSwapState(); @@ -21,11 +22,14 @@ export const useExchangeRate = () => { return 0; } - const res = await action.estimateAmount(config, { - amountIn: parseUnits('1', route.tokenIn.decimals), - tokenIn: route.tokenIn, - tokenOut: route.tokenOut, - }); + const res = await action.estimateAmount( + { config, queryClient }, + { + amountIn: parseUnits('1', route.tokenIn.decimals), + tokenIn: route.tokenIn, + tokenOut: route.tokenOut, + }, + ); const estimate = res ? +formatUnits(res, route.tokenOut.decimals) : 0; diff --git a/libs/prime/restake/src/types.ts b/libs/prime/restake/src/types.ts index d2929980b..ca2e1e479 100644 --- a/libs/prime/restake/src/types.ts +++ b/libs/prime/restake/src/types.ts @@ -1,5 +1,9 @@ import type { SupportedBoost } from '@origin/prime/shared'; +import type { PrimeRoute } from '@origin/shared/routes'; -export type RestakeAction = 'restake' | 'uniswap' | 'zapper'; +export type RestakeAction = Extract< + PrimeRoute, + 'restake-prime' | 'swap-uniswap-prime' | 'swap-zapper-prime' +>; export type Meta = { boost?: SupportedBoost }; diff --git a/libs/shared/providers/src/swapper/hooks.ts b/libs/shared/providers/src/swapper/hooks.ts index 4f0e88db8..64a6535f5 100644 --- a/libs/shared/providers/src/swapper/hooks.ts +++ b/libs/shared/providers/src/swapper/hooks.ts @@ -244,11 +244,14 @@ export const useHandleTokenFlip = () => { const newRoutes = getAvailableRoutes(swapRoutes, tokenOut, tokenIn); const availabilities = await Promise.allSettled( newRoutes.map((r) => - swapActions[r.action].isRouteAvailable(config, { - amountIn: amountIn, - tokenIn: r.tokenIn, - tokenOut: r.tokenOut, - }), + swapActions[r.action].isRouteAvailable( + { config, queryClient }, + { + amountIn: amountIn, + tokenIn: r.tokenIn, + tokenOut: r.tokenOut, + }, + ), ), ); const filteredRoutes = newRoutes.filter( @@ -313,14 +316,17 @@ export const useHandleTokenFlip = () => { queryFn: async () => { let res: EstimatedSwapRoute; try { - res = await swapActions[route.action].estimateRoute(config, { - tokenIn: route.tokenIn, - tokenOut: route.tokenOut, - amountIn: scaledAmountIn, - amountOut: scaledAmountOut, - route, - slippage, - }); + res = await swapActions[route.action].estimateRoute( + { config, queryClient }, + { + tokenIn: route.tokenIn, + tokenOut: route.tokenOut, + amountIn: scaledAmountIn, + amountOut: scaledAmountOut, + route, + slippage, + }, + ); } catch (error) { console.error( `Fail to estimate route ${route.action}\n${formatError(error)}`, @@ -416,6 +422,7 @@ export const useHandleSelectSwapRoute = () => { export const useIsSwapRouteAvailable = ( route: SwapRoute | undefined | null, ) => { + const queryClient = useQueryClient(); const config = useConfig(); const [{ amountIn, swapActions }] = useSwapState(); @@ -433,11 +440,14 @@ export const useIsSwapRouteAvailable = ( } let res = false; try { - res = await swapActions[route.action].isRouteAvailable(config, { - tokenIn: route.tokenIn, - tokenOut: route.tokenOut, - amountIn, - }); + res = await swapActions[route.action].isRouteAvailable( + { config, queryClient }, + { + tokenIn: route.tokenIn, + tokenOut: route.tokenOut, + amountIn, + }, + ); } catch {} return res; @@ -447,6 +457,7 @@ export const useIsSwapRouteAvailable = ( }; export const useSwapRouteAllowance = (route: SwapRoute | undefined | null) => { + const queryClient = useQueryClient(); const config = useConfig(); const [{ swapActions }] = useSwapState(); @@ -463,10 +474,13 @@ export const useSwapRouteAllowance = (route: SwapRoute | undefined | null) => { } let res = 0n; try { - res = await swapActions[route.action].allowance(config, { - tokenIn: route.tokenIn, - tokenOut: route.tokenOut, - }); + res = await swapActions[route.action].allowance( + { config, queryClient }, + { + tokenIn: route.tokenIn, + tokenOut: route.tokenOut, + }, + ); } catch {} return res; @@ -514,11 +528,14 @@ export const useHandleApprove = () => { let notifId; try { - const hash = await swapActions[selectedSwapRoute.action].approve(config, { - tokenIn, - tokenOut, - amountIn, - }); + const hash = await swapActions[selectedSwapRoute.action].approve( + { config, queryClient }, + { + tokenIn, + tokenOut, + amountIn, + }, + ); notifId = onApproveSigned?.({ ...state, trackId }); setSwapState((state) => ({ ...state, @@ -642,14 +659,17 @@ export const useHandleSwap = () => { let notifId; try { - const hash = await swapActions[selectedSwapRoute.action].swap(config, { - tokenIn, - tokenOut, - amountIn, - estimatedRoute: selectedSwapRoute, - slippage, - amountOut, - }); + const hash = await swapActions[selectedSwapRoute.action].swap( + { config, queryClient }, + { + tokenIn, + tokenOut, + amountIn, + estimatedRoute: selectedSwapRoute, + slippage, + amountOut, + }, + ); notifId = onSwapSigned?.({ ...state, trackId }); setSwapState((state) => ({ ...state, diff --git a/libs/shared/providers/src/swapper/state.ts b/libs/shared/providers/src/swapper/state.ts index b1a1e152f..46d371598 100644 --- a/libs/shared/providers/src/swapper/state.ts +++ b/libs/shared/providers/src/swapper/state.ts @@ -129,11 +129,14 @@ export const { Provider: SwapProvider, useTracked: useSwapState } = ); const availabilities = await Promise.allSettled( availableRoutes.map((r) => - swapActions[r.action].isRouteAvailable(config, { - amountIn: state.amountIn, - tokenIn: r.tokenIn, - tokenOut: r.tokenOut, - }), + swapActions[r.action].isRouteAvailable( + { config, queryClient }, + { + amountIn: state.amountIn, + tokenIn: r.tokenIn, + tokenOut: r.tokenOut, + }, + ), ), ); const filteredRoutes = availableRoutes.filter( @@ -170,7 +173,7 @@ export const { Provider: SwapProvider, useTracked: useSwapState } = let res: EstimatedSwapRoute; try { res = await swapActions[route.action].estimateRoute( - config, + { config, queryClient }, { tokenIn: route.tokenIn, tokenOut: route.tokenOut, diff --git a/libs/shared/providers/src/swapper/types.ts b/libs/shared/providers/src/swapper/types.ts index e79dc3927..2a2314e9e 100644 --- a/libs/shared/providers/src/swapper/types.ts +++ b/libs/shared/providers/src/swapper/types.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import type { Token } from '@origin/shared/contracts'; import type { HexAddress } from '@origin/shared/utils'; +import type { QueryClient } from '@tanstack/react-query'; import type { MessageDescriptor } from 'react-intl'; import type { TransactionReceipt } from 'viem'; import type { Config } from 'wagmi'; @@ -19,18 +20,23 @@ type Args = { estimatedRoute: EstimatedSwapRoute; }; +type Client = { + config: Config; + queryClient: QueryClient; +}; + export type IsRouteAvailable = ( - config: Config, + client: Client, args: Pick, ) => Promise; export type EstimateAmount = ( - config: Config, + client: Client, args: Pick, ) => Promise; export type EstimateGas = ( - config: Config, + client: Client, args: Pick< Args, 'tokenIn' | 'tokenOut' | 'amountIn' | 'amountOut' | 'slippage' @@ -38,7 +44,7 @@ export type EstimateGas = ( ) => Promise; export type EstimateRoute = ( - config: Config, + client: Client, args: Pick< Args, 'tokenIn' | 'tokenOut' | 'amountIn' | 'amountOut' | 'slippage' | 'route' @@ -46,22 +52,22 @@ export type EstimateRoute = ( ) => Promise; export type Allowance = ( - config: Config, + client: Client, args: Pick, ) => Promise; export type EstimateApprovalGas = ( - config: Config, + client: Client, args: Pick, ) => Promise; export type Approve = ( - config: Config, + client: Client, args: Pick, ) => Promise; export type Swap = ( - config: Config, + client: Client, args: Pick< Args, | 'tokenIn' diff --git a/libs/shared/routes/.babelrc b/libs/shared/routes/.babelrc new file mode 100644 index 000000000..1ea870ead --- /dev/null +++ b/libs/shared/routes/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@nx/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ], + "plugins": [] +} diff --git a/libs/shared/routes/.eslintrc.json b/libs/shared/routes/.eslintrc.json new file mode 100644 index 000000000..75b85077d --- /dev/null +++ b/libs/shared/routes/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["plugin:@nx/react", "../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/shared/routes/README.md b/libs/shared/routes/README.md new file mode 100644 index 000000000..2564dc132 --- /dev/null +++ b/libs/shared/routes/README.md @@ -0,0 +1,7 @@ +# shared-routes + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test shared-routes` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/shared/routes/project.json b/libs/shared/routes/project.json new file mode 100644 index 000000000..952f5ac3c --- /dev/null +++ b/libs/shared/routes/project.json @@ -0,0 +1,12 @@ +{ + "name": "shared-routes", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/shared/routes/src", + "projectType": "library", + "tags": [], + "targets": { + "lint": { + "executor": "@nx/eslint:lint" + } + } +} diff --git a/libs/shared/routes/src/constants.ts b/libs/shared/routes/src/constants.ts new file mode 100644 index 000000000..50e0bb57c --- /dev/null +++ b/libs/shared/routes/src/constants.ts @@ -0,0 +1,2 @@ +export const GAS_BUFFER = 10n; // 10% +export const MAX_PRICE = 1.2; diff --git a/libs/prime/restake/src/actions/index.ts b/libs/shared/routes/src/defaultRoute.ts similarity index 61% rename from libs/prime/restake/src/actions/index.ts rename to libs/shared/routes/src/defaultRoute.ts index 3876690e1..2daf60b2d 100644 --- a/libs/prime/restake/src/actions/index.ts +++ b/libs/shared/routes/src/defaultRoute.ts @@ -1,14 +1,8 @@ import { defineMessage } from 'react-intl'; -import restake from './restake'; -import uniswap from './uniwsap'; -import zapper from './zapper'; - import type { SwapApi } from '@origin/shared/providers'; -import type { RestakeAction } from '../types'; - -const defaultApi: SwapApi = { +export const defaultRoute: SwapApi = { isRouteAvailable: async () => true, estimateAmount: async (config, { amountIn }) => { console.log('Amount estimation not implemented'); @@ -50,23 +44,6 @@ const defaultApi: SwapApi = { console.log('Route swap operation not implemented'); return null; }, - buttonLabel: defineMessage({ defaultMessage: 'Stake' }), - routeLabel: defineMessage({ defaultMessage: 'PrimeStaked' }), -}; - -export const restakeActions: Record = { - restake: { - ...defaultApi, - ...restake, - }, - uniswap: { - ...defaultApi, - ...uniswap, - buttonLabel: defineMessage({ defaultMessage: 'Swap with Uniswap' }), - routeLabel: defineMessage({ defaultMessage: 'Uniswap V3' }), - }, - zapper: { - ...defaultApi, - ...zapper, - }, + buttonLabel: defineMessage({ defaultMessage: 'Swap' }), + routeLabel: defineMessage({ defaultMessage: 'Swap' }), }; diff --git a/libs/shared/routes/src/index.ts b/libs/shared/routes/src/index.ts new file mode 100644 index 000000000..3808f9b5f --- /dev/null +++ b/libs/shared/routes/src/index.ts @@ -0,0 +1,4 @@ +export * from './oeth'; +export * from './ousd'; +export * from './prime'; +export * from './types'; diff --git a/libs/shared/routes/src/oeth/index.ts b/libs/shared/routes/src/oeth/index.ts new file mode 100644 index 000000000..217a220cb --- /dev/null +++ b/libs/shared/routes/src/oeth/index.ts @@ -0,0 +1,9 @@ +export * from './mintVaultOeth'; +export * from './redeemVaultOeth'; +export * from './swapCurveOeth'; +export * from './swapCurveOethEth'; +export * from './swapCurveOethSfrxeth'; +export * from './swapZapperOethEth'; +export * from './swapZapperOethSfrxeth'; +export * from './unwrapOethWoeth'; +export * from './wrapOethWoeth'; diff --git a/libs/oeth/swap/src/actions/mintVault.ts b/libs/shared/routes/src/oeth/mintVaultOeth.ts similarity index 82% rename from libs/oeth/swap/src/actions/mintVault.ts rename to libs/shared/routes/src/oeth/mintVaultOeth.ts index a1e752952..e16ea1bd6 100644 --- a/libs/oeth/swap/src/actions/mintVault.ts +++ b/libs/shared/routes/src/oeth/mintVaultOeth.ts @@ -1,7 +1,6 @@ -import { queryClient } from '@origin/oeth/shared'; import { contracts } from '@origin/shared/contracts'; import { simulateContractWithTxTracker } from '@origin/shared/providers'; -import { isNilOrEmpty, subtractSlippage } from '@origin/shared/utils'; +import { isNilOrEmpty, subPercentage } from '@origin/shared/utils'; import { getAccount, getPublicClient, @@ -13,6 +12,7 @@ import { import { erc20Abi, formatUnits, parseUnits } from 'viem'; import { GAS_BUFFER } from '../constants'; +import { defaultRoute } from '../defaultRoute'; import type { Allowance, @@ -25,7 +25,7 @@ import type { } from '@origin/shared/providers'; import type { EstimateAmount } from '@origin/shared/providers'; -const isRouteAvailable: IsRouteAvailable = async (config, { tokenIn }) => { +const isRouteAvailable: IsRouteAvailable = async ({ config }, { tokenIn }) => { try { if (tokenIn?.address) { await readContract(config, { @@ -44,7 +44,7 @@ const isRouteAvailable: IsRouteAvailable = async (config, { tokenIn }) => { }; const estimateAmount: EstimateAmount = async ( - config, + { config }, { tokenIn, tokenOut, amountIn }, ) => { if (amountIn === 0n || !tokenIn?.address) { @@ -69,7 +69,7 @@ const estimateAmount: EstimateAmount = async ( }; const estimateGas: EstimateGas = async ( - config, + { config, queryClient }, { tokenIn, tokenOut, amountIn, slippage, amountOut }, ) => { let gasEstimate = 0n; @@ -83,14 +83,17 @@ const estimateGas: EstimateGas = async ( const { address } = getAccount(config); - const minAmountOut = subtractSlippage(amountOut, tokenOut.decimals, slippage); + const minAmountOut = subPercentage( + [amountOut ?? 0n, tokenOut.decimals], + slippage, + ); try { gasEstimate = await publicClient.estimateContractGas({ address: contracts.mainnet.OETHVault.address, abi: contracts.mainnet.OETHVault.abi, functionName: 'mint', - args: [tokenIn.address, amountIn, minAmountOut], + args: [tokenIn.address, amountIn, minAmountOut[0]], account: address, }); @@ -131,7 +134,7 @@ const estimateGas: EstimateGas = async ( return gasEstimate; }; -const allowance: Allowance = async (config, { tokenIn }) => { +const allowance: Allowance = async ({ config }, { tokenIn }) => { const { address } = getAccount(config); if (!address || !tokenIn?.address) { @@ -150,7 +153,7 @@ const allowance: Allowance = async (config, { tokenIn }) => { }; const estimateApprovalGas: EstimateApprovalGas = async ( - config, + { config }, { tokenIn, amountIn }, ) => { let approvalEstimate = 0n; @@ -177,7 +180,7 @@ const estimateApprovalGas: EstimateApprovalGas = async ( }; const estimateRoute: EstimateRoute = async ( - config, + client, { tokenIn, tokenOut, amountIn, route, slippage }, ) => { if (amountIn === 0n) { @@ -192,11 +195,11 @@ const estimateRoute: EstimateRoute = async ( } const [estimatedAmount, allowanceAmount, approvalGas] = await Promise.all([ - estimateAmount(config, { tokenIn, tokenOut, amountIn }), - allowance(config, { tokenIn, tokenOut }), - estimateApprovalGas(config, { amountIn, tokenIn, tokenOut }), + estimateAmount(client, { tokenIn, tokenOut, amountIn }), + allowance(client, { tokenIn, tokenOut }), + estimateApprovalGas(client, { amountIn, tokenIn, tokenOut }), ]); - const gas = await estimateGas(config, { + const gas = await estimateGas(client, { tokenIn, tokenOut, amountIn, @@ -216,7 +219,7 @@ const estimateRoute: EstimateRoute = async ( }; }; -const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { +const approve: Approve = async ({ config }, { tokenIn, amountIn }) => { if (!tokenIn?.address) { return null; } @@ -234,7 +237,7 @@ const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { }; const swap: Swap = async ( - config, + { config, queryClient }, { tokenIn, tokenOut, amountIn, slippage, amountOut }, ) => { const { address } = getAccount(config); @@ -243,28 +246,37 @@ const swap: Swap = async ( return null; } - const approved = await allowance(config, { tokenIn, tokenOut }); + const approved = await allowance( + { config, queryClient }, + { tokenIn, tokenOut }, + ); if (approved < amountIn) { throw new Error(`Mint vault is not approved`); } - const minAmountOut = subtractSlippage(amountOut, tokenOut.decimals, slippage); - - const estimatedGas = await estimateGas(config, { - amountIn, + const minAmountOut = subPercentage( + [amountOut ?? 0n, tokenOut.decimals], slippage, - tokenIn, - tokenOut, - amountOut, - }); + ); + + const estimatedGas = await estimateGas( + { config, queryClient }, + { + amountIn, + slippage, + tokenIn, + tokenOut, + amountOut, + }, + ); const gas = estimatedGas + (estimatedGas * GAS_BUFFER) / 100n; const { request } = await simulateContractWithTxTracker(config, { address: contracts.mainnet.OETHVault.address, abi: contracts.mainnet.OETHVault.abi, functionName: 'mint', - args: [tokenIn.address, amountIn, minAmountOut], + args: [tokenIn.address, amountIn, minAmountOut[0]], gas, chainId: contracts.mainnet.OETHVault.chainId, }); @@ -273,7 +285,8 @@ const swap: Swap = async ( return hash; }; -export default { +export const mintVaultOeth = { + ...defaultRoute, isRouteAvailable, estimateAmount, estimateGas, diff --git a/libs/defi/oeth/src/redeem/actions/redeemVault.ts b/libs/shared/routes/src/oeth/redeemVaultOeth.ts similarity index 83% rename from libs/defi/oeth/src/redeem/actions/redeemVault.ts rename to libs/shared/routes/src/oeth/redeemVaultOeth.ts index 01937d627..133144cea 100644 --- a/libs/defi/oeth/src/redeem/actions/redeemVault.ts +++ b/libs/shared/routes/src/oeth/redeemVaultOeth.ts @@ -11,6 +11,7 @@ import { import { erc20Abi, formatUnits, maxUint256 } from 'viem'; import { GAS_BUFFER } from '../constants'; +import { defaultRoute } from '../defaultRoute'; import type { Allowance, @@ -24,7 +25,7 @@ import type { import type { EstimateAmount } from '@origin/shared/providers'; const isRouteAvailable: IsRouteAvailable = async ( - config, + { config }, { tokenIn, amountIn }, ) => { try { @@ -34,6 +35,7 @@ const isRouteAvailable: IsRouteAvailable = async ( abi: tokens.mainnet.WETH.abi, functionName: 'balanceOf', args: [contracts.mainnet.OETHVault.address], + chainId: tokens.mainnet.WETH.chainId, }); return amountIn <= bal; @@ -43,17 +45,19 @@ const isRouteAvailable: IsRouteAvailable = async ( return false; }; -const estimateAmount: EstimateAmount = async (config, { amountIn }) => { +const estimateAmount: EstimateAmount = async ({ config }, { amountIn }) => { // 0.1% redeem fee return amountIn - amountIn / 1000n; }; const estimateGas: EstimateGas = async ( - config, + { config }, { tokenIn, tokenOut, amountIn, slippage, amountOut }, ) => { let gasEstimate = 0n; - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { + chainId: contracts.mainnet.OETHVault.chainId, + }); if (amountIn === 0n || !publicClient || !tokenIn?.address) { return gasEstimate; @@ -79,17 +83,17 @@ const estimateGas: EstimateGas = async ( return gasEstimate; }; -const allowance: Allowance = async (config, { tokenIn }) => { +const allowance: Allowance = async ({ config }, { tokenIn }) => { return maxUint256; }; const estimateApprovalGas: EstimateApprovalGas = async ( - config, + { config }, { tokenIn, amountIn }, ) => { let approvalEstimate = 0n; const { address } = getAccount(config); - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { chainId: tokenIn.chainId }); if (amountIn === 0n || !address || !publicClient || !tokenIn?.address) { return approvalEstimate; @@ -150,7 +154,7 @@ const estimateRoute: EstimateRoute = async ( }; }; -const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { +const approve: Approve = async ({ config }, { tokenIn, amountIn }) => { if (!tokenIn?.address) { return null; } @@ -160,6 +164,7 @@ const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { abi: erc20Abi, functionName: 'approve', args: [contracts.mainnet.OETHVault.address, amountIn], + chainId: tokenIn.chainId, }); const hash = await writeContract(config, request); @@ -167,7 +172,7 @@ const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { }; const swap: Swap = async ( - config, + { config, queryClient }, { tokenIn, tokenOut, amountIn, slippage, amountOut }, ) => { const { address } = getAccount(config); @@ -181,13 +186,16 @@ const swap: Swap = async ( slippage, ); - const estimatedGas = await estimateGas(config, { - amountIn, - slippage, - tokenIn, - tokenOut, - amountOut, - }); + const estimatedGas = await estimateGas( + { config, queryClient }, + { + amountIn, + slippage, + tokenIn, + tokenOut, + amountOut, + }, + ); const gas = estimatedGas + (estimatedGas * GAS_BUFFER) / 100n; const { request } = await simulateContractWithTxTracker(config, { @@ -196,13 +204,15 @@ const swap: Swap = async ( functionName: 'redeem', args: [amountIn, minAmountOut[0]], gas, + chainId: contracts.mainnet.OETHVault.chainId, }); const hash = await writeContract(config, request); return hash; }; -export default { +export const redeemVaultOeth = { + ...defaultRoute, isRouteAvailable, estimateAmount, estimateGas, diff --git a/libs/defi/oeth/src/redeem/actions/swapCurve/curveRoutes.ts b/libs/shared/routes/src/oeth/swapCurveOeth/curveRoutes.ts similarity index 100% rename from libs/defi/oeth/src/redeem/actions/swapCurve/curveRoutes.ts rename to libs/shared/routes/src/oeth/swapCurveOeth/curveRoutes.ts diff --git a/libs/oeth/swap/src/actions/swapCurve/index.ts b/libs/shared/routes/src/oeth/swapCurveOeth/index.ts similarity index 85% rename from libs/oeth/swap/src/actions/swapCurve/index.ts rename to libs/shared/routes/src/oeth/swapCurveOeth/index.ts index 9fc9a4a9f..e4a0a620f 100644 --- a/libs/oeth/swap/src/actions/swapCurve/index.ts +++ b/libs/shared/routes/src/oeth/swapCurveOeth/index.ts @@ -1,4 +1,3 @@ -import { queryClient } from '@origin/oeth/shared'; import { isNativeCurrency, simulateContractWithTxTracker, @@ -7,7 +6,7 @@ import { import { ETH_ADDRESS_CURVE, isNilOrEmpty, - subtractSlippage, + subPercentage, ZERO_ADDRESS, } from '@origin/shared/utils'; import { @@ -21,6 +20,7 @@ import { path } from 'ramda'; import { erc20Abi, formatUnits, maxUint256 } from 'viem'; import { GAS_BUFFER } from '../../constants'; +import { defaultRoute } from '../../defaultRoute'; import { curveRoutes } from './curveRoutes'; import type { @@ -34,7 +34,7 @@ import type { } from '@origin/shared/providers'; const estimateAmount: EstimateAmount = async ( - config, + { config, queryClient }, { tokenIn, tokenOut, amountIn }, ) => { const curve = await queryClient.fetchQuery({ @@ -69,7 +69,7 @@ const estimateAmount: EstimateAmount = async ( }; const estimateGas: EstimateGas = async ( - config, + { config, queryClient }, { tokenIn, tokenOut, amountIn, amountOut, slippage }, ) => { let gasEstimate = 0n; @@ -80,7 +80,10 @@ const estimateGas: EstimateGas = async ( const { address } = getAccount(config); - const minAmountOut = subtractSlippage(amountOut, tokenOut.decimals, slippage); + const minAmountOut = subPercentage( + [amountOut ?? 0n, tokenOut.decimals], + slippage, + ); const curveConfig = path<{ routes: unknown[]; swapParams: unknown[] }>( [tokenIn.symbol, tokenOut.symbol], @@ -99,7 +102,6 @@ const estimateGas: EstimateGas = async ( staleTime: Infinity, }); - const isTokenInNative = isNativeCurrency(tokenIn); const publicClient = getPublicClient(config, { chainId: curve.CurveRegistryExchange.chainId, }); @@ -108,6 +110,8 @@ const estimateGas: EstimateGas = async ( return gasEstimate; } + const isTokenInNative = isNativeCurrency(tokenIn); + try { gasEstimate = await publicClient.estimateContractGas({ address: curve.CurveRegistryExchange.address, @@ -117,7 +121,7 @@ const estimateGas: EstimateGas = async ( curveConfig.routes, curveConfig.swapParams, amountIn, - minAmountOut, + minAmountOut[0], ], account: address ?? ETH_ADDRESS_CURVE, ...(isTokenInNative && { value: amountIn }), @@ -129,7 +133,7 @@ const estimateGas: EstimateGas = async ( return gasEstimate; }; -const allowance: Allowance = async (config, { tokenIn }) => { +const allowance: Allowance = async ({ config, queryClient }, { tokenIn }) => { const { address } = getAccount(config); const curve = await queryClient.fetchQuery({ queryKey: useCurve.getKey(), @@ -157,7 +161,7 @@ const allowance: Allowance = async (config, { tokenIn }) => { }; const estimateApprovalGas: EstimateApprovalGas = async ( - config, + { config, queryClient }, { tokenIn, amountIn }, ) => { let approvalEstimate = 0n; @@ -190,7 +194,7 @@ const estimateApprovalGas: EstimateApprovalGas = async ( }; const estimateRoute: EstimateRoute = async ( - config, + client, { tokenIn, tokenOut, amountIn, route, slippage }, ) => { if (amountIn === 0n) { @@ -205,11 +209,11 @@ const estimateRoute: EstimateRoute = async ( } const [estimatedAmount, allowanceAmount, approvalGas] = await Promise.all([ - estimateAmount(config, { tokenIn, tokenOut, amountIn }), - allowance(config, { tokenIn, tokenOut }), - estimateApprovalGas(config, { amountIn, tokenIn, tokenOut }), + estimateAmount(client, { tokenIn, tokenOut, amountIn }), + allowance(client, { tokenIn, tokenOut }), + estimateApprovalGas(client, { amountIn, tokenIn, tokenOut }), ]); - const gas = await estimateGas(config, { + const gas = await estimateGas(client, { tokenIn, tokenOut, amountIn, @@ -229,7 +233,10 @@ const estimateRoute: EstimateRoute = async ( }; }; -const approve: Approve = async (config, { tokenIn, amountIn }) => { +const approve: Approve = async ( + { config, queryClient }, + { tokenIn, amountIn }, +) => { if (!tokenIn?.address) { return null; } @@ -251,7 +258,7 @@ const approve: Approve = async (config, { tokenIn, amountIn }) => { }; const swap: Swap = async ( - config, + { config, queryClient }, { tokenIn, tokenOut, amountIn, amountOut, slippage }, ) => { const { address } = getAccount(config); @@ -265,13 +272,19 @@ const swap: Swap = async ( queryFn: useCurve.fetcher(config), staleTime: Infinity, }); - const approved = await allowance(config, { tokenIn, tokenOut }); + const approved = await allowance( + { config, queryClient }, + { tokenIn, tokenOut }, + ); if (approved < amountIn) { throw new Error(`Swap curve is not approved`); } - const minAmountOut = subtractSlippage(amountOut, tokenOut.decimals, slippage); + const minAmountOut = subPercentage( + [amountOut ?? 0n, tokenOut.decimals], + slippage, + ); const curveConfig = path<{ routes: unknown[]; swapParams: unknown[] }>( [tokenIn.symbol, tokenOut?.symbol], @@ -284,13 +297,16 @@ const swap: Swap = async ( ); } - const estimatedGas = await estimateGas(config, { - amountIn, - slippage, - tokenIn, - tokenOut, - amountOut, - }); + const estimatedGas = await estimateGas( + { config, queryClient }, + { + amountIn, + slippage, + tokenIn, + tokenOut, + amountOut, + }, + ); const gas = estimatedGas + (estimatedGas * GAS_BUFFER) / 100n; const isTokenInNative = isNativeCurrency(tokenIn); @@ -299,9 +315,14 @@ const swap: Swap = async ( address: curve.CurveRegistryExchange.address, abi: curve.CurveRegistryExchange.abi, functionName: 'exchange_multiple', - args: [curveConfig.routes, curveConfig.swapParams, amountIn, minAmountOut], - chainId: curve.CurveRegistryExchange.chainId, + args: [ + curveConfig.routes, + curveConfig.swapParams, + amountIn, + minAmountOut[0], + ], gas, + chainId: curve.CurveRegistryExchange.chainId, ...(isTokenInNative && { value: amountIn }), }); const hash = await writeContract(config, request); @@ -309,7 +330,8 @@ const swap: Swap = async ( return hash; }; -export default { +export const SwapCurveOeth = { + ...defaultRoute, estimateAmount, estimateGas, estimateRoute, diff --git a/libs/defi/oeth/src/swap/actions/swapCurveEth.ts b/libs/shared/routes/src/oeth/swapCurveOethEth.ts similarity index 90% rename from libs/defi/oeth/src/swap/actions/swapCurveEth.ts rename to libs/shared/routes/src/oeth/swapCurveOethEth.ts index 8595c58a0..bbd999994 100644 --- a/libs/defi/oeth/src/swap/actions/swapCurveEth.ts +++ b/libs/shared/routes/src/oeth/swapCurveOethEth.ts @@ -1,4 +1,3 @@ -import { queryClient } from '@origin/defi/shared'; import { contracts } from '@origin/shared/contracts'; import { isNativeCurrency, @@ -15,6 +14,7 @@ import { import { formatUnits, isAddressEqual, maxUint256 } from 'viem'; import { GAS_BUFFER } from '../constants'; +import { defaultRoute } from '../defaultRoute'; import type { Allowance, @@ -27,7 +27,7 @@ import type { } from '@origin/shared/providers'; const estimateAmount: EstimateAmount = async ( - config, + { config, queryClient }, { tokenIn, tokenOut, amountIn }, ) => { if (amountIn === 0n) { @@ -39,6 +39,7 @@ const estimateAmount: EstimateAmount = async ( queryFn: useCurve.fetcher(config), staleTime: Infinity, }); + const amountOut = await readContract(config, { address: curve.CurveRegistryExchange.address, abi: curve.CurveRegistryExchange.abi, @@ -49,17 +50,20 @@ const estimateAmount: EstimateAmount = async ( tokenOut.address ?? ETH_ADDRESS_CURVE, amountIn, ], + chainId: curve.CurveRegistryExchange.chainId, }); return amountOut as unknown as bigint; }; const estimateGas: EstimateGas = async ( - config, + { config, queryClient }, { tokenIn, tokenOut, amountIn, amountOut, slippage }, ) => { let gasEstimate = 0n; - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { + chainId: contracts.mainnet.OETHCurvePool.chainId, + }); if (amountIn === 0n || !publicClient) { return gasEstimate; @@ -165,7 +169,7 @@ const approve: Approve = async () => { }; const swap: Swap = async ( - config, + { config, queryClient }, { tokenIn, tokenOut, amountIn, amountOut, slippage }, ) => { if (amountIn === 0n) { @@ -182,13 +186,16 @@ const swap: Swap = async ( queryFn: useCurve.fetcher(config), staleTime: Infinity, }); - const estimatedGas = await estimateGas(config, { - amountIn, - slippage, - tokenIn, - tokenOut, - amountOut, - }); + const estimatedGas = await estimateGas( + { config, queryClient }, + { + amountIn, + slippage, + tokenIn, + tokenOut, + amountOut, + }, + ); const gas = estimatedGas + (estimatedGas * GAS_BUFFER) / 100n; const isTokenInNative = isNativeCurrency(tokenIn); @@ -212,6 +219,7 @@ const swap: Swap = async ( minAmountOut[0], ], gas, + chainId: contracts.mainnet.OETHCurvePool.chainId, ...(isTokenInNative && { value: amountIn }), }); const hash = await writeContract(config, request); @@ -219,7 +227,8 @@ const swap: Swap = async ( return hash; }; -export default { +export const swapCurveOethEth = { + ...defaultRoute, estimateAmount, estimateGas, estimateRoute, diff --git a/libs/oeth/swap/src/actions/swapCurveSfrxeth.ts b/libs/shared/routes/src/oeth/swapCurveOethSfrxeth.ts similarity index 86% rename from libs/oeth/swap/src/actions/swapCurveSfrxeth.ts rename to libs/shared/routes/src/oeth/swapCurveOethSfrxeth.ts index e6d7c6297..e6e8ab616 100644 --- a/libs/oeth/swap/src/actions/swapCurveSfrxeth.ts +++ b/libs/shared/routes/src/oeth/swapCurveOethSfrxeth.ts @@ -3,7 +3,7 @@ import { simulateContractWithTxTracker } from '@origin/shared/providers'; import { ETH_ADDRESS_CURVE, isNilOrEmpty, - subtractSlippage, + subPercentage, } from '@origin/shared/utils'; import { getAccount, @@ -15,6 +15,7 @@ import { import { erc20Abi, formatUnits } from 'viem'; import { GAS_BUFFER } from '../constants'; +import { defaultRoute } from '../defaultRoute'; import type { Allowance, @@ -49,7 +50,7 @@ const curveConfig = { ], } as const; -const estimateAmount: EstimateAmount = async (config, { amountIn }) => { +const estimateAmount: EstimateAmount = async ({ config }, { amountIn }) => { if (amountIn === 0n) { return 0n; } @@ -66,7 +67,7 @@ const estimateAmount: EstimateAmount = async (config, { amountIn }) => { }; const estimateGas: EstimateGas = async ( - config, + { config }, { tokenOut, amountIn, amountOut, slippage }, ) => { let gasEstimate = 0n; @@ -79,14 +80,17 @@ const estimateGas: EstimateGas = async ( return gasEstimate; } - const minAmountOut = subtractSlippage(amountOut, tokenOut.decimals, slippage); + const minAmountOut = subPercentage( + [amountOut ?? 0n, tokenOut.decimals], + slippage, + ); try { gasEstimate = await publicClient.estimateContractGas({ address: contracts.mainnet.CurveRouter.address, abi: contracts.mainnet.CurveRouter.abi, functionName: 'exchange', - args: [curveConfig.routes, curveConfig.params, amountIn, minAmountOut], + args: [curveConfig.routes, curveConfig.params, amountIn, minAmountOut[0]], account: address ?? ETH_ADDRESS_CURVE, }); } catch (e) { @@ -96,7 +100,7 @@ const estimateGas: EstimateGas = async ( return gasEstimate; }; -const allowance: Allowance = async (config, { tokenIn }) => { +const allowance: Allowance = async ({ config }, { tokenIn }) => { const { address } = getAccount(config); if (!address || !tokenIn?.address) { @@ -115,7 +119,7 @@ const allowance: Allowance = async (config, { tokenIn }) => { }; const estimateApprovalGas: EstimateApprovalGas = async ( - config, + { config }, { tokenIn, amountIn }, ) => { let approvalEstimate = 0n; @@ -181,7 +185,7 @@ const estimateRoute: EstimateRoute = async ( }; }; -const approve: Approve = async (config, { tokenIn, amountIn }) => { +const approve: Approve = async ({ config }, { tokenIn, amountIn }) => { if (!tokenIn?.address) { return null; } @@ -199,7 +203,7 @@ const approve: Approve = async (config, { tokenIn, amountIn }) => { }; const swap: Swap = async ( - config, + { config, queryClient }, { tokenIn, tokenOut, amountIn, amountOut, slippage }, ) => { const { address } = getAccount(config); @@ -208,28 +212,37 @@ const swap: Swap = async ( return null; } - const approved = await allowance(config, { tokenIn, tokenOut }); + const approved = await allowance( + { config, queryClient }, + { tokenIn, tokenOut }, + ); if (approved < amountIn) { throw new Error(`Swap curve is not approved`); } - const minAmountOut = subtractSlippage(amountOut, tokenOut.decimals, slippage); - - const estimatedGas = await estimateGas(config, { - amountIn, + const minAmountOut = subPercentage( + [amountOut ?? 0n, tokenOut.decimals], slippage, - tokenIn, - tokenOut, - amountOut, - }); + ); + + const estimatedGas = await estimateGas( + { config, queryClient }, + { + amountIn, + slippage, + tokenIn, + tokenOut, + amountOut, + }, + ); const gas = estimatedGas + (estimatedGas * GAS_BUFFER) / 100n; const { request } = await simulateContractWithTxTracker(config, { address: contracts.mainnet.CurveRouter.address, abi: contracts.mainnet.CurveRouter.abi, functionName: 'exchange', - args: [curveConfig.routes, curveConfig.params, amountIn, minAmountOut], + args: [curveConfig.routes, curveConfig.params, amountIn, minAmountOut[0]], account: address, gas, chainId: contracts.mainnet.CurveRouter.chainId, @@ -239,7 +252,8 @@ const swap: Swap = async ( return hash; }; -export default { +export const swapCurveOethSfrxeth = { + ...defaultRoute, estimateAmount, estimateGas, estimateRoute, diff --git a/libs/oeth/swap/src/actions/swapZapperEth.ts b/libs/shared/routes/src/oeth/swapZapperOethEth.ts similarity index 87% rename from libs/oeth/swap/src/actions/swapZapperEth.ts rename to libs/shared/routes/src/oeth/swapZapperOethEth.ts index a8d5c8086..c15ae5a06 100644 --- a/libs/oeth/swap/src/actions/swapZapperEth.ts +++ b/libs/shared/routes/src/oeth/swapZapperOethEth.ts @@ -11,6 +11,7 @@ import { import { erc20Abi, formatUnits, maxUint256 } from 'viem'; import { GAS_BUFFER } from '../constants'; +import { defaultRoute } from '../defaultRoute'; import type { Allowance, @@ -26,7 +27,7 @@ const estimateAmount: EstimateAmount = async (config, { amountIn }) => { return amountIn; }; -const estimateGas: EstimateGas = async (config, { amountIn }) => { +const estimateGas: EstimateGas = async ({ config }, { amountIn }) => { let gasEstimate = 200000n; const { address } = getAccount(config); @@ -51,7 +52,7 @@ const estimateGas: EstimateGas = async (config, { amountIn }) => { return gasEstimate; }; -const allowance: Allowance = async (config, { tokenIn, tokenOut }) => { +const allowance: Allowance = async ({ config }, { tokenIn, tokenOut }) => { const { address } = getAccount(config); if (!address) { @@ -74,7 +75,7 @@ const allowance: Allowance = async (config, { tokenIn, tokenOut }) => { }; const estimateApprovalGas: EstimateApprovalGas = async ( - config, + { config }, { tokenIn, tokenOut, amountIn }, ) => { let approvalEstimate = 0n; @@ -141,7 +142,10 @@ const estimateRoute: EstimateRoute = async ( }; }; -const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { +const approve: Approve = async ( + { config }, + { tokenIn, tokenOut, amountIn }, +) => { if (!tokenIn?.address || !tokenOut?.address) { return null; } @@ -159,7 +163,7 @@ const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { }; const swap: Swap = async ( - config, + { config, queryClient }, { tokenIn, tokenOut, amountIn, slippage }, ) => { const { address } = getAccount(config); @@ -168,18 +172,24 @@ const swap: Swap = async ( return null; } - const approved = await allowance(config, { tokenIn, tokenOut }); + const approved = await allowance( + { config, queryClient }, + { tokenIn, tokenOut }, + ); if (approved < amountIn) { throw new Error(`Swap zapper is not approved`); } - const estimatedGas = await estimateGas(config, { - tokenIn, - tokenOut, - amountIn, - slippage, - }); + const estimatedGas = await estimateGas( + { config, queryClient }, + { + tokenIn, + tokenOut, + amountIn, + slippage, + }, + ); const gas = estimatedGas + (estimatedGas * GAS_BUFFER) / 100n; const { request } = await simulateContractWithTxTracker(config, { @@ -195,7 +205,8 @@ const swap: Swap = async ( return hash; }; -export default { +export const swapZapperOethEth = { + ...defaultRoute, estimateAmount, estimateGas, estimateRoute, diff --git a/libs/oeth/swap/src/actions/swapZapperSfrxeth.ts b/libs/shared/routes/src/oeth/swapZapperOethSfrxeth.ts similarity index 85% rename from libs/oeth/swap/src/actions/swapZapperSfrxeth.ts rename to libs/shared/routes/src/oeth/swapZapperOethSfrxeth.ts index cfdcd6adf..28973b4e3 100644 --- a/libs/oeth/swap/src/actions/swapZapperSfrxeth.ts +++ b/libs/shared/routes/src/oeth/swapZapperOethSfrxeth.ts @@ -1,6 +1,6 @@ import { contracts, tokens } from '@origin/shared/contracts'; import { simulateContractWithTxTracker } from '@origin/shared/providers'; -import { isNilOrEmpty, subtractSlippage } from '@origin/shared/utils'; +import { isNilOrEmpty, subPercentage } from '@origin/shared/utils'; import { getAccount, getPublicClient, @@ -12,6 +12,7 @@ import { import { erc20Abi, formatUnits, maxUint256, parseUnits } from 'viem'; import { GAS_BUFFER } from '../constants'; +import { defaultRoute } from '../defaultRoute'; import type { Allowance, @@ -24,7 +25,7 @@ import type { } from '@origin/shared/providers'; const estimateAmount: EstimateAmount = async ( - config, + { config }, { tokenOut, amountIn }, ) => { if (amountIn === 0n) { @@ -69,7 +70,7 @@ const estimateGas: EstimateGas = async () => { return 90000n; }; -const allowance: Allowance = async (config, { tokenIn, tokenOut }) => { +const allowance: Allowance = async ({ config }, { tokenIn, tokenOut }) => { const { address } = getAccount(config); if (!address) { @@ -92,7 +93,7 @@ const allowance: Allowance = async (config, { tokenIn, tokenOut }) => { }; const estimateApprovalGas: EstimateApprovalGas = async ( - config, + { config }, { tokenIn, tokenOut, amountIn }, ) => { let approvalEstimate = 0n; @@ -159,7 +160,10 @@ const estimateRoute: EstimateRoute = async ( }; }; -const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { +const approve: Approve = async ( + { config }, + { tokenIn, tokenOut, amountIn }, +) => { if (!tokenIn?.address || !tokenOut?.address) { return null; } @@ -177,7 +181,7 @@ const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { }; const swap: Swap = async ( - config, + { config, queryClient }, { tokenIn, tokenOut, amountIn, slippage, amountOut }, ) => { const { address } = getAccount(config); @@ -186,28 +190,37 @@ const swap: Swap = async ( return null; } - const approved = await allowance(config, { tokenIn, tokenOut }); + const approved = await allowance( + { config, queryClient }, + { tokenIn, tokenOut }, + ); if (approved < amountIn) { throw new Error(`Swap zapper sfrxETH is not approved`); } - const minAmountOut = subtractSlippage(amountOut, tokenOut.decimals, slippage); - - const estimatedGas = await estimateGas(config, { - amountIn, + const minAmountOut = subPercentage( + [amountOut ?? 0n, tokenOut.decimals], slippage, - tokenIn, - tokenOut, - amountOut, - }); + ); + + const estimatedGas = await estimateGas( + { config, queryClient }, + { + amountIn, + slippage, + tokenIn, + tokenOut, + amountOut, + }, + ); const gas = estimatedGas + (estimatedGas * GAS_BUFFER) / 100n; const { request } = await simulateContractWithTxTracker(config, { address: contracts.mainnet.OETHZapper.address, abi: contracts.mainnet.OETHZapper.abi, functionName: 'depositSFRXETH', - args: [amountIn, minAmountOut], + args: [amountIn, minAmountOut[0]], gas, chainId: contracts.mainnet.OETHZapper.chainId, }); @@ -216,7 +229,8 @@ const swap: Swap = async ( return hash; }; -export default { +export const swapZapperOethSfrxeth = { + ...defaultRoute, estimateAmount, estimateGas, estimateRoute, diff --git a/libs/oeth/swap/src/actions/unwrapWOETH.ts b/libs/shared/routes/src/oeth/unwrapOethWoeth.ts similarity index 91% rename from libs/oeth/swap/src/actions/unwrapWOETH.ts rename to libs/shared/routes/src/oeth/unwrapOethWoeth.ts index 7b5bcb5a3..ed80173ed 100644 --- a/libs/oeth/swap/src/actions/unwrapWOETH.ts +++ b/libs/shared/routes/src/oeth/unwrapOethWoeth.ts @@ -8,6 +8,8 @@ import { } from '@wagmi/core'; import { formatUnits, maxUint256 } from 'viem'; +import { defaultRoute } from '../defaultRoute'; + import type { Allowance, Approve, @@ -18,7 +20,7 @@ import type { Swap, } from '@origin/shared/providers'; -const estimateAmount: EstimateAmount = async (config, { amountIn }) => { +const estimateAmount: EstimateAmount = async ({ config }, { amountIn }) => { if (amountIn === 0n) { return 0n; } @@ -34,7 +36,7 @@ const estimateAmount: EstimateAmount = async (config, { amountIn }) => { return data as unknown as bigint; }; -const estimateGas: EstimateGas = async (config, { amountIn }) => { +const estimateGas: EstimateGas = async ({ config }, { amountIn }) => { let gasEstimate = 0n; const publicClient = getPublicClient(config, { @@ -128,7 +130,7 @@ const approve: Approve = async () => { return null; }; -const swap: Swap = async (config, { amountIn }) => { +const swap: Swap = async ({ config }, { amountIn }) => { const { address } = getAccount(config); if (amountIn === 0n || !address) { @@ -147,7 +149,8 @@ const swap: Swap = async (config, { amountIn }) => { return hash; }; -export default { +export const unwrapOethWoeth = { + ...defaultRoute, estimateAmount, estimateGas, estimateRoute, diff --git a/libs/oeth/swap/src/actions/wrapOETH.ts b/libs/shared/routes/src/oeth/wrapOethWoeth.ts similarity index 88% rename from libs/oeth/swap/src/actions/wrapOETH.ts rename to libs/shared/routes/src/oeth/wrapOethWoeth.ts index 02a6d3d4c..8d479d7f4 100644 --- a/libs/oeth/swap/src/actions/wrapOETH.ts +++ b/libs/shared/routes/src/oeth/wrapOethWoeth.ts @@ -10,6 +10,8 @@ import { } from '@wagmi/core'; import { erc20Abi, formatUnits } from 'viem'; +import { defaultRoute } from '../defaultRoute'; + import type { Allowance, Approve, @@ -20,7 +22,7 @@ import type { Swap, } from '@origin/shared/providers'; -const estimateAmount: EstimateAmount = async (config, { amountIn }) => { +const estimateAmount: EstimateAmount = async ({ config }, { amountIn }) => { if (amountIn === 0n) { return 0n; } @@ -36,7 +38,7 @@ const estimateAmount: EstimateAmount = async (config, { amountIn }) => { return data as unknown as bigint; }; -const estimateGas: EstimateGas = async (config, { amountIn }) => { +const estimateGas: EstimateGas = async ({ config }, { amountIn }) => { let gasEstimate = 0n; const publicClient = getPublicClient(config, { @@ -81,7 +83,7 @@ const estimateGas: EstimateGas = async (config, { amountIn }) => { return gasEstimate; }; -const allowance: Allowance = async (config, { tokenIn }) => { +const allowance: Allowance = async ({ config }, { tokenIn }) => { const { address } = getAccount(config); if (!address || !tokenIn?.address) { @@ -100,7 +102,7 @@ const allowance: Allowance = async (config, { tokenIn }) => { }; const estimateApprovalGas: EstimateApprovalGas = async ( - config, + { config }, { tokenIn, amountIn }, ) => { let approvalEstimate = 0n; @@ -164,7 +166,7 @@ const estimateRoute: EstimateRoute = async ( }; }; -const approve: Approve = async (config, { tokenIn, amountIn }) => { +const approve: Approve = async ({ config }, { tokenIn, amountIn }) => { if (!tokenIn?.address) { return null; } @@ -181,14 +183,20 @@ const approve: Approve = async (config, { tokenIn, amountIn }) => { return hash; }; -const swap: Swap = async (config, { tokenIn, tokenOut, amountIn }) => { +const swap: Swap = async ( + { config, queryClient }, + { tokenIn, tokenOut, amountIn }, +) => { const { address } = getAccount(config); if (amountIn === 0n || isNilOrEmpty(address)) { return null; } - const approved = await allowance(config, { tokenIn, tokenOut }); + const approved = await allowance( + { config, queryClient }, + { tokenIn, tokenOut }, + ); if (approved < amountIn) { throw new Error(`Wrap OETH is not approved`); @@ -206,7 +214,8 @@ const swap: Swap = async (config, { tokenIn, tokenOut, amountIn }) => { return hash; }; -export default { +export const wrapOethWoeth = { + ...defaultRoute, estimateAmount, estimateGas, estimateRoute, diff --git a/libs/shared/routes/src/ousd/index.ts b/libs/shared/routes/src/ousd/index.ts new file mode 100644 index 000000000..0be4fea5b --- /dev/null +++ b/libs/shared/routes/src/ousd/index.ts @@ -0,0 +1,8 @@ +export * from './mintVaultOusd'; +export * from './swapCurveOusd'; +export * from './swapFlipperOusd'; +export * from './swapSushiswapOusd'; +export * from './swapUniswapV2Ousd'; +export * from './swapUniswapV3Ousd'; +export * from './unwrapOusdWousd'; +export * from './wrapOusdWousd'; diff --git a/libs/defi/ousd/src/swap/actions/mintVault.ts b/libs/shared/routes/src/ousd/mintVaultOusd.ts similarity index 85% rename from libs/defi/ousd/src/swap/actions/mintVault.ts rename to libs/shared/routes/src/ousd/mintVaultOusd.ts index 7cca9da17..eaaaa36b0 100644 --- a/libs/defi/ousd/src/swap/actions/mintVault.ts +++ b/libs/shared/routes/src/ousd/mintVaultOusd.ts @@ -1,4 +1,3 @@ -import { queryClient } from '@origin/ousd/shared'; import { contracts } from '@origin/shared/contracts'; import { simulateContractWithTxTracker } from '@origin/shared/providers'; import { @@ -17,6 +16,7 @@ import { import { formatUnits, parseUnits } from 'viem'; import { GAS_BUFFER } from '../constants'; +import { defaultRoute } from '../defaultRoute'; import type { Allowance, @@ -30,7 +30,7 @@ import type { } from '@origin/shared/providers'; const isRouteAvailable: IsRouteAvailable = async ( - config, + { config }, { amountIn, tokenIn }, ) => { try { @@ -40,6 +40,7 @@ const isRouteAvailable: IsRouteAvailable = async ( abi: contracts.mainnet.OUSDVault.abi, functionName: 'priceUnitMint', args: [tokenIn.address], + chainId: contracts.mainnet.OUSDVault.chainId, }); return ( @@ -54,7 +55,7 @@ const isRouteAvailable: IsRouteAvailable = async ( }; const estimateAmount: EstimateAmount = async ( - config, + { config }, { amountIn, tokenIn, tokenOut }, ) => { if (amountIn === 0n || !tokenIn?.address) { @@ -66,6 +67,7 @@ const estimateAmount: EstimateAmount = async ( abi: contracts.mainnet.OUSDVault.abi, functionName: 'priceUnitMint', args: [tokenIn.address], + chainId: contracts.mainnet.OUSDVault.chainId, }); return parseUnits( @@ -77,11 +79,13 @@ const estimateAmount: EstimateAmount = async ( }; const estimateGas: EstimateGas = async ( - config, + { config, queryClient }, { tokenIn, tokenOut, amountIn, slippage, amountOut }, ) => { let gasEstimate = 0n; - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { + chainId: contracts.mainnet.OUSDVault.chainId, + }); if (amountIn === 0n || !publicClient || !tokenIn?.address) { return gasEstimate; @@ -116,11 +120,13 @@ const estimateGas: EstimateGas = async ( address: contracts.mainnet.OUSDVault.address, abi: contracts.mainnet.OUSDVault.abi, functionName: 'rebaseThreshold', + chainId: contracts.mainnet.OUSDVault.chainId, }, { address: contracts.mainnet.OUSDVault.address, abi: contracts.mainnet.OUSDVault.abi, functionName: 'autoAllocateThreshold', + chainId: contracts.mainnet.OUSDVault.chainId, }, ], }), @@ -178,7 +184,7 @@ const estimateRoute: EstimateRoute = async ( }; }; -const allowance: Allowance = async (config, { tokenIn }) => { +const allowance: Allowance = async ({ config }, { tokenIn }) => { const { address } = getAccount(config); if (!address || !tokenIn?.address) { @@ -190,18 +196,19 @@ const allowance: Allowance = async (config, { tokenIn }) => { abi: tokenIn.abi, functionName: 'allowance', args: [address, contracts.mainnet.OUSDVault.address], + chainId: tokenIn.chainId, }); return allowance as unknown as bigint; }; const estimateApprovalGas: EstimateApprovalGas = async ( - config, + { config }, { tokenIn, amountIn }, ) => { let approvalEstimate = 0n; const { address } = getAccount(config); - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { chainId: tokenIn.chainId }); if (amountIn === 0n || !publicClient || !tokenIn?.address) { return approvalEstimate; @@ -222,7 +229,10 @@ const estimateApprovalGas: EstimateApprovalGas = async ( return approvalEstimate; }; -const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { +const approve: Approve = async ( + { config }, + { tokenIn, tokenOut, amountIn }, +) => { if (amountIn === 0n || !tokenIn?.address) { return null; } @@ -232,6 +242,7 @@ const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { abi: tokenIn.abi, functionName: 'approve', args: [contracts.mainnet.OUSDVault.address, amountIn], + chainId: tokenIn.chainId, }); const hash = await writeContract(config, request); @@ -239,7 +250,7 @@ const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { }; const swap: Swap = async ( - config, + { config, queryClient }, { tokenIn, tokenOut, amountIn, slippage, amountOut }, ) => { const { address } = getAccount(config); @@ -248,7 +259,10 @@ const swap: Swap = async ( return null; } - const approved = await allowance(config, { tokenIn, tokenOut }); + const approved = await allowance( + { config, queryClient }, + { tokenIn, tokenOut }, + ); if (approved < amountIn) { throw new Error(`Mint vault is not approved`); @@ -259,13 +273,16 @@ const swap: Swap = async ( slippage, ); - const estimatedGas = await estimateGas(config, { - amountIn, - slippage, - tokenIn, - tokenOut, - amountOut, - }); + const estimatedGas = await estimateGas( + { config, queryClient }, + { + amountIn, + slippage, + tokenIn, + tokenOut, + amountOut, + }, + ); const gas = estimatedGas + (estimatedGas * GAS_BUFFER) / 100n; const { request } = await simulateContractWithTxTracker(config, { @@ -274,13 +291,15 @@ const swap: Swap = async ( functionName: 'mint', args: [tokenIn.address, amountIn, minAmountOut], gas, + chainId: contracts.mainnet.OUSDVault.chainId, }); const hash = await writeContract(config, request); return hash; }; -export default { +export const mintVaultOusd = { + ...defaultRoute, isRouteAvailable, estimateAmount, estimateGas, diff --git a/libs/defi/ousd/src/swap/actions/swapCurve.ts b/libs/shared/routes/src/ousd/swapCurveOusd.ts similarity index 86% rename from libs/defi/ousd/src/swap/actions/swapCurve.ts rename to libs/shared/routes/src/ousd/swapCurveOusd.ts index ffeb08a98..943731ff8 100644 --- a/libs/defi/ousd/src/swap/actions/swapCurve.ts +++ b/libs/shared/routes/src/ousd/swapCurveOusd.ts @@ -1,4 +1,3 @@ -import { queryClient } from '@origin/ousd/shared'; import { contracts } from '@origin/shared/contracts'; import { simulateContractWithTxTracker, @@ -19,6 +18,7 @@ import { import { formatUnits } from 'viem'; import { GAS_BUFFER, MAX_PRICE } from '../constants'; +import { defaultRoute } from '../defaultRoute'; import type { Allowance, @@ -32,7 +32,7 @@ import type { } from '@origin/shared/providers'; const isRouteAvailable: IsRouteAvailable = async ( - config, + { config, queryClient }, { amountIn, tokenIn, tokenOut }, ) => { const curve = await queryClient.fetchQuery({ @@ -51,6 +51,7 @@ const isRouteAvailable: IsRouteAvailable = async ( tokenOut.address, amountIn, ], + chainId: curve.CurveRegistryExchange.chainId, }); return ( +formatUnits(amountIn, tokenIn.decimals) / @@ -63,7 +64,7 @@ const isRouteAvailable: IsRouteAvailable = async ( }; const estimateAmount: EstimateAmount = async ( - config, + { config, queryClient }, { amountIn, tokenIn, tokenOut }, ) => { if (amountIn === 0n) { @@ -85,17 +86,20 @@ const estimateAmount: EstimateAmount = async ( tokenOut.address, amountIn, ], + chainId: curve.CurveRegistryExchange.chainId, }); return estimate as unknown as bigint; }; const estimateGas: EstimateGas = async ( - config, + { config, queryClient }, { tokenIn, tokenOut, amountIn, amountOut, slippage }, ) => { let gasEstimate = 0n; - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { + chainId: contracts.mainnet.OUSDCurveMetaPool.chainId, + }); if ( amountIn === 0n || @@ -174,7 +178,7 @@ const estimateRoute: EstimateRoute = async ( }; }; -const allowance: Allowance = async (config, { tokenIn }) => { +const allowance: Allowance = async ({ config }, { tokenIn }) => { const { address } = getAccount(config); if (!address || !tokenIn?.address) { @@ -186,18 +190,19 @@ const allowance: Allowance = async (config, { tokenIn }) => { abi: tokenIn.abi, functionName: 'allowance', args: [address, contracts.mainnet.OUSDCurveMetaPool.address], + chainId: tokenIn.chainId, }); return allowance as unknown as bigint; }; const estimateApprovalGas: EstimateApprovalGas = async ( - config, + { config }, { tokenIn, amountIn }, ) => { let approvalEstimate = 0n; const { address } = getAccount(config); - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { chainId: tokenIn.chainId }); if (amountIn === 0n || !address || !publicClient || !tokenIn?.address) { return approvalEstimate; @@ -218,7 +223,7 @@ const estimateApprovalGas: EstimateApprovalGas = async ( return approvalEstimate; }; -const approve: Approve = async (config, { tokenIn, amountIn }) => { +const approve: Approve = async ({ config }, { tokenIn, amountIn }) => { if (amountIn === 0n || !tokenIn?.address) { return null; } @@ -228,6 +233,7 @@ const approve: Approve = async (config, { tokenIn, amountIn }) => { abi: tokenIn.abi, functionName: 'approve', args: [contracts.mainnet.OUSDCurveMetaPool.address, amountIn], + chainId: tokenIn.chainId, }); const hash = await writeContract(config, request); @@ -235,7 +241,7 @@ const approve: Approve = async (config, { tokenIn, amountIn }) => { }; const swap: Swap = async ( - config, + { config, queryClient }, { tokenIn, tokenOut, amountIn, slippage, amountOut }, ) => { const { address } = getAccount(config); @@ -244,7 +250,10 @@ const swap: Swap = async ( return null; } - const approved = await allowance(config, { tokenIn, tokenOut }); + const approved = await allowance( + { config, queryClient }, + { tokenIn, tokenOut }, + ); if (approved < amountIn) { throw new Error(`Curve swap is not approved`); @@ -255,13 +264,16 @@ const swap: Swap = async ( slippage, ); - const estimatedGas = await estimateGas(config, { - tokenIn, - tokenOut, - amountIn, - amountOut, - slippage, - }); + const estimatedGas = await estimateGas( + { config, queryClient }, + { + tokenIn, + tokenOut, + amountIn, + amountOut, + slippage, + }, + ); const gas = estimatedGas + (estimatedGas * GAS_BUFFER) / 100n; const curve = await queryClient.fetchQuery({ queryKey: useCurve.getKey(), @@ -289,13 +301,15 @@ const swap: Swap = async ( ], account: address, gas, + chainId: contracts.mainnet.OUSDCurveMetaPool.chainId, }); const hash = await writeContract(config, request); return hash; }; -export default { +export const swapCurveOusd = { + ...defaultRoute, isRouteAvailable, estimateAmount, estimateGas, diff --git a/libs/defi/ousd/src/swap/actions/flipper.ts b/libs/shared/routes/src/ousd/swapFlipperOusd.ts similarity index 86% rename from libs/defi/ousd/src/swap/actions/flipper.ts rename to libs/shared/routes/src/ousd/swapFlipperOusd.ts index c3c4704b7..1117706d0 100644 --- a/libs/defi/ousd/src/swap/actions/flipper.ts +++ b/libs/shared/routes/src/ousd/swapFlipperOusd.ts @@ -10,6 +10,8 @@ import { } from '@wagmi/core'; import { formatUnits } from 'viem'; +import { defaultRoute } from '../defaultRoute'; + import type { Token } from '@origin/shared/contracts'; import type { Allowance, @@ -38,7 +40,7 @@ const getFunctionName = (tokenIn: Token, tokenOut: Token) => { }; const isRouteAvailable: IsRouteAvailable = async ( - config, + { config }, { amountIn, tokenIn, tokenOut }, ) => { const amtIn = +formatUnits(amountIn, tokenIn.decimals); @@ -53,6 +55,7 @@ const isRouteAvailable: IsRouteAvailable = async ( abi: tokenOut.abi, functionName: 'balanceOf', args: [contracts.mainnet.OUSDFlipper.address], + chainId: tokenOut.chainId, }); const bal = +formatUnits(balance as unknown as bigint, tokenOut.decimals); @@ -64,10 +67,12 @@ const isRouteAvailable: IsRouteAvailable = async ( }; const estimateAmount: EstimateAmount = async ( - config, + { config }, { amountIn, tokenIn, tokenOut }, ) => { - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { + chainId: contracts.mainnet.OUSDFlipper.chainId, + }); const functionName = getFunctionName(tokenIn, tokenOut); try { @@ -117,7 +122,7 @@ const estimateRoute: EstimateRoute = async ( }; }; -const allowance: Allowance = async (config, { tokenIn }) => { +const allowance: Allowance = async ({ config }, { tokenIn }) => { const { address } = getAccount(config); if (!address || !tokenIn?.address) { @@ -129,18 +134,19 @@ const allowance: Allowance = async (config, { tokenIn }) => { abi: tokenIn.abi, functionName: 'allowance', args: [address, contracts.mainnet.OUSDFlipper.address], + chainId: tokenIn.chainId, }); return allowance as unknown as bigint; }; const estimateApprovalGas: EstimateApprovalGas = async ( - config, + { config }, { tokenIn, amountIn }, ) => { let approvalEstimate = 0n; const { address } = getAccount(config); - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { chainId: tokenIn.chainId }); if (amountIn === 0n || !publicClient || !tokenIn?.address) { return approvalEstimate; @@ -161,7 +167,7 @@ const estimateApprovalGas: EstimateApprovalGas = async ( return approvalEstimate; }; -const approve: Approve = async (config, { tokenIn, amountIn }) => { +const approve: Approve = async ({ config }, { tokenIn, amountIn }) => { if (!tokenIn?.address) { return null; } @@ -171,13 +177,17 @@ const approve: Approve = async (config, { tokenIn, amountIn }) => { abi: tokenIn.abi, functionName: 'approve', args: [contracts.mainnet.OUSDFlipper.address, amountIn], + chainId: tokenIn.chainId, }); const hash = await writeContract(config, request); return hash; }; -const swap: Swap = async (config, { tokenIn, tokenOut, amountIn }) => { +const swap: Swap = async ( + { config, queryClient }, + { tokenIn, tokenOut, amountIn }, +) => { const { address } = getAccount(config); const functionName = getFunctionName(tokenIn, tokenOut); @@ -185,7 +195,10 @@ const swap: Swap = async (config, { tokenIn, tokenOut, amountIn }) => { return null; } - const approved = await allowance(config, { tokenIn, tokenOut }); + const approved = await allowance( + { config, queryClient }, + { tokenIn, tokenOut }, + ); if (approved < amountIn) { throw new Error(`Flipper is not approved`); @@ -198,13 +211,15 @@ const swap: Swap = async (config, { tokenIn, tokenOut, amountIn }) => { abi: contracts.mainnet.OUSDFlipper.abi, functionName, args: [scaledAmount], + chainId: contracts.mainnet.OUSDFlipper.chainId, }); const hash = await writeContract(config, request); return hash; }; -export default { +export const swapFlipperOusd = { + ...defaultRoute, isRouteAvailable, estimateAmount, estimateGas, diff --git a/libs/defi/ousd/src/swap/actions/sushiswap.ts b/libs/shared/routes/src/ousd/swapSushiswapOusd.ts similarity index 87% rename from libs/defi/ousd/src/swap/actions/sushiswap.ts rename to libs/shared/routes/src/ousd/swapSushiswapOusd.ts index 1848c85b8..5c2ca151e 100644 --- a/libs/defi/ousd/src/swap/actions/sushiswap.ts +++ b/libs/shared/routes/src/ousd/swapSushiswapOusd.ts @@ -12,6 +12,7 @@ import { last } from 'ramda'; import { formatUnits } from 'viem'; import { GAS_BUFFER, MAX_PRICE } from '../constants'; +import { defaultRoute } from '../defaultRoute'; import type { Token } from '@origin/shared/contracts'; import type { @@ -64,7 +65,7 @@ const getPath = (tokenIn: Token, tokenOut: Token) => { }; const isRouteAvailable: IsRouteAvailable = async ( - config, + { config }, { amountIn, tokenIn, tokenOut }, ) => { const path = getPath(tokenIn, tokenOut); @@ -75,6 +76,7 @@ const isRouteAvailable: IsRouteAvailable = async ( abi: contracts.mainnet.sushiswapRouter.abi, functionName: 'getAmountsOut', args: [amountIn, path], + chainId: contracts.mainnet.sushiswapRouter.chainId, }); return ( @@ -89,7 +91,7 @@ const isRouteAvailable: IsRouteAvailable = async ( }; const estimateAmount: EstimateAmount = async ( - config, + { config }, { amountIn, tokenIn, tokenOut }, ) => { const path = getPath(tokenIn, tokenOut); @@ -103,17 +105,20 @@ const estimateAmount: EstimateAmount = async ( abi: contracts.mainnet.sushiswapRouter.abi, functionName: 'getAmountsOut', args: [amountIn, path], + chainId: contracts.mainnet.sushiswapRouter.chainId, }); return last(estimate) ?? 0n; }; const estimateGas: EstimateGas = async ( - config, + { config }, { tokenIn, tokenOut, amountIn, amountOut, slippage }, ) => { let gasEstimate = 0n; - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { + chainId: contracts.mainnet.sushiswapRouter.chainId, + }); if (amountIn === 0n || !publicClient) { return gasEstimate; @@ -182,7 +187,7 @@ const estimateRoute: EstimateRoute = async ( }; }; -const allowance: Allowance = async (config, { tokenIn }) => { +const allowance: Allowance = async ({ config }, { tokenIn }) => { const { address } = getAccount(config); if (!address || !tokenIn?.address) { @@ -194,18 +199,19 @@ const allowance: Allowance = async (config, { tokenIn }) => { abi: tokenIn.abi, functionName: 'allowance', args: [address, contracts.mainnet.sushiswapRouter.address], + chainId: tokenIn.chainId, }); return allowance as unknown as bigint; }; const estimateApprovalGas: EstimateApprovalGas = async ( - config, + { config }, { tokenIn, amountIn }, ) => { let approvalEstimate = 0n; const { address } = getAccount(config); - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { chainId: tokenIn.chainId }); if (amountIn === 0n || !publicClient || !tokenIn?.address) { return approvalEstimate; @@ -226,7 +232,10 @@ const estimateApprovalGas: EstimateApprovalGas = async ( return approvalEstimate; }; -const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { +const approve: Approve = async ( + { config }, + { tokenIn, tokenOut, amountIn }, +) => { if (amountIn === 0n || !tokenIn?.address) { return null; } @@ -236,6 +245,7 @@ const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { abi: tokenIn.abi, functionName: 'approve', args: [contracts.mainnet.sushiswapRouter.address, amountIn], + chainId: tokenIn.chainId, }); const hash = await writeContract(config, request); @@ -243,7 +253,7 @@ const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { }; const swap: Swap = async ( - config, + { config, queryClient }, { tokenIn, tokenOut, amountIn, slippage, amountOut }, ) => { const { address } = getAccount(config); @@ -252,7 +262,10 @@ const swap: Swap = async ( return null; } - const approved = await allowance(config, { tokenIn, tokenOut }); + const approved = await allowance( + { config, queryClient }, + { tokenIn, tokenOut }, + ); if (approved < amountIn) { throw new Error(`SushiSwap is not approved`); @@ -263,13 +276,16 @@ const swap: Swap = async ( slippage, ); - const estimatedGas = await estimateGas(config, { - tokenIn, - tokenOut, - amountIn, - amountOut, - slippage, - }); + const estimatedGas = await estimateGas( + { config, queryClient }, + { + tokenIn, + tokenOut, + amountIn, + amountOut, + slippage, + }, + ); const gas = estimatedGas + (estimatedGas * GAS_BUFFER) / 100n; const { request } = await simulateContractWithTxTracker(config, { @@ -285,13 +301,15 @@ const swap: Swap = async ( ], account: address, gas, + chainId: contracts.mainnet.sushiswapRouter.chainId, }); const hash = await writeContract(config, request); return hash; }; -export default { +export const swapSushiswapOusd = { + ...defaultRoute, isRouteAvailable, estimateAmount, estimateGas, diff --git a/libs/defi/ousd/src/swap/actions/uniswapV2.ts b/libs/shared/routes/src/ousd/swapUniswapV2Ousd.ts similarity index 87% rename from libs/defi/ousd/src/swap/actions/uniswapV2.ts rename to libs/shared/routes/src/ousd/swapUniswapV2Ousd.ts index 3da364c9b..000c73b82 100644 --- a/libs/defi/ousd/src/swap/actions/uniswapV2.ts +++ b/libs/shared/routes/src/ousd/swapUniswapV2Ousd.ts @@ -16,6 +16,7 @@ import { last } from 'ramda'; import { formatUnits } from 'viem'; import { GAS_BUFFER, MAX_PRICE } from '../constants'; +import { defaultRoute } from '../defaultRoute'; import type { Token } from '@origin/shared/contracts'; import type { @@ -68,7 +69,7 @@ const getPath = (tokenIn: Token, tokenOut: Token) => { }; const isRouteAvailable: IsRouteAvailable = async ( - config, + { config }, { amountIn, tokenIn, tokenOut }, ) => { const path = getPath(tokenIn, tokenOut); @@ -80,6 +81,7 @@ const isRouteAvailable: IsRouteAvailable = async ( abi: contracts.mainnet.uniswapV2Router.abi, functionName: 'getAmountsOut', args: [amountIn, path], + chainId: contracts.mainnet.uniswapV2Router.chainId, }); return ( @@ -94,7 +96,7 @@ const isRouteAvailable: IsRouteAvailable = async ( }; const estimateAmount: EstimateAmount = async ( - config, + { config }, { amountIn, tokenIn, tokenOut }, ) => { const path = getPath(tokenIn, tokenOut); @@ -107,17 +109,20 @@ const estimateAmount: EstimateAmount = async ( abi: contracts.mainnet.uniswapV2Router.abi, functionName: 'getAmountsOut', args: [amountIn, path], + chainId: contracts.mainnet.uniswapV2Router.chainId, }); return last(estimate) ?? 0n; }; const estimateGas: EstimateGas = async ( - config, + { config }, { tokenIn, tokenOut, amountIn, amountOut, slippage }, ) => { let gasEstimate = 0n; - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { + chainId: contracts.mainnet.uniswapV2Router.chainId, + }); const { address } = getAccount(config); const path = getPath(tokenIn, tokenOut); @@ -184,7 +189,7 @@ const estimateRoute: EstimateRoute = async ( }; }; -const allowance: Allowance = async (config, { tokenIn }) => { +const allowance: Allowance = async ({ config }, { tokenIn }) => { const { address } = getAccount(config); if (!address || !tokenIn?.address) { @@ -196,18 +201,19 @@ const allowance: Allowance = async (config, { tokenIn }) => { abi: tokenIn.abi, functionName: 'allowance', args: [address, contracts.mainnet.uniswapV2Router.address], + chainId: tokenIn.chainId, }); return allowance as unknown as bigint; }; const estimateApprovalGas: EstimateApprovalGas = async ( - config, + { config }, { tokenIn, amountIn }, ) => { let approvalEstimate = 0n; const { address } = getAccount(config); - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { chainId: tokenIn.chainId }); if (amountIn === 0n || !address || !publicClient || !tokenIn?.address) { return approvalEstimate; @@ -228,7 +234,10 @@ const estimateApprovalGas: EstimateApprovalGas = async ( return approvalEstimate; }; -const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { +const approve: Approve = async ( + { config }, + { tokenIn, tokenOut, amountIn }, +) => { if (!tokenIn?.address) { return null; } @@ -238,6 +247,7 @@ const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { abi: tokenIn.abi, functionName: 'approve', args: [contracts.mainnet.uniswapV2Router.address, amountIn], + chainId: tokenIn.chainId, }); const hash = await writeContract(config, request); @@ -245,7 +255,7 @@ const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { }; const swap: Swap = async ( - config, + { config, queryClient }, { tokenIn, tokenOut, amountIn, slippage, amountOut }, ) => { const { address } = getAccount(config); @@ -254,7 +264,10 @@ const swap: Swap = async ( return null; } - const approved = await allowance(config, { tokenIn, tokenOut }); + const approved = await allowance( + { config, queryClient }, + { tokenIn, tokenOut }, + ); if (approved < amountIn) { throw new Error(`Uniswap V2 is not approved`); @@ -265,13 +278,16 @@ const swap: Swap = async ( slippage, ); - const estimatedGas = await estimateGas(config, { - tokenIn, - tokenOut, - amountIn, - amountOut, - slippage, - }); + const estimatedGas = await estimateGas( + { config, queryClient }, + { + tokenIn, + tokenOut, + amountIn, + amountOut, + slippage, + }, + ); const gas = estimatedGas + (estimatedGas * GAS_BUFFER) / 100n; const { request } = await simulateContractWithTxTracker(config, { @@ -287,13 +303,15 @@ const swap: Swap = async ( ], account: address, gas, + chainId: contracts.mainnet.uniswapV2Router.chainId, }); const hash = await writeContract(config, request); return hash; }; -export default { +export const swapUniswapV2Ousd = { + ...defaultRoute, isRouteAvailable, estimateAmount, estimateGas, diff --git a/libs/defi/ousd/src/swap/actions/uniswapV3.ts b/libs/shared/routes/src/ousd/swapUniswapV3Ousd.ts similarity index 90% rename from libs/defi/ousd/src/swap/actions/uniswapV3.ts rename to libs/shared/routes/src/ousd/swapUniswapV3Ousd.ts index 36b044e50..8d5f273f2 100644 --- a/libs/defi/ousd/src/swap/actions/uniswapV3.ts +++ b/libs/shared/routes/src/ousd/swapUniswapV3Ousd.ts @@ -14,6 +14,8 @@ import { } from '@wagmi/core'; import { encodePacked, formatUnits } from 'viem'; +import { defaultRoute } from '../defaultRoute'; + import type { Token } from '@origin/shared/contracts'; import type { Allowance, @@ -77,18 +79,20 @@ const getPath = (tokenIn: Token, tokenOut: Token) => { }; const isRouteAvailable: IsRouteAvailable = async ( - config, + { config }, { amountIn, tokenIn }, ) => { return +formatUnits(amountIn, tokenIn.decimals) > 0.000003; }; const estimateAmount: EstimateAmount = async ( - config, + { config }, { amountIn, tokenIn, tokenOut }, ) => { let estimate = 0n; - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { + chainId: contracts.mainnet.uniswapV3Quoter.chainId, + }); if ( amountIn === 0n || !publicClient || @@ -124,11 +128,13 @@ const estimateAmount: EstimateAmount = async ( }; const estimateGas: EstimateGas = async ( - config, + { config }, { tokenIn, tokenOut, amountIn, amountOut, slippage }, ) => { let gasEstimate = 0n; - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { + chainId: contracts.mainnet.uniswapV3Router.chainId, + }); if ( amountIn === 0n || @@ -219,7 +225,7 @@ const estimateRoute: EstimateRoute = async ( }; }; -const allowance: Allowance = async (config, { tokenIn }) => { +const allowance: Allowance = async ({ config }, { tokenIn }) => { const { address } = getAccount(config); if (!address || !tokenIn?.address) { @@ -231,18 +237,19 @@ const allowance: Allowance = async (config, { tokenIn }) => { abi: tokenIn.abi, functionName: 'allowance', args: [address, contracts.mainnet.uniswapV3Router.address], + chainId: tokenIn.chainId, }); return allowance as unknown as bigint; }; const estimateApprovalGas: EstimateApprovalGas = async ( - config, + { config }, { tokenIn, amountIn }, ) => { let approvalEstimate = 0n; const { address } = getAccount(config); - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { chainId: tokenIn.chainId }); if (amountIn === 0n || !publicClient || !tokenIn?.address) { return approvalEstimate; @@ -263,7 +270,10 @@ const estimateApprovalGas: EstimateApprovalGas = async ( return approvalEstimate; }; -const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { +const approve: Approve = async ( + { config }, + { tokenIn, tokenOut, amountIn }, +) => { if (!tokenIn?.address) { return null; } @@ -273,6 +283,7 @@ const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { abi: tokenIn.abi, functionName: 'approve', args: [contracts.mainnet.uniswapV3Router.address, amountIn], + chainId: tokenIn.chainId, }); const hash = await writeContract(config, request); @@ -280,7 +291,7 @@ const approve: Approve = async (config, { tokenIn, tokenOut, amountIn }) => { }; const swap: Swap = async ( - config, + { config, queryClient }, { tokenIn, tokenOut, amountIn, slippage, amountOut }, ) => { const { address } = getAccount(config); @@ -289,7 +300,10 @@ const swap: Swap = async ( return null; } - const approved = await allowance(config, { tokenIn, tokenOut }); + const approved = await allowance( + { config, queryClient }, + { tokenIn, tokenOut }, + ); if (approved < amountIn) { throw new Error(`Uniswap V3 is not approved`); @@ -318,6 +332,7 @@ const swap: Swap = async ( sqrtPriceLimitX96: 0n, }, ], + chainId: contracts.mainnet.uniswapV3Router.chainId, }); const hash = await writeContract(config, request); txHash = hash; @@ -335,6 +350,7 @@ const swap: Swap = async ( recipient: address, }, ], + chainId: contracts.mainnet.uniswapV3Router.chainId, }); const hash = await writeContract(config, request); txHash = hash; @@ -343,7 +359,8 @@ const swap: Swap = async ( return txHash; }; -export default { +export const swapUniswapV3Ousd = { + ...defaultRoute, isRouteAvailable, estimateAmount, estimateGas, diff --git a/libs/ousd/swap/src/actions/unwrapWOUSD.ts b/libs/shared/routes/src/ousd/unwrapOusdWousd.ts similarity index 86% rename from libs/ousd/swap/src/actions/unwrapWOUSD.ts rename to libs/shared/routes/src/ousd/unwrapOusdWousd.ts index 4257e2243..aa9ddcaa4 100644 --- a/libs/ousd/swap/src/actions/unwrapWOUSD.ts +++ b/libs/shared/routes/src/ousd/unwrapOusdWousd.ts @@ -8,6 +8,8 @@ import { } from '@wagmi/core'; import { formatUnits, maxUint256 } from 'viem'; +import { defaultRoute } from '../defaultRoute'; + import type { Allowance, Approve, @@ -18,7 +20,7 @@ import type { Swap, } from '@origin/shared/providers'; -const estimateAmount: EstimateAmount = async (config, { amountIn }) => { +const estimateAmount: EstimateAmount = async ({ config }, { amountIn }) => { if (amountIn === 0n) { return 0n; } @@ -28,15 +30,18 @@ const estimateAmount: EstimateAmount = async (config, { amountIn }) => { abi: tokens.mainnet.wOUSD.abi, functionName: 'convertToAssets', args: [amountIn], + chainId: tokens.mainnet.wOUSD.chainId, }); return data; }; -const estimateGas: EstimateGas = async (config, { amountIn }) => { +const estimateGas: EstimateGas = async ({ config }, { amountIn }) => { let gasEstimate = 0n; - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { + chainId: tokens.mainnet.wOUSD.chainId, + }); if (amountIn === 0n || !publicClient) { return gasEstimate; @@ -123,7 +128,7 @@ const approve: Approve = async () => { return null; }; -const swap: Swap = async (config, { amountIn }) => { +const swap: Swap = async ({ config }, { amountIn }) => { const { address } = getAccount(config); if (amountIn === 0n || !address) { @@ -135,13 +140,15 @@ const swap: Swap = async (config, { amountIn }) => { abi: tokens.mainnet.wOUSD.abi, functionName: 'redeem', args: [amountIn, address, address], + chainId: tokens.mainnet.wOUSD.chainId, }); const hash = await writeContract(config, request); return hash; }; -export default { +export const unwrapOusdWousd = { + ...defaultRoute, estimateAmount, estimateGas, estimateRoute, diff --git a/libs/ousd/swap/src/actions/wrapOUSD.ts b/libs/shared/routes/src/ousd/wrapOusdWousd.ts similarity index 82% rename from libs/ousd/swap/src/actions/wrapOUSD.ts rename to libs/shared/routes/src/ousd/wrapOusdWousd.ts index e4709e20a..65a89bafc 100644 --- a/libs/ousd/swap/src/actions/wrapOUSD.ts +++ b/libs/shared/routes/src/ousd/wrapOusdWousd.ts @@ -10,6 +10,8 @@ import { } from '@wagmi/core'; import { erc20Abi, formatUnits } from 'viem'; +import { defaultRoute } from '../defaultRoute'; + import type { Allowance, Approve, @@ -20,7 +22,7 @@ import type { Swap, } from '@origin/shared/providers'; -const estimateAmount: EstimateAmount = async (config, { amountIn }) => { +const estimateAmount: EstimateAmount = async ({ config }, { amountIn }) => { if (amountIn === 0n) { return 0n; } @@ -30,15 +32,18 @@ const estimateAmount: EstimateAmount = async (config, { amountIn }) => { abi: tokens.mainnet.wOUSD.abi, functionName: 'convertToShares', args: [amountIn], + chainId: tokens.mainnet.wOUSD.chainId, }); return data; }; -const estimateGas: EstimateGas = async (config, { amountIn }) => { +const estimateGas: EstimateGas = async ({ config }, { amountIn }) => { let gasEstimate = 0n; - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { + chainId: tokens.mainnet.wOUSD.chainId, + }); if (amountIn === 0n || !publicClient) { return gasEstimate; @@ -75,7 +80,7 @@ const estimateGas: EstimateGas = async (config, { amountIn }) => { return gasEstimate; }; -const allowance: Allowance = async (config, { tokenIn }) => { +const allowance: Allowance = async ({ config }, { tokenIn }) => { const { address } = getAccount(config); if (!address || !tokenIn?.address) { @@ -87,18 +92,19 @@ const allowance: Allowance = async (config, { tokenIn }) => { abi: tokenIn.abi, functionName: 'allowance', args: [address, tokens.mainnet.wOUSD.address], + chainId: tokenIn.chainId, }); return allowance as unknown as bigint; }; const estimateApprovalGas: EstimateApprovalGas = async ( - config, + { config }, { tokenIn, amountIn }, ) => { let approvalEstimate = 0n; const { address } = getAccount(config); - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { chainId: tokenIn.chainId }); if (amountIn === 0n || !publicClient || !tokenIn?.address) { return approvalEstimate; @@ -154,7 +160,7 @@ const estimateRoute: EstimateRoute = async ( }; }; -const approve: Approve = async (config, { tokenIn, amountIn }) => { +const approve: Approve = async ({ config }, { tokenIn, amountIn }) => { if (!tokenIn?.address) { return null; } @@ -164,20 +170,27 @@ const approve: Approve = async (config, { tokenIn, amountIn }) => { abi: tokenIn.abi, functionName: 'approve', args: [tokens.mainnet.wOUSD.address, amountIn], + chainId: tokenIn.chainId, }); const hash = await writeContract(config, request); return hash; }; -const swap: Swap = async (config, { tokenIn, tokenOut, amountIn }) => { +const swap: Swap = async ( + { config, queryClient }, + { tokenIn, tokenOut, amountIn }, +) => { const { address } = getAccount(config); if (amountIn === 0n || isNilOrEmpty(address)) { return null; } - const approved = await allowance(config, { tokenIn, tokenOut }); + const approved = await allowance( + { config, queryClient }, + { tokenIn, tokenOut }, + ); if (approved < amountIn) { throw new Error(`wOUSD is not approved`); @@ -188,13 +201,15 @@ const swap: Swap = async (config, { tokenIn, tokenOut, amountIn }) => { abi: tokens.mainnet.wOUSD.abi, functionName: 'deposit', args: [amountIn, address], + chainId: tokens.mainnet.wOUSD.chainId, }); const hash = await writeContract(config, request); return hash; }; -export default { +export const wrapOusdWousd = { + ...defaultRoute, estimateAmount, estimateGas, estimateRoute, diff --git a/libs/shared/routes/src/prime/index.ts b/libs/shared/routes/src/prime/index.ts new file mode 100644 index 000000000..86c8aa872 --- /dev/null +++ b/libs/shared/routes/src/prime/index.ts @@ -0,0 +1,3 @@ +export * from './restakePrime'; +export * from './swapUniwsapPrime'; +export * from './swapZapperPrime'; diff --git a/libs/prime/restake/src/actions/restake.ts b/libs/shared/routes/src/prime/restakePrime.ts similarity index 84% rename from libs/prime/restake/src/actions/restake.ts rename to libs/shared/routes/src/prime/restakePrime.ts index 228937bd6..908090410 100644 --- a/libs/prime/restake/src/actions/restake.ts +++ b/libs/shared/routes/src/prime/restakePrime.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable unused-imports/no-unused-vars */ -import { queryClient } from '@origin/prime/shared'; import { contracts } from '@origin/shared/contracts'; import { getReferrerId } from '@origin/shared/providers'; import { @@ -18,6 +17,8 @@ import { } from '@wagmi/core'; import { formatUnits } from 'viem'; +import { defaultRoute } from '../defaultRoute'; + import type { Allowance, Approve, @@ -30,12 +31,13 @@ import type { } from '@origin/shared/providers'; import type { HexAddress } from '@origin/shared/utils'; -const isRouteAvailable: IsRouteAvailable = async (config) => { +const isRouteAvailable: IsRouteAvailable = async ({ config }) => { try { const paused = await readContract(config, { address: contracts.mainnet.lrtDepositPool.address, abi: contracts.mainnet.lrtDepositPool.abi, functionName: 'paused', + chainId: contracts.mainnet.lrtDepositPool.chainId, }); return !paused; @@ -45,7 +47,7 @@ const isRouteAvailable: IsRouteAvailable = async (config) => { }; const estimateAmount: EstimateAmount = async ( - config, + { config, queryClient }, { amountIn, tokenIn, tokenOut }, ) => { if (amountIn === 0n || !tokenIn?.address) { @@ -61,12 +63,14 @@ const estimateAmount: EstimateAmount = async ( address: contracts.mainnet.lrtOracle.address, abi: contracts.mainnet.lrtOracle.abi, functionName: 'primeETHPrice', + chainId: contracts.mainnet.lrtOracle.chainId, }, { address: contracts.mainnet.lrtOracle.address, abi: contracts.mainnet.lrtOracle.abi, functionName: 'getAssetPrice', args: [tokenIn.address ?? ZERO_ADDRESS], + chainId: contracts.mainnet.lrtOracle.chainId, }, ], }), @@ -80,11 +84,13 @@ const estimateAmount: EstimateAmount = async ( }; const estimateGas: EstimateGas = async ( - config, + { config }, { tokenIn, tokenOut, amountIn, amountOut, slippage }, ) => { let gasEstimate = 0n; - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { + chainId: contracts.mainnet.lrtDepositPool.chainId, + }); if (amountIn === 0n || !publicClient || !tokenIn?.address) { return gasEstimate; @@ -128,7 +134,7 @@ const estimateRoute: EstimateRoute = async ( }; }; -const allowance: Allowance = async (config, { tokenIn }) => { +const allowance: Allowance = async ({ config, queryClient }, { tokenIn }) => { const { address } = getAccount(config); if (!address || !tokenIn?.address) { @@ -143,6 +149,7 @@ const allowance: Allowance = async (config, { tokenIn }) => { abi: tokenIn.abi, functionName: 'allowance', args: [address, contracts.mainnet.lrtDepositPool.address], + chainId: tokenIn.chainId, }), staleTime: 15e3, }); @@ -151,12 +158,12 @@ const allowance: Allowance = async (config, { tokenIn }) => { }; const estimateApprovalGas: EstimateApprovalGas = async ( - config, + { config }, { tokenIn, amountIn }, ) => { let approvalEstimate = 0n; const { address } = getAccount(config); - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { chainId: tokenIn.chainId }); if (amountIn === 0n || !address || !publicClient || !tokenIn?.address) { return approvalEstimate; @@ -177,7 +184,7 @@ const estimateApprovalGas: EstimateApprovalGas = async ( return approvalEstimate; }; -const approve: Approve = async (config, { tokenIn, amountIn }) => { +const approve: Approve = async ({ config }, { tokenIn, amountIn }) => { if (!tokenIn?.address) { return null; } @@ -187,6 +194,7 @@ const approve: Approve = async (config, { tokenIn, amountIn }) => { abi: tokenIn.abi, functionName: 'approve', args: [contracts.mainnet.lrtDepositPool.address, amountIn], + chainId: tokenIn.chainId, }); const hash = await writeContract(config, request); @@ -194,7 +202,7 @@ const approve: Approve = async (config, { tokenIn, amountIn }) => { }; const swap: Swap = async ( - config, + { config, queryClient }, { tokenIn, tokenOut, amountIn, slippage, amountOut }, ) => { const { address } = getAccount(config); @@ -204,7 +212,10 @@ const swap: Swap = async ( return null; } - const approved = await allowance(config, { tokenIn, tokenOut }); + const approved = await allowance( + { config, queryClient }, + { tokenIn, tokenOut }, + ); if (approved < amountIn) { throw new Error(`Flipper is not approved`); @@ -225,13 +236,15 @@ const swap: Swap = async ( minAmountOut[0], referrerId ?? '', ], + chainId: contracts.mainnet.lrtDepositPool.chainId, }); const hash = await writeContract(config, request); return hash; }; -export default { +export const restakePrime = { + ...defaultRoute, isRouteAvailable, estimateAmount, estimateRoute, diff --git a/libs/prime/restake/src/actions/uniwsap.ts b/libs/shared/routes/src/prime/swapUniwsapPrime.ts similarity index 87% rename from libs/prime/restake/src/actions/uniwsap.ts rename to libs/shared/routes/src/prime/swapUniwsapPrime.ts index 779049374..9d68d6bf6 100644 --- a/libs/prime/restake/src/actions/uniwsap.ts +++ b/libs/shared/routes/src/prime/swapUniwsapPrime.ts @@ -9,6 +9,8 @@ import { } from '@wagmi/core'; import { formatUnits, maxUint256 } from 'viem'; +import { defaultRoute } from '../defaultRoute'; + import type { Allowance, EstimateAmount, @@ -18,13 +20,14 @@ import type { Swap, } from '@origin/shared/providers'; -const isRouteAvailable: IsRouteAvailable = async (config, { amountIn }) => { +const isRouteAvailable: IsRouteAvailable = async ({ config }, { amountIn }) => { try { const poolBalance = await readContract(config, { address: tokens.mainnet.primeETH.address, abi: tokens.mainnet.primeETH.abi, functionName: 'balanceOf', args: [contracts.mainnet.uniswapV3WETHPrimeETHPool.address], + chainId: tokens.mainnet.primeETH.chainId, }); return poolBalance > amountIn; @@ -34,11 +37,13 @@ const isRouteAvailable: IsRouteAvailable = async (config, { amountIn }) => { }; const estimateAmount: EstimateAmount = async ( - config, + { config }, { amountIn, tokenOut }, ) => { let estimate = 0n; - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { + chainId: contracts.mainnet.uniswapV3Quoter.chainId, + }); if (amountIn === 0n || !tokenOut?.address || !publicClient) { return estimate; } @@ -56,11 +61,13 @@ const estimateAmount: EstimateAmount = async ( }; const estimateGas: EstimateGas = async ( - config, + { config }, { tokenOut, amountIn, amountOut, slippage }, ) => { let gasEstimate = 0n; - const publicClient = getPublicClient(config); + const publicClient = getPublicClient(config, { + chainId: contracts.mainnet.uniswapV3Quoter.chainId, + }); if (amountIn === 0n || !publicClient || !tokenOut?.address) { return gasEstimate; @@ -124,7 +131,7 @@ const allowance: Allowance = async () => { }; const swap: Swap = async ( - config, + { config }, { tokenOut, amountIn, slippage, amountOut }, ) => { const { address } = getAccount(config); @@ -156,13 +163,15 @@ const swap: Swap = async ( sqrtPriceLimitX96: 0n, }, ], + chainId: contracts.mainnet.uniswapV3Quoter.chainId, }); const hash = await writeContract(config, request); return hash; }; -export default { +export const swapUniswapPrime = { + ...defaultRoute, isRouteAvailable, estimateAmount, estimateGas, diff --git a/libs/prime/restake/src/actions/zapper.ts b/libs/shared/routes/src/prime/swapZapperPrime.ts similarity index 92% rename from libs/prime/restake/src/actions/zapper.ts rename to libs/shared/routes/src/prime/swapZapperPrime.ts index 3bb84251a..045925a24 100644 --- a/libs/prime/restake/src/actions/zapper.ts +++ b/libs/shared/routes/src/prime/swapZapperPrime.ts @@ -1,4 +1,3 @@ -import { queryClient } from '@origin/prime/shared'; import { contracts } from '@origin/shared/contracts'; import { getReferrerId, useTokenPrices } from '@origin/shared/providers'; import { isNilOrEmpty, subPercentage } from '@origin/shared/utils'; @@ -6,6 +5,8 @@ import { getAccount, simulateContract, writeContract } from '@wagmi/core'; import { div, eq, setDecimals } from 'dnum'; import { formatUnits, maxUint256 } from 'viem'; +import { defaultRoute } from '../defaultRoute'; + import type { Allowance, Approve, @@ -15,7 +16,7 @@ import type { } from '@origin/shared/providers'; const estimateAmount: EstimateAmount = async ( - config, + { config, queryClient }, { amountIn, tokenIn, tokenOut }, ) => { if (amountIn === 0n) { @@ -68,7 +69,7 @@ const approve: Approve = async () => { }; const swap: Swap = async ( - config, + { config }, { tokenOut, amountIn, slippage, amountOut }, ) => { const { address } = getAccount(config); @@ -90,13 +91,15 @@ const swap: Swap = async ( args: [minAmountOut[0], referrerId ?? ''], value: amountIn, account: address, + chainId: contracts.mainnet.PrimeETHZapper.chainId, }); const hash = await writeContract(config, request); return hash; }; -export default { +export const swapZapperPrime = { + ...defaultRoute, estimateAmount, estimateRoute, allowance, diff --git a/libs/shared/routes/src/types.ts b/libs/shared/routes/src/types.ts new file mode 100644 index 000000000..b63edf9d9 --- /dev/null +++ b/libs/shared/routes/src/types.ts @@ -0,0 +1,25 @@ +export type OethRoute = + | 'mint-vault-oeth' + | 'redeem-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'; + +export type OusdRoute = + | 'mint-vault-ousd' + | 'swap-flipper-ousd' + | 'swap-curve-ousd' + | 'swap-sushiswap-ousd' + | 'swap-uniswap-v2-ousd' + | 'swap-uniswap-v3-ousd' + | 'unwrap-ousd-wousd' + | 'wrap-ousd-wousd'; + +export type PrimeRoute = + | 'restake-prime' + | 'swap-uniswap-prime' + | 'swap-zapper-prime'; diff --git a/libs/shared/routes/tsconfig.json b/libs/shared/routes/tsconfig.json new file mode 100644 index 000000000..89f8ac085 --- /dev/null +++ b/libs/shared/routes/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": false, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ], + "extends": "../../../tsconfig.base.json" +} diff --git a/libs/shared/routes/tsconfig.lib.json b/libs/shared/routes/tsconfig.lib.json new file mode 100644 index 000000000..21799b3e6 --- /dev/null +++ b/libs/shared/routes/tsconfig.lib.json @@ -0,0 +1,24 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [ + "node", + + "@nx/react/typings/cssmodule.d.ts", + "@nx/react/typings/image.d.ts" + ] + }, + "exclude": [ + "jest.config.ts", + "src/**/*.spec.ts", + "src/**/*.test.ts", + "src/**/*.spec.tsx", + "src/**/*.test.tsx", + "src/**/*.spec.js", + "src/**/*.test.js", + "src/**/*.spec.jsx", + "src/**/*.test.jsx" + ], + "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"] +} diff --git a/tsconfig.base.json b/tsconfig.base.json index bbcd20ac8..8a88203a1 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -50,6 +50,7 @@ "@origin/shared/contracts": ["libs/shared/contracts/src/index.ts"], "@origin/shared/icons": ["libs/shared/icons/src/index.ts"], "@origin/shared/providers": ["libs/shared/providers/src/index.ts"], + "@origin/shared/routes": ["libs/shared/routes/src/index.ts"], "@origin/shared/utils": ["libs/shared/utils/src/index.ts"] } },