@subwallet/extension-base 1.3.10-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 (32) 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/utils/patch.js +1 -1
  6. package/cjs/services/swap-service/handler/chainflip-handler.js +0 -1
  7. package/cjs/services/swap-service/handler/simpleswap-handler.js +444 -0
  8. package/cjs/services/swap-service/index.js +15 -3
  9. package/cjs/services/swap-service/utils.js +13 -2
  10. package/cjs/services/transaction-service/index.js +14 -10
  11. package/cjs/services/transaction-service/utils.js +9 -1
  12. package/cjs/types/swap/index.js +7 -3
  13. package/cjs/utils/number.js +11 -6
  14. package/core/logic-validation/swap.d.ts +2 -1
  15. package/core/logic-validation/swap.js +26 -0
  16. package/package.json +11 -6
  17. package/packageInfo.js +1 -1
  18. package/services/chain-service/utils/patch.js +1 -1
  19. package/services/swap-service/handler/chainflip-handler.js +0 -1
  20. package/services/swap-service/handler/simpleswap-handler.d.ts +24 -0
  21. package/services/swap-service/handler/simpleswap-handler.js +434 -0
  22. package/services/swap-service/index.d.ts +2 -1
  23. package/services/swap-service/index.js +13 -2
  24. package/services/swap-service/utils.d.ts +2 -0
  25. package/services/swap-service/utils.js +10 -1
  26. package/services/transaction-service/index.js +15 -11
  27. package/services/transaction-service/utils.d.ts +2 -1
  28. package/services/transaction-service/utils.js +9 -2
  29. package/types/swap/index.d.ts +15 -3
  30. package/types/swap/index.js +5 -2
  31. package/utils/number.d.ts +9 -1
  32. package/utils/number.js +10 -4
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.getChainflipExplorerLink = getChainflipExplorerLink;
7
7
  exports.getExplorerLink = getExplorerLink;
8
+ exports.getSimpleSwapExplorerLink = getSimpleSwapExplorerLink;
8
9
  exports.parseTransactionData = parseTransactionData;
9
10
  var _utils = require("@subwallet/extension-base/services/chain-service/utils");
10
11
  var _utils2 = require("@subwallet/extension-base/services/swap-service/utils");
@@ -54,7 +55,7 @@ function getBlockExplorerTxRoute(chainInfo) {
54
55
  if (['aventus', 'deeper_network'].includes(chainInfo.slug)) {
55
56
  return 'transaction';
56
57
  }
57
- if (['invarch'].includes(chainInfo.slug)) {
58
+ if (['invarch', 'tangle'].includes(chainInfo.slug)) {
58
59
  return '#/extrinsics';
59
60
  }
60
61
  return 'extrinsic';
@@ -70,6 +71,9 @@ function getExplorerLink(chainInfo, value, type) {
70
71
  return undefined;
71
72
  }
72
73
  const route = getBlockExplorerTxRoute(chainInfo);
74
+ if (chainInfo.slug === 'tangle') {
75
+ return `${explorerLink}${explorerLink.endsWith('/') ? '' : '/'}extrinsic/${value}${route}/${value}`;
76
+ }
73
77
  return `${explorerLink}${explorerLink.endsWith('/') ? '' : '/'}${route}/${value}`;
74
78
  }
75
79
  return undefined;
@@ -77,4 +81,8 @@ function getExplorerLink(chainInfo, value, type) {
77
81
  function getChainflipExplorerLink(data, chainInfo) {
78
82
  const chainflipDomain = (0, _utils._isChainTestNet)(chainInfo) ? _utils2.CHAIN_FLIP_TESTNET_EXPLORER : _utils2.CHAIN_FLIP_MAINNET_EXPLORER;
79
83
  return `${chainflipDomain}/channels/${data.depositChannelId}`;
84
+ }
85
+ function getSimpleSwapExplorerLink(data) {
86
+ const simpleswapDomain = _utils2.SIMPLE_SWAP_EXPLORER;
87
+ return `${simpleswapDomain}/exchange?id=${data.id}`;
80
88
  }
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports._SUPPORTED_SWAP_PROVIDERS = exports.SwapStepType = exports.SwapProviderId = exports.SwapFeeType = exports.SwapErrorType = exports.CHAINFLIP_SLIPPAGE = void 0;
6
+ exports._SUPPORTED_SWAP_PROVIDERS = exports.SwapStepType = exports.SwapProviderId = exports.SwapFeeType = exports.SwapErrorType = exports.SIMPLE_SWAP_SLIPPAGE = exports.CHAINFLIP_SLIPPAGE = void 0;
7
7
  // Copyright 2019-2022 @subwallet/extension-base
8
8
  // SPDX-License-Identifier: Apache-2.0
9
9
  // core
@@ -21,6 +21,7 @@ exports.SwapErrorType = SwapErrorType;
21
21
  SwapErrorType["NOT_ENOUGH_LIQUIDITY"] = "NOT_ENOUGH_LIQUIDITY";
22
22
  SwapErrorType["MAKE_POOL_NOT_ENOUGH_EXISTENTIAL_DEPOSIT"] = "MAKE_POOL_NOT_ENOUGH_EXISTENTIAL_DEPOSIT";
23
23
  SwapErrorType["AMOUNT_CANNOT_BE_ZERO"] = "AMOUNT_CANNOT_BE_ZERO";
24
+ SwapErrorType["NOT_MEET_MIN_EXPECTED"] = "NOT_MEET_MIN_EXPECTED";
24
25
  })(SwapErrorType || (exports.SwapErrorType = SwapErrorType = {}));
