@keplr-wallet/background 0.12.313 → 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.
Files changed (83) hide show
  1. package/build/index.d.ts +1 -0
  2. package/build/index.js +7 -1
  3. package/build/index.js.map +1 -1
  4. package/build/keyring-cosmos/service.d.ts +10 -0
  5. package/build/keyring-cosmos/service.js +100 -0
  6. package/build/keyring-cosmos/service.js.map +1 -1
  7. package/build/keyring-ethereum/service.d.ts +5 -0
  8. package/build/keyring-ethereum/service.js +66 -0
  9. package/build/keyring-ethereum/service.js.map +1 -1
  10. package/build/recent-send-history/api.d.ts +31 -0
  11. package/build/recent-send-history/api.js +97 -0
  12. package/build/recent-send-history/api.js.map +1 -0
  13. package/build/recent-send-history/handler.js +36 -0
  14. package/build/recent-send-history/handler.js.map +1 -1
  15. package/build/recent-send-history/init.js +5 -0
  16. package/build/recent-send-history/init.js.map +1 -1
  17. package/build/recent-send-history/messages.d.ts +76 -1
  18. package/build/recent-send-history/messages.js +121 -1
  19. package/build/recent-send-history/messages.js.map +1 -1
  20. package/build/recent-send-history/service.d.ts +262 -9
  21. package/build/recent-send-history/service.js +2103 -812
  22. package/build/recent-send-history/service.js.map +1 -1
  23. package/build/recent-send-history/types.d.ts +214 -22
  24. package/build/recent-send-history/types.js +21 -0
  25. package/build/recent-send-history/types.js.map +1 -1
  26. package/build/tx/service.d.ts +2 -0
  27. package/build/tx/service.js +35 -0
  28. package/build/tx/service.js.map +1 -1
  29. package/build/tx-ethereum/service.d.ts +2 -0
  30. package/build/tx-ethereum/service.js +42 -0
  31. package/build/tx-ethereum/service.js.map +1 -1
  32. package/build/tx-executor/constants.d.ts +1 -0
  33. package/build/tx-executor/constants.js +5 -0
  34. package/build/tx-executor/constants.js.map +1 -0
  35. package/build/tx-executor/handler.d.ts +3 -0
  36. package/build/tx-executor/handler.js +45 -0
  37. package/build/tx-executor/handler.js.map +1 -0
  38. package/build/tx-executor/index.d.ts +3 -0
  39. package/build/tx-executor/index.js +20 -0
  40. package/build/tx-executor/index.js.map +1 -0
  41. package/build/tx-executor/init.d.ts +3 -0
  42. package/build/tx-executor/init.js +14 -0
  43. package/build/tx-executor/init.js.map +1 -0
  44. package/build/tx-executor/internal.d.ts +4 -0
  45. package/build/tx-executor/internal.js +24 -0
  46. package/build/tx-executor/internal.js.map +1 -0
  47. package/build/tx-executor/messages.d.ts +53 -0
  48. package/build/tx-executor/messages.js +116 -0
  49. package/build/tx-executor/messages.js.map +1 -0
  50. package/build/tx-executor/service.d.ts +67 -0
  51. package/build/tx-executor/service.js +715 -0
  52. package/build/tx-executor/service.js.map +1 -0
  53. package/build/tx-executor/types.d.ts +105 -0
  54. package/build/tx-executor/types.js +33 -0
  55. package/build/tx-executor/types.js.map +1 -0
  56. package/build/tx-executor/utils/cosmos.d.ts +59 -0
  57. package/build/tx-executor/utils/cosmos.js +526 -0
  58. package/build/tx-executor/utils/cosmos.js.map +1 -0
  59. package/build/tx-executor/utils/evm.d.ts +4 -0
  60. package/build/tx-executor/utils/evm.js +236 -0
  61. package/build/tx-executor/utils/evm.js.map +1 -0
  62. package/package.json +13 -13
  63. package/src/index.ts +24 -1
  64. package/src/keyring-cosmos/service.ts +151 -0
  65. package/src/keyring-ethereum/service.ts +103 -6
  66. package/src/recent-send-history/api.ts +119 -0
  67. package/src/recent-send-history/handler.ts +84 -0
  68. package/src/recent-send-history/init.ts +10 -0
  69. package/src/recent-send-history/messages.ts +163 -1
  70. package/src/recent-send-history/service.ts +3042 -1153
  71. package/src/recent-send-history/types.ts +268 -31
  72. package/src/tx/service.ts +41 -0
  73. package/src/tx-ethereum/service.ts +57 -0
  74. package/src/tx-executor/constants.ts +1 -0
  75. package/src/tx-executor/handler.ts +71 -0
  76. package/src/tx-executor/index.ts +3 -0
  77. package/src/tx-executor/init.ts +20 -0
  78. package/src/tx-executor/internal.ts +9 -0
  79. package/src/tx-executor/messages.ts +157 -0
  80. package/src/tx-executor/service.ts +1025 -0
  81. package/src/tx-executor/types.ts +161 -0
  82. package/src/tx-executor/utils/cosmos.ts +771 -0
  83. package/src/tx-executor/utils/evm.ts +310 -0
