@rango-dev/queue-manager-rango-preset 0.1.10-next.77
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 +9 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/helpers.d.ts +159 -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 +10 -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 +2249 -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 +2242 -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/shared-api.d.ts +10 -0
- package/dist/shared-api.d.ts.map +1 -0
- package/dist/shared-errors.d.ts +56 -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 +149 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/types.d.ts +46 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +59 -0
- package/readme.md +9 -0
- package/src/actions/checkStatus.ts +213 -0
- package/src/actions/createTransaction.ts +101 -0
- package/src/actions/executeTransaction.ts +117 -0
- package/src/actions/scheduleNextStep.ts +60 -0
- package/src/actions/start.ts +10 -0
- package/src/constants.ts +23 -0
- package/src/helpers.ts +1252 -0
- package/src/hooks.ts +75 -0
- package/src/index.ts +39 -0
- package/src/migration.ts +112 -0
- package/src/queueDef.ts +39 -0
- package/src/shared-api.ts +175 -0
- package/src/shared-errors.ts +156 -0
- package/src/shared-sentry.ts +24 -0
- package/src/shared.ts +333 -0
- package/src/types.ts +76 -0
package/src/helpers.ts
ADDED
|
@@ -0,0 +1,1252 @@
|
|
|
1
|
+
import { ExecuterActions, QueueType } from '@rango-dev/queue-manager-core';
|
|
2
|
+
import {
|
|
3
|
+
BlockReason,
|
|
4
|
+
SwapActionTypes,
|
|
5
|
+
SwapQueueContext,
|
|
6
|
+
SwapQueueDef,
|
|
7
|
+
SwapStorage,
|
|
8
|
+
} from './types';
|
|
9
|
+
import {
|
|
10
|
+
EvmBlockchainMeta,
|
|
11
|
+
getBlockChainNameFromId,
|
|
12
|
+
Meta,
|
|
13
|
+
Network,
|
|
14
|
+
Transaction,
|
|
15
|
+
TransactionType,
|
|
16
|
+
WalletState,
|
|
17
|
+
WalletType,
|
|
18
|
+
} from '@rango-dev/wallets-shared';
|
|
19
|
+
import { Providers, readAccountAddress } from '@rango-dev/wallets-core';
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
TronTransaction,
|
|
23
|
+
StarknetTransaction,
|
|
24
|
+
CosmosTransaction,
|
|
25
|
+
EvmTransaction,
|
|
26
|
+
SolanaTransaction,
|
|
27
|
+
Transfer as TransferTransaction,
|
|
28
|
+
} from 'rango-sdk';
|
|
29
|
+
|
|
30
|
+
import {
|
|
31
|
+
ERROR_MESSAGE_WAIT_FOR_CHANGE_NETWORK,
|
|
32
|
+
ERROR_MESSAGE_WAIT_FOR_WALLET,
|
|
33
|
+
ERROR_MESSAGE_WAIT_FOR_WALLET_DESCRIPTION,
|
|
34
|
+
} from './constants';
|
|
35
|
+
import { Manager } from '@rango-dev/queue-manager-core';
|
|
36
|
+
import { Status } from '@rango-dev/queue-manager-core';
|
|
37
|
+
import {
|
|
38
|
+
EventType,
|
|
39
|
+
getCurrentBlockchainOf,
|
|
40
|
+
getCurrentBlockchainOfOrNull,
|
|
41
|
+
getEvmApproveUrl,
|
|
42
|
+
getRelatedWalletOrNull,
|
|
43
|
+
MessageSeverity,
|
|
44
|
+
PendingSwap,
|
|
45
|
+
PendingSwapNetworkStatus,
|
|
46
|
+
PendingSwapStep,
|
|
47
|
+
prettifyErrorMessage,
|
|
48
|
+
StepStatus,
|
|
49
|
+
SwapStatus,
|
|
50
|
+
Wallet,
|
|
51
|
+
WalletTypeAndAddress,
|
|
52
|
+
} from './shared';
|
|
53
|
+
import { logRPCError } from './shared-sentry';
|
|
54
|
+
import { PrettyError } from './shared-errors';
|
|
55
|
+
import { mapAppErrorCodesToAPIErrorCode, reportFailed } from './shared-api';
|
|
56
|
+
|
|
57
|
+
type WhenTaskBlocked = Parameters<NonNullable<SwapQueueDef['whenTaskBlocked']>>;
|
|
58
|
+
type WhenTaskBlockedEvent = WhenTaskBlocked[0];
|
|
59
|
+
type WhenTaskBlockedMeta = WhenTaskBlocked[1];
|
|
60
|
+
|
|
61
|
+
let swapClaimedBy: { id: string } | null = null;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
*
|
|
65
|
+
* We simply use module-level variable to keep track of which queue has claimed the execution of parallel runnings.
|
|
66
|
+
*
|
|
67
|
+
*/
|
|
68
|
+
function claimQueue() {
|
|
69
|
+
return {
|
|
70
|
+
claimedBy: () => swapClaimedBy?.id,
|
|
71
|
+
setClaimer: (queue_id: string) => {
|
|
72
|
+
swapClaimedBy = {
|
|
73
|
+
id: queue_id,
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
reset: () => {
|
|
77
|
+
swapClaimedBy = null;
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
*
|
|
84
|
+
* Returns `steps`, if it's a `running` swap.
|
|
85
|
+
* Each `PendingSwap` has a `steps` inside it, `steps` shows how many tasks should be created and run to finish the swap.
|
|
86
|
+
*
|
|
87
|
+
*/
|
|
88
|
+
export const getCurrentStep = (swap: PendingSwap): PendingSwapStep | null => {
|
|
89
|
+
return (
|
|
90
|
+
swap.steps.find(
|
|
91
|
+
(step) => step.status !== 'failed' && step.status !== 'success'
|
|
92
|
+
) || null
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* When we are doing a swap, there are some common fields that will be updated together.
|
|
98
|
+
* This function helps us to update a swap status and also it will update some more fields like `extraMessageSeverity` based on the input.
|
|
99
|
+
*/
|
|
100
|
+
export function updateSwapStatus({
|
|
101
|
+
getStorage,
|
|
102
|
+
setStorage,
|
|
103
|
+
nextStatus,
|
|
104
|
+
nextStepStatus,
|
|
105
|
+
message,
|
|
106
|
+
details,
|
|
107
|
+
errorCode = null,
|
|
108
|
+
}: {
|
|
109
|
+
getStorage: ExecuterActions<
|
|
110
|
+
SwapStorage,
|
|
111
|
+
SwapActionTypes,
|
|
112
|
+
SwapQueueContext
|
|
113
|
+
>['getStorage'];
|
|
114
|
+
setStorage: ExecuterActions<
|
|
115
|
+
SwapStorage,
|
|
116
|
+
SwapActionTypes,
|
|
117
|
+
SwapQueueContext
|
|
118
|
+
>['setStorage'];
|
|
119
|
+
nextStatus?: SwapStatus;
|
|
120
|
+
nextStepStatus?: StepStatus;
|
|
121
|
+
message?: string;
|
|
122
|
+
details?: string | null | undefined;
|
|
123
|
+
errorCode?: string | null;
|
|
124
|
+
}): {
|
|
125
|
+
swap: PendingSwap;
|
|
126
|
+
step: PendingSwapStep | null;
|
|
127
|
+
} {
|
|
128
|
+
const swap = getStorage().swapDetails;
|
|
129
|
+
const currentStep = getCurrentStep(swap);
|
|
130
|
+
if (!!nextStepStatus && !!currentStep) currentStep.status = nextStepStatus;
|
|
131
|
+
|
|
132
|
+
if (!!nextStatus) swap.status = nextStatus;
|
|
133
|
+
|
|
134
|
+
if (!!nextStatus && ['failed', 'success'].includes(nextStatus))
|
|
135
|
+
swap.finishTime = new Date().getTime().toString();
|
|
136
|
+
|
|
137
|
+
if (!!message) swap.extraMessage = message;
|
|
138
|
+
|
|
139
|
+
if (!!details) swap.extraMessageDetail = details;
|
|
140
|
+
|
|
141
|
+
if (!!nextStepStatus && ['failed'].includes(nextStepStatus)) {
|
|
142
|
+
//if user cancel the swap, we should pass relevant reason to the server.
|
|
143
|
+
const errorReason =
|
|
144
|
+
details && details.includes('Warning')
|
|
145
|
+
? 'Swap canceled by user.'
|
|
146
|
+
: details;
|
|
147
|
+
swap.extraMessageSeverity = MessageSeverity.error;
|
|
148
|
+
reportFailed(
|
|
149
|
+
swap.requestId,
|
|
150
|
+
currentStep?.id || 1,
|
|
151
|
+
mapAppErrorCodesToAPIErrorCode(errorCode),
|
|
152
|
+
errorReason || '',
|
|
153
|
+
(currentStep
|
|
154
|
+
? getRelatedWalletOrNull(swap, currentStep)?.walletType
|
|
155
|
+
: null) || null
|
|
156
|
+
).then();
|
|
157
|
+
} else if (!!nextStepStatus && ['running'].includes(nextStepStatus))
|
|
158
|
+
swap.extraMessageSeverity = MessageSeverity.info;
|
|
159
|
+
else if (!!nextStepStatus && ['success', 'approved'].includes(nextStepStatus))
|
|
160
|
+
swap.extraMessageSeverity = MessageSeverity.success;
|
|
161
|
+
else if (nextStepStatus && ['waitingForApproval'].includes(nextStepStatus))
|
|
162
|
+
swap.extraMessageSeverity = MessageSeverity.warning;
|
|
163
|
+
|
|
164
|
+
if (nextStepStatus === 'running' && currentStep)
|
|
165
|
+
currentStep.startTransactionTime = new Date().getTime();
|
|
166
|
+
|
|
167
|
+
setStorage({
|
|
168
|
+
...getStorage(),
|
|
169
|
+
swapDetails: swap,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
swap,
|
|
174
|
+
step: currentStep,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function setStepTransactionIds(
|
|
179
|
+
{ getStorage, setStorage }: ExecuterActions<SwapStorage, SwapActionTypes>,
|
|
180
|
+
txId: string | null,
|
|
181
|
+
eventType: EventType,
|
|
182
|
+
notifier: SwapQueueContext['notifier']
|
|
183
|
+
): void {
|
|
184
|
+
const swap = getStorage().swapDetails;
|
|
185
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
186
|
+
const currentStep = getCurrentStep(swap)!;
|
|
187
|
+
currentStep.executedTransactionId = txId || currentStep.executedTransactionId;
|
|
188
|
+
setStorage({
|
|
189
|
+
...getStorage(),
|
|
190
|
+
swapDetails: swap,
|
|
191
|
+
});
|
|
192
|
+
notifier({ eventType: eventType, swap: swap, step: currentStep });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* If a swap needs a wallet to be connected,
|
|
197
|
+
* By calling this function some related fields will be updated to show a correct message and state for notfiying the user.
|
|
198
|
+
*/
|
|
199
|
+
export function markRunningSwapAsWaitingForConnectingWallet(
|
|
200
|
+
{
|
|
201
|
+
getStorage,
|
|
202
|
+
setStorage,
|
|
203
|
+
}: Pick<ExecuterActions, 'getStorage' | 'setStorage'>,
|
|
204
|
+
reason: string,
|
|
205
|
+
reasonDetail: string
|
|
206
|
+
): void {
|
|
207
|
+
const swap = getStorage().swapDetails as SwapStorage['swapDetails'];
|
|
208
|
+
const currentStep = getCurrentStep(swap);
|
|
209
|
+
if (!currentStep) return;
|
|
210
|
+
const currentTime = new Date();
|
|
211
|
+
swap.lastNotificationTime = currentTime.getTime().toString();
|
|
212
|
+
|
|
213
|
+
const isAlreadyMarked =
|
|
214
|
+
currentStep.networkStatus ===
|
|
215
|
+
PendingSwapNetworkStatus.WaitingForConnectingWallet &&
|
|
216
|
+
swap.networkStatusExtraMessage === reason &&
|
|
217
|
+
swap.networkStatusExtraMessageDetail === reasonDetail;
|
|
218
|
+
|
|
219
|
+
if (isAlreadyMarked) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
currentStep.networkStatus =
|
|
224
|
+
PendingSwapNetworkStatus.WaitingForConnectingWallet;
|
|
225
|
+
swap.networkStatusExtraMessage = reason;
|
|
226
|
+
swap.networkStatusExtraMessageDetail = reasonDetail;
|
|
227
|
+
|
|
228
|
+
setStorage({
|
|
229
|
+
...getStorage(),
|
|
230
|
+
swapDetails: swap,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* If a swap needs a certain network to proceed,
|
|
236
|
+
* By calling this function some related fields will be updated to show a correct message and state for notfiying the user.
|
|
237
|
+
*/
|
|
238
|
+
export function markRunningSwapAsSwitchingNetwork({
|
|
239
|
+
getStorage,
|
|
240
|
+
setStorage,
|
|
241
|
+
}: Pick<ExecuterActions, 'getStorage' | 'setStorage'>):
|
|
242
|
+
| {
|
|
243
|
+
swap: PendingSwap;
|
|
244
|
+
step: PendingSwapStep;
|
|
245
|
+
}
|
|
246
|
+
| undefined {
|
|
247
|
+
const swap = getStorage().swapDetails as SwapStorage['swapDetails'];
|
|
248
|
+
|
|
249
|
+
const currentStep = getCurrentStep(swap);
|
|
250
|
+
if (!currentStep) return;
|
|
251
|
+
|
|
252
|
+
// Generate message
|
|
253
|
+
const { type } = getRequiredWallet(swap);
|
|
254
|
+
const fromBlockchain = getCurrentBlockchainOf(swap, currentStep);
|
|
255
|
+
const reason = `Change ${type} wallet network to ${fromBlockchain}`;
|
|
256
|
+
let metamaskMessage = '';
|
|
257
|
+
if (type === WalletType.META_MASK)
|
|
258
|
+
metamaskMessage = `(Networks -> Select '${fromBlockchain}' network.)`;
|
|
259
|
+
const reasonDetail = `Please change your ${type} wallet network to ${fromBlockchain}. ${metamaskMessage}`;
|
|
260
|
+
|
|
261
|
+
const currentTime = new Date();
|
|
262
|
+
swap.lastNotificationTime = currentTime.getTime().toString();
|
|
263
|
+
|
|
264
|
+
currentStep.networkStatus = PendingSwapNetworkStatus.WaitingForNetworkChange;
|
|
265
|
+
swap.networkStatusExtraMessage = reason;
|
|
266
|
+
swap.networkStatusExtraMessageDetail = reasonDetail;
|
|
267
|
+
|
|
268
|
+
setStorage({
|
|
269
|
+
...getStorage(),
|
|
270
|
+
swapDetails: swap,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
swap,
|
|
275
|
+
step: currentStep,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* We are marking the queue as it depends on other queues to be run (on Parallel mode)
|
|
281
|
+
* By calling this function some related fields will be updated to show a correct message and state for notfiying the user.
|
|
282
|
+
*/
|
|
283
|
+
export function markRunningSwapAsDependsOnOtherQueues({
|
|
284
|
+
getStorage,
|
|
285
|
+
setStorage,
|
|
286
|
+
}: Pick<ExecuterActions, 'getStorage' | 'setStorage'>):
|
|
287
|
+
| {
|
|
288
|
+
swap: PendingSwap;
|
|
289
|
+
step: PendingSwapStep;
|
|
290
|
+
}
|
|
291
|
+
| undefined {
|
|
292
|
+
const swap = getStorage().swapDetails as SwapStorage['swapDetails'];
|
|
293
|
+
const currentStep = getCurrentStep(swap);
|
|
294
|
+
if (!currentStep) return;
|
|
295
|
+
|
|
296
|
+
swap.networkStatusExtraMessage = '';
|
|
297
|
+
swap.networkStatusExtraMessageDetail = '';
|
|
298
|
+
currentStep.networkStatus = PendingSwapNetworkStatus.WaitingForQueue;
|
|
299
|
+
|
|
300
|
+
setStorage({
|
|
301
|
+
...getStorage(),
|
|
302
|
+
swapDetails: swap,
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
swap,
|
|
307
|
+
step: currentStep,
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export function delay(ms: number): Promise<unknown> {
|
|
312
|
+
return new Promise((res) => setTimeout(res, ms));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export const isEvmTransaction = (tx: Transaction): tx is EvmTransaction =>
|
|
316
|
+
tx.type === TransactionType.EVM;
|
|
317
|
+
|
|
318
|
+
export const isCosmosTransaction = (tx: Transaction): tx is CosmosTransaction =>
|
|
319
|
+
tx.type === TransactionType.COSMOS;
|
|
320
|
+
export const isSolanaTransaction = (tx: Transaction): tx is SolanaTransaction =>
|
|
321
|
+
tx.type === TransactionType.SOLANA;
|
|
322
|
+
export const isTrasnferTransaction = (
|
|
323
|
+
tx: Transaction
|
|
324
|
+
): tx is TransferTransaction => tx.type === TransactionType.TRANSFER;
|
|
325
|
+
export const isStarknetTransaction = (
|
|
326
|
+
tx: Transaction
|
|
327
|
+
): tx is StarknetTransaction => tx.type === TransactionType.STARKNET;
|
|
328
|
+
export const isTronTransaction = (tx: Transaction): tx is TronTransaction =>
|
|
329
|
+
tx.type === TransactionType.TRON;
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
*
|
|
333
|
+
* To execute a swap, we are keeping the user prefrences on what wallet they are going to use for a sepecific blockchain
|
|
334
|
+
* By passing the swap and the network we are looking for, it returns the wallet name that user selected.
|
|
335
|
+
*
|
|
336
|
+
*/
|
|
337
|
+
export const getSwapWalletType = (
|
|
338
|
+
swap: PendingSwap,
|
|
339
|
+
network: Network
|
|
340
|
+
): WalletType => {
|
|
341
|
+
return swap.wallets[network]?.walletType;
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
*
|
|
346
|
+
* We are keeping the connected wallet in a specific structure (`Wallet`),
|
|
347
|
+
* By using this function we normally want to check a specific wallet is connected and exists or not.
|
|
348
|
+
*
|
|
349
|
+
*/
|
|
350
|
+
export function isWalletNull(wallet: Wallet | null): boolean {
|
|
351
|
+
return (
|
|
352
|
+
wallet === null ||
|
|
353
|
+
wallet?.blockchains === null ||
|
|
354
|
+
wallet?.blockchains.length === 0
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* On our implementation for `wallets` package, We keep the instance in 2 ways
|
|
360
|
+
* If it's a single chain wallet, it returns the instance directly,
|
|
361
|
+
* If it's a multichain wallet, it returns a `Map` of instances.
|
|
362
|
+
* This function will get the `ETHEREUM` instance in both types.
|
|
363
|
+
*/
|
|
364
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
365
|
+
export function getEvmProvider(providers: Providers, type: WalletType): any {
|
|
366
|
+
if (type && providers[type]) {
|
|
367
|
+
// 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.
|
|
368
|
+
if (providers[type].size) return providers[type].get(Network.ETHEREUM);
|
|
369
|
+
|
|
370
|
+
return providers[type];
|
|
371
|
+
}
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* In a `PendingSwap`, each step needs a wallet to proceed,
|
|
377
|
+
* By using this function we can access what wallet exactly we need to run current step.
|
|
378
|
+
*/
|
|
379
|
+
export function getRequiredWallet(swap: PendingSwap): {
|
|
380
|
+
type: WalletType | null;
|
|
381
|
+
network: Network | null;
|
|
382
|
+
address: string | null;
|
|
383
|
+
} {
|
|
384
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
385
|
+
const step = getCurrentStep(swap)!;
|
|
386
|
+
const bcName = getCurrentBlockchainOfOrNull(swap, step);
|
|
387
|
+
if (!bcName) {
|
|
388
|
+
return {
|
|
389
|
+
type: null,
|
|
390
|
+
network: null,
|
|
391
|
+
address: null,
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const walletType = getSwapWalletType(swap, bcName);
|
|
396
|
+
const sourceWallet = swap.wallets[bcName];
|
|
397
|
+
|
|
398
|
+
return {
|
|
399
|
+
type: walletType || null,
|
|
400
|
+
network: bcName,
|
|
401
|
+
address: sourceWallet ? sourceWallet.address : null,
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* On EVM compatible wallets, There is one instance with different chains (like Polygon)
|
|
407
|
+
* To get the chain from instance we will use this function.
|
|
408
|
+
*/
|
|
409
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
410
|
+
async function getChainId(provider: any): Promise<string | number | null> {
|
|
411
|
+
const chainId: number | string | null =
|
|
412
|
+
(await provider.request({ method: 'eth_chainId' })) || provider?.chainId;
|
|
413
|
+
return chainId;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* For running a swap safely, we need to make sure about the state of wallet
|
|
418
|
+
* which means the netowrk/chain of wallet should be exactly on what a transaction needs.
|
|
419
|
+
*/
|
|
420
|
+
export async function isNetworkMatchedForTransaction(
|
|
421
|
+
swap: PendingSwap,
|
|
422
|
+
step: PendingSwapStep,
|
|
423
|
+
wallet: Wallet | null,
|
|
424
|
+
meta: Meta,
|
|
425
|
+
providers: Providers
|
|
426
|
+
): Promise<boolean> {
|
|
427
|
+
if (isWalletNull(wallet)) {
|
|
428
|
+
console.warn('wallet object is null');
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
const fromBlockChain = getCurrentBlockchainOfOrNull(swap, step);
|
|
432
|
+
if (!fromBlockChain) return false;
|
|
433
|
+
|
|
434
|
+
if (
|
|
435
|
+
!!meta.evmBasedChains.find(
|
|
436
|
+
(evmBlochain) => evmBlochain.name === fromBlockChain
|
|
437
|
+
)
|
|
438
|
+
) {
|
|
439
|
+
try {
|
|
440
|
+
const sourceWallet = swap.wallets[fromBlockChain];
|
|
441
|
+
if (sourceWallet) {
|
|
442
|
+
if (
|
|
443
|
+
[
|
|
444
|
+
WalletType.META_MASK,
|
|
445
|
+
WalletType.BINANCE_CHAIN,
|
|
446
|
+
WalletType.XDEFI,
|
|
447
|
+
WalletType.WALLET_CONNECT,
|
|
448
|
+
WalletType.TRUST_WALLET,
|
|
449
|
+
WalletType.COIN98,
|
|
450
|
+
WalletType.EXODUS,
|
|
451
|
+
WalletType.OKX,
|
|
452
|
+
WalletType.COINBASE,
|
|
453
|
+
WalletType.TOKEN_POCKET,
|
|
454
|
+
WalletType.MATH,
|
|
455
|
+
WalletType.SAFEPAL,
|
|
456
|
+
WalletType.COSMOSTATION,
|
|
457
|
+
WalletType.CLOVER,
|
|
458
|
+
WalletType.BRAVE,
|
|
459
|
+
].includes(sourceWallet.walletType)
|
|
460
|
+
) {
|
|
461
|
+
const provider = getEvmProvider(providers, sourceWallet.walletType);
|
|
462
|
+
const chainId: number | string | null = await getChainId(provider);
|
|
463
|
+
if (chainId) {
|
|
464
|
+
const blockChain = getBlockChainNameFromId(
|
|
465
|
+
chainId,
|
|
466
|
+
Object.entries(meta.blockchains).map(
|
|
467
|
+
([, blockchainMeta]) => blockchainMeta
|
|
468
|
+
)
|
|
469
|
+
);
|
|
470
|
+
if (
|
|
471
|
+
blockChain &&
|
|
472
|
+
blockChain.toLowerCase() === fromBlockChain.toLowerCase()
|
|
473
|
+
)
|
|
474
|
+
return true;
|
|
475
|
+
if (
|
|
476
|
+
blockChain &&
|
|
477
|
+
blockChain.toLowerCase() !== fromBlockChain.toLowerCase()
|
|
478
|
+
)
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
} else {
|
|
482
|
+
return true;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
} catch (e) {
|
|
486
|
+
console.log(e);
|
|
487
|
+
}
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
return true;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Returns the wallet address, based on the current step of `PendingSwap`.
|
|
495
|
+
*/
|
|
496
|
+
export const getCurrentAddressOf = (
|
|
497
|
+
swap: PendingSwap,
|
|
498
|
+
step: PendingSwapStep
|
|
499
|
+
): string => {
|
|
500
|
+
const result =
|
|
501
|
+
swap.wallets[step.evmTransaction?.blockChain || ''] ||
|
|
502
|
+
swap.wallets[step.evmApprovalTransaction?.blockChain || ''] ||
|
|
503
|
+
swap.wallets[step.cosmosTransaction?.blockChain || ''] ||
|
|
504
|
+
swap.wallets[step.solanaTransaction?.blockChain || ''] ||
|
|
505
|
+
(step.transferTransaction?.fromWalletAddress
|
|
506
|
+
? { address: step.transferTransaction?.fromWalletAddress }
|
|
507
|
+
: null) ||
|
|
508
|
+
null;
|
|
509
|
+
if (result == null) throw PrettyError.WalletMissing();
|
|
510
|
+
return result.address;
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
// Todo: Is it same with `getRequiredWallet`?
|
|
514
|
+
export function getRelatedWallet(
|
|
515
|
+
swap: PendingSwap,
|
|
516
|
+
currentStep: PendingSwapStep
|
|
517
|
+
): WalletTypeAndAddress {
|
|
518
|
+
const walletAddress = getCurrentAddressOf(swap, currentStep);
|
|
519
|
+
const walletKV =
|
|
520
|
+
Object.keys(swap.wallets)
|
|
521
|
+
.map((k) => ({ k, v: swap.wallets[k] }))
|
|
522
|
+
.find(({ v }) => v.address === walletAddress) || null;
|
|
523
|
+
const blockchain = walletKV?.k || null;
|
|
524
|
+
const wallet = walletKV?.v || null;
|
|
525
|
+
|
|
526
|
+
const walletType = wallet?.walletType;
|
|
527
|
+
if (walletType === WalletType.UNKNOWN || wallet === null)
|
|
528
|
+
throw PrettyError.AssertionFailed(
|
|
529
|
+
`Wallet for source ${blockchain} not passed to transfer: walletType: ${walletType}`
|
|
530
|
+
);
|
|
531
|
+
return wallet;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
export const isTxAlreadyCreated = (
|
|
535
|
+
swap: PendingSwap,
|
|
536
|
+
step: PendingSwapStep
|
|
537
|
+
): boolean => {
|
|
538
|
+
const result =
|
|
539
|
+
swap.wallets[step.evmTransaction?.blockChain || ''] ||
|
|
540
|
+
swap.wallets[step.evmApprovalTransaction?.blockChain || ''] ||
|
|
541
|
+
swap.wallets[step.cosmosTransaction?.blockChain || ''] ||
|
|
542
|
+
swap.wallets[step.solanaTransaction?.blockChain || ''] ||
|
|
543
|
+
step.transferTransaction?.fromWalletAddress ||
|
|
544
|
+
null;
|
|
545
|
+
|
|
546
|
+
return result !== null;
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
export function resetNetworkStatus(
|
|
550
|
+
actions: ExecuterActions<SwapStorage, SwapActionTypes, SwapQueueContext>
|
|
551
|
+
): void {
|
|
552
|
+
const { getStorage, setStorage } = actions;
|
|
553
|
+
const swap = getStorage().swapDetails;
|
|
554
|
+
const currentStep = getCurrentStep(swap);
|
|
555
|
+
|
|
556
|
+
if (currentStep?.networkStatus) {
|
|
557
|
+
currentStep.networkStatus = null;
|
|
558
|
+
setStorage({ ...getStorage(), swapDetails: swap });
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
export function updateNetworkStatus(
|
|
563
|
+
actions: ExecuterActions<SwapStorage, SwapActionTypes, SwapQueueContext>,
|
|
564
|
+
data: {
|
|
565
|
+
message: string;
|
|
566
|
+
details: string;
|
|
567
|
+
status: PendingSwapNetworkStatus | null;
|
|
568
|
+
} = {
|
|
569
|
+
message: '',
|
|
570
|
+
details: '',
|
|
571
|
+
status: null,
|
|
572
|
+
}
|
|
573
|
+
): void {
|
|
574
|
+
const { message, details, status } = data;
|
|
575
|
+
const { getStorage, setStorage } = actions;
|
|
576
|
+
const swap = getStorage().swapDetails;
|
|
577
|
+
const currentStep = getCurrentStep(swap);
|
|
578
|
+
|
|
579
|
+
if (currentStep?.networkStatus) {
|
|
580
|
+
swap.networkStatusExtraMessage = message;
|
|
581
|
+
swap.networkStatusExtraMessageDetail = details;
|
|
582
|
+
currentStep.networkStatus = status;
|
|
583
|
+
setStorage({ ...getStorage(), swapDetails: swap });
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Event handler for blocked tasks.
|
|
589
|
+
* If a transcation execution is manually blocked (like for parallel or waiting for walle),
|
|
590
|
+
* This function will be called by queue manager using `queue definition`.
|
|
591
|
+
*
|
|
592
|
+
* It checks if the required wallet is connected, unblock the queue to be run.
|
|
593
|
+
*/
|
|
594
|
+
export function onBlockForConnectWallet(
|
|
595
|
+
event: WhenTaskBlockedEvent,
|
|
596
|
+
meta: WhenTaskBlockedMeta
|
|
597
|
+
): void {
|
|
598
|
+
const { context, queue } = meta;
|
|
599
|
+
const swap = queue.getStorage().swapDetails as SwapStorage['swapDetails'];
|
|
600
|
+
|
|
601
|
+
if (!isRequiredWalletConnected(swap, context.state)) {
|
|
602
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
603
|
+
const currentStep = getCurrentStep(swap)!;
|
|
604
|
+
context.notifier({
|
|
605
|
+
eventType: 'waiting_for_connecting_wallet',
|
|
606
|
+
swap: swap,
|
|
607
|
+
step: currentStep,
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
markRunningSwapAsWaitingForConnectingWallet(
|
|
611
|
+
{
|
|
612
|
+
getStorage: queue.getStorage.bind(queue),
|
|
613
|
+
setStorage: queue.setStorage.bind(queue),
|
|
614
|
+
},
|
|
615
|
+
ERROR_MESSAGE_WAIT_FOR_WALLET,
|
|
616
|
+
event.reason.description
|
|
617
|
+
);
|
|
618
|
+
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
queue.unblock();
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Event handler for blocked tasks.
|
|
627
|
+
* If a transcation execution is manually blocked (like for parallel or waiting for walle),
|
|
628
|
+
* This function will be called by queue manager using `queue definition`.
|
|
629
|
+
*
|
|
630
|
+
* It checks if the required network is connected, unblock the queue to be run.
|
|
631
|
+
* Note: it automatically try to switch the network if its `provider` supports.
|
|
632
|
+
*/
|
|
633
|
+
export function onBlockForChangeNetwork(
|
|
634
|
+
_event: WhenTaskBlockedEvent,
|
|
635
|
+
meta: WhenTaskBlockedMeta
|
|
636
|
+
): void {
|
|
637
|
+
const { context, queue } = meta;
|
|
638
|
+
const swap = queue.getStorage().swapDetails as SwapStorage['swapDetails'];
|
|
639
|
+
const currentStep = getCurrentStep(swap);
|
|
640
|
+
|
|
641
|
+
if (!currentStep) return;
|
|
642
|
+
|
|
643
|
+
const result = markRunningSwapAsSwitchingNetwork({
|
|
644
|
+
getStorage: queue.getStorage.bind(queue),
|
|
645
|
+
setStorage: queue.setStorage.bind(queue),
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
if (result) {
|
|
649
|
+
context.notifier({
|
|
650
|
+
eventType: 'waiting_for_network_change',
|
|
651
|
+
swap: result.swap,
|
|
652
|
+
step: result.step,
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Try to auto switch
|
|
657
|
+
const { type, network } = getRequiredWallet(swap);
|
|
658
|
+
if (!!type && !!network) {
|
|
659
|
+
const result = context.switchNetwork(type, network);
|
|
660
|
+
if (result) {
|
|
661
|
+
result.then(() => {
|
|
662
|
+
queue.unblock();
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Event handler for blocked tasks. (Parallel mode)
|
|
670
|
+
* If a transcation execution is manually blocked (like for parallel or waiting for walle),
|
|
671
|
+
* This function will be called by queue manager using `queue definition`.
|
|
672
|
+
*
|
|
673
|
+
* It checks the blocked tasks, if there is no active `claimed` queue, try to give it to the best candidate.
|
|
674
|
+
*/
|
|
675
|
+
export function onDependsOnOtherQueues(
|
|
676
|
+
_event: WhenTaskBlockedEvent,
|
|
677
|
+
meta: WhenTaskBlockedMeta
|
|
678
|
+
): void {
|
|
679
|
+
const { getBlockedTasks, forceExecute, queue, manager } = meta;
|
|
680
|
+
const { setClaimer, claimedBy, reset } = claimQueue();
|
|
681
|
+
|
|
682
|
+
// We only needs those blocked tasks that have DEPENDS_ON_OTHER_QUEUES reason.
|
|
683
|
+
const blockedTasks = getBlockedTasks().filter(
|
|
684
|
+
(task) => task.reason.reason === BlockReason.DEPENDS_ON_OTHER_QUEUES
|
|
685
|
+
);
|
|
686
|
+
|
|
687
|
+
if (blockedTasks.length === 0) {
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
const claimerId = claimedBy();
|
|
692
|
+
const isClaimedByAnyQueue = !!claimerId;
|
|
693
|
+
|
|
694
|
+
// Check if any queue `claimed` before, if yes, we don't should do anything.
|
|
695
|
+
if (isClaimedByAnyQueue) {
|
|
696
|
+
// We need to keep the latest swap messages
|
|
697
|
+
markRunningSwapAsDependsOnOtherQueues({
|
|
698
|
+
getStorage: queue.getStorage.bind(queue),
|
|
699
|
+
setStorage: queue.setStorage.bind(queue),
|
|
700
|
+
});
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// Prioritize current queue to be run first.
|
|
705
|
+
|
|
706
|
+
let task = blockedTasks.find((task) => {
|
|
707
|
+
return task.queue_id === meta.queue_id;
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
// If current task isn't available anymore, fallback to first blocked task.
|
|
711
|
+
if (!task) {
|
|
712
|
+
const firstBlockedTask = blockedTasks[0];
|
|
713
|
+
task = firstBlockedTask;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
setClaimer(task.queue_id);
|
|
717
|
+
const claimedStorage = task.storage.get() as SwapStorage;
|
|
718
|
+
const { type, network, address } = getRequiredWallet(
|
|
719
|
+
claimedStorage.swapDetails
|
|
720
|
+
);
|
|
721
|
+
|
|
722
|
+
// Run
|
|
723
|
+
forceExecute(task.queue_id, {
|
|
724
|
+
claimedBy: claimedBy(),
|
|
725
|
+
resetClaimedBy: () => {
|
|
726
|
+
reset();
|
|
727
|
+
// TODO: Use key generator
|
|
728
|
+
retryOn(`${type}-${network}-${address}`, manager);
|
|
729
|
+
},
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
export function isRequiredWalletConnected(
|
|
734
|
+
swap: PendingSwap,
|
|
735
|
+
getState: (type: WalletType) => WalletState
|
|
736
|
+
): boolean {
|
|
737
|
+
const { type, address } = getRequiredWallet(swap);
|
|
738
|
+
if (!type || !address) {
|
|
739
|
+
return false;
|
|
740
|
+
}
|
|
741
|
+
const walletState = getState(type);
|
|
742
|
+
const { accounts } = walletState;
|
|
743
|
+
const connectedAccounts = accounts || [];
|
|
744
|
+
|
|
745
|
+
return connectedAccounts.some((account) => {
|
|
746
|
+
const { address: accountAddress } = readAccountAddress(account);
|
|
747
|
+
return address === accountAddress;
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
export function singTransaction(
|
|
752
|
+
actions: ExecuterActions<SwapStorage, SwapActionTypes, SwapQueueContext>
|
|
753
|
+
): void {
|
|
754
|
+
const { getStorage, setStorage, failed, next, schedule, context } = actions;
|
|
755
|
+
const { meta, getSigners, notifier } = context;
|
|
756
|
+
const swap = getStorage().swapDetails;
|
|
757
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
758
|
+
const currentStep = getCurrentStep(swap)!;
|
|
759
|
+
const {
|
|
760
|
+
evmTransaction,
|
|
761
|
+
evmApprovalTransaction,
|
|
762
|
+
cosmosTransaction,
|
|
763
|
+
solanaTransaction,
|
|
764
|
+
transferTransaction,
|
|
765
|
+
} = currentStep;
|
|
766
|
+
const sourceWallet = getRelatedWallet(swap, currentStep);
|
|
767
|
+
const walletSigners = getSigners(sourceWallet.walletType);
|
|
768
|
+
const onFinish = () => {
|
|
769
|
+
if (actions.context.resetClaimedBy) {
|
|
770
|
+
actions.context.resetClaimedBy();
|
|
771
|
+
}
|
|
772
|
+
};
|
|
773
|
+
|
|
774
|
+
if (!!evmApprovalTransaction) {
|
|
775
|
+
const spenderContract = evmApprovalTransaction?.to;
|
|
776
|
+
|
|
777
|
+
if (!spenderContract)
|
|
778
|
+
throw PrettyError.AssertionFailed(
|
|
779
|
+
'contract address is null for checking approval'
|
|
780
|
+
);
|
|
781
|
+
|
|
782
|
+
// Update swap status
|
|
783
|
+
const message = `waiting for approval of ${currentStep?.fromSymbol} coin ${
|
|
784
|
+
sourceWallet.walletType === WalletType.WALLET_CONNECT
|
|
785
|
+
? 'on your mobile phone'
|
|
786
|
+
: ''
|
|
787
|
+
}`;
|
|
788
|
+
const updateResult = updateSwapStatus({
|
|
789
|
+
getStorage,
|
|
790
|
+
setStorage,
|
|
791
|
+
nextStepStatus: 'waitingForApproval',
|
|
792
|
+
message,
|
|
793
|
+
details:
|
|
794
|
+
'Waiting for approve transaction to be mined and confirmed successfully',
|
|
795
|
+
});
|
|
796
|
+
notifier({
|
|
797
|
+
eventType: 'confirm_contract',
|
|
798
|
+
...updateResult,
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
// Execute transaction
|
|
802
|
+
walletSigners.executeEvmTransaction(evmApprovalTransaction, meta).then(
|
|
803
|
+
(hash) => {
|
|
804
|
+
console.debug('transaction of approval minted successfully', hash);
|
|
805
|
+
const approveUrl = getEvmApproveUrl(
|
|
806
|
+
hash,
|
|
807
|
+
getCurrentBlockchainOf(swap, currentStep),
|
|
808
|
+
meta.evmBasedChains
|
|
809
|
+
);
|
|
810
|
+
currentStep.explorerUrl = [
|
|
811
|
+
...(currentStep.explorerUrl || []),
|
|
812
|
+
{
|
|
813
|
+
url: approveUrl,
|
|
814
|
+
description: `approve`,
|
|
815
|
+
},
|
|
816
|
+
];
|
|
817
|
+
|
|
818
|
+
// `currentStep` has been mutated, let's update storage.
|
|
819
|
+
setStorage({
|
|
820
|
+
...getStorage(),
|
|
821
|
+
swapDetails: swap,
|
|
822
|
+
});
|
|
823
|
+
schedule(SwapActionTypes.CHECK_TRANSACTION_STATUS);
|
|
824
|
+
next();
|
|
825
|
+
onFinish();
|
|
826
|
+
},
|
|
827
|
+
|
|
828
|
+
(error) => {
|
|
829
|
+
if (swap.status === 'failed') return;
|
|
830
|
+
console.debug('error in approving', error);
|
|
831
|
+
const { extraMessage, extraMessageDetail, extraMessageErrorCode } =
|
|
832
|
+
prettifyErrorMessage(error);
|
|
833
|
+
if (
|
|
834
|
+
error &&
|
|
835
|
+
error?.root &&
|
|
836
|
+
error?.root?.message &&
|
|
837
|
+
error?.root?.code &&
|
|
838
|
+
error?.root?.reason
|
|
839
|
+
) {
|
|
840
|
+
logRPCError(error.root, swap, currentStep, sourceWallet?.walletType);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
const updateResult = updateSwapStatus({
|
|
844
|
+
getStorage,
|
|
845
|
+
setStorage,
|
|
846
|
+
nextStatus: 'failed',
|
|
847
|
+
nextStepStatus: 'failed',
|
|
848
|
+
message: extraMessage,
|
|
849
|
+
details: extraMessageDetail,
|
|
850
|
+
errorCode: extraMessageErrorCode,
|
|
851
|
+
});
|
|
852
|
+
notifier({
|
|
853
|
+
eventType: 'contract_rejected',
|
|
854
|
+
...updateResult,
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
failed();
|
|
858
|
+
onFinish();
|
|
859
|
+
}
|
|
860
|
+
);
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
const executeMessage = 'executing transaction';
|
|
865
|
+
const executeDetails = `${
|
|
866
|
+
sourceWallet.walletType === WalletType.WALLET_CONNECT
|
|
867
|
+
? 'Check your mobile phone'
|
|
868
|
+
: ''
|
|
869
|
+
}`;
|
|
870
|
+
|
|
871
|
+
if (!!transferTransaction) {
|
|
872
|
+
const updateResult = updateSwapStatus({
|
|
873
|
+
getStorage,
|
|
874
|
+
setStorage,
|
|
875
|
+
nextStepStatus: 'running',
|
|
876
|
+
message: executeMessage,
|
|
877
|
+
details: executeDetails,
|
|
878
|
+
});
|
|
879
|
+
notifier({
|
|
880
|
+
eventType: 'confirm_transfer',
|
|
881
|
+
...updateResult,
|
|
882
|
+
});
|
|
883
|
+
|
|
884
|
+
walletSigners.executeTransfer(transferTransaction, meta).then(
|
|
885
|
+
(txId) => {
|
|
886
|
+
setStepTransactionIds(actions, txId, 'transfer_confirmed', notifier);
|
|
887
|
+
schedule(SwapActionTypes.CHECK_TRANSACTION_STATUS);
|
|
888
|
+
next();
|
|
889
|
+
onFinish();
|
|
890
|
+
},
|
|
891
|
+
(error) => {
|
|
892
|
+
if (swap.status === 'failed') return;
|
|
893
|
+
const { extraMessage, extraMessageDetail, extraMessageErrorCode } =
|
|
894
|
+
prettifyErrorMessage(error);
|
|
895
|
+
const updateResult = updateSwapStatus({
|
|
896
|
+
getStorage,
|
|
897
|
+
setStorage,
|
|
898
|
+
nextStatus: 'failed',
|
|
899
|
+
nextStepStatus: 'failed',
|
|
900
|
+
message: extraMessage,
|
|
901
|
+
details: extraMessageDetail,
|
|
902
|
+
errorCode: extraMessageErrorCode,
|
|
903
|
+
});
|
|
904
|
+
notifier({
|
|
905
|
+
eventType: 'transfer_rejected',
|
|
906
|
+
...updateResult,
|
|
907
|
+
});
|
|
908
|
+
failed();
|
|
909
|
+
onFinish();
|
|
910
|
+
}
|
|
911
|
+
);
|
|
912
|
+
} else if (!!evmTransaction) {
|
|
913
|
+
const updateResult = updateSwapStatus({
|
|
914
|
+
getStorage,
|
|
915
|
+
setStorage,
|
|
916
|
+
nextStepStatus: 'running',
|
|
917
|
+
message: executeMessage,
|
|
918
|
+
details: executeDetails,
|
|
919
|
+
});
|
|
920
|
+
notifier({
|
|
921
|
+
eventType: 'calling_smart_contract',
|
|
922
|
+
...updateResult,
|
|
923
|
+
});
|
|
924
|
+
|
|
925
|
+
walletSigners.executeEvmTransaction(evmTransaction, meta).then(
|
|
926
|
+
(id) => {
|
|
927
|
+
setStepTransactionIds(actions, id, 'smart_contract_called', notifier);
|
|
928
|
+
schedule(SwapActionTypes.CHECK_TRANSACTION_STATUS);
|
|
929
|
+
next();
|
|
930
|
+
onFinish();
|
|
931
|
+
},
|
|
932
|
+
(error) => {
|
|
933
|
+
if (swap.status === 'failed') return;
|
|
934
|
+
const { extraMessage, extraMessageDetail, extraMessageErrorCode } =
|
|
935
|
+
prettifyErrorMessage(error);
|
|
936
|
+
if (
|
|
937
|
+
error &&
|
|
938
|
+
error?.root &&
|
|
939
|
+
error?.root?.message &&
|
|
940
|
+
error?.root?.code &&
|
|
941
|
+
error?.root?.reason
|
|
942
|
+
) {
|
|
943
|
+
logRPCError(error.root, swap, currentStep, sourceWallet?.walletType);
|
|
944
|
+
}
|
|
945
|
+
const updateResult = updateSwapStatus({
|
|
946
|
+
getStorage,
|
|
947
|
+
setStorage,
|
|
948
|
+
nextStatus: 'failed',
|
|
949
|
+
nextStepStatus: 'failed',
|
|
950
|
+
message: extraMessage,
|
|
951
|
+
details: extraMessageDetail,
|
|
952
|
+
errorCode: extraMessageErrorCode,
|
|
953
|
+
});
|
|
954
|
+
notifier({
|
|
955
|
+
eventType: 'smart_contract_call_failed',
|
|
956
|
+
...updateResult,
|
|
957
|
+
});
|
|
958
|
+
|
|
959
|
+
failed();
|
|
960
|
+
onFinish();
|
|
961
|
+
}
|
|
962
|
+
);
|
|
963
|
+
} else if (!!cosmosTransaction) {
|
|
964
|
+
const updateResult = updateSwapStatus({
|
|
965
|
+
getStorage,
|
|
966
|
+
setStorage,
|
|
967
|
+
nextStepStatus: 'running',
|
|
968
|
+
message: executeMessage,
|
|
969
|
+
details: executeDetails,
|
|
970
|
+
});
|
|
971
|
+
notifier({
|
|
972
|
+
eventType: 'calling_smart_contract',
|
|
973
|
+
...updateResult,
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
// If keplr wallet is executing contracts on terra, throw error. keplr doesn't support transfer or execute contracts. only IBC messages are supported
|
|
977
|
+
if (
|
|
978
|
+
(currentStep?.swapperId.toString() === 'TerraSwap' ||
|
|
979
|
+
(currentStep?.swapperId.toString() === 'ThorChain' &&
|
|
980
|
+
currentStep?.fromBlockchain === Network.TERRA) ||
|
|
981
|
+
(currentStep?.swapperId.toString() === 'Terra Bridge' &&
|
|
982
|
+
currentStep.fromBlockchain === Network.TERRA)) && // here we must allow ibc on terrastatus
|
|
983
|
+
sourceWallet.walletType === WalletType.KEPLR
|
|
984
|
+
) {
|
|
985
|
+
const { extraMessage, extraMessageDetail, extraMessageErrorCode } =
|
|
986
|
+
prettifyErrorMessage(
|
|
987
|
+
'Keplr only supports IBC Transactions on Terra. ' +
|
|
988
|
+
'Using Terra Bridge, TerraSwap and THORChain is not possible with Keplr. Please use TerraStation or Leap wallet'
|
|
989
|
+
);
|
|
990
|
+
const updateResult = updateSwapStatus({
|
|
991
|
+
getStorage,
|
|
992
|
+
setStorage,
|
|
993
|
+
nextStatus: 'failed',
|
|
994
|
+
nextStepStatus: 'failed',
|
|
995
|
+
message: extraMessage,
|
|
996
|
+
details: extraMessageDetail,
|
|
997
|
+
errorCode: extraMessageErrorCode,
|
|
998
|
+
});
|
|
999
|
+
notifier({
|
|
1000
|
+
eventType: 'smart_contract_call_failed',
|
|
1001
|
+
...updateResult,
|
|
1002
|
+
});
|
|
1003
|
+
failed();
|
|
1004
|
+
onFinish();
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
walletSigners.executeCosmosMessage(cosmosTransaction, meta).then(
|
|
1009
|
+
// todo
|
|
1010
|
+
(id: string | null) => {
|
|
1011
|
+
setStepTransactionIds(actions, id, 'smart_contract_called', notifier);
|
|
1012
|
+
schedule(SwapActionTypes.CHECK_TRANSACTION_STATUS);
|
|
1013
|
+
next();
|
|
1014
|
+
onFinish();
|
|
1015
|
+
},
|
|
1016
|
+
(error: string | null) => {
|
|
1017
|
+
if (swap.status === 'failed') return;
|
|
1018
|
+
const { extraMessage, extraMessageDetail, extraMessageErrorCode } =
|
|
1019
|
+
prettifyErrorMessage(error);
|
|
1020
|
+
const updateResult = updateSwapStatus({
|
|
1021
|
+
getStorage,
|
|
1022
|
+
setStorage,
|
|
1023
|
+
nextStatus: 'failed',
|
|
1024
|
+
nextStepStatus: 'failed',
|
|
1025
|
+
message: extraMessage,
|
|
1026
|
+
details: extraMessageDetail,
|
|
1027
|
+
errorCode: extraMessageErrorCode,
|
|
1028
|
+
});
|
|
1029
|
+
notifier({
|
|
1030
|
+
eventType: 'smart_contract_call_failed',
|
|
1031
|
+
...updateResult,
|
|
1032
|
+
});
|
|
1033
|
+
failed();
|
|
1034
|
+
onFinish();
|
|
1035
|
+
}
|
|
1036
|
+
);
|
|
1037
|
+
} else if (!!solanaTransaction) {
|
|
1038
|
+
const updateResult = updateSwapStatus({
|
|
1039
|
+
getStorage,
|
|
1040
|
+
setStorage,
|
|
1041
|
+
nextStepStatus: 'running',
|
|
1042
|
+
message: executeMessage,
|
|
1043
|
+
details: executeDetails,
|
|
1044
|
+
});
|
|
1045
|
+
notifier({
|
|
1046
|
+
eventType: 'calling_smart_contract',
|
|
1047
|
+
...updateResult,
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1050
|
+
const tx = solanaTransaction;
|
|
1051
|
+
walletSigners.executeSolanaTransaction(tx, swap.requestId).then(
|
|
1052
|
+
(txId) => {
|
|
1053
|
+
setStepTransactionIds(actions, txId, 'smart_contract_called', notifier);
|
|
1054
|
+
schedule(SwapActionTypes.CHECK_TRANSACTION_STATUS);
|
|
1055
|
+
next();
|
|
1056
|
+
onFinish();
|
|
1057
|
+
},
|
|
1058
|
+
(error) => {
|
|
1059
|
+
if (swap.status === 'failed') return;
|
|
1060
|
+
const { extraMessage, extraMessageDetail, extraMessageErrorCode } =
|
|
1061
|
+
prettifyErrorMessage(error);
|
|
1062
|
+
const updateResult = updateSwapStatus({
|
|
1063
|
+
getStorage,
|
|
1064
|
+
setStorage,
|
|
1065
|
+
nextStatus: 'failed',
|
|
1066
|
+
nextStepStatus: 'failed',
|
|
1067
|
+
message: extraMessage,
|
|
1068
|
+
details: extraMessageDetail,
|
|
1069
|
+
errorCode: extraMessageErrorCode,
|
|
1070
|
+
});
|
|
1071
|
+
notifier({
|
|
1072
|
+
eventType: 'smart_contract_call_failed',
|
|
1073
|
+
...updateResult,
|
|
1074
|
+
});
|
|
1075
|
+
failed();
|
|
1076
|
+
onFinish();
|
|
1077
|
+
}
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
export function checkWaitingForConnectWalletChange(params: {
|
|
1083
|
+
wallet_network: string;
|
|
1084
|
+
manager?: Manager;
|
|
1085
|
+
evmChains: EvmBlockchainMeta[];
|
|
1086
|
+
}): void {
|
|
1087
|
+
const { wallet_network, evmChains, manager } = params;
|
|
1088
|
+
const [wallet, network] = wallet_network.split('-');
|
|
1089
|
+
|
|
1090
|
+
// We only need change network for EVM chains.
|
|
1091
|
+
if (!evmChains.some((chain) => chain.name == network)) return;
|
|
1092
|
+
|
|
1093
|
+
manager?.getAll().forEach((q) => {
|
|
1094
|
+
const queueStorage = q.list.getStorage() as SwapStorage | undefined;
|
|
1095
|
+
const swap = queueStorage?.swapDetails;
|
|
1096
|
+
if (swap) {
|
|
1097
|
+
const currentStep = getCurrentStep(swap);
|
|
1098
|
+
if (currentStep) {
|
|
1099
|
+
const currentStepRequiredWallet =
|
|
1100
|
+
queueStorage?.swapDetails.wallets[currentStep.fromBlockchain]
|
|
1101
|
+
?.walletType;
|
|
1102
|
+
const hasWaitingForConnect = Object.keys(q.list.state.tasks).some(
|
|
1103
|
+
(taskId) => {
|
|
1104
|
+
const task = q.list.state.tasks[taskId];
|
|
1105
|
+
return (
|
|
1106
|
+
task.status === Status.BLOCKED &&
|
|
1107
|
+
[BlockReason.WAIT_FOR_CONNECT_WALLET].includes(
|
|
1108
|
+
task.blockedFor?.reason
|
|
1109
|
+
)
|
|
1110
|
+
);
|
|
1111
|
+
}
|
|
1112
|
+
);
|
|
1113
|
+
|
|
1114
|
+
if (currentStepRequiredWallet === wallet && hasWaitingForConnect) {
|
|
1115
|
+
const queueInstance = q.list;
|
|
1116
|
+
const swap = queueInstance.getStorage()
|
|
1117
|
+
?.swapDetails as SwapStorage['swapDetails'];
|
|
1118
|
+
const { type } = getRequiredWallet(swap);
|
|
1119
|
+
const description = ERROR_MESSAGE_WAIT_FOR_CHANGE_NETWORK(type);
|
|
1120
|
+
|
|
1121
|
+
q.list.block({
|
|
1122
|
+
reason: {
|
|
1123
|
+
reason: BlockReason.WAIT_FOR_NETWORK_CHANGE,
|
|
1124
|
+
description,
|
|
1125
|
+
},
|
|
1126
|
+
silent: true,
|
|
1127
|
+
});
|
|
1128
|
+
|
|
1129
|
+
markRunningSwapAsSwitchingNetwork({
|
|
1130
|
+
getStorage: queueInstance.getStorage.bind(queueInstance),
|
|
1131
|
+
setStorage: queueInstance.setStorage.bind(queueInstance),
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
export function checkWaitingForNetworkChange(manager?: Manager): void {
|
|
1140
|
+
manager?.getAll().forEach((q) => {
|
|
1141
|
+
const hasWaitingForNetwork = Object.keys(q.list.state.tasks).some(
|
|
1142
|
+
(taskId) => {
|
|
1143
|
+
const task = q.list.state.tasks[taskId];
|
|
1144
|
+
return (
|
|
1145
|
+
task.status === Status.BLOCKED &&
|
|
1146
|
+
[
|
|
1147
|
+
BlockReason.WAIT_FOR_NETWORK_CHANGE,
|
|
1148
|
+
BlockReason.DEPENDS_ON_OTHER_QUEUES,
|
|
1149
|
+
].includes(task.blockedFor?.reason)
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
);
|
|
1153
|
+
|
|
1154
|
+
if (hasWaitingForNetwork) {
|
|
1155
|
+
const swap = q.list.getStorage()
|
|
1156
|
+
?.swapDetails as SwapStorage['swapDetails'];
|
|
1157
|
+
const { type } = getRequiredWallet(swap);
|
|
1158
|
+
const description = ERROR_MESSAGE_WAIT_FOR_WALLET_DESCRIPTION(type);
|
|
1159
|
+
|
|
1160
|
+
// Change the block reason to waiting for connecting wallet
|
|
1161
|
+
q.list.block({
|
|
1162
|
+
reason: {
|
|
1163
|
+
reason: BlockReason.WAIT_FOR_CONNECT_WALLET,
|
|
1164
|
+
description,
|
|
1165
|
+
},
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
/**
|
|
1172
|
+
*
|
|
1173
|
+
* Try to run blocked tasks by wallet and network name.
|
|
1174
|
+
* Goes through queues and extract blocked queues with matched wallet.
|
|
1175
|
+
* If found any blocked tasks with same wallet and network, runs them.
|
|
1176
|
+
* If not, runs only blocked tasks with matched wallet.
|
|
1177
|
+
*
|
|
1178
|
+
* @param wallet_network a string includes `wallet` type and `network` type.
|
|
1179
|
+
* @param manager
|
|
1180
|
+
* @returns
|
|
1181
|
+
*/
|
|
1182
|
+
export function retryOn(
|
|
1183
|
+
wallet_network: string,
|
|
1184
|
+
manager?: Manager,
|
|
1185
|
+
options = { fallbackToOnlyWallet: true }
|
|
1186
|
+
): void {
|
|
1187
|
+
const [wallet, network] = wallet_network.split('-');
|
|
1188
|
+
if (!wallet || !network) {
|
|
1189
|
+
return;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
const walletAndNetworkMatched: QueueType[] = [];
|
|
1193
|
+
const onlyWalletMatched: QueueType[] = [];
|
|
1194
|
+
|
|
1195
|
+
manager?.getAll().forEach((q) => {
|
|
1196
|
+
// retry only on affected queues
|
|
1197
|
+
if (q.status === Status.BLOCKED) {
|
|
1198
|
+
const queueStorage = q.list.getStorage() as SwapStorage | undefined;
|
|
1199
|
+
const swap = queueStorage?.swapDetails;
|
|
1200
|
+
|
|
1201
|
+
if (swap) {
|
|
1202
|
+
const currentStep = getCurrentStep(swap);
|
|
1203
|
+
if (currentStep) {
|
|
1204
|
+
if (
|
|
1205
|
+
currentStep.fromBlockchain == network &&
|
|
1206
|
+
queueStorage?.swapDetails.wallets[network]?.walletType === wallet
|
|
1207
|
+
) {
|
|
1208
|
+
walletAndNetworkMatched.push(q.list);
|
|
1209
|
+
} else if (
|
|
1210
|
+
queueStorage?.swapDetails.wallets[currentStep.fromBlockchain]
|
|
1211
|
+
?.walletType === wallet
|
|
1212
|
+
) {
|
|
1213
|
+
onlyWalletMatched.push(q.list);
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
});
|
|
1219
|
+
|
|
1220
|
+
// const isWaitingForConnectWallet = (status: Status) =>
|
|
1221
|
+
let finalQueueToBeRun: QueueType | undefined = undefined;
|
|
1222
|
+
if (walletAndNetworkMatched.length > 0) {
|
|
1223
|
+
finalQueueToBeRun = walletAndNetworkMatched[0];
|
|
1224
|
+
|
|
1225
|
+
if (walletAndNetworkMatched.length > 1) {
|
|
1226
|
+
for (let i = 1; i < walletAndNetworkMatched.length; i++) {
|
|
1227
|
+
const currentQueue = walletAndNetworkMatched[i];
|
|
1228
|
+
|
|
1229
|
+
markRunningSwapAsDependsOnOtherQueues({
|
|
1230
|
+
getStorage: currentQueue.getStorage.bind(currentQueue),
|
|
1231
|
+
setStorage: currentQueue.setStorage.bind(currentQueue),
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
} else if (onlyWalletMatched.length > 0 && options.fallbackToOnlyWallet) {
|
|
1236
|
+
finalQueueToBeRun = onlyWalletMatched[0];
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
finalQueueToBeRun?.checkBlock();
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
/*
|
|
1243
|
+
For avoiding conflict by making too many requests to wallet, we need to make sure
|
|
1244
|
+
We only run one request at a time (In parallel mode).
|
|
1245
|
+
*/
|
|
1246
|
+
export function isNeedBlockQueueForParallel(step: PendingSwapStep): boolean {
|
|
1247
|
+
return (
|
|
1248
|
+
!!step.evmTransaction ||
|
|
1249
|
+
!!step.evmApprovalTransaction ||
|
|
1250
|
+
!!step.cosmosTransaction
|
|
1251
|
+
);
|
|
1252
|
+
}
|