@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.
Files changed (75) hide show
  1. package/CHANGELOG.md +353 -0
  2. package/dist/actions/checkStatus.d.ts +13 -0
  3. package/dist/actions/checkStatus.d.ts.map +1 -0
  4. package/dist/actions/common/checkEnvironmentBeforeExecuteTransaction.d.ts +8 -0
  5. package/dist/actions/common/checkEnvironmentBeforeExecuteTransaction.d.ts.map +1 -0
  6. package/dist/actions/common/produceNextStateForTransaction.d.ts +17 -0
  7. package/dist/actions/common/produceNextStateForTransaction.d.ts.map +1 -0
  8. package/dist/actions/common/utils.d.ts +5 -0
  9. package/dist/actions/common/utils.d.ts.map +1 -0
  10. package/dist/actions/createTransaction.d.ts +12 -0
  11. package/dist/actions/createTransaction.d.ts.map +1 -0
  12. package/dist/actions/executeTransaction/executeTransaction.d.ts +14 -0
  13. package/dist/actions/executeTransaction/executeTransaction.d.ts.map +1 -0
  14. package/dist/actions/executeTransaction/index.d.ts +2 -0
  15. package/dist/actions/executeTransaction/index.d.ts.map +1 -0
  16. package/dist/actions/scheduleNextStep.d.ts +14 -0
  17. package/dist/actions/scheduleNextStep.d.ts.map +1 -0
  18. package/dist/actions/start.d.ts +4 -0
  19. package/dist/actions/start.d.ts.map +1 -0
  20. package/dist/configs.d.ts +14 -0
  21. package/dist/configs.d.ts.map +1 -0
  22. package/dist/constants.d.ts +7 -0
  23. package/dist/constants.d.ts.map +1 -0
  24. package/dist/helpers.d.ts +255 -0
  25. package/dist/helpers.d.ts.map +1 -0
  26. package/dist/hooks.d.ts +19 -0
  27. package/dist/hooks.d.ts.map +1 -0
  28. package/dist/index.d.ts +11 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +2 -0
  31. package/dist/index.js.map +7 -0
  32. package/dist/migration.d.ts +15 -0
  33. package/dist/migration.d.ts.map +1 -0
  34. package/dist/numbers.d.ts +3 -0
  35. package/dist/numbers.d.ts.map +1 -0
  36. package/dist/queue-manager-rango-preset.build.json +1 -0
  37. package/dist/queueDef.d.ts +10 -0
  38. package/dist/queueDef.d.ts.map +1 -0
  39. package/dist/services/eventEmitter.d.ts +10 -0
  40. package/dist/services/eventEmitter.d.ts.map +1 -0
  41. package/dist/services/httpService.d.ts +3 -0
  42. package/dist/services/httpService.d.ts.map +1 -0
  43. package/dist/services/index.d.ts +2 -0
  44. package/dist/services/index.d.ts.map +1 -0
  45. package/dist/shared-errors.d.ts +25 -0
  46. package/dist/shared-errors.d.ts.map +1 -0
  47. package/dist/shared.d.ts +81 -0
  48. package/dist/shared.d.ts.map +1 -0
  49. package/dist/types.d.ts +172 -0
  50. package/dist/types.d.ts.map +1 -0
  51. package/package.json +45 -0
  52. package/readme.md +2 -0
  53. package/src/actions/checkStatus.ts +564 -0
  54. package/src/actions/common/checkEnvironmentBeforeExecuteTransaction.ts +157 -0
  55. package/src/actions/common/produceNextStateForTransaction.ts +167 -0
  56. package/src/actions/common/utils.ts +25 -0
  57. package/src/actions/createTransaction.ts +117 -0
  58. package/src/actions/executeTransaction/executeTransaction.ts +107 -0
  59. package/src/actions/executeTransaction/index.ts +1 -0
  60. package/src/actions/scheduleNextStep.ts +104 -0
  61. package/src/actions/start.ts +16 -0
  62. package/src/configs.ts +38 -0
  63. package/src/constants.ts +18 -0
  64. package/src/helpers.ts +1598 -0
  65. package/src/hooks.ts +94 -0
  66. package/src/index.ts +68 -0
  67. package/src/migration.ts +124 -0
  68. package/src/numbers.ts +68 -0
  69. package/src/queueDef.ts +39 -0
  70. package/src/services/eventEmitter.ts +290 -0
  71. package/src/services/httpService.ts +10 -0
  72. package/src/services/index.ts +1 -0
  73. package/src/shared-errors.ts +165 -0
  74. package/src/shared.ts +473 -0
  75. package/src/types.ts +305 -0
