diff --git a/src/components/BridgeModal/BridgeOutModal.tsx b/src/components/BridgeModal/BridgeOutModal.tsx index 29208e212b..ec3a13d67c 100644 --- a/src/components/BridgeModal/BridgeOutModal.tsx +++ b/src/components/BridgeModal/BridgeOutModal.tsx @@ -1,7 +1,7 @@ import { t, Trans } from "@lingui/macro"; import { ReactNode, useEffect, useMemo, useState } from "react"; import Skeleton from "react-loading-skeleton"; -import { Address, encodeAbiParameters } from "viem"; +import { Address, encodeAbiParameters, zeroAddress } from "viem"; import { useAccount } from "wagmi"; import { @@ -15,10 +15,15 @@ import { getChainIcon } from "config/icons"; import { getLayerZeroEndpointId, getStargatePoolAddress } from "config/multichain"; import { useGmxAccountSettlementChainId } from "context/GmxAccountContext/hooks"; import { selectMultichainMarketTokenBalances } from "context/PoolsDetailsContext/selectors/selectMultichainMarketTokenBalances"; -import { selectDepositMarketTokensData } from "context/SyntheticsStateContext/selectors/globalSelectors"; +import { + selectDepositMarketTokensData, + selectTokensData, +} from "context/SyntheticsStateContext/selectors/globalSelectors"; import { useSelector } from "context/SyntheticsStateContext/utils"; import { useArbitraryError, useArbitraryRelayParamsAndPayload } from "domain/multichain/arbitraryRelayParams"; +import { getMultichainTransferSendParams } from "domain/multichain/getSendParams"; import type { BridgeOutParams } from "domain/multichain/types"; +import { useQuoteSendNativeFee } from "domain/multichain/useQuoteSend"; import { buildAndSignBridgeOutTxn } from "domain/synthetics/express/expressOrderUtils"; import { ExpressTransactionBuilder } from "domain/synthetics/express/types"; import { getGlvOrMarketAddress, GlvOrMarketInfo } from "domain/synthetics/markets"; @@ -61,6 +66,7 @@ export function BridgeOutModal({ const [bridgeOutInputValue, setBridgeOutInputValue] = useState(""); const [isCreatingTxn, setIsCreatingTxn] = useState(false); + const tokensData = useSelector(selectTokensData); const depositMarketTokensData = useSelector(selectDepositMarketTokensData); const glvOrMarketAddress = glvOrMarketInfo ? getGlvOrMarketAddress(glvOrMarketInfo) : undefined; const marketToken = getTokenData(depositMarketTokensData, glvOrMarketAddress); @@ -150,6 +156,53 @@ export function BridgeOutModal({ enabled: isVisible, }); + const sendParams = useMemo(() => { + if (!bridgeOutChain || !account || bridgeOutAmount === undefined || bridgeOutAmount <= 0n) { + return; + } + + return getMultichainTransferSendParams({ + dstChainId: bridgeOutChain, + account, + amountLD: bridgeOutAmount, + isToGmx: false, + isManualGas: true, + srcChainId: chainId, + }); + }, [bridgeOutChain, account, bridgeOutAmount, chainId]); + + const transferNativeFee = useQuoteSendNativeFee({ + fromChainId: chainId, + toChainId: bridgeOutChain, + sendParams, + fromStargateAddress: bridgeOutParams?.provider, + }); + + const networkFeeUsd = useMemo(() => { + if ( + transferNativeFee === undefined || + tokensData === undefined || + tokensData[zeroAddress] === undefined || + expressTxnParamsAsyncResult.data === undefined + ) { + return; + } + + const relayFeeUsd = convertToUsd( + expressTxnParamsAsyncResult.data.gasPaymentParams.gasPaymentTokenAmount, + expressTxnParamsAsyncResult.data.gasPaymentParams.gasPaymentToken.decimals, + getMidPrice(expressTxnParamsAsyncResult.data.gasPaymentParams.gasPaymentToken.prices) + )!; + + const transferNativeFeeUsd = convertToUsd( + transferNativeFee, + tokensData[zeroAddress].decimals, + tokensData[zeroAddress].prices.minPrice + )!; + + return relayFeeUsd + transferNativeFeeUsd; + }, [transferNativeFee, tokensData, expressTxnParamsAsyncResult.data]); + const errors = useArbitraryError(expressTxnParamsAsyncResult.error); const hasOutdatedUi = useHasOutdatedUi(); @@ -354,6 +407,9 @@ export function BridgeOutModal({ + + + { const chainId = useSelector(selectChainId); const srcChainId = useSelector(selectSrcChainId); - const { signer, account } = useWallet(); + const { signer } = useWallet(); const { setPendingDeposit } = useSyntheticsEvents(); const { addOptimisticTokensBalancesUpdates } = useTokensBalancesUpdates(); const { setPendingTxns } = usePendingTxns(); @@ -103,7 +97,7 @@ export const useDepositTransactions = ({ const paySource = useSelector(selectPoolsDetailsPaySource); const selectedMarketForGlv = useSelector(selectPoolsDetailsSelectedMarketAddressForGlv); - const tokensData = useSelector(selectPoolsDetailsTradeTokensDataWithSourceChainBalances); + const tokensData = useSelector(selectTokensData); const amounts = useSelector(selectDepositWithdrawalAmounts); const { @@ -126,20 +120,28 @@ export const useDepositTransactions = ({ const rawParams = useSelector(selectPoolsDetailsParams); const params = useMemo((): CreateDepositParams | CreateGlvDepositParams | undefined => { - if (!rawParams || !technicalFees || !isDeposit) { + if (!rawParams || !technicalFees || !isDeposit || !technicalFees.isDeposit) { return undefined; } - const executionFee = - paySource === "sourceChain" - ? (technicalFees as SourceChainDepositFees | SourceChainGlvDepositFees).executionFee - : (technicalFees as ExecutionFee).feeTokenAmount; + let executionFee: bigint | undefined; + if (technicalFees.kind === "sourceChain") { + executionFee = technicalFees.fees.executionFee; + } else if (technicalFees.kind === "gmxAccount") { + executionFee = technicalFees.fees.executionFee.feeTokenAmount; + } else if (technicalFees.kind === "settlementChain") { + executionFee = technicalFees.fees.feeTokenAmount; + } + + if (executionFee === undefined) { + return undefined; + } return { ...(rawParams as RawCreateDepositParams), executionFee, }; - }, [rawParams, technicalFees, isDeposit, paySource]); + }, [rawParams, technicalFees, isDeposit]); const gasPaymentTokenAddress = useSelector(selectGasPaymentTokenAddress); const gasPaymentTokenAsCollateralAmount = useMemo((): bigint | undefined => { @@ -230,7 +232,7 @@ export const useDepositTransactions = ({ sendOrderSubmittedMetric(metricData.metricId); - if (!tokensData || !account || !signer || !rawParams || !transferRequests) { + if (!signer || !rawParams) { helperToast.error(t`Error submitting order`); sendTxnValidationErrorMetric(metricData.metricId); return Promise.resolve(); @@ -241,12 +243,14 @@ export const useDepositTransactions = ({ let promise: Promise; if (paySource === "sourceChain") { - if (longTokenAmount! > 0n && shortTokenAmount! > 0n) { - throw new Error("Pay source sourceChain does not support both long and short token deposits"); + if (!transferRequests) { + helperToast.error(t`Error submitting order`); + sendTxnValidationErrorMetric(metricData.metricId); + return; } - if (!technicalFees) { - throw new Error("Technical fees are not set"); + if (longTokenAmount! > 0n && shortTokenAmount! > 0n) { + throw new Error("Pay source sourceChain does not support both long and short token deposits"); } let tokenAddress: ERC20Address | NativeTokenSupportedAddress = @@ -254,6 +258,11 @@ export const useDepositTransactions = ({ tokenAddress = convertTokenAddress(chainId, tokenAddress, "native"); const tokenAmount = longTokenAmount! > 0n ? longTokenAmount! : shortTokenAmount!; + const fees = technicalFees?.kind === "sourceChain" && technicalFees.isDeposit ? technicalFees.fees : undefined; + if (!fees) { + throw new Error("Technical fees are not set"); + } + promise = createSourceChainDepositTxn({ chainId: chainId as SettlementChainId, srcChainId: srcChainId!, @@ -262,7 +271,7 @@ export const useDepositTransactions = ({ params: rawParams as RawCreateDepositParams, tokenAddress, tokenAmount, - fees: technicalFees as SourceChainDepositFees, + fees, }) .then((res) => { if (res.transactionHash) { @@ -273,7 +282,7 @@ export const useDepositTransactions = ({ token: getGmToken(chainId, (rawParams as RawCreateDepositParams).addresses.market), amount: marketTokenAmount!, settlementChainId: chainId, - estimatedFeeUsd: (technicalFees as SourceChainDepositFees).relayFeeUsd, + estimatedFeeUsd: fees.relayFeeUsd, }) ); } @@ -283,8 +292,10 @@ export const useDepositTransactions = ({ }); } else if (paySource === "gmxAccount") { const expressTxnParams = multichainDepositExpressTxnParams.data; - if (!expressTxnParams) { - throw new Error("Express txn params are not set"); + if (!transferRequests || !expressTxnParams) { + helperToast.error(t`Error submitting order`); + sendTxnValidationErrorMetric(metricData.metricId); + return; } promise = createMultichainDepositTxn({ @@ -325,14 +336,21 @@ export const useDepositTransactions = ({ } }); } else if (paySource === "settlementChain") { + const fees = technicalFees?.kind === "settlementChain" ? technicalFees.fees : undefined; + if (!fees || !tokensData) { + helperToast.error(t`Error submitting order`); + sendTxnValidationErrorMetric(metricData.metricId); + return; + } + promise = createDepositTxn({ chainId, signer, blockTimestampData, longTokenAmount: longTokenAmount ?? 0n, shortTokenAmount: shortTokenAmount ?? 0n, - executionFee: (technicalFees as ExecutionFee).feeTokenAmount, - executionGasLimit: (technicalFees as ExecutionFee).gasLimit, + executionFee: fees.feeTokenAmount, + executionGasLimit: fees.gasLimit, skipSimulation: shouldDisableValidation, tokensData, metricId: metricData.metricId, @@ -353,7 +371,6 @@ export const useDepositTransactions = ({ isDeposit, getDepositMetricData, tokensData, - account, signer, rawParams, transferRequests, @@ -367,13 +384,13 @@ export const useDepositTransactions = ({ srcChainId, setMultichainTransferProgress, marketTokenAmount, - multichainDepositExpressTxnParams, + multichainDepositExpressTxnParams.data, params, + addOptimisticTokensBalancesUpdates, + setPendingDeposit, blockTimestampData, shouldDisableValidation, setPendingTxns, - setPendingDeposit, - addOptimisticTokensBalancesUpdates, ] ); @@ -387,7 +404,7 @@ export const useDepositTransactions = ({ sendOrderSubmittedMetric(metricData.metricId); - if (!account || !marketInfo || !amounts || !tokensData || !signer || (isGlv && !rawParams) || !transferRequests) { + if (!signer || !rawParams) { helperToast.error(t`Error submitting order`); sendTxnValidationErrorMetric(metricData.metricId); return Promise.resolve(); @@ -397,11 +414,18 @@ export const useDepositTransactions = ({ let promise: Promise; if (paySource === "sourceChain") { + if (!transferRequests || !technicalFees) { + helperToast.error(t`Error submitting order`); + sendTxnValidationErrorMetric(metricData.metricId); + return; + } + if (longTokenAmount! > 0n && shortTokenAmount! > 0n) { throw new Error("Pay source sourceChain does not support both long and short token deposits"); } - if (!technicalFees) { + const fees = technicalFees.kind === "sourceChain" && technicalFees.isDeposit ? technicalFees.fees : undefined; + if (!fees) { throw new Error("Technical fees are not set"); } @@ -432,7 +456,7 @@ export const useDepositTransactions = ({ params: rawParams as RawCreateGlvDepositParams, tokenAddress, tokenAmount, - fees: technicalFees as SourceChainGlvDepositFees, + fees, }) .then((res) => { if (res.transactionHash) { @@ -443,7 +467,7 @@ export const useDepositTransactions = ({ token: getGlvToken(chainId, (rawParams as RawCreateGlvDepositParams).addresses.glv), amount: glvTokenAmount!, settlementChainId: chainId, - estimatedFeeUsd: (technicalFees as SourceChainGlvDepositFees).relayFeeUsd, + estimatedFeeUsd: fees.relayFeeUsd, }) ); } @@ -453,8 +477,11 @@ export const useDepositTransactions = ({ }); } else if (paySource === "gmxAccount") { const expressTxnParams = multichainDepositExpressTxnParams.data; - if (!expressTxnParams) { - throw new Error("Express txn params are not set"); + + if (!transferRequests || !expressTxnParams) { + helperToast.error(t`Error submitting order`); + sendTxnValidationErrorMetric(metricData.metricId); + return; } promise = createMultichainGlvDepositTxn({ @@ -499,6 +526,12 @@ export const useDepositTransactions = ({ } }); } else if (paySource === "settlementChain") { + if (!tokensData) { + helperToast.error(t`Error submitting order`); + sendTxnValidationErrorMetric(metricData.metricId); + return; + } + const someInputTokenIsNative = firstTokenAddress === zeroAddress || secondTokenAddress === zeroAddress; const maybeNativeLongTokenAddress = @@ -511,6 +544,11 @@ export const useDepositTransactions = ({ ? zeroAddress : shortTokenAddress; + const fees = technicalFees?.kind === "settlementChain" ? technicalFees.fees : undefined; + if (!fees) { + throw new Error("Technical fees are not set"); + } + promise = createGlvDepositTxn({ chainId, signer, @@ -520,8 +558,8 @@ export const useDepositTransactions = ({ longTokenAmount: longTokenAmount ?? 0n, shortTokenAmount: shortTokenAmount ?? 0n, marketTokenAmount: marketTokenAmount ?? 0n, - executionFee: (technicalFees as ExecutionFee).feeTokenAmount, - executionGasLimit: (technicalFees as ExecutionFee).gasLimit, + executionFee: fees.feeTokenAmount, + executionGasLimit: fees.gasLimit, skipSimulation: shouldDisableValidation, tokensData, blockTimestampData, @@ -540,12 +578,8 @@ export const useDepositTransactions = ({ [ isDeposit, getDepositMetricData, - account, - marketInfo, - amounts, tokensData, signer, - isGlv, rawParams, transferRequests, chainId, @@ -584,7 +618,7 @@ export const useDepositTransactions = ({ function useDepositTransferRequests({ technicalFees, }: { - technicalFees: TechnicalFees | undefined; + technicalFees: TechnicalGmFees | undefined; }): TransferRequests | undefined { const chainId = useSelector(selectChainId); const { isDeposit } = useSelector(selectPoolsDetailsFlags); @@ -615,52 +649,20 @@ function useDepositTransferRequests({ : undefined; return useMemo((): TransferRequests | undefined => { - if (!isDeposit) { - return undefined; - } - - const vaultAddress = isGlv ? getContract(chainId, "GlvVault") : getContract(chainId, "DepositVault"); - - if (isMarketTokenDeposit) { - return getTransferRequests([ - { - to: vaultAddress, - token: marketTokenAddress, - amount: marketTokenAmount, - }, - ]); - } - - if (paySource === "sourceChain") { - let tokenAddress = - longTokenAmount !== undefined && longTokenAmount > 0n ? initialLongTokenAddress : initialShortTokenAddress; - - let amount = longTokenAmount !== undefined && longTokenAmount > 0n ? longTokenAmount : shortTokenAmount!; - - const estimatedReceivedAmount = (technicalFees as SourceChainDepositFees | SourceChainGlvDepositFees | undefined) - ?.txnEstimatedReceivedAmount; - - if (estimatedReceivedAmount !== undefined && estimatedReceivedAmount > amount) { - return undefined; - } - - amount = applySlippageToMinOut(DEFAULT_SLIPPAGE_AMOUNT, estimatedReceivedAmount ?? amount); - - return getTransferRequests([{ to: vaultAddress, token: tokenAddress, amount }]); - } - - return getTransferRequests([ - { - to: vaultAddress, - token: initialLongTokenAddress, - amount: longTokenAmount, - }, - { - to: vaultAddress, - token: initialShortTokenAddress, - amount: shortTokenAmount, - }, - ]); + return buildDepositTransferRequests({ + isDeposit, + isGlv, + chainId, + paySource, + isMarketTokenDeposit, + marketTokenAddress, + marketTokenAmount, + longTokenAmount, + shortTokenAmount, + initialLongTokenAddress, + initialShortTokenAddress, + technicalFees, + }); }, [ isDeposit, isGlv, diff --git a/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/lpTxn/useLpTransactions.tsx b/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/lpTxn/useLpTransactions.tsx index 229eab2533..adc4aaa8d0 100644 --- a/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/lpTxn/useLpTransactions.tsx +++ b/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/lpTxn/useLpTransactions.tsx @@ -2,25 +2,15 @@ import { useCallback, useState } from "react"; import { selectPoolsDetailsOperation } from "context/PoolsDetailsContext/selectors"; import { useSelector } from "context/SyntheticsStateContext/utils"; -import type { ExecutionFee } from "domain/synthetics/fees"; -import type { SourceChainDepositFees } from "domain/synthetics/markets/feeEstimation/estimateSourceChainDepositFees"; -import type { SourceChainGlvDepositFees } from "domain/synthetics/markets/feeEstimation/estimateSourceChainGlvDepositFees"; -import { SourceChainGlvWithdrawalFees } from "domain/synthetics/markets/feeEstimation/estimateSourceChainGlvWithdrawalFees"; -import { SourceChainWithdrawalFees } from "domain/synthetics/markets/feeEstimation/estimateSourceChainWithdrawalFees"; +import { TechnicalGmFees } from "domain/synthetics/markets/technicalFees/technical-fees-types"; +import { Operation } from "domain/synthetics/markets/types"; import { useDepositTransactions } from "./useDepositTransactions"; import { useWithdrawalTransactions } from "./useWithdrawalTransactions"; -import { Operation } from "../../types"; export interface UseLpTransactionProps { shouldDisableValidation?: boolean; - technicalFees: - | ExecutionFee - | SourceChainGlvDepositFees - | SourceChainDepositFees - | SourceChainWithdrawalFees - | SourceChainGlvWithdrawalFees - | undefined; + technicalFees: TechnicalGmFees | undefined; } export const useLpTransactions = ( diff --git a/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/lpTxn/useMultichainDepositExpressTxnParams.tsx b/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/lpTxn/useMultichainDepositExpressTxnParams.tsx index 2f67b80fae..d0a2c30635 100644 --- a/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/lpTxn/useMultichainDepositExpressTxnParams.tsx +++ b/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/lpTxn/useMultichainDepositExpressTxnParams.tsx @@ -7,7 +7,6 @@ import { buildAndSignMultichainGlvDepositTxn } from "domain/synthetics/markets/c import type { GmPaySource } from "domain/synthetics/markets/types"; import { useChainId } from "lib/chains"; import { AsyncResult } from "lib/useThrottledAsync"; -import useWallet from "lib/wallets/useWallet"; import { DEFAULT_EXPRESS_ORDER_DEADLINE_DURATION } from "sdk/configs/express"; import { nowInSeconds } from "sdk/utils/time"; @@ -27,14 +26,8 @@ export function useMultichainDepositExpressTxnParams({ gasPaymentTokenAsCollateralAmount: bigint | undefined; }): AsyncResult { const { chainId, srcChainId } = useChainId(); - const { signer } = useWallet(); - const enabled = - paySource === "gmxAccount" && - Boolean(params) && - isDeposit && - transferRequests !== undefined && - signer !== undefined; + const enabled = paySource === "gmxAccount" && Boolean(params) && isDeposit && transferRequests !== undefined; const multichainDepositExpressTxnParams = useArbitraryRelayParamsAndPayload({ isGmxAccount: paySource === "gmxAccount", @@ -61,7 +54,6 @@ export function useMultichainDepositExpressTxnParams({ ...relayParams, deadline: BigInt(nowInSeconds() + DEFAULT_EXPRESS_ORDER_DEADLINE_DURATION), }, - signer, transferRequests, }); @@ -84,7 +76,6 @@ export function useMultichainDepositExpressTxnParams({ ...relayParams, deadline: BigInt(nowInSeconds() + DEFAULT_EXPRESS_ORDER_DEADLINE_DURATION), }, - signer, transferRequests, }); diff --git a/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/lpTxn/useMultichainWithdrawalExpressTxnParams.tsx b/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/lpTxn/useMultichainWithdrawalExpressTxnParams.tsx index a47583ab1c..7da8681c16 100644 --- a/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/lpTxn/useMultichainWithdrawalExpressTxnParams.tsx +++ b/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/lpTxn/useMultichainWithdrawalExpressTxnParams.tsx @@ -5,7 +5,6 @@ import { buildAndSignMultichainGlvWithdrawalTxn } from "domain/synthetics/market import { buildAndSignMultichainWithdrawalTxn } from "domain/synthetics/markets/createMultichainWithdrawalTxn"; import type { GmPaySource } from "domain/synthetics/markets/types"; import { useChainId } from "lib/chains"; -import useWallet from "lib/wallets/useWallet"; import { DEFAULT_EXPRESS_ORDER_DEADLINE_DURATION } from "sdk/configs/express"; import { nowInSeconds } from "sdk/utils/time"; @@ -23,14 +22,8 @@ export function useMultichainWithdrawalExpressTxnParams({ isWithdrawal: boolean; }) { const { chainId, srcChainId } = useChainId(); - const { signer } = useWallet(); - const enabled = - paySource === "gmxAccount" && - isWithdrawal && - Boolean(params) && - transferRequests !== undefined && - signer !== undefined; + const enabled = paySource === "gmxAccount" && isWithdrawal && Boolean(params) && transferRequests !== undefined; const multichainWithdrawalExpressTxnParams = useArbitraryRelayParamsAndPayload({ isGmxAccount: paySource === "gmxAccount", @@ -56,7 +49,6 @@ export function useMultichainWithdrawalExpressTxnParams({ ...relayParams, deadline: BigInt(nowInSeconds() + DEFAULT_EXPRESS_ORDER_DEADLINE_DURATION), }, - signer, transferRequests, }); @@ -78,7 +70,6 @@ export function useMultichainWithdrawalExpressTxnParams({ ...relayParams, deadline: BigInt(nowInSeconds() + DEFAULT_EXPRESS_ORDER_DEADLINE_DURATION), }, - signer, transferRequests, }); diff --git a/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/lpTxn/useWithdrawalTransactions.tsx b/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/lpTxn/useWithdrawalTransactions.tsx index ad916ec9a7..19311eadea 100644 --- a/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/lpTxn/useWithdrawalTransactions.tsx +++ b/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/lpTxn/useWithdrawalTransactions.tsx @@ -14,19 +14,21 @@ import { selectPoolsDetailsSelectedMarketAddressForGlv, selectPoolsDetailsSelectedMarketInfoForGlv, selectPoolsDetailsShortTokenAddress, - selectPoolsDetailsTradeTokensDataWithSourceChainBalances, } from "context/PoolsDetailsContext/selectors"; import { selectDepositWithdrawalAmounts } from "context/PoolsDetailsContext/selectors/selectDepositWithdrawalAmounts"; import { selectPoolsDetailsParams } from "context/PoolsDetailsContext/selectors/selectPoolsDetailsParams"; import { useSyntheticsEvents } from "context/SyntheticsEvents"; import { selectExpressGlobalParams } from "context/SyntheticsStateContext/selectors/expressSelectors"; -import { selectBlockTimestampData, selectChainId } from "context/SyntheticsStateContext/selectors/globalSelectors"; +import { + selectBlockTimestampData, + selectChainId, + selectTokensData, +} from "context/SyntheticsStateContext/selectors/globalSelectors"; import { useSelector } from "context/SyntheticsStateContext/utils"; import { TokensBalancesUpdates, useTokensBalancesUpdates, } from "context/TokensBalancesContext/TokensBalancesContextProvider"; -import { getTransferRequests } from "domain/multichain/getTransferRequests"; import { GlvSellTask, GmSellTask } from "domain/multichain/progress/GmOrGlvSellProgress"; import { toastCustomOrStargateError } from "domain/multichain/toastCustomOrStargateError"; import { TransferRequests } from "domain/multichain/types"; @@ -37,13 +39,12 @@ import { RawCreateGlvWithdrawalParams, RawCreateWithdrawalParams, } from "domain/synthetics/markets"; +import { buildWithdrawalTransferRequests } from "domain/synthetics/markets/buildWithdrawalTransferRequests"; import { createGlvWithdrawalTxn } from "domain/synthetics/markets/createGlvWithdrawalTxn"; import { createMultichainGlvWithdrawalTxn } from "domain/synthetics/markets/createMultichainGlvWithdrawalTxn"; import { createMultichainWithdrawalTxn } from "domain/synthetics/markets/createMultichainWithdrawalTxn"; import { createSourceChainGlvWithdrawalTxn } from "domain/synthetics/markets/createSourceChainGlvWithdrawalTxn"; import { createSourceChainWithdrawalTxn } from "domain/synthetics/markets/createSourceChainWithdrawalTxn"; -import { SourceChainGlvWithdrawalFees } from "domain/synthetics/markets/feeEstimation/estimateSourceChainGlvWithdrawalFees"; -import { SourceChainWithdrawalFees } from "domain/synthetics/markets/feeEstimation/estimateSourceChainWithdrawalFees"; import { useChainId } from "lib/chains"; import { helperToast } from "lib/helperToast"; import { @@ -54,9 +55,7 @@ import { sendTxnValidationErrorMetric, } from "lib/metrics"; import useWallet from "lib/wallets/useWallet"; -import { getContract } from "sdk/configs/contracts"; import { getWrappedToken } from "sdk/configs/tokens"; -import { ExecutionFee } from "sdk/types/fees"; import { TokenBalanceType } from "sdk/types/tokens"; import { WithdrawalAmounts } from "sdk/types/trade"; import { getGlvToken, getGmToken } from "sdk/utils/tokens"; @@ -73,7 +72,7 @@ export const useWithdrawalTransactions = ({ error: Error | undefined; } => { const { chainId, srcChainId } = useChainId(); - const { signer, account } = useWallet(); + const { signer } = useWallet(); const { setPendingWithdrawal } = useSyntheticsEvents(); const { setPendingTxns } = usePendingTxns(); const { addOptimisticTokensBalancesUpdates } = useTokensBalancesUpdates(); @@ -85,7 +84,7 @@ export const useWithdrawalTransactions = ({ const amounts = useSelector(selectDepositWithdrawalAmounts); const paySource = useSelector(selectPoolsDetailsPaySource); const selectedMarketForGlv = useSelector(selectPoolsDetailsSelectedMarketAddressForGlv); - const tokensData = useSelector(selectPoolsDetailsTradeTokensDataWithSourceChainBalances); + const tokensData = useSelector(selectTokensData); const { longTokenAmount = 0n, @@ -111,20 +110,27 @@ export const useWithdrawalTransactions = ({ const rawParams = useSelector(selectPoolsDetailsParams); const params = useMemo((): CreateWithdrawalParams | CreateGlvWithdrawalParams | undefined => { - if (!rawParams || !technicalFees || !isWithdrawal) { + if (!rawParams || !technicalFees || !isWithdrawal || technicalFees.isDeposit) { return undefined; } - const executionFee = - paySource === "sourceChain" - ? (technicalFees as SourceChainWithdrawalFees | SourceChainGlvWithdrawalFees).executionFee - : (technicalFees as ExecutionFee).feeTokenAmount; + let executionFee: bigint | undefined; + if (technicalFees.kind === "sourceChain") { + executionFee = technicalFees.fees.executionFee; + } else if (technicalFees.kind === "gmxAccount") { + executionFee = technicalFees.fees.executionFee.feeTokenAmount; + } else if (technicalFees.kind === "settlementChain") { + executionFee = technicalFees.fees.feeTokenAmount; + } + if (executionFee === undefined) { + return undefined; + } return { ...(rawParams as RawCreateWithdrawalParams), executionFee, }; - }, [rawParams, technicalFees, isWithdrawal, paySource]); + }, [rawParams, technicalFees, isWithdrawal]); const multichainWithdrawalExpressTxnParams = useMultichainWithdrawalExpressTxnParams({ transferRequests, @@ -203,17 +209,7 @@ export const useWithdrawalTransactions = ({ const metricData = getWithdrawalMetricData(); - if ( - !account || - !marketInfo || - !marketToken || - !amounts || - !transferRequests || - !tokensData || - !signer || - !params || - !isGlv - ) { + if (!amounts || !signer || !params) { helperToast.error(t`Error submitting order`); sendTxnValidationErrorMetric(metricData.metricId); return Promise.resolve(); @@ -226,6 +222,17 @@ export const useWithdrawalTransactions = ({ throw new Error("An error occurred"); } + const fees = + technicalFees?.kind === "sourceChain" && !technicalFees.isDeposit && technicalFees.isGlv + ? technicalFees.fees + : undefined; + + if (!fees || !transferRequests) { + helperToast.error(t`Error submitting order`); + sendTxnValidationErrorMetric(metricData.metricId); + return; + } + promise = createSourceChainGlvWithdrawalTxn({ chainId, srcChainId, @@ -233,7 +240,7 @@ export const useWithdrawalTransactions = ({ transferRequests, params: rawParams as CreateGlvWithdrawalParams, tokenAmount: glvTokenAmount!, - fees: technicalFees as SourceChainGlvWithdrawalFees, + fees, }) .then((res) => { if (res.transactionHash) { @@ -244,7 +251,7 @@ export const useWithdrawalTransactions = ({ token: getGlvToken(chainId, (rawParams as RawCreateGlvWithdrawalParams).addresses.glv), amount: (amounts as WithdrawalAmounts).glvTokenAmount, settlementChainId: chainId, - estimatedFeeUsd: (technicalFees as SourceChainGlvWithdrawalFees).relayFeeUsd, + estimatedFeeUsd: fees.relayFeeUsd, }) ); } @@ -254,8 +261,10 @@ export const useWithdrawalTransactions = ({ }); } else if (paySource === "gmxAccount") { const expressTxnParams = multichainWithdrawalExpressTxnParams.data; - if (!expressTxnParams) { - throw new Error("Express txn params are not set"); + if (!transferRequests || !expressTxnParams) { + helperToast.error(t`Error submitting order`); + sendTxnValidationErrorMetric(metricData.metricId); + return; } promise = createMultichainGlvWithdrawalTxn({ @@ -290,11 +299,19 @@ export const useWithdrawalTransactions = ({ } }); } else if (paySource === "settlementChain") { + const fees = technicalFees?.kind === "settlementChain" ? technicalFees.fees : undefined; + + if (!fees || !tokensData) { + helperToast.error(t`Error submitting order`); + sendTxnValidationErrorMetric(metricData.metricId); + return; + } + promise = createGlvWithdrawalTxn({ chainId, signer, params: params as CreateGlvWithdrawalParams, - executionGasLimit: (technicalFees as ExecutionFee).gasLimit, + executionGasLimit: fees.gasLimit, tokensData, setPendingTxns, setPendingWithdrawal, @@ -313,15 +330,11 @@ export const useWithdrawalTransactions = ({ [ isWithdrawal, getWithdrawalMetricData, - account, - marketInfo, - marketToken, amounts, transferRequests, tokensData, signer, params, - isGlv, paySource, chainId, srcChainId, @@ -347,17 +360,7 @@ export const useWithdrawalTransactions = ({ const metricData = getWithdrawalMetricData(); - if ( - !account || - !marketInfo || - !marketToken || - !amounts || - !transferRequests || - !tokensData || - !signer || - !params || - isGlv - ) { + if (!amounts || !signer || !params) { helperToast.error(t`Error submitting order`); sendTxnValidationErrorMetric(metricData.metricId); return Promise.resolve(); @@ -370,11 +373,21 @@ export const useWithdrawalTransactions = ({ throw new Error("An error occurred"); } + const fees = + technicalFees.kind === "sourceChain" && !technicalFees.isDeposit && !technicalFees.isGlv + ? technicalFees.fees + : undefined; + if (!fees || !transferRequests) { + helperToast.error(t`Error submitting order`); + sendTxnValidationErrorMetric(metricData.metricId); + return; + } + promise = createSourceChainWithdrawalTxn({ chainId, srcChainId, signer, - fees: technicalFees as SourceChainWithdrawalFees, + fees, transferRequests, params: rawParams as RawCreateWithdrawalParams, tokenAmount: marketTokenAmount!, @@ -387,21 +400,25 @@ export const useWithdrawalTransactions = ({ token: getGmToken(chainId, (rawParams as RawCreateWithdrawalParams).addresses.market), amount: (amounts as WithdrawalAmounts).marketTokenAmount, settlementChainId: chainId, - estimatedFeeUsd: (technicalFees as SourceChainWithdrawalFees).relayFeeUsd, + estimatedFeeUsd: fees.relayFeeUsd, }) ); } }); } else if (paySource === "gmxAccount") { const expressTxnParams = multichainWithdrawalExpressTxnParams.data; - if (!expressTxnParams) { - throw new Error("Express txn params are not set"); + if (!transferRequests || !expressTxnParams) { + helperToast.error(t`Error submitting order`); + sendTxnValidationErrorMetric(metricData.metricId); + return; } + const withdrawalParams = params as CreateWithdrawalParams; + promise = createMultichainWithdrawalTxn({ chainId, signer, - params: params as CreateWithdrawalParams, + params: withdrawalParams, expressTxnParams, transferRequests, srcChainId, @@ -418,7 +435,6 @@ export const useWithdrawalTransactions = ({ }); addOptimisticTokensBalancesUpdates(balanceUpdates); - const withdrawalParams = params as CreateWithdrawalParams; setPendingWithdrawal({ account: withdrawalParams.addresses.receiver, marketAddress: withdrawalParams.addresses.market, @@ -430,12 +446,22 @@ export const useWithdrawalTransactions = ({ } }); } else if (paySource === "settlementChain") { + const fees = technicalFees?.kind === "settlementChain" ? technicalFees.fees : undefined; + + if (!fees || !tokensData) { + helperToast.error(t`Error submitting order`); + sendTxnValidationErrorMetric(metricData.metricId); + return; + } + + const withdrawalParams = params as CreateWithdrawalParams; + promise = createWithdrawalTxn({ chainId, signer, marketTokenAmount: marketTokenAmount!, - executionGasLimit: (technicalFees as ExecutionFee).gasLimit, - params: params as CreateWithdrawalParams, + params: withdrawalParams, + executionGasLimit: fees.gasLimit, tokensData, skipSimulation: shouldDisableValidation, setPendingTxns, @@ -456,15 +482,11 @@ export const useWithdrawalTransactions = ({ [ isWithdrawal, getWithdrawalMetricData, - account, - marketInfo, - marketToken, amounts, transferRequests, tokensData, signer, params, - isGlv, paySource, chainId, srcChainId, @@ -509,33 +531,12 @@ function useWithdrawalTransferRequests(): TransferRequests | undefined { const glvTokenAddress = glvInfo?.glvTokenAddress; return useMemo((): TransferRequests | undefined => { - if (!isWithdrawal) { - return undefined; - } - - if (isGlv) { - if (!glvTokenAddress) { - return undefined; - } - return getTransferRequests([ - { - to: getContract(chainId, "GlvVault"), - token: glvTokenAddress, - amount: glvTokenAmount, - }, - ]); - } - - if (!marketTokenAddress) { - return undefined; - } - - return getTransferRequests([ - { - to: getContract(chainId, "WithdrawalVault"), - token: marketTokenAddress, - amount: marketTokenAmount, - }, - ]); + return buildWithdrawalTransferRequests({ + isWithdrawal, + isGlv, + chainId: chainId, + glvOrMarketTokenAddress: isGlv ? glvTokenAddress : marketTokenAddress, + glvOrMarketAmount: isGlv ? glvTokenAmount : marketTokenAmount, + }); }, [chainId, glvTokenAddress, glvTokenAmount, isGlv, isWithdrawal, marketTokenAddress, marketTokenAmount]); } diff --git a/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useDepositWithdrawalFees.tsx b/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useDepositWithdrawalFees.tsx index 53fc2d049c..787e13d1c1 100644 --- a/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useDepositWithdrawalFees.tsx +++ b/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useDepositWithdrawalFees.tsx @@ -1,16 +1,79 @@ import { useMemo } from "react"; import { useGasMultichainUsd, useNativeTokenMultichainUsd } from "domain/multichain/useMultichainQuoteFeeUsd"; -import { ExecutionFee, getFeeItem, getTotalFeeItem, type FeeItem, type GasLimitsConfig } from "domain/synthetics/fees"; +import { getFeeItem, getTotalFeeItem, type FeeItem, type GasLimitsConfig } from "domain/synthetics/fees"; import { GlvInfo } from "domain/synthetics/markets"; -import { SourceChainDepositFees } from "domain/synthetics/markets/feeEstimation/estimateSourceChainDepositFees"; -import { SourceChainGlvDepositFees } from "domain/synthetics/markets/feeEstimation/estimateSourceChainGlvDepositFees"; -import { SourceChainWithdrawalFees } from "domain/synthetics/markets/feeEstimation/estimateSourceChainWithdrawalFees"; -import { convertToUsd, getMidPrice, TokensData } from "domain/synthetics/tokens"; +import type { TechnicalGmFees } from "domain/synthetics/markets/technicalFees/technical-fees-types"; +import { convertToUsd, getMidPrice, TokenData, TokensData } from "domain/synthetics/tokens"; import { DepositAmounts, GmSwapFees, WithdrawalAmounts } from "domain/synthetics/trade"; +import { getByKey } from "lib/objects"; import { ContractsChainId, SourceChainId } from "sdk/configs/chains"; import { getWrappedToken } from "sdk/configs/tokens"; +function calculateLogicalNetworkFeeUsd({ + technicalFees, + wrappedTokenData, + sourceChainEstimatedNativeFeeUsd, + sourceChainTxnEstimatedGasUsd, +}: { + technicalFees: TechnicalGmFees; + wrappedTokenData: TokenData | undefined; + sourceChainEstimatedNativeFeeUsd: bigint | undefined; + sourceChainTxnEstimatedGasUsd: bigint | undefined; +}): bigint | undefined { + if (technicalFees.kind === "settlementChain") { + if (!wrappedTokenData) { + return undefined; + } + const wrappedTokenPrice = getMidPrice(wrappedTokenData.prices); + const keeperUsd = convertToUsd(technicalFees.fees.feeTokenAmount, wrappedTokenData.decimals, wrappedTokenPrice)!; + return keeperUsd * -1n; + } + + if (technicalFees.kind === "gmxAccount") { + return (technicalFees.fees.executionFee.feeUsd + technicalFees.fees.relayFeeUsd) * -1n; + } + + if (technicalFees.kind === "sourceChain") { + return ((sourceChainEstimatedNativeFeeUsd ?? 0n) + (sourceChainTxnEstimatedGasUsd ?? 0n)) * -1n; + } + + return 0n; +} + +function calculateLogicalFees({ + amounts, + isDeposit, + logicalNetworkFeeUsd, +}: { + amounts: DepositAmounts | WithdrawalAmounts; + isDeposit: boolean; + logicalNetworkFeeUsd: bigint; +}): GmSwapFees { + const basisUsd = isDeposit + ? (amounts.longTokenUsd ?? 0n) + (amounts.shortTokenUsd ?? 0n) + : amounts.marketTokenUsd ?? 0n; + + const swapFee = getFeeItem(amounts.swapFeeUsd * -1n, basisUsd); + const swapPriceImpact = getFeeItem(amounts.swapPriceImpactDeltaUsd, basisUsd); + const uiFee = getFeeItem(amounts.uiFeeUsd * -1n, basisUsd, { + shouldRoundUp: true, + }); + + const logicalNetworkFee = getFeeItem(logicalNetworkFeeUsd, basisUsd)!; + // TODO ADD stargate protocol fees + const logicalProtocolFee = getTotalFeeItem([swapFee, uiFee, swapPriceImpact].filter(Boolean) as FeeItem[]); + + const logicalFees: GmSwapFees = { + totalFees: logicalProtocolFee, + swapPriceImpact, + logicalNetworkFee, + swapFee, + }; + + return logicalFees; +} + export const useDepositWithdrawalFees = ({ amounts, chainId, @@ -29,68 +92,46 @@ export const useDepositWithdrawalFees = ({ tokensData: TokensData | undefined; glvInfo: GlvInfo | undefined; isMarketTokenDeposit: boolean; - technicalFees: - | ExecutionFee - | SourceChainGlvDepositFees - | SourceChainDepositFees - | SourceChainWithdrawalFees - | undefined; + technicalFees: TechnicalGmFees | undefined; srcChainId: SourceChainId | undefined; -}): { logicalFees?: GmSwapFees } => { +}): GmSwapFees | undefined => { const sourceChainEstimatedNativeFeeUsd = useNativeTokenMultichainUsd({ sourceChainTokenAmount: - technicalFees && "txnEstimatedNativeFee" in technicalFees ? technicalFees.txnEstimatedNativeFee : undefined, + technicalFees?.kind === "sourceChain" ? technicalFees.fees.txnEstimatedNativeFee : undefined, sourceChainId: srcChainId, targetChainId: chainId, }); const sourceChainTxnEstimatedGasUsd = useGasMultichainUsd({ - sourceChainGas: - technicalFees && "txnEstimatedGasLimit" in technicalFees ? technicalFees.txnEstimatedGasLimit : undefined, + sourceChainGas: technicalFees?.kind === "sourceChain" ? technicalFees.fees.txnEstimatedGasLimit : undefined, sourceChainId: srcChainId, targetChainId: chainId, }); return useMemo(() => { if (!gasLimits || gasPrice === undefined || !tokensData || !amounts || !technicalFees) { - return { logicalFees: undefined }; + return undefined; } - const basisUsd = isDeposit - ? (amounts?.longTokenUsd ?? 0n) + (amounts?.shortTokenUsd ?? 0n) - : amounts?.marketTokenUsd || 0n; + const wrappedToken = getWrappedToken(chainId); + const wrappedTokenData = getByKey(tokensData, wrappedToken.address); - const swapFee = getFeeItem(amounts.swapFeeUsd * -1n, basisUsd); - const swapPriceImpact = getFeeItem(amounts.swapPriceImpactDeltaUsd, basisUsd); - const uiFee = getFeeItem(amounts.uiFeeUsd * -1n, basisUsd, { - shouldRoundUp: true, + const logicalNetworkFeeUsd = calculateLogicalNetworkFeeUsd({ + technicalFees, + wrappedTokenData, + sourceChainEstimatedNativeFeeUsd, + sourceChainTxnEstimatedGasUsd, }); - let logicalNetworkFeeUsd = 0n; - if ("feeTokenAmount" in technicalFees) { - logicalNetworkFeeUsd = convertToUsd( - technicalFees.feeTokenAmount * -1n, - getWrappedToken(chainId).decimals, - getMidPrice(tokensData[getWrappedToken(chainId).address].prices) - )!; - } else { - logicalNetworkFeeUsd = ((sourceChainEstimatedNativeFeeUsd ?? 0n) + (sourceChainTxnEstimatedGasUsd ?? 0n)) * -1n; + if (logicalNetworkFeeUsd === undefined) { + return undefined; } - const logicalNetworkFee = getFeeItem(logicalNetworkFeeUsd, basisUsd)!; - // TODO ADD stargate protocol fees - const logicalProtocolFee = getTotalFeeItem([swapFee, uiFee, swapPriceImpact].filter(Boolean) as FeeItem[]); - - const logicalFees: GmSwapFees = { - totalFees: logicalProtocolFee, - swapPriceImpact, - logicalNetworkFee, - swapFee, - }; - - return { - logicalFees, - }; + return calculateLogicalFees({ + amounts, + isDeposit, + logicalNetworkFeeUsd, + }); }, [ amounts, chainId, diff --git a/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useGmSwapSubmitState.tsx b/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useGmSwapSubmitState.tsx index 5e650c65cd..9b83e62518 100644 --- a/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useGmSwapSubmitState.tsx +++ b/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useGmSwapSubmitState.tsx @@ -26,12 +26,9 @@ import { selectGasPaymentTokenAddress } from "context/SyntheticsStateContext/sel import { useSelector } from "context/SyntheticsStateContext/utils"; import { useSourceChainNativeFeeError } from "domain/multichain/useSourceChainNetworkFeeError"; import { ExpressEstimationInsufficientGasPaymentTokenBalanceError } from "domain/synthetics/express/expressOrderUtils"; -import type { ExecutionFee } from "domain/synthetics/fees"; import type { GlvAndGmMarketsInfoData, GmPaySource, MarketsInfoData } from "domain/synthetics/markets"; -import type { SourceChainDepositFees } from "domain/synthetics/markets/feeEstimation/estimateSourceChainDepositFees"; -import type { SourceChainGlvDepositFees } from "domain/synthetics/markets/feeEstimation/estimateSourceChainGlvDepositFees"; -import { SourceChainGlvWithdrawalFees } from "domain/synthetics/markets/feeEstimation/estimateSourceChainGlvWithdrawalFees"; -import { SourceChainWithdrawalFees } from "domain/synthetics/markets/feeEstimation/estimateSourceChainWithdrawalFees"; +import { TechnicalGmFees } from "domain/synthetics/markets/technicalFees/technical-fees-types"; +import { Operation } from "domain/synthetics/markets/types"; import { convertToTokenAmount, type TokenData } from "domain/synthetics/tokens"; import { getCommonError, getGmSwapError } from "domain/synthetics/trade/utils/validation"; import { adjustForDecimals, formatBalanceAmount } from "lib/numbers"; @@ -42,22 +39,14 @@ import { bigMath } from "sdk/utils/bigmath"; import SpinnerIcon from "img/ic_spinner.svg?react"; -import { Operation } from "../types"; import { useLpTransactions } from "./lpTxn/useLpTransactions"; -import { TechnicalFees } from "./useTechnicalFeesAsyncResult"; import { useTokensToApprove } from "./useTokensToApprove"; interface Props { longTokenLiquidityUsd?: bigint | undefined; shortTokenLiquidityUsd?: bigint | undefined; shouldDisableValidation?: boolean; - technicalFees: - | ExecutionFee - | SourceChainGlvDepositFees - | SourceChainDepositFees - | SourceChainWithdrawalFees - | SourceChainGlvWithdrawalFees - | undefined; + technicalFees: TechnicalGmFees | undefined; logicalFees: GmSwapFees | undefined; marketsInfoData?: MarketsInfoData; glvAndMarketsInfoData: GlvAndGmMarketsInfoData; @@ -390,7 +379,7 @@ function useExpressError({ isDeposit, }: { paySource: GmPaySource | undefined; - technicalFees: TechnicalFees | undefined; + technicalFees: TechnicalGmFees | undefined; gasPaymentToken: TokenData | undefined; gasPaymentTokenAddress: string | undefined; longTokenAddress: string | undefined; @@ -404,18 +393,17 @@ function useExpressError({ return undefined; } - if (!("feeUsd" in technicalFees)) { + const fees = technicalFees.kind === "gmxAccount" ? technicalFees.fees : undefined; + if (!fees) { return undefined; } - const executionFee = technicalFees as ExecutionFee; - if (gasPaymentToken.prices.minPrice === undefined) { return undefined; } const gasPaymentTokenAmount = convertToTokenAmount( - executionFee.feeUsd, + fees.executionFee.feeUsd + fees.relayFeeUsd, gasPaymentToken.decimals, gasPaymentToken.prices.minPrice ); diff --git a/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useTechnicalFeesAsyncResult.tsx b/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useTechnicalFeesAsyncResult.tsx index fe8cdbf695..589c80410a 100644 --- a/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useTechnicalFeesAsyncResult.tsx +++ b/src/components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useTechnicalFeesAsyncResult.tsx @@ -1,13 +1,14 @@ -import { SettlementChainId, SourceChainId } from "config/chains"; import { selectPoolsDetailsFirstTokenAddress, selectPoolsDetailsFirstTokenAmount, selectPoolsDetailsFlags, selectPoolsDetailsGlvInfo, + selectPoolsDetailsLongTokenAmount, selectPoolsDetailsMarketOrGlvTokenAmount, selectPoolsDetailsOperation, selectPoolsDetailsPaySource, selectPoolsDetailsSelectedMarketAddressForGlv, + selectPoolsDetailsShortTokenAmount, } from "context/PoolsDetailsContext/selectors"; import { selectDepositWithdrawalAmounts } from "context/PoolsDetailsContext/selectors/selectDepositWithdrawalAmounts"; import { selectPoolsDetailsParams } from "context/PoolsDetailsContext/selectors/selectPoolsDetailsParams"; @@ -18,48 +19,17 @@ import { selectTokensData, } from "context/SyntheticsStateContext/selectors/globalSelectors"; import { useSelector } from "context/SyntheticsStateContext/utils"; -import { - RawCreateDepositParams, - RawCreateGlvDepositParams, - RawCreateGlvWithdrawalParams, - RawCreateWithdrawalParams, -} from "domain/synthetics/markets"; -import { estimatePureLpActionExecutionFee } from "domain/synthetics/markets/feeEstimation/estimatePureLpActionExecutionFee"; -import { - estimateSourceChainDepositFees, - SourceChainDepositFees, -} from "domain/synthetics/markets/feeEstimation/estimateSourceChainDepositFees"; -import { - estimateSourceChainGlvDepositFees, - SourceChainGlvDepositFees, -} from "domain/synthetics/markets/feeEstimation/estimateSourceChainGlvDepositFees"; -import { - estimateSourceChainGlvWithdrawalFees, - SourceChainGlvWithdrawalFees, -} from "domain/synthetics/markets/feeEstimation/estimateSourceChainGlvWithdrawalFees"; -import { - estimateSourceChainWithdrawalFees, - SourceChainWithdrawalFees, -} from "domain/synthetics/markets/feeEstimation/estimateSourceChainWithdrawalFees"; +import { calculateTechnicalFees } from "domain/synthetics/markets/technicalFees/calculateTechnicalFees"; +import type { + CalculateTechnicalFeesParams, + TechnicalGmFees, +} from "domain/synthetics/markets/technicalFees/technical-fees-types"; import { useChainId } from "lib/chains"; import { usePrevious } from "lib/usePrevious"; import { useThrottledAsync } from "lib/useThrottledAsync"; -import { MARKETS } from "sdk/configs/markets"; -import { ExecutionFee } from "sdk/types/fees"; -import { WithdrawalAmounts } from "sdk/types/trade"; import { absDiffBps } from "sdk/utils/numbers"; -import { Operation } from "../types"; - -export type TechnicalFees = - | ExecutionFee - | SourceChainGlvDepositFees - | SourceChainDepositFees - | SourceChainWithdrawalFees - | SourceChainGlvWithdrawalFees - | undefined; - -export function useTechnicalFees(): TechnicalFees { +export function useTechnicalFees(): TechnicalGmFees | undefined { const { chainId, srcChainId } = useChainId(); const operation = useSelector(selectPoolsDetailsOperation); @@ -72,6 +42,8 @@ export function useTechnicalFees(): TechnicalFees { const firstTokenAddress = useSelector(selectPoolsDetailsFirstTokenAddress); const firstTokenAmount = useSelector(selectPoolsDetailsFirstTokenAmount); + const longTokenAmount = useSelector(selectPoolsDetailsLongTokenAmount); + const shortTokenAmount = useSelector(selectPoolsDetailsShortTokenAmount); const marketOrGlvTokenAmount = useSelector(selectPoolsDetailsMarketOrGlvTokenAmount); const prevPaySource = usePrevious(paySource); @@ -94,183 +66,33 @@ export function useTechnicalFees(): TechnicalFees { const rawParams = useSelector(selectPoolsDetailsParams); const amounts = useSelector(selectDepositWithdrawalAmounts); - const technicalFeesAsyncResult = useThrottledAsync( - async (p) => { - if (p.params.paySource === "gmxAccount" || p.params.paySource === "settlementChain") { - if (p.params.operation === Operation.Deposit) { - if (p.params.isGlv) { - const castedParams = p.params.rawParams as RawCreateGlvDepositParams; - return estimatePureLpActionExecutionFee({ - action: { - operation: Operation.Deposit, - isGlv: true, - marketsCount: BigInt(p.params.glvInfo!.markets.length), - swapsCount: BigInt( - castedParams.addresses.longTokenSwapPath.length + castedParams.addresses.shortTokenSwapPath.length - ), - isMarketTokenDeposit: castedParams.isMarketTokenDeposit, - }, - chainId: p.params.chainId, - gasLimits: p.params.gasLimits, - tokensData: p.params.tokensData, - gasPrice: p.params.gasPrice, - }); - } else { - const castedParams = p.params.rawParams as RawCreateDepositParams; - return estimatePureLpActionExecutionFee({ - action: { - operation: Operation.Deposit, - isGlv: false, - swapsCount: BigInt( - castedParams.addresses.longTokenSwapPath.length + castedParams.addresses.shortTokenSwapPath.length - ), - }, - chainId: p.params.chainId, - gasLimits: p.params.gasLimits, - tokensData: p.params.tokensData, - gasPrice: p.params.gasPrice, - }); - } - } else if (p.params.operation === Operation.Withdrawal) { - if (p.params.isGlv) { - const castedParams = p.params.rawParams as RawCreateGlvWithdrawalParams; - return estimatePureLpActionExecutionFee({ - action: { - operation: Operation.Withdrawal, - isGlv: true, - marketsCount: BigInt(p.params.glvInfo!.markets.length), - swapsCount: BigInt( - castedParams.addresses.longTokenSwapPath.length + castedParams.addresses.shortTokenSwapPath.length - ), - }, - chainId: p.params.chainId, - gasLimits: p.params.gasLimits, - tokensData: p.params.tokensData, - gasPrice: p.params.gasPrice, - }); - } - - const castedParams = p.params.rawParams as RawCreateWithdrawalParams; - return estimatePureLpActionExecutionFee({ - action: { - operation: Operation.Withdrawal, - isGlv: false, - swapsCount: BigInt( - castedParams.addresses.longTokenSwapPath.length + castedParams.addresses.shortTokenSwapPath.length - ), - }, - chainId: p.params.chainId, - gasLimits: p.params.gasLimits, - tokensData: p.params.tokensData, - gasPrice: p.params.gasPrice, - }); - } - } else if (p.params.paySource === "sourceChain") { - if ( - p.params.tokenAddress === undefined || - p.params.tokenAmount === undefined || - !p.params.globalExpressParams - ) { - return undefined; - } - if (p.params.operation === Operation.Deposit) { - if (p.params.isGlv) { - const castedParams = p.params.rawParams as RawCreateGlvDepositParams; - return await estimateSourceChainGlvDepositFees({ - chainId: p.params.chainId as SettlementChainId, - srcChainId: p.params.srcChainId as SourceChainId, - params: castedParams, - tokenAddress: p.params.tokenAddress, - tokenAmount: p.params.tokenAmount, - globalExpressParams: p.params.globalExpressParams, - glvMarketCount: BigInt(p.params.glvInfo!.markets.length), - }); - } else { - const castedParams = p.params.rawParams as RawCreateDepositParams; - return await estimateSourceChainDepositFees({ - chainId: p.params.chainId as SettlementChainId, - srcChainId: p.params.srcChainId as SourceChainId, - params: castedParams, - tokenAddress: p.params.tokenAddress, - tokenAmount: p.params.tokenAmount, - globalExpressParams: p.params.globalExpressParams, - }); - } - } else if (p.params.operation === Operation.Withdrawal) { - if (p.params.isGlv) { - const castedParams = p.params.rawParams as RawCreateGlvWithdrawalParams; - const glvWithdrawalAmounts = p.params.amounts as WithdrawalAmounts; - const outputLongTokenAddress = - glvWithdrawalAmounts.longTokenSwapPathStats?.tokenOutAddress ?? glvInfo!.longTokenAddress; - const outputShortTokenAddress = - glvWithdrawalAmounts.shortTokenSwapPathStats?.tokenOutAddress ?? glvInfo!.shortTokenAddress; - - return await estimateSourceChainGlvWithdrawalFees({ - chainId: p.params.chainId as SettlementChainId, - srcChainId: p.params.srcChainId as SourceChainId, - params: castedParams, - tokenAddress: castedParams.addresses.glv, - tokenAmount: p.params.marketTokenAmount, - globalExpressParams: p.params.globalExpressParams, - marketsCount: BigInt(p.params.glvInfo!.markets.length), - outputLongTokenAddress, - outputShortTokenAddress, - }); - } else { - const castedParams = p.params.rawParams as RawCreateWithdrawalParams; - if (!p.params.amounts) { - return undefined; - } - - const gmWithdrawalAmounts = p.params.amounts as WithdrawalAmounts; - - const outputLongTokenAddress = - gmWithdrawalAmounts.longTokenSwapPathStats?.tokenOutAddress ?? - MARKETS[p.params.chainId][p.params.rawParams.addresses.market].longTokenAddress; - const outputShortTokenAddress = - gmWithdrawalAmounts.shortTokenSwapPathStats?.tokenOutAddress ?? - MARKETS[p.params.chainId][p.params.rawParams.addresses.market].shortTokenAddress; - - return await estimateSourceChainWithdrawalFees({ - chainId: p.params.chainId as SettlementChainId, - srcChainId: p.params.srcChainId as SourceChainId, - params: castedParams, - tokenAddress: p.params.rawParams.addresses.market, - tokenAmount: p.params.marketTokenAmount, - globalExpressParams: p.params.globalExpressParams, - outputLongTokenAddress, - outputShortTokenAddress, - }); - } - } - } - }, - { - params: - rawParams && gasLimits && tokensData && gasPrice !== undefined - ? { - chainId, - globalExpressParams, - rawParams, - isGlv, - glvInfo, - paySource, - srcChainId, - tokenAddress: firstTokenAddress, - tokenAmount: firstTokenAmount, - marketTokenAmount: marketOrGlvTokenAmount, - operation, - amounts, - gasLimits, - tokensData, - gasPrice, - } - : undefined, - withLoading: false, - forceRecalculate, - resetOnForceRecalculate: true, - } - ); + const technicalFeesAsyncResult = useThrottledAsync(async (p) => calculateTechnicalFees(p.params), { + params: + rawParams && gasLimits && tokensData && gasPrice !== undefined + ? ({ + chainId, + globalExpressParams, + rawParams, + isGlv, + glvInfo, + paySource, + srcChainId, + firstTokenAddress, + firstTokenAmount, + longTokenAmount, + shortTokenAmount, + marketTokenAmount: marketOrGlvTokenAmount, + operation, + amounts, + gasLimits, + tokensData, + gasPrice, + } satisfies CalculateTechnicalFeesParams) + : undefined, + withLoading: false, + forceRecalculate, + resetOnForceRecalculate: true, + }); return technicalFeesAsyncResult.data; } diff --git a/src/components/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx b/src/components/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx index fd1c38a053..ceccb79bd9 100644 --- a/src/components/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +++ b/src/components/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx @@ -16,6 +16,7 @@ import { selectShiftAvailableMarkets } from "context/SyntheticsStateContext/sele import { useSelector } from "context/SyntheticsStateContext/utils"; import { GlvOrMarketInfo, getGlvOrMarketAddress, getMarketIndexName } from "domain/synthetics/markets"; import { isGlvInfo } from "domain/synthetics/markets/glv"; +import { Operation } from "domain/synthetics/markets/types"; import { useMarketTokensData } from "domain/synthetics/markets/useMarketTokensData"; import useSortedPoolsWithIndexToken from "domain/synthetics/trade/useSortedPoolsWithIndexToken"; import { ERC20Address, NativeTokenSupportedAddress } from "domain/tokens"; @@ -34,7 +35,6 @@ import { SwitchToSettlementChainWarning } from "components/SwitchToSettlementCha import { GmFees } from "../../GmFees/GmFees"; import { GmSwapWarningsRow } from "../GmSwapWarningsRow"; import { SelectedPool } from "../SelectedPool"; -import { Operation } from "../types"; import { useGmWarningState } from "../useGmWarningState"; import { useShiftAmounts } from "./useShiftAmounts"; import { useShiftAvailableRelatedMarkets } from "./useShiftAvailableRelatedMarkets"; diff --git a/src/components/GmSwap/GmSwapBox/GmSwapBox.tsx b/src/components/GmSwap/GmSwapBox/GmSwapBox.tsx index cdedb4a9fc..ef8a12643a 100644 --- a/src/components/GmSwap/GmSwapBox/GmSwapBox.tsx +++ b/src/components/GmSwap/GmSwapBox/GmSwapBox.tsx @@ -12,13 +12,13 @@ import { selectPoolsDetailsSetSelectedMarketAddressForGlv, } from "context/PoolsDetailsContext/selectors"; import { useSelector } from "context/SyntheticsStateContext/utils"; +import { Mode, Operation } from "domain/synthetics/markets/types"; import { useLocalizedMap } from "lib/i18n"; import Tabs from "components/Tabs/Tabs"; import { GmSwapBoxDepositWithdrawal } from "./GmDepositWithdrawalBox/GmDepositWithdrawalBox"; import { GmShiftBox } from "./GmShiftBox/GmShiftBox"; -import { Mode, Operation } from "./types"; import "./GmSwapBox.scss"; diff --git a/src/components/GmSwap/GmSwapBox/GmSwapBoxHeader.tsx b/src/components/GmSwap/GmSwapBox/GmSwapBoxHeader.tsx index 206517fdd6..97a596d970 100644 --- a/src/components/GmSwap/GmSwapBox/GmSwapBoxHeader.tsx +++ b/src/components/GmSwap/GmSwapBox/GmSwapBoxHeader.tsx @@ -10,14 +10,13 @@ import { selectGlvAndMarketsInfoData } from "context/SyntheticsStateContext/sele import { selectShiftAvailableMarkets } from "context/SyntheticsStateContext/selectors/shiftSelectors"; import { useSelector } from "context/SyntheticsStateContext/utils"; import { isGlvInfo } from "domain/synthetics/markets/glv"; +import { Operation } from "domain/synthetics/markets/types"; import { getGlvOrMarketAddress } from "domain/synthetics/markets/utils"; import { useLocalizedMap } from "lib/i18n"; import { getByKey } from "lib/objects"; import Tabs from "components/Tabs/Tabs"; -import { Operation } from "./types"; - const OPERATION_LABELS_GM = { [Operation.Deposit]: msg`Buy GM`, [Operation.Withdrawal]: msg`Sell GM`, diff --git a/src/components/GmSwap/GmSwapBox/getGmSwapBoxAvailableModes.tsx b/src/components/GmSwap/GmSwapBox/getGmSwapBoxAvailableModes.tsx index f194d4a403..a528000bd6 100644 --- a/src/components/GmSwap/GmSwapBox/getGmSwapBoxAvailableModes.tsx +++ b/src/components/GmSwap/GmSwapBox/getGmSwapBoxAvailableModes.tsx @@ -1,6 +1,4 @@ -import { GmPaySource, Market } from "domain/synthetics/markets/types"; - -import { Mode, Operation } from "./types"; +import { GmPaySource, Market, Mode, Operation } from "domain/synthetics/markets/types"; export function getGmSwapBoxAvailableModes({ operation, diff --git a/src/components/GmSwap/GmSwapBox/types.ts b/src/components/GmSwap/GmSwapBox/types.ts deleted file mode 100644 index 6393145d7d..0000000000 --- a/src/components/GmSwap/GmSwapBox/types.ts +++ /dev/null @@ -1,18 +0,0 @@ -export enum Operation { - Deposit = "Deposit", - Withdrawal = "Withdrawal", - Shift = "Shift", -} - -export enum Mode { - Single = "Single", - Pair = "Pair", -} - -export function isOperation(operation: string): operation is Operation { - return Object.values(Operation).includes(operation as Operation); -} - -export function isMode(mode: string): mode is Mode { - return Object.values(Mode).includes(mode as Mode); -} diff --git a/src/components/GmxAccountModal/DepositView.tsx b/src/components/GmxAccountModal/DepositView.tsx index 41e5e4539b..386edf77f1 100644 --- a/src/components/GmxAccountModal/DepositView.tsx +++ b/src/components/GmxAccountModal/DepositView.tsx @@ -381,7 +381,6 @@ export const DepositView = () => { const quoteSendNativeFee = useQuoteSendNativeFee({ sendParams: sendParamsWithSlippage, fromStargateAddress: selectedTokenSourceChainTokenId?.stargate, - fromChainProvider: sourceChainProvider, fromChainId: depositViewChain, toChainId: settlementChainId, composeGas, diff --git a/src/components/GmxAccountModal/TransferDetailsView.tsx b/src/components/GmxAccountModal/TransferDetailsView.tsx index 9fe18d41b7..501375d51c 100644 --- a/src/components/GmxAccountModal/TransferDetailsView.tsx +++ b/src/components/GmxAccountModal/TransferDetailsView.tsx @@ -18,7 +18,7 @@ import { useChainId } from "lib/chains"; import { CHAIN_ID_TO_EXPLORER_NAME, CHAIN_ID_TO_TX_URL_BUILDER } from "lib/chains/blockExplorers"; import { formatAmountFree } from "lib/numbers"; import { shortenAddressOrEns } from "lib/wallets"; -import { getToken } from "sdk/configs/tokens"; +import { convertTokenAddress, getToken } from "sdk/configs/tokens"; import { AlertInfoCard } from "components/AlertInfo/AlertInfoCard"; import { Amount } from "components/Amount/Amount"; @@ -84,7 +84,7 @@ export const TransferDetailsView = () => { if (selectedTransfer.operation === "withdrawal") { setGmxAccountWithdrawalViewChain(selectedTransfer.sourceChainId as SourceChainId); - setGmxAccountWithdrawalViewTokenAddress(selectedTransfer.token); + setGmxAccountWithdrawalViewTokenAddress(convertTokenAddress(chainId, selectedTransfer.token, "wrapped")); setGmxAccountWithdrawalViewTokenInputValue(formatAmountFree(selectedTransfer.sentAmount, token.decimals)); setGmxAccountModalOpen("withdraw"); } diff --git a/src/components/GmxAccountModal/WithdrawalView.tsx b/src/components/GmxAccountModal/WithdrawalView.tsx index e77a40fe0e..b1f7ea0426 100644 --- a/src/components/GmxAccountModal/WithdrawalView.tsx +++ b/src/components/GmxAccountModal/WithdrawalView.tsx @@ -296,7 +296,6 @@ export const WithdrawalView = () => { const nativeFee = useQuoteSendNativeFee({ sendParams: sendParamsWithSlippage, fromStargateAddress: selectedTokenSettlementChainTokenId?.stargate, - fromChainProvider: provider, fromChainId: chainId, toChainId: withdrawalViewChain, }); @@ -332,7 +331,6 @@ export const WithdrawalView = () => { const baseNativeFee = useQuoteSendNativeFee({ sendParams: baseSendParams, fromStargateAddress: selectedTokenSettlementChainTokenId?.stargate, - fromChainProvider: provider, fromChainId: chainId, toChainId: withdrawalViewChain, }); @@ -853,7 +851,7 @@ export const WithdrawalView = () => { text: t`Insufficient ${isOutOfTokenErrorToken?.symbol} balance`, disabled: true, }; - } else if (showWntWarning) { + } else if (showWntWarning && !expressTxnParamsAsyncResult.isLoading) { buttonState = { text: t`Insufficient ${wrappedNativeToken?.symbol} balance`, disabled: true, @@ -865,9 +863,13 @@ export const WithdrawalView = () => { }; } else if ( // We do not show loading state if we have valid params - // But show loafing periodically if the params are not valid to show the user some action + // But show loading periodically if the params are not valid to show the user some action !expressTxnParamsAsyncResult.data || - (expressTxnParamsAsyncResult.isLoading && !expressTxnParamsAsyncResult.data.gasPaymentValidations.isValid) + (expressTxnParamsAsyncResult.isLoading && + (!expressTxnParamsAsyncResult.data.gasPaymentValidations.isValid || + showWntWarning || + errors?.isOutOfTokenError || + expressTxnParamsAsyncResult.error)) ) { buttonState = { text: ( @@ -881,10 +883,11 @@ export const WithdrawalView = () => { } } - const hasSelectedToken = selectedTokenAddress !== undefined; + const hasValidSelectedToken = + selectedTokenAddress !== undefined && MULTI_CHAIN_WITHDRAWAL_TRADE_TOKENS[chainId]?.includes(selectedTokenAddress); useEffect( function fallbackWithdrawTokens() { - if (hasSelectedToken || !withdrawalViewChain || !isSettlementChain(chainId) || isVisibleOrView === false) { + if (hasValidSelectedToken || !withdrawalViewChain || !isSettlementChain(chainId) || isVisibleOrView === false) { return; } @@ -934,7 +937,7 @@ export const WithdrawalView = () => { setSelectedTokenAddress(maxBalanceSettlementChainTokenAddress); } }, - [chainId, hasSelectedToken, isVisibleOrView, setSelectedTokenAddress, tokensData, withdrawalViewChain] + [chainId, hasValidSelectedToken, isVisibleOrView, setSelectedTokenAddress, tokensData, withdrawalViewChain] ); const isTestnet = isTestnetChain(chainId); diff --git a/src/components/MarketStats/hooks/useBestGmPoolForGlv.ts b/src/components/MarketStats/hooks/useBestGmPoolForGlv.ts index 9765111e2d..aceca12bb5 100644 --- a/src/components/MarketStats/hooks/useBestGmPoolForGlv.ts +++ b/src/components/MarketStats/hooks/useBestGmPoolForGlv.ts @@ -2,10 +2,10 @@ import { useEffect, useMemo } from "react"; import { selectPoolsDetailsFlags, - selectPoolsDetailsIsMarketForGlvSelectedManually, selectPoolsDetailsFocusedInput, selectPoolsDetailsGlvInfo, selectPoolsDetailsGlvTokenAmount, + selectPoolsDetailsIsMarketForGlvSelectedManually, selectPoolsDetailsIsMarketTokenDeposit, selectPoolsDetailsLongTokenAddress, selectPoolsDetailsLongTokenAmount, @@ -21,12 +21,14 @@ import { TokensData } from "domain/synthetics/tokens"; import { getDepositAmounts } from "domain/synthetics/trade/utils/deposit"; import { getGmSwapError } from "domain/synthetics/trade/utils/validation"; import { useChainId } from "lib/chains"; +import { absDiffBps } from "lib/numbers"; import { usePrevious } from "lib/usePrevious"; - -import type { useDepositWithdrawalFees } from "components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useDepositWithdrawalFees"; +import type { GmSwapFees } from "sdk/types/trade"; import { useGlvGmMarketsWithComposition } from "./useMarketGlvGmMarketsCompositions"; +const AMOUNT_CHANGE_THRESHOLD_BPS = 50n; // 0.5% + export const useBestGmPoolAddressForGlv = ({ fees, uiFeeFactor, @@ -36,7 +38,7 @@ export const useBestGmPoolAddressForGlv = ({ uiFeeFactor: bigint; marketTokenAmount: bigint; marketTokensData: TokensData | undefined; - fees: ReturnType["logicalFees"]; + fees: GmSwapFees | undefined; }) => { const { chainId, srcChainId } = useChainId(); @@ -230,18 +232,29 @@ export const useBestGmPoolAddressForGlv = ({ return; } + if (!selectedMarketForGlv && bestGmMarketAddress) { + setSelectedMarketAddressForGlv(bestGmMarketAddress); + return; + } + + if (isMarketForGlvSelectedManually || bestGmMarketAddress === selectedMarketForGlv) { + return; + } + + const longAmountChanged = absDiffBps(longTokenAmount ?? 0n, previousLongAmount ?? 0n) > AMOUNT_CHANGE_THRESHOLD_BPS; + const shortAmountChanged = + absDiffBps(shortTokenAmount ?? 0n, previousShortAmount ?? 0n) > AMOUNT_CHANGE_THRESHOLD_BPS; + const marketTokenAmountChanged = + absDiffBps(marketTokenAmount ?? 0n, previousMarketTokenAmount ?? 0n) > AMOUNT_CHANGE_THRESHOLD_BPS; + const shouldSetBestGmMarket = - !isMarketForGlvSelectedManually && - (previousLongAmount !== longTokenAmount || - previousShortAmount !== shortTokenAmount || - previousMarketTokenAmount !== marketTokenAmount || - previousLongTokenAddress !== longTokenAddress || - previousShortTokenAddress !== shortTokenAddress); - - if ( - ((!selectedMarketForGlv && bestGmMarketAddress) || shouldSetBestGmMarket) && - bestGmMarketAddress !== selectedMarketForGlv - ) { + longAmountChanged || + shortAmountChanged || + marketTokenAmountChanged || + previousLongTokenAddress !== longTokenAddress || + previousShortTokenAddress !== shortTokenAddress; + + if (shouldSetBestGmMarket && bestGmMarketAddress !== selectedMarketForGlv) { setSelectedMarketAddressForGlv(bestGmMarketAddress); } }, [ diff --git a/src/config/multichain.ts b/src/config/multichain.ts index d33fca2cab..1f36d4b02f 100644 --- a/src/config/multichain.ts +++ b/src/config/multichain.ts @@ -25,7 +25,7 @@ import invert from "lodash/invert"; import mapValues from "lodash/mapValues"; import uniq from "lodash/uniq"; import type { Abi, Hex } from "viem"; -import { zeroAddress } from "viem"; +import { maxUint256, zeroAddress } from "viem"; import { AnyChainId, @@ -482,6 +482,11 @@ export const FAKE_INPUT_AMOUNT_MAP: Record = { export const RANDOM_SLOT = "0x23995301f0ea59f7cace2ae906341fc4662f3f5d23f124431ee3520d1070148c"; export const RANDOM_WALLET = Wallet.createRandom(); +/** + * Uses maxUint256 / 100n to avoid number overflows in EVM operations. + */ +export const SIMULATED_MULTICHAIN_BALANCE = maxUint256 / 100n; + export function getSourceChainDecimalsMapped( chainId: ContractsChainId, srcChainId: SourceChainId, diff --git a/src/context/PoolsDetailsContext/PoolsDetailsContext.tsx b/src/context/PoolsDetailsContext/PoolsDetailsContext.tsx index 865cd7129d..567d24031c 100644 --- a/src/context/PoolsDetailsContext/PoolsDetailsContext.tsx +++ b/src/context/PoolsDetailsContext/PoolsDetailsContext.tsx @@ -4,7 +4,7 @@ import { SYNTHETICS_MARKET_DEPOSIT_TOKEN_KEY } from "config/localStorage"; import { getAreBothCollateralsCrossChain } from "domain/multichain/areBothCollateralsCrossChain"; import { GlvInfoData, MarketsInfoData, useMarketTokensDataRequest } from "domain/synthetics/markets"; import { isGlvAddress } from "domain/synthetics/markets/glv"; -import { GmPaySource } from "domain/synthetics/markets/types"; +import { GmPaySource, Mode, Operation, isMode, isOperation } from "domain/synthetics/markets/types"; import { TokensData } from "domain/synthetics/tokens"; import { ERC20Address, NativeTokenSupportedAddress } from "domain/tokens"; import { useChainId } from "lib/chains"; @@ -17,7 +17,6 @@ import { isMarketTokenAddress } from "sdk/configs/markets"; import { getGmSwapBoxAvailableModes } from "components/GmSwap/GmSwapBox/getGmSwapBoxAvailableModes"; import { FocusedInput } from "components/GmSwap/GmSwapBox/GmDepositWithdrawalBox/types"; -import { Mode, Operation, isMode, isOperation } from "components/GmSwap/GmSwapBox/types"; import { useMultichainMarketTokensBalancesRequest, useMultichainTokens, diff --git a/src/context/PoolsDetailsContext/selectors/baseSelectors.ts b/src/context/PoolsDetailsContext/selectors/baseSelectors.ts index a233f5a827..222eef3886 100644 --- a/src/context/PoolsDetailsContext/selectors/baseSelectors.ts +++ b/src/context/PoolsDetailsContext/selectors/baseSelectors.ts @@ -1,10 +1,9 @@ import noop from "lodash/noop"; import { SyntheticsState } from "context/SyntheticsStateContext/SyntheticsStateContextProvider"; +import { Mode, Operation } from "domain/synthetics/markets/types"; import { EMPTY_ARRAY } from "lib/objects"; -import { Mode, Operation } from "components/GmSwap/GmSwapBox/types"; - export const PLATFORM_TOKEN_DECIMALS = 18; const FALLBACK_STRING_SETTER = noop as (value: string) => void; diff --git a/src/context/PoolsDetailsContext/selectors/poolsDetailsDerivedSelectors.ts b/src/context/PoolsDetailsContext/selectors/poolsDetailsDerivedSelectors.ts index 4913026653..27852523c2 100644 --- a/src/context/PoolsDetailsContext/selectors/poolsDetailsDerivedSelectors.ts +++ b/src/context/PoolsDetailsContext/selectors/poolsDetailsDerivedSelectors.ts @@ -16,6 +16,7 @@ import { createSelector } from "context/SyntheticsStateContext/utils"; import { getAreBothCollateralsCrossChain } from "domain/multichain/areBothCollateralsCrossChain"; import { isMarketInfo } from "domain/synthetics/markets"; import { isGlvInfo } from "domain/synthetics/markets/glv"; +import { Mode, Operation } from "domain/synthetics/markets/types"; import { ERC20Address, getGmToken, getTokenData, Token, TokenBalanceType } from "domain/tokens"; import { parseValue } from "lib/numbers"; import { getByKey } from "lib/objects"; @@ -29,7 +30,6 @@ import { convertTokenAddress, getToken } from "sdk/configs/tokens"; import { SwapPricingType } from "sdk/types/orders"; import { getGmSwapBoxAvailableModes } from "components/GmSwap/GmSwapBox/getGmSwapBoxAvailableModes"; -import { Mode, Operation } from "components/GmSwap/GmSwapBox/types"; import { PLATFORM_TOKEN_DECIMALS, diff --git a/src/context/PoolsDetailsContext/selectors/selectPoolsDetailsParams.tsx b/src/context/PoolsDetailsContext/selectors/selectPoolsDetailsParams.tsx index 295d007758..a96c8be60f 100644 --- a/src/context/PoolsDetailsContext/selectors/selectPoolsDetailsParams.tsx +++ b/src/context/PoolsDetailsContext/selectors/selectPoolsDetailsParams.tsx @@ -29,7 +29,6 @@ import { selectAccount, selectChainId, selectGlvAndMarketsInfoData, - selectMarketsInfoData, selectSrcChainId, } from "context/SyntheticsStateContext/selectors/globalSelectors"; import { createSelector } from "context/SyntheticsStateContext/utils"; @@ -82,7 +81,6 @@ export const selectPoolsDetailsParams = createSelector((q): PoolsDetailsParams = const shortTokenAmount = q(selectPoolsDetailsShortTokenAmount); const glvAndMarketsInfoData = q(selectGlvAndMarketsInfoData); - const marketsInfoData = q(selectMarketsInfoData); const marketOrGlvTokenAddress = q(selectPoolsDetailsGlvOrMarketAddress); const selectedMarketForGlv = q(selectPoolsDetailsSelectedMarketAddressForGlv); @@ -109,12 +107,6 @@ export const selectPoolsDetailsParams = createSelector((q): PoolsDetailsParams = return undefined; } - const marketInfo = isGlv - ? selectedMarketForGlv - ? marketsInfoData?.[selectedMarketForGlv] - : undefined - : glvOrMarketInfo; - const glvInfo = isGlv ? glvOrMarketInfo : undefined; const wrappedNativeTokenAddress = getWrappedToken(chainId); @@ -188,7 +180,7 @@ export const selectPoolsDetailsParams = createSelector((q): PoolsDetailsParams = //#region GLV Deposit if (isDeposit && isGlv) { // Raw GLV Deposit Params - if (!marketInfo || !glvTokenAddress || !selectedMarketForGlv || marketOrGlvTokenAmount === undefined) { + if (!glvTokenAddress || !selectedMarketForGlv || marketOrGlvTokenAmount === undefined) { return undefined; } diff --git a/src/domain/multichain/arbitraryRelayParams.ts b/src/domain/multichain/arbitraryRelayParams.ts index d8862d7336..041d9d4f40 100644 --- a/src/domain/multichain/arbitraryRelayParams.ts +++ b/src/domain/multichain/arbitraryRelayParams.ts @@ -1,9 +1,10 @@ import { useMemo } from "react"; -import { encodeAbiParameters, encodePacked, keccak256, maxUint256, PublicClient, toHex } from "viem"; +import { encodeAbiParameters, encodePacked, EstimateGasParameters, keccak256, PublicClient, toHex } from "viem"; import type { ContractsChainId } from "config/chains"; import { getContract } from "config/contracts"; import { GMX_SIMULATION_ORIGIN, multichainBalanceKey } from "config/dataStore"; +import { SIMULATED_MULTICHAIN_BALANCE } from "config/multichain"; import { selectExpressGlobalParams } from "context/SyntheticsStateContext/selectors/expressSelectors"; import { selectAccount, @@ -151,30 +152,29 @@ async function estimateArbitraryGasLimit({ [baseTxnData.callData, getContract(chainId, "GelatoRelayAddress"), baseTxnData.feeToken, baseTxnData.feeAmount] ); + const params: EstimateGasParameters = { + account: GMX_SIMULATION_ORIGIN, + to: baseTxnData.to, + data: baseData, + value: 0n, + stateOverride: [ + { + address: getContract(chainId, "DataStore"), + stateDiff: [ + { + slot: calculateMappingSlot( + multichainBalanceKey(account, gasPaymentParams.gasPaymentTokenAddress), + DATASTORE_SLOT_INDEXES.uintValues + ), + value: toHex(SIMULATED_MULTICHAIN_BALANCE, { size: 32 }), + }, + ], + }, + ], + }; + const gasLimit = await fallbackCustomError( - async () => - client - .estimateGas({ - account: GMX_SIMULATION_ORIGIN, - to: baseTxnData.to, - data: baseData, - value: 0n, - stateOverride: [ - { - address: getContract(chainId, "DataStore"), - stateDiff: [ - { - slot: calculateMappingSlot( - multichainBalanceKey(account, gasPaymentParams.gasPaymentTokenAddress), - DATASTORE_SLOT_INDEXES.uintValues - ), - value: toHex(maxUint256 / 100n, { size: 32 }), - }, - ], - }, - ], - }) - .then(applyGasLimitBuffer), + async () => client.estimateGas(params).then(applyGasLimitBuffer), "gasLimit" ); diff --git a/src/domain/multichain/progress/GmOrGlvBuyProgress.ts b/src/domain/multichain/progress/GmOrGlvBuyProgress.ts index b063caaaf1..b25f1d9640 100644 --- a/src/domain/multichain/progress/GmOrGlvBuyProgress.ts +++ b/src/domain/multichain/progress/GmOrGlvBuyProgress.ts @@ -1,12 +1,11 @@ import { decodeEventLog, encodeEventTopics, getAbiItem } from "viem"; import { ContractsChainId } from "config/chains"; +import { Operation } from "domain/synthetics/markets/types"; import { Token } from "domain/tokens"; import { abis } from "sdk/abis"; import { getContract } from "sdk/configs/contracts"; -import { Operation } from "components/GmSwap/GmSwapBox/types"; - import { fetchLogsInTx } from "./fetchLogsInTx"; import { getOrWaitLogs } from "./getOrWaitLogs"; import { debugLog, isStringEqualInsensitive, matchLogRequest } from "./LongCrossChainTask"; diff --git a/src/domain/multichain/progress/GmOrGlvSellProgress.ts b/src/domain/multichain/progress/GmOrGlvSellProgress.ts index 37ff40c293..615e5c9313 100644 --- a/src/domain/multichain/progress/GmOrGlvSellProgress.ts +++ b/src/domain/multichain/progress/GmOrGlvSellProgress.ts @@ -1,12 +1,11 @@ import { decodeEventLog, encodeEventTopics, getAbiItem } from "viem"; import { ContractsChainId } from "config/chains"; +import { Operation } from "domain/synthetics/markets/types"; import { Token } from "domain/tokens"; import { abis } from "sdk/abis"; import { getContract } from "sdk/configs/contracts"; -import { Operation } from "components/GmSwap/GmSwapBox/types"; - import { fetchLogsInTx } from "./fetchLogsInTx"; import { getOrWaitLogs } from "./getOrWaitLogs"; import { debugLog, isStringEqualInsensitive, matchLogRequest } from "./LongCrossChainTask"; diff --git a/src/domain/multichain/progress/MultichainTransferProgress.ts b/src/domain/multichain/progress/MultichainTransferProgress.ts index 5f092a3fca..a169646da3 100644 --- a/src/domain/multichain/progress/MultichainTransferProgress.ts +++ b/src/domain/multichain/progress/MultichainTransferProgress.ts @@ -1,8 +1,7 @@ /* eslint-disable @typescript-eslint/no-namespace */ +import { Operation } from "domain/synthetics/markets/types"; import { Token } from "domain/tokens"; -import { Operation } from "components/GmSwap/GmSwapBox/types"; - import { LongCrossChainTask } from "./LongCrossChainTask"; type FundsLeftIn = "source" | "lz" | "gmx-lz" | "unknown"; diff --git a/src/domain/multichain/progress/MultichainTransferProgressView.tsx b/src/domain/multichain/progress/MultichainTransferProgressView.tsx index a267daacb2..7941a5b4f4 100644 --- a/src/domain/multichain/progress/MultichainTransferProgressView.tsx +++ b/src/domain/multichain/progress/MultichainTransferProgressView.tsx @@ -8,17 +8,18 @@ import { useCopyToClipboard } from "react-use"; import { getChainName } from "config/chains"; import { getChainIcon } from "config/icons"; import { getTokenAddressByGlv } from "domain/synthetics/markets/glv"; +import { Operation } from "domain/synthetics/markets/types"; import { Token } from "domain/tokens"; import { useChainId } from "lib/chains"; import { CHAIN_ID_TO_TX_URL_BUILDER } from "lib/chains/blockExplorers"; import { shortenAddressOrEns } from "lib/wallets"; import { getRainbowKitConfig } from "lib/wallets/rainbowKitConfig"; import { - getMarketIndexToken, getIsSpotOnlyMarket, - isMarketTokenAddress, + getMarketIndexToken, getTokenAddressByMarket, getTokenSymbolByMarket, + isMarketTokenAddress, } from "sdk/configs/markets"; import { getToken } from "sdk/configs/tokens"; import { getMarketIndexName, getMarketPoolName } from "sdk/utils/markets"; @@ -28,7 +29,6 @@ import Button from "components/Button/Button"; import { ColorfulBanner } from "components/ColorfulBanner/ColorfulBanner"; import { EXPAND_ANIMATION_VARIANTS } from "components/ExpandableRow"; import ExternalLink from "components/ExternalLink/ExternalLink"; -import { Operation } from "components/GmSwap/GmSwapBox/types"; import { SyntheticsInfoRow } from "components/SyntheticsInfoRow"; import TokenIcon from "components/TokenIcon/TokenIcon"; diff --git a/src/domain/multichain/progress/__tests__/tracker.spec.ts b/src/domain/multichain/progress/__tests__/tracker.spec.ts index e06e9bb759..fb0a25d21a 100644 --- a/src/domain/multichain/progress/__tests__/tracker.spec.ts +++ b/src/domain/multichain/progress/__tests__/tracker.spec.ts @@ -1,11 +1,10 @@ import { describe, expect, it } from "vitest"; import { ARBITRUM, ARBITRUM_SEPOLIA, SOURCE_BASE_MAINNET, SOURCE_SEPOLIA } from "config/chains"; +import { Operation } from "domain/synthetics/markets/types"; import { getGlvToken, getGmToken } from "domain/tokens"; import { expandDecimals, numberToBigint } from "lib/numbers"; -import { Operation } from "components/GmSwap/GmSwapBox/types"; - import { GlvBuyTask, GmBuyTask } from "../GmOrGlvBuyProgress"; import { GlvSellTask, GmSellTask } from "../GmOrGlvSellProgress"; import { BridgeInFailed, ConversionFailed } from "../MultichainTransferProgress"; diff --git a/src/domain/multichain/useQuoteSend.ts b/src/domain/multichain/useQuoteSend.ts index 2eafefde2e..a65f965b07 100644 --- a/src/domain/multichain/useQuoteSend.ts +++ b/src/domain/multichain/useQuoteSend.ts @@ -1,4 +1,3 @@ -import { Provider } from "ethers"; import useSWR from "swr"; import type { AnyChainId } from "config/chains"; @@ -9,14 +8,12 @@ import { CONFIG_UPDATE_INTERVAL } from "lib/timeConstants"; export function useQuoteSendNativeFee({ sendParams, fromStargateAddress, - fromChainProvider, fromChainId, toChainId, composeGas, }: { sendParams: SendParam | undefined; fromStargateAddress: string | undefined; - fromChainProvider: Provider | undefined; fromChainId: AnyChainId | undefined; toChainId: AnyChainId | undefined; composeGas?: bigint; @@ -24,7 +21,6 @@ export function useQuoteSendNativeFee({ const quoteSendCondition = sendParams !== undefined && fromStargateAddress !== undefined && - fromChainProvider !== undefined && toChainId !== undefined && fromChainId !== undefined && fromChainId !== toChainId; diff --git a/src/domain/synthetics/express/expressOrderUtils.ts b/src/domain/synthetics/express/expressOrderUtils.ts index 379df7acf1..c7e5b96790 100644 --- a/src/domain/synthetics/express/expressOrderUtils.ts +++ b/src/domain/synthetics/express/expressOrderUtils.ts @@ -2,7 +2,6 @@ import { AbstractSigner, Provider, Signer, Wallet } from "ethers"; import { Address, encodeFunctionData, - maxUint256, PublicClient, recoverTypedDataAddress, size, @@ -15,6 +14,7 @@ import { BOTANIX } from "config/chains"; import { getContract } from "config/contracts"; import { GMX_SIMULATION_ORIGIN, multichainBalanceKey } from "config/dataStore"; import { BASIS_POINTS_DIVISOR_BIGINT } from "config/factors"; +import { SIMULATED_MULTICHAIN_BALANCE } from "config/multichain"; import { isSourceChain } from "config/multichain"; import { calculateMappingSlot, DATASTORE_SLOT_INDEXES } from "domain/multichain/arbitraryRelayParams"; import { fallbackCustomError } from "domain/multichain/fallbackCustomError"; @@ -386,7 +386,7 @@ export async function estimateExpressParams({ multichainBalanceKey(account, gasPaymentToken.address), DATASTORE_SLOT_INDEXES.uintValues ), - value: toHex(maxUint256 / 100n, { size: 32 }), + value: toHex(SIMULATED_MULTICHAIN_BALANCE, { size: 32 }), }, ], }, diff --git a/src/domain/synthetics/express/relayParamsUtils.ts b/src/domain/synthetics/express/relayParamsUtils.ts index 5362aeaf17..fe236f423d 100644 --- a/src/domain/synthetics/express/relayParamsUtils.ts +++ b/src/domain/synthetics/express/relayParamsUtils.ts @@ -205,7 +205,6 @@ export function getRawRelayerParams({ feeParams, externalCalls, tokenPermits, - // marketsInfoData, }: { chainId: ContractsChainId; gasPaymentTokenAddress: string; @@ -213,7 +212,6 @@ export function getRawRelayerParams({ feeParams: RelayFeePayload; externalCalls: ExternalCallsPayload; tokenPermits: SignedTokenPermit[]; - // marketsInfoData: MarketsInfoData; }): RawRelayParamsPayload { const oracleParams = getOracleParamsForRelayParams({ chainId, @@ -221,7 +219,6 @@ export function getRawRelayerParams({ feeSwapPath: feeParams.feeSwapPath, gasPaymentTokenAddress, relayerFeeTokenAddress, - // marketsInfoData, }); const relayParamsPayload: RawRelayParamsPayload = { diff --git a/src/domain/synthetics/markets/buildDepositTransferRequests.ts b/src/domain/synthetics/markets/buildDepositTransferRequests.ts new file mode 100644 index 0000000000..cff0e85e2e --- /dev/null +++ b/src/domain/synthetics/markets/buildDepositTransferRequests.ts @@ -0,0 +1,89 @@ +import type { ContractsChainId } from "config/chains"; +import { getContract } from "config/contracts"; +import { DEFAULT_SLIPPAGE_AMOUNT } from "config/factors"; +import { getTransferRequests } from "domain/multichain/getTransferRequests"; +import type { TransferRequests } from "domain/multichain/types"; +import type { GmPaySource } from "domain/synthetics/markets"; +import { applySlippageToMinOut } from "sdk/utils/trade"; + +import type { TechnicalGmFees } from "./technicalFees/technical-fees-types"; + +export function buildDepositTransferRequests({ + isDeposit, + isGlv, + chainId, + paySource, + isMarketTokenDeposit, + marketTokenAddress, + marketTokenAmount, + longTokenAmount, + shortTokenAmount, + initialLongTokenAddress, + initialShortTokenAddress, + technicalFees, +}: { + isDeposit: boolean; + isGlv: boolean; + chainId: ContractsChainId; + paySource: GmPaySource; + isMarketTokenDeposit: boolean; + marketTokenAddress: string | undefined; + marketTokenAmount: bigint; + longTokenAmount: bigint | undefined; + shortTokenAmount: bigint | undefined; + initialLongTokenAddress: string | undefined; + initialShortTokenAddress: string | undefined; + /** + * Used for source chain deposit to adjust the transfer amount + */ + technicalFees: TechnicalGmFees | undefined; +}): TransferRequests | undefined { + if (!isDeposit) { + return undefined; + } + + const vaultAddress = isGlv ? getContract(chainId, "GlvVault") : getContract(chainId, "DepositVault"); + + if (isMarketTokenDeposit) { + return getTransferRequests([ + { + to: vaultAddress, + token: marketTokenAddress, + amount: marketTokenAmount, + }, + ]); + } + + if (paySource === "sourceChain") { + let tokenAddress = + longTokenAmount !== undefined && longTokenAmount > 0n ? initialLongTokenAddress : initialShortTokenAddress; + + let amount = longTokenAmount !== undefined && longTokenAmount > 0n ? longTokenAmount : shortTokenAmount!; + + const estimatedReceivedAmount = + technicalFees?.kind === "sourceChain" && technicalFees.isDeposit + ? technicalFees.fees.txnEstimatedReceivedAmount + : undefined; + + if (estimatedReceivedAmount !== undefined && estimatedReceivedAmount > amount) { + return undefined; + } + + amount = applySlippageToMinOut(DEFAULT_SLIPPAGE_AMOUNT, estimatedReceivedAmount ?? amount); + + return getTransferRequests([{ to: vaultAddress, token: tokenAddress, amount }]); + } + + return getTransferRequests([ + { + to: vaultAddress, + token: initialLongTokenAddress, + amount: longTokenAmount, + }, + { + to: vaultAddress, + token: initialShortTokenAddress, + amount: shortTokenAmount, + }, + ]); +} diff --git a/src/domain/synthetics/markets/buildWithdrawalTransferRequests.ts b/src/domain/synthetics/markets/buildWithdrawalTransferRequests.ts new file mode 100644 index 0000000000..159807c19a --- /dev/null +++ b/src/domain/synthetics/markets/buildWithdrawalTransferRequests.ts @@ -0,0 +1,37 @@ +import type { ContractsChainId } from "config/chains"; +import { getContract } from "config/contracts"; +import { getTransferRequests } from "domain/multichain/getTransferRequests"; +import type { TransferRequests } from "domain/multichain/types"; +import type { ContractName } from "sdk/configs/contracts"; + +export function buildWithdrawalTransferRequests({ + isWithdrawal, + isGlv, + chainId, + glvOrMarketTokenAddress, + glvOrMarketAmount, +}: { + isWithdrawal: boolean; + isGlv: boolean; + chainId: ContractsChainId; + glvOrMarketTokenAddress: string | undefined; + glvOrMarketAmount: bigint; +}): TransferRequests | undefined { + if (!isWithdrawal) { + return undefined; + } + + if (!glvOrMarketTokenAddress) { + return undefined; + } + + const vaultContract: ContractName = isGlv ? "GlvVault" : "WithdrawalVault"; + + return getTransferRequests([ + { + to: getContract(chainId, vaultContract), + token: glvOrMarketTokenAddress, + amount: glvOrMarketAmount, + }, + ]); +} diff --git a/src/domain/synthetics/markets/createMultichainDepositTxn.ts b/src/domain/synthetics/markets/createMultichainDepositTxn.ts index 3ec2fed01d..6d9160a726 100644 --- a/src/domain/synthetics/markets/createMultichainDepositTxn.ts +++ b/src/domain/synthetics/markets/createMultichainDepositTxn.ts @@ -16,15 +16,22 @@ import { signCreateDeposit } from "./signCreateDeposit"; type TxnParams = { chainId: ContractsChainId; srcChainId: SourceChainId | undefined; - signer: WalletSigner; relayParams: RelayParamsPayload; - emptySignature?: boolean; account: string; transferRequests: TransferRequests; params: CreateDepositParams; relayerFeeTokenAddress: string; relayerFeeAmount: bigint; -}; +} & ( + | { + signer?: undefined; + emptySignature: true; + } + | { + signer: WalletSigner; + emptySignature?: false; + } +); export async function buildAndSignMultichainDepositTxn({ chainId, @@ -43,6 +50,9 @@ export async function buildAndSignMultichainDepositTxn({ if (emptySignature) { signature = "0x"; } else { + if (!signer) { + throw new Error("Signer is required when emptySignature is false"); + } signature = await signCreateDeposit({ chainId, srcChainId, diff --git a/src/domain/synthetics/markets/createMultichainGlvDepositTxn.ts b/src/domain/synthetics/markets/createMultichainGlvDepositTxn.ts index 341701851f..f1f203ba4b 100644 --- a/src/domain/synthetics/markets/createMultichainGlvDepositTxn.ts +++ b/src/domain/synthetics/markets/createMultichainGlvDepositTxn.ts @@ -16,15 +16,22 @@ import { signCreateGlvDeposit } from "./signCreateGlvDeposit"; export type CreateMultichainGlvDepositParams = { chainId: ContractsChainId; srcChainId: SourceChainId | undefined; - signer: WalletSigner; relayParams: RelayParamsPayload; - emptySignature?: boolean; account: string; transferRequests: TransferRequests; params: CreateGlvDepositParams; relayerFeeTokenAddress: string; relayerFeeAmount: bigint; -}; +} & ( + | { + signer?: undefined; + emptySignature: true; + } + | { + signer: WalletSigner; + emptySignature?: false; + } +); export async function buildAndSignMultichainGlvDepositTxn({ chainId, @@ -43,6 +50,9 @@ export async function buildAndSignMultichainGlvDepositTxn({ if (emptySignature) { signature = "0x"; } else { + if (!signer) { + throw new Error("Signer is required when emptySignature is false"); + } signature = await signCreateGlvDeposit({ chainId, srcChainId, diff --git a/src/domain/synthetics/markets/createMultichainGlvWithdrawalTxn.ts b/src/domain/synthetics/markets/createMultichainGlvWithdrawalTxn.ts index 9fc9c23d83..092aa459ed 100644 --- a/src/domain/synthetics/markets/createMultichainGlvWithdrawalTxn.ts +++ b/src/domain/synthetics/markets/createMultichainGlvWithdrawalTxn.ts @@ -16,15 +16,22 @@ import type { CreateGlvWithdrawalParams } from "./types"; type TxnParams = { chainId: ContractsChainId; srcChainId: SourceChainId | undefined; - signer: WalletSigner; relayParams: RelayParamsPayload; - emptySignature?: boolean; account: string; transferRequests: TransferRequests; params: CreateGlvWithdrawalParams; relayerFeeTokenAddress: string; relayerFeeAmount: bigint; -}; +} & ( + | { + signer?: undefined; + emptySignature: true; + } + | { + signer: WalletSigner; + emptySignature?: false; + } +); export async function buildAndSignMultichainGlvWithdrawalTxn({ chainId, @@ -43,6 +50,9 @@ export async function buildAndSignMultichainGlvWithdrawalTxn({ if (emptySignature) { signature = "0x"; } else { + if (!signer) { + throw new Error("Signer is required when emptySignature is false"); + } signature = await signCreateGlvWithdrawal({ chainId, srcChainId, diff --git a/src/domain/synthetics/markets/createMultichainWithdrawalTxn.ts b/src/domain/synthetics/markets/createMultichainWithdrawalTxn.ts index 134b0cd6dc..9217c89022 100644 --- a/src/domain/synthetics/markets/createMultichainWithdrawalTxn.ts +++ b/src/domain/synthetics/markets/createMultichainWithdrawalTxn.ts @@ -16,15 +16,22 @@ import { signCreateWithdrawal } from "./signCreateWithdrawal"; type TxnParams = { chainId: ContractsChainId; srcChainId: SourceChainId | undefined; - signer: WalletSigner; relayParams: RelayParamsPayload; - emptySignature?: boolean; account: string; transferRequests: TransferRequests; params: CreateWithdrawalParams; relayerFeeTokenAddress: string; relayerFeeAmount: bigint; -}; +} & ( + | { + signer?: undefined; + emptySignature: true; + } + | { + signer: WalletSigner; + emptySignature?: false; + } +); export async function buildAndSignMultichainWithdrawalTxn({ chainId, @@ -43,6 +50,9 @@ export async function buildAndSignMultichainWithdrawalTxn({ if (emptySignature) { signature = "0x"; } else { + if (!signer) { + throw new Error("Signer is required when emptySignature is false"); + } signature = await signCreateWithdrawal({ chainId, srcChainId, diff --git a/src/domain/synthetics/markets/feeEstimation/estimateGlvWithdrawalPlatformTokenTransferInFees.ts b/src/domain/synthetics/markets/feeEstimation/estimateGlvWithdrawalPlatformTokenTransferInFees.ts index a55f309a37..ae45c97201 100644 --- a/src/domain/synthetics/markets/feeEstimation/estimateGlvWithdrawalPlatformTokenTransferInFees.ts +++ b/src/domain/synthetics/markets/feeEstimation/estimateGlvWithdrawalPlatformTokenTransferInFees.ts @@ -13,7 +13,7 @@ import { getWrappedToken } from "sdk/configs/tokens"; import { getEmptyExternalCallsPayload } from "sdk/utils/orderTransactions"; import { nowInSeconds } from "sdk/utils/time"; -import { signCreateWithdrawal } from "../signCreateWithdrawal"; +import { signCreateGlvWithdrawal } from "../signCreateGlvWithdrawal"; import { CreateGlvWithdrawalParams } from "../types"; import { stargateTransferFees } from "./stargateTransferFees"; @@ -72,7 +72,7 @@ export async function estimateGlvWithdrawalPlatformTokenTransferInFees({ throw new Error("Transfer requests not found"); } - const signature = await signCreateWithdrawal({ + const signature = await signCreateGlvWithdrawal({ chainId, srcChainId, signer: RANDOM_WALLET, diff --git a/src/domain/synthetics/markets/feeEstimation/estimatePureLpActionExecutionFee.ts b/src/domain/synthetics/markets/feeEstimation/estimatePureLpActionExecutionFee.ts index 895d61e5cd..b1bae47257 100644 --- a/src/domain/synthetics/markets/feeEstimation/estimatePureLpActionExecutionFee.ts +++ b/src/domain/synthetics/markets/feeEstimation/estimatePureLpActionExecutionFee.ts @@ -13,7 +13,7 @@ import { getExecutionFee, } from "sdk/utils/fees"; -import { Operation } from "components/GmSwap/GmSwapBox/types"; +import { Operation } from "../types"; export type PureAction = | { operation: Operation.Deposit; isGlv: false; swapsCount: bigint } diff --git a/src/domain/synthetics/markets/feeEstimation/estimateSourceChainDepositFees.ts b/src/domain/synthetics/markets/feeEstimation/estimateSourceChainDepositFees.ts index 0dd2ab1d06..fd28a8fa60 100644 --- a/src/domain/synthetics/markets/feeEstimation/estimateSourceChainDepositFees.ts +++ b/src/domain/synthetics/markets/feeEstimation/estimateSourceChainDepositFees.ts @@ -18,11 +18,9 @@ import { convertTokenAddress, getToken, getWrappedToken } from "sdk/configs/toke import { getEmptyExternalCallsPayload } from "sdk/utils/orderTransactions"; import { nowInSeconds } from "sdk/utils/time"; -import { Operation } from "components/GmSwap/GmSwapBox/types"; - import { convertToUsd, getMidPrice } from "../../tokens"; import { signCreateDeposit } from "../signCreateDeposit"; -import { CreateDepositParams, RawCreateDepositParams } from "../types"; +import { CreateDepositParams, Operation, RawCreateDepositParams } from "../types"; import { estimateDepositPlatformTokenTransferOutFees } from "./estimateDepositPlatformTokenTransferOutFees"; import { estimatePureLpActionExecutionFee } from "./estimatePureLpActionExecutionFee"; import { stargateTransferFees } from "./stargateTransferFees"; diff --git a/src/domain/synthetics/markets/feeEstimation/estimateSourceChainGlvDepositFees.ts b/src/domain/synthetics/markets/feeEstimation/estimateSourceChainGlvDepositFees.ts index b767de887d..e337daac26 100644 --- a/src/domain/synthetics/markets/feeEstimation/estimateSourceChainGlvDepositFees.ts +++ b/src/domain/synthetics/markets/feeEstimation/estimateSourceChainGlvDepositFees.ts @@ -18,11 +18,9 @@ import { convertTokenAddress, getToken, getWrappedToken } from "sdk/configs/toke import { getEmptyExternalCallsPayload } from "sdk/utils/orderTransactions"; import { nowInSeconds } from "sdk/utils/time"; -import { Operation } from "components/GmSwap/GmSwapBox/types"; - import { convertToUsd, getMidPrice } from "../../tokens"; import { signCreateGlvDeposit } from "../signCreateGlvDeposit"; -import { CreateGlvDepositParams, RawCreateGlvDepositParams } from "../types"; +import { CreateGlvDepositParams, Operation, RawCreateGlvDepositParams } from "../types"; import { estimateDepositPlatformTokenTransferOutFees } from "./estimateDepositPlatformTokenTransferOutFees"; import { estimatePureLpActionExecutionFee } from "./estimatePureLpActionExecutionFee"; import { stargateTransferFees } from "./stargateTransferFees"; diff --git a/src/domain/synthetics/markets/feeEstimation/estimateSourceChainGlvWithdrawalFees.ts b/src/domain/synthetics/markets/feeEstimation/estimateSourceChainGlvWithdrawalFees.ts index 52968cf560..7b9dac83cf 100644 --- a/src/domain/synthetics/markets/feeEstimation/estimateSourceChainGlvWithdrawalFees.ts +++ b/src/domain/synthetics/markets/feeEstimation/estimateSourceChainGlvWithdrawalFees.ts @@ -2,13 +2,11 @@ import { SettlementChainId, SourceChainId } from "config/chains"; import { GlobalExpressParams, RelayParamsPayload } from "domain/synthetics/express"; import { getToken } from "sdk/configs/tokens"; -import { Operation } from "components/GmSwap/GmSwapBox/types"; - +import { convertToUsd, getMidPrice } from "../../tokens"; +import { CreateGlvWithdrawalParams, Operation, RawCreateGlvWithdrawalParams } from "../types"; import { estimateGlvWithdrawalPlatformTokenTransferInFees } from "./estimateGlvWithdrawalPlatformTokenTransferInFees"; import { estimatePureLpActionExecutionFee } from "./estimatePureLpActionExecutionFee"; import { estimateSourceChainWithdrawalReturnTokenTransferFees } from "./estimateSourceChainWithdrawalFees"; -import { convertToUsd, getMidPrice } from "../../tokens"; -import { CreateGlvWithdrawalParams, RawCreateGlvWithdrawalParams } from "../types"; export type SourceChainGlvWithdrawalFees = { /** diff --git a/src/domain/synthetics/markets/feeEstimation/estimateSourceChainWithdrawalFees.ts b/src/domain/synthetics/markets/feeEstimation/estimateSourceChainWithdrawalFees.ts index c9db118d09..788a1ca2fe 100644 --- a/src/domain/synthetics/markets/feeEstimation/estimateSourceChainWithdrawalFees.ts +++ b/src/domain/synthetics/markets/feeEstimation/estimateSourceChainWithdrawalFees.ts @@ -8,10 +8,8 @@ import { GlobalExpressParams, RelayParamsPayload } from "domain/synthetics/expre import { expandDecimals } from "lib/numbers"; import { convertTokenAddress, getToken } from "sdk/configs/tokens"; -import { Operation } from "components/GmSwap/GmSwapBox/types"; - import { convertToUsd, getMidPrice } from "../../tokens"; -import { CreateWithdrawalParams, RawCreateWithdrawalParams } from "../types"; +import { CreateWithdrawalParams, Operation, RawCreateWithdrawalParams } from "../types"; import { estimatePureLpActionExecutionFee } from "./estimatePureLpActionExecutionFee"; import { estimateWithdrawalPlatformTokenTransferInFees } from "./estimateWithdrawalPlatformTokenTransferInFees"; import { stargateTransferFees } from "./stargateTransferFees"; diff --git a/src/domain/synthetics/markets/signCreateGlvWithdrawal.ts b/src/domain/synthetics/markets/signCreateGlvWithdrawal.ts index 5fbed3cbe1..72f5108c96 100644 --- a/src/domain/synthetics/markets/signCreateGlvWithdrawal.ts +++ b/src/domain/synthetics/markets/signCreateGlvWithdrawal.ts @@ -1,13 +1,13 @@ -import type { Wallet } from "ethers"; +import type { AbstractSigner, Wallet } from "ethers"; import type { ContractsChainId, SourceChainId } from "config/chains"; import { getContract } from "config/contracts"; -import { TransferRequests } from "domain/multichain/types"; -import { ISigner } from "lib/transactions/iSigner"; +import type { TransferRequests } from "domain/multichain/types"; +import type { ISigner } from "lib/transactions/iSigner"; import type { WalletSigner } from "lib/wallets"; import { signTypedData } from "lib/wallets/signing"; -import type { CreateWithdrawalParams } from "."; +import type { CreateGlvWithdrawalParams } from "."; import { getGelatoRelayRouterDomain, hashRelayParams } from "../express/relayParamsUtils"; import type { RelayParamsPayload } from "../express/types"; @@ -18,13 +18,15 @@ export async function signCreateGlvWithdrawal({ relayParams, transferRequests, params, + shouldUseSignerMethod, }: { - signer: WalletSigner | Wallet | ISigner; + signer: WalletSigner | Wallet | ISigner | AbstractSigner; relayParams: RelayParamsPayload; transferRequests: TransferRequests; - params: CreateWithdrawalParams; + params: CreateGlvWithdrawalParams; chainId: ContractsChainId; srcChainId: SourceChainId | undefined; + shouldUseSignerMethod?: boolean; }) { const types = { CreateGlvWithdrawal: [ @@ -66,5 +68,5 @@ export async function signCreateGlvWithdrawal({ relayParams: hashRelayParams(relayParams), }; - return signTypedData({ signer, domain, types, typedData }); + return signTypedData({ signer, domain, types, typedData, shouldUseSignerMethod }); } diff --git a/src/domain/synthetics/markets/technicalFees/calculateGmxAccountTechnicalFees.ts b/src/domain/synthetics/markets/technicalFees/calculateGmxAccountTechnicalFees.ts new file mode 100644 index 0000000000..a075c5763d --- /dev/null +++ b/src/domain/synthetics/markets/technicalFees/calculateGmxAccountTechnicalFees.ts @@ -0,0 +1,286 @@ +import { estimateArbitraryRelayFee, getRawBaseRelayerParams } from "domain/multichain/arbitraryRelayParams"; +import { ExpressTransactionBuilder } from "domain/synthetics/express"; +import { + RawCreateDepositParams, + RawCreateGlvDepositParams, + RawCreateGlvWithdrawalParams, + RawCreateWithdrawalParams, +} from "domain/synthetics/markets"; +import { buildAndSignMultichainDepositTxn } from "domain/synthetics/markets/createMultichainDepositTxn"; +import { buildAndSignMultichainGlvDepositTxn } from "domain/synthetics/markets/createMultichainGlvDepositTxn"; +import { buildAndSignMultichainGlvWithdrawalTxn } from "domain/synthetics/markets/createMultichainGlvWithdrawalTxn"; +import { buildAndSignMultichainWithdrawalTxn } from "domain/synthetics/markets/createMultichainWithdrawalTxn"; +import { estimatePureLpActionExecutionFee } from "domain/synthetics/markets/feeEstimation/estimatePureLpActionExecutionFee"; +import { convertToUsd } from "domain/tokens"; +import { getPublicClientWithRpc } from "lib/wallets/rainbowKitConfig"; +import { DEFAULT_EXPRESS_ORDER_DEADLINE_DURATION } from "sdk/configs/express"; +import { nowInSeconds } from "sdk/utils/time"; + +import { buildDepositTransferRequests } from "../buildDepositTransferRequests"; +import { buildWithdrawalTransferRequests } from "../buildWithdrawalTransferRequests"; +import { CalculateTechnicalFeesParams, TechnicalGmFees } from "./technical-fees-types"; +import { Operation } from "../types"; + +type GmxAccountFeesParams = CalculateTechnicalFeesParams & + Required> & { + globalExpressParams: NonNullable; + }; + +async function calculateGmxAccountDepositTechnicalFees( + params: GmxAccountFeesParams +): Promise { + const castedParams = params.rawParams as RawCreateGlvDepositParams | RawCreateDepositParams; + + const swapsCount = BigInt( + castedParams.addresses.longTokenSwapPath.length + castedParams.addresses.shortTokenSwapPath.length + ); + + const executionFee = estimatePureLpActionExecutionFee({ + action: params.isGlv + ? { + operation: Operation.Deposit, + isGlv: true, + marketsCount: BigInt(params.glvInfo!.markets.length), + swapsCount, + isMarketTokenDeposit: (params.rawParams as RawCreateGlvDepositParams).isMarketTokenDeposit, + } + : { + operation: Operation.Deposit, + isGlv: false, + swapsCount, + }, + chainId: params.chainId, + gasLimits: params.gasLimits, + tokensData: params.tokensData, + gasPrice: params.gasPrice, + }); + + const transferRequests = buildDepositTransferRequests({ + isDeposit: true, + isGlv: params.isGlv, + chainId: params.chainId, + paySource: params.paySource, + isMarketTokenDeposit: params.isGlv ? (params.rawParams as RawCreateGlvDepositParams).isMarketTokenDeposit : false, + marketTokenAddress: params.isGlv ? params.rawParams.addresses.market : undefined, + marketTokenAmount: params.isGlv ? params.firstTokenAmount : 0n, + longTokenAmount: params.longTokenAmount, + shortTokenAmount: params.shortTokenAmount, + initialLongTokenAddress: castedParams.addresses.initialLongToken, + initialShortTokenAddress: castedParams.addresses.initialShortToken, + technicalFees: undefined, + }); + + if (!transferRequests) { + return undefined; + } + + const expressTransactionBuilder: ExpressTransactionBuilder = async ({ relayParams, gasPaymentParams }) => { + if (params.isGlv) { + const glvParams = params.rawParams as RawCreateGlvDepositParams; + return { + txnData: await buildAndSignMultichainGlvDepositTxn({ + chainId: params.chainId, + srcChainId: params.srcChainId, + emptySignature: true, + signer: undefined, + account: params.rawParams.addresses.receiver, + params: { ...glvParams, executionFee: executionFee.feeTokenAmount }, + relayerFeeAmount: gasPaymentParams.relayerFeeAmount, + relayerFeeTokenAddress: gasPaymentParams.relayerFeeTokenAddress, + relayParams: { + ...relayParams, + deadline: BigInt(nowInSeconds() + DEFAULT_EXPRESS_ORDER_DEADLINE_DURATION), + }, + transferRequests, + }), + }; + } else { + const depositParams = params.rawParams as RawCreateDepositParams; + return { + txnData: await buildAndSignMultichainDepositTxn({ + chainId: params.chainId, + srcChainId: params.srcChainId, + emptySignature: true, + signer: undefined, + account: params.rawParams.addresses.receiver, + params: { ...depositParams, executionFee: executionFee.feeTokenAmount }, + relayerFeeAmount: gasPaymentParams.relayerFeeAmount, + relayerFeeTokenAddress: gasPaymentParams.relayerFeeTokenAddress, + relayParams: { + ...relayParams, + deadline: BigInt(nowInSeconds() + DEFAULT_EXPRESS_ORDER_DEADLINE_DURATION), + }, + transferRequests, + }), + }; + } + }; + + const relayFee = await estimateArbitraryRelayFee({ + account: params.rawParams.addresses.receiver, + chainId: params.chainId, + client: getPublicClientWithRpc(params.chainId), + rawRelayParamsPayload: params.rawBaseRelayParamsPayload, + expressTransactionBuilder: expressTransactionBuilder, + gasPaymentParams: params.baseRelayFeeSwapParams.gasPaymentParams, + subaccount: undefined, + }); + + const relayFeeUsd = convertToUsd( + relayFee, + params.globalExpressParams.relayerFeeToken.decimals, + params.globalExpressParams.relayerFeeToken.prices.maxPrice + )!; + + return { + kind: "gmxAccount", + fees: { + executionFee, + relayFeeUsd, + }, + isDeposit: true, + isGlv: params.isGlv, + }; +} + +async function calculateGmxAccountWithdrawalTechnicalFees( + params: GmxAccountFeesParams +): Promise { + const castedParams = params.rawParams as RawCreateGlvWithdrawalParams | RawCreateWithdrawalParams; + + const swapsCount = BigInt( + castedParams.addresses.longTokenSwapPath.length + castedParams.addresses.shortTokenSwapPath.length + ); + + const fees = estimatePureLpActionExecutionFee({ + action: params.isGlv + ? { + operation: Operation.Withdrawal, + isGlv: true, + marketsCount: BigInt(params.glvInfo!.markets.length), + swapsCount, + } + : { + operation: Operation.Withdrawal, + isGlv: false, + swapsCount, + }, + chainId: params.chainId, + gasLimits: params.gasLimits, + tokensData: params.tokensData, + gasPrice: params.gasPrice, + }); + + const transferRequests = buildWithdrawalTransferRequests({ + isWithdrawal: true, + isGlv: params.isGlv, + chainId: params.chainId, + glvOrMarketTokenAddress: params.isGlv + ? (params.rawParams as RawCreateGlvWithdrawalParams).addresses.glv + : (params.rawParams as RawCreateWithdrawalParams).addresses.market, + glvOrMarketAmount: params.marketTokenAmount, + }); + + if (!transferRequests) { + return undefined; + } + + const expressTransactionBuilder: ExpressTransactionBuilder = async ({ relayParams, gasPaymentParams }) => { + if (params.isGlv) { + const glvParams = params.rawParams as RawCreateGlvWithdrawalParams; + return { + txnData: await buildAndSignMultichainGlvWithdrawalTxn({ + chainId: params.chainId, + srcChainId: params.srcChainId, + emptySignature: true, + signer: undefined, + account: params.rawParams.addresses.receiver, + params: { ...glvParams, executionFee: fees.feeTokenAmount }, + relayerFeeAmount: gasPaymentParams.relayerFeeAmount, + relayerFeeTokenAddress: gasPaymentParams.relayerFeeTokenAddress, + relayParams: { + ...relayParams, + deadline: BigInt(nowInSeconds() + DEFAULT_EXPRESS_ORDER_DEADLINE_DURATION), + }, + transferRequests, + }), + }; + } else { + const withdrawalParams = params.rawParams as RawCreateWithdrawalParams; + return { + txnData: await buildAndSignMultichainWithdrawalTxn({ + chainId: params.chainId, + srcChainId: params.srcChainId, + emptySignature: true, + signer: undefined, + account: params.rawParams.addresses.receiver, + params: { ...withdrawalParams, executionFee: fees.feeTokenAmount }, + relayerFeeAmount: gasPaymentParams.relayerFeeAmount, + relayerFeeTokenAddress: gasPaymentParams.relayerFeeTokenAddress, + relayParams: { + ...relayParams, + deadline: BigInt(nowInSeconds() + DEFAULT_EXPRESS_ORDER_DEADLINE_DURATION), + }, + transferRequests, + }), + }; + } + }; + + const relayFee = await estimateArbitraryRelayFee({ + account: params.rawParams.addresses.receiver, + chainId: params.chainId, + client: getPublicClientWithRpc(params.chainId), + rawRelayParamsPayload: params.rawBaseRelayParamsPayload, + expressTransactionBuilder: expressTransactionBuilder, + gasPaymentParams: params.baseRelayFeeSwapParams.gasPaymentParams, + subaccount: undefined, + }); + + const relayFeeUsd = convertToUsd( + relayFee, + params.globalExpressParams.relayerFeeToken.decimals, + params.globalExpressParams.relayerFeeToken.prices.maxPrice + )!; + + return { + kind: "gmxAccount", + fees: { + executionFee: fees, + relayFeeUsd, + }, + isDeposit: false, + isGlv: params.isGlv, + }; +} + +export async function calculateGmxAccountTechnicalFees( + params: CalculateTechnicalFeesParams +): Promise { + if (!params.globalExpressParams) { + return undefined; + } + + const { rawBaseRelayParamsPayload, baseRelayFeeSwapParams } = getRawBaseRelayerParams({ + chainId: params.chainId, + account: params.rawParams.addresses.receiver, + globalExpressParams: params.globalExpressParams, + }); + + if (!rawBaseRelayParamsPayload || !baseRelayFeeSwapParams) { + return undefined; + } + + const feesParams: GmxAccountFeesParams = { + ...params, + rawBaseRelayParamsPayload, + baseRelayFeeSwapParams, + globalExpressParams: params.globalExpressParams, + }; + + if (params.operation === Operation.Deposit) { + return calculateGmxAccountDepositTechnicalFees(feesParams); + } else if (params.operation === Operation.Withdrawal) { + return calculateGmxAccountWithdrawalTechnicalFees(feesParams); + } +} diff --git a/src/domain/synthetics/markets/technicalFees/calculateSettlementChainTechnicalFees.ts b/src/domain/synthetics/markets/technicalFees/calculateSettlementChainTechnicalFees.ts new file mode 100644 index 0000000000..cc687a6ac3 --- /dev/null +++ b/src/domain/synthetics/markets/technicalFees/calculateSettlementChainTechnicalFees.ts @@ -0,0 +1,81 @@ +import { + RawCreateGlvDepositParams, + RawCreateDepositParams, + RawCreateGlvWithdrawalParams, + RawCreateWithdrawalParams, +} from "domain/synthetics/markets"; +import { estimatePureLpActionExecutionFee } from "domain/synthetics/markets/feeEstimation/estimatePureLpActionExecutionFee"; + +import { CalculateTechnicalFeesParams, TechnicalGmFees } from "./technical-fees-types"; +import { Operation } from "../types"; + +export async function calculateSettlementChainTechnicalFees( + params: CalculateTechnicalFeesParams +): Promise { + if (params.operation === Operation.Deposit) { + const castedParams = params.rawParams as RawCreateGlvDepositParams | RawCreateDepositParams; + + const swapsCount = BigInt( + castedParams.addresses.longTokenSwapPath.length + castedParams.addresses.shortTokenSwapPath.length + ); + + const fees = estimatePureLpActionExecutionFee({ + action: params.isGlv + ? { + operation: Operation.Deposit, + isGlv: true, + marketsCount: BigInt(params.glvInfo!.markets.length), + swapsCount, + isMarketTokenDeposit: (params.rawParams as RawCreateGlvDepositParams).isMarketTokenDeposit, + } + : { + operation: Operation.Deposit, + isGlv: false, + swapsCount, + }, + chainId: params.chainId, + gasLimits: params.gasLimits, + tokensData: params.tokensData, + gasPrice: params.gasPrice, + }); + + return { + kind: "settlementChain", + fees, + isDeposit: true, + isGlv: params.isGlv, + }; + } else if (params.operation === Operation.Withdrawal) { + const castedParams = params.rawParams as RawCreateGlvWithdrawalParams | RawCreateWithdrawalParams; + + const swapsCount = BigInt( + castedParams.addresses.longTokenSwapPath.length + castedParams.addresses.shortTokenSwapPath.length + ); + + const fees = estimatePureLpActionExecutionFee({ + action: params.isGlv + ? { + operation: Operation.Withdrawal, + isGlv: true, + marketsCount: BigInt(params.glvInfo!.markets.length), + swapsCount, + } + : { + operation: Operation.Withdrawal, + isGlv: false, + swapsCount, + }, + chainId: params.chainId, + gasLimits: params.gasLimits, + tokensData: params.tokensData, + gasPrice: params.gasPrice, + }); + + return { + kind: "settlementChain", + fees, + isDeposit: false, + isGlv: params.isGlv, + }; + } +} diff --git a/src/domain/synthetics/markets/technicalFees/calculateSourceChainTechnicalFees.ts b/src/domain/synthetics/markets/technicalFees/calculateSourceChainTechnicalFees.ts new file mode 100644 index 0000000000..1355b258fb --- /dev/null +++ b/src/domain/synthetics/markets/technicalFees/calculateSourceChainTechnicalFees.ts @@ -0,0 +1,121 @@ +import { SettlementChainId, SourceChainId } from "config/chains"; +import { + RawCreateGlvDepositParams, + RawCreateDepositParams, + RawCreateGlvWithdrawalParams, + RawCreateWithdrawalParams, +} from "domain/synthetics/markets"; +import { estimateSourceChainDepositFees } from "domain/synthetics/markets/feeEstimation/estimateSourceChainDepositFees"; +import { estimateSourceChainGlvDepositFees } from "domain/synthetics/markets/feeEstimation/estimateSourceChainGlvDepositFees"; +import { estimateSourceChainGlvWithdrawalFees } from "domain/synthetics/markets/feeEstimation/estimateSourceChainGlvWithdrawalFees"; +import { estimateSourceChainWithdrawalFees } from "domain/synthetics/markets/feeEstimation/estimateSourceChainWithdrawalFees"; +import { MARKETS } from "sdk/configs/markets"; +import { WithdrawalAmounts } from "sdk/types/trade"; + +import { CalculateTechnicalFeesParams, TechnicalGmFees } from "./technical-fees-types"; +import { Operation } from "../types"; + +export async function calculateSourceChainTechnicalFees( + params: CalculateTechnicalFeesParams +): Promise { + if (params.firstTokenAddress === undefined || params.firstTokenAmount === undefined || !params.globalExpressParams) { + return undefined; + } + if (params.operation === Operation.Deposit) { + if (params.isGlv) { + const castedParams = params.rawParams as RawCreateGlvDepositParams; + const fees = await estimateSourceChainGlvDepositFees({ + chainId: params.chainId as SettlementChainId, + srcChainId: params.srcChainId as SourceChainId, + params: castedParams, + tokenAddress: params.firstTokenAddress, + tokenAmount: params.firstTokenAmount, + globalExpressParams: params.globalExpressParams, + glvMarketCount: BigInt(params.glvInfo!.markets.length), + }); + + return { + kind: "sourceChain", + isGlv: true, + isDeposit: true, + fees, + }; + } else { + const castedParams = params.rawParams as RawCreateDepositParams; + const fees = await estimateSourceChainDepositFees({ + chainId: params.chainId as SettlementChainId, + srcChainId: params.srcChainId as SourceChainId, + params: castedParams, + tokenAddress: params.firstTokenAddress, + tokenAmount: params.firstTokenAmount, + globalExpressParams: params.globalExpressParams, + }); + return { + kind: "sourceChain", + isGlv: false, + isDeposit: true, + fees, + }; + } + } else if (params.operation === Operation.Withdrawal) { + if (params.isGlv) { + const castedParams = params.rawParams as RawCreateGlvWithdrawalParams; + const glvWithdrawalAmounts = params.amounts as WithdrawalAmounts; + const outputLongTokenAddress = + glvWithdrawalAmounts.longTokenSwapPathStats?.tokenOutAddress ?? params.glvInfo!.longTokenAddress; + const outputShortTokenAddress = + glvWithdrawalAmounts.shortTokenSwapPathStats?.tokenOutAddress ?? params.glvInfo!.shortTokenAddress; + + const fees = await estimateSourceChainGlvWithdrawalFees({ + chainId: params.chainId as SettlementChainId, + srcChainId: params.srcChainId as SourceChainId, + params: castedParams, + tokenAddress: castedParams.addresses.glv, + tokenAmount: params.marketTokenAmount, + globalExpressParams: params.globalExpressParams, + marketsCount: BigInt(params.glvInfo!.markets.length), + outputLongTokenAddress, + outputShortTokenAddress, + }); + + return { + kind: "sourceChain", + isGlv: true, + isDeposit: false, + fees, + }; + } else { + const castedParams = params.rawParams as RawCreateWithdrawalParams; + if (!params.amounts) { + return undefined; + } + + const gmWithdrawalAmounts = params.amounts as WithdrawalAmounts; + + const outputLongTokenAddress = + gmWithdrawalAmounts.longTokenSwapPathStats?.tokenOutAddress ?? + MARKETS[params.chainId][params.rawParams.addresses.market].longTokenAddress; + const outputShortTokenAddress = + gmWithdrawalAmounts.shortTokenSwapPathStats?.tokenOutAddress ?? + MARKETS[params.chainId][params.rawParams.addresses.market].shortTokenAddress; + + const fees = await estimateSourceChainWithdrawalFees({ + chainId: params.chainId as SettlementChainId, + srcChainId: params.srcChainId as SourceChainId, + params: castedParams, + tokenAddress: params.rawParams.addresses.market, + tokenAmount: params.marketTokenAmount, + globalExpressParams: params.globalExpressParams, + outputLongTokenAddress, + outputShortTokenAddress, + }); + + return { + kind: "sourceChain", + isGlv: false, + isDeposit: false, + fees, + }; + } + } +} diff --git a/src/domain/synthetics/markets/technicalFees/calculateTechnicalFees.ts b/src/domain/synthetics/markets/technicalFees/calculateTechnicalFees.ts new file mode 100644 index 0000000000..3a1c229ef4 --- /dev/null +++ b/src/domain/synthetics/markets/technicalFees/calculateTechnicalFees.ts @@ -0,0 +1,16 @@ +import { calculateGmxAccountTechnicalFees } from "./calculateGmxAccountTechnicalFees"; +import { calculateSettlementChainTechnicalFees } from "./calculateSettlementChainTechnicalFees"; +import { calculateSourceChainTechnicalFees } from "./calculateSourceChainTechnicalFees"; +import { CalculateTechnicalFeesParams, TechnicalGmFees } from "./technical-fees-types"; + +export async function calculateTechnicalFees( + params: CalculateTechnicalFeesParams +): Promise { + if (params.paySource === "settlementChain") { + return calculateSettlementChainTechnicalFees(params); + } else if (params.paySource === "gmxAccount") { + return calculateGmxAccountTechnicalFees(params); + } else if (params.paySource === "sourceChain") { + return calculateSourceChainTechnicalFees(params); + } +} diff --git a/src/domain/synthetics/markets/technicalFees/technical-fees-types.ts b/src/domain/synthetics/markets/technicalFees/technical-fees-types.ts new file mode 100644 index 0000000000..606ded5b53 --- /dev/null +++ b/src/domain/synthetics/markets/technicalFees/technical-fees-types.ts @@ -0,0 +1,86 @@ +import { type ContractsChainId, SourceChainId } from "config/chains"; +import type { GlobalExpressParams } from "domain/synthetics/express"; +import { + RawCreateDepositParams, + RawCreateGlvDepositParams, + RawCreateWithdrawalParams, + RawCreateGlvWithdrawalParams, + type GlvInfo, +} from "domain/synthetics/markets"; +import type { SourceChainDepositFees } from "domain/synthetics/markets/feeEstimation/estimateSourceChainDepositFees"; +import type { SourceChainGlvDepositFees } from "domain/synthetics/markets/feeEstimation/estimateSourceChainGlvDepositFees"; +import type { SourceChainGlvWithdrawalFees } from "domain/synthetics/markets/feeEstimation/estimateSourceChainGlvWithdrawalFees"; +import type { SourceChainWithdrawalFees } from "domain/synthetics/markets/feeEstimation/estimateSourceChainWithdrawalFees"; +import type { NativeTokenSupportedAddress, ERC20Address, TokensData } from "domain/tokens"; +import type { ExecutionFee, GasLimitsConfig } from "sdk/types/fees"; +import { type DepositAmounts, WithdrawalAmounts } from "sdk/types/trade"; + +import { Operation } from "../types"; + +type SameChainGmFees = { + kind: "settlementChain"; + fees: ExecutionFee; + isDeposit: boolean; + isGlv: boolean; +}; + +type GmxAccountGmFees = { + kind: "gmxAccount"; + fees: { + executionFee: ExecutionFee; + relayFeeUsd: bigint; + }; + isDeposit: boolean; + isGlv: boolean; +}; + +type SourceChainGmFees = { + kind: "sourceChain"; +} & ( + | { + isGlv: false; + isDeposit: false; + fees: SourceChainWithdrawalFees; + } + | { + isGlv: false; + isDeposit: true; + fees: SourceChainDepositFees; + } + | { + isGlv: true; + isDeposit: false; + fees: SourceChainGlvWithdrawalFees; + } + | { + isGlv: true; + isDeposit: true; + fees: SourceChainGlvDepositFees; + } +); + +export type TechnicalGmFees = SameChainGmFees | GmxAccountGmFees | SourceChainGmFees; + +export type CalculateTechnicalFeesParams = { + chainId: ContractsChainId; + globalExpressParams: GlobalExpressParams | undefined; + rawParams: + | RawCreateDepositParams + | RawCreateGlvDepositParams + | RawCreateWithdrawalParams + | RawCreateGlvWithdrawalParams; + isGlv: boolean; + glvInfo: GlvInfo | undefined; + paySource: "settlementChain" | "gmxAccount" | "sourceChain"; + srcChainId: SourceChainId | undefined; + firstTokenAddress: NativeTokenSupportedAddress | ERC20Address | undefined; + firstTokenAmount: bigint; + longTokenAmount: bigint; + shortTokenAmount: bigint; + marketTokenAmount: bigint; + operation: Operation; + amounts: DepositAmounts | WithdrawalAmounts | undefined; + gasLimits: GasLimitsConfig; + tokensData: TokensData; + gasPrice: bigint; +}; diff --git a/src/domain/synthetics/markets/types.ts b/src/domain/synthetics/markets/types.ts index 35f7516e4d..e78b701ad9 100644 --- a/src/domain/synthetics/markets/types.ts +++ b/src/domain/synthetics/markets/types.ts @@ -162,3 +162,22 @@ export type RawCreateGlvWithdrawalParams = Omit