25
26
  let SwapStepType;
26
27
  exports.SwapStepType = SwapStepType;
@@ -37,8 +38,9 @@ exports.SwapProviderId = SwapProviderId;
37
38
  SwapProviderId["POLKADOT_ASSET_HUB"] = "POLKADOT_ASSET_HUB";
38
39
  SwapProviderId["KUSAMA_ASSET_HUB"] = "KUSAMA_ASSET_HUB";
39
40
  SwapProviderId["ROCOCO_ASSET_HUB"] = "ROCOCO_ASSET_HUB";
41
+ SwapProviderId["SIMPLE_SWAP"] = "SIMPLE_SWAP";
40
42
  })(SwapProviderId || (exports.SwapProviderId = SwapProviderId = {}));
41
- const _SUPPORTED_SWAP_PROVIDERS = [SwapProviderId.CHAIN_FLIP_TESTNET, SwapProviderId.CHAIN_FLIP_MAINNET, SwapProviderId.HYDRADX_MAINNET, SwapProviderId.HYDRADX_TESTNET, SwapProviderId.POLKADOT_ASSET_HUB, SwapProviderId.KUSAMA_ASSET_HUB, SwapProviderId.ROCOCO_ASSET_HUB];
43
+ const _SUPPORTED_SWAP_PROVIDERS = [SwapProviderId.CHAIN_FLIP_TESTNET, SwapProviderId.CHAIN_FLIP_MAINNET, SwapProviderId.HYDRADX_MAINNET, SwapProviderId.HYDRADX_TESTNET, SwapProviderId.POLKADOT_ASSET_HUB, SwapProviderId.KUSAMA_ASSET_HUB, SwapProviderId.ROCOCO_ASSET_HUB, SwapProviderId.SIMPLE_SWAP];
42
44
  exports._SUPPORTED_SWAP_PROVIDERS = _SUPPORTED_SWAP_PROVIDERS;
43
45
  // process handling
44
46
  let SwapFeeType;
@@ -49,4 +51,6 @@ exports.SwapFeeType = SwapFeeType;
49
51
  SwapFeeType["WALLET_FEE"] = "WALLET_FEE";
50
52
  })(SwapFeeType || (exports.SwapFeeType = SwapFeeType = {}));
51
53
  const CHAINFLIP_SLIPPAGE = 0.02; // Example: 0.01 for 1%
