@subwallet/extension-base 1.3.9-0 → 1.3.11-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/background/errors/SwapError.js +5 -0
  2. package/cjs/background/errors/SwapError.js +5 -0
  3. package/cjs/core/logic-validation/swap.js +27 -0
  4. package/cjs/packageInfo.js +1 -1
  5. package/cjs/services/chain-service/constants.js +2 -2
  6. package/cjs/services/chain-service/health-check/utils/new-utils/asset-asset-validate.js +1 -1
  7. package/cjs/services/chain-service/utils/patch.js +1 -1
  8. package/cjs/services/swap-service/handler/asset-hub/utils.js +1 -0
  9. package/cjs/services/swap-service/handler/chainflip-handler.js +0 -1
  10. package/cjs/services/swap-service/handler/simpleswap-handler.js +444 -0
  11. package/cjs/services/swap-service/index.js +15 -3
  12. package/cjs/services/swap-service/utils.js +13 -2
  13. package/cjs/services/transaction-service/index.js +14 -10
  14. package/cjs/services/transaction-service/utils.js +9 -1
  15. package/cjs/types/swap/index.js +7 -3
  16. package/cjs/utils/number.js +11 -6
  17. package/core/logic-validation/swap.d.ts +2 -1
  18. package/core/logic-validation/swap.js +26 -0
  19. package/koni/api/staking/bonding/utils.d.ts +1 -0
  20. package/package.json +23 -18
  21. package/packageInfo.js +1 -1
  22. package/services/chain-service/constants.js +2 -2
  23. package/services/chain-service/health-check/utils/new-utils/asset-asset-validate.js +1 -1
  24. package/services/chain-service/utils/patch.js +1 -1
  25. package/services/earning-service/handlers/native-staking/relay-chain.d.ts +1 -0
  26. package/services/earning-service/handlers/special.d.ts +1 -0
  27. package/services/earning-service/utils/index.d.ts +1 -0
  28. package/services/swap-service/handler/asset-hub/utils.js +1 -0
  29. package/services/swap-service/handler/chainflip-handler.js +0 -1
  30. package/services/swap-service/handler/simpleswap-handler.d.ts +24 -0
  31. package/services/swap-service/handler/simpleswap-handler.js +434 -0
  32. package/services/swap-service/index.d.ts +2 -1
  33. package/services/swap-service/index.js +13 -2
  34. package/services/swap-service/utils.d.ts +2 -0
  35. package/services/swap-service/utils.js +10 -1
  36. package/services/transaction-service/index.js +15 -11
  37. package/services/transaction-service/utils.d.ts +2 -1
  38. package/services/transaction-service/utils.js +9 -2
  39. package/types/balance/index.d.ts +1 -0
  40. package/types/swap/index.d.ts +15 -3
  41. package/types/swap/index.js +5 -2
  42. package/types/yield/info/pallet.d.ts +1 -0
  43. package/utils/index.d.ts +1 -0
  44. package/utils/number.d.ts +9 -1
  45. package/utils/number.js +10 -4
