@keplr-wallet/background 0.12.312 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.d.ts +1 -0
- package/build/index.js +7 -1
- package/build/index.js.map +1 -1
- package/build/keyring-cosmos/service.d.ts +10 -0
- package/build/keyring-cosmos/service.js +100 -0
- package/build/keyring-cosmos/service.js.map +1 -1
- package/build/keyring-ethereum/service.d.ts +5 -0
- package/build/keyring-ethereum/service.js +66 -0
- package/build/keyring-ethereum/service.js.map +1 -1
- package/build/recent-send-history/api.d.ts +31 -0
- package/build/recent-send-history/api.js +97 -0
- package/build/recent-send-history/api.js.map +1 -0
- package/build/recent-send-history/handler.js +36 -0
- package/build/recent-send-history/handler.js.map +1 -1
- package/build/recent-send-history/init.js +5 -0
- package/build/recent-send-history/init.js.map +1 -1
- package/build/recent-send-history/messages.d.ts +76 -1
- package/build/recent-send-history/messages.js +121 -1
- package/build/recent-send-history/messages.js.map +1 -1
- package/build/recent-send-history/service.d.ts +262 -9
- package/build/recent-send-history/service.js +2103 -812
- package/build/recent-send-history/service.js.map +1 -1
- package/build/recent-send-history/types.d.ts +214 -22
- package/build/recent-send-history/types.js +21 -0
- package/build/recent-send-history/types.js.map +1 -1
- package/build/tx/service.d.ts +2 -0
- package/build/tx/service.js +35 -0
- package/build/tx/service.js.map +1 -1
- package/build/tx-ethereum/service.d.ts +2 -0
- package/build/tx-ethereum/service.js +42 -0
- package/build/tx-ethereum/service.js.map +1 -1
- package/build/tx-executor/constants.d.ts +1 -0
- package/build/tx-executor/constants.js +5 -0
- package/build/tx-executor/constants.js.map +1 -0
- package/build/tx-executor/handler.d.ts +3 -0
- package/build/tx-executor/handler.js +45 -0
- package/build/tx-executor/handler.js.map +1 -0
- package/build/tx-executor/index.d.ts +3 -0
- package/build/tx-executor/index.js +20 -0
- package/build/tx-executor/index.js.map +1 -0
- package/build/tx-executor/init.d.ts +3 -0
- package/build/tx-executor/init.js +14 -0
- package/build/tx-executor/init.js.map +1 -0
- package/build/tx-executor/internal.d.ts +4 -0
- package/build/tx-executor/internal.js +24 -0
- package/build/tx-executor/internal.js.map +1 -0
- package/build/tx-executor/messages.d.ts +53 -0
- package/build/tx-executor/messages.js +116 -0
- package/build/tx-executor/messages.js.map +1 -0
- package/build/tx-executor/service.d.ts +67 -0
- package/build/tx-executor/service.js +715 -0
- package/build/tx-executor/service.js.map +1 -0
- package/build/tx-executor/types.d.ts +105 -0
- package/build/tx-executor/types.js +33 -0
- package/build/tx-executor/types.js.map +1 -0
- package/build/tx-executor/utils/cosmos.d.ts +59 -0
- package/build/tx-executor/utils/cosmos.js +526 -0
- package/build/tx-executor/utils/cosmos.js.map +1 -0
- package/build/tx-executor/utils/evm.d.ts +4 -0
- package/build/tx-executor/utils/evm.js +236 -0
- package/build/tx-executor/utils/evm.js.map +1 -0
- package/package.json +13 -13
- package/src/index.ts +24 -1
- package/src/keyring-cosmos/service.ts +151 -0
- package/src/keyring-ethereum/service.ts +103 -6
- package/src/recent-send-history/api.ts +119 -0
- package/src/recent-send-history/handler.ts +84 -0
- package/src/recent-send-history/init.ts +10 -0
- package/src/recent-send-history/messages.ts +163 -1
- package/src/recent-send-history/service.ts +3042 -1153
- package/src/recent-send-history/types.ts +268 -31
- package/src/tx/service.ts +41 -0
- package/src/tx-ethereum/service.ts +57 -0
- package/src/tx-executor/constants.ts +1 -0
- package/src/tx-executor/handler.ts +71 -0
- package/src/tx-executor/index.ts +3 -0
- package/src/tx-executor/init.ts +20 -0
- package/src/tx-executor/internal.ts +9 -0
- package/src/tx-executor/messages.ts +157 -0
- package/src/tx-executor/service.ts +1025 -0
- package/src/tx-executor/types.ts +161 -0
- package/src/tx-executor/utils/cosmos.ts +771 -0
- package/src/tx-executor/utils/evm.ts +310 -0
|
@@ -16,22 +16,26 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
16
16
|
};
|
|
17
17
|
var _a;
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.RecentSendHistoryService = void 0;
|
|
19
|
+
exports.RecentSendHistoryService = exports.UNKNOWN_TX_STATUS_TIMEOUT_MS = void 0;
|
|
20
20
|
const cosmos_1 = require("@keplr-wallet/cosmos");
|
|
21
21
|
const mobx_1 = require("mobx");
|
|
22
22
|
const common_1 = require("@keplr-wallet/common");
|
|
23
|
+
const types_1 = require("./types");
|
|
23
24
|
const buffer_1 = require("buffer/");
|
|
24
|
-
const
|
|
25
|
+
const types_2 = require("@keplr-wallet/types");
|
|
25
26
|
const unit_1 = require("@keplr-wallet/unit");
|
|
26
|
-
const simple_fetch_1 = require("@keplr-wallet/simple-fetch");
|
|
27
27
|
const hash_1 = require("@ethersproject/hash");
|
|
28
|
+
const api_1 = require("./api");
|
|
29
|
+
exports.UNKNOWN_TX_STATUS_TIMEOUT_MS = 5 * 60 * 1000; // 5분
|
|
28
30
|
const SWAP_API_ENDPOINT = (_a = process.env["KEPLR_API_ENDPOINT"]) !== null && _a !== void 0 ? _a : "";
|
|
29
31
|
class RecentSendHistoryService {
|
|
30
|
-
constructor(kvStore, chainsService, txService, notification) {
|
|
32
|
+
constructor(kvStore, chainsService, txService, analyticsService, notification, publisher) {
|
|
31
33
|
this.kvStore = kvStore;
|
|
32
34
|
this.chainsService = chainsService;
|
|
33
35
|
this.txService = txService;
|
|
36
|
+
this.analyticsService = analyticsService;
|
|
34
37
|
this.notification = notification;
|
|
38
|
+
this.publisher = publisher;
|
|
35
39
|
// Key: {chain_identifier}/{type}
|
|
36
40
|
this.recentSendHistoryMap = new Map();
|
|
37
41
|
this.recentIBCHistorySeq = 0;
|
|
@@ -40,6 +44,9 @@ class RecentSendHistoryService {
|
|
|
40
44
|
this.recentSkipHistorySeq = 0;
|
|
41
45
|
// Key: id (sequence, it should be increased by 1 for each)
|
|
42
46
|
this.recentSkipHistoryMap = new Map();
|
|
47
|
+
this.recentSwapV2HistorySeq = 0;
|
|
48
|
+
// Key: id (sequence, it should be increased by 1 for each)
|
|
49
|
+
this.recentSwapV2HistoryMap = new Map();
|
|
43
50
|
// ibc packet forwarding을 위한 recursive function이면서
|
|
44
51
|
// 실패시 retry를 수행하기 위해서 분리되어 있음
|
|
45
52
|
// trackIBCPacketForwardingRecursive도 참고
|
|
@@ -51,505 +58,178 @@ class RecentSendHistoryService {
|
|
|
51
58
|
onFulfill();
|
|
52
59
|
return;
|
|
53
60
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return history.ibcHistory.find((h) => h.error != null) != null;
|
|
62
|
-
})();
|
|
63
|
-
if (needRewind) {
|
|
64
|
-
if (history.ibcHistory.find((h) => h.rewoundButNextRewindingBlocked)) {
|
|
65
|
-
onFulfill();
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
const isTimeoutPacket = history.packetTimeout || false;
|
|
69
|
-
const lastRewoundChannelIndex = history.ibcHistory.findIndex((h) => {
|
|
70
|
-
if (h.rewound) {
|
|
71
|
-
return true;
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
const targetChannel = (() => {
|
|
75
|
-
if (lastRewoundChannelIndex >= 0) {
|
|
76
|
-
if (lastRewoundChannelIndex === 0) {
|
|
77
|
-
return undefined;
|
|
78
|
-
}
|
|
79
|
-
return history.ibcHistory[lastRewoundChannelIndex - 1];
|
|
80
|
-
}
|
|
81
|
-
return history.ibcHistory.find((h) => h.error != null);
|
|
82
|
-
})();
|
|
83
|
-
const isSwapTargetChannel = targetChannel &&
|
|
84
|
-
"swapChannelIndex" in history &&
|
|
85
|
-
history.ibcHistory.indexOf(targetChannel) ===
|
|
86
|
-
history.swapChannelIndex + 1;
|
|
87
|
-
if (targetChannel && targetChannel.sequence) {
|
|
88
|
-
const prevChainInfo = (() => {
|
|
89
|
-
const targetChannelIndex = history.ibcHistory.findIndex((h) => h === targetChannel);
|
|
90
|
-
if (targetChannelIndex < 0) {
|
|
91
|
-
return undefined;
|
|
92
|
-
}
|
|
93
|
-
if (targetChannelIndex === 0) {
|
|
94
|
-
return this.chainsService.getChainInfo(history.chainId);
|
|
95
|
-
}
|
|
96
|
-
return this.chainsService.getChainInfo(history.ibcHistory[targetChannelIndex - 1].counterpartyChainId);
|
|
97
|
-
})();
|
|
98
|
-
if (prevChainInfo) {
|
|
99
|
-
const txTracer = new cosmos_1.TendermintTxTracer(prevChainInfo.rpc, "/websocket");
|
|
100
|
-
txTracer.addEventListener("close", onClose);
|
|
101
|
-
txTracer.addEventListener("error", onError);
|
|
102
|
-
txTracer
|
|
103
|
-
.traceTx(isTimeoutPacket
|
|
104
|
-
? {
|
|
105
|
-
// "timeout_packet.packet_src_port": targetChannel.portId,
|
|
106
|
-
"timeout_packet.packet_src_channel": targetChannel.channelId,
|
|
107
|
-
"timeout_packet.packet_sequence": targetChannel.sequence,
|
|
108
|
-
}
|
|
109
|
-
: {
|
|
110
|
-
// "acknowledge_packet.packet_src_port": targetChannel.portId,
|
|
111
|
-
"acknowledge_packet.packet_src_channel": targetChannel.channelId,
|
|
112
|
-
"acknowledge_packet.packet_sequence": targetChannel.sequence,
|
|
113
|
-
})
|
|
114
|
-
.then((res) => {
|
|
115
|
-
txTracer.close();
|
|
116
|
-
if (!res) {
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
(0, mobx_1.runInAction)(() => {
|
|
120
|
-
if (isSwapTargetChannel) {
|
|
121
|
-
const txs = res.txs
|
|
122
|
-
? res.txs.map((res) => res.tx_result || res)
|
|
123
|
-
: [res.tx_result || res];
|
|
124
|
-
if (txs && Array.isArray(txs)) {
|
|
125
|
-
for (const tx of txs) {
|
|
126
|
-
if (targetChannel.sequence && "swapReceiver" in history) {
|
|
127
|
-
const index = isTimeoutPacket
|
|
128
|
-
? this.getIBCTimeoutPacketIndexFromTx(tx, targetChannel.portId, targetChannel.channelId, targetChannel.sequence)
|
|
129
|
-
: this.getIBCAcknowledgementPacketIndexFromTx(tx, targetChannel.portId, targetChannel.channelId, targetChannel.sequence);
|
|
130
|
-
if (index >= 0) {
|
|
131
|
-
// 좀 빡치게 timeout packet은 refund 로직이 실행되고 나서 "timeout_packet" event가 발생한다.
|
|
132
|
-
const refunded = isTimeoutPacket
|
|
133
|
-
? this.getIBCSwapResAmountFromTx(tx, history.swapReceiver[history.swapChannelIndex + 1], (() => {
|
|
134
|
-
const i = this.getLastIBCTimeoutPacketBeforeIndexFromTx(tx, index);
|
|
135
|
-
if (i < 0) {
|
|
136
|
-
return 0;
|
|
137
|
-
}
|
|
138
|
-
return i;
|
|
139
|
-
})(), index)
|
|
140
|
-
: this.getIBCSwapResAmountFromTx(tx, history.swapReceiver[history.swapChannelIndex + 1], index);
|
|
141
|
-
history.swapRefundInfo = {
|
|
142
|
-
chainId: prevChainInfo.chainId,
|
|
143
|
-
amount: refunded,
|
|
144
|
-
};
|
|
145
|
-
targetChannel.rewoundButNextRewindingBlocked = true;
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
targetChannel.rewound = true;
|
|
153
|
-
});
|
|
154
|
-
onFulfill();
|
|
155
|
-
this.trackIBCPacketForwardingRecursive(id);
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
else if (!history.txFulfilled) {
|
|
161
|
-
const chainId = history.chainId;
|
|
162
|
-
const chainInfo = this.chainsService.getChainInfo(chainId);
|
|
163
|
-
const txHash = buffer_1.Buffer.from(history.txHash, "hex");
|
|
164
|
-
if (chainInfo) {
|
|
165
|
-
const txTracer = new cosmos_1.TendermintTxTracer(chainInfo.rpc, "/websocket");
|
|
166
|
-
txTracer.addEventListener("close", onClose);
|
|
167
|
-
txTracer.addEventListener("error", onError);
|
|
168
|
-
txTracer.traceTx(txHash).then((tx) => {
|
|
169
|
-
txTracer.close();
|
|
61
|
+
if (!history.txFulfilled) {
|
|
62
|
+
this.trackIBCTxFulfillment({
|
|
63
|
+
chainId: history.chainId,
|
|
64
|
+
txHash: history.txHash,
|
|
65
|
+
ibcHistory: history.ibcHistory,
|
|
66
|
+
swapReceiver: "swapReceiver" in history ? history.swapReceiver : undefined,
|
|
67
|
+
onTxFulfilled: (_tx, firstHopResAmount) => {
|
|
170
68
|
(0, mobx_1.runInAction)(() => {
|
|
171
69
|
history.txFulfilled = true;
|
|
172
|
-
if (
|
|
173
|
-
history.
|
|
174
|
-
// TODO: In this case, it is not currently displayed in the UI. So, delete it for now.
|
|
175
|
-
// 어차피 tx 자체의 실패는 notification으로 알 수 있기 때문에 여기서 지우더라도 유저는 실패를 인지할 수 있다.
|
|
176
|
-
this.removeRecentIBCHistory(id);
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
if ("swapReceiver" in history) {
|
|
180
|
-
const resAmount = this.getIBCSwapResAmountFromTx(tx, history.swapReceiver[0]);
|
|
181
|
-
history.resAmount.push(resAmount);
|
|
182
|
-
}
|
|
183
|
-
if (history.ibcHistory.length > 0) {
|
|
184
|
-
const firstChannel = history.ibcHistory[0];
|
|
185
|
-
firstChannel.sequence = this.getIBCPacketSequenceFromTx(tx, firstChannel.portId, firstChannel.channelId);
|
|
186
|
-
firstChannel.dstChannelId = this.getDstChannelIdFromTx(tx, firstChannel.portId, firstChannel.channelId);
|
|
187
|
-
onFulfill();
|
|
188
|
-
this.trackIBCPacketForwardingRecursive(id);
|
|
189
|
-
}
|
|
70
|
+
if ("swapReceiver" in history && firstHopResAmount) {
|
|
71
|
+
history.resAmount.push(firstHopResAmount);
|
|
190
72
|
}
|
|
191
73
|
});
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
74
|
+
this.trackIBCPacketForwardingRecursive((onFulfill, onClose, onError) => {
|
|
75
|
+
this.trackIBCPacketForwardingRecursiveInternal(id, onFulfill, onClose, onError);
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
onTxError: () => {
|
|
79
|
+
this.removeRecentIBCHistory(id);
|
|
80
|
+
},
|
|
81
|
+
onFulfill: onFulfill,
|
|
82
|
+
onClose: onClose,
|
|
83
|
+
onError: onError,
|
|
198
84
|
});
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
closables.forEach((closable) => {
|
|
213
|
-
if (closable.readyState === cosmos_1.WsReadyState.OPEN ||
|
|
214
|
-
closable.readyState === cosmos_1.WsReadyState.CONNECTING) {
|
|
215
|
-
closable.close();
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
onFulfill();
|
|
219
|
-
}
|
|
220
|
-
};
|
|
221
|
-
let _onCloseOnce = false;
|
|
222
|
-
const onCloseOnce = () => {
|
|
223
|
-
if (!_onCloseOnce) {
|
|
224
|
-
_onCloseOnce = true;
|
|
225
|
-
closables.forEach((closable) => {
|
|
226
|
-
if (closable.readyState === cosmos_1.WsReadyState.OPEN ||
|
|
227
|
-
closable.readyState === cosmos_1.WsReadyState.CONNECTING) {
|
|
228
|
-
closable.close();
|
|
229
|
-
}
|
|
230
|
-
});
|
|
231
|
-
onClose();
|
|
232
|
-
}
|
|
233
|
-
};
|
|
234
|
-
let _onErrorOnce = false;
|
|
235
|
-
const onErrorOnce = () => {
|
|
236
|
-
if (!_onErrorOnce) {
|
|
237
|
-
_onErrorOnce = true;
|
|
238
|
-
closables.forEach((closable) => {
|
|
239
|
-
if (closable.readyState === cosmos_1.WsReadyState.OPEN ||
|
|
240
|
-
closable.readyState === cosmos_1.WsReadyState.CONNECTING) {
|
|
241
|
-
closable.close();
|
|
242
|
-
}
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (this.handleIbcRewindIfNeeded({
|
|
88
|
+
sourceChainId: history.chainId,
|
|
89
|
+
ibcHistory: history.ibcHistory,
|
|
90
|
+
packetTimeout: history.packetTimeout,
|
|
91
|
+
swapContext: "swapReceiver" in history && "swapChannelIndex" in history
|
|
92
|
+
? {
|
|
93
|
+
swapReceiver: history.swapReceiver,
|
|
94
|
+
swapChannelIndex: history.swapChannelIndex,
|
|
95
|
+
setSwapRefundInfo: (refundInfo) => {
|
|
96
|
+
(0, mobx_1.runInAction)(() => {
|
|
97
|
+
history.swapRefundInfo = refundInfo;
|
|
243
98
|
});
|
|
244
|
-
|
|
99
|
+
},
|
|
100
|
+
}
|
|
101
|
+
: undefined,
|
|
102
|
+
onFulfill,
|
|
103
|
+
onClose,
|
|
104
|
+
onError,
|
|
105
|
+
onRewindComplete: () => {
|
|
106
|
+
this.trackIBCPacketForwardingRecursive((onFulfill, onClose, onError) => {
|
|
107
|
+
this.trackIBCPacketForwardingRecursiveInternal(id, onFulfill, onClose, onError);
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
})) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
this.trackIbcHopFlowWithTimeout({
|
|
114
|
+
ibcHistory: history.ibcHistory,
|
|
115
|
+
sourceChainId: history.chainId,
|
|
116
|
+
swapReceiver: "swapReceiver" in history ? history.swapReceiver : undefined,
|
|
117
|
+
onHopCompleted: (resAmount) => {
|
|
118
|
+
(0, mobx_1.runInAction)(() => {
|
|
119
|
+
if (resAmount && "resAmount" in history) {
|
|
120
|
+
history.resAmount.push(resAmount);
|
|
245
121
|
}
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
};
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
? res.txs.map((res) => res.tx_result || res)
|
|
265
|
-
: [res.tx_result || res];
|
|
266
|
-
if (txs && Array.isArray(txs)) {
|
|
267
|
-
(0, mobx_1.runInAction)(() => {
|
|
268
|
-
targetChannel.completed = true;
|
|
269
|
-
for (const tx of txs) {
|
|
270
|
-
try {
|
|
271
|
-
const ack = this.getIBCWriteAcknowledgementAckFromTx(tx, targetChannel.portId, targetChannel.channelId, targetChannel.sequence);
|
|
272
|
-
if (ack && ack.length > 0) {
|
|
273
|
-
const str = buffer_1.Buffer.from(ack);
|
|
274
|
-
try {
|
|
275
|
-
const decoded = JSON.parse(str.toString());
|
|
276
|
-
if (decoded.error) {
|
|
277
|
-
// XXX: {key: 'packet_ack', value: '{"error":"ABCI code: 6: error handling packet: see events for details"}'}
|
|
278
|
-
// 오류가 있을 경우 이딴식으로 오류가 나오기 때문에 뭐 유저에게 보여줄 방법이 없다...
|
|
279
|
-
targetChannel.error = "Packet processing failed";
|
|
280
|
-
onFulfillOnce();
|
|
281
|
-
this.trackIBCPacketForwardingRecursive(id);
|
|
282
|
-
break;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
catch (e) {
|
|
286
|
-
// decode가 실패한 경우 사실 방법이 없다.
|
|
287
|
-
// 일단 packet이 성공했다고 치고 진행한다.
|
|
288
|
-
console.log(e);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
// Because a tx can contain multiple messages, it's hard to know exactly which event we want.
|
|
292
|
-
// But logically, the events closest to the recv_packet event is the events we want.
|
|
293
|
-
const index = this.getIBCRecvPacketIndexFromTx(tx, targetChannel.portId, targetChannel.channelId, targetChannel.sequence);
|
|
294
|
-
if (index >= 0) {
|
|
295
|
-
if ("swapReceiver" in history) {
|
|
296
|
-
const res = this.getIBCSwapResAmountFromTx(tx, history.swapReceiver[targetChannelIndex + 1], index);
|
|
297
|
-
history.resAmount.push(res);
|
|
298
|
-
}
|
|
299
|
-
if (nextChannel) {
|
|
300
|
-
nextChannel.sequence = this.getIBCPacketSequenceFromTx(tx, nextChannel.portId, nextChannel.channelId, index);
|
|
301
|
-
nextChannel.dstChannelId = this.getDstChannelIdFromTx(tx, nextChannel.portId, nextChannel.channelId, index);
|
|
302
|
-
onFulfillOnce();
|
|
303
|
-
this.trackIBCPacketForwardingRecursive(id);
|
|
304
|
-
break;
|
|
305
|
-
}
|
|
306
|
-
else {
|
|
307
|
-
// Packet received to destination chain.
|
|
308
|
-
if (history.notificationInfo && !history.notified) {
|
|
309
|
-
(0, mobx_1.runInAction)(() => {
|
|
310
|
-
history.notified = true;
|
|
311
|
-
});
|
|
312
|
-
const chainInfo = this.chainsService.getChainInfo(history.destinationChainId);
|
|
313
|
-
if (chainInfo) {
|
|
314
|
-
if ("swapType" in history) {
|
|
315
|
-
if (history.resAmount.length > 0) {
|
|
316
|
-
const amount = history.resAmount[history.resAmount.length - 1];
|
|
317
|
-
const assetsText = amount
|
|
318
|
-
.filter((amt) => history.notificationInfo.currencies.find((cur) => cur.coinMinimalDenom === amt.denom))
|
|
319
|
-
.map((amt) => {
|
|
320
|
-
const currency = history.notificationInfo.currencies.find((cur) => cur.coinMinimalDenom === amt.denom);
|
|
321
|
-
return new unit_1.CoinPretty(currency, amt.amount)
|
|
322
|
-
.hideIBCMetadata(true)
|
|
323
|
-
.shrink(true)
|
|
324
|
-
.maxDecimals(6)
|
|
325
|
-
.inequalitySymbol(true)
|
|
326
|
-
.trim(true)
|
|
327
|
-
.toString();
|
|
328
|
-
});
|
|
329
|
-
if (assetsText.length > 0) {
|
|
330
|
-
// Notify user
|
|
331
|
-
this.notification.create({
|
|
332
|
-
iconRelativeUrl: "assets/logo-256.png",
|
|
333
|
-
title: "IBC Swap Succeeded",
|
|
334
|
-
message: `${assetsText.join(", ")} received on ${chainInfo.chainName}`,
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
else {
|
|
340
|
-
const assetsText = history.amount
|
|
341
|
-
.filter((amt) => history.notificationInfo.currencies.find((cur) => cur.coinMinimalDenom === amt.denom))
|
|
342
|
-
.map((amt) => {
|
|
343
|
-
const currency = history.notificationInfo.currencies.find((cur) => cur.coinMinimalDenom === amt.denom);
|
|
344
|
-
return new unit_1.CoinPretty(currency, amt.amount)
|
|
345
|
-
.hideIBCMetadata(true)
|
|
346
|
-
.shrink(true)
|
|
347
|
-
.maxDecimals(6)
|
|
348
|
-
.inequalitySymbol(true)
|
|
349
|
-
.trim(true)
|
|
350
|
-
.toString();
|
|
351
|
-
});
|
|
352
|
-
if (assetsText.length > 0) {
|
|
353
|
-
// Notify user
|
|
354
|
-
this.notification.create({
|
|
355
|
-
iconRelativeUrl: "assets/logo-256.png",
|
|
356
|
-
title: "IBC Transfer Succeeded",
|
|
357
|
-
message: `${assetsText.join(", ")} sent to ${chainInfo.chainName}`,
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
onFulfillOnce();
|
|
364
|
-
break;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
catch (_a) {
|
|
369
|
-
// noop
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
onAllCompleted: () => {
|
|
125
|
+
const notificationInfo = history.notificationInfo;
|
|
126
|
+
if (notificationInfo && !history.notified) {
|
|
127
|
+
(0, mobx_1.runInAction)(() => {
|
|
128
|
+
history.notified = true;
|
|
129
|
+
});
|
|
130
|
+
const chainInfo = this.chainsService.getChainInfo(history.destinationChainId);
|
|
131
|
+
if (chainInfo) {
|
|
132
|
+
if ("swapType" in history) {
|
|
133
|
+
if (history.resAmount.length > 0) {
|
|
134
|
+
const amount = history.resAmount[history.resAmount.length - 1];
|
|
135
|
+
const assetsText = amount
|
|
136
|
+
.map((amt) => {
|
|
137
|
+
const currency = notificationInfo.currencies.find((cur) => cur.coinMinimalDenom === amt.denom);
|
|
138
|
+
if (!currency) {
|
|
139
|
+
return undefined;
|
|
370
140
|
}
|
|
141
|
+
return new unit_1.CoinPretty(currency, amt.amount)
|
|
142
|
+
.hideIBCMetadata(true)
|
|
143
|
+
.shrink(true)
|
|
144
|
+
.maxDecimals(6)
|
|
145
|
+
.inequalitySymbol(true)
|
|
146
|
+
.trim(true)
|
|
147
|
+
.toString();
|
|
148
|
+
})
|
|
149
|
+
.filter((text) => Boolean(text));
|
|
150
|
+
if (assetsText.length > 0) {
|
|
151
|
+
this.notification.create({
|
|
152
|
+
iconRelativeUrl: "assets/logo-256.png",
|
|
153
|
+
title: "IBC Swap Succeeded",
|
|
154
|
+
message: `${assetsText.join(", ")} received on ${chainInfo.chainName}`,
|
|
155
|
+
});
|
|
371
156
|
}
|
|
372
|
-
}
|
|
157
|
+
}
|
|
373
158
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
const txTracer = new cosmos_1.TendermintTxTracer(prevChainInfo.rpc, "/websocket");
|
|
397
|
-
closables.push(txTracer);
|
|
398
|
-
txTracer.addEventListener("close", onCloseOnce);
|
|
399
|
-
txTracer.addEventListener("error", onErrorOnce);
|
|
400
|
-
txTracer.traceTx(queryEvents).then((res) => {
|
|
401
|
-
txTracer.close();
|
|
402
|
-
if (!res) {
|
|
403
|
-
return;
|
|
159
|
+
else {
|
|
160
|
+
const assetsText = history.amount
|
|
161
|
+
.map((amt) => {
|
|
162
|
+
const currency = notificationInfo.currencies.find((cur) => cur.coinMinimalDenom === amt.denom);
|
|
163
|
+
if (!currency) {
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
return new unit_1.CoinPretty(currency, amt.amount)
|
|
167
|
+
.hideIBCMetadata(true)
|
|
168
|
+
.shrink(true)
|
|
169
|
+
.maxDecimals(6)
|
|
170
|
+
.inequalitySymbol(true)
|
|
171
|
+
.trim(true)
|
|
172
|
+
.toString();
|
|
173
|
+
})
|
|
174
|
+
.filter((text) => Boolean(text));
|
|
175
|
+
if (assetsText.length > 0) {
|
|
176
|
+
this.notification.create({
|
|
177
|
+
iconRelativeUrl: "assets/logo-256.png",
|
|
178
|
+
title: "IBC Transfer Succeeded",
|
|
179
|
+
message: `${assetsText.join(", ")} sent to ${chainInfo.chainName}`,
|
|
180
|
+
});
|
|
404
181
|
}
|
|
405
|
-
|
|
406
|
-
// 이 경우 따로 정보를 얻을 필요는 없으므로 이후에 res를 쓰지는 않는다.
|
|
407
|
-
// 위에 res null check는 사실 필요 없지만 혹시나 해서 넣어둔다.
|
|
408
|
-
(0, mobx_1.runInAction)(() => {
|
|
409
|
-
targetChannel.error = "Packet timeout";
|
|
410
|
-
history.packetTimeout = true;
|
|
411
|
-
onFulfillOnce();
|
|
412
|
-
this.trackIBCPacketForwardingRecursive(id);
|
|
413
|
-
});
|
|
414
|
-
});
|
|
182
|
+
}
|
|
415
183
|
}
|
|
416
184
|
}
|
|
417
|
-
}
|
|
418
|
-
|
|
185
|
+
},
|
|
186
|
+
onContinue: () => {
|
|
187
|
+
this.trackIBCPacketForwardingRecursive((onFulfill, onClose, onError) => {
|
|
188
|
+
this.trackIBCPacketForwardingRecursiveInternal(id, onFulfill, onClose, onError);
|
|
189
|
+
});
|
|
190
|
+
},
|
|
191
|
+
onRetry: () => {
|
|
192
|
+
this.trackIBCPacketForwardingRecursive((onFulfill, onClose, onError) => {
|
|
193
|
+
this.trackIBCPacketForwardingRecursiveInternal(id, onFulfill, onClose, onError);
|
|
194
|
+
});
|
|
195
|
+
},
|
|
196
|
+
onPacketTimeout: () => {
|
|
197
|
+
(0, mobx_1.runInAction)(() => {
|
|
198
|
+
history.packetTimeout = true;
|
|
199
|
+
});
|
|
200
|
+
},
|
|
201
|
+
onFulfill,
|
|
202
|
+
onClose,
|
|
203
|
+
onError,
|
|
204
|
+
});
|
|
419
205
|
};
|
|
420
|
-
this.
|
|
421
|
-
const
|
|
206
|
+
this.checkAndTrackSwapTxFulfilledRecursive = (params) => {
|
|
207
|
+
const { chainId, txHash, onSuccess, onPending, onFailed, onError } = params;
|
|
208
|
+
const chainInfo = this.chainsService.getChainInfo(chainId);
|
|
422
209
|
if (!chainInfo) {
|
|
423
|
-
|
|
210
|
+
onFailed();
|
|
424
211
|
return;
|
|
425
212
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
jsonrpc: "2.0",
|
|
440
|
-
method: "eth_getTransactionReceipt",
|
|
441
|
-
params: [history.txHash],
|
|
442
|
-
id: 1,
|
|
443
|
-
}),
|
|
444
|
-
})
|
|
445
|
-
.then((res) => {
|
|
446
|
-
const txReceipt = res.data.result;
|
|
447
|
-
if (txReceipt) {
|
|
448
|
-
if (txReceipt.status === types_1.EthTxStatus.Success) {
|
|
449
|
-
setTimeout(() => {
|
|
450
|
-
(0, simple_fetch_1.simpleFetch)(SWAP_API_ENDPOINT, "/v1/swap/tx", {
|
|
451
|
-
method: "POST",
|
|
452
|
-
headers: Object.assign({ "content-type": "application/json" }, (() => {
|
|
453
|
-
const res = {};
|
|
454
|
-
if (process.env["SKIP_API_KEY"]) {
|
|
455
|
-
res.authorization = process.env["SKIP_API_KEY"];
|
|
456
|
-
}
|
|
457
|
-
return res;
|
|
458
|
-
})()),
|
|
459
|
-
body: JSON.stringify({
|
|
460
|
-
tx_hash: history.txHash,
|
|
461
|
-
chain_id: history.chainId.replace("eip155:", ""),
|
|
462
|
-
}),
|
|
463
|
-
})
|
|
464
|
-
.then((result) => {
|
|
465
|
-
console.log(`Skip tx track result: ${JSON.stringify(result)}`);
|
|
466
|
-
onFulfill(true);
|
|
467
|
-
})
|
|
468
|
-
.catch((e) => {
|
|
469
|
-
console.log(e);
|
|
470
|
-
this.removeRecentSkipHistory(history.id);
|
|
471
|
-
onFulfill(false);
|
|
472
|
-
});
|
|
473
|
-
}, 2000);
|
|
474
|
-
}
|
|
475
|
-
else {
|
|
476
|
-
// tx가 실패한거면 종료
|
|
477
|
-
this.removeRecentSkipHistory(history.id);
|
|
478
|
-
onFulfill(false);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
else {
|
|
482
|
-
onError();
|
|
483
|
-
}
|
|
484
|
-
})
|
|
485
|
-
.catch(() => {
|
|
486
|
-
// 오류가 발생하면 종료
|
|
487
|
-
onFulfill(false);
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
else {
|
|
491
|
-
const txTracer = new cosmos_1.TendermintTxTracer(chainInfo.rpc, "/websocket");
|
|
492
|
-
txTracer.addEventListener("error", () => onFulfill(false));
|
|
493
|
-
txTracer
|
|
494
|
-
.traceTx(buffer_1.Buffer.from(history.txHash.replace("0x", ""), "hex"))
|
|
495
|
-
.then((res) => {
|
|
496
|
-
txTracer.close();
|
|
497
|
-
let txResult;
|
|
498
|
-
if (Array.isArray(res.txs)) {
|
|
499
|
-
if (res.txs && res.txs.length > 0) {
|
|
500
|
-
txResult = res.txs[0].tx_result;
|
|
501
|
-
}
|
|
502
|
-
else {
|
|
503
|
-
// In case tx is not confirmed, just wait for next check
|
|
504
|
-
onError();
|
|
505
|
-
return;
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
else {
|
|
509
|
-
txResult = res;
|
|
510
|
-
}
|
|
511
|
-
if (!txResult || typeof txResult.code !== "number") {
|
|
213
|
+
this.resolveTxExecutionStatus(chainInfo, chainId, txHash)
|
|
214
|
+
.then((status) => {
|
|
215
|
+
switch (status) {
|
|
216
|
+
case "success":
|
|
217
|
+
onSuccess();
|
|
218
|
+
break;
|
|
219
|
+
case "pending":
|
|
220
|
+
onPending();
|
|
221
|
+
break;
|
|
222
|
+
case "failed":
|
|
223
|
+
onFailed();
|
|
224
|
+
break;
|
|
225
|
+
default:
|
|
512
226
|
onError();
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
headers: Object.assign({ "content-type": "application/json" }, (() => {
|
|
520
|
-
const res = {};
|
|
521
|
-
if (process.env["SKIP_API_KEY"]) {
|
|
522
|
-
res.authorization = process.env["SKIP_API_KEY"];
|
|
523
|
-
}
|
|
524
|
-
return res;
|
|
525
|
-
})()),
|
|
526
|
-
body: JSON.stringify({
|
|
527
|
-
tx_hash: history.txHash,
|
|
528
|
-
chain_id: history.chainId,
|
|
529
|
-
}),
|
|
530
|
-
})
|
|
531
|
-
.then((result) => {
|
|
532
|
-
console.log(`Skip tx track result: ${JSON.stringify(result)}`);
|
|
533
|
-
onFulfill(true);
|
|
534
|
-
})
|
|
535
|
-
.catch((e) => {
|
|
536
|
-
console.log(e);
|
|
537
|
-
this.removeRecentSkipHistory(history.id);
|
|
538
|
-
onFulfill(false);
|
|
539
|
-
});
|
|
540
|
-
}, 2000);
|
|
541
|
-
}
|
|
542
|
-
else {
|
|
543
|
-
// tx가 실패한거면 종료
|
|
544
|
-
this.removeRecentSkipHistory(history.id);
|
|
545
|
-
onFulfill(false);
|
|
546
|
-
}
|
|
547
|
-
})
|
|
548
|
-
.catch(() => {
|
|
549
|
-
// 오류가 발생하면 종료
|
|
550
|
-
onFulfill(false);
|
|
551
|
-
});
|
|
552
|
-
}
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
})
|
|
230
|
+
.catch(() => {
|
|
231
|
+
onError();
|
|
232
|
+
});
|
|
553
233
|
};
|
|
554
234
|
this.checkAndUpdateSkipSwapHistoryRecursive = (id, onFulfill, onError) => {
|
|
555
235
|
const history = this.getRecentSkipHistory(id);
|
|
@@ -576,21 +256,10 @@ class RecentSendHistoryService {
|
|
|
576
256
|
onFulfill();
|
|
577
257
|
return;
|
|
578
258
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
};
|
|
584
|
-
const requestParams = new URLSearchParams(request).toString();
|
|
585
|
-
(0, simple_fetch_1.simpleFetch)(SWAP_API_ENDPOINT, `/v1/swap/tx?${requestParams}`, {
|
|
586
|
-
method: "GET",
|
|
587
|
-
headers: Object.assign({ "content-type": "application/json" }, (() => {
|
|
588
|
-
const res = {};
|
|
589
|
-
if (process.env["SKIP_API_KEY"]) {
|
|
590
|
-
res.authorization = process.env["SKIP_API_KEY"];
|
|
591
|
-
}
|
|
592
|
-
return res;
|
|
593
|
-
})()),
|
|
259
|
+
(0, api_1.requestSkipTxStatus)({
|
|
260
|
+
endpoint: SWAP_API_ENDPOINT,
|
|
261
|
+
chainId: chainId.replace("eip155:", ""),
|
|
262
|
+
txHash,
|
|
594
263
|
})
|
|
595
264
|
.then((res) => {
|
|
596
265
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
@@ -835,7 +504,7 @@ class RecentSendHistoryService {
|
|
|
835
504
|
history.routeIndex = simpleRoute.length - 1;
|
|
836
505
|
}
|
|
837
506
|
if (receiveTxHash) {
|
|
838
|
-
this.
|
|
507
|
+
this.trackSkipDestinationAssetAmount(id, receiveTxHash, onFulfill);
|
|
839
508
|
}
|
|
840
509
|
else {
|
|
841
510
|
history.trackDone = true;
|
|
@@ -854,36 +523,35 @@ class RecentSendHistoryService {
|
|
|
854
523
|
onError();
|
|
855
524
|
});
|
|
856
525
|
};
|
|
526
|
+
// ============================================================================
|
|
527
|
+
// Chain removed handler
|
|
528
|
+
// ============================================================================
|
|
857
529
|
this.onChainRemoved = (chainInfo) => {
|
|
858
530
|
const chainIdentifier = cosmos_1.ChainIdHelper.parse(chainInfo.chainId).identifier;
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
chainIdentifier) {
|
|
868
|
-
removingIds.push(history.id);
|
|
869
|
-
continue;
|
|
870
|
-
}
|
|
871
|
-
if (history.ibcHistory.some((history) => {
|
|
872
|
-
return (cosmos_1.ChainIdHelper.parse(history.counterpartyChainId).identifier ===
|
|
873
|
-
chainIdentifier);
|
|
874
|
-
})) {
|
|
875
|
-
removingIds.push(history.id);
|
|
876
|
-
continue;
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
for (const id of removingIds) {
|
|
880
|
-
this.recentIBCHistoryMap.delete(id);
|
|
881
|
-
}
|
|
882
|
-
});
|
|
531
|
+
try {
|
|
532
|
+
this.removeIBCHistoriesByChainIdentifier(chainIdentifier);
|
|
533
|
+
this.removeSkipHistoriesByChainIdentifier(chainIdentifier);
|
|
534
|
+
this.removeSwapV2HistoriesByChainIdentifier(chainIdentifier);
|
|
535
|
+
}
|
|
536
|
+
catch (e) {
|
|
537
|
+
console.error(e);
|
|
538
|
+
}
|
|
883
539
|
};
|
|
884
540
|
(0, mobx_1.makeObservable)(this);
|
|
885
541
|
}
|
|
542
|
+
// ============================================================================
|
|
543
|
+
// Init – load & persist histories
|
|
544
|
+
// ============================================================================
|
|
886
545
|
init() {
|
|
546
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
547
|
+
yield this.initRecentSendHistory();
|
|
548
|
+
yield this.initRecentIBCHistory();
|
|
549
|
+
yield this.initRecentSkipHistory();
|
|
550
|
+
yield this.initRecentSwapV2History();
|
|
551
|
+
this.chainsService.addChainRemovedHandler(this.onChainRemoved);
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
initRecentSendHistory() {
|
|
887
555
|
return __awaiter(this, void 0, void 0, function* () {
|
|
888
556
|
const recentSendHistoryMapSaved = yield this.kvStore.get("recentSendHistoryMap");
|
|
889
557
|
if (recentSendHistoryMapSaved) {
|
|
@@ -898,6 +566,10 @@ class RecentSendHistoryService {
|
|
|
898
566
|
const obj = Object.fromEntries(js);
|
|
899
567
|
this.kvStore.set("recentSendHistoryMap", obj);
|
|
900
568
|
});
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
initRecentIBCHistory() {
|
|
572
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
901
573
|
// 밑의 storage의 key들이 ibc transfer를 포함하는데
|
|
902
574
|
// 이 이유는 이전에 transfer history만 지원되었을때
|
|
903
575
|
// key를 그렇게 정했었기 때문이다
|
|
@@ -934,8 +606,14 @@ class RecentSendHistoryService {
|
|
|
934
606
|
this.kvStore.set("recentIBCTransferHistoryMap", obj);
|
|
935
607
|
});
|
|
936
608
|
for (const history of this.getRecentIBCHistories()) {
|
|
937
|
-
this.trackIBCPacketForwardingRecursive(
|
|
609
|
+
this.trackIBCPacketForwardingRecursive((onFulfill, onClose, onError) => {
|
|
610
|
+
this.trackIBCPacketForwardingRecursiveInternal(history.id, onFulfill, onClose, onError);
|
|
611
|
+
});
|
|
938
612
|
}
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
initRecentSkipHistory() {
|
|
616
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
939
617
|
// Load skip history sequence from the storage
|
|
940
618
|
const recentSkipHistorySeqSaved = yield this.kvStore.get("recentSkipHistorySeq");
|
|
941
619
|
if (recentSkipHistorySeqSaved) {
|
|
@@ -972,9 +650,51 @@ class RecentSendHistoryService {
|
|
|
972
650
|
for (const history of this.getRecentSkipHistories()) {
|
|
973
651
|
this.trackSkipSwapRecursive(history.id);
|
|
974
652
|
}
|
|
975
|
-
this.chainsService.addChainRemovedHandler(this.onChainRemoved);
|
|
976
653
|
});
|
|
977
654
|
}
|
|
655
|
+
initRecentSwapV2History() {
|
|
656
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
657
|
+
const recentSwapV2HistorySeqSaved = yield this.kvStore.get("recentSwapV2HistorySeq");
|
|
658
|
+
if (recentSwapV2HistorySeqSaved) {
|
|
659
|
+
(0, mobx_1.runInAction)(() => {
|
|
660
|
+
this.recentSwapV2HistorySeq = recentSwapV2HistorySeqSaved;
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
// Save the swap v2 history sequence to the storage when the swap v2 history sequence is changed
|
|
664
|
+
(0, mobx_1.autorun)(() => {
|
|
665
|
+
const js = (0, mobx_1.toJS)(this.recentSwapV2HistorySeq);
|
|
666
|
+
this.kvStore.set("recentSwapV2HistorySeq", js);
|
|
667
|
+
});
|
|
668
|
+
// Load swap v2 history from the storage
|
|
669
|
+
const recentSwapV2HistoryMapSaved = yield this.kvStore.get("recentSwapV2HistoryMap");
|
|
670
|
+
if (recentSwapV2HistoryMapSaved) {
|
|
671
|
+
(0, mobx_1.runInAction)(() => {
|
|
672
|
+
let entries = Object.entries(recentSwapV2HistoryMapSaved);
|
|
673
|
+
entries = entries.sort(([, a], [, b]) => {
|
|
674
|
+
return parseInt(a.id) - parseInt(b.id);
|
|
675
|
+
});
|
|
676
|
+
for (const [key, value] of entries) {
|
|
677
|
+
this.recentSwapV2HistoryMap.set(key, value);
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
// Save the swap v2 history to the storage when the swap v2 history is changed
|
|
682
|
+
(0, mobx_1.autorun)(() => {
|
|
683
|
+
const js = (0, mobx_1.toJS)(this.recentSwapV2HistoryMap);
|
|
684
|
+
const obj = Object.fromEntries(js);
|
|
685
|
+
this.kvStore.set("recentSwapV2HistoryMap", obj);
|
|
686
|
+
});
|
|
687
|
+
for (const history of this.getRecentSwapV2Histories()) {
|
|
688
|
+
this.trackSwapV2Recursive(history.id);
|
|
689
|
+
if (history.additionalTrackingData && !history.additionalTrackDone) {
|
|
690
|
+
this.trackSwapV2AdditionalRecursive(history.id);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
// ============================================================================
|
|
696
|
+
// Send tx and record
|
|
697
|
+
// ============================================================================
|
|
978
698
|
sendTxAndRecord(type, sourceChainId, destinationChainId, tx, mode, silent, sender, recipient, amount, memo, ibcChannels, notificationInfo, shouldLegacyTrack = false) {
|
|
979
699
|
var _a, _b;
|
|
980
700
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1004,19 +724,10 @@ class RecentSendHistoryService {
|
|
|
1004
724
|
if (shouldLegacyTrack) {
|
|
1005
725
|
// no wait
|
|
1006
726
|
setTimeout(() => {
|
|
1007
|
-
(0,
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
if (process.env["SKIP_API_KEY"]) {
|
|
1012
|
-
res.authorization = process.env["SKIP_API_KEY"];
|
|
1013
|
-
}
|
|
1014
|
-
return res;
|
|
1015
|
-
})()),
|
|
1016
|
-
body: JSON.stringify({
|
|
1017
|
-
tx_hash: buffer_1.Buffer.from(tx.hash).toString("hex"),
|
|
1018
|
-
chain_id: sourceChainId,
|
|
1019
|
-
}),
|
|
727
|
+
(0, api_1.requestSkipTxTrack)({
|
|
728
|
+
endpoint: SWAP_API_ENDPOINT,
|
|
729
|
+
chainId: sourceChainId,
|
|
730
|
+
txHash: buffer_1.Buffer.from(tx.hash).toString("hex"),
|
|
1020
731
|
})
|
|
1021
732
|
.then((result) => {
|
|
1022
733
|
console.log(`Skip tx track result: ${JSON.stringify(result)}`);
|
|
@@ -1032,11 +743,29 @@ class RecentSendHistoryService {
|
|
|
1032
743
|
});
|
|
1033
744
|
if (ibcChannels && ibcChannels.length > 0) {
|
|
1034
745
|
const id = this.addRecentIBCTransferHistory(sourceChainId, destinationChainId, sender, recipient, amount, memo, ibcChannels, notificationInfo, txHash);
|
|
1035
|
-
this.trackIBCPacketForwardingRecursive(
|
|
746
|
+
this.trackIBCPacketForwardingRecursive((onFulfill, onClose, onError) => {
|
|
747
|
+
this.trackIBCPacketForwardingRecursiveInternal(id, onFulfill, onClose, onError);
|
|
748
|
+
});
|
|
1036
749
|
}
|
|
1037
750
|
return txHash;
|
|
1038
751
|
});
|
|
1039
752
|
}
|
|
753
|
+
getRecentSendHistories(chainId, type) {
|
|
754
|
+
var _a;
|
|
755
|
+
const key = `${cosmos_1.ChainIdHelper.parse(chainId).identifier}/${type}`;
|
|
756
|
+
return ((_a = this.recentSendHistoryMap.get(key)) !== null && _a !== void 0 ? _a : []).slice(0, 20);
|
|
757
|
+
}
|
|
758
|
+
addRecentSendHistory(chainId, type, history) {
|
|
759
|
+
var _a;
|
|
760
|
+
const key = `${cosmos_1.ChainIdHelper.parse(chainId).identifier}/${type}`;
|
|
761
|
+
let histories = (_a = this.recentSendHistoryMap.get(key)) !== null && _a !== void 0 ? _a : [];
|
|
762
|
+
histories.unshift(Object.assign({ timestamp: Date.now() }, history));
|
|
763
|
+
histories = histories.slice(0, 20);
|
|
764
|
+
this.recentSendHistoryMap.set(key, histories);
|
|
765
|
+
}
|
|
766
|
+
// ============================================================================
|
|
767
|
+
// Send tx and record IBC swap/transfer
|
|
768
|
+
// ============================================================================
|
|
1040
769
|
sendTxAndRecordIBCSwap(swapType, sourceChainId, destinationChainId, tx, mode, silent, sender, amount, memo, ibcChannels, destinationAsset, swapChannelIndex, swapReceiver, notificationInfo, shouldLegacyTrack = false) {
|
|
1041
770
|
var _a;
|
|
1042
771
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1051,19 +780,10 @@ class RecentSendHistoryService {
|
|
|
1051
780
|
if (shouldLegacyTrack) {
|
|
1052
781
|
setTimeout(() => {
|
|
1053
782
|
// no wait
|
|
1054
|
-
(0,
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
if (process.env["SKIP_API_KEY"]) {
|
|
1059
|
-
res.authorization = process.env["SKIP_API_KEY"];
|
|
1060
|
-
}
|
|
1061
|
-
return res;
|
|
1062
|
-
})()),
|
|
1063
|
-
body: JSON.stringify({
|
|
1064
|
-
tx_hash: buffer_1.Buffer.from(tx.hash).toString("hex"),
|
|
1065
|
-
chain_id: sourceChainId,
|
|
1066
|
-
}),
|
|
783
|
+
(0, api_1.requestSkipTxTrack)({
|
|
784
|
+
endpoint: SWAP_API_ENDPOINT,
|
|
785
|
+
chainId: sourceChainId,
|
|
786
|
+
txHash: buffer_1.Buffer.from(tx.hash).toString("hex"),
|
|
1067
787
|
})
|
|
1068
788
|
.then((result) => {
|
|
1069
789
|
console.log(`Skip tx track result: ${JSON.stringify(result)}`);
|
|
@@ -1079,49 +799,13 @@ class RecentSendHistoryService {
|
|
|
1079
799
|
});
|
|
1080
800
|
if (shouldLegacyTrack) {
|
|
1081
801
|
const id = this.addRecentIBCSwapHistory(swapType, sourceChainId, destinationChainId, sender, amount, memo, ibcChannels, destinationAsset, swapChannelIndex, swapReceiver, notificationInfo, txHash);
|
|
1082
|
-
this.trackIBCPacketForwardingRecursive(
|
|
802
|
+
this.trackIBCPacketForwardingRecursive((onFulfill, onClose, onError) => {
|
|
803
|
+
this.trackIBCPacketForwardingRecursiveInternal(id, onFulfill, onClose, onError);
|
|
804
|
+
});
|
|
1083
805
|
}
|
|
1084
806
|
return txHash;
|
|
1085
807
|
});
|
|
1086
808
|
}
|
|
1087
|
-
trackIBCPacketForwardingRecursive(id) {
|
|
1088
|
-
(0, common_1.retry)(() => {
|
|
1089
|
-
return new Promise((resolve, reject) => {
|
|
1090
|
-
this.trackIBCPacketForwardingRecursiveInternal(id, () => {
|
|
1091
|
-
resolve();
|
|
1092
|
-
}, () => {
|
|
1093
|
-
// reject if ws closed before fulfilled
|
|
1094
|
-
// 하지만 로직상 fulfill 되기 전에 ws가 닫히는게 되기 때문에
|
|
1095
|
-
// delay를 좀 준다.
|
|
1096
|
-
// 현재 trackIBCPacketForwardingRecursiveInternal에 ws close 이후에는 동기적인 로직밖에 없으므로
|
|
1097
|
-
// 문제될게 없다.
|
|
1098
|
-
setTimeout(() => {
|
|
1099
|
-
reject();
|
|
1100
|
-
}, 500);
|
|
1101
|
-
}, () => {
|
|
1102
|
-
// reject if ws error occurred before fulfilled
|
|
1103
|
-
reject();
|
|
1104
|
-
});
|
|
1105
|
-
});
|
|
1106
|
-
}, {
|
|
1107
|
-
maxRetries: 10,
|
|
1108
|
-
waitMsAfterError: 10 * 1000,
|
|
1109
|
-
maxWaitMsAfterError: 5 * 60 * 1000, // 5min
|
|
1110
|
-
});
|
|
1111
|
-
}
|
|
1112
|
-
getRecentSendHistories(chainId, type) {
|
|
1113
|
-
var _a;
|
|
1114
|
-
const key = `${cosmos_1.ChainIdHelper.parse(chainId).identifier}/${type}`;
|
|
1115
|
-
return ((_a = this.recentSendHistoryMap.get(key)) !== null && _a !== void 0 ? _a : []).slice(0, 20);
|
|
1116
|
-
}
|
|
1117
|
-
addRecentSendHistory(chainId, type, history) {
|
|
1118
|
-
var _a;
|
|
1119
|
-
const key = `${cosmos_1.ChainIdHelper.parse(chainId).identifier}/${type}`;
|
|
1120
|
-
let histories = (_a = this.recentSendHistoryMap.get(key)) !== null && _a !== void 0 ? _a : [];
|
|
1121
|
-
histories.unshift(Object.assign({ timestamp: Date.now() }, history));
|
|
1122
|
-
histories = histories.slice(0, 20);
|
|
1123
|
-
this.recentSendHistoryMap.set(key, histories);
|
|
1124
|
-
}
|
|
1125
809
|
addRecentIBCTransferHistory(chainId, destinationChainId, sender, recipient, amount, memo, ibcChannels, notificationInfo, txHash) {
|
|
1126
810
|
const id = (this.recentIBCHistorySeq++).toString();
|
|
1127
811
|
const history = {
|
|
@@ -1201,16 +885,311 @@ class RecentSendHistoryService {
|
|
|
1201
885
|
clearAllRecentIBCHistory() {
|
|
1202
886
|
this.recentIBCHistoryMap.clear();
|
|
1203
887
|
}
|
|
1204
|
-
//
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
888
|
+
// ============================================================================
|
|
889
|
+
// Common functions for history tracking (IBC, Skip, Swap V2)
|
|
890
|
+
// ============================================================================
|
|
891
|
+
trackIBCPacketForwardingRecursive(trackHandler) {
|
|
892
|
+
(0, common_1.retry)(() => {
|
|
893
|
+
return new Promise((resolve, reject) => {
|
|
894
|
+
trackHandler(() => {
|
|
895
|
+
resolve();
|
|
896
|
+
}, () => {
|
|
897
|
+
// reject if ws closed before fulfilled
|
|
898
|
+
// 하지만 로직상 fulfill 되기 전에 ws가 닫히는게 되기 때문에
|
|
899
|
+
// delay를 좀 준다.
|
|
900
|
+
// 현재 trackIBCPacketForwardingRecursiveInternal에 ws close 이후에는 동기적인 로직밖에 없으므로
|
|
901
|
+
// 문제될게 없다.
|
|
902
|
+
setTimeout(() => {
|
|
903
|
+
reject();
|
|
904
|
+
}, 500);
|
|
905
|
+
}, () => {
|
|
906
|
+
// reject if ws error occurred before fulfilled
|
|
907
|
+
reject();
|
|
908
|
+
});
|
|
909
|
+
});
|
|
910
|
+
}, {
|
|
911
|
+
maxRetries: 10,
|
|
912
|
+
waitMsAfterError: 10 * 1000,
|
|
913
|
+
maxWaitMsAfterError: 5 * 60 * 1000, // 5min
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
resolveTxExecutionStatus(chainInfo, chainId, txHash) {
|
|
917
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
918
|
+
if (this.chainsService.isEvmChain(chainId)) {
|
|
919
|
+
const evmInfo = chainInfo.evm;
|
|
920
|
+
if (!evmInfo) {
|
|
921
|
+
return Promise.resolve("error");
|
|
922
|
+
}
|
|
923
|
+
const res = yield (0, api_1.requestEthTxReceipt)({
|
|
924
|
+
rpc: evmInfo.rpc,
|
|
925
|
+
txHash,
|
|
926
|
+
origin,
|
|
927
|
+
});
|
|
928
|
+
if (res.data.error) {
|
|
929
|
+
return "error";
|
|
930
|
+
}
|
|
931
|
+
const txReceipt = res.data.result;
|
|
932
|
+
if (!txReceipt) {
|
|
933
|
+
return "pending";
|
|
934
|
+
}
|
|
935
|
+
if (txReceipt.status === types_2.EthTxStatus.Success) {
|
|
936
|
+
return "success";
|
|
937
|
+
}
|
|
938
|
+
return "failed";
|
|
939
|
+
}
|
|
940
|
+
const txTracer = new cosmos_1.TendermintTxTracer(chainInfo.rpc, "/websocket");
|
|
941
|
+
txTracer.addEventListener("error", () => {
|
|
942
|
+
txTracer.close();
|
|
943
|
+
});
|
|
944
|
+
return txTracer
|
|
945
|
+
.traceTx(buffer_1.Buffer.from(txHash.replace("0x", ""), "hex"))
|
|
946
|
+
.then((res) => {
|
|
947
|
+
txTracer.close();
|
|
948
|
+
const txResult = Array.isArray(res.txs)
|
|
949
|
+
? res.txs && res.txs.length > 0
|
|
950
|
+
? res.txs[0].tx_result
|
|
951
|
+
: undefined
|
|
952
|
+
: res;
|
|
953
|
+
if (!txResult) {
|
|
954
|
+
return "pending";
|
|
955
|
+
}
|
|
956
|
+
if (typeof txResult.code !== "number") {
|
|
957
|
+
return "error";
|
|
958
|
+
}
|
|
959
|
+
return txResult.code === 0 ? "success" : "failed";
|
|
960
|
+
})
|
|
961
|
+
.catch(() => {
|
|
962
|
+
txTracer.close();
|
|
963
|
+
return "error";
|
|
964
|
+
});
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
trackDestinationAssetAmount(params) {
|
|
968
|
+
const { chainId, txHash, recipient, targetDenom, onResult, onRefund, onFulfill, } = params;
|
|
969
|
+
const chainInfo = this.chainsService.getChainInfo(chainId);
|
|
970
|
+
if (!chainInfo) {
|
|
971
|
+
onFulfill();
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
if (this.chainsService.isEvmChain(chainId)) {
|
|
975
|
+
this.traceEVMTransactionResult({
|
|
976
|
+
chainId,
|
|
977
|
+
txHash,
|
|
978
|
+
recipient,
|
|
979
|
+
targetDenom,
|
|
980
|
+
onResult: (result) => {
|
|
981
|
+
if (result.resAmount) {
|
|
982
|
+
onResult(result.resAmount);
|
|
983
|
+
}
|
|
984
|
+
if (result.refundInfo && onRefund) {
|
|
985
|
+
onRefund(result.refundInfo, result.error);
|
|
986
|
+
}
|
|
987
|
+
},
|
|
988
|
+
onFulfill,
|
|
989
|
+
});
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
this.traceCosmosTransactionResult({
|
|
993
|
+
chainInfo,
|
|
994
|
+
txHash,
|
|
995
|
+
recipient,
|
|
996
|
+
onResult,
|
|
997
|
+
onFulfill,
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
traceCosmosTransactionResult(params) {
|
|
1001
|
+
const { chainInfo, txHash, recipient, onResult, onFulfill } = params;
|
|
1002
|
+
const txTracer = new cosmos_1.TendermintTxTracer(chainInfo.rpc, "/websocket");
|
|
1003
|
+
txTracer.addEventListener("error", () => onFulfill());
|
|
1004
|
+
txTracer
|
|
1005
|
+
.queryTx({
|
|
1006
|
+
"tx.hash": txHash,
|
|
1007
|
+
})
|
|
1008
|
+
.then((res) => {
|
|
1009
|
+
txTracer.close();
|
|
1010
|
+
if (!res) {
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
const txs = res.txs
|
|
1014
|
+
? res.txs.map((r) => r.tx_result || r)
|
|
1015
|
+
: [res.tx_result || res];
|
|
1016
|
+
for (const tx of txs) {
|
|
1017
|
+
const resAmount = this.getIBCSwapResAmountFromTx(tx, recipient);
|
|
1018
|
+
onResult(resAmount);
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
})
|
|
1022
|
+
.finally(() => {
|
|
1023
|
+
onFulfill();
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
// CHECK: move tracing logic (requestEthTxReceipt, requestEthTxTrace, parseEVMTxReceiptLogs) to tx-ethereum service
|
|
1027
|
+
traceEVMTransactionResult(params) {
|
|
1028
|
+
const { chainId, txHash, recipient, targetDenom, onResult, onFulfill } = params;
|
|
1029
|
+
const chainInfo = this.chainsService.getChainInfo(chainId);
|
|
1030
|
+
if (!chainInfo) {
|
|
1031
|
+
onResult({ success: false });
|
|
1032
|
+
onFulfill();
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
if (!this.chainsService.isEvmChain(chainId)) {
|
|
1036
|
+
onResult({ success: false, error: "Not an EVM chain" });
|
|
1037
|
+
onFulfill();
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
const evmInfo = chainInfo.evm;
|
|
1041
|
+
if (!evmInfo) {
|
|
1042
|
+
onResult({ success: false });
|
|
1043
|
+
onFulfill();
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
(0, api_1.requestEthTxReceipt)({
|
|
1047
|
+
rpc: evmInfo.rpc,
|
|
1048
|
+
txHash,
|
|
1049
|
+
origin,
|
|
1050
|
+
})
|
|
1051
|
+
.then((res) => {
|
|
1052
|
+
const txReceipt = res.data.result;
|
|
1053
|
+
if (!txReceipt) {
|
|
1054
|
+
onResult({ success: false });
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
(0, api_1.requestEthTxTrace)({
|
|
1058
|
+
rpc: evmInfo.rpc,
|
|
1059
|
+
txHash,
|
|
1060
|
+
origin,
|
|
1061
|
+
}).then((traceRes) => {
|
|
1062
|
+
let isFoundFromCall = false;
|
|
1063
|
+
const foundResAmount = [];
|
|
1064
|
+
if (traceRes.data.result) {
|
|
1065
|
+
const searchForTransfers = (calls) => {
|
|
1066
|
+
var _a, _b;
|
|
1067
|
+
for (const call of calls) {
|
|
1068
|
+
if (call.type === "CALL" &&
|
|
1069
|
+
((_a = call.to) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === recipient.toLowerCase()) {
|
|
1070
|
+
const isERC20Transfer = (_b = call.input) === null || _b === void 0 ? void 0 : _b.startsWith("0xa9059cbb");
|
|
1071
|
+
const value = BigInt(isERC20Transfer
|
|
1072
|
+
? `0x${call.input.substring(74)}`
|
|
1073
|
+
: call.value || "0x0");
|
|
1074
|
+
foundResAmount.push({
|
|
1075
|
+
amount: value.toString(10),
|
|
1076
|
+
denom: targetDenom,
|
|
1077
|
+
});
|
|
1078
|
+
isFoundFromCall = true;
|
|
1079
|
+
}
|
|
1080
|
+
if (call.calls && call.calls.length > 0) {
|
|
1081
|
+
searchForTransfers(call.calls);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
searchForTransfers(traceRes.data.result.calls || []);
|
|
1086
|
+
}
|
|
1087
|
+
if (isFoundFromCall) {
|
|
1088
|
+
onResult({ success: true, resAmount: foundResAmount });
|
|
1089
|
+
return;
|
|
1090
|
+
}
|
|
1091
|
+
// fallback to logs if debug_traceTransaction fails
|
|
1092
|
+
this.parseEVMTxReceiptLogs({
|
|
1093
|
+
txReceipt,
|
|
1094
|
+
recipient,
|
|
1095
|
+
targetChainId: chainId,
|
|
1096
|
+
targetDenom,
|
|
1097
|
+
onResult,
|
|
1098
|
+
});
|
|
1099
|
+
});
|
|
1100
|
+
})
|
|
1101
|
+
.finally(() => {
|
|
1102
|
+
onFulfill();
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
parseEVMTxReceiptLogs(params) {
|
|
1106
|
+
var _a;
|
|
1107
|
+
const { txReceipt, recipient, targetChainId, targetDenom, onResult } = params;
|
|
1108
|
+
const logs = txReceipt.logs;
|
|
1109
|
+
const transferTopic = (0, hash_1.id)("Transfer(address,address,uint256)");
|
|
1110
|
+
const withdrawTopic = (0, hash_1.id)("Withdrawal(address,uint256)");
|
|
1111
|
+
const hyperlaneReceiveTopic = (0, hash_1.id)("ReceivedTransferRemote(uint32,bytes32,uint256)");
|
|
1112
|
+
for (const log of logs) {
|
|
1113
|
+
if (log.topics[0] === transferTopic) {
|
|
1114
|
+
const to = "0x" + log.topics[2].slice(26);
|
|
1115
|
+
if (to.toLowerCase() === recipient.toLowerCase()) {
|
|
1116
|
+
const expectedAssetDenom = targetDenom.replace("erc20:", "");
|
|
1117
|
+
const amount = BigInt(log.data).toString(10);
|
|
1118
|
+
if (log.address.toLowerCase() === expectedAssetDenom.toLowerCase()) {
|
|
1119
|
+
onResult({
|
|
1120
|
+
success: true,
|
|
1121
|
+
resAmount: [{ amount, denom: targetDenom }],
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1124
|
+
else {
|
|
1125
|
+
console.log("refunded", log.address);
|
|
1126
|
+
// Transfer 토픽인 경우엔 ERC20의 tranfer 호출일텐데
|
|
1127
|
+
// 받을 토큰의 컨트랙트가 아닌 다른 컨트랙트에서 호출된 경우는 Swap을 실패한 것으로 추측
|
|
1128
|
+
// 고로 실제로 받은 토큰의 컨트랙트 주소로 환불 정보에 저장한다.
|
|
1129
|
+
onResult({
|
|
1130
|
+
success: false,
|
|
1131
|
+
error: "Swap failed",
|
|
1132
|
+
refundInfo: {
|
|
1133
|
+
chainId: targetChainId,
|
|
1134
|
+
amount: [
|
|
1135
|
+
{
|
|
1136
|
+
amount,
|
|
1137
|
+
denom: `erc20:${log.address.toLowerCase()}`,
|
|
1138
|
+
},
|
|
1139
|
+
],
|
|
1140
|
+
},
|
|
1141
|
+
});
|
|
1142
|
+
}
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
else if (log.topics[0] === withdrawTopic) {
|
|
1147
|
+
const to = "0x" + log.topics[1].slice(26);
|
|
1148
|
+
if (to.toLowerCase() === ((_a = txReceipt.to) === null || _a === void 0 ? void 0 : _a.toLowerCase())) {
|
|
1149
|
+
const amount = BigInt(log.data).toString(10);
|
|
1150
|
+
onResult({
|
|
1151
|
+
success: true,
|
|
1152
|
+
resAmount: [{ amount, denom: targetDenom }],
|
|
1153
|
+
});
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
else if (log.topics[0] === hyperlaneReceiveTopic) {
|
|
1158
|
+
const to = "0x" + log.topics[2].slice(26);
|
|
1159
|
+
if (to.toLowerCase() === recipient.toLowerCase()) {
|
|
1160
|
+
const amount = BigInt(log.data).toString(10);
|
|
1161
|
+
// Hyperlane을 통해 Forma로 TIA를 받는 경우 토큰 수량이 decimal 6으로 기록되는데,
|
|
1162
|
+
// Forma에서는 decimal 18이기 때문에 12자리 만큼 0을 붙여준다.
|
|
1163
|
+
onResult({
|
|
1164
|
+
success: true,
|
|
1165
|
+
resAmount: [
|
|
1166
|
+
{
|
|
1167
|
+
amount: targetDenom === "forma-native"
|
|
1168
|
+
? `${amount}000000000000`
|
|
1169
|
+
: amount,
|
|
1170
|
+
denom: targetDenom,
|
|
1171
|
+
},
|
|
1172
|
+
],
|
|
1173
|
+
});
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
// 결과를 찾지 못한 경우
|
|
1179
|
+
onResult({ success: false });
|
|
1180
|
+
}
|
|
1181
|
+
// ============================================================================
|
|
1182
|
+
// Skip swap history
|
|
1183
|
+
// ============================================================================
|
|
1184
|
+
recordTxWithSkipSwap(sourceChainId, destinationChainId, destinationAsset, simpleRoute, sender, recipient, amount, notificationInfo, routeDurationSeconds = 0, txHash, isOnlyUseBridge) {
|
|
1185
|
+
const id = (this.recentIBCHistorySeq++).toString();
|
|
1186
|
+
const history = {
|
|
1187
|
+
id,
|
|
1188
|
+
chainId: sourceChainId,
|
|
1189
|
+
destinationChainId: destinationChainId,
|
|
1190
|
+
destinationAsset,
|
|
1191
|
+
simpleRoute,
|
|
1192
|
+
sender,
|
|
1214
1193
|
recipient,
|
|
1215
1194
|
amount,
|
|
1216
1195
|
notificationInfo,
|
|
@@ -1228,241 +1207,1302 @@ class RecentSendHistoryService {
|
|
|
1228
1207
|
getRecentSkipHistory(id) {
|
|
1229
1208
|
return this.recentSkipHistoryMap.get(id);
|
|
1230
1209
|
}
|
|
1231
|
-
getRecentSkipHistories() {
|
|
1232
|
-
return Array.from(this.recentSkipHistoryMap.values()).filter((history) => {
|
|
1233
|
-
if (!this.chainsService.hasChainInfo(history.chainId)) {
|
|
1234
|
-
return false;
|
|
1210
|
+
getRecentSkipHistories() {
|
|
1211
|
+
return Array.from(this.recentSkipHistoryMap.values()).filter((history) => {
|
|
1212
|
+
if (!this.chainsService.hasChainInfo(history.chainId)) {
|
|
1213
|
+
return false;
|
|
1214
|
+
}
|
|
1215
|
+
if (!this.chainsService.hasChainInfo(history.destinationChainId)) {
|
|
1216
|
+
return false;
|
|
1217
|
+
}
|
|
1218
|
+
if (history.simpleRoute.some((route) => {
|
|
1219
|
+
return !this.chainsService.hasChainInfo(route.chainId);
|
|
1220
|
+
})) {
|
|
1221
|
+
return false;
|
|
1222
|
+
}
|
|
1223
|
+
return true;
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
trackSkipSwapRecursive(id) {
|
|
1227
|
+
const history = this.getRecentSkipHistory(id);
|
|
1228
|
+
if (!history) {
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
// check tx fulfilled and update history
|
|
1232
|
+
(0, common_1.retry)(() => {
|
|
1233
|
+
return new Promise((txFulfilledResolve, txFulfilledReject) => {
|
|
1234
|
+
this.checkAndTrackSwapTxFulfilledRecursive({
|
|
1235
|
+
chainId: history.chainId,
|
|
1236
|
+
txHash: history.txHash,
|
|
1237
|
+
onSuccess: () => {
|
|
1238
|
+
this.requestSkipTxTrackInternal({
|
|
1239
|
+
chainId: history.chainId,
|
|
1240
|
+
txHash: history.txHash,
|
|
1241
|
+
onRemoveHistory: () => this.removeRecentSkipHistory(id),
|
|
1242
|
+
onFulfill: (keepTracking) => {
|
|
1243
|
+
txFulfilledResolve();
|
|
1244
|
+
if (!keepTracking) {
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1247
|
+
(0, common_1.retry)(() => {
|
|
1248
|
+
return new Promise((resolve, reject) => {
|
|
1249
|
+
this.checkAndUpdateSkipSwapHistoryRecursive(id, resolve, reject);
|
|
1250
|
+
});
|
|
1251
|
+
}, {
|
|
1252
|
+
maxRetries: 50,
|
|
1253
|
+
waitMsAfterError: 500,
|
|
1254
|
+
maxWaitMsAfterError: 15000,
|
|
1255
|
+
});
|
|
1256
|
+
},
|
|
1257
|
+
});
|
|
1258
|
+
},
|
|
1259
|
+
onPending: txFulfilledReject,
|
|
1260
|
+
onFailed: () => {
|
|
1261
|
+
this.removeRecentSkipHistory(id);
|
|
1262
|
+
txFulfilledResolve();
|
|
1263
|
+
},
|
|
1264
|
+
onError: () => {
|
|
1265
|
+
txFulfilledResolve();
|
|
1266
|
+
},
|
|
1267
|
+
});
|
|
1268
|
+
});
|
|
1269
|
+
}, {
|
|
1270
|
+
maxRetries: 50,
|
|
1271
|
+
waitMsAfterError: 500,
|
|
1272
|
+
maxWaitMsAfterError: 15000,
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1275
|
+
requestSkipTxTrackInternal(params) {
|
|
1276
|
+
const { chainId, txHash, onFulfill, onRemoveHistory } = params;
|
|
1277
|
+
const chainIdForApi = this.chainsService.isEvmChain(chainId)
|
|
1278
|
+
? chainId.replace("eip155:", "")
|
|
1279
|
+
: chainId;
|
|
1280
|
+
setTimeout(() => {
|
|
1281
|
+
(0, api_1.requestSkipTxTrack)({
|
|
1282
|
+
endpoint: SWAP_API_ENDPOINT,
|
|
1283
|
+
chainId: chainIdForApi,
|
|
1284
|
+
txHash,
|
|
1285
|
+
})
|
|
1286
|
+
.then((result) => {
|
|
1287
|
+
console.log(`Skip tx track result: ${JSON.stringify(result)}`);
|
|
1288
|
+
onFulfill(true);
|
|
1289
|
+
})
|
|
1290
|
+
.catch((e) => {
|
|
1291
|
+
console.log(e);
|
|
1292
|
+
onRemoveHistory();
|
|
1293
|
+
onFulfill(false);
|
|
1294
|
+
});
|
|
1295
|
+
}, 2000);
|
|
1296
|
+
}
|
|
1297
|
+
trackSkipDestinationAssetAmount(historyId, txHash, onFulfill) {
|
|
1298
|
+
const history = this.getRecentSkipHistory(historyId);
|
|
1299
|
+
if (!history) {
|
|
1300
|
+
onFulfill();
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1303
|
+
const chainInfo = this.chainsService.getChainInfo(history.destinationChainId);
|
|
1304
|
+
if (!chainInfo) {
|
|
1305
|
+
onFulfill();
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
this.trackDestinationAssetAmount({
|
|
1309
|
+
chainId: history.destinationChainId,
|
|
1310
|
+
txHash,
|
|
1311
|
+
recipient: history.recipient,
|
|
1312
|
+
targetDenom: history.destinationAsset.denom,
|
|
1313
|
+
onResult: (resAmount) => {
|
|
1314
|
+
(0, mobx_1.runInAction)(() => {
|
|
1315
|
+
history.resAmount.push(resAmount);
|
|
1316
|
+
history.trackDone = true;
|
|
1317
|
+
});
|
|
1318
|
+
},
|
|
1319
|
+
onRefund: (refundInfo, error) => {
|
|
1320
|
+
(0, mobx_1.runInAction)(() => {
|
|
1321
|
+
history.trackError = error;
|
|
1322
|
+
history.swapRefundInfo = refundInfo;
|
|
1323
|
+
history.trackDone = true;
|
|
1324
|
+
});
|
|
1325
|
+
},
|
|
1326
|
+
onFulfill: () => {
|
|
1327
|
+
// ensure completion even if no result parsed
|
|
1328
|
+
(0, mobx_1.runInAction)(() => {
|
|
1329
|
+
history.trackDone = true;
|
|
1330
|
+
});
|
|
1331
|
+
onFulfill();
|
|
1332
|
+
},
|
|
1333
|
+
});
|
|
1334
|
+
}
|
|
1335
|
+
removeRecentSkipHistory(id) {
|
|
1336
|
+
return this.recentSkipHistoryMap.delete(id);
|
|
1337
|
+
}
|
|
1338
|
+
clearAllRecentSkipHistory() {
|
|
1339
|
+
this.recentSkipHistoryMap.clear();
|
|
1340
|
+
}
|
|
1341
|
+
// ============================================================================
|
|
1342
|
+
// Swap V2 history
|
|
1343
|
+
// ============================================================================
|
|
1344
|
+
recordTxWithSwapV2(fromChainId, toChainId, provider, destinationAsset, simpleRoute, sender, recipient, amount, notificationInfo, routeDurationSeconds = 0, txHash, isOnlyUseBridge, backgroundExecutionId) {
|
|
1345
|
+
const id = (this.recentSwapV2HistorySeq++).toString();
|
|
1346
|
+
const history = {
|
|
1347
|
+
id,
|
|
1348
|
+
fromChainId,
|
|
1349
|
+
toChainId,
|
|
1350
|
+
provider,
|
|
1351
|
+
timestamp: Date.now(),
|
|
1352
|
+
sender,
|
|
1353
|
+
recipient,
|
|
1354
|
+
amount,
|
|
1355
|
+
notificationInfo,
|
|
1356
|
+
routeDurationSeconds,
|
|
1357
|
+
txHash,
|
|
1358
|
+
isOnlyUseBridge,
|
|
1359
|
+
status: types_1.SwapV2TxStatus.IN_PROGRESS,
|
|
1360
|
+
simpleRoute,
|
|
1361
|
+
routeIndex: -1,
|
|
1362
|
+
destinationAsset,
|
|
1363
|
+
resAmount: [],
|
|
1364
|
+
assetLocationInfo: undefined,
|
|
1365
|
+
notified: undefined,
|
|
1366
|
+
backgroundExecutionId,
|
|
1367
|
+
};
|
|
1368
|
+
this.recentSwapV2HistoryMap.set(id, history);
|
|
1369
|
+
this.trackSwapV2Recursive(id);
|
|
1370
|
+
return id;
|
|
1371
|
+
}
|
|
1372
|
+
trackSwapV2Recursive(id) {
|
|
1373
|
+
const history = this.getRecentSwapV2History(id);
|
|
1374
|
+
if (!history) {
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
(0, common_1.retry)(() => {
|
|
1378
|
+
return new Promise((txFulfilledResolve, txFulfilledReject) => {
|
|
1379
|
+
this.checkAndTrackSwapTxFulfilledRecursive({
|
|
1380
|
+
chainId: history.fromChainId,
|
|
1381
|
+
txHash: history.txHash,
|
|
1382
|
+
onSuccess: () => {
|
|
1383
|
+
txFulfilledResolve();
|
|
1384
|
+
(0, common_1.retry)(() => {
|
|
1385
|
+
return new Promise((resolve, reject) => {
|
|
1386
|
+
this.checkAndUpdateSwapV2HistoryRecursive(id, resolve, reject);
|
|
1387
|
+
});
|
|
1388
|
+
}, {
|
|
1389
|
+
maxRetries: 60,
|
|
1390
|
+
waitMsAfterError: 1000,
|
|
1391
|
+
maxWaitMsAfterError: 45000,
|
|
1392
|
+
});
|
|
1393
|
+
},
|
|
1394
|
+
onPending: txFulfilledReject,
|
|
1395
|
+
onFailed: () => {
|
|
1396
|
+
this.removeRecentSwapV2History(id);
|
|
1397
|
+
txFulfilledResolve();
|
|
1398
|
+
},
|
|
1399
|
+
onError: txFulfilledResolve,
|
|
1400
|
+
});
|
|
1401
|
+
});
|
|
1402
|
+
}, {
|
|
1403
|
+
maxRetries: 60,
|
|
1404
|
+
waitMsAfterError: 1000,
|
|
1405
|
+
maxWaitMsAfterError: 45000,
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
checkAndUpdateSwapV2HistoryRecursive(id, onFulfill, onError) {
|
|
1409
|
+
const history = this.getRecentSwapV2History(id);
|
|
1410
|
+
if (!history) {
|
|
1411
|
+
onFulfill();
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
// if already tracked, fulfill
|
|
1415
|
+
if (history.trackDone) {
|
|
1416
|
+
onFulfill();
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
const { txHash, fromChainId, toChainId, provider } = history;
|
|
1420
|
+
const normalizeChainId = (chainId) => {
|
|
1421
|
+
return chainId.replace("eip155:", "");
|
|
1422
|
+
};
|
|
1423
|
+
(0, api_1.requestSwapV2TxStatus)({
|
|
1424
|
+
endpoint: SWAP_API_ENDPOINT,
|
|
1425
|
+
fromChainId: normalizeChainId(fromChainId),
|
|
1426
|
+
toChainId: normalizeChainId(toChainId),
|
|
1427
|
+
provider,
|
|
1428
|
+
txHash,
|
|
1429
|
+
})
|
|
1430
|
+
.then((res) => {
|
|
1431
|
+
this.processSwapV2StatusResponse(id, res.data, onFulfill, onError);
|
|
1432
|
+
})
|
|
1433
|
+
.catch((e) => {
|
|
1434
|
+
console.error("SwapV2 status tracking error:", e);
|
|
1435
|
+
onError();
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1438
|
+
processSwapV2StatusResponse(id, response, onFulfill, onError) {
|
|
1439
|
+
var _a, _b, _c;
|
|
1440
|
+
const history = this.getRecentSwapV2History(id);
|
|
1441
|
+
if (!history) {
|
|
1442
|
+
onFulfill();
|
|
1443
|
+
return;
|
|
1444
|
+
}
|
|
1445
|
+
const { status, steps, asset_location } = response;
|
|
1446
|
+
const { simpleRoute } = history;
|
|
1447
|
+
const prevRouteIndex = history.routeIndex;
|
|
1448
|
+
// 모든 상태 즉시 업데이트 (UNKNOWN 포함)
|
|
1449
|
+
history.status = status;
|
|
1450
|
+
history.trackError = undefined;
|
|
1451
|
+
// This might be the state where tracking has just started,
|
|
1452
|
+
// so handle the error and retry
|
|
1453
|
+
if (!steps || steps.length === 0) {
|
|
1454
|
+
if (status === types_1.SwapV2TxStatus.IN_PROGRESS ||
|
|
1455
|
+
status === types_1.SwapV2TxStatus.UNKNOWN) {
|
|
1456
|
+
onError();
|
|
1457
|
+
}
|
|
1458
|
+
else {
|
|
1459
|
+
// swap on single evm chain might not have steps
|
|
1460
|
+
history.trackDone = true;
|
|
1461
|
+
onFulfill();
|
|
1462
|
+
}
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1465
|
+
// find current step (not success first, otherwise last step)
|
|
1466
|
+
const currentStep = (_a = steps.find((s) => s.status !== types_1.SwapV2RouteStepStatus.SUCCESS)) !== null && _a !== void 0 ? _a : steps[steps.length - 1];
|
|
1467
|
+
const normalizeChainId = (chainId) => {
|
|
1468
|
+
return chainId.replace("eip155:", "").toLowerCase();
|
|
1469
|
+
};
|
|
1470
|
+
// Find the LAST step that matches the actual destination chain (history.toChainId)
|
|
1471
|
+
// Use reverse + find to handle routes that visit the destination chain multiple times
|
|
1472
|
+
// This handles cases where intermediate steps fail but the final destination is reached
|
|
1473
|
+
const destinationStep = [...steps].reverse().find((s) => {
|
|
1474
|
+
if (!s.chain_id) {
|
|
1475
|
+
return false;
|
|
1476
|
+
}
|
|
1477
|
+
return (normalizeChainId(s.chain_id) === normalizeChainId(history.toChainId));
|
|
1478
|
+
});
|
|
1479
|
+
const isDestinationStepSuccessful = destinationStep &&
|
|
1480
|
+
destinationStep.status === types_1.SwapV2RouteStepStatus.SUCCESS &&
|
|
1481
|
+
!!destinationStep.tx_hash;
|
|
1482
|
+
const findSimpleRouteIndex = (chainId) => {
|
|
1483
|
+
const normalizedChainId = normalizeChainId(chainId);
|
|
1484
|
+
for (let i = 0; i < simpleRoute.length; i++) {
|
|
1485
|
+
const routeChainId = normalizeChainId(simpleRoute[i].chainId);
|
|
1486
|
+
if (routeChainId === normalizedChainId) {
|
|
1487
|
+
return i;
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
return -1;
|
|
1491
|
+
};
|
|
1492
|
+
// NOTE: The lengths of simpleRoute and steps may differ.
|
|
1493
|
+
let updatedRouteIndex = Math.max(0, history.routeIndex);
|
|
1494
|
+
// 1. Find highest completed simpleRoute index from all SUCCESS steps
|
|
1495
|
+
let highestCompletedIndex = -1;
|
|
1496
|
+
for (const step of steps) {
|
|
1497
|
+
if (step.status === types_1.SwapV2RouteStepStatus.SUCCESS && step.chain_id) {
|
|
1498
|
+
const routeIdx = findSimpleRouteIndex(step.chain_id);
|
|
1499
|
+
if (routeIdx > highestCompletedIndex) {
|
|
1500
|
+
highestCompletedIndex = routeIdx;
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
// 2. Also check if currentStep is in simpleRoute
|
|
1505
|
+
let currentStepIndex = -1;
|
|
1506
|
+
if (currentStep.chain_id) {
|
|
1507
|
+
currentStepIndex = findSimpleRouteIndex(currentStep.chain_id);
|
|
1508
|
+
}
|
|
1509
|
+
// 3. Use the higher value as updatedRouteIndex
|
|
1510
|
+
const candidateIndex = Math.max(highestCompletedIndex, currentStepIndex);
|
|
1511
|
+
if (candidateIndex >= 0) {
|
|
1512
|
+
updatedRouteIndex = candidateIndex;
|
|
1513
|
+
}
|
|
1514
|
+
const publishExecutableChains = (chainIds) => {
|
|
1515
|
+
if (!history.backgroundExecutionId) {
|
|
1516
|
+
return;
|
|
1517
|
+
}
|
|
1518
|
+
const executableChainIds = chainIds !== null && chainIds !== void 0 ? chainIds : this.getExecutableChainIdsFromSwapV2History(history);
|
|
1519
|
+
this.publisher.publish({
|
|
1520
|
+
type: "executable",
|
|
1521
|
+
executionId: history.backgroundExecutionId,
|
|
1522
|
+
executableChainIds,
|
|
1523
|
+
});
|
|
1524
|
+
};
|
|
1525
|
+
const isUnknownStatus = status === types_1.SwapV2TxStatus.UNKNOWN ||
|
|
1526
|
+
steps.some((s) => s.status === types_1.SwapV2RouteStepStatus.UNKNOWN);
|
|
1527
|
+
if (isUnknownStatus) {
|
|
1528
|
+
if (!history.unknownStatusFirstSeenAt) {
|
|
1529
|
+
// UNKNOWN 상태 처음 발견 - 타임스탬프 기록
|
|
1530
|
+
history.unknownStatusFirstSeenAt = Date.now();
|
|
1531
|
+
}
|
|
1532
|
+
else {
|
|
1533
|
+
const elapsedMs = Date.now() - history.unknownStatusFirstSeenAt;
|
|
1534
|
+
if (elapsedMs >= exports.UNKNOWN_TX_STATUS_TIMEOUT_MS) {
|
|
1535
|
+
this.analyticsService.logEventIgnoreError("swapV2UnknownTxStatusWithTimeout", {
|
|
1536
|
+
provider: history.provider,
|
|
1537
|
+
fromChainId: history.fromChainId,
|
|
1538
|
+
toChainId: history.toChainId,
|
|
1539
|
+
txHash: history.txHash,
|
|
1540
|
+
executedAt: history.timestamp,
|
|
1541
|
+
});
|
|
1542
|
+
history.trackDone = true;
|
|
1543
|
+
onFulfill();
|
|
1544
|
+
return;
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
else {
|
|
1549
|
+
// UNKNOWN 상태가 아니면 타임스탬프 초기화
|
|
1550
|
+
if (history.unknownStatusFirstSeenAt !== undefined) {
|
|
1551
|
+
history.unknownStatusFirstSeenAt = undefined;
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
switch (status) {
|
|
1555
|
+
case types_1.SwapV2TxStatus.IN_PROGRESS:
|
|
1556
|
+
case types_1.SwapV2TxStatus.UNKNOWN:
|
|
1557
|
+
// publish executable chains if routeIndex increased
|
|
1558
|
+
if (updatedRouteIndex > prevRouteIndex) {
|
|
1559
|
+
history.routeIndex = updatedRouteIndex;
|
|
1560
|
+
publishExecutableChains();
|
|
1561
|
+
}
|
|
1562
|
+
// Continue polling
|
|
1563
|
+
onError();
|
|
1564
|
+
break;
|
|
1565
|
+
case types_1.SwapV2TxStatus.SUCCESS:
|
|
1566
|
+
case types_1.SwapV2TxStatus.PARTIAL_SUCCESS:
|
|
1567
|
+
case types_1.SwapV2TxStatus.FAILED:
|
|
1568
|
+
// If current step is still in progress, retry a few more times before finalizing
|
|
1569
|
+
if (currentStep.status === types_1.SwapV2RouteStepStatus.IN_PROGRESS) {
|
|
1570
|
+
const maxRetries = 3;
|
|
1571
|
+
const retryCount = (_b = history.finalizationRetryCount) !== null && _b !== void 0 ? _b : 0;
|
|
1572
|
+
if (retryCount < maxRetries) {
|
|
1573
|
+
history.finalizationRetryCount = retryCount + 1;
|
|
1574
|
+
onError();
|
|
1575
|
+
break;
|
|
1576
|
+
}
|
|
1577
|
+
// Max retries reached, fall through to finalize
|
|
1578
|
+
}
|
|
1579
|
+
let executableChainIdsToPublish;
|
|
1580
|
+
// NOTE: 현재 asset_location은 skip의 multichain operation인 경우에만 주어지는 값이다.
|
|
1581
|
+
if (asset_location) {
|
|
1582
|
+
const chainId = asset_location.chain_id;
|
|
1583
|
+
const evmLikeChainId = Number(chainId);
|
|
1584
|
+
const isEVMChainId = !Number.isNaN(evmLikeChainId) && evmLikeChainId > 0;
|
|
1585
|
+
const chainIdInKeplr = isEVMChainId ? `eip155:${chainId}` : chainId;
|
|
1586
|
+
const denomInKeplr = isEVMChainId
|
|
1587
|
+
? `erc20:${asset_location.denom}`
|
|
1588
|
+
: asset_location.denom;
|
|
1589
|
+
// destination chain에 destination denom으로 도착했으면 완전 성공이므로
|
|
1590
|
+
// assetLocationInfo를 설정하지 않음
|
|
1591
|
+
const isDestinationReached = chainIdInKeplr === history.toChainId &&
|
|
1592
|
+
denomInKeplr.toLowerCase() ===
|
|
1593
|
+
history.destinationAsset.denom.toLowerCase();
|
|
1594
|
+
if (isDestinationReached) {
|
|
1595
|
+
history.routeIndex = simpleRoute.length - 1;
|
|
1596
|
+
executableChainIdsToPublish =
|
|
1597
|
+
this.getExecutableChainIdsFromSwapV2History(history, true);
|
|
1598
|
+
history.resAmount.push([
|
|
1599
|
+
{
|
|
1600
|
+
amount: asset_location.amount,
|
|
1601
|
+
denom: denomInKeplr,
|
|
1602
|
+
},
|
|
1603
|
+
]);
|
|
1604
|
+
this.notifySwapV2Success(history);
|
|
1605
|
+
}
|
|
1606
|
+
else {
|
|
1607
|
+
/*
|
|
1608
|
+
Determine the type of asset location:
|
|
1609
|
+
- "intermediate": SUCCESS 상태이지만 asset_location이 최종 목적지가 아닌 경우
|
|
1610
|
+
(예: base USDC -> osmosis OSMO 스왑 시, noble USDC가 먼저 도착하고
|
|
1611
|
+
이후 noble USDC -> osmosis OSMO로 ibc swap하는 transaction이 필요한 경우)
|
|
1612
|
+
이 경우 추가 transaction을 실행하거나 현재 받은 자산을 그대로 둘 수 있음
|
|
1613
|
+
- "refund": 중간에서 또는 destination에서 스왑 실패 등으로 destination asset이 아닌 자산이 릴리즈된 경우
|
|
1614
|
+
backgroundExecutionId가 있으면 멀티 transaction 케이스이므로 다음 transaction을 실행할 수 있도록 'intermediate'로 설정
|
|
1615
|
+
*/
|
|
1616
|
+
const assetLocationType = status === types_1.SwapV2TxStatus.SUCCESS && history.backgroundExecutionId
|
|
1617
|
+
? "intermediate"
|
|
1618
|
+
: "refund";
|
|
1619
|
+
history.assetLocationInfo = {
|
|
1620
|
+
chainId: chainIdInKeplr,
|
|
1621
|
+
amount: [
|
|
1622
|
+
{
|
|
1623
|
+
amount: asset_location.amount,
|
|
1624
|
+
denom: denomInKeplr,
|
|
1625
|
+
},
|
|
1626
|
+
],
|
|
1627
|
+
type: assetLocationType,
|
|
1628
|
+
};
|
|
1629
|
+
// refund 타입인 경우 status도 FAILED로 변경하여 UI에서 refund 상황을 표시할 수 있도록 함
|
|
1630
|
+
if (assetLocationType === "refund") {
|
|
1631
|
+
history.status = types_1.SwapV2TxStatus.FAILED;
|
|
1632
|
+
}
|
|
1633
|
+
// asset location chain까지 routeIndex가 이동해야 하는지 확인
|
|
1634
|
+
const assetLocationChainIndex = simpleRoute.findIndex((route) => route.chainId === chainIdInKeplr);
|
|
1635
|
+
if (assetLocationChainIndex !== -1 &&
|
|
1636
|
+
assetLocationChainIndex > updatedRouteIndex) {
|
|
1637
|
+
history.routeIndex = assetLocationChainIndex;
|
|
1638
|
+
}
|
|
1639
|
+
executableChainIdsToPublish =
|
|
1640
|
+
this.getExecutableChainIdsFromSwapV2History(history);
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
else if (status === types_1.SwapV2TxStatus.SUCCESS) {
|
|
1644
|
+
// For SUCCESS without asset_location, move routeIndex to end
|
|
1645
|
+
history.routeIndex = simpleRoute.length - 1;
|
|
1646
|
+
executableChainIdsToPublish =
|
|
1647
|
+
this.getExecutableChainIdsFromSwapV2History(history, true);
|
|
1648
|
+
this.notifySwapV2Success(history);
|
|
1649
|
+
}
|
|
1650
|
+
// Publish executable chains
|
|
1651
|
+
publishExecutableChains(executableChainIdsToPublish);
|
|
1652
|
+
// destination 체인/denom 기준으로만 추가 추적을 시도한다.
|
|
1653
|
+
const targetChainId = history.toChainId;
|
|
1654
|
+
const targetDenom = history.destinationAsset.denom;
|
|
1655
|
+
// 해당 위치의 tx_hash를 찾아서 자산 추적, 없을 수도 있다.
|
|
1656
|
+
const targetTxHash = (_c = destinationStep === null || destinationStep === void 0 ? void 0 : destinationStep.tx_hash) !== null && _c !== void 0 ? _c : currentStep.tx_hash;
|
|
1657
|
+
const isAtDestinationChain = (() => {
|
|
1658
|
+
// Top-level SUCCESS + destination step successful → check against destination step
|
|
1659
|
+
if (status === types_1.SwapV2TxStatus.SUCCESS &&
|
|
1660
|
+
isDestinationStepSuccessful &&
|
|
1661
|
+
(destinationStep === null || destinationStep === void 0 ? void 0 : destinationStep.chain_id)) {
|
|
1662
|
+
return (normalizeChainId(destinationStep.chain_id) ===
|
|
1663
|
+
normalizeChainId(targetChainId));
|
|
1664
|
+
}
|
|
1665
|
+
// Default logic (other cases)
|
|
1666
|
+
if (!currentStep.chain_id) {
|
|
1667
|
+
return false;
|
|
1668
|
+
}
|
|
1669
|
+
return (normalizeChainId(currentStep.chain_id) ===
|
|
1670
|
+
normalizeChainId(targetChainId));
|
|
1671
|
+
})();
|
|
1672
|
+
const skipAssetTracking = history.resAmount.length > 0 || history.assetLocationInfo != null;
|
|
1673
|
+
// resAmount 또는 assetLocationInfo가 없으면 추가적으로 자산 추적을 해야 한다.
|
|
1674
|
+
if (targetTxHash && !skipAssetTracking && isAtDestinationChain) {
|
|
1675
|
+
console.log("trackSwapV2ReleasedAssetAmount", id, targetTxHash);
|
|
1676
|
+
this.trackSwapV2ReleasedAssetAmount(id, targetTxHash, targetChainId, targetDenom, onFulfill);
|
|
1677
|
+
}
|
|
1678
|
+
else if (status === types_1.SwapV2TxStatus.SUCCESS &&
|
|
1679
|
+
!skipAssetTracking &&
|
|
1680
|
+
!isAtDestinationChain) {
|
|
1681
|
+
// response status는 SUCCESS인데 destination chain에 도달하지 않음 → 실패 처리
|
|
1682
|
+
(0, mobx_1.runInAction)(() => {
|
|
1683
|
+
history.status = types_1.SwapV2TxStatus.FAILED;
|
|
1684
|
+
history.trackDone = true;
|
|
1685
|
+
});
|
|
1686
|
+
// TODO: additional tracking for failed case...
|
|
1687
|
+
onFulfill();
|
|
1688
|
+
}
|
|
1689
|
+
else {
|
|
1690
|
+
history.trackDone = true;
|
|
1691
|
+
onFulfill();
|
|
1692
|
+
}
|
|
1693
|
+
break;
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
/**
|
|
1697
|
+
* Track released asset amount from tx receipt.
|
|
1698
|
+
* - SUCCESS: destination asset 추적
|
|
1699
|
+
* - FAILED/PARTIAL_SUCCESS + assetLocationInfo: refund된 자산 추적
|
|
1700
|
+
*/
|
|
1701
|
+
trackSwapV2ReleasedAssetAmount(historyId, txHash, targetChainId, targetDenom, onFulfill) {
|
|
1702
|
+
const history = this.getRecentSwapV2History(historyId);
|
|
1703
|
+
if (!history) {
|
|
1704
|
+
onFulfill();
|
|
1705
|
+
return;
|
|
1706
|
+
}
|
|
1707
|
+
const chainInfo = this.chainsService.getChainInfo(targetChainId);
|
|
1708
|
+
if (!chainInfo) {
|
|
1709
|
+
onFulfill();
|
|
1710
|
+
return;
|
|
1711
|
+
}
|
|
1712
|
+
this.trackDestinationAssetAmount({
|
|
1713
|
+
chainId: targetChainId,
|
|
1714
|
+
txHash,
|
|
1715
|
+
recipient: history.recipient,
|
|
1716
|
+
targetDenom,
|
|
1717
|
+
onResult: (resAmount) => {
|
|
1718
|
+
(0, mobx_1.runInAction)(() => {
|
|
1719
|
+
history.resAmount.push(resAmount);
|
|
1720
|
+
history.trackDone = true;
|
|
1721
|
+
this.notifySwapV2Success(history);
|
|
1722
|
+
});
|
|
1723
|
+
},
|
|
1724
|
+
onRefund: (refundInfo, error) => {
|
|
1725
|
+
(0, mobx_1.runInAction)(() => {
|
|
1726
|
+
history.trackError = error;
|
|
1727
|
+
history.assetLocationInfo = Object.assign(Object.assign({}, refundInfo), { type: "refund" });
|
|
1728
|
+
history.trackDone = true;
|
|
1729
|
+
});
|
|
1730
|
+
},
|
|
1731
|
+
onFulfill: () => {
|
|
1732
|
+
(0, mobx_1.runInAction)(() => {
|
|
1733
|
+
history.trackDone = true;
|
|
1734
|
+
});
|
|
1735
|
+
onFulfill();
|
|
1736
|
+
},
|
|
1737
|
+
});
|
|
1738
|
+
}
|
|
1739
|
+
getRecentSwapV2History(id) {
|
|
1740
|
+
return this.recentSwapV2HistoryMap.get(id);
|
|
1741
|
+
}
|
|
1742
|
+
getRecentSwapV2Histories() {
|
|
1743
|
+
return Array.from(this.recentSwapV2HistoryMap.values()).filter((history) => {
|
|
1744
|
+
if (!this.chainsService.hasChainInfo(history.fromChainId)) {
|
|
1745
|
+
return false;
|
|
1746
|
+
}
|
|
1747
|
+
if (!this.chainsService.hasChainInfo(history.toChainId)) {
|
|
1748
|
+
return false;
|
|
1749
|
+
}
|
|
1750
|
+
if (history.simpleRoute.some((route) => {
|
|
1751
|
+
return !this.chainsService.hasChainInfo(route.chainId);
|
|
1752
|
+
})) {
|
|
1753
|
+
return false;
|
|
1754
|
+
}
|
|
1755
|
+
return true;
|
|
1756
|
+
});
|
|
1757
|
+
}
|
|
1758
|
+
removeRecentSwapV2History(id) {
|
|
1759
|
+
const history = this.getRecentSwapV2History(id);
|
|
1760
|
+
const removed = this.recentSwapV2HistoryMap.delete(id);
|
|
1761
|
+
if (removed && (history === null || history === void 0 ? void 0 : history.backgroundExecutionId)) {
|
|
1762
|
+
this.publisher.publish({
|
|
1763
|
+
type: "remove",
|
|
1764
|
+
executionId: history.backgroundExecutionId,
|
|
1765
|
+
});
|
|
1766
|
+
}
|
|
1767
|
+
return removed;
|
|
1768
|
+
}
|
|
1769
|
+
clearAllRecentSwapV2History() {
|
|
1770
|
+
const executionIds = [];
|
|
1771
|
+
for (const history of this.recentSwapV2HistoryMap.values()) {
|
|
1772
|
+
if (history.backgroundExecutionId) {
|
|
1773
|
+
executionIds.push(history.backgroundExecutionId);
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
this.recentSwapV2HistoryMap.clear();
|
|
1777
|
+
for (const executionId of executionIds) {
|
|
1778
|
+
this.publisher.publish({
|
|
1779
|
+
type: "remove",
|
|
1780
|
+
executionId,
|
|
1781
|
+
});
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
notifySwapV2Success(history) {
|
|
1785
|
+
const notificationInfo = history.notificationInfo;
|
|
1786
|
+
if (!notificationInfo || history.notified) {
|
|
1787
|
+
return;
|
|
1788
|
+
}
|
|
1789
|
+
const latestResAmount = history.resAmount.length > 0
|
|
1790
|
+
? history.resAmount[history.resAmount.length - 1]
|
|
1791
|
+
: undefined;
|
|
1792
|
+
if (!latestResAmount || latestResAmount.length === 0) {
|
|
1793
|
+
return;
|
|
1794
|
+
}
|
|
1795
|
+
const chainInfo = this.chainsService.getChainInfo(history.toChainId);
|
|
1796
|
+
if (!chainInfo) {
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1799
|
+
const assetsText = latestResAmount
|
|
1800
|
+
.map((amt) => {
|
|
1801
|
+
const currency = notificationInfo.currencies.find((cur) => cur.coinMinimalDenom === amt.denom);
|
|
1802
|
+
if (!currency) {
|
|
1803
|
+
return undefined;
|
|
1804
|
+
}
|
|
1805
|
+
return new unit_1.CoinPretty(currency, amt.amount)
|
|
1806
|
+
.hideIBCMetadata(true)
|
|
1807
|
+
.shrink(true)
|
|
1808
|
+
.maxDecimals(6)
|
|
1809
|
+
.inequalitySymbol(true)
|
|
1810
|
+
.trim(true)
|
|
1811
|
+
.toString();
|
|
1812
|
+
})
|
|
1813
|
+
.filter((text) => Boolean(text));
|
|
1814
|
+
if (assetsText.length === 0) {
|
|
1815
|
+
return;
|
|
1816
|
+
}
|
|
1817
|
+
(0, mobx_1.runInAction)(() => {
|
|
1818
|
+
history.notified = true;
|
|
1819
|
+
});
|
|
1820
|
+
this.notification.create({
|
|
1821
|
+
iconRelativeUrl: "assets/logo-256.png",
|
|
1822
|
+
title: "Swap Succeeded",
|
|
1823
|
+
message: `${assetsText.join(", ")} received on ${chainInfo.chainName}`,
|
|
1824
|
+
});
|
|
1825
|
+
}
|
|
1826
|
+
hideSwapV2History(id) {
|
|
1827
|
+
const history = this.getRecentSwapV2History(id);
|
|
1828
|
+
if (!history) {
|
|
1829
|
+
return false;
|
|
1830
|
+
}
|
|
1831
|
+
if (!history.backgroundExecutionId) {
|
|
1832
|
+
return false;
|
|
1833
|
+
}
|
|
1834
|
+
// only hide if multi tx case
|
|
1835
|
+
history.hidden = true;
|
|
1836
|
+
return true;
|
|
1837
|
+
}
|
|
1838
|
+
showSwapV2History(id) {
|
|
1839
|
+
const history = this.getRecentSwapV2History(id);
|
|
1840
|
+
if (!history) {
|
|
1841
|
+
return false;
|
|
1842
|
+
}
|
|
1843
|
+
history.hidden = false;
|
|
1844
|
+
return true;
|
|
1845
|
+
}
|
|
1846
|
+
setSwapV2HistoryError(id, error) {
|
|
1847
|
+
const history = this.getRecentSwapV2History(id);
|
|
1848
|
+
if (!history) {
|
|
1849
|
+
return false;
|
|
1850
|
+
}
|
|
1851
|
+
history.trackError = error;
|
|
1852
|
+
history.trackDone = true;
|
|
1853
|
+
return true;
|
|
1854
|
+
}
|
|
1855
|
+
clearSwapV2HistoryBackgroundExecutionId(id) {
|
|
1856
|
+
const history = this.getRecentSwapV2History(id);
|
|
1857
|
+
if (!history) {
|
|
1858
|
+
return false;
|
|
1859
|
+
}
|
|
1860
|
+
history.backgroundExecutionId = undefined;
|
|
1861
|
+
return true;
|
|
1862
|
+
}
|
|
1863
|
+
setSwapV2AdditionalTrackingData(id, data) {
|
|
1864
|
+
const history = this.getRecentSwapV2History(id);
|
|
1865
|
+
if (!history) {
|
|
1866
|
+
return false;
|
|
1867
|
+
}
|
|
1868
|
+
if (data.type === "cosmos-ibc") {
|
|
1869
|
+
history.additionalTrackingData = {
|
|
1870
|
+
type: "cosmos-ibc",
|
|
1871
|
+
chainId: data.ibcSwapData.chainId,
|
|
1872
|
+
swapReceiver: data.ibcSwapData.swapReceiver,
|
|
1873
|
+
swapChannelIndex: data.ibcSwapData.swapChannelIndex,
|
|
1874
|
+
txHash: data.txHash,
|
|
1875
|
+
txFulfilled: false,
|
|
1876
|
+
packetTimeout: false,
|
|
1877
|
+
ibcHistory: data.ibcSwapData.ibcChannels.map((ch) => ({
|
|
1878
|
+
portId: ch.portId,
|
|
1879
|
+
channelId: ch.channelId,
|
|
1880
|
+
counterpartyChainId: ch.counterpartyChainId,
|
|
1881
|
+
completed: false,
|
|
1882
|
+
})),
|
|
1883
|
+
};
|
|
1884
|
+
}
|
|
1885
|
+
else {
|
|
1886
|
+
history.additionalTrackingData = data;
|
|
1887
|
+
}
|
|
1888
|
+
history.additionalTrackDone = false;
|
|
1889
|
+
history.additionalTrackError = undefined;
|
|
1890
|
+
this.trackSwapV2AdditionalRecursive(id);
|
|
1891
|
+
return true;
|
|
1892
|
+
}
|
|
1893
|
+
trackSwapV2AdditionalRecursive(id) {
|
|
1894
|
+
const history = this.getRecentSwapV2History(id);
|
|
1895
|
+
if (!history) {
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1898
|
+
// no additional tracking data
|
|
1899
|
+
if (!history.additionalTrackingData) {
|
|
1900
|
+
return;
|
|
1901
|
+
}
|
|
1902
|
+
// already done
|
|
1903
|
+
if (history.additionalTrackDone) {
|
|
1904
|
+
return;
|
|
1905
|
+
}
|
|
1906
|
+
if (history.additionalTrackingData.type === "evm") {
|
|
1907
|
+
this.trackSwapV2AdditionalEVM(id);
|
|
1908
|
+
}
|
|
1909
|
+
else if (history.additionalTrackingData.type === "cosmos-ibc") {
|
|
1910
|
+
this.trackIBCPacketForwardingRecursive((onFulfill, onClose, onError) => {
|
|
1911
|
+
this.trackSwapV2AdditionalCosmosIBCInternal(id, onFulfill, onClose, onError);
|
|
1912
|
+
});
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
trackSwapV2AdditionalEVM(id) {
|
|
1916
|
+
var _a;
|
|
1917
|
+
const history = this.getRecentSwapV2History(id);
|
|
1918
|
+
if (!history) {
|
|
1919
|
+
return;
|
|
1920
|
+
}
|
|
1921
|
+
if (((_a = history.additionalTrackingData) === null || _a === void 0 ? void 0 : _a.type) !== "evm") {
|
|
1922
|
+
return;
|
|
1923
|
+
}
|
|
1924
|
+
const txHash = history.additionalTrackingData.txHash;
|
|
1925
|
+
this.traceEVMTransactionResult({
|
|
1926
|
+
chainId: history.toChainId,
|
|
1927
|
+
txHash,
|
|
1928
|
+
recipient: history.recipient,
|
|
1929
|
+
targetDenom: history.destinationAsset.denom,
|
|
1930
|
+
onResult: (result) => {
|
|
1931
|
+
(0, mobx_1.runInAction)(() => {
|
|
1932
|
+
if (result.success && result.resAmount) {
|
|
1933
|
+
history.resAmount.push(result.resAmount);
|
|
1934
|
+
history.assetLocationInfo = undefined;
|
|
1935
|
+
this.notifySwapV2Success(history);
|
|
1936
|
+
}
|
|
1937
|
+
else if (result.refundInfo) {
|
|
1938
|
+
history.additionalTrackError = result.error;
|
|
1939
|
+
history.assetLocationInfo = Object.assign(Object.assign({}, result.refundInfo), { type: "refund" });
|
|
1940
|
+
}
|
|
1941
|
+
history.additionalTrackDone = true;
|
|
1942
|
+
});
|
|
1943
|
+
},
|
|
1944
|
+
onFulfill: () => {
|
|
1945
|
+
(0, mobx_1.runInAction)(() => {
|
|
1946
|
+
history.additionalTrackDone = true;
|
|
1947
|
+
});
|
|
1948
|
+
},
|
|
1949
|
+
});
|
|
1950
|
+
}
|
|
1951
|
+
trackSwapV2AdditionalCosmosIBCInternal(id, onFulfill, onClose, onError) {
|
|
1952
|
+
var _a;
|
|
1953
|
+
const history = this.getRecentSwapV2History(id);
|
|
1954
|
+
if (!history) {
|
|
1955
|
+
onFulfill();
|
|
1956
|
+
return;
|
|
1957
|
+
}
|
|
1958
|
+
if (history.additionalTrackDone) {
|
|
1959
|
+
onFulfill();
|
|
1960
|
+
return;
|
|
1961
|
+
}
|
|
1962
|
+
const existingTrackingData = history.additionalTrackingData;
|
|
1963
|
+
if (!existingTrackingData || existingTrackingData.type !== "cosmos-ibc") {
|
|
1964
|
+
onFulfill();
|
|
1965
|
+
return;
|
|
1966
|
+
}
|
|
1967
|
+
const trackingData = existingTrackingData;
|
|
1968
|
+
const { chainId, txHash, ibcHistory, swapReceiver, txFulfilled } = trackingData;
|
|
1969
|
+
if (!txFulfilled) {
|
|
1970
|
+
this.trackIBCTxFulfillment({
|
|
1971
|
+
chainId,
|
|
1972
|
+
txHash,
|
|
1973
|
+
ibcHistory,
|
|
1974
|
+
swapReceiver,
|
|
1975
|
+
onTxFulfilled: (_tx, firstHopResAmount) => {
|
|
1976
|
+
(0, mobx_1.runInAction)(() => {
|
|
1977
|
+
trackingData.txFulfilled = true;
|
|
1978
|
+
if (firstHopResAmount) {
|
|
1979
|
+
history.resAmount.push(firstHopResAmount);
|
|
1980
|
+
for (let i = history.routeIndex; i < history.simpleRoute.length; i++) {
|
|
1981
|
+
const route = history.simpleRoute[i];
|
|
1982
|
+
if (route.chainId === chainId) {
|
|
1983
|
+
history.routeIndex = i + 1; // move to next route
|
|
1984
|
+
break;
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
});
|
|
1989
|
+
this.trackIBCPacketForwardingRecursive((onFulfill, onClose, onError) => {
|
|
1990
|
+
this.trackSwapV2AdditionalCosmosIBCInternal(id, onFulfill, onClose, onError);
|
|
1991
|
+
});
|
|
1992
|
+
},
|
|
1993
|
+
onTxError: (error) => {
|
|
1994
|
+
(0, mobx_1.runInAction)(() => {
|
|
1995
|
+
history.additionalTrackError = error;
|
|
1996
|
+
history.additionalTrackDone = true;
|
|
1997
|
+
});
|
|
1998
|
+
},
|
|
1999
|
+
onFulfill,
|
|
2000
|
+
onClose,
|
|
2001
|
+
onError,
|
|
2002
|
+
});
|
|
2003
|
+
return;
|
|
2004
|
+
}
|
|
2005
|
+
if (this.handleIbcRewindIfNeeded({
|
|
2006
|
+
sourceChainId: chainId,
|
|
2007
|
+
ibcHistory,
|
|
2008
|
+
packetTimeout: trackingData.packetTimeout,
|
|
2009
|
+
swapContext: ((_a = history.additionalTrackingData) === null || _a === void 0 ? void 0 : _a.type) === "cosmos-ibc"
|
|
2010
|
+
? {
|
|
2011
|
+
swapReceiver,
|
|
2012
|
+
swapChannelIndex: history.additionalTrackingData.swapChannelIndex,
|
|
2013
|
+
setSwapRefundInfo: (refundInfo) => {
|
|
2014
|
+
(0, mobx_1.runInAction)(() => {
|
|
2015
|
+
history.assetLocationInfo = Object.assign(Object.assign({}, refundInfo), { type: "refund" });
|
|
2016
|
+
});
|
|
2017
|
+
},
|
|
2018
|
+
}
|
|
2019
|
+
: undefined,
|
|
2020
|
+
onFulfill,
|
|
2021
|
+
onClose,
|
|
2022
|
+
onError,
|
|
2023
|
+
onRewindComplete: () => {
|
|
2024
|
+
// rewound되었다면 실패 처리하는 것이 맞겠지...
|
|
2025
|
+
(0, mobx_1.runInAction)(() => {
|
|
2026
|
+
var _a;
|
|
2027
|
+
history.status = types_1.SwapV2TxStatus.FAILED;
|
|
2028
|
+
history.additionalTrackDone = true;
|
|
2029
|
+
const rewoundChainId = (_a = ibcHistory.find((h) => h.rewound)) === null || _a === void 0 ? void 0 : _a.counterpartyChainId;
|
|
2030
|
+
if (rewoundChainId) {
|
|
2031
|
+
for (let i = history.simpleRoute.length - 1; i >= 0; i--) {
|
|
2032
|
+
const route = history.simpleRoute[i];
|
|
2033
|
+
if (route.chainId === rewoundChainId) {
|
|
2034
|
+
history.routeIndex = i; // 실패한 채널 인덱스로 이동
|
|
2035
|
+
break;
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
});
|
|
2040
|
+
this.trackIBCPacketForwardingRecursive((onFulfill, onClose, onError) => {
|
|
2041
|
+
this.trackSwapV2AdditionalCosmosIBCInternal(id, onFulfill, onClose, onError);
|
|
2042
|
+
});
|
|
2043
|
+
},
|
|
2044
|
+
})) {
|
|
2045
|
+
return;
|
|
2046
|
+
}
|
|
2047
|
+
this.trackIbcHopFlowWithTimeout({
|
|
2048
|
+
ibcHistory,
|
|
2049
|
+
sourceChainId: chainId,
|
|
2050
|
+
swapReceiver,
|
|
2051
|
+
destinationAsset: history.destinationAsset,
|
|
2052
|
+
onHopCompleted: (resAmount) => {
|
|
2053
|
+
(0, mobx_1.runInAction)(() => {
|
|
2054
|
+
if (resAmount) {
|
|
2055
|
+
history.resAmount.push(resAmount);
|
|
2056
|
+
}
|
|
2057
|
+
});
|
|
2058
|
+
},
|
|
2059
|
+
onAllCompleted: () => {
|
|
2060
|
+
(0, mobx_1.runInAction)(() => {
|
|
2061
|
+
history.additionalTrackDone = true;
|
|
2062
|
+
history.assetLocationInfo = undefined;
|
|
2063
|
+
// Update routeIndex to the last index to show completion state in UI
|
|
2064
|
+
history.routeIndex = history.simpleRoute.length;
|
|
2065
|
+
this.notifySwapV2Success(history);
|
|
2066
|
+
});
|
|
2067
|
+
},
|
|
2068
|
+
onContinue: () => {
|
|
2069
|
+
this.trackIBCPacketForwardingRecursive((onFulfill, onClose, onError) => {
|
|
2070
|
+
this.trackSwapV2AdditionalCosmosIBCInternal(id, onFulfill, onClose, onError);
|
|
2071
|
+
});
|
|
2072
|
+
},
|
|
2073
|
+
onRetry: () => {
|
|
2074
|
+
this.trackIBCPacketForwardingRecursive((onFulfill, onClose, onError) => {
|
|
2075
|
+
this.trackSwapV2AdditionalCosmosIBCInternal(id, onFulfill, onClose, onError);
|
|
2076
|
+
});
|
|
2077
|
+
},
|
|
2078
|
+
onPacketTimeout: () => {
|
|
2079
|
+
(0, mobx_1.runInAction)(() => {
|
|
2080
|
+
trackingData.packetTimeout = true;
|
|
2081
|
+
});
|
|
2082
|
+
},
|
|
2083
|
+
onDynamicHopDetected: () => {
|
|
2084
|
+
(0, mobx_1.runInAction)(() => {
|
|
2085
|
+
trackingData.dynamicHopDetected = true;
|
|
2086
|
+
});
|
|
2087
|
+
},
|
|
2088
|
+
onFulfill,
|
|
2089
|
+
onClose,
|
|
2090
|
+
onError,
|
|
2091
|
+
});
|
|
2092
|
+
}
|
|
2093
|
+
// ============================================================================
|
|
2094
|
+
// IBC Packet Tracking Core Functions
|
|
2095
|
+
// ============================================================================
|
|
2096
|
+
/**
|
|
2097
|
+
* IBC tx 완료 대기 및 첫 번째 hop res amount 추출
|
|
2098
|
+
*/
|
|
2099
|
+
trackIBCTxFulfillment(params) {
|
|
2100
|
+
const { chainId, txHash, ibcHistory, swapReceiver, onTxFulfilled, onTxError, onFulfill, onClose, onError, } = params;
|
|
2101
|
+
const chainInfo = this.chainsService.getChainInfo(chainId);
|
|
2102
|
+
if (!chainInfo) {
|
|
2103
|
+
onFulfill();
|
|
2104
|
+
return;
|
|
2105
|
+
}
|
|
2106
|
+
const txTracer = new cosmos_1.TendermintTxTracer(chainInfo.rpc, "/websocket");
|
|
2107
|
+
txTracer.addEventListener("close", onClose);
|
|
2108
|
+
txTracer.addEventListener("error", onError);
|
|
2109
|
+
txTracer.traceTx(buffer_1.Buffer.from(txHash, "hex")).then((tx) => {
|
|
2110
|
+
txTracer.close();
|
|
2111
|
+
if (tx.code != null && tx.code !== 0) {
|
|
2112
|
+
onTxError(tx.log || tx.raw_log || "Tx failed");
|
|
2113
|
+
onFulfill();
|
|
2114
|
+
return;
|
|
1235
2115
|
}
|
|
1236
|
-
|
|
1237
|
-
|
|
2116
|
+
let resAmount;
|
|
2117
|
+
if (swapReceiver && swapReceiver.length > 0) {
|
|
2118
|
+
resAmount = this.getIBCSwapResAmountFromTx(tx, swapReceiver[0]);
|
|
1238
2119
|
}
|
|
1239
|
-
if (
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
2120
|
+
if (ibcHistory.length > 0) {
|
|
2121
|
+
const firstChannel = ibcHistory[0];
|
|
2122
|
+
firstChannel.sequence = this.getIBCPacketSequenceFromTx(tx, firstChannel.portId, firstChannel.channelId);
|
|
2123
|
+
firstChannel.dstChannelId = this.getDstChannelIdFromTx(tx, firstChannel.portId, firstChannel.channelId);
|
|
1243
2124
|
}
|
|
1244
|
-
|
|
2125
|
+
onTxFulfilled(tx, resAmount);
|
|
2126
|
+
onFulfill();
|
|
1245
2127
|
});
|
|
1246
2128
|
}
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
2129
|
+
/**
|
|
2130
|
+
* IBC rewind 필요 여부 확인 및 rewind 처리
|
|
2131
|
+
*/
|
|
2132
|
+
handleIbcRewindIfNeeded(params) {
|
|
2133
|
+
const { sourceChainId, ibcHistory, packetTimeout, swapContext, onFulfill, onClose, onError, onRewindComplete, } = params;
|
|
2134
|
+
if (ibcHistory.length === 0) {
|
|
2135
|
+
return false;
|
|
1251
2136
|
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
return
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
2137
|
+
const needRewind = ibcHistory.find((h) => h.error != null) != null;
|
|
2138
|
+
if (!needRewind) {
|
|
2139
|
+
return false;
|
|
2140
|
+
}
|
|
2141
|
+
if (ibcHistory.find((h) => h.rewoundButNextRewindingBlocked)) {
|
|
2142
|
+
onFulfill();
|
|
2143
|
+
return true;
|
|
2144
|
+
}
|
|
2145
|
+
const isTimeoutPacket = packetTimeout !== null && packetTimeout !== void 0 ? packetTimeout : false;
|
|
2146
|
+
const lastRewoundChannelIndex = ibcHistory.findIndex((h) => {
|
|
2147
|
+
if (h.rewound) {
|
|
2148
|
+
return true;
|
|
2149
|
+
}
|
|
2150
|
+
});
|
|
2151
|
+
const targetChannel = (() => {
|
|
2152
|
+
if (lastRewoundChannelIndex >= 0) {
|
|
2153
|
+
if (lastRewoundChannelIndex === 0) {
|
|
2154
|
+
return undefined;
|
|
2155
|
+
}
|
|
2156
|
+
return ibcHistory[lastRewoundChannelIndex - 1];
|
|
2157
|
+
}
|
|
2158
|
+
return ibcHistory.find((h) => h.error != null);
|
|
2159
|
+
})();
|
|
2160
|
+
const isSwapTargetChannel = !!(targetChannel &&
|
|
2161
|
+
swapContext &&
|
|
2162
|
+
ibcHistory.indexOf(targetChannel) === swapContext.swapChannelIndex + 1);
|
|
2163
|
+
if (targetChannel !== undefined && targetChannel.sequence !== undefined) {
|
|
2164
|
+
const targetSequence = targetChannel.sequence;
|
|
2165
|
+
const prevChainInfo = (() => {
|
|
2166
|
+
const targetChannelIndex = ibcHistory.findIndex((h) => h === targetChannel);
|
|
2167
|
+
if (targetChannelIndex < 0) {
|
|
2168
|
+
return undefined;
|
|
2169
|
+
}
|
|
2170
|
+
if (targetChannelIndex === 0) {
|
|
2171
|
+
return this.chainsService.getChainInfo(sourceChainId);
|
|
2172
|
+
}
|
|
2173
|
+
return this.chainsService.getChainInfo(ibcHistory[targetChannelIndex - 1].counterpartyChainId);
|
|
2174
|
+
})();
|
|
2175
|
+
if (prevChainInfo) {
|
|
2176
|
+
const txTracer = new cosmos_1.TendermintTxTracer(prevChainInfo.rpc, "/websocket");
|
|
2177
|
+
txTracer.addEventListener("close", onClose);
|
|
2178
|
+
txTracer.addEventListener("error", onError);
|
|
2179
|
+
txTracer
|
|
2180
|
+
.traceTx(isTimeoutPacket
|
|
2181
|
+
? {
|
|
2182
|
+
// "timeout_packet.packet_src_port": targetChannel.portId,
|
|
2183
|
+
"timeout_packet.packet_src_channel": targetChannel.channelId,
|
|
2184
|
+
"timeout_packet.packet_sequence": targetChannel.sequence,
|
|
2185
|
+
}
|
|
2186
|
+
: {
|
|
2187
|
+
// "acknowledge_packet.packet_src_port": targetChannel.portId,
|
|
2188
|
+
"acknowledge_packet.packet_src_channel": targetChannel.channelId,
|
|
2189
|
+
"acknowledge_packet.packet_sequence": targetChannel.sequence,
|
|
2190
|
+
})
|
|
2191
|
+
.then((res) => {
|
|
2192
|
+
txTracer.close();
|
|
2193
|
+
if (!res) {
|
|
1258
2194
|
return;
|
|
1259
2195
|
}
|
|
1260
|
-
(0,
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
2196
|
+
(0, mobx_1.runInAction)(() => {
|
|
2197
|
+
var _a;
|
|
2198
|
+
if (isSwapTargetChannel) {
|
|
2199
|
+
const txs = res.txs
|
|
2200
|
+
? res.txs.map((res) => res.tx_result || res)
|
|
2201
|
+
: [res.tx_result || res];
|
|
2202
|
+
if (txs && Array.isArray(txs)) {
|
|
2203
|
+
for (const tx of txs) {
|
|
2204
|
+
const index = isTimeoutPacket
|
|
2205
|
+
? this.getIBCTimeoutPacketIndexFromTx(tx, targetChannel.portId, targetChannel.channelId, targetSequence)
|
|
2206
|
+
: this.getIBCAcknowledgementPacketIndexFromTx(tx, targetChannel.portId, targetChannel.channelId, targetSequence);
|
|
2207
|
+
if (index >= 0) {
|
|
2208
|
+
// 좀 빡치게 timeout packet은 refund 로직이 실행되고 나서 "timeout_packet" event가 발생한다.
|
|
2209
|
+
const refunded = isTimeoutPacket
|
|
2210
|
+
? this.getIBCSwapResAmountFromTx(tx, swapContext.swapReceiver[swapContext.swapChannelIndex + 1], (() => {
|
|
2211
|
+
const i = this.getLastIBCTimeoutPacketBeforeIndexFromTx(tx, index);
|
|
2212
|
+
if (i < 0) {
|
|
2213
|
+
return 0;
|
|
2214
|
+
}
|
|
2215
|
+
return i;
|
|
2216
|
+
})(), index)
|
|
2217
|
+
: this.getIBCSwapResAmountFromTx(tx, swapContext.swapReceiver[swapContext.swapChannelIndex + 1], index);
|
|
2218
|
+
(_a = swapContext.setSwapRefundInfo) === null || _a === void 0 ? void 0 : _a.call(swapContext, {
|
|
2219
|
+
chainId: prevChainInfo.chainId,
|
|
2220
|
+
amount: refunded,
|
|
2221
|
+
});
|
|
2222
|
+
targetChannel.rewoundButNextRewindingBlocked = true;
|
|
2223
|
+
break;
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
targetChannel.rewound = true;
|
|
1268
2229
|
});
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
waitMsAfterError: 500,
|
|
1274
|
-
maxWaitMsAfterError: 15000,
|
|
1275
|
-
});
|
|
1276
|
-
}
|
|
1277
|
-
trackDestinationAssetAmount(historyId, txHash, onFulfill) {
|
|
1278
|
-
const history = this.getRecentSkipHistory(historyId);
|
|
1279
|
-
if (!history) {
|
|
1280
|
-
onFulfill();
|
|
1281
|
-
return;
|
|
2230
|
+
onFulfill();
|
|
2231
|
+
onRewindComplete();
|
|
2232
|
+
});
|
|
2233
|
+
}
|
|
1282
2234
|
}
|
|
1283
|
-
|
|
1284
|
-
|
|
2235
|
+
return true;
|
|
2236
|
+
}
|
|
2237
|
+
trackIbcHopFlowWithTimeout(params) {
|
|
2238
|
+
const { ibcHistory, sourceChainId, swapReceiver, destinationAsset, onHopCompleted, onAllCompleted, onContinue, onRetry, onPacketTimeout, onDynamicHopDetected, onFulfill, onClose, onError, } = params;
|
|
2239
|
+
const targetChannelIndex = ibcHistory.findIndex((history) => {
|
|
2240
|
+
return !history.completed;
|
|
2241
|
+
});
|
|
2242
|
+
const targetChannel = ibcHistory[targetChannelIndex];
|
|
2243
|
+
if (!targetChannel || !targetChannel.sequence) {
|
|
1285
2244
|
onFulfill();
|
|
1286
2245
|
return;
|
|
1287
2246
|
}
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
2247
|
+
const closables = [];
|
|
2248
|
+
const closeClosables = () => {
|
|
2249
|
+
closables.forEach((closable) => {
|
|
2250
|
+
if (closable.readyState === cosmos_1.WsReadyState.OPEN ||
|
|
2251
|
+
closable.readyState === cosmos_1.WsReadyState.CONNECTING) {
|
|
2252
|
+
closable.close();
|
|
2253
|
+
}
|
|
2254
|
+
});
|
|
2255
|
+
};
|
|
2256
|
+
let _onFulfillOnce = false;
|
|
2257
|
+
const onFulfillOnce = () => {
|
|
2258
|
+
if (!_onFulfillOnce) {
|
|
2259
|
+
_onFulfillOnce = true;
|
|
2260
|
+
closeClosables();
|
|
1291
2261
|
onFulfill();
|
|
1292
|
-
return;
|
|
1293
2262
|
}
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
};
|
|
1348
|
-
searchForTransfers(res.data.result.calls || []);
|
|
1349
|
-
}
|
|
1350
|
-
if (isFoundFromCall) {
|
|
1351
|
-
history.trackDone = true;
|
|
1352
|
-
return;
|
|
1353
|
-
}
|
|
1354
|
-
const logs = txReceipt.logs;
|
|
1355
|
-
const transferTopic = (0, hash_1.id)("Transfer(address,address,uint256)");
|
|
1356
|
-
const withdrawTopic = (0, hash_1.id)("Withdrawal(address,uint256)");
|
|
1357
|
-
const hyperlaneReceiveTopic = (0, hash_1.id)("ReceivedTransferRemote(uint32,bytes32,uint256)");
|
|
1358
|
-
for (const log of logs) {
|
|
1359
|
-
if (log.topics[0] === transferTopic) {
|
|
1360
|
-
const to = "0x" + log.topics[2].slice(26);
|
|
1361
|
-
if (to.toLowerCase() === history.recipient.toLowerCase()) {
|
|
1362
|
-
const destinationAssetDenom = history.destinationAsset.denom.replace("erc20:", "");
|
|
1363
|
-
const amount = BigInt(log.data).toString(10);
|
|
1364
|
-
if (log.address === destinationAssetDenom) {
|
|
1365
|
-
history.resAmount.push([
|
|
1366
|
-
{
|
|
1367
|
-
amount,
|
|
1368
|
-
denom: history.destinationAsset.denom,
|
|
1369
|
-
},
|
|
1370
|
-
]);
|
|
1371
|
-
}
|
|
1372
|
-
else {
|
|
1373
|
-
console.log("refunded", log.address);
|
|
1374
|
-
// Transfer 토픽인 경우엔 ERC20의 tranfer 호출일텐데
|
|
1375
|
-
// 받을 토큰의 컨트랙트가 아닌 다른 컨트랙트에서 호출된 경우는 Swap을 실패한 것으로 추측
|
|
1376
|
-
// 고로 실제로 받은 토큰의 컨트랙트 주소로 환불 정보에 저장한다.
|
|
1377
|
-
history.trackError = "Swap failed";
|
|
1378
|
-
history.swapRefundInfo = {
|
|
1379
|
-
chainId: history.destinationChainId,
|
|
1380
|
-
amount: [
|
|
1381
|
-
{
|
|
1382
|
-
amount,
|
|
1383
|
-
denom: `erc20:${log.address.toLowerCase()}`,
|
|
1384
|
-
},
|
|
1385
|
-
],
|
|
1386
|
-
};
|
|
1387
|
-
}
|
|
1388
|
-
history.trackDone = true;
|
|
1389
|
-
return;
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
|
-
else if (log.topics[0] === withdrawTopic) {
|
|
1393
|
-
const to = "0x" + log.topics[1].slice(26);
|
|
1394
|
-
if (to.toLowerCase() === txReceipt.to.toLowerCase()) {
|
|
1395
|
-
const amount = BigInt(log.data).toString(10);
|
|
1396
|
-
history.resAmount.push([
|
|
1397
|
-
{ amount, denom: history.destinationAsset.denom },
|
|
1398
|
-
]);
|
|
1399
|
-
history.trackDone = true;
|
|
1400
|
-
return;
|
|
1401
|
-
}
|
|
1402
|
-
}
|
|
1403
|
-
else if (log.topics[0] === hyperlaneReceiveTopic) {
|
|
1404
|
-
const to = "0x" + log.topics[2].slice(26);
|
|
1405
|
-
if (to.toLowerCase() === history.recipient.toLowerCase()) {
|
|
1406
|
-
const amount = BigInt(log.data).toString(10);
|
|
1407
|
-
// Hyperlane을 통해 Forma로 TIA를 받는 경우 토큰 수량이 decimal 6으로 기록되는데,
|
|
1408
|
-
// Forma에서는 decimal 18이기 때문에 12자리 만큼 0을 붙여준다.
|
|
1409
|
-
history.resAmount.push([
|
|
1410
|
-
{
|
|
1411
|
-
amount: history.destinationAsset.denom === "forma-native"
|
|
1412
|
-
? `${amount}000000000000`
|
|
1413
|
-
: amount,
|
|
1414
|
-
denom: history.destinationAsset.denom,
|
|
1415
|
-
},
|
|
1416
|
-
]);
|
|
1417
|
-
history.trackDone = true;
|
|
1418
|
-
return;
|
|
1419
|
-
}
|
|
2263
|
+
};
|
|
2264
|
+
let _onCloseOnce = false;
|
|
2265
|
+
const onCloseOnce = () => {
|
|
2266
|
+
if (!_onCloseOnce) {
|
|
2267
|
+
_onCloseOnce = true;
|
|
2268
|
+
closeClosables();
|
|
2269
|
+
onClose();
|
|
2270
|
+
}
|
|
2271
|
+
};
|
|
2272
|
+
let _onErrorOnce = false;
|
|
2273
|
+
const onErrorOnce = () => {
|
|
2274
|
+
if (!_onErrorOnce) {
|
|
2275
|
+
_onErrorOnce = true;
|
|
2276
|
+
closeClosables();
|
|
2277
|
+
onError();
|
|
2278
|
+
}
|
|
2279
|
+
};
|
|
2280
|
+
const registerClosable = (closable) => closables.push(closable);
|
|
2281
|
+
let hopFailed = false;
|
|
2282
|
+
const hopTracer = this.trackIBCHopRecvPacket({
|
|
2283
|
+
ibcHistory,
|
|
2284
|
+
targetChannelIndex,
|
|
2285
|
+
swapReceiver,
|
|
2286
|
+
destinationAsset,
|
|
2287
|
+
onHopCompleted: (resAmount, tx) => {
|
|
2288
|
+
onHopCompleted === null || onHopCompleted === void 0 ? void 0 : onHopCompleted(resAmount, tx);
|
|
2289
|
+
const sequence = targetChannel.sequence;
|
|
2290
|
+
if (!sequence) {
|
|
2291
|
+
hopFailed = true;
|
|
2292
|
+
onFulfillOnce();
|
|
2293
|
+
return;
|
|
2294
|
+
}
|
|
2295
|
+
if (!tx) {
|
|
2296
|
+
return;
|
|
2297
|
+
}
|
|
2298
|
+
const txs = tx.txs
|
|
2299
|
+
? tx.txs.map((res) => res.tx_result || res)
|
|
2300
|
+
: [tx.tx_result || tx];
|
|
2301
|
+
for (const txItem of txs) {
|
|
2302
|
+
try {
|
|
2303
|
+
const ack = this.getIBCWriteAcknowledgementAckFromTx(txItem, targetChannel.portId, targetChannel.channelId, sequence);
|
|
2304
|
+
if (ack && ack.length > 0) {
|
|
2305
|
+
const str = buffer_1.Buffer.from(ack);
|
|
2306
|
+
try {
|
|
2307
|
+
const decoded = JSON.parse(str.toString());
|
|
2308
|
+
if (decoded.error) {
|
|
2309
|
+
(0, mobx_1.runInAction)(() => {
|
|
2310
|
+
targetChannel.error = "Packet processing failed";
|
|
2311
|
+
});
|
|
2312
|
+
hopFailed = true;
|
|
2313
|
+
onFulfillOnce();
|
|
2314
|
+
onRetry();
|
|
2315
|
+
return;
|
|
1420
2316
|
}
|
|
1421
2317
|
}
|
|
1422
|
-
|
|
1423
|
-
|
|
2318
|
+
catch (e) {
|
|
2319
|
+
console.log(e);
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
catch (_a) {
|
|
2324
|
+
// noop
|
|
2325
|
+
}
|
|
2326
|
+
const index = this.getIBCRecvPacketIndexFromTx(txItem, targetChannel.portId, targetChannel.channelId, sequence);
|
|
2327
|
+
if (index >= 0) {
|
|
2328
|
+
break;
|
|
2329
|
+
}
|
|
1424
2330
|
}
|
|
1425
|
-
}
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
2331
|
+
},
|
|
2332
|
+
onAllCompleted: () => {
|
|
2333
|
+
if (hopFailed) {
|
|
2334
|
+
return;
|
|
2335
|
+
}
|
|
2336
|
+
onAllCompleted();
|
|
2337
|
+
onFulfillOnce();
|
|
2338
|
+
},
|
|
2339
|
+
onContinue: () => {
|
|
2340
|
+
if (hopFailed) {
|
|
2341
|
+
return;
|
|
2342
|
+
}
|
|
2343
|
+
onContinue();
|
|
2344
|
+
onFulfillOnce();
|
|
2345
|
+
},
|
|
2346
|
+
onFulfill: onFulfillOnce,
|
|
2347
|
+
onClose: onCloseOnce,
|
|
2348
|
+
onError: onErrorOnce,
|
|
2349
|
+
onDynamicHopDetected: (hopInfo) => {
|
|
2350
|
+
if (hopFailed) {
|
|
1441
2351
|
return;
|
|
1442
2352
|
}
|
|
2353
|
+
// 동적으로 감지된 새 홉을 ibcHistory에 추가한다.
|
|
1443
2354
|
(0, mobx_1.runInAction)(() => {
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
:
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
2355
|
+
ibcHistory.push({
|
|
2356
|
+
portId: hopInfo.portId,
|
|
2357
|
+
channelId: hopInfo.channelId,
|
|
2358
|
+
counterpartyChainId: hopInfo.counterpartyChainId,
|
|
2359
|
+
sequence: hopInfo.sequence,
|
|
2360
|
+
dstChannelId: hopInfo.dstChannelId,
|
|
2361
|
+
completed: false,
|
|
2362
|
+
});
|
|
2363
|
+
if (swapReceiver && hopInfo.packetData.receiver) {
|
|
2364
|
+
swapReceiver.push(hopInfo.packetData.receiver);
|
|
2365
|
+
}
|
|
2366
|
+
});
|
|
2367
|
+
onDynamicHopDetected === null || onDynamicHopDetected === void 0 ? void 0 : onDynamicHopDetected();
|
|
2368
|
+
onContinue();
|
|
2369
|
+
onFulfillOnce();
|
|
2370
|
+
},
|
|
2371
|
+
});
|
|
2372
|
+
if (hopTracer) {
|
|
2373
|
+
registerClosable(hopTracer);
|
|
2374
|
+
}
|
|
2375
|
+
let prevChainId;
|
|
2376
|
+
if (targetChannelIndex > 0) {
|
|
2377
|
+
prevChainId = ibcHistory[targetChannelIndex - 1].counterpartyChainId;
|
|
2378
|
+
}
|
|
2379
|
+
else {
|
|
2380
|
+
prevChainId = sourceChainId;
|
|
2381
|
+
}
|
|
2382
|
+
if (prevChainId) {
|
|
2383
|
+
const prevChainInfo = this.chainsService.getChainInfo(prevChainId);
|
|
2384
|
+
if (prevChainInfo) {
|
|
2385
|
+
const queryEvents = {
|
|
2386
|
+
// acknowledge_packet과는 다르게 timeout_packet은 이전의 체인의 이벤트로부터만 알 수 있다.
|
|
2387
|
+
// 방법이 없기 때문에 여기서 이전의 체인으로부터 subscribe를 해서 이벤트를 받아야 한다.
|
|
2388
|
+
// 하지만 이 경우 ibc error tracking 로직에서 이것과 똑같은 subscription을 한번 더 하게 된다.
|
|
2389
|
+
// 이미 로직이 많이 복잡하기 때문에 로직을 덜 복잡하게 하기 위해서 이러한 비효율성(?)을 감수한다.
|
|
2390
|
+
// "timeout_packet.packet_src_port": targetChannel.portId,
|
|
2391
|
+
"timeout_packet.packet_src_channel": targetChannel.channelId,
|
|
2392
|
+
"timeout_packet.packet_sequence": targetChannel.sequence,
|
|
2393
|
+
};
|
|
2394
|
+
const txTracer = new cosmos_1.TendermintTxTracer(prevChainInfo.rpc, "/websocket");
|
|
2395
|
+
registerClosable(txTracer);
|
|
2396
|
+
txTracer.addEventListener("close", onCloseOnce);
|
|
2397
|
+
txTracer.addEventListener("error", onErrorOnce);
|
|
2398
|
+
txTracer.traceTx(queryEvents).then((res) => {
|
|
2399
|
+
txTracer.close();
|
|
2400
|
+
if (!res) {
|
|
1451
2401
|
return;
|
|
1452
2402
|
}
|
|
2403
|
+
(0, mobx_1.runInAction)(() => {
|
|
2404
|
+
targetChannel.error = "Packet timeout";
|
|
2405
|
+
onPacketTimeout === null || onPacketTimeout === void 0 ? void 0 : onPacketTimeout();
|
|
2406
|
+
hopFailed = true;
|
|
2407
|
+
onFulfillOnce();
|
|
2408
|
+
onRetry();
|
|
2409
|
+
});
|
|
1453
2410
|
});
|
|
1454
|
-
}
|
|
1455
|
-
.finally(() => {
|
|
1456
|
-
history.trackDone = true;
|
|
1457
|
-
onFulfill();
|
|
1458
|
-
});
|
|
2411
|
+
}
|
|
1459
2412
|
}
|
|
1460
2413
|
}
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
2414
|
+
trackIBCHopRecvPacket(params) {
|
|
2415
|
+
const { ibcHistory, targetChannelIndex, swapReceiver, destinationAsset, onHopCompleted, onAllCompleted, onContinue, onFulfill, onClose, onError, onDynamicHopDetected, } = params;
|
|
2416
|
+
const targetChannel = ibcHistory[targetChannelIndex];
|
|
2417
|
+
const nextChannel = targetChannelIndex + 1 < ibcHistory.length
|
|
2418
|
+
? ibcHistory[targetChannelIndex + 1]
|
|
2419
|
+
: undefined;
|
|
2420
|
+
if (!targetChannel || !targetChannel.sequence) {
|
|
2421
|
+
onAllCompleted();
|
|
2422
|
+
return;
|
|
2423
|
+
}
|
|
2424
|
+
const sequence = targetChannel.sequence;
|
|
2425
|
+
const counterpartyChainInfo = this.chainsService.getChainInfo(targetChannel.counterpartyChainId);
|
|
2426
|
+
if (!counterpartyChainInfo) {
|
|
2427
|
+
onFulfill();
|
|
2428
|
+
return;
|
|
2429
|
+
}
|
|
2430
|
+
const queryEvents = {
|
|
2431
|
+
// "recv_packet.packet_src_port": targetChannel.portId,
|
|
2432
|
+
"recv_packet.packet_dst_channel": targetChannel.dstChannelId,
|
|
2433
|
+
"recv_packet.packet_sequence": targetChannel.sequence,
|
|
2434
|
+
};
|
|
2435
|
+
const txTracer = new cosmos_1.TendermintTxTracer(counterpartyChainInfo.rpc, "/websocket");
|
|
2436
|
+
txTracer.addEventListener("close", onClose);
|
|
2437
|
+
txTracer.addEventListener("error", onError);
|
|
2438
|
+
txTracer.traceTx(queryEvents).then((res) => {
|
|
2439
|
+
txTracer.close();
|
|
2440
|
+
if (!res) {
|
|
2441
|
+
onError();
|
|
2442
|
+
return;
|
|
2443
|
+
}
|
|
2444
|
+
const txs = res.txs
|
|
2445
|
+
? res.txs.map((t) => t.tx_result || t)
|
|
2446
|
+
: [res.tx_result || res];
|
|
2447
|
+
const matchedTx = txs.find((tx) => {
|
|
2448
|
+
const idx = this.getIBCRecvPacketIndexFromTx(tx, targetChannel.portId, targetChannel.channelId, sequence);
|
|
2449
|
+
return idx >= 0;
|
|
2450
|
+
});
|
|
2451
|
+
const tx = matchedTx || txs[0];
|
|
2452
|
+
targetChannel.completed = true;
|
|
2453
|
+
let resAmount;
|
|
2454
|
+
const receiverIndex = targetChannelIndex + 1;
|
|
2455
|
+
if (tx && swapReceiver && receiverIndex < swapReceiver.length) {
|
|
2456
|
+
const index = this.getIBCRecvPacketIndexFromTx(tx, targetChannel.portId, targetChannel.channelId, sequence);
|
|
2457
|
+
if (index >= 0) {
|
|
2458
|
+
resAmount = this.getIBCSwapResAmountFromTx(tx, swapReceiver[receiverIndex], index, undefined);
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
onHopCompleted(resAmount, tx);
|
|
2462
|
+
// 1. 다음 채널이 있고, tx가 있는 경우 => 다음 채널로 이동
|
|
2463
|
+
// 2. 마지막 채널이면서, destinationAsset가 있고, onDynamicHopDetected가 있는 경우 => 동적 홉 감지
|
|
2464
|
+
// 3. 그 외의 경우 => 종료(allCompleted)
|
|
2465
|
+
if (nextChannel && tx) {
|
|
2466
|
+
const index = this.getIBCRecvPacketIndexFromTx(tx, targetChannel.portId, targetChannel.channelId, sequence);
|
|
2467
|
+
if (index >= 0) {
|
|
2468
|
+
nextChannel.sequence = this.getIBCPacketSequenceFromTx(tx, nextChannel.portId, nextChannel.channelId, index);
|
|
2469
|
+
nextChannel.dstChannelId = this.getDstChannelIdFromTx(tx, nextChannel.portId, nextChannel.channelId, index);
|
|
2470
|
+
}
|
|
2471
|
+
onContinue();
|
|
2472
|
+
}
|
|
2473
|
+
else if (tx && destinationAsset && onDynamicHopDetected) {
|
|
2474
|
+
// 마지막 채널에서 recv_packet 이후 새로운 send_packet이 있는 경우를 감지
|
|
2475
|
+
const index = this.getIBCRecvPacketIndexFromTx(tx, targetChannel.portId, targetChannel.channelId, sequence);
|
|
2476
|
+
if (index >= 0) {
|
|
2477
|
+
const sendPackets = this.getSendPacketInfoFromTx(tx, index);
|
|
2478
|
+
if (sendPackets.length > 0) {
|
|
2479
|
+
// 마지막 send_packet을 확인
|
|
2480
|
+
const lastSendPacket = sendPackets[sendPackets.length - 1];
|
|
2481
|
+
// destination chain에서 IBC wrapped token을 unwrap하는 경우를 가정한다.
|
|
2482
|
+
// 예를 들어, source chain -> osmosis (swap) -> destination chain으로 이동하는 경우,
|
|
2483
|
+
// osmosis에서 IBC wrapped token이 destination chain으로 전송되고,
|
|
2484
|
+
// destination chain에서 IBC wrapped token을 unwrap하는 케이스가 있을 수 있다.
|
|
2485
|
+
const destinationChainInfo = this.chainsService.getChainInfo(destinationAsset.chainId);
|
|
2486
|
+
if (destinationChainInfo) {
|
|
2487
|
+
onDynamicHopDetected({
|
|
2488
|
+
portId: "transfer",
|
|
2489
|
+
channelId: lastSendPacket.srcChannel,
|
|
2490
|
+
counterpartyChainId: destinationChainInfo.chainId,
|
|
2491
|
+
sequence: lastSendPacket.sequence,
|
|
2492
|
+
dstChannelId: lastSendPacket.dstChannel,
|
|
2493
|
+
packetData: lastSendPacket.packetData,
|
|
2494
|
+
});
|
|
2495
|
+
return;
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
onAllCompleted();
|
|
2500
|
+
}
|
|
2501
|
+
else {
|
|
2502
|
+
onAllCompleted();
|
|
2503
|
+
}
|
|
2504
|
+
});
|
|
2505
|
+
return txTracer;
|
|
1466
2506
|
}
|
|
1467
2507
|
getIBCWriteAcknowledgementAckFromTx(tx, sourcePortId, sourceChannelId, sequence) {
|
|
1468
2508
|
const events = tx.events;
|
|
@@ -1597,6 +2637,92 @@ class RecentSendHistoryService {
|
|
|
1597
2637
|
}
|
|
1598
2638
|
return [];
|
|
1599
2639
|
}
|
|
2640
|
+
/**
|
|
2641
|
+
* TX에서 send_packet 이벤트 정보를 추출하여 추가 IBC 홉을 감지하기 위해 사용됩니다.
|
|
2642
|
+
*
|
|
2643
|
+
* 예를 들어, 마지막 목적지 체인에서 IBC wrapped token을 unwrap하는 경우,
|
|
2644
|
+
* send_packet 이벤트를 통해 추가 IBC 홉을 감지할 수 있습니다.
|
|
2645
|
+
*/
|
|
2646
|
+
getSendPacketInfoFromTx(tx, startEventsIndex = 0) {
|
|
2647
|
+
var _a, _b, _c, _d;
|
|
2648
|
+
const events = tx.events;
|
|
2649
|
+
if (!events || !Array.isArray(events)) {
|
|
2650
|
+
return [];
|
|
2651
|
+
}
|
|
2652
|
+
const compareStringWithBase64OrPlain = (target, value) => {
|
|
2653
|
+
if (target === value) {
|
|
2654
|
+
return [true, false];
|
|
2655
|
+
}
|
|
2656
|
+
if (target === buffer_1.Buffer.from(value).toString("base64")) {
|
|
2657
|
+
return [true, true];
|
|
2658
|
+
}
|
|
2659
|
+
return [false, false];
|
|
2660
|
+
};
|
|
2661
|
+
const results = [];
|
|
2662
|
+
const slicedEvents = events.slice(startEventsIndex);
|
|
2663
|
+
for (const event of slicedEvents) {
|
|
2664
|
+
if (event.type !== "send_packet") {
|
|
2665
|
+
continue;
|
|
2666
|
+
}
|
|
2667
|
+
let isBase64 = false;
|
|
2668
|
+
const srcChannelAttr = (_a = event.attributes) === null || _a === void 0 ? void 0 : _a.find((attr) => {
|
|
2669
|
+
const c = compareStringWithBase64OrPlain(attr.key, "packet_src_channel");
|
|
2670
|
+
isBase64 = c[1];
|
|
2671
|
+
return c[0];
|
|
2672
|
+
});
|
|
2673
|
+
if (!srcChannelAttr) {
|
|
2674
|
+
continue;
|
|
2675
|
+
}
|
|
2676
|
+
const dstChannelAttr = (_b = event.attributes) === null || _b === void 0 ? void 0 : _b.find((attr) => {
|
|
2677
|
+
return compareStringWithBase64OrPlain(attr.key, "packet_dst_channel")[0];
|
|
2678
|
+
});
|
|
2679
|
+
if (!dstChannelAttr)
|
|
2680
|
+
continue;
|
|
2681
|
+
const sequenceAttr = (_c = event.attributes) === null || _c === void 0 ? void 0 : _c.find((attr) => {
|
|
2682
|
+
return compareStringWithBase64OrPlain(attr.key, "packet_sequence")[0];
|
|
2683
|
+
});
|
|
2684
|
+
if (!sequenceAttr)
|
|
2685
|
+
continue;
|
|
2686
|
+
const packetDataAttr = (_d = event.attributes) === null || _d === void 0 ? void 0 : _d.find((attr) => {
|
|
2687
|
+
return compareStringWithBase64OrPlain(attr.key, "packet_data")[0];
|
|
2688
|
+
});
|
|
2689
|
+
if (!packetDataAttr)
|
|
2690
|
+
continue;
|
|
2691
|
+
try {
|
|
2692
|
+
const srcChannel = isBase64
|
|
2693
|
+
? buffer_1.Buffer.from(srcChannelAttr.value, "base64").toString()
|
|
2694
|
+
: srcChannelAttr.value;
|
|
2695
|
+
const dstChannel = isBase64
|
|
2696
|
+
? buffer_1.Buffer.from(dstChannelAttr.value, "base64").toString()
|
|
2697
|
+
: dstChannelAttr.value;
|
|
2698
|
+
const sequence = isBase64
|
|
2699
|
+
? buffer_1.Buffer.from(sequenceAttr.value, "base64").toString()
|
|
2700
|
+
: sequenceAttr.value;
|
|
2701
|
+
const packetDataValue = isBase64
|
|
2702
|
+
? buffer_1.Buffer.from(packetDataAttr.value, "base64").toString()
|
|
2703
|
+
: packetDataAttr.value.startsWith("{")
|
|
2704
|
+
? packetDataAttr.value
|
|
2705
|
+
: buffer_1.Buffer.from(packetDataAttr.value, "base64").toString();
|
|
2706
|
+
const packetData = JSON.parse(packetDataValue);
|
|
2707
|
+
if (packetData.denom && packetData.amount && packetData.receiver) {
|
|
2708
|
+
results.push({
|
|
2709
|
+
srcChannel,
|
|
2710
|
+
dstChannel,
|
|
2711
|
+
sequence,
|
|
2712
|
+
packetData: {
|
|
2713
|
+
denom: packetData.denom,
|
|
2714
|
+
amount: packetData.amount,
|
|
2715
|
+
receiver: packetData.receiver,
|
|
2716
|
+
},
|
|
2717
|
+
});
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
catch (_e) {
|
|
2721
|
+
// noop
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
return results;
|
|
2725
|
+
}
|
|
1600
2726
|
getIBCAcknowledgementPacketIndexFromTx(tx, sourcePortId, sourceChannelId, sequence) {
|
|
1601
2727
|
const events = tx.events;
|
|
1602
2728
|
if (!events) {
|
|
@@ -1942,6 +3068,129 @@ class RecentSendHistoryService {
|
|
|
1942
3068
|
}
|
|
1943
3069
|
throw new Error("Invalid tx");
|
|
1944
3070
|
}
|
|
3071
|
+
removeIBCHistoriesByChainIdentifier(chainIdentifier) {
|
|
3072
|
+
const removingIds = [];
|
|
3073
|
+
for (const history of this.recentIBCHistoryMap.values()) {
|
|
3074
|
+
if (cosmos_1.ChainIdHelper.parse(history.chainId).identifier === chainIdentifier) {
|
|
3075
|
+
removingIds.push(history.id);
|
|
3076
|
+
continue;
|
|
3077
|
+
}
|
|
3078
|
+
if (cosmos_1.ChainIdHelper.parse(history.destinationChainId).identifier ===
|
|
3079
|
+
chainIdentifier) {
|
|
3080
|
+
removingIds.push(history.id);
|
|
3081
|
+
continue;
|
|
3082
|
+
}
|
|
3083
|
+
if (history.ibcHistory.some((history) => {
|
|
3084
|
+
return (cosmos_1.ChainIdHelper.parse(history.counterpartyChainId).identifier ===
|
|
3085
|
+
chainIdentifier);
|
|
3086
|
+
})) {
|
|
3087
|
+
removingIds.push(history.id);
|
|
3088
|
+
continue;
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
for (const id of removingIds) {
|
|
3092
|
+
this.recentIBCHistoryMap.delete(id);
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
removeSkipHistoriesByChainIdentifier(chainIdentifier) {
|
|
3096
|
+
const removingIds = [];
|
|
3097
|
+
for (const history of this.recentSkipHistoryMap.values()) {
|
|
3098
|
+
if (cosmos_1.ChainIdHelper.parse(history.chainId).identifier === chainIdentifier) {
|
|
3099
|
+
removingIds.push(history.id);
|
|
3100
|
+
continue;
|
|
3101
|
+
}
|
|
3102
|
+
if (cosmos_1.ChainIdHelper.parse(history.destinationChainId).identifier ===
|
|
3103
|
+
chainIdentifier) {
|
|
3104
|
+
removingIds.push(history.id);
|
|
3105
|
+
continue;
|
|
3106
|
+
}
|
|
3107
|
+
if (cosmos_1.ChainIdHelper.parse(history.destinationAsset.chainId).identifier ===
|
|
3108
|
+
chainIdentifier) {
|
|
3109
|
+
removingIds.push(history.id);
|
|
3110
|
+
continue;
|
|
3111
|
+
}
|
|
3112
|
+
if (history.simpleRoute.some((route) => cosmos_1.ChainIdHelper.parse(route.chainId).identifier === chainIdentifier)) {
|
|
3113
|
+
removingIds.push(history.id);
|
|
3114
|
+
continue;
|
|
3115
|
+
}
|
|
3116
|
+
if (history.swapRefundInfo &&
|
|
3117
|
+
cosmos_1.ChainIdHelper.parse(history.swapRefundInfo.chainId).identifier ===
|
|
3118
|
+
chainIdentifier) {
|
|
3119
|
+
removingIds.push(history.id);
|
|
3120
|
+
continue;
|
|
3121
|
+
}
|
|
3122
|
+
if (history.transferAssetRelease) {
|
|
3123
|
+
const chainId = history.transferAssetRelease.chain_id;
|
|
3124
|
+
const isOnlyEvm = parseInt(chainId) > 0;
|
|
3125
|
+
const chainIdInKeplr = isOnlyEvm ? `eip155:${chainId}` : chainId;
|
|
3126
|
+
if (cosmos_1.ChainIdHelper.parse(chainIdInKeplr).identifier === chainIdentifier) {
|
|
3127
|
+
removingIds.push(history.id);
|
|
3128
|
+
continue;
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
}
|
|
3132
|
+
for (const id of removingIds) {
|
|
3133
|
+
this.recentSkipHistoryMap.delete(id);
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3136
|
+
removeSwapV2HistoriesByChainIdentifier(chainIdentifier) {
|
|
3137
|
+
const removingIds = [];
|
|
3138
|
+
for (const history of this.recentSwapV2HistoryMap.values()) {
|
|
3139
|
+
if (cosmos_1.ChainIdHelper.parse(history.fromChainId).identifier === chainIdentifier) {
|
|
3140
|
+
removingIds.push(history.id);
|
|
3141
|
+
continue;
|
|
3142
|
+
}
|
|
3143
|
+
if (cosmos_1.ChainIdHelper.parse(history.toChainId).identifier === chainIdentifier) {
|
|
3144
|
+
removingIds.push(history.id);
|
|
3145
|
+
continue;
|
|
3146
|
+
}
|
|
3147
|
+
if (history.simpleRoute.some((route) => cosmos_1.ChainIdHelper.parse(route.chainId).identifier === chainIdentifier)) {
|
|
3148
|
+
removingIds.push(history.id);
|
|
3149
|
+
continue;
|
|
3150
|
+
}
|
|
3151
|
+
if (history.assetLocationInfo &&
|
|
3152
|
+
cosmos_1.ChainIdHelper.parse(history.assetLocationInfo.chainId).identifier ===
|
|
3153
|
+
chainIdentifier) {
|
|
3154
|
+
removingIds.push(history.id);
|
|
3155
|
+
continue;
|
|
3156
|
+
}
|
|
3157
|
+
if (history.additionalTrackingData) {
|
|
3158
|
+
if (cosmos_1.ChainIdHelper.parse(history.additionalTrackingData.chainId)
|
|
3159
|
+
.identifier === chainIdentifier) {
|
|
3160
|
+
removingIds.push(history.id);
|
|
3161
|
+
continue;
|
|
3162
|
+
}
|
|
3163
|
+
if (history.additionalTrackingData.type === "cosmos-ibc" &&
|
|
3164
|
+
history.additionalTrackingData.ibcHistory.some((h) => {
|
|
3165
|
+
return (cosmos_1.ChainIdHelper.parse(h.counterpartyChainId).identifier ===
|
|
3166
|
+
chainIdentifier);
|
|
3167
|
+
})) {
|
|
3168
|
+
removingIds.push(history.id);
|
|
3169
|
+
continue;
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3173
|
+
for (const id of removingIds) {
|
|
3174
|
+
this.recentSwapV2HistoryMap.delete(id);
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
// ============================================================================
|
|
3178
|
+
// Helper Functions
|
|
3179
|
+
// ============================================================================
|
|
3180
|
+
getExecutableChainIdsFromSwapV2History(history, includeAllChainIds = false) {
|
|
3181
|
+
const chainIds = [];
|
|
3182
|
+
const endIndex = includeAllChainIds
|
|
3183
|
+
? history.simpleRoute.length
|
|
3184
|
+
: Math.max(0, history.routeIndex + 1);
|
|
3185
|
+
for (let i = 0; i < endIndex; i++) {
|
|
3186
|
+
chainIds.push(history.simpleRoute[i].chainId);
|
|
3187
|
+
}
|
|
3188
|
+
if (history.assetLocationInfo &&
|
|
3189
|
+
history.assetLocationInfo.type === "intermediate") {
|
|
3190
|
+
chainIds.push(history.assetLocationInfo.chainId);
|
|
3191
|
+
}
|
|
3192
|
+
return chainIds;
|
|
3193
|
+
}
|
|
1945
3194
|
}
|
|
1946
3195
|
__decorate([
|
|
1947
3196
|
mobx_1.observable
|
|
@@ -1958,6 +3207,12 @@ __decorate([
|
|
|
1958
3207
|
__decorate([
|
|
1959
3208
|
mobx_1.observable
|
|
1960
3209
|
], RecentSendHistoryService.prototype, "recentSkipHistoryMap", void 0);
|
|
3210
|
+
__decorate([
|
|
3211
|
+
mobx_1.observable
|
|
3212
|
+
], RecentSendHistoryService.prototype, "recentSwapV2HistorySeq", void 0);
|
|
3213
|
+
__decorate([
|
|
3214
|
+
mobx_1.observable
|
|
3215
|
+
], RecentSendHistoryService.prototype, "recentSwapV2HistoryMap", void 0);
|
|
1961
3216
|
__decorate([
|
|
1962
3217
|
mobx_1.action
|
|
1963
3218
|
], RecentSendHistoryService.prototype, "addRecentSendHistory", null);
|
|
@@ -1982,5 +3237,41 @@ __decorate([
|
|
|
1982
3237
|
__decorate([
|
|
1983
3238
|
mobx_1.action
|
|
1984
3239
|
], RecentSendHistoryService.prototype, "clearAllRecentSkipHistory", null);
|
|
3240
|
+
__decorate([
|
|
3241
|
+
mobx_1.action
|
|
3242
|
+
], RecentSendHistoryService.prototype, "recordTxWithSwapV2", null);
|
|
3243
|
+
__decorate([
|
|
3244
|
+
mobx_1.action
|
|
3245
|
+
], RecentSendHistoryService.prototype, "processSwapV2StatusResponse", null);
|
|
3246
|
+
__decorate([
|
|
3247
|
+
mobx_1.action
|
|
3248
|
+
], RecentSendHistoryService.prototype, "removeRecentSwapV2History", null);
|
|
3249
|
+
__decorate([
|
|
3250
|
+
mobx_1.action
|
|
3251
|
+
], RecentSendHistoryService.prototype, "clearAllRecentSwapV2History", null);
|
|
3252
|
+
__decorate([
|
|
3253
|
+
mobx_1.action
|
|
3254
|
+
], RecentSendHistoryService.prototype, "hideSwapV2History", null);
|
|
3255
|
+
__decorate([
|
|
3256
|
+
mobx_1.action
|
|
3257
|
+
], RecentSendHistoryService.prototype, "showSwapV2History", null);
|
|
3258
|
+
__decorate([
|
|
3259
|
+
mobx_1.action
|
|
3260
|
+
], RecentSendHistoryService.prototype, "setSwapV2HistoryError", null);
|
|
3261
|
+
__decorate([
|
|
3262
|
+
mobx_1.action
|
|
3263
|
+
], RecentSendHistoryService.prototype, "clearSwapV2HistoryBackgroundExecutionId", null);
|
|
3264
|
+
__decorate([
|
|
3265
|
+
mobx_1.action
|
|
3266
|
+
], RecentSendHistoryService.prototype, "setSwapV2AdditionalTrackingData", null);
|
|
3267
|
+
__decorate([
|
|
3268
|
+
mobx_1.action
|
|
3269
|
+
], RecentSendHistoryService.prototype, "removeIBCHistoriesByChainIdentifier", null);
|
|
3270
|
+
__decorate([
|
|
3271
|
+
mobx_1.action
|
|
3272
|
+
], RecentSendHistoryService.prototype, "removeSkipHistoriesByChainIdentifier", null);
|
|
3273
|
+
__decorate([
|
|
3274
|
+
mobx_1.action
|
|
3275
|
+
], RecentSendHistoryService.prototype, "removeSwapV2HistoriesByChainIdentifier", null);
|
|
1985
3276
|
exports.RecentSendHistoryService = RecentSendHistoryService;
|
|
1986
3277
|
//# sourceMappingURL=service.js.map
|