52
- exports.CHAINFLIP_SLIPPAGE = CHAINFLIP_SLIPPAGE;
54
+ exports.CHAINFLIP_SLIPPAGE = CHAINFLIP_SLIPPAGE;
55
+ const SIMPLE_SWAP_SLIPPAGE = 0.05;
56
+ exports.SIMPLE_SWAP_SLIPPAGE = SIMPLE_SWAP_SLIPPAGE;
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.toBNString = exports.formatNumber = exports.balanceNoPrefixFormater = exports.balanceFormatter = exports.PREDEFINED_FORMATTER = exports.BN_ZERO = exports.BN_WEI = exports.BN_TEN = exports.BN_ONE = void 0;
7
+ exports.formatNumber = exports.balanceNoPrefixFormater = exports.balanceFormatter = exports.PREDEFINED_FORMATTER = exports.BN_ZERO = exports.BN_WEI = exports.BN_TEN = exports.BN_ONE = void 0;
8
8
  var _bignumber = _interopRequireDefault(require("bignumber.js"));
9
9
  // Copyright 2019-2022 @subwallet/extension-koni authors & contributors
10
10
  // SPDX-License-Identifier: Apache-2.0
@@ -195,12 +195,17 @@ exports.balanceNoPrefixFormater = balanceNoPrefixFormater;
195
195
  const PREDEFINED_FORMATTER = {
196
196
  balance: balanceFormatter
197
197
  };
198
+
199
+ /** @function formatNumber
200
+ * Convert number to a formatted string by dividing by 10^decimal
201
+ * @param {string | number | BigNumber} input - Input number
202
+ * @param {number} decimal - Decimal number
203
+ * @param {NumberFormatter} [formatter] - Formatter function
204
+ * - Default: balanceFormatter: With number > 1, show decimal with 2 numbers,
205
+ * with number < 1, show decimal with 6 (default) number
206
+ * @param {Record<string, number>} [metadata] - Metadata for formatter
207
+ */
198
208
  exports.PREDEFINED_FORMATTER = PREDEFINED_FORMATTER;
