@subwallet/extension-base 1.3.34-0 → 1.3.36-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 (49) hide show
  1. package/background/KoniTypes.d.ts +10 -0
  2. package/cjs/core/logic-validation/request.js +1 -1
  3. package/cjs/koni/background/handlers/Extension.js +32 -4
  4. package/cjs/koni/background/utils.js +64 -29
  5. package/cjs/packageInfo.js +1 -1
  6. package/cjs/services/balance-service/helpers/process.js +1 -5
  7. package/cjs/services/balance-service/index.js +20 -1
  8. package/cjs/services/balance-service/transfer/xcm/acrossBridge/index.js +91 -175
  9. package/cjs/services/balance-service/transfer/xcm/index.js +7 -6
  10. package/cjs/services/price-service/coingecko.js +22 -15
  11. package/cjs/services/price-service/index.js +12 -0
  12. package/cjs/services/request-service/handler/EvmRequestHandler.js +4 -1
  13. package/cjs/services/swap-service/handler/kyber-handler.js +355 -0
  14. package/cjs/services/swap-service/handler/uniswap-handler.js +223 -41
  15. package/cjs/services/swap-service/index.js +34 -2
  16. package/cjs/services/swap-service/utils.js +4 -1
  17. package/cjs/services/transaction-service/index.js +297 -52
  18. package/cjs/types/swap/index.js +2 -1
  19. package/core/logic-validation/request.js +1 -1
  20. package/koni/background/handlers/Extension.d.ts +1 -0
  21. package/koni/background/handlers/Extension.js +32 -4
  22. package/koni/background/utils.d.ts +3 -5
  23. package/koni/background/utils.js +64 -29
  24. package/package.json +11 -6
  25. package/packageInfo.js +1 -1
  26. package/services/balance-service/helpers/process.d.ts +2 -2
  27. package/services/balance-service/helpers/process.js +1 -5
  28. package/services/balance-service/index.js +21 -2
  29. package/services/balance-service/transfer/xcm/acrossBridge/index.d.ts +9 -8
  30. package/services/balance-service/transfer/xcm/acrossBridge/index.js +91 -174
  31. package/services/balance-service/transfer/xcm/index.js +7 -6
  32. package/services/event-service/types.d.ts +6 -6
  33. package/services/price-service/coingecko.d.ts +1 -1
  34. package/services/price-service/coingecko.js +22 -16
  35. package/services/price-service/index.d.ts +1 -0
  36. package/services/price-service/index.js +12 -0
  37. package/services/request-service/handler/EvmRequestHandler.js +4 -1
  38. package/services/swap-service/handler/kyber-handler.d.ts +28 -0
  39. package/services/swap-service/handler/kyber-handler.js +346 -0
  40. package/services/swap-service/handler/uniswap-handler.d.ts +48 -0
  41. package/services/swap-service/handler/uniswap-handler.js +224 -42
  42. package/services/swap-service/index.js +34 -2
  43. package/services/swap-service/utils.js +4 -1
  44. package/services/transaction-service/helpers/index.d.ts +5 -5
  45. package/services/transaction-service/index.d.ts +10 -5
  46. package/services/transaction-service/index.js +279 -36
  47. package/services/transaction-service/types.d.ts +23 -10
  48. package/types/swap/index.d.ts +4 -1
  49. package/types/swap/index.js +2 -1
@@ -25,8 +25,36 @@ const API_URL = 'https://trade-api.gateway.uniswap.org/v1';
25
25
  const headers = {
26
26
  'x-api-key': process.env.UNISWAP_API_KEY || ''
27
27
  };
