diff --git a/src/entries/popup/components/TransactionFee/TransactionFee.tsx b/src/entries/popup/components/TransactionFee/TransactionFee.tsx
index 0ce4707751..4b69f519c5 100644
--- a/src/entries/popup/components/TransactionFee/TransactionFee.tsx
+++ b/src/entries/popup/components/TransactionFee/TransactionFee.tsx
@@ -286,7 +286,7 @@ type TransactionFeeProps = {
disableShortcuts?: boolean;
address?: Address;
defaultSpeed?: GasSpeed;
- transactionRequest: TransactionRequest;
+ transactionRequests: TransactionRequest[];
accentColor?: string;
plainTriggerBorder?: boolean;
analyticsEvents?: {
@@ -294,6 +294,8 @@ type TransactionFeeProps = {
transactionSpeedSwitched: keyof EventProperties;
transactionSpeedClicked: keyof EventProperties;
};
+ feeLabel?: string;
+ feeInfoButton?: { onClick: () => void };
};
export function TransactionFee({
@@ -301,10 +303,12 @@ export function TransactionFee({
disableShortcuts,
address,
defaultSpeed,
- transactionRequest,
+ transactionRequests,
accentColor,
plainTriggerBorder,
analyticsEvents,
+ feeLabel,
+ feeInfoButton,
}: TransactionFeeProps) {
const { defaultTxSpeed } = useDefaultTxSpeed({ chainId });
const {
@@ -322,7 +326,7 @@ export function TransactionFee({
chainId,
address,
defaultSpeed: defaultSpeed || defaultTxSpeed,
- transactionRequest,
+ transactionRequests,
});
return (
@@ -342,6 +346,8 @@ export function TransactionFee({
currentBaseFee={currentBaseFee}
baseFeeTrend={baseFeeTrend}
feeType={feeType}
+ feeLabel={feeLabel}
+ feeInfoButton={feeInfoButton}
/>
);
}
diff --git a/src/entries/popup/hooks/useGas.ts b/src/entries/popup/hooks/useGas.ts
index dd07b1b807..3da4572fd9 100644
--- a/src/entries/popup/hooks/useGas.ts
+++ b/src/entries/popup/hooks/useGas.ts
@@ -10,6 +10,7 @@ import { isLegacyMeteorologyFeeData } from '~/core/resources/gas/classification'
import { useEstimateApprovalGasLimit } from '~/core/resources/gas/estimateApprovalGasLimit';
import { useEstimateSwapGasLimit } from '~/core/resources/gas/estimateSwapGasLimit';
import { useOptimismL1SecurityFee } from '~/core/resources/gas/optimismL1SecurityFee';
+import { estimateTransactionsGasLimit } from '~/core/resources/transactions/simulation';
import { useCurrentCurrencyStore, useGasStore } from '~/core/state';
import { useNetworkStore } from '~/core/state/networks/networks';
import { ParsedAsset, ParsedSearchAsset } from '~/core/types/assets';
@@ -365,27 +366,72 @@ const useGas = ({
};
};
+const toSimulationTransaction = (
+ tx: TransactionRequest,
+): { from: string; to: string; value: string; data: string } => ({
+ from: tx.from?.toString() ?? '',
+ to: tx.to?.toString() ?? '',
+ value: tx.value?.toString() ?? '0',
+ data: tx.data?.toString() ?? '0x',
+});
+
+/**
+ * Gas estimation for one or more transactions.
+ * Single tx: uses provider estimateGas. Batch: sums gas from simulation API.
+ */
export const useTransactionGas = ({
chainId,
address,
defaultSpeed,
- transactionRequest,
+ transactionRequests,
}: {
chainId: ChainId;
address?: Address;
defaultSpeed?: GasSpeed;
- transactionRequest: TransactionRequest;
+ transactionRequests: TransactionRequest[];
}) => {
- const { data: estimatedGasLimit } = useEstimateGasLimit({
- chainId,
- transactionRequest: useDebounce(transactionRequest, 500),
+ const debouncedRequests = useDebounce(transactionRequests, 500);
+ const isSingle = (debouncedRequests?.length ?? 0) <= 1;
+
+ const { data: singleGasLimit } = useEstimateGasLimit(
+ {
+ chainId,
+ transactionRequest: debouncedRequests?.[0] ?? {},
+ },
+ { enabled: !!chainId && isSingle && !!debouncedRequests?.length },
+ );
+
+ const { data: batchGasLimit } = useQuery({
+ queryKey: [
+ 'estimateBatchGasLimit',
+ chainId,
+ address,
+ debouncedRequests
+ ?.map(
+ (r) =>
+ `${r.from ?? ''}-${r.to}-${r.value?.toString() ?? '0'}-${r.data}`,
+ )
+ .join(','),
+ ],
+ queryFn: async () => {
+ if (!debouncedRequests?.length || isSingle) return undefined;
+ const steps = debouncedRequests.map((tx, i) => ({
+ transaction: toSimulationTransaction(tx),
+ label: `Call ${i + 1}`,
+ }));
+ return estimateTransactionsGasLimit({ chainId, steps });
+ },
+ enabled: !!chainId && !isSingle && !!debouncedRequests?.length,
});
+
+ const estimatedGasLimit = isSingle ? singleGasLimit : batchGasLimit;
+
return useGas({
chainId,
address,
defaultSpeed,
- estimatedGasLimit,
- transactionRequest,
+ estimatedGasLimit: estimatedGasLimit ?? undefined,
+ transactionRequest: debouncedRequests?.[0] ?? null,
enabled: true,
});
};
diff --git a/src/entries/popup/pages/home/Delegations/RevokeDelegationPage.tsx b/src/entries/popup/pages/home/Delegations/RevokeDelegationPage.tsx
index dd585f42c9..1c8ac78a8f 100644
--- a/src/entries/popup/pages/home/Delegations/RevokeDelegationPage.tsx
+++ b/src/entries/popup/pages/home/Delegations/RevokeDelegationPage.tsx
@@ -634,13 +634,15 @@ export const RevokeDelegationPage = () => {
diff --git a/src/entries/popup/pages/messages/SendTransaction/index.tsx b/src/entries/popup/pages/messages/SendTransaction/index.tsx
index 4700a202fa..8f6af93cb5 100644
--- a/src/entries/popup/pages/messages/SendTransaction/index.tsx
+++ b/src/entries/popup/pages/messages/SendTransaction/index.tsx
@@ -315,7 +315,11 @@ export function SendTransaction({
}}
chainId={activeSession?.chainId || ChainId.mainnet}
address={activeSession?.address}
- transactionRequest={request?.params?.[0] as TransactionRequest}
+ transactionRequests={
+ request?.params?.[0]
+ ? [request.params[0] as TransactionRequest]
+ : []
+ }
plainTriggerBorder
/>
diff --git a/src/entries/popup/pages/speedUpAndCancelSheet/index.tsx b/src/entries/popup/pages/speedUpAndCancelSheet/index.tsx
index 288c39ac94..9a9ebd5b08 100644
--- a/src/entries/popup/pages/speedUpAndCancelSheet/index.tsx
+++ b/src/entries/popup/pages/speedUpAndCancelSheet/index.tsx
@@ -393,7 +393,9 @@ export function SpeedUpAndCancelSheet({
)}