@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
|
@@ -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
|
+
}
|