28
- async function fetchCheckApproval(walletAddress, fromAmount, quote) {
29
- const chainId = quote.chainId;
28
+ async function fetchCheckApproval(request) {
29
+ const {
30
+ address,
31
+ amount,
32
+ classicQuote,
33
+ dutchQuote
34
+ } = request;
35
+ let chainId;
36
+ let tokenIn;
37
+ let tokenOut;
38
+ if (classicQuote) {
39
+ chainId = classicQuote.chainId;
40
+ tokenIn = classicQuote.input.token;
41
+ tokenOut = classicQuote.output.token;
42
+ } else if (dutchQuote) {
43
+ /**
44
+ * In Dutch order swap, the outputs field is an array instead of a single output since it might contain filler's output to pay fee.
45
+ * Need to filter exactly output that come to recipient address.
46
+ */
47
+ const output = dutchQuote.orderInfo.outputs.find(output => output.recipient.toLowerCase() === address.toLowerCase());
48
+ if (!output) {
49
+ return undefined; // todo: recheck
50
+ }
51
+
52
+ chainId = dutchQuote.orderInfo.chainId;
53
+ tokenIn = dutchQuote.orderInfo.input.token;
54
+ tokenOut = output.token;
55
+ } else {
56
+ return undefined;
57
+ }
30
58
  const response = await fetch(`${API_URL}/check_approval`, {
31
59
  method: 'POST',
32
60
  headers: {
@@ -34,16 +62,16 @@ async function fetchCheckApproval(walletAddress, fromAmount, quote) {
34
62
  'Content-Type': 'application/json'
35
63
  },
36
64
  body: JSON.stringify({
37
- walletAddress,
38
- amount: (0, _bignumber.default)(fromAmount).multipliedBy(2).toString(),
39
- token: quote.input.token,
65
+ walletAddress: address,
66
+ amount: (0, _bignumber.default)(amount).multipliedBy(2).toFixed(0),
67
+ token: tokenIn,
40
68
  chainId: chainId,
41
- tokenOut: quote.output.token,
42
- tokenOutChainId: chainId
69
+ tokenOut: tokenOut,
70
+ tokenOutChainId: chainId // swap in-chain
43
71
  })
44
72
  });
45
- const data = await response.json();
46
- return data;
73
+
74
+ return await response.json();
47
75
  }
48
76
  class UniswapHandler {
49
77
  constructor(chainService, balanceService, transactionService, feeService) {
@@ -103,11 +131,38 @@ class UniswapHandler {
103
131
  if (!selectedQuote) {
104
132
  return Promise.resolve(undefined);
105
133
  }
106
- const quoteMetadata = selectedQuote.metadata.quote;
107
- const sender = quoteMetadata.swapper;
108
- const sendingValue = quoteMetadata.input.amount;
134
+ const quoteMetadata = selectedQuote.metadata;
135
+ let sender;
136
+ let sendingValue;
137
+ let checkApprovalResponse;
138
+ if (quoteMetadata.routing === 'CLASSIC') {
139
+ const quote = quoteMetadata.quote;
140
+ sender = quote.swapper;
141
+ sendingValue = quote.input.amount;
142
+ checkApprovalResponse = await fetchCheckApproval({
143
+ address: sender,
144
+ amount: sendingValue,
145
+ classicQuote: quote
146
+ });
147
+ } else if (quoteMetadata.routing === 'DUTCH_LIMIT' || quoteMetadata.routing === 'DUTCH_V2') {
148
+ const quote = quoteMetadata.quote;
149
+ sender = quote.orderInfo.swapper;
150
+ sendingValue = quote.orderInfo.input.startAmount;
151
+ checkApprovalResponse = await fetchCheckApproval({
152
+ address: sender,
153
+ amount: sendingValue,
154
+ dutchQuote: quote
155
+ });
156
+ } else {
157
+ return undefined;
158
+ }
159
+ if (!checkApprovalResponse) {
160
+ return undefined;
161
+ }
109
162
  const fromTokenInfo = this.chainService.getAssetBySlug(selectedQuote.pair.from);
110
- const checkApprovalResponse = await fetchCheckApproval(sender, sendingValue, quoteMetadata);
163
+ const fromChainInfo = this.chainService.getChainInfoByKey((0, _utils2._getAssetOriginChain)(fromTokenInfo));
164
+ const evmApi = this.chainService.getEvmApi(fromChainInfo.slug);
165
+ const tokenContract = (0, _utils2._getContractAddressOfToken)(fromTokenInfo);
111
166
  const approval = checkApprovalResponse.approval;
112
167
  if (!approval) {
113
168
  return Promise.resolve(undefined);
@@ -119,27 +174,41 @@ class UniswapHandler {
119
174
  } catch (e) {
120
175
  // Empty
121
176
  }
177
+ const tx = await (0, _web.getERC20SpendingApprovalTx)(spender, sender, tokenContract, evmApi);
178
+ const evmFeeInfo = await this.feeService.subscribeChainFee((0, _getId.getId)(), fromTokenInfo.originChain, 'evm');
179
+ const estimatedFee = await (0, _web.estimateTxFee)(tx, evmApi, evmFeeInfo);
180
+ const nativeTokenSlug = (0, _utils2._getChainNativeTokenSlug)(fromChainInfo);
181
+ const feeInfo = {
182
+ feeComponent: [{
183
+ feeType: _types.SwapFeeType.NETWORK_FEE,
184
+ amount: estimatedFee,
185
+ tokenSlug: nativeTokenSlug
186
+ }],
187
+ defaultFeeToken: nativeTokenSlug,
188
+ feeOptions: [nativeTokenSlug]
189
+ };
122
190
  const submitStep = {
123
191
  name: 'Approve token for swap',
124
192
  type: _types.CommonStepType.TOKEN_APPROVAL,
125
193
  // @ts-ignore
126
194
  metadata: {
127
195
  tokenApprove: fromTokenInfo.slug,
128
- contractAddress: (0, _utils2._getContractAddressOfToken)(fromTokenInfo),
196
+ contractAddress: (0, _utils2._getContractAddressOfToken)(fromTokenInfo) || approval.to,
129
197
  spenderAddress: spender,
130
198
  owner: sender,
199
+ // todo: use approval.from?
131
200
  amount: sendingValue,
132
201
  isUniswapApprove: true
133
202
  }
134
203
  };
135
- return Promise.resolve([submitStep, selectedQuote.feeInfo]); // todo: wrong feeInfo, please check
204
+ return Promise.resolve([submitStep, feeInfo]);
136
205
  }
137
-
138
206
  async getApproveBridge(params) {
139
207
  const quote = params.selectedQuote;
140
208
  if (!quote) {
141
209
  return Promise.resolve(undefined);
142
210
  }
211
+ console.log('params', params);
143
212
  const sendingAmount = quote.toAmount;
144
213
  const senderAddress = params.request.address;
145
214
  const fromTokenInfo = this.chainService.getAssetBySlug(quote.pair.to);
@@ -147,13 +216,25 @@ class UniswapHandler {
147
216
  const fromChainId = (0, _utils2._getEvmChainId)(fromChainInfo);
148
217
  const evmApi = this.chainService.getEvmApi(fromChainInfo.slug);
149
218
  const tokenContract = (0, _utils2._getContractAddressOfToken)(fromTokenInfo);
219
+ const toTokenInfo = this.chainService.getAssetBySlug(params.request.pair.to);
220
+ const toChainInfo = this.chainService.getChainInfoByKey((0, _utils2._getAssetOriginChain)(toTokenInfo));
150
221
  if ((0, _utils2._isNativeToken)(fromTokenInfo)) {
151
222
  return Promise.resolve(undefined);
152
223
  }
153
224
  if (!fromChainId) {
154
225
  throw Error('Error getting Evm chain Id');
155
226
  }
156
- const spokePoolAddress = _acrossBridge.SpokePoolMapping[fromChainId].SpokePool.address;
227
+ const inputData = {
228
+ destinationTokenInfo: toTokenInfo,
229
+ originTokenInfo: fromTokenInfo,
230
+ sendingValue: sendingAmount,
231
+ sender: senderAddress,
232
+ recipient: senderAddress,
233
+ destinationChain: toChainInfo,
234
+ originChain: fromChainInfo
235
+ };
236
+ const acrossQuote = await (0, _acrossBridge.getAcrossQuote)(inputData);
237
+ const spokePoolAddress = acrossQuote.to;
157
238
  const allowance = await (0, _web.getERC20Allowance)(spokePoolAddress, senderAddress, tokenContract, evmApi);
158
239
  if (allowance && (0, _bignumber.default)(allowance).gt(sendingAmount)) {
159
240
  return Promise.resolve(undefined);
@@ -362,14 +443,38 @@ class UniswapHandler {
362
443
  if (!sender || !sendingValue) {
363
444
  throw new Error('Sender or value is not found');
364
445
  }
365
-
366
- // todo: move quote param to metadata;
367
-
368
- const {
369
- quote
370
- } = params.quote.metadata;
371
- const checkApprovalResponse = await fetchCheckApproval(sender, sendingValue, quote);
446
+ const quoteMetadata = params.quote.metadata;
447
+ let spenderAddress;
448
+ let contractAddress;
449
+ let chainId;
450
+ let checkApprovalResponse;
451
+ if (quoteMetadata.routing === 'CLASSIC') {
452
+ const quote = quoteMetadata.quote;
453
+ spenderAddress = quote.output.token;
454
+ contractAddress = quote.input.token;
455
+ chainId = quote.chainId.toString();
456
+ checkApprovalResponse = await fetchCheckApproval({
457
+ address: sender,
458
+ amount: sendingValue,
459
+ classicQuote: quote
460
+ });
461
+ } else if (quoteMetadata.routing === 'DUTCH_LIMIT' || quoteMetadata.routing === 'DUTCH_V2') {
462
+ const quote = quoteMetadata.quote;
463
+ spenderAddress = quote.orderInfo.outputs[0].token;
464
+ contractAddress = quote.orderInfo.input.token;
465
+ chainId = quote.orderInfo.chainId.toString();
466
+ checkApprovalResponse = await fetchCheckApproval({
467
+ address: sender,
468
+ amount: sendingValue,
469
+ dutchQuote: quote
470
+ });
471
+ } else {
472
+ throw Error('Unsupported quote route');
473
+ }
372
474
  let transactionConfig = {};
475
+ if (!checkApprovalResponse) {
476
+ throw new Error('Check approval fail');
477
+ }
373
478
  const approval = checkApprovalResponse.approval;
374
479
  if (approval) {
375
480
  var _priority$options, _priority$options$Fee, _priority$options2;
@@ -389,11 +494,11 @@ class UniswapHandler {
389
494
  }
390
495
  const chain = fromAsset.originChain;
391
496
  const _data = {
392
- spenderAddress: quote.output.token,
393
- contractAddress: quote.input.token,
497
+ spenderAddress,
498
+ contractAddress,
394
499
  amount: params.quote.fromAmount,
395
500
  owner: params.address,
396
- chain: quote.chainId.toString()
501
+ chain: chainId
397
502
  };
398
503
  return Promise.resolve({
399
504
  txChain: chain,
@@ -465,19 +570,93 @@ class UniswapHandler {
465
570
  const transactionResponse = await postTransactionResponse.json();
466
571
  extrinsic = transactionResponse.swap;
467
572
  } else if (routing === 'DUTCH_LIMIT' || routing === 'DUTCH_V2') {
468
- // todo: update condition and add handle exception
469
- // UniswapX, UniswapX_V2, and UniswapX_V3 are alternately referred to as DutchQuote, DutchQuoteV2, and DutchQuoteV3
470
- postTransactionResponse = await fetch(`${API_URL}/order`, {
471
- method: 'POST',
472
- headers: {
473
- ...headers,
474
- 'Content-Type': 'application/json'
573
+ const dutchQuote = quote;
574
+ const submitSwapOrder = async () => {
575
+ try {
576
+ const res = await fetch(`${API_URL}/order`, {
577
+ method: 'POST',
578
+ headers: {
579
+ ...headers,
580
+ 'Content-Type': 'application/json'
581
+ },
582
+ body: JSON.stringify({
583
+ signature: signature,
584
+ quote: dutchQuote
585
+ })
586
+ });
587
+ return res.ok;
588
+ } catch (e) {
589
+ console.log(e);
590
+ return false;
591
+ }
592
+ };
593
+ const retryGetUniswapTx = async (fn, options) => {
594
+ let lastError;
595
+ for (let i = 0; i < options.retries; i++) {
596
+ try {
597
+ const orderInfo = await fn();
598
+ if (orderInfo && orderInfo.orderStatus === 'filled') {
599
+ return orderInfo;
600
+ }
601
+ await new Promise(resolve => setTimeout(resolve, options.delay));
602
+ } catch (e) {
603
+ if (e instanceof Error) {
604
+ lastError = e;
605
+ }
606
+ await new Promise(resolve => setTimeout(resolve, options.delay));
607
+ }
608
+ }
609
+ console.error('UniswapX order timeout', lastError); // throw only last error, in case no successful result from fn()
610
+
611
+ return undefined;
612
+ };
613
+ const cronCheckTxSuccess = async () => {
614
+ const delay = 10000;
615
+ const retries = 50;
616
+ const orderId = dutchQuote.orderId;
617
+ const swapper = dutchQuote.orderInfo.swapper;
618
+ return retryGetUniswapTx(async () => {
619
+ try {
620
+ const response = await fetch(`${API_URL}/orders?orderId=${orderId}&swapper=${swapper}`, {
621
+ method: 'GET',
622
+ headers: {
623
+ ...headers,
624
+ 'Content-Type': 'application/json'
625
+ }
626
+ });
627
+ if (response.ok) {
628
+ const res = await response.json();
629
+ return res.orders.find(e => e.orderId === orderId && e.swapper === swapper.toLowerCase());
630
+ }
631
+ return undefined;
632
+ } catch (e) {
633
+ return undefined;
634
+ }
635
+ }, {
636
+ retries,
637
+ delay
638
+ });
639
+ };
640
+ const txData = {
641
+ address: params.address,
642
+ provider: this.providerInfo,
643
+ quote: params.quote,
644
+ slippage: params.slippage,
645
+ recipient: params.recipient,
646
+ process: params.process
647
+ };
648
+ return {
649
+ txChain: fromAsset.originChain,
650
+ txData,
651
+ extrinsic: {
652
+ submitSwapOrder,
653
+ cronCheckTxSuccess
475
654
  },
476
- body: JSON.stringify({
477
- signature: signature,
478
- quote: quote
479
- })
480
- });
655
+ transferNativeAmount: (0, _utils2._isNativeToken)(fromAsset) ? params.quote.fromAmount : '0',
656
+ extrinsicType: _KoniTypes.ExtrinsicType.SWAP,
657
+ chainType: _KoniTypes.ChainType.EVM,
658
+ isDutch: true
659
+ };
481
660
  }
482
661
  const txData = {
483
662
  address: params.address,
@@ -499,9 +678,12 @@ class UniswapHandler {
499
678
  handlePermitStep(params) {
500
679
  const fromAsset = this.chainService.getAssetBySlug(params.quote.pair.from);
501
680
  const {
502
- permitData
681
+ permitData,
682
+ routing
503
683
  } = params.quote.metadata;
504
684
  const processId = params.cacheProcessId;
685
+ const primaryType = routing === 'CLASSIC' ? 'PermitSingle' : routing === 'DUTCH_LIMIT' || routing === 'DUTCH_V2' ? 'PermitWitnessTransferFrom' : ''; // todo
686
+
505
687
  let validatePayload;
506
688
  if (permitData) {
507
689
  const payload = {
@@ -519,7 +701,7 @@ class UniswapHandler {
519
701
  ...permitData.types
520
702
  },
521
703
  domain: permitData.domain,
522
- primaryType: 'PermitSingle',
704
+ primaryType,
523
705
  message: permitData.values
524
706
  };
525
707
  validatePayload = (0, _logicValidation.validateTypedSignMessageDataV3V4)({
@@ -25,6 +25,7 @@ var _subwalletApiSdk = _interopRequireDefault(require("@subwallet/subwallet-api-
25
25
  var _bignumber = _interopRequireDefault(require("bignumber.js"));
26
26
  var _i18next = require("i18next");
27
27
  var _rxjs = require("rxjs");
28
+ var _kyberHandler = require("./handler/kyber-handler");
28
29
  var _simpleswapHandler = require("./handler/simpleswap-handler");
29
30
  var _uniswapHandler = require("./handler/uniswap-handler");
30
31
  // Copyright 2019-2022 @subwallet/extension-base
@@ -41,12 +42,21 @@ class SwapService {
41
42
  this.eventService = state.eventService;
42
43
  this.chainService = state.chainService;
43
44
  }
44
- async askProvidersForQuote(request) {
45
+ async askProvidersForQuote(_request) {
45
46
  var _subwalletApiSdk$swap;
46
47
  const availableQuotes = [];
48
+
49
+ // hotfix
50
+ const request = {
51
+ ..._request,
52
+ isSupportKyberVersion: true
53
+ };
47
54
  const quotes = await ((_subwalletApiSdk$swap = _subwalletApiSdk.default.swapApi) === null || _subwalletApiSdk$swap === void 0 ? void 0 : _subwalletApiSdk$swap.fetchSwapQuoteData(request));
48
55
  if (Array.isArray(quotes)) {
49
56
  quotes.forEach(quoteData => {
57
+ if (!_swap._SUPPORTED_SWAP_PROVIDERS.includes(quoteData.provider)) {
58
+ return;
59
+ }
50
60
  if (!quoteData.quote || Object.keys(quoteData.quote).length === 0) {
51
61
  return;
52
62
  }
@@ -120,6 +130,12 @@ class SwapService {
120
130
  console.group('Swap Logger');
121
131
  console.log('path', path);
122
132
  console.log('swapQuoteResponse', swapQuoteResponse);
133
+ if (swapQuoteResponse.optimalQuote && swapQuoteResponse.optimalQuote.metadata) {
134
+ const routing = swapQuoteResponse.optimalQuote.metadata.routing;
135
+ if (routing) {
136
+ console.log('Uniswap routing', routing);
137
+ }
138
+ }
123
139
  let optimalProcess;
124
140
  try {
125
141
  optimalProcess = await this.generateOptimalProcessV2({
@@ -130,6 +146,12 @@ class SwapService {
130
146
  } catch (e) {
131
147
  throw new Error(e.message);
132
148
  }
149
+ if (swapQuoteResponse.error) {
150
+ return {
151
+ process: optimalProcess,
152
+ quote: swapQuoteResponse
153
+ };
154
+ }
133
155
  console.log('optimalProcess', optimalProcess);
134
156
  console.groupEnd();
135
157
  if (JSON.stringify((0, _utils2.processStepsToPathActions)(optimalProcess.steps)) !== JSON.stringify(optimalProcess.path.map(e => e.action))) {
@@ -253,7 +275,14 @@ class SwapService {
253
275
  var _subwalletApiSdk$swap2;
254
276
  const availablePath = await ((_subwalletApiSdk$swap2 = _subwalletApiSdk.default.swapApi) === null || _subwalletApiSdk$swap2 === void 0 ? void 0 : _subwalletApiSdk$swap2.findAvailablePath(request));
255
277
  if (!availablePath) {
256
- throw Error('No available path');
278
+ return {
279
+ path: [],
280
+ swapQuoteResponse: {
281
+ quotes: [],
282
+ aliveUntil: Date.now() + _utils2.SWAP_QUOTE_TIMEOUT_MAP.error,
283
+ error: new _SwapError.SwapError(_swap.SwapErrorType.ERROR_FETCHING_QUOTE)
284
+ }
285
+ };
257
286
  }
258
287
  const {
259
288
  path
@@ -361,6 +390,9 @@ class SwapService {
361
390
  case _swap.SwapProviderId.UNISWAP:
362
391
  this.handlers[providerId] = new _uniswapHandler.UniswapHandler(this.chainService, this.state.balanceService, this.state.transactionService, this.state.feeService);
363
392
  break;
393
+ case _swap.SwapProviderId.KYBER:
394
+ this.handlers[providerId] = new _kyberHandler.KyberHandler(this.chainService, this.state.balanceService, this.state.transactionService, this.state.feeService);
395
+ break;
364
396
  default:
365
397
  throw new Error('Unsupported provider');
366
398
  }
@@ -54,8 +54,11 @@ const SWAP_QUOTE_TIMEOUT_MAP = {
54
54
  // in milliseconds
55
55
  default: 90000,
56
56
  [_swap.SwapProviderId.CHAIN_FLIP_TESTNET]: 30000,
57
- [_swap.SwapProviderId.CHAIN_FLIP_MAINNET]: 30000
57
+ [_swap.SwapProviderId.CHAIN_FLIP_MAINNET]: 30000,
58
+ error: 10000
58
59
  };
60
+
61
+ // deprecated
59
62
  exports.SWAP_QUOTE_TIMEOUT_MAP = SWAP_QUOTE_TIMEOUT_MAP;
60
63
  const _PROVIDER_TO_SUPPORTED_PAIR_MAP = {
61
64
  [_swap.SwapProviderId.HYDRADX_MAINNET]: [_chainList.COMMON_CHAIN_SLUGS.HYDRADX],