Skip to content

Commit b6e358f

Browse files
committed
improve swap v2 tx status tracking logic
1 parent 70b5bbd commit b6e358f

File tree

5 files changed

+298
-88
lines changed

5 files changed

+298
-88
lines changed

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

Lines changed: 137 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import React, { FunctionComponent, useEffect, useState } from "react";
22
import { observer } from "mobx-react-lite";
33
import {
4+
BackgroundTxStatus,
45
GetIBCHistoriesMsg,
56
GetSkipHistoriesMsg,
67
GetSwapV2HistoriesMsg,
8+
GetTxExecutionMsg,
79
HideSwapV2HistoryMsg,
810
IBCHistory,
911
RemoveIBCHistoryMsg,
@@ -12,12 +14,14 @@ import {
1214
SkipHistory,
1315
SwapV2History,
1416
SwapV2TxStatus,
17+
TxExecution,
1518
} from "@keplr-wallet/background";
1619
import { InExtensionMessageRequester } from "@keplr-wallet/router-extension";
1720
import { BACKGROUND_PORT } from "@keplr-wallet/router";
1821
import { useLayoutEffectOnce } from "../../../../hooks/use-effect-once";
1922
import { Stack } from "../../../../components/stack";
2023
import { Box } from "../../../../components/box";
24+
import { Button } from "../../../../components/button";
2125
import { Gutter } from "../../../../components/gutter";
2226
import { useTheme } from "styled-components";
2327
import { ColorPalette } from "../../../../styles";
@@ -334,8 +338,8 @@ export const IbcHistoryView: FunctionComponent<{
334338
history={history}
335339
removeHistory={(id) => {
336340
const requester = new InExtensionMessageRequester();
337-
if (history.requiresNextTransaction && !history.trackDone) {
338-
// do not remove the history before the tracking is done
341+
if (history.backgroundExecutionId && !history.trackDone) {
342+
// do not remove the history before the tracking is done (multi tx case)
339343
const msg = new HideSwapV2HistoryMsg(id);
340344
requester.sendMessage(BACKGROUND_PORT, msg);
341345
} else {
@@ -1193,7 +1197,12 @@ const SkipHistoryViewItem: FunctionComponent<{
11931197

11941198
return chainIds.map((chainId, i) => {
11951199
const chainInfo = chainStore.getChain(chainId);
1196-
const completed = !!history.trackDone || i < history.routeIndex;
1200+
// Only mark as completed based on routeIndex, not trackDone
1201+
const completed =
1202+
i < history.routeIndex ||
1203+
(i === history.routeIndex &&
1204+
!!history.trackDone &&
1205+
!history.trackError);
11971206
const error = !!history.trackError && i >= failedRouteIndex;
11981207

11991208
return (
@@ -1408,17 +1417,66 @@ const SwapV2HistoryViewItem: FunctionComponent<{
14081417
}> = observer(({ history, removeHistory }) => {
14091418
const { chainStore } = useStore();
14101419

1420+
console.log("history", history);
1421+
14111422
const theme = useTheme();
14121423
const intl = useIntl();
14131424

1425+
const [txExecution, setTxExecution] = useState<TxExecution | undefined>(
1426+
undefined
1427+
);
1428+
1429+
useEffect(() => {
1430+
if (!history.backgroundExecutionId) {
1431+
setTxExecution(undefined);
1432+
return;
1433+
}
1434+
1435+
const requester = new InExtensionMessageRequester();
1436+
requester
1437+
.sendMessage(
1438+
BACKGROUND_PORT,
1439+
new GetTxExecutionMsg(history.backgroundExecutionId)
1440+
)
1441+
.then((execution) => {
1442+
setTxExecution(execution);
1443+
})
1444+
.catch((e) => {
1445+
console.error("Failed to get tx execution:", e);
1446+
setTxExecution(undefined);
1447+
});
1448+
}, [history.backgroundExecutionId]);
1449+
1450+
// 실행 가능한 트랜잭션이 있는지 확인 (멀티 TX 케이스)
1451+
const hasExecutableTx = (() => {
1452+
if (!txExecution || !history.backgroundExecutionId) {
1453+
return false;
1454+
}
1455+
// pending 상태이면서 executableChainIds에 chainId가 포함된 트랜잭션이 있는 경우
1456+
return txExecution.txs.some(
1457+
(tx) =>
1458+
(tx.status === BackgroundTxStatus.PENDING ||
1459+
tx.status === BackgroundTxStatus.BLOCKED) &&
1460+
txExecution.executableChainIds.includes(tx.chainId)
1461+
);
1462+
})();
1463+
14141464
const historyCompleted = (() => {
14151465
if (!history.trackDone) {
14161466
return false;
14171467
}
14181468

1419-
// If there's a track error, check if swapRefundInfo exists (refund completed)
1469+
// If there's a track error, check if assetLocationInfo exists (refund completed)
14201470
if (history.trackError) {
1421-
return !!history.swapRefundInfo;
1471+
return !!history.assetLocationInfo;
1472+
}
1473+
1474+
// SUCCESS 상태에서 assetLocationInfo가 없으면 destination에 도달한 것
1475+
if (
1476+
history.status === SwapV2TxStatus.SUCCESS &&
1477+
!history.assetLocationInfo
1478+
) {
1479+
return true;
14221480
}
14231481

14241482
// Success or partial success with last route completed
@@ -1429,6 +1487,8 @@ const SwapV2HistoryViewItem: FunctionComponent<{
14291487
);
14301488
})();
14311489

1490+
console.log("historyCompleted", historyCompleted, history);
1491+
14321492
const failedRouteIndex = (() => {
14331493
// Failed if status is FAILED or if there's a trackError
14341494
if (history.status === SwapV2TxStatus.FAILED || history.trackError) {
@@ -1514,7 +1574,7 @@ const SwapV2HistoryViewItem: FunctionComponent<{
15141574
if (failedRouteIndex >= 0) {
15151575
if (
15161576
history.status === SwapV2TxStatus.FAILED &&
1517-
history.swapRefundInfo
1577+
history.assetLocationInfo
15181578
) {
15191579
return intl.formatMessage({
15201580
id: "page.main.components.ibc-history-view.ibc-swap.item.refund.succeed",
@@ -1690,10 +1750,35 @@ const SwapV2HistoryViewItem: FunctionComponent<{
16901750
return route.chainId;
16911751
});
16921752

1753+
// assetLocationInfo가 있으면 해당 체인까지는 asset이 릴리즈된 것이므로 성공으로 처리
1754+
const assetReleasedRouteIndex = (() => {
1755+
if (history.assetLocationInfo) {
1756+
const idx = chainIds.findIndex(
1757+
(chainId) => chainId === history.assetLocationInfo!.chainId
1758+
);
1759+
if (idx >= 0) {
1760+
return idx;
1761+
}
1762+
}
1763+
return -1;
1764+
})();
1765+
16931766
return chainIds.map((chainId, i) => {
16941767
const chainInfo = chainStore.getChain(chainId);
1695-
const completed = !!history.trackDone || i < history.routeIndex;
1696-
const error = !!history.trackError && i >= failedRouteIndex;
1768+
// Asset이 릴리즈된 체인까지는 성공으로 처리
1769+
const completed =
1770+
i < history.routeIndex ||
1771+
(i === history.routeIndex &&
1772+
!!history.trackDone &&
1773+
!history.trackError) ||
1774+
(assetReleasedRouteIndex >= 0 &&
1775+
i <= assetReleasedRouteIndex);
1776+
1777+
// 에러는 assetReleasedRouteIndex보다 큰 인덱스에서만 표시
1778+
const error =
1779+
!!history.trackError &&
1780+
i >= failedRouteIndex &&
1781+
(assetReleasedRouteIndex < 0 || i > assetReleasedRouteIndex);
16971782

16981783
return (
16991784
// 일부분 순환하는 경우도 이론적으로 가능은 하기 때문에 chain id를 key로 사용하지 않음.
@@ -1703,6 +1788,13 @@ const SwapV2HistoryViewItem: FunctionComponent<{
17031788
completed={!error && completed}
17041789
notCompletedBlink={(() => {
17051790
if (failedRoute) {
1791+
// asset이 릴리즈된 체인까지는 blink하지 않음
1792+
if (
1793+
assetReleasedRouteIndex >= 0 &&
1794+
i <= assetReleasedRouteIndex
1795+
) {
1796+
return false;
1797+
}
17061798
return i === failedRouteIndex;
17071799
}
17081800

@@ -1721,7 +1813,12 @@ const SwapV2HistoryViewItem: FunctionComponent<{
17211813
return "right";
17221814
}
17231815

1724-
return i === failedRouteIndex ? "left" : "hide";
1816+
// asset이 릴리즈된 체인이 있으면, 그 이후는 화살표 숨김
1817+
if (assetReleasedRouteIndex >= 0) {
1818+
return i > assetReleasedRouteIndex ? "hide" : "right";
1819+
}
1820+
1821+
return i === failedRouteIndex ? "left" : "right";
17251822
})()}
17261823
error={error}
17271824
isLast={chainIds.length - 1 === i}
@@ -1748,29 +1845,31 @@ const SwapV2HistoryViewItem: FunctionComponent<{
17481845
history.status === SwapV2TxStatus.PARTIAL_SUCCESS;
17491846

17501847
// status tracking이 오류로 끝난 경우
1751-
// SwapV2에서는 swapRefundInfo를 사용하여 환불 정보 표시
1848+
// SwapV2에서는 assetLocationInfo를 사용하여 환불 정보 표시
17521849
if (
17531850
history.trackDone &&
17541851
(history.trackError ||
17551852
history.status === SwapV2TxStatus.FAILED)
17561853
) {
1757-
if (history.swapRefundInfo) {
1758-
if (chainStore.hasChain(history.swapRefundInfo.chainId)) {
1759-
const swapRefundChain = chainStore.getChain(
1760-
history.swapRefundInfo.chainId
1854+
if (history.assetLocationInfo) {
1855+
if (
1856+
chainStore.hasChain(history.assetLocationInfo.chainId)
1857+
) {
1858+
const assetLocationChain = chainStore.getChain(
1859+
history.assetLocationInfo.chainId
17611860
);
17621861

17631862
return intl.formatMessage(
17641863
{
17651864
id: "page.main.components.ibc-history-view.skip-swap.failed.after-transfer.complete",
17661865
},
17671866
{
1768-
chain: swapRefundChain.chainName,
1769-
assets: history.swapRefundInfo.amount
1867+
chain: assetLocationChain.chainName,
1868+
assets: history.assetLocationInfo.amount
17701869
.map((amount) => {
17711870
return new CoinPretty(
17721871
chainStore
1773-
.getChain(history.swapRefundInfo!.chainId)
1872+
.getChain(history.assetLocationInfo!.chainId)
17741873
.forceFindCurrency(amount.denom),
17751874
amount.amount
17761875
)
@@ -1796,6 +1895,27 @@ const SwapV2HistoryViewItem: FunctionComponent<{
17961895
</Caption1>
17971896
</VerticalCollapseTransition>
17981897

1898+
{/* 추가 트랜잭션 실행이 필요한 경우 버튼 표시 */}
1899+
<VerticalCollapseTransition collapsed={!hasExecutableTx}>
1900+
<Gutter size="1rem" />
1901+
<Button
1902+
text={intl.formatMessage({
1903+
id: "page.main.components.ibc-history-view.swap-v2.continue-transaction",
1904+
})}
1905+
size="small"
1906+
color="primary"
1907+
onClick={() => {
1908+
if (!txExecution) {
1909+
return;
1910+
}
1911+
1912+
// TODO: execution 데이터를 사용해서 다음 트랜잭션 실행 페이지로 이동
1913+
console.log("Execution data:", txExecution);
1914+
console.log("Pending txs:", txExecution.txs);
1915+
}}
1916+
/>
1917+
</VerticalCollapseTransition>
1918+
17991919
<VerticalCollapseTransition collapsed={historyCompleted}>
18001920
<Gutter size="1rem" />
18011921
<Box

packages/background/src/recent-send-history/handler.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,8 +300,7 @@ const handleRecordTxWithSwapV2Msg: (
300300
msg.notificationInfo,
301301
msg.routeDurationSeconds,
302302
msg.txHash,
303-
msg.isOnlyUseBridge,
304-
msg.requiresNextTransaction
303+
msg.isOnlyUseBridge
305304
);
306305
};
307306
};
@@ -335,6 +334,6 @@ const handleHideSwapV2HistoryMsg: (
335334
service: RecentSendHistoryService
336335
) => InternalHandler<HideSwapV2HistoryMsg> = (service) => {
337336
return (_env, msg) => {
338-
service.hideRecentSwapV2History(msg.id);
337+
service.hideSwapV2History(msg.id);
339338
};
340339
};

packages/background/src/recent-send-history/messages.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -576,8 +576,7 @@ export class RecordTxWithSwapV2Msg extends Message<string> {
576576
},
577577
public readonly routeDurationSeconds: number,
578578
public readonly txHash: string,
579-
public readonly isOnlyUseBridge?: boolean,
580-
public readonly requiresNextTransaction?: boolean
579+
public readonly isOnlyUseBridge?: boolean
581580
) {
582581
super();
583582
}

0 commit comments

Comments
 (0)