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