199
- const toBNString = (input, decimal) => {
200
- const raw = new _bignumber.default(input);
201
- return raw.multipliedBy(BN_TEN.pow(decimal)).toFixed();
202
- };
203
- exports.toBNString = toBNString;
204
209
  const formatNumber = function (input, decimal) {
205
210
  let formatter = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : balanceFormatter;
206
211
  let metadata = arguments.length > 3 ? arguments[3] : undefined;
@@ -1,10 +1,11 @@
1
1
  import { _ChainAsset, _ChainInfo } from '@subwallet/chain-list/types';
2
2
  import { SwapError } from '@subwallet/extension-base/background/errors/SwapError';
3
3
  import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
4
- import { AssetHubPreValidationMetadata, ChainflipPreValidationMetadata, HydradxPreValidationMetadata, SwapErrorType } from '@subwallet/extension-base/types/swap';
4
+ import { AssetHubPreValidationMetadata, ChainflipPreValidationMetadata, HydradxPreValidationMetadata, SimpleSwapValidationMetadata, SwapErrorType } from '@subwallet/extension-base/types/swap';
5
5
  export declare function _validateBalanceToSwapOnAssetHub(fromToken: _ChainAsset, feeToken: _ChainAsset, feeTokenChainInfo: _ChainInfo, feeAmount: string, fromTokenBalance: string, feeTokenBalance: string, swapAmount: string, isXcmOk: boolean, minSwap?: string): TransactionError | undefined;
6
6
  export declare function _validateBalanceToSwap(fromToken: _ChainAsset, feeToken: _ChainAsset, feeTokenChainInfo: _ChainInfo, feeAmount: string, fromTokenBalance: string, feeTokenBalance: string, swapAmount: string, isXcmOk: boolean, minSwap?: string): TransactionError | undefined;
7
7
  export declare function _validateSwapRecipient(destChainInfo: _ChainInfo, recipient: string): TransactionError | undefined;
8
8
  export declare function _getChainflipEarlyValidationError(error: SwapErrorType, metadata: ChainflipPreValidationMetadata): SwapError;
9
9
  export declare function _getEarlyHydradxValidationError(error: SwapErrorType, metadata: HydradxPreValidationMetadata): SwapError;
10
10
  export declare function _getEarlyAssetHubValidationError(error: SwapErrorType, metadata: AssetHubPreValidationMetadata): SwapError;
11
+ export declare function _getSimpleSwapEarlyValidationError(error: SwapErrorType, metadata: SimpleSwapValidationMetadata): SwapError;
@@ -137,4 +137,30 @@ export function _getEarlyAssetHubValidationError(error, metadata) {
137
137
  default:
138
138
  return new SwapError(error);
139
139
  }
140
+ }
141
+ export function _getSimpleSwapEarlyValidationError(error, metadata) {
142
+ // todo: support more providers
143
+ switch (error) {
144
+ case SwapErrorType.NOT_MEET_MIN_SWAP:
145
+ {
146
+ const message = `Amount too low. Increase your amount above ${metadata.minSwap.value} ${metadata.minSwap.symbol} and try again`;
147
+ return new SwapError(error, message);
148
+ }
149
+ case SwapErrorType.SWAP_EXCEED_ALLOWANCE:
150
+ {
151
+ if (metadata.maxSwap) {
152
+ return new SwapError(error, `Amount too high. Lower your amount below ${metadata.maxSwap.value} ${metadata.maxSwap.symbol} and try again`);
153
+ } else {
154
+ return new SwapError(error, 'Amount too high. Lower your amount and try again');
155
+ }
156
+ }
157
+ case SwapErrorType.ASSET_NOT_SUPPORTED:
158
+ return new SwapError(error, 'This swap pair is not supported');
159
+ case SwapErrorType.UNKNOWN:
160
+ return new SwapError(error, `Undefined error. Check your Internet and ${metadata.chain.slug} connection or contact support`);
161
+ case SwapErrorType.ERROR_FETCHING_QUOTE:
162
+ return new SwapError(error, 'No swap quote found. Adjust your amount or try again later.');
163
+ default:
164
+ return new SwapError(error);
165
+ }
140
166
  }
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "./cjs/detectPackage.js"
18
18
  ],
19
19
  "type": "module",
20
- "version": "1.3.10-0",
20
+ "version": "1.3.11-0",
21
21
  "main": "./cjs/index.js",
22
22
  "module": "./index.js",
23
23
  "types": "./index.d.ts",
@@ -1774,6 +1774,11 @@
1774
1774
  "require": "./cjs/services/swap-service/handler/hydradx-handler.js",
1775
1775
  "default": "./services/swap-service/handler/hydradx-handler.js"
1776
1776
  },
