@rango-dev/queue-manager-rango-preset 0.0.0-experimental-936229e8-20251208
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/CHANGELOG.md +353 -0
- package/dist/actions/checkStatus.d.ts +13 -0
- package/dist/actions/checkStatus.d.ts.map +1 -0
- package/dist/actions/common/checkEnvironmentBeforeExecuteTransaction.d.ts +8 -0
- package/dist/actions/common/checkEnvironmentBeforeExecuteTransaction.d.ts.map +1 -0
- package/dist/actions/common/produceNextStateForTransaction.d.ts +17 -0
- package/dist/actions/common/produceNextStateForTransaction.d.ts.map +1 -0
- package/dist/actions/common/utils.d.ts +5 -0
- package/dist/actions/common/utils.d.ts.map +1 -0
- package/dist/actions/createTransaction.d.ts +12 -0
- package/dist/actions/createTransaction.d.ts.map +1 -0
- package/dist/actions/executeTransaction/executeTransaction.d.ts +14 -0
- package/dist/actions/executeTransaction/executeTransaction.d.ts.map +1 -0
- package/dist/actions/executeTransaction/index.d.ts +2 -0
- package/dist/actions/executeTransaction/index.d.ts.map +1 -0
- package/dist/actions/scheduleNextStep.d.ts +14 -0
- package/dist/actions/scheduleNextStep.d.ts.map +1 -0
- package/dist/actions/start.d.ts +4 -0
- package/dist/actions/start.d.ts.map +1 -0
- package/dist/configs.d.ts +14 -0
- package/dist/configs.d.ts.map +1 -0
- package/dist/constants.d.ts +7 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/helpers.d.ts +255 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/hooks.d.ts +19 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +7 -0
- package/dist/migration.d.ts +15 -0
- package/dist/migration.d.ts.map +1 -0
- package/dist/numbers.d.ts +3 -0
- package/dist/numbers.d.ts.map +1 -0
- package/dist/queue-manager-rango-preset.build.json +1 -0
- package/dist/queueDef.d.ts +10 -0
- package/dist/queueDef.d.ts.map +1 -0
- package/dist/services/eventEmitter.d.ts +10 -0
- package/dist/services/eventEmitter.d.ts.map +1 -0
- package/dist/services/httpService.d.ts +3 -0
- package/dist/services/httpService.d.ts.map +1 -0
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/shared-errors.d.ts +25 -0
- package/dist/shared-errors.d.ts.map +1 -0
- package/dist/shared.d.ts +81 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/types.d.ts +172 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +45 -0
- package/readme.md +2 -0
- package/src/actions/checkStatus.ts +564 -0
- package/src/actions/common/checkEnvironmentBeforeExecuteTransaction.ts +157 -0
- package/src/actions/common/produceNextStateForTransaction.ts +167 -0
- package/src/actions/common/utils.ts +25 -0
- package/src/actions/createTransaction.ts +117 -0
- package/src/actions/executeTransaction/executeTransaction.ts +107 -0
- package/src/actions/executeTransaction/index.ts +1 -0
- package/src/actions/scheduleNextStep.ts +104 -0
- package/src/actions/start.ts +16 -0
- package/src/configs.ts +38 -0
- package/src/constants.ts +18 -0
- package/src/helpers.ts +1598 -0
- package/src/hooks.ts +94 -0
- package/src/index.ts +68 -0
- package/src/migration.ts +124 -0
- package/src/numbers.ts +68 -0
- package/src/queueDef.ts +39 -0
- package/src/services/eventEmitter.ts +290 -0
- package/src/services/httpService.ts +10 -0
- package/src/services/index.ts +1 -0
- package/src/shared-errors.ts +165 -0
- package/src/shared.ts +473 -0
- package/src/types.ts +305 -0
package/src/helpers.ts
ADDED
|
@@ -0,0 +1,1598 @@
|
|
|
1
|
+
/* eslint-disable destructuring/in-params */
|
|
2
|
+
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-floating-promises */
|
|
4
|
+
import type { NotifierParams } from './services/eventEmitter';
|
|
5
|
+
import type { SwapStatus, TargetNamespace, Wallet } from './shared';
|
|
6
|
+
import type {
|
|
7
|
+
ArrayElement,
|
|
8
|
+
LastConnectedWallet,
|
|
9
|
+
Step,
|
|
10
|
+
SwapQueueContext,
|
|
11
|
+
SwapQueueDef,
|
|
12
|
+
SwapStorage,
|
|
13
|
+
UseQueueManagerParams,
|
|
14
|
+
} from './types';
|
|
15
|
+
import type {
|
|
16
|
+
ExecuterActions,
|
|
17
|
+
Manager,
|
|
18
|
+
QueueInfo,
|
|
19
|
+
QueueName,
|
|
20
|
+
QueueType,
|
|
21
|
+
SetStorage,
|
|
22
|
+
} from '@rango-dev/queue-manager-core';
|
|
23
|
+
import type { Provider } from '@rango-dev/wallets-core';
|
|
24
|
+
import type {
|
|
25
|
+
Meta,
|
|
26
|
+
Network,
|
|
27
|
+
Providers,
|
|
28
|
+
WalletState,
|
|
29
|
+
WalletType,
|
|
30
|
+
} from '@rango-dev/wallets-shared';
|
|
31
|
+
import type {
|
|
32
|
+
CreateTransactionResponse,
|
|
33
|
+
EvmBlockchainMeta,
|
|
34
|
+
Transaction,
|
|
35
|
+
} from 'rango-sdk';
|
|
36
|
+
import type {
|
|
37
|
+
APIErrorCode,
|
|
38
|
+
PendingSwap,
|
|
39
|
+
PendingSwapStep,
|
|
40
|
+
SignerErrorCode,
|
|
41
|
+
StepStatus,
|
|
42
|
+
} from 'rango-types';
|
|
43
|
+
|
|
44
|
+
import { warn } from '@rango-dev/logging-core';
|
|
45
|
+
import { Status } from '@rango-dev/queue-manager-core';
|
|
46
|
+
import { legacyReadAccountAddress as readAccountAddress } from '@rango-dev/wallets-core/legacy';
|
|
47
|
+
import {
|
|
48
|
+
getBlockChainNameFromId,
|
|
49
|
+
getEvmProvider,
|
|
50
|
+
} from '@rango-dev/wallets-shared';
|
|
51
|
+
import BigNumber from 'bignumber.js';
|
|
52
|
+
import {
|
|
53
|
+
PendingSwapNetworkStatus,
|
|
54
|
+
SignerError,
|
|
55
|
+
TransactionType,
|
|
56
|
+
} from 'rango-types';
|
|
57
|
+
|
|
58
|
+
import {
|
|
59
|
+
DEFAULT_ERROR_CODE,
|
|
60
|
+
ERROR_MESSAGE_WAIT_FOR_CHANGE_NETWORK,
|
|
61
|
+
ERROR_MESSAGE_WAIT_FOR_WALLET,
|
|
62
|
+
ERROR_MESSAGE_WAIT_FOR_WALLET_DESCRIPTION,
|
|
63
|
+
} from './constants';
|
|
64
|
+
import { httpService } from './services';
|
|
65
|
+
import { notifier } from './services/eventEmitter';
|
|
66
|
+
import {
|
|
67
|
+
getCurrentNamespaceOf,
|
|
68
|
+
getCurrentNamespaceOfOrNull,
|
|
69
|
+
getRelatedWallet,
|
|
70
|
+
getRelatedWalletOrNull,
|
|
71
|
+
getScannerUrl,
|
|
72
|
+
MessageSeverity,
|
|
73
|
+
} from './shared';
|
|
74
|
+
import {
|
|
75
|
+
mapAppErrorCodesToAPIErrorCode,
|
|
76
|
+
prettifyErrorMessage,
|
|
77
|
+
PrettyError,
|
|
78
|
+
} from './shared-errors';
|
|
79
|
+
import {
|
|
80
|
+
BlockReason,
|
|
81
|
+
StepEventType,
|
|
82
|
+
StepExecutionBlockedEventStatus,
|
|
83
|
+
StepExecutionEventStatus,
|
|
84
|
+
SwapActionTypes,
|
|
85
|
+
} from './types';
|
|
86
|
+
|
|
87
|
+
type WhenTaskBlocked = Parameters<NonNullable<SwapQueueDef['whenTaskBlocked']>>;
|
|
88
|
+
type WhenTaskBlockedEvent = WhenTaskBlocked[0];
|
|
89
|
+
type WhenTaskBlockedMeta = WhenTaskBlocked[1];
|
|
90
|
+
|
|
91
|
+
let swapClaimedBy: { id: string } | null = null;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
*
|
|
95
|
+
* We simply use module-level variable to keep track of which queue has claimed the execution of parallel runnings.
|
|
96
|
+
*
|
|
97
|
+
*/
|
|
98
|
+
export function claimQueue() {
|
|
99
|
+
return {
|
|
100
|
+
claimedBy: () => swapClaimedBy?.id,
|
|
101
|
+
setClaimer: (queue_id: string) => {
|
|
102
|
+
swapClaimedBy = {
|
|
103
|
+
id: queue_id,
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
reset: () => {
|
|
107
|
+
swapClaimedBy = null;
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
*
|
|
114
|
+
* We use module-level variable to keep track of
|
|
115
|
+
* map of transactions hash to the TransactionResponse and ...
|
|
116
|
+
*
|
|
117
|
+
*/
|
|
118
|
+
type TransactionData = {
|
|
119
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
120
|
+
response?: any; // e.g. TransactionResponse in case of EVM transactions
|
|
121
|
+
receiptReceived?: boolean; // e.g. is TransactionReceipt ready in case of EVM transactions
|
|
122
|
+
};
|
|
123
|
+
const swapTransactionToDataMap: { [id: string]: TransactionData } = {};
|
|
124
|
+
export function inMemoryTransactionsData() {
|
|
125
|
+
return {
|
|
126
|
+
getTransactionDataByHash: (hash: string) =>
|
|
127
|
+
swapTransactionToDataMap[hash] || {},
|
|
128
|
+
setTransactionDataByHash: (hash: string, data: TransactionData) => {
|
|
129
|
+
const r = swapTransactionToDataMap[hash];
|
|
130
|
+
if (!r) {
|
|
131
|
+
swapTransactionToDataMap[hash] = {};
|
|
132
|
+
}
|
|
133
|
+
swapTransactionToDataMap[hash].response =
|
|
134
|
+
data.response || swapTransactionToDataMap[hash].response;
|
|
135
|
+
swapTransactionToDataMap[hash].receiptReceived =
|
|
136
|
+
data.receiptReceived ||
|
|
137
|
+
swapTransactionToDataMap[hash].receiptReceived ||
|
|
138
|
+
false;
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
*
|
|
145
|
+
* Returns `steps`, if it's a `running` swap.
|
|
146
|
+
* Each `PendingSwap` has a `steps` inside it, `steps` shows how many tasks should be created and run to finish the swap.
|
|
147
|
+
*
|
|
148
|
+
*/
|
|
149
|
+
export const getCurrentStep = (swap: PendingSwap): PendingSwapStep | null => {
|
|
150
|
+
return (
|
|
151
|
+
swap.steps.find(
|
|
152
|
+
(step) => step.status !== 'failed' && step.status !== 'success'
|
|
153
|
+
) || null
|
|
154
|
+
);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
*
|
|
159
|
+
* Returns current step transaction
|
|
160
|
+
*
|
|
161
|
+
*/
|
|
162
|
+
export const getCurrentStepTx = (
|
|
163
|
+
currentStep: PendingSwapStep
|
|
164
|
+
): Transaction | null => {
|
|
165
|
+
const {
|
|
166
|
+
evmTransaction,
|
|
167
|
+
evmApprovalTransaction,
|
|
168
|
+
cosmosTransaction,
|
|
169
|
+
solanaTransaction,
|
|
170
|
+
transferTransaction,
|
|
171
|
+
starknetApprovalTransaction,
|
|
172
|
+
starknetTransaction,
|
|
173
|
+
tronApprovalTransaction,
|
|
174
|
+
tronTransaction,
|
|
175
|
+
tonTransaction,
|
|
176
|
+
suiTransaction,
|
|
177
|
+
} = currentStep;
|
|
178
|
+
return (
|
|
179
|
+
evmTransaction ||
|
|
180
|
+
evmApprovalTransaction ||
|
|
181
|
+
cosmosTransaction ||
|
|
182
|
+
solanaTransaction ||
|
|
183
|
+
transferTransaction ||
|
|
184
|
+
starknetApprovalTransaction ||
|
|
185
|
+
starknetTransaction ||
|
|
186
|
+
tronApprovalTransaction ||
|
|
187
|
+
tronTransaction ||
|
|
188
|
+
tonTransaction ||
|
|
189
|
+
suiTransaction
|
|
190
|
+
);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
*
|
|
195
|
+
* Set current step transaction
|
|
196
|
+
*
|
|
197
|
+
*/
|
|
198
|
+
export const setCurrentStepTx = (
|
|
199
|
+
currentStep: PendingSwapStep,
|
|
200
|
+
transaction: Transaction
|
|
201
|
+
): PendingSwapStep => {
|
|
202
|
+
currentStep.transferTransaction = null;
|
|
203
|
+
currentStep.cosmosTransaction = null;
|
|
204
|
+
currentStep.evmTransaction = null;
|
|
205
|
+
currentStep.solanaTransaction = null;
|
|
206
|
+
currentStep.evmApprovalTransaction = null;
|
|
207
|
+
currentStep.starknetApprovalTransaction = null;
|
|
208
|
+
currentStep.starknetTransaction = null;
|
|
209
|
+
currentStep.tronApprovalTransaction = null;
|
|
210
|
+
currentStep.tronTransaction = null;
|
|
211
|
+
currentStep.tonTransaction = null;
|
|
212
|
+
currentStep.suiTransaction = null;
|
|
213
|
+
|
|
214
|
+
const txType = transaction.type;
|
|
215
|
+
switch (txType) {
|
|
216
|
+
case TransactionType.EVM:
|
|
217
|
+
if (transaction.isApprovalTx) {
|
|
218
|
+
currentStep.evmApprovalTransaction = transaction;
|
|
219
|
+
} else {
|
|
220
|
+
currentStep.evmTransaction = transaction;
|
|
221
|
+
}
|
|
222
|
+
break;
|
|
223
|
+
case TransactionType.TRON:
|
|
224
|
+
if (transaction.isApprovalTx) {
|
|
225
|
+
currentStep.tronApprovalTransaction = transaction;
|
|
226
|
+
} else {
|
|
227
|
+
currentStep.tronTransaction = transaction;
|
|
228
|
+
}
|
|
229
|
+
break;
|
|
230
|
+
case TransactionType.STARKNET:
|
|
231
|
+
if (transaction.isApprovalTx) {
|
|
232
|
+
currentStep.starknetApprovalTransaction = transaction;
|
|
233
|
+
} else {
|
|
234
|
+
currentStep.starknetTransaction = transaction;
|
|
235
|
+
}
|
|
236
|
+
break;
|
|
237
|
+
case TransactionType.COSMOS:
|
|
238
|
+
currentStep.cosmosTransaction = transaction;
|
|
239
|
+
break;
|
|
240
|
+
case TransactionType.SOLANA:
|
|
241
|
+
currentStep.solanaTransaction = transaction;
|
|
242
|
+
break;
|
|
243
|
+
case TransactionType.TRANSFER:
|
|
244
|
+
currentStep.transferTransaction = transaction;
|
|
245
|
+
break;
|
|
246
|
+
case TransactionType.TON:
|
|
247
|
+
currentStep.tonTransaction = transaction;
|
|
248
|
+
break;
|
|
249
|
+
case TransactionType.SUI:
|
|
250
|
+
currentStep.suiTransaction = transaction;
|
|
251
|
+
break;
|
|
252
|
+
case TransactionType.XRPL:
|
|
253
|
+
currentStep.xrplTransaction = transaction;
|
|
254
|
+
break;
|
|
255
|
+
default:
|
|
256
|
+
((x: never) => {
|
|
257
|
+
throw new Error(`${x} was unhandled!`);
|
|
258
|
+
})(txType);
|
|
259
|
+
}
|
|
260
|
+
return currentStep;
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
*
|
|
265
|
+
* Returns current step transaction type
|
|
266
|
+
*
|
|
267
|
+
*/
|
|
268
|
+
export const getCurrentStepTxType = (
|
|
269
|
+
currentStep: PendingSwapStep
|
|
270
|
+
): TransactionType | undefined => {
|
|
271
|
+
return getCurrentStepTx(currentStep)?.type;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
*
|
|
276
|
+
* Returns a boolean indicating that current step is an approval tx or not.
|
|
277
|
+
*
|
|
278
|
+
*/
|
|
279
|
+
export const isApprovalCurrentStepTx = (
|
|
280
|
+
currentStep: PendingSwapStep
|
|
281
|
+
): boolean => {
|
|
282
|
+
const {
|
|
283
|
+
evmApprovalTransaction,
|
|
284
|
+
starknetApprovalTransaction,
|
|
285
|
+
tronApprovalTransaction,
|
|
286
|
+
} = currentStep;
|
|
287
|
+
return !!(
|
|
288
|
+
evmApprovalTransaction ||
|
|
289
|
+
starknetApprovalTransaction ||
|
|
290
|
+
tronApprovalTransaction
|
|
291
|
+
);
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* When we are doing a swap, there are some common fields that will be updated together.
|
|
296
|
+
* This function helps us to update a swap status and also it will update some more fields like `extraMessageSeverity` based on the input.
|
|
297
|
+
*/
|
|
298
|
+
export function updateSwapStatus({
|
|
299
|
+
getStorage,
|
|
300
|
+
setStorage,
|
|
301
|
+
nextStatus,
|
|
302
|
+
nextStepStatus,
|
|
303
|
+
message,
|
|
304
|
+
details,
|
|
305
|
+
errorCode = null,
|
|
306
|
+
hasAlreadyProceededToSign,
|
|
307
|
+
trace = null,
|
|
308
|
+
}: {
|
|
309
|
+
getStorage: ExecuterActions<
|
|
310
|
+
SwapStorage,
|
|
311
|
+
SwapActionTypes,
|
|
312
|
+
SwapQueueContext
|
|
313
|
+
>['getStorage'];
|
|
314
|
+
setStorage: ExecuterActions<
|
|
315
|
+
SwapStorage,
|
|
316
|
+
SwapActionTypes,
|
|
317
|
+
SwapQueueContext
|
|
318
|
+
>['setStorage'];
|
|
319
|
+
nextStatus?: SwapStatus;
|
|
320
|
+
nextStepStatus?: StepStatus;
|
|
321
|
+
message?: string;
|
|
322
|
+
details?: string | null | undefined;
|
|
323
|
+
errorCode?: APIErrorCode | SignerErrorCode | null;
|
|
324
|
+
hasAlreadyProceededToSign?: boolean;
|
|
325
|
+
trace?: Error | null | undefined;
|
|
326
|
+
}): {
|
|
327
|
+
swap: PendingSwap;
|
|
328
|
+
step: PendingSwapStep | null;
|
|
329
|
+
failureType?: APIErrorCode;
|
|
330
|
+
} {
|
|
331
|
+
const swap = getStorage().swapDetails;
|
|
332
|
+
const currentStep = getCurrentStep(swap);
|
|
333
|
+
const updatedResult: {
|
|
334
|
+
swap: PendingSwap;
|
|
335
|
+
step: PendingSwapStep | null;
|
|
336
|
+
failureType?: APIErrorCode;
|
|
337
|
+
} = {
|
|
338
|
+
swap,
|
|
339
|
+
step: currentStep,
|
|
340
|
+
};
|
|
341
|
+
if (!!nextStepStatus && !!currentStep) {
|
|
342
|
+
currentStep.status = nextStepStatus;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (nextStatus) {
|
|
346
|
+
swap.status = nextStatus;
|
|
347
|
+
}
|
|
348
|
+
swap.hasAlreadyProceededToSign = hasAlreadyProceededToSign;
|
|
349
|
+
if (!!nextStatus && ['failed', 'success'].includes(nextStatus)) {
|
|
350
|
+
swap.finishTime = new Date().getTime().toString();
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (!!message || !!details) {
|
|
354
|
+
swap.extraMessage = message || '';
|
|
355
|
+
swap.extraMessageDetail = details || '';
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (!!nextStepStatus && ['failed'].includes(nextStepStatus)) {
|
|
359
|
+
//if user cancel the swap, we should pass relevant reason to the server.
|
|
360
|
+
const errorReason =
|
|
361
|
+
details && details.includes('Warning')
|
|
362
|
+
? 'Swap canceled by user.'
|
|
363
|
+
: details;
|
|
364
|
+
const walletType = getRelatedWalletOrNull(swap, currentStep)?.walletType;
|
|
365
|
+
swap.extraMessageSeverity = MessageSeverity.error;
|
|
366
|
+
|
|
367
|
+
const failureType = mapAppErrorCodesToAPIErrorCode(errorCode);
|
|
368
|
+
updatedResult.failureType = failureType;
|
|
369
|
+
|
|
370
|
+
// If trace of error was available, we will send it to the api (except user rejection)
|
|
371
|
+
const errorReasonForAPI =
|
|
372
|
+
errorCode !== 'REJECTED_BY_USER' &&
|
|
373
|
+
trace?.message &&
|
|
374
|
+
typeof trace.message === 'string'
|
|
375
|
+
? trace.message
|
|
376
|
+
: errorReason || '';
|
|
377
|
+
|
|
378
|
+
httpService()
|
|
379
|
+
.reportFailure({
|
|
380
|
+
requestId: swap.requestId,
|
|
381
|
+
step: currentStep?.id || 1,
|
|
382
|
+
eventType: failureType,
|
|
383
|
+
reason: errorReasonForAPI,
|
|
384
|
+
tags: walletType
|
|
385
|
+
? {
|
|
386
|
+
wallet: walletType,
|
|
387
|
+
}
|
|
388
|
+
: undefined,
|
|
389
|
+
})
|
|
390
|
+
.then()
|
|
391
|
+
.catch();
|
|
392
|
+
} else if (!!nextStepStatus && ['running'].includes(nextStepStatus)) {
|
|
393
|
+
swap.extraMessageSeverity = MessageSeverity.info;
|
|
394
|
+
} else if (
|
|
395
|
+
!!nextStepStatus &&
|
|
396
|
+
['success', 'approved'].includes(nextStepStatus)
|
|
397
|
+
) {
|
|
398
|
+
swap.extraMessageSeverity = MessageSeverity.success;
|
|
399
|
+
} else if (
|
|
400
|
+
nextStepStatus &&
|
|
401
|
+
['waitingForApproval'].includes(nextStepStatus)
|
|
402
|
+
) {
|
|
403
|
+
swap.extraMessageSeverity = MessageSeverity.warning;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (nextStepStatus === 'running' && currentStep) {
|
|
407
|
+
currentStep.startTransactionTime = new Date().getTime();
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
setStorage({
|
|
411
|
+
...getStorage(),
|
|
412
|
+
swapDetails: swap,
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
return updatedResult;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
*
|
|
420
|
+
* Set current step transaction hash, update pending swap status, and notify user if needed
|
|
421
|
+
*
|
|
422
|
+
*/
|
|
423
|
+
export function setStepTransactionIds(
|
|
424
|
+
{ getStorage, setStorage }: ExecuterActions<SwapStorage, SwapActionTypes>,
|
|
425
|
+
txId: string | null,
|
|
426
|
+
explorerUrl?: { url?: string; description?: string }
|
|
427
|
+
): void {
|
|
428
|
+
const swap = getStorage().swapDetails;
|
|
429
|
+
swap.hasAlreadyProceededToSign = null;
|
|
430
|
+
|
|
431
|
+
const currentStep = getCurrentStep(swap)!;
|
|
432
|
+
currentStep.executedTransactionId = txId;
|
|
433
|
+
currentStep.executedTransactionTime = new Date().getTime().toString();
|
|
434
|
+
if (explorerUrl?.url) {
|
|
435
|
+
currentStep.explorerUrl = [
|
|
436
|
+
...(currentStep.explorerUrl || []),
|
|
437
|
+
{
|
|
438
|
+
url: explorerUrl.url,
|
|
439
|
+
description: explorerUrl.description || null,
|
|
440
|
+
},
|
|
441
|
+
];
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const isApproval = isApprovalCurrentStepTx(currentStep);
|
|
445
|
+
|
|
446
|
+
if (isApproval) {
|
|
447
|
+
swap.extraMessage = 'Checking approve transaction status ...';
|
|
448
|
+
} else {
|
|
449
|
+
swap.extraMessage = 'Checking transaction status ...';
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
swap.extraMessageDetail = '';
|
|
453
|
+
swap.extraMessageSeverity = MessageSeverity.info;
|
|
454
|
+
|
|
455
|
+
setStorage({
|
|
456
|
+
...getStorage(),
|
|
457
|
+
swapDetails: swap,
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
notifier({
|
|
461
|
+
event: {
|
|
462
|
+
type: StepEventType.TX_EXECUTION,
|
|
463
|
+
status: StepExecutionEventStatus.TX_SENT,
|
|
464
|
+
},
|
|
465
|
+
swap: swap,
|
|
466
|
+
step: currentStep,
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
notifier({
|
|
470
|
+
event: { type: StepEventType.CHECK_STATUS },
|
|
471
|
+
swap: swap,
|
|
472
|
+
step: currentStep,
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* If a swap needs a wallet to be connected,
|
|
478
|
+
* By calling this function some related fields will be updated to show a correct message and state for notfiying the user.
|
|
479
|
+
*/
|
|
480
|
+
export function markRunningSwapAsWaitingForConnectingWallet(
|
|
481
|
+
{
|
|
482
|
+
getStorage,
|
|
483
|
+
setStorage,
|
|
484
|
+
}: Pick<ExecuterActions, 'getStorage' | 'setStorage'>,
|
|
485
|
+
reason: string,
|
|
486
|
+
reasonDetail: string
|
|
487
|
+
): void {
|
|
488
|
+
const swap = getStorage().swapDetails as SwapStorage['swapDetails'];
|
|
489
|
+
const currentStep = getCurrentStep(swap);
|
|
490
|
+
if (!currentStep) {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
const currentTime = new Date();
|
|
494
|
+
swap.lastNotificationTime = currentTime.getTime().toString();
|
|
495
|
+
|
|
496
|
+
const isAlreadyMarked =
|
|
497
|
+
currentStep.networkStatus ===
|
|
498
|
+
PendingSwapNetworkStatus.WaitingForConnectingWallet &&
|
|
499
|
+
swap.networkStatusExtraMessage === reason &&
|
|
500
|
+
swap.networkStatusExtraMessageDetail === reasonDetail;
|
|
501
|
+
|
|
502
|
+
if (isAlreadyMarked) {
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
currentStep.networkStatus =
|
|
507
|
+
PendingSwapNetworkStatus.WaitingForConnectingWallet;
|
|
508
|
+
swap.networkStatusExtraMessage = reason;
|
|
509
|
+
swap.networkStatusExtraMessageDetail = reasonDetail;
|
|
510
|
+
|
|
511
|
+
setStorage({
|
|
512
|
+
...getStorage(),
|
|
513
|
+
swapDetails: swap,
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* If a swap needs a certain network to proceed,
|
|
519
|
+
* By calling this function some related fields will be updated to show a correct message and state for notfiying the user.
|
|
520
|
+
*/
|
|
521
|
+
export function markRunningSwapAsSwitchingNetwork({
|
|
522
|
+
getStorage,
|
|
523
|
+
setStorage,
|
|
524
|
+
}: Pick<ExecuterActions, 'getStorage' | 'setStorage'>):
|
|
525
|
+
| {
|
|
526
|
+
swap: PendingSwap;
|
|
527
|
+
step: PendingSwapStep;
|
|
528
|
+
}
|
|
529
|
+
| undefined {
|
|
530
|
+
const swap = getStorage().swapDetails as SwapStorage['swapDetails'];
|
|
531
|
+
|
|
532
|
+
const currentStep = getCurrentStep(swap);
|
|
533
|
+
if (!currentStep) {
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Generate message
|
|
538
|
+
const { type } = getRequiredWallet(swap);
|
|
539
|
+
const fromNamespace = getCurrentNamespaceOf(swap, currentStep);
|
|
540
|
+
const reason = `Change ${type} wallet network to ${fromNamespace.network}`;
|
|
541
|
+
const reasonDetail = `We’re switching the connected network to ${fromNamespace.network}. Please check your wallet.`;
|
|
542
|
+
|
|
543
|
+
const currentTime = new Date();
|
|
544
|
+
swap.lastNotificationTime = currentTime.getTime().toString();
|
|
545
|
+
|
|
546
|
+
currentStep.networkStatus = PendingSwapNetworkStatus.WaitingForNetworkChange;
|
|
547
|
+
swap.networkStatusExtraMessage = reason;
|
|
548
|
+
swap.networkStatusExtraMessageDetail = reasonDetail;
|
|
549
|
+
|
|
550
|
+
setStorage({
|
|
551
|
+
...getStorage(),
|
|
552
|
+
swapDetails: swap,
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
return {
|
|
556
|
+
swap,
|
|
557
|
+
step: currentStep,
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* We are marking the queue as it depends on other queues to be run (on Parallel mode)
|
|
563
|
+
* By calling this function some related fields will be updated to show a correct message and state for notfiying the user.
|
|
564
|
+
*/
|
|
565
|
+
export function markRunningSwapAsDependsOnOtherQueues({
|
|
566
|
+
getStorage,
|
|
567
|
+
setStorage,
|
|
568
|
+
}: Pick<ExecuterActions, 'getStorage' | 'setStorage'>):
|
|
569
|
+
| {
|
|
570
|
+
swap: PendingSwap;
|
|
571
|
+
step: PendingSwapStep;
|
|
572
|
+
}
|
|
573
|
+
| undefined {
|
|
574
|
+
const swap = getStorage().swapDetails as SwapStorage['swapDetails'];
|
|
575
|
+
const currentStep = getCurrentStep(swap);
|
|
576
|
+
if (!currentStep) {
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
swap.networkStatusExtraMessage = '';
|
|
581
|
+
swap.networkStatusExtraMessageDetail = '';
|
|
582
|
+
currentStep.networkStatus = PendingSwapNetworkStatus.WaitingForQueue;
|
|
583
|
+
|
|
584
|
+
notifier({
|
|
585
|
+
event: {
|
|
586
|
+
type: StepEventType.TX_EXECUTION_BLOCKED,
|
|
587
|
+
status: StepExecutionBlockedEventStatus.WAITING_FOR_QUEUE,
|
|
588
|
+
},
|
|
589
|
+
swap,
|
|
590
|
+
step: currentStep,
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
setStorage({
|
|
594
|
+
...getStorage(),
|
|
595
|
+
swapDetails: swap,
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
return {
|
|
599
|
+
swap,
|
|
600
|
+
step: currentStep,
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
export async function delay(ms: number): Promise<unknown> {
|
|
605
|
+
return new Promise((res) => setTimeout(res, ms));
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
*
|
|
610
|
+
* To execute a swap, we are keeping the user prefrences on what wallet they are going to use for a sepecific blockchain
|
|
611
|
+
* By passing the swap and the network we are looking for, it returns the wallet name that user selected.
|
|
612
|
+
*
|
|
613
|
+
*/
|
|
614
|
+
export const getSwapWalletType = (
|
|
615
|
+
swap: PendingSwap,
|
|
616
|
+
network: Network
|
|
617
|
+
): WalletType => {
|
|
618
|
+
return swap.wallets[network]?.walletType;
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
*
|
|
623
|
+
* We are keeping the connected wallet in a specific structure (`Wallet`),
|
|
624
|
+
* By using this function we normally want to check a specific wallet is connected and exists or not.
|
|
625
|
+
*
|
|
626
|
+
*/
|
|
627
|
+
export function isWalletNull(wallet: Wallet | null): boolean {
|
|
628
|
+
return (
|
|
629
|
+
wallet === null ||
|
|
630
|
+
wallet?.blockchains === null ||
|
|
631
|
+
wallet?.blockchains.length === 0
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* In a `PendingSwap`, each step needs a wallet to proceed,
|
|
637
|
+
* By using this function we can access what wallet exactly we need to run current step.
|
|
638
|
+
*/
|
|
639
|
+
export function getRequiredWallet(swap: PendingSwap): {
|
|
640
|
+
type: WalletType | null;
|
|
641
|
+
namespace: TargetNamespace | null;
|
|
642
|
+
address: string | null;
|
|
643
|
+
} {
|
|
644
|
+
const step = getCurrentStep(swap)!;
|
|
645
|
+
const currentNamespace = getCurrentNamespaceOfOrNull(swap, step);
|
|
646
|
+
if (!currentNamespace) {
|
|
647
|
+
return {
|
|
648
|
+
type: null,
|
|
649
|
+
namespace: null,
|
|
650
|
+
address: null,
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
const walletType = getSwapWalletType(swap, currentNamespace.network);
|
|
655
|
+
const sourceWallet = swap.wallets[currentNamespace.network];
|
|
656
|
+
|
|
657
|
+
return {
|
|
658
|
+
type: walletType || null,
|
|
659
|
+
namespace: currentNamespace,
|
|
660
|
+
address: sourceWallet ? sourceWallet.address : null,
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* On EVM compatible wallets, There is one instance with different chains (like Polygon)
|
|
666
|
+
* To get the chain from instance we will use this function.
|
|
667
|
+
*/
|
|
668
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
669
|
+
async function getChainId(provider: any): Promise<string | number | null> {
|
|
670
|
+
try {
|
|
671
|
+
const chainId: number | string | null =
|
|
672
|
+
(await provider.request({ method: 'eth_chainId' })) || provider?.chainId;
|
|
673
|
+
return chainId;
|
|
674
|
+
} catch {
|
|
675
|
+
return provider?.chainId;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
export async function getProviderChainId(
|
|
680
|
+
providers: Providers,
|
|
681
|
+
hubProvider: (type: WalletType) => Provider,
|
|
682
|
+
walletType: string
|
|
683
|
+
) {
|
|
684
|
+
const legacyProvider = getEvmProvider(providers, walletType);
|
|
685
|
+
if (legacyProvider) {
|
|
686
|
+
const chainId = await getChainId(legacyProvider);
|
|
687
|
+
return chainId;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// Legacy provider was not found, try to get the chain id from hub provider.
|
|
691
|
+
const provider = hubProvider(walletType);
|
|
692
|
+
const evmNamespace = provider.get('evm');
|
|
693
|
+
if (!evmNamespace) {
|
|
694
|
+
throw new Error('EVM namespace not found for wallet type: ' + walletType);
|
|
695
|
+
}
|
|
696
|
+
const chainId = evmNamespace.getChainId();
|
|
697
|
+
return chainId;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* For running a swap safely, we need to make sure about the state of wallet
|
|
702
|
+
* which means the netowrk/chain of wallet should be exactly on what a transaction needs.
|
|
703
|
+
*/
|
|
704
|
+
export async function isNetworkMatchedForTransaction(
|
|
705
|
+
swap: PendingSwap,
|
|
706
|
+
step: PendingSwapStep,
|
|
707
|
+
wallet: Wallet | null,
|
|
708
|
+
meta: Meta,
|
|
709
|
+
providers: Providers,
|
|
710
|
+
hubProvider: (type: WalletType) => Provider
|
|
711
|
+
): Promise<boolean> {
|
|
712
|
+
if (isWalletNull(wallet)) {
|
|
713
|
+
return false;
|
|
714
|
+
}
|
|
715
|
+
const fromNamespace = getCurrentNamespaceOfOrNull(swap, step);
|
|
716
|
+
if (!fromNamespace) {
|
|
717
|
+
return false;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
if (
|
|
721
|
+
meta.evmBasedChains.find(
|
|
722
|
+
(evmBlochain) => evmBlochain.name === fromNamespace.network
|
|
723
|
+
)
|
|
724
|
+
) {
|
|
725
|
+
try {
|
|
726
|
+
const sourceWallet = swap.wallets[fromNamespace.network];
|
|
727
|
+
if (sourceWallet) {
|
|
728
|
+
const chainId = await getProviderChainId(
|
|
729
|
+
providers,
|
|
730
|
+
hubProvider,
|
|
731
|
+
sourceWallet.walletType
|
|
732
|
+
);
|
|
733
|
+
if (chainId) {
|
|
734
|
+
const blockChain = getBlockChainNameFromId(
|
|
735
|
+
chainId,
|
|
736
|
+
Object.entries(meta.blockchains).map(
|
|
737
|
+
([, blockchainMeta]) => blockchainMeta
|
|
738
|
+
)
|
|
739
|
+
);
|
|
740
|
+
if (
|
|
741
|
+
blockChain &&
|
|
742
|
+
blockChain.toLowerCase() === fromNamespace.network.toLowerCase()
|
|
743
|
+
) {
|
|
744
|
+
return true;
|
|
745
|
+
}
|
|
746
|
+
if (
|
|
747
|
+
blockChain &&
|
|
748
|
+
blockChain.toLowerCase() !== fromNamespace.network.toLowerCase()
|
|
749
|
+
) {
|
|
750
|
+
return false;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
} catch (e) {
|
|
755
|
+
console.log(e);
|
|
756
|
+
}
|
|
757
|
+
return false;
|
|
758
|
+
}
|
|
759
|
+
return true;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
export const isTxAlreadyCreated = (
|
|
763
|
+
swap: PendingSwap,
|
|
764
|
+
step: PendingSwapStep
|
|
765
|
+
): boolean => {
|
|
766
|
+
const result =
|
|
767
|
+
swap.wallets[step.evmTransaction?.blockChain || ''] ||
|
|
768
|
+
swap.wallets[step.evmApprovalTransaction?.blockChain || ''] ||
|
|
769
|
+
swap.wallets[step.tronTransaction?.blockChain || ''] ||
|
|
770
|
+
swap.wallets[step.tronApprovalTransaction?.blockChain || ''] ||
|
|
771
|
+
swap.wallets[step.starknetTransaction?.blockChain || ''] ||
|
|
772
|
+
swap.wallets[step.starknetApprovalTransaction?.blockChain || ''] ||
|
|
773
|
+
swap.wallets[step.cosmosTransaction?.blockChain || ''] ||
|
|
774
|
+
swap.wallets[step.solanaTransaction?.blockChain || ''] ||
|
|
775
|
+
swap.wallets[step.tonTransaction?.blockChain || ''] ||
|
|
776
|
+
swap.wallets[step.suiTransaction?.blockChain || ''] ||
|
|
777
|
+
step.transferTransaction?.fromWalletAddress ||
|
|
778
|
+
null;
|
|
779
|
+
|
|
780
|
+
return result !== null;
|
|
781
|
+
};
|
|
782
|
+
|
|
783
|
+
export function resetNetworkStatus(
|
|
784
|
+
actions: ExecuterActions<SwapStorage, SwapActionTypes, SwapQueueContext>
|
|
785
|
+
): void {
|
|
786
|
+
const { getStorage, setStorage } = actions;
|
|
787
|
+
const swap = getStorage().swapDetails;
|
|
788
|
+
const currentStep = getCurrentStep(swap);
|
|
789
|
+
|
|
790
|
+
if (currentStep?.networkStatus) {
|
|
791
|
+
currentStep.networkStatus = null;
|
|
792
|
+
setStorage({ ...getStorage(), swapDetails: swap });
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
export function updateNetworkStatus(
|
|
797
|
+
actions: Pick<
|
|
798
|
+
ExecuterActions<SwapStorage, SwapActionTypes, SwapQueueContext>,
|
|
799
|
+
'getStorage' | 'setStorage'
|
|
800
|
+
>,
|
|
801
|
+
data: {
|
|
802
|
+
message: string;
|
|
803
|
+
details: string;
|
|
804
|
+
status: PendingSwapNetworkStatus | null;
|
|
805
|
+
} = {
|
|
806
|
+
message: '',
|
|
807
|
+
details: '',
|
|
808
|
+
status: null,
|
|
809
|
+
}
|
|
810
|
+
): void {
|
|
811
|
+
const { message, details, status } = data;
|
|
812
|
+
const { getStorage, setStorage } = actions;
|
|
813
|
+
const swap = getStorage().swapDetails;
|
|
814
|
+
const currentStep = getCurrentStep(swap);
|
|
815
|
+
|
|
816
|
+
if (currentStep?.networkStatus) {
|
|
817
|
+
swap.networkStatusExtraMessage = message;
|
|
818
|
+
swap.networkStatusExtraMessageDetail = details;
|
|
819
|
+
currentStep.networkStatus = status;
|
|
820
|
+
setStorage({ ...getStorage(), swapDetails: swap });
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* Event handler for blocked tasks.
|
|
826
|
+
* If a transcation execution is manually blocked (like for parallel or waiting for wallet),
|
|
827
|
+
* This function will be called by queue manager using `queue definition`.
|
|
828
|
+
*
|
|
829
|
+
* It checks if the required wallet is connected, unblock the queue to be run.
|
|
830
|
+
*/
|
|
831
|
+
export function onBlockForConnectWallet(
|
|
832
|
+
event: WhenTaskBlockedEvent,
|
|
833
|
+
meta: WhenTaskBlockedMeta
|
|
834
|
+
): void {
|
|
835
|
+
const { context, queue } = meta;
|
|
836
|
+
const swap = queue.getStorage().swapDetails as SwapStorage['swapDetails'];
|
|
837
|
+
|
|
838
|
+
const { ok, reason } = isRequiredWalletConnected(swap, context.state);
|
|
839
|
+
|
|
840
|
+
if (!ok) {
|
|
841
|
+
const currentStep = getCurrentStep(swap)!;
|
|
842
|
+
const { type: walletType, address } = getRequiredWallet(swap);
|
|
843
|
+
notifier({
|
|
844
|
+
event: {
|
|
845
|
+
type: StepEventType.TX_EXECUTION_BLOCKED,
|
|
846
|
+
...(reason === 'account_miss_match'
|
|
847
|
+
? {
|
|
848
|
+
status:
|
|
849
|
+
StepExecutionBlockedEventStatus.WAITING_FOR_CHANGE_WALLET_ACCOUNT,
|
|
850
|
+
requiredAccount: address ?? undefined,
|
|
851
|
+
}
|
|
852
|
+
: {
|
|
853
|
+
status:
|
|
854
|
+
StepExecutionBlockedEventStatus.WAITING_FOR_WALLET_CONNECT,
|
|
855
|
+
requiredWallet: walletType ?? undefined,
|
|
856
|
+
requiredAccount: address ?? undefined,
|
|
857
|
+
}),
|
|
858
|
+
},
|
|
859
|
+
swap: swap,
|
|
860
|
+
step: currentStep,
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
markRunningSwapAsWaitingForConnectingWallet(
|
|
864
|
+
{
|
|
865
|
+
getStorage: queue.getStorage.bind(queue),
|
|
866
|
+
setStorage: queue.setStorage.bind(queue),
|
|
867
|
+
},
|
|
868
|
+
ERROR_MESSAGE_WAIT_FOR_WALLET,
|
|
869
|
+
event.reason.description
|
|
870
|
+
);
|
|
871
|
+
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
queue.unblock();
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* Event handler for blocked tasks.
|
|
880
|
+
* If a transcation execution is manually blocked (like for parallel or waiting for walle),
|
|
881
|
+
* This function will be called by queue manager using `queue definition`.
|
|
882
|
+
*
|
|
883
|
+
* It checks if the required network is connected, unblock the queue to be run.
|
|
884
|
+
* Note: it automatically try to switch the network if its `provider` supports.
|
|
885
|
+
*/
|
|
886
|
+
export function onBlockForChangeNetwork(
|
|
887
|
+
_event: WhenTaskBlockedEvent,
|
|
888
|
+
meta: WhenTaskBlockedMeta
|
|
889
|
+
): void {
|
|
890
|
+
const { context, queue } = meta;
|
|
891
|
+
const swap = queue.getStorage().swapDetails as SwapStorage['swapDetails'];
|
|
892
|
+
const currentStep = getCurrentStep(swap);
|
|
893
|
+
|
|
894
|
+
if (!currentStep || swap.status !== 'running') {
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
const result = markRunningSwapAsSwitchingNetwork({
|
|
899
|
+
getStorage: queue.getStorage.bind(queue),
|
|
900
|
+
setStorage: queue.setStorage.bind(queue),
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
const requiredNetwork = getCurrentNamespaceOfOrNull(
|
|
904
|
+
swap,
|
|
905
|
+
currentStep
|
|
906
|
+
)?.network;
|
|
907
|
+
|
|
908
|
+
const requiredWallet = getRequiredWallet(swap).type;
|
|
909
|
+
|
|
910
|
+
const currentNetwork = requiredWallet
|
|
911
|
+
? context.state(requiredWallet).network
|
|
912
|
+
: undefined;
|
|
913
|
+
|
|
914
|
+
if (result) {
|
|
915
|
+
notifier({
|
|
916
|
+
event: {
|
|
917
|
+
type: StepEventType.TX_EXECUTION_BLOCKED,
|
|
918
|
+
status: StepExecutionBlockedEventStatus.WAITING_FOR_NETWORK_CHANGE,
|
|
919
|
+
requiredNetwork: requiredNetwork ?? undefined,
|
|
920
|
+
currentNetwork: currentNetwork ?? undefined,
|
|
921
|
+
},
|
|
922
|
+
swap: result.swap,
|
|
923
|
+
step: result.step,
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
// Try to auto switch
|
|
928
|
+
const { type, namespace } = getRequiredWallet(swap);
|
|
929
|
+
if (!!type && !!namespace?.network) {
|
|
930
|
+
if (context.canSwitchNetworkTo(type, namespace.network, namespace)) {
|
|
931
|
+
const result = context.switchNetwork(type, namespace);
|
|
932
|
+
if (result) {
|
|
933
|
+
result
|
|
934
|
+
.then(() => {
|
|
935
|
+
queue.unblock();
|
|
936
|
+
})
|
|
937
|
+
.catch((error) => {
|
|
938
|
+
// Update network to mark it as network change failed.
|
|
939
|
+
updateNetworkStatus(
|
|
940
|
+
{
|
|
941
|
+
getStorage: queue.getStorage.bind(queue) as () => SwapStorage,
|
|
942
|
+
setStorage: queue.setStorage.bind(
|
|
943
|
+
queue
|
|
944
|
+
) as SetStorage<SwapStorage>,
|
|
945
|
+
},
|
|
946
|
+
{
|
|
947
|
+
message: error.message,
|
|
948
|
+
details: error.message,
|
|
949
|
+
status: PendingSwapNetworkStatus.NetworkChangeFailed,
|
|
950
|
+
}
|
|
951
|
+
);
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
/**
|
|
959
|
+
* Event handler for blocked tasks. (Parallel mode)
|
|
960
|
+
* If a transcation execution flow is manually blocked (like for parallel or waiting for walle),
|
|
961
|
+
* This function will be called by queue manager using `queue definition`.
|
|
962
|
+
*
|
|
963
|
+
* It checks the blocked tasks, if there is no active `claimed` queue, try to give it to the best candidate.
|
|
964
|
+
*/
|
|
965
|
+
export function onDependsOnOtherQueues(
|
|
966
|
+
_event: WhenTaskBlockedEvent,
|
|
967
|
+
meta: WhenTaskBlockedMeta
|
|
968
|
+
): void {
|
|
969
|
+
const { getBlockedTasks, forceExecute, queue, manager, context } = meta;
|
|
970
|
+
const { setClaimer, claimedBy, reset } = claimQueue();
|
|
971
|
+
|
|
972
|
+
// We only needs those blocked tasks that have DEPENDS_ON_OTHER_QUEUES reason.
|
|
973
|
+
const blockedTasks = getBlockedTasks().filter(
|
|
974
|
+
(task) => task.reason.reason === BlockReason.DEPENDS_ON_OTHER_QUEUES
|
|
975
|
+
);
|
|
976
|
+
|
|
977
|
+
if (blockedTasks.length === 0) {
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
const claimerId = claimedBy();
|
|
982
|
+
const isClaimedByAnyQueue = !!claimerId;
|
|
983
|
+
|
|
984
|
+
if (claimerId === queue.id) {
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
// Check if any queue `claimed` before, if yes, we don't should do anything.
|
|
989
|
+
if (isClaimedByAnyQueue) {
|
|
990
|
+
// We need to keep the latest swap messages
|
|
991
|
+
markRunningSwapAsDependsOnOtherQueues({
|
|
992
|
+
getStorage: queue.getStorage.bind(queue),
|
|
993
|
+
setStorage: queue.setStorage.bind(queue),
|
|
994
|
+
});
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
// Prioritize current queue to be run first.
|
|
999
|
+
|
|
1000
|
+
let task = blockedTasks.find((task) => {
|
|
1001
|
+
return task.queue_id === meta.queue_id;
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
// If current task isn't available anymore, fallback to first blocked task.
|
|
1005
|
+
if (!task) {
|
|
1006
|
+
const firstBlockedTask = blockedTasks[0];
|
|
1007
|
+
task = firstBlockedTask;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
setClaimer(task.queue_id);
|
|
1011
|
+
const claimedStorage = task.storage.get() as SwapStorage;
|
|
1012
|
+
const { type, namespace, address } = getRequiredWallet(
|
|
1013
|
+
claimedStorage.swapDetails
|
|
1014
|
+
);
|
|
1015
|
+
|
|
1016
|
+
// Run
|
|
1017
|
+
forceExecute(task.queue_id, {
|
|
1018
|
+
claimedBy: claimedBy(),
|
|
1019
|
+
resetClaimedBy: () => {
|
|
1020
|
+
reset();
|
|
1021
|
+
// TODO: Use key generator
|
|
1022
|
+
if (type) {
|
|
1023
|
+
retryOn(
|
|
1024
|
+
{
|
|
1025
|
+
walletType: type,
|
|
1026
|
+
network: namespace?.network,
|
|
1027
|
+
accounts: address ? [address] : [],
|
|
1028
|
+
},
|
|
1029
|
+
manager,
|
|
1030
|
+
context.canSwitchNetworkTo
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
},
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
export function isRequiredWalletConnected(
|
|
1038
|
+
swap: PendingSwap,
|
|
1039
|
+
getState: (type: WalletType) => WalletState
|
|
1040
|
+
): { ok: boolean; reason: 'not_connected' | 'account_miss_match' } {
|
|
1041
|
+
const { type, address } = getRequiredWallet(swap);
|
|
1042
|
+
if (!type || !address) {
|
|
1043
|
+
return { ok: false, reason: 'not_connected' };
|
|
1044
|
+
}
|
|
1045
|
+
const walletState = getState(type);
|
|
1046
|
+
const { accounts, connected } = walletState;
|
|
1047
|
+
const connectedAccounts = accounts || [];
|
|
1048
|
+
if (!connected) {
|
|
1049
|
+
return { ok: false, reason: 'not_connected' };
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
const matched = connectedAccounts.some((account) => {
|
|
1053
|
+
const { address: accountAddress } = readAccountAddress(account);
|
|
1054
|
+
return address.toLocaleLowerCase() === accountAddress.toLocaleLowerCase();
|
|
1055
|
+
});
|
|
1056
|
+
return { ok: matched, reason: 'account_miss_match' };
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
export function handleSuccessfulSign(
|
|
1060
|
+
actions: ExecuterActions<SwapStorage, SwapActionTypes, SwapQueueContext>,
|
|
1061
|
+
data: { isApproval: boolean }
|
|
1062
|
+
) {
|
|
1063
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1064
|
+
return ({ hash, response }: { hash: string; response?: any }) => {
|
|
1065
|
+
const { setTransactionDataByHash } = inMemoryTransactionsData();
|
|
1066
|
+
const { getStorage, next, schedule, context } = actions;
|
|
1067
|
+
const { meta } = context;
|
|
1068
|
+
const swap = getStorage().swapDetails;
|
|
1069
|
+
|
|
1070
|
+
const currentStep = getCurrentStep(swap)!;
|
|
1071
|
+
|
|
1072
|
+
const currentStepNamespace = getCurrentNamespaceOf(swap, currentStep);
|
|
1073
|
+
const explorerUrl = getScannerUrl(
|
|
1074
|
+
hash,
|
|
1075
|
+
currentStepNamespace.network,
|
|
1076
|
+
meta.blockchains
|
|
1077
|
+
);
|
|
1078
|
+
setStepTransactionIds(
|
|
1079
|
+
actions,
|
|
1080
|
+
hash,
|
|
1081
|
+
explorerUrl && (!response || (response && !response.hashRequiringUpdate))
|
|
1082
|
+
? {
|
|
1083
|
+
url: explorerUrl,
|
|
1084
|
+
description: data.isApproval ? 'Approve' : 'Swap',
|
|
1085
|
+
}
|
|
1086
|
+
: undefined
|
|
1087
|
+
);
|
|
1088
|
+
// response used for evm transactions to get receipt and track replaced
|
|
1089
|
+
if (response) {
|
|
1090
|
+
setTransactionDataByHash(hash, { response });
|
|
1091
|
+
}
|
|
1092
|
+
schedule(SwapActionTypes.CHECK_TRANSACTION_STATUS);
|
|
1093
|
+
next();
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
export function handleRejectedSign(
|
|
1098
|
+
actions: ExecuterActions<SwapStorage, SwapActionTypes, SwapQueueContext>
|
|
1099
|
+
) {
|
|
1100
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1101
|
+
return (error: any) => {
|
|
1102
|
+
const { getStorage, setStorage, failed } = actions;
|
|
1103
|
+
const swap = getStorage().swapDetails;
|
|
1104
|
+
|
|
1105
|
+
const currentStep = getCurrentStep(swap)!;
|
|
1106
|
+
|
|
1107
|
+
const sourceWallet = getRelatedWallet(swap, currentStep);
|
|
1108
|
+
if (swap.status === 'failed') {
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
const { extraMessage, extraMessageDetail, extraMessageErrorCode } =
|
|
1113
|
+
prettifyErrorMessage(error);
|
|
1114
|
+
|
|
1115
|
+
warn(error, {
|
|
1116
|
+
tags: {
|
|
1117
|
+
requestId: swap.requestId,
|
|
1118
|
+
rpc: true,
|
|
1119
|
+
swapper: currentStep?.swapperId || '',
|
|
1120
|
+
walletType: sourceWallet?.walletType || '',
|
|
1121
|
+
},
|
|
1122
|
+
context: SignerError.isSignerError(error) ? error.getErrorContext() : {},
|
|
1123
|
+
});
|
|
1124
|
+
|
|
1125
|
+
const updateResult = updateSwapStatus({
|
|
1126
|
+
getStorage,
|
|
1127
|
+
setStorage,
|
|
1128
|
+
nextStatus: 'failed',
|
|
1129
|
+
nextStepStatus: 'failed',
|
|
1130
|
+
message: extraMessage,
|
|
1131
|
+
details: extraMessageDetail,
|
|
1132
|
+
errorCode: extraMessageErrorCode,
|
|
1133
|
+
trace: error?.cause,
|
|
1134
|
+
});
|
|
1135
|
+
|
|
1136
|
+
notifier({
|
|
1137
|
+
event: {
|
|
1138
|
+
type: StepEventType.FAILED,
|
|
1139
|
+
reason: extraMessage,
|
|
1140
|
+
reasonCode: updateResult.failureType ?? DEFAULT_ERROR_CODE,
|
|
1141
|
+
inputAmount: getLastFinishedStepInput(swap),
|
|
1142
|
+
inputAmountUsd: getLastFinishedStepInputUsd(swap),
|
|
1143
|
+
},
|
|
1144
|
+
...updateResult,
|
|
1145
|
+
});
|
|
1146
|
+
failed();
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
export function checkWaitingForConnectWalletChange(params: {
|
|
1151
|
+
lastConnectedWallet: LastConnectedWallet;
|
|
1152
|
+
manager?: Manager;
|
|
1153
|
+
evmChains: EvmBlockchainMeta[];
|
|
1154
|
+
}): void {
|
|
1155
|
+
const { lastConnectedWallet, evmChains, manager } = params;
|
|
1156
|
+
const { walletType: wallet, network } = lastConnectedWallet;
|
|
1157
|
+
// We only need change network for EVM chains.
|
|
1158
|
+
if (!evmChains.some((chain) => chain.name == network)) {
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
manager?.getAll().forEach((q) => {
|
|
1163
|
+
const queueStorage = q.list.getStorage() as SwapStorage | undefined;
|
|
1164
|
+
const swap = queueStorage?.swapDetails;
|
|
1165
|
+
if (swap && swap.status === 'running') {
|
|
1166
|
+
const currentStep = getCurrentStep(swap);
|
|
1167
|
+
if (currentStep) {
|
|
1168
|
+
const currentStepRequiredWallet =
|
|
1169
|
+
queueStorage?.swapDetails.wallets[currentStep.fromBlockchain]
|
|
1170
|
+
?.walletType;
|
|
1171
|
+
const hasWaitingForConnect = Object.keys(q.list.state.tasks).some(
|
|
1172
|
+
(taskId) => {
|
|
1173
|
+
const task = q.list.state.tasks[taskId];
|
|
1174
|
+
return (
|
|
1175
|
+
task.status === Status.BLOCKED &&
|
|
1176
|
+
// TODO double check later
|
|
1177
|
+
[BlockReason.WAIT_FOR_CONNECT_WALLET].includes(
|
|
1178
|
+
task.blockedFor?.reason
|
|
1179
|
+
)
|
|
1180
|
+
);
|
|
1181
|
+
}
|
|
1182
|
+
);
|
|
1183
|
+
|
|
1184
|
+
const requiredNetwork = getCurrentNamespaceOfOrNull(
|
|
1185
|
+
swap,
|
|
1186
|
+
currentStep
|
|
1187
|
+
)?.network;
|
|
1188
|
+
|
|
1189
|
+
// We only need change network for EVM chains.
|
|
1190
|
+
if (!evmChains.some((chain) => chain.name == requiredNetwork)) {
|
|
1191
|
+
return;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
if (
|
|
1195
|
+
currentStepRequiredWallet === wallet &&
|
|
1196
|
+
hasWaitingForConnect &&
|
|
1197
|
+
requiredNetwork != network
|
|
1198
|
+
) {
|
|
1199
|
+
const queueInstance = q.list;
|
|
1200
|
+
const { type } = getRequiredWallet(swap);
|
|
1201
|
+
const description = ERROR_MESSAGE_WAIT_FOR_CHANGE_NETWORK(type);
|
|
1202
|
+
|
|
1203
|
+
q.list.block({
|
|
1204
|
+
reason: {
|
|
1205
|
+
reason: BlockReason.WAIT_FOR_NETWORK_CHANGE,
|
|
1206
|
+
description,
|
|
1207
|
+
},
|
|
1208
|
+
silent: true,
|
|
1209
|
+
});
|
|
1210
|
+
|
|
1211
|
+
const result = markRunningSwapAsSwitchingNetwork({
|
|
1212
|
+
getStorage: queueInstance.getStorage.bind(queueInstance),
|
|
1213
|
+
setStorage: queueInstance.setStorage.bind(queueInstance),
|
|
1214
|
+
});
|
|
1215
|
+
|
|
1216
|
+
if (result) {
|
|
1217
|
+
notifier({
|
|
1218
|
+
event: {
|
|
1219
|
+
type: StepEventType.TX_EXECUTION_BLOCKED,
|
|
1220
|
+
status:
|
|
1221
|
+
StepExecutionBlockedEventStatus.WAITING_FOR_NETWORK_CHANGE,
|
|
1222
|
+
currentNetwork: network,
|
|
1223
|
+
requiredNetwork: requiredNetwork ?? undefined,
|
|
1224
|
+
},
|
|
1225
|
+
swap: result.swap,
|
|
1226
|
+
step: result.step,
|
|
1227
|
+
});
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
export function checkWaitingForNetworkChange(manager?: Manager): void {
|
|
1236
|
+
manager?.getAll().forEach((q) => {
|
|
1237
|
+
const hasWaitingForNetwork = Object.keys(q.list.state.tasks).some(
|
|
1238
|
+
(taskId) => {
|
|
1239
|
+
const task = q.list.state.tasks[taskId];
|
|
1240
|
+
return (
|
|
1241
|
+
task.status === Status.BLOCKED &&
|
|
1242
|
+
[
|
|
1243
|
+
BlockReason.WAIT_FOR_NETWORK_CHANGE,
|
|
1244
|
+
BlockReason.DEPENDS_ON_OTHER_QUEUES,
|
|
1245
|
+
].includes(task.blockedFor?.reason)
|
|
1246
|
+
);
|
|
1247
|
+
}
|
|
1248
|
+
);
|
|
1249
|
+
|
|
1250
|
+
if (hasWaitingForNetwork) {
|
|
1251
|
+
const swap = q.list.getStorage()
|
|
1252
|
+
?.swapDetails as SwapStorage['swapDetails'];
|
|
1253
|
+
if (swap.status === 'running') {
|
|
1254
|
+
const { type } = getRequiredWallet(swap);
|
|
1255
|
+
const description = ERROR_MESSAGE_WAIT_FOR_WALLET_DESCRIPTION(type);
|
|
1256
|
+
|
|
1257
|
+
// Change the block reason to waiting for connecting wallet
|
|
1258
|
+
q.list.block({
|
|
1259
|
+
reason: {
|
|
1260
|
+
reason: BlockReason.WAIT_FOR_CONNECT_WALLET,
|
|
1261
|
+
description,
|
|
1262
|
+
},
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
/**
|
|
1270
|
+
* Get list of all running swaps
|
|
1271
|
+
*
|
|
1272
|
+
* @param manager
|
|
1273
|
+
* @returns list of pending swaps
|
|
1274
|
+
*/
|
|
1275
|
+
export function getRunningSwaps(manager: Manager): PendingSwap[] {
|
|
1276
|
+
const queues = manager?.getAll() || new Map<QueueName, QueueInfo>();
|
|
1277
|
+
const result: PendingSwap[] = [];
|
|
1278
|
+
queues.forEach((q) => {
|
|
1279
|
+
// retry only on affected queues
|
|
1280
|
+
const queueStorage = q.list.getStorage() as SwapStorage | undefined;
|
|
1281
|
+
const swap = queueStorage?.swapDetails;
|
|
1282
|
+
if (!swap || swap.status !== 'running') {
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1285
|
+
result.push(swap);
|
|
1286
|
+
});
|
|
1287
|
+
return result;
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
/**
|
|
1291
|
+
*
|
|
1292
|
+
* Trying to reset notifications for pending swaps to correct message on page load.
|
|
1293
|
+
* We could remove this after supporting auto connect for wallets.
|
|
1294
|
+
*
|
|
1295
|
+
* @param swaps
|
|
1296
|
+
* @param notifier
|
|
1297
|
+
* @returns
|
|
1298
|
+
*/
|
|
1299
|
+
export function resetRunningSwapNotifsOnPageLoad(runningSwaps: PendingSwap[]) {
|
|
1300
|
+
runningSwaps.forEach((swap) => {
|
|
1301
|
+
const currentStep = getCurrentStep(swap);
|
|
1302
|
+
const eventType = StepEventType.TX_EXECUTION_BLOCKED;
|
|
1303
|
+
let eventSubtype:
|
|
1304
|
+
| StepExecutionBlockedEventStatus.WAITING_FOR_QUEUE
|
|
1305
|
+
| StepExecutionBlockedEventStatus.WAITING_FOR_WALLET_CONNECT
|
|
1306
|
+
| undefined;
|
|
1307
|
+
if (
|
|
1308
|
+
currentStep?.networkStatus === PendingSwapNetworkStatus.WaitingForQueue
|
|
1309
|
+
) {
|
|
1310
|
+
eventSubtype = StepExecutionBlockedEventStatus.WAITING_FOR_QUEUE;
|
|
1311
|
+
} else if (swap?.status === 'running') {
|
|
1312
|
+
eventSubtype = StepExecutionBlockedEventStatus.WAITING_FOR_WALLET_CONNECT;
|
|
1313
|
+
}
|
|
1314
|
+
if (!!eventType && !!notifier) {
|
|
1315
|
+
notifier({
|
|
1316
|
+
event: {
|
|
1317
|
+
type: eventType,
|
|
1318
|
+
status:
|
|
1319
|
+
eventSubtype ?? StepExecutionBlockedEventStatus.WAITING_FOR_QUEUE,
|
|
1320
|
+
},
|
|
1321
|
+
swap: swap,
|
|
1322
|
+
step: currentStep,
|
|
1323
|
+
});
|
|
1324
|
+
}
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
/**
|
|
1329
|
+
*
|
|
1330
|
+
* Try to run blocked tasks by wallet and network name.
|
|
1331
|
+
* Goes through queues and extract blocked queues with matched wallet.
|
|
1332
|
+
* If found any blocked tasks with same wallet and network, runs them.
|
|
1333
|
+
* If not, runs only blocked tasks with matched wallet.
|
|
1334
|
+
*
|
|
1335
|
+
* @param wallet_network a string includes `wallet` type and `network` type.
|
|
1336
|
+
* @param manager
|
|
1337
|
+
* @returns
|
|
1338
|
+
*/
|
|
1339
|
+
export function retryOn(
|
|
1340
|
+
lastConnectedWallet: LastConnectedWallet,
|
|
1341
|
+
manager?: Manager,
|
|
1342
|
+
canSwitchNetworkTo?: UseQueueManagerParams['canSwitchNetworkTo'],
|
|
1343
|
+
options = { fallbackToOnlyWallet: true }
|
|
1344
|
+
): void {
|
|
1345
|
+
const { walletType: wallet, network } = lastConnectedWallet;
|
|
1346
|
+
if (!wallet) {
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
const walletAndNetworkMatched: QueueType[] = [];
|
|
1351
|
+
const onlyWalletMatched: QueueType[] = [];
|
|
1352
|
+
|
|
1353
|
+
manager?.getAll().forEach((q) => {
|
|
1354
|
+
// retry only on affected queues
|
|
1355
|
+
if (q.status === Status.BLOCKED) {
|
|
1356
|
+
const queueStorage = q.list.getStorage() as SwapStorage | undefined;
|
|
1357
|
+
const swap = queueStorage?.swapDetails;
|
|
1358
|
+
|
|
1359
|
+
if (swap && swap.status === 'running') {
|
|
1360
|
+
const currentStep = getCurrentStep(swap);
|
|
1361
|
+
if (currentStep) {
|
|
1362
|
+
if (
|
|
1363
|
+
network &&
|
|
1364
|
+
getCurrentNamespaceOfOrNull(swap, currentStep)?.network ==
|
|
1365
|
+
network &&
|
|
1366
|
+
queueStorage?.swapDetails.wallets[network]?.walletType === wallet
|
|
1367
|
+
) {
|
|
1368
|
+
walletAndNetworkMatched.push(q.list);
|
|
1369
|
+
} else if (
|
|
1370
|
+
queueStorage?.swapDetails.wallets[currentStep.fromBlockchain]
|
|
1371
|
+
?.walletType === wallet
|
|
1372
|
+
) {
|
|
1373
|
+
onlyWalletMatched.push(q.list);
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
});
|
|
1379
|
+
|
|
1380
|
+
let finalQueueToBeRun: QueueType | undefined = undefined;
|
|
1381
|
+
if (walletAndNetworkMatched.length > 0) {
|
|
1382
|
+
finalQueueToBeRun = walletAndNetworkMatched[0];
|
|
1383
|
+
|
|
1384
|
+
if (walletAndNetworkMatched.length > 1) {
|
|
1385
|
+
for (let i = 1; i < walletAndNetworkMatched.length; i++) {
|
|
1386
|
+
const currentQueue = walletAndNetworkMatched[i];
|
|
1387
|
+
|
|
1388
|
+
markRunningSwapAsDependsOnOtherQueues({
|
|
1389
|
+
getStorage: currentQueue.getStorage.bind(currentQueue),
|
|
1390
|
+
setStorage: currentQueue.setStorage.bind(currentQueue),
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
} else if (onlyWalletMatched.length > 0 && options.fallbackToOnlyWallet) {
|
|
1395
|
+
finalQueueToBeRun = onlyWalletMatched[0];
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
if (finalQueueToBeRun) {
|
|
1399
|
+
const finalQueueStorage = finalQueueToBeRun.getStorage() as SwapStorage;
|
|
1400
|
+
const currentSwap = getCurrentStep(finalQueueStorage?.swapDetails);
|
|
1401
|
+
|
|
1402
|
+
const currentNamespace = currentSwap
|
|
1403
|
+
? getCurrentNamespaceOfOrNull(finalQueueStorage.swapDetails, currentSwap)
|
|
1404
|
+
: null;
|
|
1405
|
+
if (
|
|
1406
|
+
!network ||
|
|
1407
|
+
!currentNamespace ||
|
|
1408
|
+
!canSwitchNetworkTo?.(wallet, network, currentNamespace)
|
|
1409
|
+
) {
|
|
1410
|
+
finalQueueToBeRun.unblock();
|
|
1411
|
+
} else {
|
|
1412
|
+
finalQueueToBeRun.checkBlock();
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
/*
|
|
1418
|
+
*For avoiding conflict by making too many requests to wallet, we need to make sure
|
|
1419
|
+
*We only run one request at a time (In parallel mode).
|
|
1420
|
+
*/
|
|
1421
|
+
export function isNeedBlockQueueForParallel(step: PendingSwapStep): boolean {
|
|
1422
|
+
return !!step.evmTransaction || !!step.evmApprovalTransaction;
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
/*
|
|
1426
|
+
*Create transaction endpoint doesn't return error code on http status code,
|
|
1427
|
+
*For backward compatibilty with server and sdk, we use this wrapper to reject the promise.
|
|
1428
|
+
*/
|
|
1429
|
+
export async function throwOnOK(
|
|
1430
|
+
rawResponse: Promise<CreateTransactionResponse>
|
|
1431
|
+
): Promise<CreateTransactionResponse> {
|
|
1432
|
+
const responseBody = await rawResponse;
|
|
1433
|
+
if (!responseBody.ok || !responseBody.transaction) {
|
|
1434
|
+
throw PrettyError.CreateTransaction(
|
|
1435
|
+
responseBody.error || 'bad response from create tx endpoint'
|
|
1436
|
+
);
|
|
1437
|
+
}
|
|
1438
|
+
return responseBody;
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
export function cancelSwap(
|
|
1442
|
+
swap: QueueInfo,
|
|
1443
|
+
manager?: Manager
|
|
1444
|
+
): {
|
|
1445
|
+
swap: PendingSwap;
|
|
1446
|
+
step: PendingSwapStep | null;
|
|
1447
|
+
} {
|
|
1448
|
+
const { reset } = claimQueue();
|
|
1449
|
+
swap.actions.cancel();
|
|
1450
|
+
|
|
1451
|
+
const updateResult = updateSwapStatus({
|
|
1452
|
+
getStorage: swap.actions.getStorage,
|
|
1453
|
+
setStorage: swap.actions.setStorage,
|
|
1454
|
+
message: 'Swap canceled by user.',
|
|
1455
|
+
details:
|
|
1456
|
+
"Warning: If you've already signed and sent a transaction, it won't be affected, but next swap steps will not be executed.",
|
|
1457
|
+
nextStatus: 'failed',
|
|
1458
|
+
nextStepStatus: 'failed',
|
|
1459
|
+
errorCode: 'USER_CANCEL',
|
|
1460
|
+
});
|
|
1461
|
+
|
|
1462
|
+
notifier({
|
|
1463
|
+
event: {
|
|
1464
|
+
type: StepEventType.FAILED,
|
|
1465
|
+
reasonCode: 'USER_CANCEL',
|
|
1466
|
+
reason: updateResult.swap.extraMessage ?? undefined,
|
|
1467
|
+
inputAmount: getLastFinishedStepInput(updateResult.swap),
|
|
1468
|
+
inputAmountUsd: getLastFinishedStepInputUsd(updateResult.swap),
|
|
1469
|
+
},
|
|
1470
|
+
|
|
1471
|
+
swap: updateResult.swap,
|
|
1472
|
+
step: updateResult.step,
|
|
1473
|
+
});
|
|
1474
|
+
|
|
1475
|
+
reset();
|
|
1476
|
+
if (manager) {
|
|
1477
|
+
manager?.retry();
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
return updateResult;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
export function getLastSuccessfulStep<T extends { status: StepStatus }[]>(
|
|
1484
|
+
steps: T
|
|
1485
|
+
): ArrayElement<T> | undefined {
|
|
1486
|
+
return steps
|
|
1487
|
+
.slice()
|
|
1488
|
+
.reverse()
|
|
1489
|
+
.find((step) => step.status === 'success') as ArrayElement<T> | undefined;
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
export function getFailedStep<T extends { status: StepStatus }[]>(
|
|
1493
|
+
steps: T
|
|
1494
|
+
): ArrayElement<T> | undefined {
|
|
1495
|
+
return steps
|
|
1496
|
+
.slice()
|
|
1497
|
+
.reverse()
|
|
1498
|
+
.find((step) => step.status === 'failed') as ArrayElement<T> | undefined;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
export function isApprovalTX(step: Step): boolean {
|
|
1502
|
+
const { transaction } = step;
|
|
1503
|
+
const approvalTx =
|
|
1504
|
+
(transaction?.type === TransactionType.EVM && transaction.isApprovalTx) ||
|
|
1505
|
+
(transaction?.type === TransactionType.STARKNET &&
|
|
1506
|
+
transaction.isApprovalTx) ||
|
|
1507
|
+
(transaction?.type === TransactionType.TRON && transaction.isApprovalTx);
|
|
1508
|
+
|
|
1509
|
+
return approvalTx;
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
export function getTokenAmountInUsd(
|
|
1513
|
+
amount: string | number,
|
|
1514
|
+
usdPrice: string | number
|
|
1515
|
+
): string {
|
|
1516
|
+
const usdValue = new BigNumber(amount).multipliedBy(usdPrice);
|
|
1517
|
+
if (isNaN(usdValue.toNumber())) {
|
|
1518
|
+
return '';
|
|
1519
|
+
}
|
|
1520
|
+
return usdValue.toString();
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
export function getSwapInputUsd(swap: PendingSwap): string {
|
|
1524
|
+
return getTokenAmountInUsd(
|
|
1525
|
+
swap.inputAmount,
|
|
1526
|
+
swap.steps[0].fromUsdPrice ?? ''
|
|
1527
|
+
);
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
export function getSwapOutputUsd(swap: PendingSwap): string {
|
|
1531
|
+
const lastStep = swap.steps[swap.steps.length - 1];
|
|
1532
|
+
|
|
1533
|
+
return getTokenAmountInUsd(
|
|
1534
|
+
lastStep.outputAmount ?? '',
|
|
1535
|
+
lastStep.toUsdPrice ?? ''
|
|
1536
|
+
);
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
export function getLastFinishedStep(
|
|
1540
|
+
swap: PendingSwap
|
|
1541
|
+
): { step: PendingSwapStep; index: number } | undefined {
|
|
1542
|
+
const FINISHED_STATUS: PendingSwap['steps'][number]['status'][] = [
|
|
1543
|
+
'success',
|
|
1544
|
+
'failed',
|
|
1545
|
+
];
|
|
1546
|
+
|
|
1547
|
+
const lastFinishedStepIndex = swap.steps.findLastIndex((step) =>
|
|
1548
|
+
FINISHED_STATUS.includes(step.status)
|
|
1549
|
+
);
|
|
1550
|
+
|
|
1551
|
+
return lastFinishedStepIndex < 0
|
|
1552
|
+
? undefined
|
|
1553
|
+
: { step: swap.steps[lastFinishedStepIndex], index: lastFinishedStepIndex };
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
export function getLastFinishedStepInput(swap: PendingSwap): string {
|
|
1557
|
+
const lastFinishedStep = getLastFinishedStep(swap);
|
|
1558
|
+
|
|
1559
|
+
if (!lastFinishedStep) {
|
|
1560
|
+
return '';
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
return lastFinishedStep.index === 0
|
|
1564
|
+
? swap.inputAmount
|
|
1565
|
+
: swap.steps[lastFinishedStep.index - 1].outputAmount ?? '';
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
export function getLastFinishedStepInputUsd(swap: PendingSwap): string {
|
|
1569
|
+
const lastSuccessfulStep = getLastFinishedStep(swap);
|
|
1570
|
+
|
|
1571
|
+
return getTokenAmountInUsd(
|
|
1572
|
+
getLastFinishedStepInput(swap),
|
|
1573
|
+
lastSuccessfulStep?.step?.fromUsdPrice ?? ''
|
|
1574
|
+
);
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
export function getLastSuccessfulStepOutputUsd(swap: PendingSwap): string {
|
|
1578
|
+
const lastSuccessfulStep = getLastSuccessfulStep(swap.steps);
|
|
1579
|
+
|
|
1580
|
+
return getTokenAmountInUsd(
|
|
1581
|
+
lastSuccessfulStep?.outputAmount ?? '',
|
|
1582
|
+
lastSuccessfulStep?.toUsdPrice ?? ''
|
|
1583
|
+
);
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
export function createStepFailedEvent(
|
|
1587
|
+
swap: PendingSwap,
|
|
1588
|
+
message: string,
|
|
1589
|
+
reasonCode?: APIErrorCode
|
|
1590
|
+
): NotifierParams['event'] & { type: StepEventType.FAILED } {
|
|
1591
|
+
return {
|
|
1592
|
+
type: StepEventType.FAILED,
|
|
1593
|
+
reason: message,
|
|
1594
|
+
reasonCode: reasonCode ?? DEFAULT_ERROR_CODE,
|
|
1595
|
+
inputAmount: getLastFinishedStepInput(swap),
|
|
1596
|
+
inputAmountUsd: getLastFinishedStepInputUsd(swap),
|
|
1597
|
+
};
|
|
1598
|
+
}
|