@@ -0,0 +1,434 @@
1
+ // Copyright 2019-2022 @subwallet/extension-base
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { SwapError } from '@subwallet/extension-base/background/errors/SwapError';
5
+ import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
6
+ import { ChainType, ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
7
+ import { _getSimpleSwapEarlyValidationError } from '@subwallet/extension-base/core/logic-validation/swap';
8
+ import { _getAssetDecimals, _getChainNativeTokenSlug, _getContractAddressOfToken, _isChainSubstrateCompatible, _isNativeToken } from '@subwallet/extension-base/services/chain-service/utils';
9
+ import { BasicTxErrorType, CommonStepType, SwapErrorType, SwapFeeType, SwapProviderId, SwapStepType } from '@subwallet/extension-base/types';
10
+ import { _reformatAddressWithChain, formatNumber } from '@subwallet/extension-base/utils';
11
+ import BigN, { BigNumber } from 'bignumber.js';
12
+ import { getERC20TransactionObject, getEVMTransactionObject } from "../../balance-service/transfer/smart-contract.js";
13
+ import { createTransferExtrinsic, getTransferMockTxFee } from "../../balance-service/transfer/token.js";
14
+ import { calculateSwapRate, SIMPLE_SWAP_SUPPORTED_TESTNET_ASSET_MAPPING, SWAP_QUOTE_TIMEOUT_MAP } from "../utils.js";
15
+ import { SwapBaseHandler } from "./base-handler.js";
16
+ const apiUrl = 'https://api.simpleswap.io';
17
+ export const simpleSwapApiKey = process.env.SIMPLE_SWAP_API_KEY || '';
18
+ const toBNString = (input, decimal) => {
19
+ const raw = new BigNumber(input);
20
+ return raw.shiftedBy(decimal).integerValue(BigNumber.ROUND_CEIL).toFixed();
21
+ };
22
+ const fetchSwapList = async params => {
23
+ const swapListParams = new URLSearchParams({
24
+ api_key: `${simpleSwapApiKey}`,
25
+ fixed: 'false',
26
+ symbol: params.fromSymbol
27
+ });
28
+ const response = await fetch(`${apiUrl}/get_pairs?${swapListParams.toString()}`, {
29
+ headers: {
30
+ accept: 'application/json'
31
+ }
32
+ });
33
+ return await response.json();
34
+ };
35
+ const fetchRanges = async params => {
36
+ const rangesParams = new URLSearchParams({
37
+ api_key: `${simpleSwapApiKey}`,
38
+ fixed: 'false',
39
+ currency_from: params.fromSymbol,
40
+ currency_to: params.toSymbol
41
+ });
42
+ const response = await fetch(`${apiUrl}/get_ranges?${rangesParams.toString()}`, {
43
+ headers: {
44
+ accept: 'application/json'
45
+ }
46
+ });
47
+ return await response.json();
48
+ };
49
+ async function getEstimate(request, fromAsset, toAsset) {
50
+ const fromSymbol = SIMPLE_SWAP_SUPPORTED_TESTNET_ASSET_MAPPING[fromAsset.slug];
51
+ const toSymbol = SIMPLE_SWAP_SUPPORTED_TESTNET_ASSET_MAPPING[toAsset.slug];
52
+ const assetDecimals = _getAssetDecimals(fromAsset);
53
+ if (!fromSymbol || !toSymbol) {
54
+ throw new SwapError(SwapErrorType.ASSET_NOT_SUPPORTED);
55
+ }
56
+ const formatedAmount = formatNumber(request.fromAmount, assetDecimals, s => s);
57
+ const params = new URLSearchParams({
58
+ api_key: `${simpleSwapApiKey}`,
59
+ fixed: 'false',
60
+ currency_from: fromSymbol,
61
+ currency_to: toSymbol,
62
+ amount: formatedAmount
63
+ });
64
+ try {
65
+ const response = await fetch(`${apiUrl}/get_estimated?${params.toString()}`, {
66
+ headers: {
67
+ accept: 'application/json'
68
+ }
69
+ });
70
+ if (!response.ok) {
71
+ throw new SwapError(SwapErrorType.ERROR_FETCHING_QUOTE);
72
+ }
73
+ const resToAmount = await response.json();
74
+ const toAmount = toBNString(resToAmount, _getAssetDecimals(toAsset));
75
+ const bnToAmount = new BigN(toAmount);
76
+ const walletFeeRate = 4 / 1000;
77
+ const toAmountBeforeFee = bnToAmount.dividedBy(new BigN(1 - walletFeeRate));
78
+ const walletFeeAmount = toAmountBeforeFee.multipliedBy(4).dividedBy(1000).toString();
79
+ return {
80
+ toAmount,
81
+ walletFeeAmount
82
+ };
83
+ } catch (err) {
84
+ console.error('Error:', err);
85
+ throw new SwapError(SwapErrorType.ERROR_FETCHING_QUOTE);
86
+ }
87
+ }
88
+ const createSwapRequest = async params => {
89
+ const fromDecimals = _getAssetDecimals(params.fromAsset);
90
+ const toDecimals = _getAssetDecimals(params.toAsset);
91
+ const formatedAmount = formatNumber(params.fromAmount, fromDecimals, s => s);
92
+ const requestBody = {
93
+ fixed: false,
94
+ currency_from: params.fromSymbol,
95
+ currency_to: params.toSymbol,
96
+ amount: formatedAmount,
97
+ // Convert to small number due to require of api
98
+ address_to: params.receiver,
99
+ extra_id_to: '',
100
+ user_refund_address: params.sender,
101
+ user_refund_extra_id: ''
102
+ };
103
+ const response = await fetch(`${apiUrl}/create_exchange?api_key=${simpleSwapApiKey}`, {
104
+ method: 'POST',
105
+ headers: {
106
+ 'Content-Type': 'application/json',
107
+ Accept: 'application/json'
108
+ },
109
+ body: JSON.stringify(requestBody)
110
+ });
111
+ const depositAddressResponse = await response.json();
112
+ return {
113
+ id: depositAddressResponse.id,
114
+ addressFrom: depositAddressResponse.address_from,
115
+ amountTo: toBNString(depositAddressResponse.amount_to, toDecimals)
116
+ };
117
+ };
118
+ export class SimpleSwapHandler {
119
+ constructor(chainService, balanceService) {
120
+ this.swapBaseHandler = new SwapBaseHandler({
121
+ chainService,
122
+ balanceService,
123
+ providerName: 'SimpleSwap',
124
+ providerSlug: SwapProviderId.SIMPLE_SWAP
125
+ });
126
+ this.providerSlug = SwapProviderId.SIMPLE_SWAP;
127
+ }
128
+ async validateSwapProcess(params) {
129
+ const amount = params.selectedQuote.fromAmount;
130
+ const bnAmount = BigInt(amount);
131
+ if (bnAmount <= BigInt(0)) {
132
+ return Promise.resolve([new TransactionError(BasicTxErrorType.INVALID_PARAMS, 'Amount must be greater than 0')]);
133
+ }
134
+ let isXcmOk = false;
135
+ for (const [index, step] of params.process.steps.entries()) {
136
+ const getErrors = async () => {
137
+ switch (step.type) {
138
+ case CommonStepType.DEFAULT:
139
+ return Promise.resolve([]);
140
+ case CommonStepType.TOKEN_APPROVAL:
141
+ return Promise.reject(new TransactionError(BasicTxErrorType.UNSUPPORTED));
142
+ default:
143
+ return this.swapBaseHandler.validateSwapStep(params, isXcmOk, index);
144
+ }
145
+ };
146
+ const errors = await getErrors();
147
+ if (errors.length) {
148
+ return errors;
149
+ } else if (step.type === CommonStepType.XCM) {
150
+ isXcmOk = true;
151
+ }
152
+ }
153
+ return [];
154
+ }
155
+ get chainService() {
156
+ return this.swapBaseHandler.chainService;
157
+ }
158
+ get balanceService() {
159
+ return this.swapBaseHandler.balanceService;
160
+ }
161
+ get providerInfo() {
162
+ return this.swapBaseHandler.providerInfo;
163
+ }
164
+ get name() {
165
+ return this.swapBaseHandler.name;
166
+ }
167
+ get slug() {
168
+ return this.swapBaseHandler.slug;
169
+ }
170
+ async getSwapQuote(request) {
171
+ try {
172
+ var _metadata$maxSwap;
173
+ const fromAsset = this.chainService.getAssetBySlug(request.pair.from);
174
+ const toAsset = this.chainService.getAssetBySlug(request.pair.to);
175
+ if (!fromAsset || !toAsset) {
176
+ return new SwapError(SwapErrorType.UNKNOWN);
177
+ }
178
+ const earlyValidation = await this.validateSwapRequest(request);
179
+ const metadata = earlyValidation.metadata;
180
+ if (earlyValidation.error) {
181
+ return _getSimpleSwapEarlyValidationError(earlyValidation.error, metadata);
182
+ }
183
+ const {
184
+ toAmount,
185
+ walletFeeAmount
186
+ } = await getEstimate(request, fromAsset, toAsset);
187
+ const fromAmount = request.fromAmount;
188
+ const rate = calculateSwapRate(request.fromAmount, toAmount, fromAsset, toAsset);
189
+ const fromChain = this.chainService.getChainInfoByKey(fromAsset.originChain);
190
+ const fromChainNativeTokenSlug = _getChainNativeTokenSlug(fromChain);
191
+ const defaultFeeToken = _isNativeToken(fromAsset) ? fromAsset.slug : fromChainNativeTokenSlug;
192
+ const chainType = _isChainSubstrateCompatible(fromChain) ? ChainType.SUBSTRATE : ChainType.EVM;
193
+ let api;
194
+ if (chainType === ChainType.SUBSTRATE) {
195
+ api = this.chainService.getSubstrateApi(fromChain.slug);
196
+ } else {
197
+ api = this.chainService.getEvmApi(fromChain.slug);
198
+ }
199
+ const networkFeeAmount = await getTransferMockTxFee(request.address, fromChain, fromAsset, api);
200
+ const networkFee = {
201
+ tokenSlug: fromChainNativeTokenSlug,
202
+ amount: networkFeeAmount.toString(),
203
+ feeType: SwapFeeType.NETWORK_FEE
204
+ };
205
+ const walletFee = {
206
+ tokenSlug: toAsset.slug,
207
+ amount: walletFeeAmount,
208
+ feeType: SwapFeeType.WALLET_FEE
209
+ };
210
+ return {
211
+ pair: request.pair,
212
+ fromAmount,
213
+ toAmount,
214
+ rate,
215
+ provider: this.providerInfo,
216
+ aliveUntil: +Date.now() + (SWAP_QUOTE_TIMEOUT_MAP[this.slug] || SWAP_QUOTE_TIMEOUT_MAP.default),
217
+ minSwap: toBNString(metadata.minSwap.value, _getAssetDecimals(fromAsset)),
218
+ maxSwap: toBNString((_metadata$maxSwap = metadata.maxSwap) === null || _metadata$maxSwap === void 0 ? void 0 : _metadata$maxSwap.value, _getAssetDecimals(fromAsset)),
219
+ estimatedArrivalTime: 3600,
220
+ isLowLiquidity: false,
221
+ feeInfo: {
222
+ feeComponent: [networkFee, walletFee],
223
+ defaultFeeToken,
224
+ feeOptions: [defaultFeeToken]
225
+ },
226
+ route: {
227
+ path: [fromAsset.slug, toAsset.slug]
228
+ }
229
+ };
230
+ } catch (e) {
231
+ return new SwapError(SwapErrorType.UNKNOWN);
232
+ }
233
+ }
234
+ generateOptimalProcess(params) {
235
+ return this.swapBaseHandler.generateOptimalProcess(params, [this.getSubmitStep]);
236
+ }
237
+ async getSubmitStep(params) {
238
+ if (params.selectedQuote) {
239
+ const submitStep = {
240
+ name: 'Swap',
241
+ type: SwapStepType.SWAP
242
+ };
243
+ return Promise.resolve([submitStep, params.selectedQuote.feeInfo]);
244
+ }
245
+ return Promise.resolve(undefined);
246
+ }
247
+ async validateSwapRequest(request) {
248
+ try {
249
+ const fromAsset = this.chainService.getAssetBySlug(request.pair.from);
250
+ const toAsset = this.chainService.getAssetBySlug(request.pair.to);
251
+ if (!fromAsset || !toAsset) {
252
+ return {
253
+ error: SwapErrorType.ERROR_FETCHING_QUOTE
254
+ };
255
+ }
256
+ const fromSymbol = SIMPLE_SWAP_SUPPORTED_TESTNET_ASSET_MAPPING[fromAsset.slug];
257
+ const toSymbol = SIMPLE_SWAP_SUPPORTED_TESTNET_ASSET_MAPPING[toAsset.slug];
258
+ if (!fromSymbol || !toSymbol) {
259
+ return {
260
+ error: SwapErrorType.ASSET_NOT_SUPPORTED
261
+ };
262
+ }
263
+ try {
264
+ const swapList = await fetchSwapList({
265
+ fromSymbol
266
+ });
267
+ if (!swapList.includes(toSymbol)) {
268
+ return {
269
+ error: SwapErrorType.ASSET_NOT_SUPPORTED
270
+ };
271
+ }
272
+ } catch (err) {
273
+ console.error('Error:', err);
274
+ }
275
+ const ranges = await fetchRanges({
276
+ fromSymbol,
277
+ toSymbol
278
+ });
279
+ const {
280
+ max,
281
+ min
282
+ } = ranges;
283
+ const bnMin = toBNString(min, _getAssetDecimals(fromAsset));
284
+ const bnAmount = BigInt(request.fromAmount);
285
+ if (bnAmount < BigInt(bnMin)) {
286
+ return {
287
+ error: SwapErrorType.NOT_MEET_MIN_SWAP,
288
+ metadata: {
289
+ minSwap: {
290
+ value: min,
291
+ symbol: fromAsset.symbol
292
+ },
293
+ maxSwap: max ? {
294
+ value: max,
295
+ symbol: fromAsset.symbol
296
+ } : undefined,
297
+ chain: this.chainService.getChainInfoByKey(fromAsset.originChain)
298
+ }
299
+ };
300
+ }
301
+ if (max && bnAmount > BigInt(toBNString(max, _getAssetDecimals(fromAsset)))) {
302
+ return {
303
+ error: SwapErrorType.SWAP_EXCEED_ALLOWANCE,
304
+ metadata: {
305
+ minSwap: {
306
+ value: min,
307
+ symbol: fromAsset.symbol
308
+ },
309
+ maxSwap: {
310
+ value: max,
311
+ symbol: fromAsset.symbol
312
+ },
313
+ chain: this.chainService.getChainInfoByKey(fromAsset.originChain)
314
+ }
315
+ };
316
+ }
317
+ return {
318
+ metadata: {
319
+ minSwap: {
320
+ value: min,
321
+ symbol: fromAsset.symbol
322
+ },
323
+ maxSwap: max ? {
324
+ value: max,
325
+ symbol: fromAsset.symbol
326
+ } : undefined,
327
+ chain: this.chainService.getChainInfoByKey(fromAsset.originChain)
328
+ }
329
+ };
330
+ } catch (e) {
331
+ console.error(e);
332
+ return {
333
+ error: SwapErrorType.UNKNOWN
334
+ };
335
+ }
336
+ }
337
+ async handleSwapProcess(params) {
338
+ const {
339
+ currentStep,
340
+ process
341
+ } = params;
342
+ const type = process.steps[currentStep].type;
343
+ switch (type) {
344
+ case CommonStepType.DEFAULT:
345
+ return Promise.reject(new TransactionError(BasicTxErrorType.UNSUPPORTED));
346
+ case SwapStepType.SWAP:
347
+ return this.handleSubmitStep(params);
348
+ default:
349
+ return this.handleSubmitStep(params);
350
+ }
351
+ }
352
+ async handleSubmitStep(params) {
353
+ const {
354
+ address,
355
+ quote,
356
+ recipient
357
+ } = params;
358
+ const pair = quote.pair;
359
+ const fromAsset = this.chainService.getAssetBySlug(pair.from);
360
+ const toAsset = this.chainService.getAssetBySlug(pair.to);
361
+ const chainInfo = this.chainService.getChainInfoByKey(fromAsset.originChain);
362
+ const toChainInfo = this.chainService.getChainInfoByKey(toAsset.originChain);
363
+ const chainType = _isChainSubstrateCompatible(chainInfo) ? ChainType.SUBSTRATE : ChainType.EVM;
364
+ const sender = _reformatAddressWithChain(address, chainInfo);
365
+ const receiver = _reformatAddressWithChain(recipient !== null && recipient !== void 0 ? recipient : sender, toChainInfo);
366
+ const fromSymbol = SIMPLE_SWAP_SUPPORTED_TESTNET_ASSET_MAPPING[fromAsset.slug];
367
+ const toSymbol = SIMPLE_SWAP_SUPPORTED_TESTNET_ASSET_MAPPING[toAsset.slug];
368
+ const {
369
+ fromAmount
370
+ } = quote;
371
+ const {
372
+ addressFrom,
373
+ amountTo,
374
+ id
375
+ } = await createSwapRequest({
376
+ fromSymbol,
377
+ toSymbol,
378
+ fromAmount,
379
+ fromAsset,
380
+ receiver,
381
+ sender,
382
+ toAsset
383
+ });
384
+
385
+ // Validate the amount to be swapped
386
+ const rate = BigN(amountTo).div(BigN(quote.toAmount)).multipliedBy(100);
387
+ if (rate.lt(95)) {
388
+ throw new SwapError(SwapErrorType.NOT_MEET_MIN_EXPECTED);
389
+ }
390
+
391
+ // Can modify quote.toAmount to amountTo after confirm real amount received
392
+
393
+ const txData = {
394
+ id: id,
395
+ address,
396
+ provider: this.providerInfo,
397
+ quote: params.quote,
398
+ slippage: params.slippage,
399
+ recipient: receiver,
400
+ process: params.process
401
+ };
402
+ let extrinsic;
403
+ if (chainType === ChainType.SUBSTRATE) {
404
+ const chainApi = this.chainService.getSubstrateApi(chainInfo.slug);
405
+ const substrateApi = await chainApi.isReady;
406
+ const [submittableExtrinsic] = await createTransferExtrinsic({
407
+ from: address,
408
+ networkKey: chainInfo.slug,
409
+ substrateApi,
410
+ to: addressFrom,
411
+ tokenInfo: fromAsset,
412
+ transferAll: false,
413
+ value: quote.fromAmount
414
+ });
415
+ extrinsic = submittableExtrinsic;
416
+ } else {
417
+ if (_isNativeToken(fromAsset)) {
418
+ const [transactionConfig] = await getEVMTransactionObject(chainInfo, address, addressFrom, quote.fromAmount, false, this.chainService.getEvmApi(chainInfo.slug));
419
+ extrinsic = transactionConfig;
420
+ } else {
421
+ const [transactionConfig] = await getERC20TransactionObject(_getContractAddressOfToken(fromAsset), chainInfo, address, addressFrom, quote.fromAmount, false, this.chainService.getEvmApi(chainInfo.slug));
422
+ extrinsic = transactionConfig;
423
+ }
424
+ }
425
+ return {
426
+ txChain: fromAsset.originChain,
427
+ txData,
428
+ extrinsic,
429
+ transferNativeAmount: _isNativeToken(fromAsset) ? quote.fromAmount : '0',
430
+ extrinsicType: ExtrinsicType.SWAP,
431
+ chainType
432
+ };
433
+ }
434
+ }
@@ -2,8 +2,9 @@ import { TransactionError } from '@subwallet/extension-base/background/errors/Tr
2
2
  import KoniState from '@subwallet/extension-base/koni/background/handlers/State';
3
3
  import { ServiceStatus, ServiceWithProcessInterface, StoppableServiceInterface } from '@subwallet/extension-base/services/base/types';
4
4
  import { CommonOptimalPath } from '@subwallet/extension-base/types/service-base';
5
- import { OptimalSwapPathParams, SwapPair, SwapQuoteResponse, SwapRequest, SwapRequestResult, SwapSubmitParams, SwapSubmitStepData, ValidateSwapProcessParams } from '@subwallet/extension-base/types/swap';
5
+ import { OptimalSwapPathParams, SwapPair, SwapProviderId, SwapQuoteResponse, SwapRequest, SwapRequestResult, SwapSubmitParams, SwapSubmitStepData, ValidateSwapProcessParams } from '@subwallet/extension-base/types/swap';
6
6
  import { PromiseHandler } from '@subwallet/extension-base/utils';
7
+ export declare const _isChainSupportedByProvider: (providerSlug: SwapProviderId, chain: string) => boolean;
7
8
  export declare class SwapService implements ServiceWithProcessInterface, StoppableServiceInterface {
8
9
  protected readonly state: KoniState;
9
10
  private eventService;
@@ -13,6 +13,11 @@ import { DEFAULT_FIRST_STEP, MOCK_STEP_FEE } from '@subwallet/extension-base/typ
13
13
  import { _SUPPORTED_SWAP_PROVIDERS, SwapErrorType, SwapProviderId, SwapStepType } from '@subwallet/extension-base/types/swap';
14
14
  import { createPromiseHandler } from '@subwallet/extension-base/utils';
15
15
  import { BehaviorSubject } from 'rxjs';
16
+ import { SimpleSwapHandler } from "./handler/simpleswap-handler.js";
17
+ export const _isChainSupportedByProvider = (providerSlug, chain) => {
18
+ const supportedChains = _PROVIDER_TO_SUPPORTED_PAIR_MAP[providerSlug];
19
+ return supportedChains ? supportedChains.includes(chain) : false;
20
+ };
16
21
  export class SwapService {
17
22
  swapPairSubject = new BehaviorSubject([]);
18
23
  handlers = {};
@@ -29,7 +34,7 @@ export class SwapService {
29
34
  const swappingSrcChain = this.chainService.getAssetBySlug(request.pair.from).originChain;
30
35
  await Promise.all(Object.values(this.handlers).map(async handler => {
31
36
  // temporary solution to reduce number of requests to providers, will work as long as there's only 1 provider for 1 chain
32
- if (!_PROVIDER_TO_SUPPORTED_PAIR_MAP[handler.providerSlug].includes(swappingSrcChain)) {
37
+ if (!_isChainSupportedByProvider(handler.providerSlug, swappingSrcChain)) {
33
38
  return;
34
39
  }
35
40
  if (handler.init && handler.isReady === false) {
@@ -116,7 +121,10 @@ export class SwapService {
116
121
  quoteError = (preferredErrorResp === null || preferredErrorResp === void 0 ? void 0 : preferredErrorResp.error) || (defaultErrorResp === null || defaultErrorResp === void 0 ? void 0 : defaultErrorResp.error);
117
122
  } else {
118
123
  var _selectedQuote;
119
- selectedQuote = availableQuotes[0];
124
+ selectedQuote = availableQuotes.find(quote => {
125
+ var _request$currentQuote;
126
+ return quote.provider.id === ((_request$currentQuote = request.currentQuote) === null || _request$currentQuote === void 0 ? void 0 : _request$currentQuote.id);
127
+ }) || availableQuotes[0];
120
128
  aliveUntil = ((_selectedQuote = selectedQuote) === null || _selectedQuote === void 0 ? void 0 : _selectedQuote.aliveUntil) || +Date.now() + SWAP_QUOTE_TIMEOUT_MAP.default;
121
129
  }
122
130
  return {
@@ -150,6 +158,9 @@ export class SwapService {
150
158
  case SwapProviderId.ROCOCO_ASSET_HUB:
151
159
  this.handlers[providerId] = new AssetHubSwapHandler(this.chainService, this.state.balanceService, 'rococo_assethub');
152
160
  break;
161
+ case SwapProviderId.SIMPLE_SWAP:
162
+ this.handlers[providerId] = new SimpleSwapHandler(this.chainService, this.state.balanceService);
163
+ break;
153
164
  default:
154
165
  throw new Error('Unsupported provider');
155
166
  }
@@ -3,10 +3,12 @@ import { _ChainAsset } from '@subwallet/chain-list/types';
3
3
  import { SwapPair } from '@subwallet/extension-base/types/swap';
4
4
  export declare const CHAIN_FLIP_TESTNET_EXPLORER = "https://blocks-perseverance.chainflip.io";
5
5
  export declare const CHAIN_FLIP_MAINNET_EXPLORER = "https://scan.chainflip.io";
6
+ export declare const SIMPLE_SWAP_EXPLORER = "https://simpleswap.io";
6
7
  export declare const CHAIN_FLIP_SUPPORTED_MAINNET_MAPPING: Record<string, Chain>;
7
8
  export declare const CHAIN_FLIP_SUPPORTED_TESTNET_MAPPING: Record<string, Chain>;
8
9
  export declare const CHAIN_FLIP_SUPPORTED_MAINNET_ASSET_MAPPING: Record<string, Asset>;
9
10
  export declare const CHAIN_FLIP_SUPPORTED_TESTNET_ASSET_MAPPING: Record<string, Asset>;
11
+ export declare const SIMPLE_SWAP_SUPPORTED_TESTNET_ASSET_MAPPING: Record<string, string>;
10
12
  export declare const SWAP_QUOTE_TIMEOUT_MAP: Record<string, number>;
11
13
  export declare const _PROVIDER_TO_SUPPORTED_PAIR_MAP: Record<string, string[]>;
12
14
  export declare function getSwapAlternativeAsset(swapPair: SwapPair): string | undefined;
@@ -9,6 +9,7 @@ import { SwapProviderId } from '@subwallet/extension-base/types/swap';
9
9
  import BigN from 'bignumber.js';
10
10
  export const CHAIN_FLIP_TESTNET_EXPLORER = 'https://blocks-perseverance.chainflip.io';
11
11
  export const CHAIN_FLIP_MAINNET_EXPLORER = 'https://scan.chainflip.io';
12
+ export const SIMPLE_SWAP_EXPLORER = 'https://simpleswap.io';
12
13
  export const CHAIN_FLIP_SUPPORTED_MAINNET_MAPPING = {
13
14
  [COMMON_CHAIN_SLUGS.POLKADOT]: Chains.Polkadot,
14
15
  [COMMON_CHAIN_SLUGS.ETHEREUM]: Chains.Ethereum,
@@ -29,6 +30,13 @@ export const CHAIN_FLIP_SUPPORTED_TESTNET_ASSET_MAPPING = {
29
30
  [COMMON_ASSETS.ETH_SEPOLIA]: Assets.ETH,
30
31
  [COMMON_ASSETS.USDC_SEPOLIA]: Assets.USDC
31
32
  };
33
+ export const SIMPLE_SWAP_SUPPORTED_TESTNET_ASSET_MAPPING = {
34
+ 'bittensor-NATIVE-TAO': 'tao',
35
+ [COMMON_ASSETS.ETH]: 'eth',
36
+ [COMMON_ASSETS.DOT]: 'dot',
37
+ [COMMON_ASSETS.USDC_ETHEREUM]: 'usdc',
38
+ [COMMON_ASSETS.USDT_ETHEREUM]: 'usdterc20'
39
+ };
32
40
  export const SWAP_QUOTE_TIMEOUT_MAP = {
33
41
  // in milliseconds
34
42
  default: 30000,
@@ -42,7 +50,8 @@ export const _PROVIDER_TO_SUPPORTED_PAIR_MAP = {
42
50
  [SwapProviderId.CHAIN_FLIP_TESTNET]: [COMMON_CHAIN_SLUGS.CHAINFLIP_POLKADOT, COMMON_CHAIN_SLUGS.ETHEREUM_SEPOLIA],
43
51
  [SwapProviderId.POLKADOT_ASSET_HUB]: [COMMON_CHAIN_SLUGS.POLKADOT_ASSET_HUB],
44
52
  [SwapProviderId.KUSAMA_ASSET_HUB]: [COMMON_CHAIN_SLUGS.KUSAMA_ASSET_HUB],
45
- [SwapProviderId.ROCOCO_ASSET_HUB]: [COMMON_CHAIN_SLUGS.ROCOCO_ASSET_HUB]
53
+ [SwapProviderId.ROCOCO_ASSET_HUB]: [COMMON_CHAIN_SLUGS.ROCOCO_ASSET_HUB],
54
+ [SwapProviderId.SIMPLE_SWAP]: ['bittensor', COMMON_CHAIN_SLUGS.ETHEREUM, COMMON_CHAIN_SLUGS.POLKADOT]
46
55
  };
47
56
  export function getSwapAlternativeAsset(swapPair) {
48
57
  var _swapPair$metadata;
@@ -15,7 +15,7 @@ import { getBaseTransactionInfo, getTransactionId, isSubstrateTransaction, isTon
15
15
  import { getExplorerLink, parseTransactionData } from '@subwallet/extension-base/services/transaction-service/utils';
16
16
  import { isWalletConnectRequest } from '@subwallet/extension-base/services/wallet-connect-service/helpers';
17
17
  import { BasicTxErrorType, YieldPoolType } from '@subwallet/extension-base/types';
18
- import { _isRuntimeUpdated, anyNumberToBN, pairToAccount, reformatAddress } from '@subwallet/extension-base/utils';
18
+ import { anyNumberToBN, pairToAccount, reformatAddress } from '@subwallet/extension-base/utils';
19
19
  import { mergeTransactionAndSignature } from '@subwallet/extension-base/utils/eth/mergeTransactionAndSignature';
20
20
  import { isContractAddress, parseContractInput } from '@subwallet/extension-base/utils/eth/parseTransaction';
21
21
  import { BN_ZERO } from '@subwallet/extension-base/utils/number';
@@ -1047,7 +1047,7 @@ export default class TransactionService {
1047
1047
  }
1048
1048
  return emitter;
1049
1049
  }
1050
- async signAndSendSubstrateTransaction({
1050
+ signAndSendSubstrateTransaction({
1051
1051
  address,
1052
1052
  chain,
1053
1053
  id,
@@ -1062,8 +1062,9 @@ export default class TransactionService {
1062
1062
  extrinsicHash: id
1063
1063
  };
1064
1064
  const extrinsic = transaction;
1065
- const registry = extrinsic.registry;
1066
- const signedExtensions = registry.signedExtensions;
1065
+ // const registry = extrinsic.registry;
1066
+ // const signedExtensions = registry.signedExtensions;
1067
+
1067
1068
  const signerOption = {
1068
1069
  signer: {
1069
1070
  signPayload: async payload => {
@@ -1080,13 +1081,16 @@ export default class TransactionService {
1080
1081
  },
1081
1082
  withSignedTransaction: true
1082
1083
  };
1083
- if (_isRuntimeUpdated(signedExtensions)) {
1084
- const metadataHash = await this.state.chainService.calculateMetadataHash(chain);
1085
- if (metadataHash) {
1086
- signerOption.mode = 1;
1087
- signerOption.metadataHash = metadataHash;
1088
- }
1089
- }
1084
+
1085
+ // if (_isRuntimeUpdated(signedExtensions)) {
1086
+ // const metadataHash = await this.state.chainService.calculateMetadataHash(chain);
1087
+ //
1088
+ // if (metadataHash) {
1089
+ // signerOption.mode = 1;
1090
+ // signerOption.metadataHash = metadataHash;
1091
+ // }
1092
+ // }
1093
+
1090
1094
  extrinsic.signAsync(address, signerOption).then(async rs => {
1091
1095
  // Emit signed event
1092
1096
  emitter.emit('signed', eventData);
@@ -1,6 +1,7 @@
1
1
  import { _ChainInfo } from '@subwallet/chain-list/types';
2
2
  import { ExtrinsicDataTypeMap, ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
3
- import { ChainflipSwapTxData } from '@subwallet/extension-base/types/swap';
3
+ import { ChainflipSwapTxData, SimpleSwapTxData } from '@subwallet/extension-base/types/swap';
4
4
  export declare function parseTransactionData<T extends ExtrinsicType>(data: unknown): ExtrinsicDataTypeMap[T];
5
5
  export declare function getExplorerLink(chainInfo: _ChainInfo, value: string, type: 'account' | 'tx'): string | undefined;
6
6
  export declare function getChainflipExplorerLink(data: ChainflipSwapTxData, chainInfo: _ChainInfo): string;
7
+ export declare function getSimpleSwapExplorerLink(data: SimpleSwapTxData): string;
@@ -2,7 +2,7 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import { _getBlockExplorerFromChain, _isChainTestNet, _isPureEvmChain } from '@subwallet/extension-base/services/chain-service/utils';
5
- import { CHAIN_FLIP_MAINNET_EXPLORER, CHAIN_FLIP_TESTNET_EXPLORER } from '@subwallet/extension-base/services/swap-service/utils';
5
+ import { CHAIN_FLIP_MAINNET_EXPLORER, CHAIN_FLIP_TESTNET_EXPLORER, SIMPLE_SWAP_EXPLORER } from '@subwallet/extension-base/services/swap-service/utils';
6
6
  // @ts-ignore
7
7
  export function parseTransactionData(data) {
8
8
  // @ts-ignore
@@ -46,7 +46,7 @@ function getBlockExplorerTxRoute(chainInfo) {
46
46
  if (['aventus', 'deeper_network'].includes(chainInfo.slug)) {
47
47
  return 'transaction';
48
48
  }
49
- if (['invarch'].includes(chainInfo.slug)) {
49
+ if (['invarch', 'tangle'].includes(chainInfo.slug)) {
50
50
  return '#/extrinsics';
51
51
  }
52
52
  return 'extrinsic';
@@ -62,6 +62,9 @@ export function getExplorerLink(chainInfo, value, type) {
62
62
  return undefined;
63
63
  }
64
64
  const route = getBlockExplorerTxRoute(chainInfo);
65
+ if (chainInfo.slug === 'tangle') {
66
+ return `${explorerLink}${explorerLink.endsWith('/') ? '' : '/'}extrinsic/${value}${route}/${value}`;
67
+ }
65
68
  return `${explorerLink}${explorerLink.endsWith('/') ? '' : '/'}${route}/${value}`;
66
69
  }
67
70
  return undefined;
@@ -69,4 +72,8 @@ export function getExplorerLink(chainInfo, value, type) {
69
72
  export function getChainflipExplorerLink(data, chainInfo) {
70
73
  const chainflipDomain = _isChainTestNet(chainInfo) ? CHAIN_FLIP_TESTNET_EXPLORER : CHAIN_FLIP_MAINNET_EXPLORER;
71
74
  return `${chainflipDomain}/channels/${data.depositChannelId}`;
75
+ }
76
+ export function getSimpleSwapExplorerLink(data) {
77
+ const simpleswapDomain = SIMPLE_SWAP_EXPLORER;
78
+ return `${simpleswapDomain}/exchange?id=${data.id}`;
72
79
  }
@@ -1,3 +1,4 @@
1
+ /// <reference types="bn.js" />
1
2
  import { _ChainAsset, _ChainInfo } from '@subwallet/chain-list/types';
2
3
  import { _BalanceMetadata, APIItemState, ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
3
4
  import { _EvmApi, _SubstrateApi, _TonApi } from '@subwallet/extension-base/services/chain-service/types';