1777
+ "./services/swap-service/handler/simpleswap-handler": {
1778
+ "types": "./services/swap-service/handler/simpleswap-handler.d.ts",
1779
+ "require": "./cjs/services/swap-service/handler/simpleswap-handler.js",
1780
+ "default": "./services/swap-service/handler/simpleswap-handler.js"
1781
+ },
1777
1782
  "./services/swap-service/utils": {
1778
1783
  "types": "./services/swap-service/utils.d.ts",
1779
1784
  "require": "./cjs/services/swap-service/utils.js",
@@ -2491,11 +2496,11 @@
2491
2496
  "@reduxjs/toolkit": "^1.9.1",
2492
2497
  "@sora-substrate/type-definitions": "^1.17.7",
2493
2498
  "@substrate/connect": "^0.8.9",
2494
- "@subwallet/chain-list": "0.2.95-beta.1",
2495
- "@subwallet/extension-base": "^1.3.10-0",
2496
- "@subwallet/extension-chains": "^1.3.10-0",
2497
- "@subwallet/extension-dapp": "^1.3.10-0",
2498
- "@subwallet/extension-inject": "^1.3.10-0",
2499
+ "@subwallet/chain-list": "0.2.96",
2500
+ "@subwallet/extension-base": "^1.3.11-0",
2501
+ "@subwallet/extension-chains": "^1.3.11-0",
2502
+ "@subwallet/extension-dapp": "^1.3.11-0",
2503
+ "@subwallet/extension-inject": "^1.3.11-0",
2499
2504
  "@subwallet/keyring": "^0.1.8-beta.0",
2500
2505
  "@subwallet/ui-keyring": "^0.1.8-beta.0",
2501
2506
  "@ton/core": "^0.56.3",
package/packageInfo.js CHANGED
@@ -7,5 +7,5 @@ export const packageInfo = {
7
7
  name: '@subwallet/extension-base',
8
8
  path: (import.meta && import.meta.url) ? new URL(import.meta.url).pathname.substring(0, new URL(import.meta.url).pathname.lastIndexOf('/') + 1) : 'auto',
9
9
  type: 'esm',
10
- version: '1.3.10-0'
10
+ version: '1.3.11-0'
11
11
  };
@@ -5,7 +5,7 @@ const PRODUCTION_BRANCHES = ['master', 'webapp', 'webapp-dev'];
5
5
  const branchName = process.env.BRANCH_NAME || 'subwallet-dev';
6
6
  const fetchDomain = PRODUCTION_BRANCHES.indexOf(branchName) > -1 ? 'https://chain-list-assets.subwallet.app' : 'https://dev.sw-chain-list-assets.pages.dev';
7
7
  const fetchFile = PRODUCTION_BRANCHES.indexOf(branchName) > -1 ? 'list.json' : 'preview.json';
8
- const ChainListVersion = '0.2.95'; // update this when build chainlist
8
+ const ChainListVersion = '0.2.96'; // update this when build chainlist
9
9
 
10
10
  // todo: move this interface to chainlist
11
11
 
@@ -362,7 +362,6 @@ export class ChainflipSwapHandler {
362
362
  }
363
363
  });
364
364
 
365
- console.log('depositAddressResp', depositAddressResponse);
366
365
  const txData = {
367
366
  address,
368
367
  provider: this.providerInfo,
@@ -0,0 +1,24 @@
1
+ import { SwapError } from '@subwallet/extension-base/background/errors/SwapError';
2
+ import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
3
+ import { BaseStepDetail, CommonOptimalPath, CommonStepFeeInfo, OptimalSwapPathParams, SwapEarlyValidation, SwapProviderId, SwapQuote, SwapRequest, SwapSubmitParams, SwapSubmitStepData, ValidateSwapProcessParams } from '@subwallet/extension-base/types';
4
+ import { BalanceService } from '../../balance-service';
5
+ import { ChainService } from '../../chain-service';
6
+ import { SwapBaseInterface } from './base-handler';
7
+ export declare const simpleSwapApiKey: string;
8
+ export declare class SimpleSwapHandler implements SwapBaseInterface {
9
+ private swapBaseHandler;
10
+ providerSlug: SwapProviderId;
11
+ constructor(chainService: ChainService, balanceService: BalanceService);
12
+ validateSwapProcess(params: ValidateSwapProcessParams): Promise<TransactionError[]>;
13
+ get chainService(): ChainService;
14
+ get balanceService(): BalanceService;
15
+ get providerInfo(): import("@subwallet/extension-base/types").SwapProvider;
16
+ get name(): string;
17
+ get slug(): string;
18
+ getSwapQuote(request: SwapRequest): Promise<SwapQuote | SwapError>;
19
+ generateOptimalProcess(params: OptimalSwapPathParams): Promise<CommonOptimalPath>;
20
+ getSubmitStep(params: OptimalSwapPathParams): Promise<[BaseStepDetail, CommonStepFeeInfo] | undefined>;
21
+ validateSwapRequest(request: SwapRequest): Promise<SwapEarlyValidation>;
22
+ handleSwapProcess(params: SwapSubmitParams): Promise<SwapSubmitStepData>;
23
+ handleSubmitStep(params: SwapSubmitParams): Promise<SwapSubmitStepData>;
24
+ }
@@ -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
+ }