@@ -0,0 +1,564 @@
1
+ import type { SwapQueueContext, SwapStorage } from '../types';
2
+ import type { ExecuterActions } from '@rango-dev/queue-manager-core';
3
+ import type {
4
+ CheckTxStatusRequest,
5
+ Transaction,
6
+ TransactionStatusResponse,
7
+ } from 'rango-sdk';
8
+
9
+ import { warn } from '@rango-dev/logging-core';
10
+ import { type GenericSigner, SignerError } from 'rango-types';
11
+
12
+ import {
13
+ createStepFailedEvent,
14
+ delay,
15
+ getCurrentStep,
16
+ getCurrentStepTx,
17
+ getCurrentStepTxType,
18
+ getLastFinishedStepInput,
19
+ getLastFinishedStepInputUsd,
20
+ getLastSuccessfulStepOutputUsd,
21
+ inMemoryTransactionsData,
22
+ resetNetworkStatus,
23
+ setCurrentStepTx,
24
+ updateSwapStatus,
25
+ } from '../helpers';
26
+ import { httpService } from '../services';
27
+ import { notifier } from '../services/eventEmitter';
28
+ import {
29
+ getCurrentNamespaceOf,
30
+ getNextStep,
31
+ getRelatedWallet,
32
+ getScannerUrl,
33
+ MessageSeverity,
34
+ } from '../shared';
35
+ import { prettifyErrorMessage } from '../shared-errors';
36
+ import { StepEventType, SwapActionTypes } from '../types';
37
+
38
+ const INTERVAL_FOR_CHECK_STATUS = 5_000;
39
+ const INTERVAL_FOR_CHECK_APPROVAL = 5_000;
40
+
41
+ /**
42
+ * Subscribe to status of swap transaction by checking from server periodically.
43
+ * After getting the status, notify the user and schedule `SCHEDULE_NEXT_STEP`.
44
+ */
45
+ async function checkTransactionStatus({
46
+ getStorage,
47
+ setStorage,
48
+ next,
49
+ schedule,
50
+ retry,
51
+ failed,
52
+ context,
53
+ }: ExecuterActions<
54
+ SwapStorage,
55
+ SwapActionTypes,
56
+ SwapQueueContext
57
+ >): Promise<void> {
58
+ const swap = getStorage().swapDetails;
59
+ const { meta } = context;
60
+ const checkStatusError = new Error('check status Error');
61
+
62
+ const currentStep = getCurrentStep(swap)!;
63
+
64
+ if (!currentStep?.executedTransactionId) {
65
+ return;
66
+ }
67
+ const tx = getCurrentStepTx(currentStep);
68
+ let txId = currentStep.executedTransactionId;
69
+ let getTxReceiptFailed = false;
70
+ let status: TransactionStatusResponse | null = null;
71
+ let signer: GenericSigner<Transaction> | null = null;
72
+ const { getTransactionDataByHash, setTransactionDataByHash } =
73
+ inMemoryTransactionsData();
74
+
75
+ try {
76
+ const txType = getCurrentStepTxType(currentStep);
77
+ const sourceWallet = getRelatedWallet(swap, currentStep);
78
+ if (txType && sourceWallet) {
79
+ signer = (await context.getSigners(sourceWallet.walletType)).getSigner(
80
+ txType
81
+ );
82
+ }
83
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
84
+ } catch (error) {
85
+ /*
86
+ * wallet is not connected yet
87
+ * no need to do anything
88
+ */
89
+ }
90
+
91
+ try {
92
+ // if wallet is connected, try to get transaction reciept
93
+ const { response: txResponse, receiptReceived } =
94
+ getTransactionDataByHash(txId);
95
+ if (signer?.wait && !receiptReceived) {
96
+ const chainId =
97
+ (tx?.blockChain && meta.blockchains?.[tx?.blockChain]?.chainId) ||
98
+ undefined;
99
+ const { hash: updatedTxHash, response: updatedTxResponse } =
100
+ await signer.wait(txId, chainId, txResponse);
101
+ if (updatedTxHash !== txId) {
102
+ currentStep.executedTransactionId =
103
+ updatedTxHash || currentStep.executedTransactionId;
104
+ const currentStepNamespace = getCurrentNamespaceOf(swap, currentStep);
105
+ let explorerUrl: string | undefined;
106
+ const blockchainsMetaNotEmpty = !!Object.keys(meta.blockchains).length;
107
+ if (blockchainsMetaNotEmpty) {
108
+ explorerUrl = getScannerUrl(
109
+ currentStep.executedTransactionId,
110
+ currentStepNamespace.network,
111
+ meta.blockchains
112
+ );
113
+ }
114
+ if (explorerUrl) {
115
+ if (currentStep.explorerUrl && currentStep.explorerUrl?.length >= 1) {
116
+ currentStep.explorerUrl[currentStep.explorerUrl.length - 1] = {
117
+ url: explorerUrl,
118
+ description: 'Replaced Swap',
119
+ };
120
+ }
121
+ }
122
+ txId = currentStep.executedTransactionId;
123
+ if (updatedTxHash && updatedTxResponse) {
124
+ setTransactionDataByHash(updatedTxHash, {
125
+ response: updatedTxResponse,
126
+ });
127
+ }
128
+ } else {
129
+ setTransactionDataByHash(updatedTxHash, {
130
+ receiptReceived: true,
131
+ });
132
+ }
133
+ }
134
+ } catch (error) {
135
+ const { extraMessage, extraMessageDetail, extraMessageErrorCode } =
136
+ prettifyErrorMessage(error);
137
+ const updateResult = updateSwapStatus({
138
+ getStorage,
139
+ setStorage,
140
+ nextStatus: 'failed',
141
+ nextStepStatus: 'failed',
142
+ message: extraMessage,
143
+ details: extraMessageDetail,
144
+ errorCode: extraMessageErrorCode,
145
+ });
146
+
147
+ const event = createStepFailedEvent(
148
+ swap,
149
+ extraMessage,
150
+ updateResult.failureType
151
+ );
152
+
153
+ warn(checkStatusError, {
154
+ tags: {
155
+ type: 'singer-error',
156
+ reason: event.reason,
157
+ reasonCode: event.reasonCode,
158
+ message: extraMessage,
159
+ messageDetail: extraMessageDetail,
160
+ pendingSwap: swap,
161
+ },
162
+ context: SignerError.isSignerError(error) ? error.getErrorContext() : {},
163
+ });
164
+
165
+ getTxReceiptFailed = true;
166
+ /*
167
+ * We shouldn't return here, because we need to trigger check status job in backend.
168
+ * This is not a ui requirement but the backend one.
169
+ */
170
+ }
171
+
172
+ const requestBody: CheckTxStatusRequest = {
173
+ requestId: swap.requestId,
174
+ txId,
175
+ step: currentStep.id,
176
+ };
177
+
178
+ try {
179
+ status = await httpService().checkStatus(requestBody);
180
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
181
+ } catch (e) {
182
+ await delay(INTERVAL_FOR_CHECK_STATUS);
183
+ retry();
184
+ warn(checkStatusError, {
185
+ tags: { type: 'request-error', requestBody, pendingSwap: swap },
186
+ });
187
+ return;
188
+ }
189
+
190
+ /*
191
+ * If user cancel swap during check status api call,
192
+ * or getting transaction receipt failed,
193
+ * we should ignore check status response and return
194
+ */
195
+ if (getTxReceiptFailed) {
196
+ return failed();
197
+ }
198
+ if (currentStep?.status === 'failed') {
199
+ return;
200
+ }
201
+
202
+ const outputAmount: string | null =
203
+ status?.outputAmount ||
204
+ (currentStep.outputAmount ? currentStep.outputAmount : null);
205
+ const prevOutputAmount = currentStep.outputAmount || null;
206
+ swap.extraMessage = status?.extraMessage || swap.extraMessage;
207
+ swap.extraMessageSeverity = MessageSeverity.info;
208
+ swap.extraMessageDetail = '';
209
+
210
+ currentStep.status = status?.status || currentStep.status;
211
+ currentStep.diagnosisUrl =
212
+ status?.diagnosisUrl || currentStep.diagnosisUrl || null;
213
+ currentStep.outputAmount = outputAmount || currentStep.outputAmount;
214
+ currentStep.explorerUrl = status?.explorerUrl || currentStep.explorerUrl;
215
+ currentStep.internalSteps = status?.steps || null;
216
+
217
+ const newTransaction = status?.newTx;
218
+
219
+ if (newTransaction) {
220
+ currentStep.status = 'created';
221
+ currentStep.executedTransactionId = null;
222
+ currentStep.executedTransactionTime = null;
223
+ setCurrentStepTx(currentStep, newTransaction);
224
+ }
225
+
226
+ if (prevOutputAmount === null && outputAmount !== null) {
227
+ notifier({
228
+ event: { type: StepEventType.OUTPUT_REVEALED, outputAmount },
229
+ swap: swap,
230
+ step: currentStep,
231
+ });
232
+ } else if (prevOutputAmount === null && outputAmount === null) {
233
+ // it is needed to set notification after reloading the page
234
+ notifier({
235
+ event: { type: StepEventType.CHECK_STATUS },
236
+ swap: swap,
237
+ step: currentStep,
238
+ });
239
+ }
240
+
241
+ if (currentStep.status === 'success') {
242
+ const nextStep = getNextStep(swap, currentStep);
243
+ swap.extraMessageDetail = '';
244
+ swap.extraMessage = nextStep
245
+ ? `starting next step: ${nextStep.swapperId}: ${nextStep.fromBlockchain} -> ${nextStep.toBlockchain}`
246
+ : '';
247
+ notifier({
248
+ event: {
249
+ type: StepEventType.SUCCEEDED,
250
+ inputAmount: getLastFinishedStepInput(swap),
251
+ inputAmountUsd: getLastFinishedStepInputUsd(swap),
252
+ outputAmount: currentStep.outputAmount ?? '',
253
+ outputAmountUsd: getLastSuccessfulStepOutputUsd(swap),
254
+ },
255
+ swap,
256
+ step: currentStep,
257
+ });
258
+ } else if (currentStep.status === 'failed') {
259
+ swap.extraMessage = 'Transaction failed in blockchain';
260
+ swap.extraMessageSeverity = MessageSeverity.error;
261
+ swap.extraMessageDetail = status?.extraMessage || '';
262
+ swap.status = 'failed';
263
+ swap.finishTime = new Date().getTime().toString();
264
+ warn(checkStatusError, {
265
+ tags: {
266
+ type: 'transaction-failed',
267
+ message: swap.extraMessage,
268
+ messageDetails: swap.extraMessageDetail,
269
+ pendingSwap: swap,
270
+ },
271
+ });
272
+ }
273
+
274
+ // Sync data with storage
275
+ setStorage({ ...getStorage(), swapDetails: swap });
276
+
277
+ if (status?.status === 'failed') {
278
+ failed();
279
+ } else if (
280
+ status?.status === 'success' ||
281
+ (status?.status === 'running' && !!status.newTx)
282
+ ) {
283
+ schedule(SwapActionTypes.SCHEDULE_NEXT_STEP);
284
+ next();
285
+ } else {
286
+ await delay(INTERVAL_FOR_CHECK_STATUS);
287
+ retry();
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Subscribe to status of approval transaction by checking from server periodically.
293
+ * After getting the status, notify the user and schedule `SCHEDULE_NEXT_STEP`.
294
+ */
295
+ async function checkApprovalStatus({
296
+ getStorage,
297
+ setStorage,
298
+ next,
299
+ schedule,
300
+ retry,
301
+ failed,
302
+ context,
303
+ }: ExecuterActions<
304
+ SwapStorage,
305
+ SwapActionTypes,
306
+ SwapQueueContext
307
+ >): Promise<void> {
308
+ const swap = getStorage().swapDetails;
309
+ const { meta } = context;
310
+ const { getTransactionDataByHash, setTransactionDataByHash } =
311
+ inMemoryTransactionsData();
312
+ const checkApprovalStatusError = new Error('check approval status error');
313
+
314
+ const currentStep = getCurrentStep(swap);
315
+ if (!currentStep) {
316
+ console.log('ignore check status, current step is null');
317
+ return;
318
+ }
319
+ const tx = getCurrentStepTx(currentStep);
320
+
321
+ if (!currentStep?.executedTransactionId) {
322
+ return;
323
+ }
324
+ let txId = currentStep.executedTransactionId;
325
+
326
+ let signer: GenericSigner<Transaction> | null = null;
327
+ try {
328
+ const txType = getCurrentStepTxType(currentStep);
329
+ const sourceWallet = getRelatedWallet(swap, currentStep);
330
+ if (txType && sourceWallet) {
331
+ const walletSigners = await context.getSigners(sourceWallet.walletType);
332
+ signer = walletSigners.getSigner(txType);
333
+ }
334
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
335
+ } catch (error) {
336
+ /*
337
+ * wallet is not connected yet
338
+ * no need to do anything
339
+ */
340
+ }
341
+
342
+ try {
343
+ const { response: txResponse, receiptReceived } =
344
+ getTransactionDataByHash(txId);
345
+ // if wallet is connected, try to get transaction reciept
346
+ if (signer?.wait && !receiptReceived) {
347
+ const chainId =
348
+ (tx?.blockChain && meta.blockchains?.[tx?.blockChain]?.chainId) ||
349
+ undefined;
350
+ const { hash: updatedTxHash, response: updatedTxResponse } =
351
+ await signer.wait(txId, chainId, txResponse);
352
+ if (updatedTxHash !== txId) {
353
+ currentStep.executedTransactionId =
354
+ updatedTxHash || currentStep.executedTransactionId;
355
+ const currentStepNamespace = getCurrentNamespaceOf(swap, currentStep);
356
+ let explorerUrl: string | undefined;
357
+ const blockchainsMetaNotEmpty = !!Object.keys(meta.blockchains).length;
358
+ if (blockchainsMetaNotEmpty) {
359
+ explorerUrl = getScannerUrl(
360
+ currentStep.executedTransactionId,
361
+ currentStepNamespace.network,
362
+ meta.blockchains
363
+ );
364
+ }
365
+ if (explorerUrl) {
366
+ if (currentStep.explorerUrl && currentStep.explorerUrl?.length >= 1) {
367
+ currentStep.explorerUrl[currentStep.explorerUrl.length - 1] = {
368
+ url: explorerUrl,
369
+ description: 'Replaced Approve',
370
+ };
371
+ }
372
+ }
373
+ txId = currentStep.executedTransactionId;
374
+ if (updatedTxHash && updatedTxResponse) {
375
+ setTransactionDataByHash(updatedTxHash, {
376
+ response: updatedTxResponse,
377
+ });
378
+ }
379
+ } else {
380
+ setTransactionDataByHash(updatedTxHash, {
381
+ receiptReceived: true,
382
+ });
383
+ }
384
+ }
385
+ } catch (error) {
386
+ const { extraMessage, extraMessageDetail, extraMessageErrorCode } =
387
+ prettifyErrorMessage(error);
388
+ const updateResult = updateSwapStatus({
389
+ getStorage,
390
+ setStorage,
391
+ nextStatus: 'failed',
392
+ nextStepStatus: 'failed',
393
+ message: extraMessage,
394
+ details: extraMessageDetail,
395
+ errorCode: extraMessageErrorCode,
396
+ });
397
+
398
+ const event = createStepFailedEvent(
399
+ swap,
400
+ extraMessage,
401
+ updateResult.failureType
402
+ );
403
+
404
+ notifier({
405
+ event,
406
+ ...updateResult,
407
+ });
408
+ warn(checkApprovalStatusError, {
409
+ tags: {
410
+ type: 'singer-error',
411
+ reason: event.reason,
412
+ reasonCode: event.reasonCode,
413
+ message: extraMessage,
414
+ messageDetail: extraMessageDetail,
415
+ pendingSwap: swap,
416
+ },
417
+ context: SignerError.isSignerError(error) ? error.getErrorContext() : {},
418
+ });
419
+ return failed();
420
+ }
421
+
422
+ let isApproved = false;
423
+ const request: { requestId: string; txId?: string } = {
424
+ requestId: swap.requestId,
425
+ txId: currentStep.executedTransactionId,
426
+ };
427
+
428
+ try {
429
+ const response = await httpService().checkApproval(
430
+ request.requestId,
431
+ request.txId
432
+ );
433
+ // If user cancel swap during check status api call, we should ignore check approval response
434
+ if (currentStep?.status === 'failed') {
435
+ return;
436
+ }
437
+
438
+ isApproved = response.isApproved;
439
+ if (
440
+ !isApproved &&
441
+ (response.txStatus === 'failed' || response.txStatus === 'success')
442
+ ) {
443
+ let message, details;
444
+ if (response.txStatus === 'failed') {
445
+ message = 'Approve transaction failed';
446
+ details = 'Smart contract approval tx failed in blockchain.';
447
+ } else {
448
+ message = 'Not enough approval';
449
+ if (response.requiredApprovedAmount && response.currentApprovedAmount) {
450
+ details = `Required approval: ${response.requiredApprovedAmount}, current approval: ${response.currentApprovedAmount}`;
451
+ } else {
452
+ details = `You still don't have enough approval for this swap.`;
453
+ }
454
+ }
455
+ /*
456
+ * approve transaction failed on
457
+ * we should fail the whole swap
458
+ */
459
+ const updateResult = updateSwapStatus({
460
+ getStorage,
461
+ setStorage,
462
+ nextStatus: 'failed',
463
+ nextStepStatus: 'failed',
464
+ errorCode: 'INSUFFICIENT_APPROVE',
465
+ message: message,
466
+ details: details,
467
+ });
468
+
469
+ const event = createStepFailedEvent(
470
+ swap,
471
+ message,
472
+ updateResult.failureType
473
+ );
474
+
475
+ notifier({
476
+ event,
477
+ ...updateResult,
478
+ });
479
+
480
+ warn(checkApprovalStatusError, {
481
+ tags: {
482
+ type: 'approval-failed',
483
+ message: swap.extraMessage,
484
+ messageDetails: swap.extraMessageDetail,
485
+ pendingSwap: swap,
486
+ },
487
+ });
488
+
489
+ failed();
490
+ } else if (!isApproved) {
491
+ // it is needed to set notification after reloading the page
492
+ notifier({
493
+ event: { type: StepEventType.CHECK_STATUS },
494
+ swap,
495
+ step: currentStep,
496
+ });
497
+ }
498
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
499
+ } catch (e) {
500
+ isApproved = false;
501
+ warn(checkApprovalStatusError, {
502
+ tags: { type: 'request-error', requestBody: request, pendingSwap: swap },
503
+ });
504
+ }
505
+ if (isApproved) {
506
+ currentStep.status = 'approved';
507
+ swap.extraMessage = `Spending ${currentStep.fromSymbol} approved successfully.`;
508
+ swap.extraMessageDetail = null;
509
+ swap.extraMessageSeverity = MessageSeverity.success;
510
+ currentStep.evmApprovalTransaction = null;
511
+ currentStep.executedTransactionId = null;
512
+ currentStep.executedTransactionTime = null;
513
+ currentStep.starknetApprovalTransaction = null;
514
+ currentStep.tronApprovalTransaction = null;
515
+
516
+ setStorage({
517
+ ...getStorage(),
518
+ swapDetails: swap,
519
+ });
520
+
521
+ notifier({
522
+ event: { type: StepEventType.APPROVAL_TX_SUCCEEDED },
523
+ swap: swap,
524
+ step: currentStep,
525
+ });
526
+
527
+ schedule(SwapActionTypes.SCHEDULE_NEXT_STEP);
528
+ next();
529
+ } else {
530
+ await delay(INTERVAL_FOR_CHECK_APPROVAL);
531
+ retry();
532
+ }
533
+ }
534
+
535
+ /**
536
+ *
537
+ * For doing a swap the user needs to accept a `contract` so it can use the user balance.
538
+ * There is two types of check status:
539
+ * 1. Checking approval transaction (Give permission to a contract)
540
+ * 2. Checking swap transaction.
541
+ *
542
+ */
543
+ export async function checkStatus(
544
+ actions: ExecuterActions<SwapStorage, SwapActionTypes, SwapQueueContext>
545
+ ): Promise<void> {
546
+ const swap = actions.getStorage().swapDetails;
547
+ const currentStep = getCurrentStep(swap);
548
+ if (!currentStep) {
549
+ console.log('ignore check status, current step is null', swap.requestId);
550
+ return;
551
+ }
552
+
553
+ /*
554
+ * Reset network status
555
+ * Because when check status is on `loading` or `failed` status, it shows previous message that isn't related to current state.
556
+ */
557
+ resetNetworkStatus(actions);
558
+
559
+ if (currentStep.status === 'running') {
560
+ await checkTransactionStatus(actions);
561
+ } else if (currentStep.status === 'waitingForApproval') {
562
+ await checkApprovalStatus(actions);
563
+ }
564
+ }
@@ -0,0 +1,157 @@
1
+ import type {
2
+ SwapActionTypes,
3
+ SwapQueueContext,
4
+ SwapStorage,
5
+ } from '../../types';
6
+ import type {
7
+ BlockedReason,
8
+ ExecuterActions,
9
+ } from '@rango-dev/queue-manager-core';
10
+
11
+ import { PendingSwapNetworkStatus } from 'rango-types';
12
+ import { Err, Ok, type Result } from 'ts-results';
13
+
14
+ import {
15
+ ERROR_MESSAGE_DEPENDS_ON_OTHER_QUEUES,
16
+ ERROR_MESSAGE_WAIT_FOR_CHANGE_NETWORK,
17
+ ERROR_MESSAGE_WAIT_FOR_WALLET_DESCRIPTION,
18
+ ERROR_MESSAGE_WAIT_FOR_WALLET_DESCRIPTION_WRONG_WALLET,
19
+ } from '../../constants';
20
+ import {
21
+ claimQueue,
22
+ getCurrentStep,
23
+ getRequiredWallet,
24
+ isNeedBlockQueueForParallel,
25
+ isNetworkMatchedForTransaction,
26
+ isRequiredWalletConnected,
27
+ isWalletNull,
28
+ updateNetworkStatus,
29
+ } from '../../helpers';
30
+ import { getCurrentNamespaceOf } from '../../shared';
31
+ import { BlockReason } from '../../types';
32
+
33
+ import { isClaimedByCurrentQueue } from './utils';
34
+
35
+ /**
36
+ * Check for network & address be matched and queue to not be blocked and update the swap accordingly.
37
+ */
38
+ export async function checkEnvironmentBeforeExecuteTransaction(
39
+ actions: ExecuterActions<SwapStorage, SwapActionTypes, SwapQueueContext>
40
+ ): Promise<Result<true, BlockedReason>> {
41
+ const { getStorage, context } = actions;
42
+
43
+ const swap = getStorage().swapDetails;
44
+
45
+ const currentStep = getCurrentStep(swap)!;
46
+
47
+ /* Make sure wallet is connected and also the connected wallet is matched with tx by checking address. */
48
+ const addressCheckResult = ensureRequiredWalletIsConnected(actions);
49
+ if (addressCheckResult.err) {
50
+ return addressCheckResult;
51
+ }
52
+
53
+ /* Wallet should be on correct network */
54
+ const networkResult = await ensureWalletIsOnCorrectNetwork(actions);
55
+ if (networkResult.err) {
56
+ return networkResult;
57
+ }
58
+
59
+ if (currentStep.networkStatus === PendingSwapNetworkStatus.NetworkChanged) {
60
+ // Considering that network is matched now, if currently network status of the current step is equal to `NetworkChanged`, we need to update the network status to mark it as network changed successfully.
61
+ updateNetworkStatus(actions, {
62
+ message: '',
63
+ details: 'The network has been successfully changed.',
64
+ status: PendingSwapNetworkStatus.NetworkChanged,
65
+ });
66
+ }
67
+
68
+ /*
69
+ *For avoiding conflict by making too many requests to wallet, we need to make sure
70
+ *We only run one request at a time (In parallel mode).
71
+ */
72
+ const needsToBlockQueue = isNeedBlockQueueForParallel(currentStep);
73
+ const isClaimed = isClaimedByCurrentQueue(context);
74
+ if (needsToBlockQueue && !isClaimed) {
75
+ const blockedFor = {
76
+ reason: BlockReason.DEPENDS_ON_OTHER_QUEUES,
77
+ description: ERROR_MESSAGE_DEPENDS_ON_OTHER_QUEUES,
78
+ details: {},
79
+ };
80
+ return new Err(blockedFor);
81
+ }
82
+
83
+ return new Ok(true);
84
+ }
85
+
86
+ function ensureRequiredWalletIsConnected(
87
+ actions: ExecuterActions<SwapStorage, SwapActionTypes, SwapQueueContext>
88
+ ): Result<true, BlockedReason> {
89
+ const { getStorage, context } = actions;
90
+ const { wallets } = context;
91
+ const swap = getStorage().swapDetails;
92
+
93
+ const isWrongAddress = !isRequiredWalletConnected(swap, context.state).ok;
94
+ if (isWrongAddress) {
95
+ const { type, address } = getRequiredWallet(swap);
96
+ const isWalletInCompatible = wallets?.blockchains?.find(
97
+ (w) => !w.accounts?.find((account) => account.walletType === type)
98
+ );
99
+ const description =
100
+ isWalletNull(wallets) || isWalletInCompatible
101
+ ? ERROR_MESSAGE_WAIT_FOR_WALLET_DESCRIPTION(type)
102
+ : ERROR_MESSAGE_WAIT_FOR_WALLET_DESCRIPTION_WRONG_WALLET(type, address);
103
+
104
+ const blockedFor = {
105
+ reason: BlockReason.WAIT_FOR_CONNECT_WALLET,
106
+ description,
107
+ };
108
+ return new Err(blockedFor);
109
+ }
110
+
111
+ return new Ok(true);
112
+ }
113
+
114
+ async function ensureWalletIsOnCorrectNetwork(
115
+ actions: ExecuterActions<SwapStorage, SwapActionTypes, SwapQueueContext>
116
+ ): Promise<Result<true, BlockedReason>> {
117
+ const { getStorage, context } = actions;
118
+ const { meta, wallets, providers, hubProvider } = context;
119
+ const swap = getStorage().swapDetails;
120
+ const currentStep = getCurrentStep(swap)!;
121
+
122
+ const networkMatched = await isNetworkMatchedForTransaction(
123
+ swap,
124
+ currentStep,
125
+ wallets,
126
+ meta,
127
+ providers,
128
+ hubProvider
129
+ );
130
+
131
+ const { claimedBy } = claimQueue();
132
+ const claimerId = claimedBy();
133
+ const isClaimedByAnyQueue = !!claimerId && !isClaimedByCurrentQueue(context);
134
+
135
+ if (isClaimedByAnyQueue && !networkMatched) {
136
+ const details = ERROR_MESSAGE_DEPENDS_ON_OTHER_QUEUES;
137
+
138
+ const blockedFor = {
139
+ reason: BlockReason.DEPENDS_ON_OTHER_QUEUES,
140
+ details: details,
141
+ };
142
+ return new Err(blockedFor);
143
+ } else if (!networkMatched) {
144
+ const fromNamespace = getCurrentNamespaceOf(swap, currentStep);
145
+ const details = ERROR_MESSAGE_WAIT_FOR_CHANGE_NETWORK(
146
+ fromNamespace.network
147
+ );
148
+
149
+ const blockedFor = {
150
+ reason: BlockReason.WAIT_FOR_NETWORK_CHANGE,
151
+ details: details,
152
+ };
153
+ return new Err(blockedFor);
154
+ }
155
+
156
+ return new Ok(true);
157
+ }