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