@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.
- package/background/errors/SwapError.js +5 -0
- package/cjs/background/errors/SwapError.js +5 -0
- package/cjs/core/logic-validation/swap.js +27 -0
- package/cjs/packageInfo.js +1 -1
- package/cjs/services/chain-service/utils/patch.js +1 -1
- package/cjs/services/swap-service/handler/chainflip-handler.js +0 -1
- package/cjs/services/swap-service/handler/simpleswap-handler.js +444 -0
- package/cjs/services/swap-service/index.js +15 -3
- package/cjs/services/swap-service/utils.js +13 -2
- package/cjs/services/transaction-service/index.js +14 -10
- package/cjs/services/transaction-service/utils.js +9 -1
- package/cjs/types/swap/index.js +7 -3
- package/cjs/utils/number.js +11 -6
- package/core/logic-validation/swap.d.ts +2 -1
- package/core/logic-validation/swap.js +26 -0
- package/package.json +11 -6
- package/packageInfo.js +1 -1
- package/services/chain-service/utils/patch.js +1 -1
- package/services/swap-service/handler/chainflip-handler.js +0 -1
- package/services/swap-service/handler/simpleswap-handler.d.ts +24 -0
- package/services/swap-service/handler/simpleswap-handler.js +434 -0
- package/services/swap-service/index.d.ts +2 -1
- package/services/swap-service/index.js +13 -2
- package/services/swap-service/utils.d.ts +2 -0
- package/services/swap-service/utils.js +10 -1
- package/services/transaction-service/index.js +15 -11
- package/services/transaction-service/utils.d.ts +2 -1
- package/services/transaction-service/utils.js +9 -2
- package/types/swap/index.d.ts +15 -3
- package/types/swap/index.js +5 -2
- package/utils/number.d.ts +9 -1
- 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
|
}
|
package/cjs/types/swap/index.js
CHANGED
|
@@ -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;
|
package/cjs/utils/number.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
2495
|
-
"@subwallet/extension-base": "^1.3.
|
|
2496
|
-
"@subwallet/extension-chains": "^1.3.
|
|
2497
|
-
"@subwallet/extension-dapp": "^1.3.
|
|
2498
|
-
"@subwallet/extension-inject": "^1.3.
|
|
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
|
+
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.
|
|
8
|
+
const ChainListVersion = '0.2.96'; // update this when build chainlist
|
|
9
9
|
|
|
10
10
|
// todo: move this interface to chainlist
|
|
11
11
|
|
|
@@ -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
|
+
}
|