@@ -0,0 +1,715 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
9
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
10
+ return new (P || (P = Promise))(function (resolve, reject) {
11
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
12
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
13
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
14
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
15
+ });
16
+ };
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.BackgroundTxExecutorService = void 0;
19
+ const chains_1 = require("../chains");
20
+ const router_1 = require("@keplr-wallet/router");
21
+ const types_1 = require("./types");
22
+ const mobx_1 = require("mobx");
23
+ const types_2 = require("@keplr-wallet/types");
24
+ const transactions_1 = require("@ethersproject/transactions");
25
+ const cosmos_1 = require("@keplr-wallet/cosmos");
26
+ const tx_1 = require("@keplr-wallet/proto-types/cosmos/tx/v1beta1/tx");
27
+ const cosmos_2 = require("./utils/cosmos");
28
+ const evm_1 = require("./utils/evm");
29
+ class BackgroundTxExecutorService {
30
+ constructor(kvStore, chainsService, keyRingCosmosService, keyRingEthereumService, backgroundTxService, backgroundTxEthereumService, analyticsService, recentSendHistoryService, subscriber) {
31
+ this.kvStore = kvStore;
32
+ this.chainsService = chainsService;
33
+ this.keyRingCosmosService = keyRingCosmosService;
34
+ this.keyRingEthereumService = keyRingEthereumService;
35
+ this.backgroundTxService = backgroundTxService;
36
+ this.backgroundTxEthereumService = backgroundTxEthereumService;
37
+ this.analyticsService = analyticsService;
38
+ this.recentSendHistoryService = recentSendHistoryService;
39
+ this.subscriber = subscriber;
40
+ this.recentTxExecutionSeq = 0;
41
+ // Key: id (sequence, it should be increased by 1 for each)
42
+ this.recentTxExecutionMap = new Map();
43
+ (0, mobx_1.makeObservable)(this);
44
+ }
45
+ init() {
46
+ return __awaiter(this, void 0, void 0, function* () {
47
+ const recentTxExecutionSeqSaved = yield this.kvStore.get("recentTxExecutionSeq");
48
+ if (recentTxExecutionSeqSaved) {
49
+ (0, mobx_1.runInAction)(() => {
50
+ this.recentTxExecutionSeq = recentTxExecutionSeqSaved;
51
+ });
52
+ }
53
+ (0, mobx_1.autorun)(() => {
54
+ const js = (0, mobx_1.toJS)(this.recentTxExecutionSeq);
55
+ this.kvStore.set("recentTxExecutionSeq", js);
56
+ });
57
+ const recentTxExecutionMapSaved = yield this.kvStore.get("recentSerializedTxExecutionMap");
58
+ if (recentTxExecutionMapSaved) {
59
+ (0, mobx_1.runInAction)(() => {
60
+ const entries = Object.entries(recentTxExecutionMapSaved);
61
+ const sorted = entries
62
+ .map(([key, value]) => [key, JSON.parse(value)])
63
+ .sort(([, a], [, b]) => parseInt(a.id) - parseInt(b.id));
64
+ for (const [key, execution] of sorted) {
65
+ this.recentTxExecutionMap.set(key, execution);
66
+ }
67
+ this.cleanupOldExecutions();
68
+ });
69
+ }
70
+ (0, mobx_1.autorun)(() => {
71
+ const js = (0, mobx_1.toJS)(this.recentTxExecutionMap);
72
+ const serialized = {};
73
+ for (const [key, value] of js) {
74
+ // only persist executions that are BLOCKED
75
+ if (value.status === types_1.TxExecutionStatus.BLOCKED) {
76
+ serialized[key] = JSON.stringify(value);
77
+ }
78
+ }
79
+ this.kvStore
80
+ .set("recentSerializedTxExecutionMap", serialized)
81
+ .catch((e) => {
82
+ console.error("[TxExecutor] kvStore save failed:", e);
83
+ });
84
+ });
85
+ this.subscriber.subscribe((event) => this.handleTxExecutionEvent(event));
86
+ });
87
+ }
88
+ handleTxExecutionEvent(event) {
89
+ if (event.type === "remove") {
90
+ this.removeTxExecution(event.executionId);
91
+ return;
92
+ }
93
+ const { executionId, executableChainIds } = event;
94
+ const execution = this.getTxExecution(executionId);
95
+ if (!execution) {
96
+ return;
97
+ }
98
+ const newExecutableChainIds = executableChainIds.filter((chainId) => !execution.executableChainIds.includes(chainId));
99
+ if (newExecutableChainIds.length === 0) {
100
+ return;
101
+ }
102
+ // update the executable chain ids
103
+ execution.executableChainIds = Array.from(new Set([...execution.executableChainIds, ...newExecutableChainIds]));
104
+ // if there is a pending tx that is executable, force display the swap v2 history
105
+ if (execution.type === types_1.TxExecutionType.SWAP_V2 &&
106
+ execution.historyId != null) {
107
+ const hasExecutableTx = execution.txs.some((tx) => (tx.status === types_1.BackgroundTxStatus.PENDING ||
108
+ tx.status === types_1.BackgroundTxStatus.BLOCKED) &&
109
+ execution.executableChainIds.includes(tx.chainId));
110
+ if (hasExecutableTx) {
111
+ this.recentSendHistoryService.showSwapV2History(execution.historyId);
112
+ }
113
+ }
114
+ }
115
+ recordAndExecuteTxs(env, vaultId, type, txs, executableChainIds, historyData, historyTxIndex) {
116
+ return __awaiter(this, void 0, void 0, function* () {
117
+ if (!env.isInternalMsg) {
118
+ throw new router_1.KeplrError("direct-tx-executor", 101, "Not internal message");
119
+ }
120
+ const keyInfo = this.keyRingCosmosService.keyRingService.getKeyInfo(vaultId);
121
+ if (!keyInfo) {
122
+ throw new router_1.KeplrError("direct-tx-executor", 120, "Key info not found");
123
+ }
124
+ // If any of the transactions are not executable or the key is hardware wallet,
125
+ // auto sign is disabled.
126
+ const preventAutoSign = txs.some((tx) => !executableChainIds.includes(tx.chainId)) ||
127
+ keyInfo.type === "ledger" ||
128
+ keyInfo.type === "keystone";
129
+ /**
130
+ * If preventAutoSign is true, at least one executable transaction must already be signed.
131
+ * For example, in an EVM bundle (like ERC20 approve + swap) where simulation is not possible,
132
+ * the UI might execute 'approve' (tx[0]) first and set its txHash, then sign the swap (tx[1]).
133
+ * Both tx[0] and tx[1] are executable, but tx[0] has already been executed and doesn't need to be signed again.
134
+ * So, ensure that at least one executable tx is already signed before proceeding.
135
+ */
136
+ if (preventAutoSign) {
137
+ const executableTxs = txs.filter((tx) => executableChainIds.includes(tx.chainId));
138
+ if (executableTxs.length === 0) {
139
+ throw new router_1.KeplrError("direct-tx-executor", 122, "No executable txs");
140
+ }
141
+ if (executableTxs.every((tx) => tx.signedTx == null)) {
142
+ throw new router_1.KeplrError("direct-tx-executor", 123, "No signed txs found with preventAutoSign");
143
+ }
144
+ }
145
+ const id = (0, mobx_1.runInAction)(() => {
146
+ return (this.recentTxExecutionSeq++).toString();
147
+ });
148
+ const execution = Object.assign({ id, status: types_1.TxExecutionStatus.PENDING, vaultId: vaultId, txs, txIndex: -1, executableChainIds: executableChainIds, timestamp: Date.now(), type,
149
+ preventAutoSign,
150
+ historyTxIndex }, (type !== types_1.TxExecutionType.UNDEFINED ? { historyData } : {}));
151
+ (0, mobx_1.runInAction)(() => {
152
+ this.recentTxExecutionMap.set(id, execution);
153
+ });
154
+ return yield this.executeTxs(id);
155
+ });
156
+ }
157
+ /**
158
+ * Execute blocked transactions by execution id and transaction index
159
+ */
160
+ resumeTx(env, id, txIndex, signedTx, ibcSwapData) {
161
+ return __awaiter(this, void 0, void 0, function* () {
162
+ if (!env.isInternalMsg) {
163
+ throw new router_1.KeplrError("direct-tx-executor", 101, "Not internal message");
164
+ }
165
+ return yield this.executeTxs(id, {
166
+ txIndex,
167
+ signedTx,
168
+ ibcSwapData,
169
+ });
170
+ });
171
+ }
172
+ executeTxs(id, options) {
173
+ var _a, _b, _c;
174
+ return __awaiter(this, void 0, void 0, function* () {
175
+ const execution = this.getTxExecution(id);
176
+ if (!execution) {
177
+ throw new router_1.KeplrError("direct-tx-executor", 121, "Execution not found");
178
+ }
179
+ if (execution.status === types_1.TxExecutionStatus.PROCESSING) {
180
+ throw new router_1.KeplrError("direct-tx-executor", 130, "Execution is already processing");
181
+ }
182
+ // Only pending or blocked executions can be executed
183
+ const needExecute = execution.status === types_1.TxExecutionStatus.PENDING ||
184
+ execution.status === types_1.TxExecutionStatus.BLOCKED;
185
+ if (!needExecute) {
186
+ return {
187
+ status: execution.status,
188
+ };
189
+ }
190
+ const keyInfo = this.keyRingCosmosService.keyRingService.getKeyInfo(execution.vaultId);
191
+ if (!keyInfo) {
192
+ throw new router_1.KeplrError("direct-tx-executor", 120, "Key info not found");
193
+ }
194
+ const executionStartIndex = Math.min((_a = options === null || options === void 0 ? void 0 : options.txIndex) !== null && _a !== void 0 ? _a : (execution.txIndex < 0 ? 0 : execution.txIndex), execution.txs.length - 1);
195
+ (0, mobx_1.runInAction)(() => {
196
+ execution.status = types_1.TxExecutionStatus.PROCESSING;
197
+ });
198
+ for (let i = executionStartIndex; i < execution.txs.length; i++) {
199
+ const currentTx = execution.txs[i];
200
+ const providedSignedTx = (options === null || options === void 0 ? void 0 : options.txIndex) != null && i === options.txIndex
201
+ ? options.signedTx
202
+ : undefined;
203
+ const result = yield this.executePendingTx(execution.vaultId, currentTx, execution.executableChainIds, (_b = execution.preventAutoSign) !== null && _b !== void 0 ? _b : false, providedSignedTx);
204
+ // update the tx status and related fields
205
+ (0, mobx_1.runInAction)(() => {
206
+ execution.txIndex = i;
207
+ currentTx.status = result.status;
208
+ if (result.txHash != null) {
209
+ currentTx.txHash = result.txHash;
210
+ }
211
+ if (result.error != null) {
212
+ currentTx.error = result.error;
213
+ }
214
+ currentTx.signedTx = undefined;
215
+ });
216
+ switch (result.status) {
217
+ case types_1.BackgroundTxStatus.CONFIRMED: {
218
+ if (providedSignedTx) {
219
+ // 외부에서 제공된 signed tx로 실행한 경우 (= multi tx 재개 케이스)
220
+ //
221
+ // 이번에 처리된 트랜잭션이 multi tx swap의 마지막 트랜잭션이라고 optimistically 가정하고,
222
+ // 추가적인 히스토리 데이터를 기록해야 한다.
223
+ //
224
+ // [배경]
225
+ // Skip에서 기본적으로 smart relay 기능을 활성화해 놓았으므로,
226
+ // multi tx swap이 필요한 경우는 다음과 같다:
227
+ // - A 체인에서 브릿지, 메시징 프로토콜, 또는 IBC Eureka를 통해 B 체인으로 자산을 전송
228
+ // - B 체인에서 사용자 주소로 릴리즈되는 자산이 wrapped asset이거나 IBC swap이 필요한 asset
229
+ //
230
+ // [마지막 트랜잭션의 유형]
231
+ // 따라서 multi tx swap의 마지막 트랜잭션은 아래 두 가지 중 하나라고 가정할 수 있다:
232
+ // 1. Wrapped asset → Native asset 변환 트랜잭션 (EVM)
233
+ // 2. IBC swap이 필요한 asset의 IBC swap 트랜잭션 (Cosmos)
234
+ //
235
+ // [트랜잭션 타입별 처리]
236
+ // 1. EVM: txHash를 additionalTrackingData에 저장 → debug_traceTransaction으로 추적
237
+ // 2. Cosmos: 외부에서 IBC swap data를 받아 additionalTrackingData에 저장 → IBC swap tracking
238
+ if (execution.type === types_1.TxExecutionType.SWAP_V2 &&
239
+ execution.historyId != null) {
240
+ const currentTx = execution.txs[i];
241
+ switch (currentTx.type) {
242
+ case types_1.BackgroundTxType.EVM: {
243
+ if (result.txHash != null) {
244
+ this.recentSendHistoryService.setSwapV2AdditionalTrackingData(execution.historyId, {
245
+ type: "evm",
246
+ chainId: currentTx.chainId,
247
+ txHash: result.txHash,
248
+ });
249
+ }
250
+ break;
251
+ }
252
+ case types_1.BackgroundTxType.COSMOS: {
253
+ const ibcSwapData = options === null || options === void 0 ? void 0 : options.ibcSwapData;
254
+ if (ibcSwapData != null && result.txHash != null) {
255
+ this.recentSendHistoryService.setSwapV2AdditionalTrackingData(execution.historyId, { type: "cosmos-ibc", ibcSwapData, txHash: result.txHash });
256
+ }
257
+ break;
258
+ }
259
+ default: {
260
+ // noop
261
+ break;
262
+ }
263
+ }
264
+ }
265
+ }
266
+ continue;
267
+ }
268
+ case types_1.BackgroundTxStatus.FAILED: {
269
+ this.recordSwapV2HistoryErrorIfNeeded(execution, (_c = result.error) !== null && _c !== void 0 ? _c : `${i + 1}th transaction failed`);
270
+ this.removeTxExecution(id);
271
+ return {
272
+ status: types_1.TxExecutionStatus.FAILED,
273
+ error: result.error,
274
+ };
275
+ }
276
+ case types_1.BackgroundTxStatus.BLOCKED: {
277
+ /**
278
+ * If the tx is BLOCKED, it means multiple transactions are required
279
+ * to be executed on different chains.
280
+ *
281
+ * - The execution should be stopped here,
282
+ * - Record the history if needed,
283
+ * - The execution should be resumed later when the condition is met.
284
+ */
285
+ (0, mobx_1.runInAction)(() => {
286
+ execution.status = types_1.TxExecutionStatus.BLOCKED;
287
+ this.recordHistoryIfNeeded(execution);
288
+ // no need to keep the history data anymore
289
+ delete execution.historyData;
290
+ });
291
+ return {
292
+ status: types_1.TxExecutionStatus.BLOCKED,
293
+ };
294
+ }
295
+ default: {
296
+ throw new router_1.KeplrError("direct-tx-executor", 131, "Unexpected tx status: " + result.status);
297
+ }
298
+ }
299
+ }
300
+ this.recordHistoryIfNeeded(execution);
301
+ this.clearSwapV2HistoryBackgroundExecutionIdIfNeeded(execution);
302
+ this.removeTxExecution(id);
303
+ return {
304
+ status: types_1.TxExecutionStatus.COMPLETED,
305
+ };
306
+ });
307
+ }
308
+ /**
309
+ * Execute a pending transaction without modifying observable state.
310
+ * Returns the result which should be applied by the caller using runInAction.
311
+ * This reduces autorun trigger count by batching state updates.
312
+ */
313
+ executePendingTx(vaultId, tx, executableChainIds, preventAutoSign, providedSignedTx) {
314
+ var _a, _b, _c, _d;
315
+ return __awaiter(this, void 0, void 0, function* () {
316
+ const status = tx.status;
317
+ let signedTx = (_a = tx.signedTx) !== null && _a !== void 0 ? _a : providedSignedTx;
318
+ let txHash = tx.txHash;
319
+ let error;
320
+ // Already in final state
321
+ if (status === types_1.BackgroundTxStatus.CONFIRMED ||
322
+ status === types_1.BackgroundTxStatus.FAILED) {
323
+ return { status, txHash, error };
324
+ }
325
+ // Check if blocked
326
+ const isBlocked = !executableChainIds.includes(tx.chainId);
327
+ if (isBlocked) {
328
+ return { status: types_1.BackgroundTxStatus.BLOCKED, txHash, error };
329
+ }
330
+ // If preventAutoSign and not signed, block
331
+ if (preventAutoSign && signedTx == null) {
332
+ return { status: types_1.BackgroundTxStatus.BLOCKED, txHash, error };
333
+ }
334
+ // if not signed, sign the tx
335
+ if (signedTx == null) {
336
+ try {
337
+ const signResult = yield this.signTx(vaultId, tx);
338
+ signedTx = signResult;
339
+ }
340
+ catch (e) {
341
+ console.error(`[TxExecutor] tx signing failed:`, e);
342
+ return {
343
+ status: types_1.BackgroundTxStatus.FAILED,
344
+ txHash,
345
+ error: (_b = e.message) !== null && _b !== void 0 ? _b : "Transaction signing failed",
346
+ };
347
+ }
348
+ }
349
+ // if tx hash is not set, broadcast the tx
350
+ if (txHash == null) {
351
+ try {
352
+ const txWithSignedTx = Object.assign(Object.assign({}, tx), { signedTx });
353
+ const broadcastResult = yield this.broadcastTx(txWithSignedTx);
354
+ txHash = broadcastResult;
355
+ }
356
+ catch (e) {
357
+ console.error(`[TxExecutor] tx broadcast failed:`, e);
358
+ return {
359
+ status: types_1.BackgroundTxStatus.FAILED,
360
+ txHash,
361
+ error: (_c = e.message) !== null && _c !== void 0 ? _c : "Transaction broadcasting failed",
362
+ };
363
+ }
364
+ }
365
+ // trace the tx
366
+ try {
367
+ const txWithHash = Object.assign(Object.assign({}, tx), { txHash });
368
+ const confirmed = yield this.traceTx(txWithHash);
369
+ if (confirmed) {
370
+ return { status: types_1.BackgroundTxStatus.CONFIRMED, txHash };
371
+ }
372
+ return {
373
+ status: types_1.BackgroundTxStatus.FAILED,
374
+ txHash,
375
+ error: "Transaction confirmation failed",
376
+ };
377
+ }
378
+ catch (e) {
379
+ console.error(`[TxExecutor] tx trace failed:`, e);
380
+ return {
381
+ status: types_1.BackgroundTxStatus.FAILED,
382
+ txHash,
383
+ error: (_d = e.message) !== null && _d !== void 0 ? _d : "Transaction confirmation failed",
384
+ };
385
+ }
386
+ });
387
+ }
388
+ signTx(vaultId, tx) {
389
+ return __awaiter(this, void 0, void 0, function* () {
390
+ switch (tx.type) {
391
+ case types_1.BackgroundTxType.EVM: {
392
+ return this.signEvmTx(vaultId, tx);
393
+ }
394
+ case types_1.BackgroundTxType.COSMOS: {
395
+ return this.signCosmosTx(vaultId, tx);
396
+ }
397
+ default: {
398
+ throw new router_1.KeplrError("direct-tx-executor", 143, "Unknown tx type");
399
+ }
400
+ }
401
+ });
402
+ }
403
+ signEvmTx(vaultId, tx) {
404
+ var _a;
405
+ return __awaiter(this, void 0, void 0, function* () {
406
+ const keyInfo = yield this.keyRingCosmosService.getKey(vaultId, tx.chainId);
407
+ const isHardware = keyInfo.isNanoLedger || keyInfo.isKeystone;
408
+ const signer = keyInfo.ethereumHexAddress;
409
+ // For hardware wallets, the signedTx must be provided externally when calling resumeTx or recordAndExecuteTxs.
410
+ if (isHardware) {
411
+ throw new router_1.KeplrError("direct-tx-executor", 140, "Hardware wallet signing should be triggered from user interaction");
412
+ }
413
+ const origin = typeof browser !== "undefined"
414
+ ? new URL(browser.runtime.getURL("/")).origin
415
+ : "extension";
416
+ const chainInfo = this.chainsService.getChainInfoOrThrow(tx.chainId);
417
+ const evmInfo = chains_1.ChainsService.getEVMInfo(chainInfo);
418
+ if (!evmInfo) {
419
+ throw new router_1.KeplrError("direct-tx-executor", 142, "Not EVM chain");
420
+ }
421
+ const unsignedTx = yield (0, evm_1.fillUnsignedEVMTx)(origin, evmInfo, signer, tx.txData, (_a = tx.feeType) !== null && _a !== void 0 ? _a : "average");
422
+ const result = yield this.keyRingEthereumService.signEthereumPreAuthorized(vaultId, tx.chainId, signer, Buffer.from(JSON.stringify(unsignedTx)), types_2.EthSignType.TRANSACTION);
423
+ const signedTxData = JSON.parse(Buffer.from(result.signingData).toString());
424
+ const isEIP1559 = !!signedTxData.maxFeePerGas || !!signedTxData.maxPriorityFeePerGas;
425
+ if (isEIP1559) {
426
+ signedTxData.type = transactions_1.TransactionTypes.eip1559;
427
+ }
428
+ delete signedTxData.from;
429
+ return (0, transactions_1.serialize)(signedTxData, result.signature);
430
+ });
431
+ }
432
+ signCosmosTx(vaultId, tx) {
433
+ var _a, _b, _c, _d, _e;
434
+ return __awaiter(this, void 0, void 0, function* () {
435
+ const keyInfo = yield this.keyRingCosmosService.getKey(vaultId, tx.chainId);
436
+ const isHardware = keyInfo.isNanoLedger || keyInfo.isKeystone;
437
+ const signer = keyInfo.bech32Address;
438
+ // For hardware wallets, the signedTx must be provided externally when calling resumeTx or recordAndExecuteTxs.
439
+ if (isHardware) {
440
+ throw new router_1.KeplrError("direct-tx-executor", 140, "Hardware wallet signing should be triggered from user interaction");
441
+ }
442
+ const origin = typeof browser !== "undefined"
443
+ ? new URL(browser.runtime.getURL("/")).origin
444
+ : "extension";
445
+ const chainInfo = this.chainsService.getChainInfoOrThrow(tx.chainId);
446
+ const aminoMsgs = (_a = tx.txData.aminoMsgs) !== null && _a !== void 0 ? _a : [];
447
+ const protoMsgs = tx.txData.protoMsgs;
448
+ const pseudoFee = {
449
+ amount: [
450
+ {
451
+ denom: chainInfo.currencies[0].coinMinimalDenom,
452
+ amount: "1",
453
+ },
454
+ ],
455
+ gas: "100000",
456
+ };
457
+ const memo = (_b = tx.txData.memo) !== null && _b !== void 0 ? _b : "";
458
+ const isDirectSign = aminoMsgs.length === 0;
459
+ if (protoMsgs.length === 0) {
460
+ throw new Error("There is no msg to send");
461
+ }
462
+ if (!isDirectSign && aminoMsgs.length !== protoMsgs.length) {
463
+ throw new Error("The length of aminoMsgs and protoMsgs are different");
464
+ }
465
+ const account = yield cosmos_1.BaseAccount.fetchFromRest(chainInfo.rest, signer, true);
466
+ let fee = tx.txData.fee; // use provided fee if exists
467
+ if (fee == null) {
468
+ const { gasUsed } = yield (0, cosmos_2.simulateCosmosTx)(signer, chainInfo, protoMsgs, pseudoFee, memo);
469
+ const feeCurrency = (_c = chainInfo.feeCurrencies.find((currency) => currency.coinMinimalDenom === tx.feeCurrencyDenom)) !== null && _c !== void 0 ? _c : chainInfo.currencies[0];
470
+ const { gasPrice } = yield (0, cosmos_2.getCosmosGasPrice)(chainInfo, (_d = tx.feeType) !== null && _d !== void 0 ? _d : "average", feeCurrency);
471
+ fee = (0, cosmos_2.calculateCosmosStdFee)(feeCurrency, gasUsed, gasPrice, chainInfo.features);
472
+ }
473
+ if (isDirectSign) {
474
+ const { signDoc, bodyBytes, authInfoBytes } = (0, cosmos_2.prepareSignDocForDirectSigning)({
475
+ chainInfo,
476
+ accountNumber: account.getAccountNumber().toString(),
477
+ sequence: account.getSequence().toString(),
478
+ protoMsgs,
479
+ fee,
480
+ memo,
481
+ pubKey: keyInfo.pubKey,
482
+ });
483
+ const { signature } = yield this.keyRingCosmosService.signDirectPreAuthorized(origin, vaultId, tx.chainId, signer, signDoc);
484
+ const signedTx = tx_1.TxRaw.encode({
485
+ bodyBytes,
486
+ authInfoBytes,
487
+ signatures: [Buffer.from(signature.signature, "base64")],
488
+ }).finish();
489
+ return Buffer.from(signedTx).toString("base64");
490
+ }
491
+ else {
492
+ const signDoc = (0, cosmos_2.prepareSignDocForAminoSigning)({
493
+ chainInfo,
494
+ accountNumber: account.getAccountNumber().toString(),
495
+ sequence: account.getSequence().toString(),
496
+ aminoMsgs: (_e = tx.txData.aminoMsgs) !== null && _e !== void 0 ? _e : [],
497
+ fee,
498
+ memo,
499
+ eip712Signing: false,
500
+ signer,
501
+ });
502
+ const signResponse = yield this.keyRingCosmosService.signAminoPreAuthorized(origin, vaultId, tx.chainId, signer, signDoc);
503
+ const signedTx = (0, cosmos_2.buildSignedTxFromAminoSignResponse)({
504
+ protoMsgs,
505
+ signResponse,
506
+ chainInfo,
507
+ eip712Signing: false,
508
+ });
509
+ return Buffer.from(signedTx.tx).toString("base64");
510
+ }
511
+ });
512
+ }
513
+ broadcastTx(tx) {
514
+ return __awaiter(this, void 0, void 0, function* () {
515
+ switch (tx.type) {
516
+ case types_1.BackgroundTxType.EVM: {
517
+ return this.broadcastEvmTx(tx);
518
+ }
519
+ case types_1.BackgroundTxType.COSMOS: {
520
+ return this.broadcastCosmosTx(tx);
521
+ }
522
+ default: {
523
+ throw new router_1.KeplrError("direct-tx-executor", 143, "Unknown tx type");
524
+ }
525
+ }
526
+ });
527
+ }
528
+ broadcastEvmTx(tx) {
529
+ return __awaiter(this, void 0, void 0, function* () {
530
+ // assume the signed tx is valid if exists
531
+ if (!tx.signedTx) {
532
+ throw new router_1.KeplrError("direct-tx-executor", 132, "Signed tx not found");
533
+ }
534
+ const origin = typeof browser !== "undefined"
535
+ ? new URL(browser.runtime.getURL("/")).origin
536
+ : "extension";
537
+ const signedTxBytes = Buffer.from(tx.signedTx.replace("0x", ""), "hex");
538
+ const txHash = yield this.backgroundTxEthereumService.sendEthereumTx(origin, tx.chainId, signedTxBytes, {
539
+ silent: true,
540
+ skipTracingTxResult: true,
541
+ });
542
+ return txHash;
543
+ });
544
+ }
545
+ broadcastCosmosTx(tx) {
546
+ return __awaiter(this, void 0, void 0, function* () {
547
+ if (!tx.signedTx) {
548
+ throw new router_1.KeplrError("direct-tx-executor", 132, "Signed tx not found");
549
+ }
550
+ const signedTxBytes = Buffer.from(tx.signedTx, "base64");
551
+ // broadcast the tx
552
+ const txHash = yield this.backgroundTxService.sendTx(tx.chainId, signedTxBytes, "sync", {
553
+ silent: true,
554
+ skipTracingTxResult: true,
555
+ });
556
+ return Buffer.from(txHash).toString("hex");
557
+ });
558
+ }
559
+ traceTx(tx) {
560
+ return __awaiter(this, void 0, void 0, function* () {
561
+ switch (tx.type) {
562
+ case types_1.BackgroundTxType.EVM: {
563
+ return this.traceEvmTx(tx);
564
+ }
565
+ case types_1.BackgroundTxType.COSMOS: {
566
+ return this.traceCosmosTx(tx);
567
+ }
568
+ default: {
569
+ throw new router_1.KeplrError("direct-tx-executor", 143, "Unknown tx type");
570
+ }
571
+ }
572
+ });
573
+ }
574
+ traceEvmTx(tx) {
575
+ return __awaiter(this, void 0, void 0, function* () {
576
+ if (!tx.txHash) {
577
+ throw new router_1.KeplrError("direct-tx-executor", 133, "Tx hash not found");
578
+ }
579
+ const origin = typeof browser !== "undefined"
580
+ ? new URL(browser.runtime.getURL("/")).origin
581
+ : "extension";
582
+ const txReceipt = yield this.backgroundTxEthereumService.getEthereumTxReceipt(origin, tx.chainId, tx.txHash);
583
+ if (!txReceipt) {
584
+ return false;
585
+ }
586
+ return txReceipt.status === types_2.EthTxStatus.Success;
587
+ });
588
+ }
589
+ traceCosmosTx(tx) {
590
+ return __awaiter(this, void 0, void 0, function* () {
591
+ if (!tx.txHash) {
592
+ throw new router_1.KeplrError("direct-tx-executor", 133, "Tx hash not found");
593
+ }
594
+ const txResult = yield this.backgroundTxService.traceTx(tx.chainId, tx.txHash);
595
+ if (!txResult) {
596
+ return false;
597
+ }
598
+ // Tendermint/CometBFT omits the code field when tx is successful (code=0)
599
+ // If code is present and non-zero, it's a failure
600
+ if (txResult.code != null && txResult.code !== 0) {
601
+ return false;
602
+ }
603
+ return true;
604
+ });
605
+ }
606
+ /**
607
+ * Find the index of the most recent confirmed transaction with executable chain ids.
608
+ * Returns -1 if not found.
609
+ */
610
+ findHistoryTxIndex(execution) {
611
+ if (execution.historyTxIndex != null) {
612
+ return execution.historyTxIndex;
613
+ }
614
+ for (let i = execution.txs.length - 1; i >= 0; i--) {
615
+ const tx = execution.txs[i];
616
+ if (execution.executableChainIds.includes(tx.chainId) &&
617
+ tx.status === types_1.BackgroundTxStatus.CONFIRMED) {
618
+ return i;
619
+ }
620
+ }
621
+ return -1;
622
+ }
623
+ recordHistoryIfNeeded(execution) {
624
+ switch (execution.type) {
625
+ case types_1.TxExecutionType.SWAP_V2: {
626
+ if (execution.historyId != null || execution.historyData == null) {
627
+ return;
628
+ }
629
+ const historyTxIndex = this.findHistoryTxIndex(execution);
630
+ if (historyTxIndex < 0) {
631
+ return;
632
+ }
633
+ const tx = execution.txs[historyTxIndex];
634
+ if (!tx || tx.txHash == null) {
635
+ return;
636
+ }
637
+ const historyData = execution.historyData;
638
+ const backgroundExecutionId = execution.txs.some((tx) => tx.status === types_1.BackgroundTxStatus.BLOCKED)
639
+ ? execution.id
640
+ : undefined;
641
+ const id = this.recentSendHistoryService.recordTxWithSwapV2(historyData.fromChainId, historyData.toChainId, historyData.provider, historyData.destinationAsset, historyData.simpleRoute, historyData.sender, historyData.recipient, historyData.amount, historyData.notificationInfo, historyData.routeDurationSeconds, tx.txHash, historyData.isOnlyUseBridge, backgroundExecutionId);
642
+ execution.historyId = id;
643
+ break;
644
+ }
645
+ default: {
646
+ return;
647
+ }
648
+ }
649
+ }
650
+ getRecentTxExecutions() {
651
+ return Array.from(this.recentTxExecutionMap.values());
652
+ }
653
+ getTxExecution(id) {
654
+ const execution = this.recentTxExecutionMap.get(id);
655
+ if (!execution) {
656
+ return undefined;
657
+ }
658
+ return execution;
659
+ }
660
+ removeTxExecution(id) {
661
+ this.recentTxExecutionMap.delete(id);
662
+ }
663
+ cleanupOldExecutions() {
664
+ const completedStatuses = [
665
+ types_1.TxExecutionStatus.COMPLETED,
666
+ types_1.TxExecutionStatus.FAILED,
667
+ ];
668
+ const idsToDelete = [];
669
+ for (const [id, execution] of this.recentTxExecutionMap) {
670
+ // 비정상 종료된 PROCESSING 상태 → FAILED 처리
671
+ // (브라우저 종료, 시스템 재부팅, 익스텐션 업데이트 등)
672
+ if (execution.status === types_1.TxExecutionStatus.PROCESSING) {
673
+ execution.status = types_1.TxExecutionStatus.FAILED;
674
+ }
675
+ if (completedStatuses.includes(execution.status)) {
676
+ idsToDelete.push(id);
677
+ }
678
+ }
679
+ for (const id of idsToDelete) {
680
+ this.recentTxExecutionMap.delete(id);
681
+ }
682
+ }
683
+ recordSwapV2HistoryErrorIfNeeded(execution, error) {
684
+ if (execution.type === types_1.TxExecutionType.SWAP_V2 &&
685
+ execution.historyId != null) {
686
+ this.recentSendHistoryService.setSwapV2HistoryError(execution.historyId, error);
687
+ }
688
+ }
689
+ clearSwapV2HistoryBackgroundExecutionIdIfNeeded(execution) {
690
+ if (execution.type === types_1.TxExecutionType.SWAP_V2 &&
691
+ execution.historyId != null) {
692
+ this.recentSendHistoryService.clearSwapV2HistoryBackgroundExecutionId(execution.historyId);
693
+ }
694
+ }
695
+ }
696
+ __decorate([
697
+ mobx_1.observable
698
+ ], BackgroundTxExecutorService.prototype, "recentTxExecutionSeq", void 0);
699
+ __decorate([
700
+ mobx_1.observable
701
+ ], BackgroundTxExecutorService.prototype, "recentTxExecutionMap", void 0);
702
+ __decorate([
703
+ mobx_1.action
704
+ ], BackgroundTxExecutorService.prototype, "handleTxExecutionEvent", null);
705
+ __decorate([
706
+ mobx_1.action
707
+ ], BackgroundTxExecutorService.prototype, "recordHistoryIfNeeded", null);
708
+ __decorate([
709
+ mobx_1.action
710
+ ], BackgroundTxExecutorService.prototype, "removeTxExecution", null);
711
+ __decorate([
712
+ mobx_1.action
713
+ ], BackgroundTxExecutorService.prototype, "cleanupOldExecutions", null);
714
+ exports.BackgroundTxExecutorService = BackgroundTxExecutorService;
715
+ //# sourceMappingURL=service.js.map