@subwallet/extension-base 1.3.35-0 → 1.3.37-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 (39) hide show
  1. package/background/KoniTypes.d.ts +13 -0
  2. package/background/types.d.ts +2 -1
  3. package/cjs/core/logic-validation/request.js +13 -1
  4. package/cjs/koni/background/handlers/Extension.js +189 -108
  5. package/cjs/koni/background/handlers/State.js +14 -9
  6. package/cjs/packageInfo.js +1 -1
  7. package/cjs/services/request-service/handler/AuthRequestHandler.js +4 -1
  8. package/cjs/services/request-service/handler/EvmRequestHandler.js +4 -1
  9. package/cjs/services/swap-service/handler/kyber-handler.js +355 -0
  10. package/cjs/services/swap-service/handler/uniswap-handler.js +209 -40
  11. package/cjs/services/swap-service/index.js +28 -2
  12. package/cjs/services/swap-service/utils.js +4 -1
  13. package/cjs/services/transaction-service/index.js +252 -28
  14. package/cjs/types/swap/index.js +2 -1
  15. package/cjs/utils/cardano.js +10 -2
  16. package/core/logic-validation/request.js +13 -1
  17. package/koni/background/handlers/Extension.d.ts +1 -0
  18. package/koni/background/handlers/Extension.js +82 -2
  19. package/koni/background/handlers/State.js +15 -10
  20. package/package.json +14 -9
  21. package/packageInfo.js +1 -1
  22. package/services/chain-service/types.d.ts +1 -1
  23. package/services/event-service/types.d.ts +6 -6
  24. package/services/request-service/handler/AuthRequestHandler.js +4 -1
  25. package/services/request-service/handler/EvmRequestHandler.js +4 -1
  26. package/services/swap-service/handler/kyber-handler.d.ts +28 -0
  27. package/services/swap-service/handler/kyber-handler.js +346 -0
  28. package/services/swap-service/handler/uniswap-handler.d.ts +48 -0
  29. package/services/swap-service/handler/uniswap-handler.js +209 -40
  30. package/services/swap-service/index.js +28 -2
  31. package/services/swap-service/utils.js +4 -1
  32. package/services/transaction-service/helpers/index.d.ts +5 -5
  33. package/services/transaction-service/index.d.ts +10 -5
  34. package/services/transaction-service/index.js +234 -12
  35. package/services/transaction-service/types.d.ts +23 -10
  36. package/types/swap/index.d.ts +4 -1
  37. package/types/swap/index.js +2 -1
  38. package/utils/cardano.d.ts +2 -0
  39. package/utils/cardano.js +7 -0
@@ -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,22 +174,35 @@ 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) {
@@ -375,14 +443,38 @@ class UniswapHandler {
375
443
  if (!sender || !sendingValue) {
376
444
  throw new Error('Sender or value is not found');
377
445
  }
378
-
379
- // todo: move quote param to metadata;
380
-
381
- const {
382
- quote
383
- } = params.quote.metadata;
384
- 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
+ }
385
474
  let transactionConfig = {};
475
+ if (!checkApprovalResponse) {
476
+ throw new Error('Check approval fail');
477
+ }
386
478
  const approval = checkApprovalResponse.approval;
387
479
  if (approval) {
388
480
  var _priority$options, _priority$options$Fee, _priority$options2;
@@ -402,11 +494,11 @@ class UniswapHandler {
402
494
  }
403
495
  const chain = fromAsset.originChain;
404
496
  const _data = {
405
- spenderAddress: quote.output.token,
406
- contractAddress: quote.input.token,
497
+ spenderAddress,
498
+ contractAddress,
407
499
  amount: params.quote.fromAmount,
408
500
  owner: params.address,
409
- chain: quote.chainId.toString()
501
+ chain: chainId
410
502
  };
411
503
  return Promise.resolve({
412
504
  txChain: chain,
@@ -478,19 +570,93 @@ class UniswapHandler {
478
570
  const transactionResponse = await postTransactionResponse.json();
479
571
  extrinsic = transactionResponse.swap;
480
572
  } else if (routing === 'DUTCH_LIMIT' || routing === 'DUTCH_V2') {
481
- // todo: update condition and add handle exception
482
- // UniswapX, UniswapX_V2, and UniswapX_V3 are alternately referred to as DutchQuote, DutchQuoteV2, and DutchQuoteV3
483
- postTransactionResponse = await fetch(`${API_URL}/order`, {
484
- method: 'POST',
485
- headers: {
486
- ...headers,
487
- '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
488
654
  },
489
- body: JSON.stringify({
490
- signature: signature,
491
- quote: quote
492
- })
493
- });
655
+ transferNativeAmount: (0, _utils2._isNativeToken)(fromAsset) ? params.quote.fromAmount : '0',
656
+ extrinsicType: _KoniTypes.ExtrinsicType.SWAP,
657
+ chainType: _KoniTypes.ChainType.EVM,
658
+ isDutch: true
659
+ };
494
660
  }
495
661
  const txData = {
496
662
  address: params.address,
@@ -512,9 +678,12 @@ class UniswapHandler {
512
678
  handlePermitStep(params) {
513
679
  const fromAsset = this.chainService.getAssetBySlug(params.quote.pair.from);
514
680
  const {
515
- permitData
681
+ permitData,
682
+ routing
516
683
  } = params.quote.metadata;
517
684
  const processId = params.cacheProcessId;
685
+ const primaryType = routing === 'CLASSIC' ? 'PermitSingle' : routing === 'DUTCH_LIMIT' || routing === 'DUTCH_V2' ? 'PermitWitnessTransferFrom' : ''; // todo
686
+
518
687
  let validatePayload;
519
688
  if (permitData) {
520
689
  const payload = {
@@ -532,7 +701,7 @@ class UniswapHandler {
532
701
  ...permitData.types
533
702
  },
534
703
  domain: permitData.domain,
535
- primaryType: 'PermitSingle',
704
+ primaryType,
536
705
  message: permitData.values
537
706
  };
538
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({
@@ -259,7 +275,14 @@ class SwapService {
259
275
  var _subwalletApiSdk$swap2;
260
276
  const availablePath = await ((_subwalletApiSdk$swap2 = _subwalletApiSdk.default.swapApi) === null || _subwalletApiSdk$swap2 === void 0 ? void 0 : _subwalletApiSdk$swap2.findAvailablePath(request));
261
277
  if (!availablePath) {
262
- 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
+ };
263
286
  }
264
287
  const {
265
288
  path
@@ -367,6 +390,9 @@ class SwapService {
367
390
  case _swap.SwapProviderId.UNISWAP:
368
391
  this.handlers[providerId] = new _uniswapHandler.UniswapHandler(this.chainService, this.state.balanceService, this.state.transactionService, this.state.feeService);
369
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;
370
396
  default:
371
397
  throw new Error('Unsupported provider');
372
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],