Skip to content

Commit f8fc7b5

Browse files
authored
Merge pull request #1708 from chainapsis/rowan/KEPLR-1550
Multiple txs case swap
2 parents 9d6f323 + 767711e commit f8fc7b5

File tree

30 files changed

+1809
-1367
lines changed

30 files changed

+1809
-1367
lines changed

apps/extension/src/pages/ibc-swap/index.tsx

Lines changed: 646 additions & 854 deletions
Large diffs are not rendered by default.

apps/extension/src/pages/main/components/ibc-history-view/index.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
GetIBCHistoriesMsg,
55
GetSkipHistoriesMsg,
66
GetSwapV2HistoriesMsg,
7+
HideSwapV2HistoryMsg,
78
IBCHistory,
89
RemoveIBCHistoryMsg,
910
RemoveSkipHistoryMsg,
@@ -333,12 +334,18 @@ export const IbcHistoryView: FunctionComponent<{
333334
history={history}
334335
removeHistory={(id) => {
335336
const requester = new InExtensionMessageRequester();
336-
const msg = new RemoveSwapV2HistoryMsg(id);
337-
requester
338-
.sendMessage(BACKGROUND_PORT, msg)
339-
.then((histories) => {
340-
setSwapV2Histories(histories);
341-
});
337+
if (history.requiresNextTransaction && !history.trackDone) {
338+
// do not remove the history before the tracking is done
339+
const msg = new HideSwapV2HistoryMsg(id);
340+
requester.sendMessage(BACKGROUND_PORT, msg);
341+
} else {
342+
const msg = new RemoveSwapV2HistoryMsg(id);
343+
requester
344+
.sendMessage(BACKGROUND_PORT, msg)
345+
.then((histories) => {
346+
setSwapV2Histories(histories);
347+
});
348+
}
342349
}}
343350
/>
344351
);
@@ -1436,6 +1443,10 @@ const SwapV2HistoryViewItem: FunctionComponent<{
14361443
}
14371444
})();
14381445

1446+
if (history.hidden) {
1447+
return null;
1448+
}
1449+
14391450
return (
14401451
<Box
14411452
padding="1.25rem"

apps/extension/src/pages/sign/ethereum/views/sign-tx-view.tsx

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import { useNavigate } from "react-router";
5454
import { ApproveIcon, CancelIcon } from "../../../../components/button";
5555
import { HeaderProps } from "../../../../layouts/header/types";
5656
import { getKeplrFromWindow } from "@keplr-wallet/stores";
57+
import { UnsignedEVMTransactionWithErc20Approvals } from "@keplr-wallet/stores-eth";
5758

5859
export const EthereumSignTxView: FunctionComponent<{
5960
interactionData: NonNullable<SignEthereumInteractionStore["waitingData"]>;
@@ -103,30 +104,89 @@ export const EthereumSignTxView: FunctionComponent<{
103104
gasConfig
104105
);
105106

106-
const [signingDataBuff, setSigningDataBuff] = useState(Buffer.from(message));
107+
const [requiredErc20Approvals] = useState<
108+
NonNullable<
109+
UnsignedEVMTransactionWithErc20Approvals["requiredErc20Approvals"]
110+
>
111+
>(() => {
112+
const parsed = JSON.parse(Buffer.from(message).toString("utf8"));
113+
if (
114+
parsed.requiredErc20Approvals &&
115+
Array.isArray(parsed.requiredErc20Approvals) &&
116+
parsed.requiredErc20Approvals.length > 0
117+
) {
118+
return parsed.requiredErc20Approvals;
119+
}
120+
return [];
121+
});
122+
123+
const [signingDataBuff, setSigningDataBuff] = useState(() => {
124+
const parsed = JSON.parse(Buffer.from(message).toString("utf8"));
125+
if (parsed.requiredErc20Approvals) {
126+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
127+
const { requiredErc20Approvals: _, ...unsignedTx } = parsed;
128+
return Buffer.from(JSON.stringify(unsignedTx), "utf8");
129+
}
130+
return Buffer.from(message);
131+
});
107132
const [preferNoSetFee, setPreferNoSetFee] = useState<boolean>(false);
108133

134+
const simulatorKey = useMemo(() => {
135+
if (requiredErc20Approvals.length === 0) {
136+
return "evm/native";
137+
}
138+
return "evm/native/bundle";
139+
}, [requiredErc20Approvals]);
140+
109141
const gasSimulator = useGasSimulator(
110142
new MemoryKVStore("gas-simulator.ethereum.sign"),
111143
chainStore,
112144
chainInfo.chainId,
113145
gasConfig,
114146
feeConfig,
115-
"evm/native",
147+
simulatorKey,
116148
() => {
117149
if (chainInfo.evm == null) {
118150
throw new Error("Gas simulator is only working with EVM info");
119151
}
120152

121153
const unsignedTx = JSON.parse(Buffer.from(message).toString("utf8"));
154+
if (requiredErc20Approvals.length === 0) {
155+
return {
156+
simulate: () =>
157+
ethereumAccount.simulateGas(account.ethereumHexAddress, {
158+
to: unsignedTx.to,
159+
data: unsignedTx.data,
160+
value: unsignedTx.value,
161+
}),
162+
};
163+
}
122164

165+
if (requiredErc20Approvals.length > 1) {
166+
throw new Error("Multiple required ERC20 approvals are not supported");
167+
}
168+
169+
// NOTE:
170+
// If multiple ERC20 approvals are needed, or
171+
// bundle simulation using state diff tracing is not possible,
172+
// execute ERC20 approvals first—then simulate the main transaction on this page.
123173
return {
124174
simulate: () =>
125-
ethereumAccount.simulateGas(account.ethereumHexAddress, {
126-
to: unsignedTx.to,
127-
data: unsignedTx.data,
128-
value: unsignedTx.value,
129-
}),
175+
ethereumAccount
176+
.simulateGasWithPendingErc20Approval(account.ethereumHexAddress, {
177+
to: unsignedTx.to,
178+
data: unsignedTx.data,
179+
value: unsignedTx.value,
180+
requiredErc20Approvals,
181+
})
182+
.then((result) => {
183+
const { gasUsed } = result;
184+
// only consider the gas used for the main tx
185+
if (gasUsed == null || gasUsed <= 0) {
186+
throw new Error("Gas used is not positive");
187+
}
188+
return { gasUsed };
189+
}),
130190
};
131191
}
132192
);
@@ -185,6 +245,7 @@ export const EthereumSignTxView: FunctionComponent<{
185245
}, []);
186246

187247
useEffect(() => {
248+
// NOTE: set fee only for external requests
188249
if (!interactionData.isInternal) {
189250
const unsignedTx = JSON.parse(Buffer.from(message).toString("utf8"));
190251

@@ -298,6 +359,7 @@ export const EthereumSignTxView: FunctionComponent<{
298359
const { to, gasLimit, value, data, chainId }: UnsignedTransaction =
299360
JSON.parse(Buffer.from(message).toString("utf8"));
300361

362+
// TODO: bundle simulation인 경우에 대한 처리 필요
301363
const l1DataFee = await ethereumAccount.simulateOpStackL1Fee({
302364
to,
303365
gasLimit,

0 commit comments

Comments
 (0)