@subwallet/extension-base 1.1.54-0 → 1.1.55-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/KoniTypes.d.ts +21 -1
- package/background/KoniTypes.js +1 -0
- package/background/errors/SwapError.d.ts +6 -0
- package/background/errors/SwapError.js +57 -0
- package/background/errors/TransactionError.js +9 -0
- package/background/types.d.ts +2 -0
- package/cjs/background/KoniTypes.js +1 -0
- package/cjs/background/errors/SwapError.js +64 -0
- package/cjs/background/errors/TransactionError.js +9 -0
- package/cjs/koni/api/staking/bonding/utils.js +35 -6
- package/cjs/koni/background/handlers/Extension.js +214 -102
- package/cjs/koni/background/handlers/State.js +5 -2
- package/cjs/packageInfo.js +1 -1
- package/cjs/services/balance-service/index.js +6 -3
- package/cjs/services/chain-service/constants.js +1 -1
- package/cjs/services/chain-service/index.js +39 -18
- package/cjs/services/chain-service/utils/index.js +15 -4
- package/cjs/services/chain-service/utils/patch.js +1 -1
- package/cjs/services/earning-service/constants/chains.js +4 -2
- package/cjs/services/earning-service/handlers/native-staking/astar.js +4 -3
- package/cjs/services/earning-service/handlers/native-staking/relay-chain.js +22 -3
- package/cjs/services/migration-service/scripts/MigrateTransactionHistoryBySymbol.js +4 -17
- package/cjs/services/migration-service/scripts/databases/MigrateAssetSetting.js +4 -17
- package/cjs/services/migration-service/scripts/index.js +3 -3
- package/cjs/services/swap-service/handler/base-handler.js +189 -0
- package/cjs/services/swap-service/handler/chainflip-handler.js +407 -0
- package/cjs/services/swap-service/handler/hydradx-handler.js +531 -0
- package/cjs/services/swap-service/index.js +250 -0
- package/cjs/services/swap-service/utils.js +126 -0
- package/cjs/services/transaction-service/index.js +20 -0
- package/cjs/services/transaction-service/utils.js +6 -0
- package/cjs/types/fee/evm.js +1 -0
- package/cjs/types/fee/fee.js +70 -0
- package/cjs/types/fee/index.js +27 -1
- package/cjs/types/service-base.js +1 -0
- package/cjs/types/swap/index.js +50 -0
- package/cjs/utils/index.js +12 -0
- package/cjs/utils/swap.js +78 -0
- package/koni/api/staking/bonding/utils.d.ts +3 -1
- package/koni/api/staking/bonding/utils.js +32 -6
- package/koni/background/handlers/Extension.d.ts +6 -0
- package/koni/background/handlers/Extension.js +111 -0
- package/koni/background/handlers/State.d.ts +2 -0
- package/koni/background/handlers/State.js +5 -2
- package/package.json +65 -8
- package/packageInfo.js +1 -1
- package/services/balance-service/index.js +6 -3
- package/services/base/types.d.ts +4 -0
- package/services/chain-service/constants.js +1 -1
- package/services/chain-service/index.d.ts +4 -0
- package/services/chain-service/index.js +21 -1
- package/services/chain-service/utils/index.d.ts +7 -5
- package/services/chain-service/utils/index.js +9 -2
- package/services/chain-service/utils/patch.js +1 -1
- package/services/earning-service/constants/chains.d.ts +1 -0
- package/services/earning-service/constants/chains.js +1 -0
- package/services/earning-service/handlers/native-staking/astar.js +4 -3
- package/services/earning-service/handlers/native-staking/relay-chain.js +24 -5
- package/services/event-service/types.d.ts +1 -0
- package/services/migration-service/scripts/MigrateTransactionHistoryBySymbol.js +4 -17
- package/services/migration-service/scripts/databases/MigrateAssetSetting.js +4 -17
- package/services/migration-service/scripts/index.js +3 -3
- package/services/swap-service/handler/base-handler.d.ts +38 -0
- package/services/swap-service/handler/base-handler.js +180 -0
- package/services/swap-service/handler/chainflip-handler.d.ts +30 -0
- package/services/swap-service/handler/chainflip-handler.js +399 -0
- package/services/swap-service/handler/hydradx-handler.d.ts +36 -0
- package/services/swap-service/handler/hydradx-handler.js +522 -0
- package/services/swap-service/index.d.ts +32 -0
- package/services/swap-service/index.js +241 -0
- package/services/swap-service/utils.d.ts +18 -0
- package/services/swap-service/utils.js +105 -0
- package/services/transaction-service/index.js +20 -0
- package/services/transaction-service/utils.d.ts +2 -0
- package/services/transaction-service/utils.js +6 -2
- package/types/fee/evm.d.ts +49 -0
- package/types/fee/evm.js +1 -0
- package/types/fee/fee.d.ts +32 -0
- package/types/fee/fee.js +63 -0
- package/types/fee/index.d.ts +2 -49
- package/types/fee/index.js +5 -1
- package/types/service-base.d.ts +10 -0
- package/types/service-base.js +1 -0
- package/types/swap/index.d.ts +168 -0
- package/types/swap/index.js +41 -0
- package/types/yield/info/chain/target.d.ts +2 -0
- package/types/yield/info/pallet.d.ts +4 -0
- package/utils/index.d.ts +1 -0
- package/utils/index.js +2 -1
- package/utils/swap.d.ts +3 -0
- package/utils/swap.js +70 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.SwapBaseHandler = void 0;
|
|
8
|
+
var _TransactionError = require("@subwallet/extension-base/background/errors/TransactionError");
|
|
9
|
+
var _KoniTypes = require("@subwallet/extension-base/background/KoniTypes");
|
|
10
|
+
var _utils = require("@subwallet/extension-base/services/chain-service/utils");
|
|
11
|
+
var _utils2 = require("@subwallet/extension-base/services/swap-service/utils");
|
|
12
|
+
var _swap = require("@subwallet/extension-base/types/swap");
|
|
13
|
+
var _utils3 = require("@subwallet/extension-base/utils");
|
|
14
|
+
var _bignumber = _interopRequireDefault(require("bignumber.js"));
|
|
15
|
+
var _i18next = require("i18next");
|
|
16
|
+
var _utilCrypto = require("@polkadot/util-crypto");
|
|
17
|
+
// Copyright 2019-2022 @subwallet/extension-base
|
|
18
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
19
|
+
|
|
20
|
+
class SwapBaseHandler {
|
|
21
|
+
constructor(_ref) {
|
|
22
|
+
let {
|
|
23
|
+
balanceService,
|
|
24
|
+
chainService,
|
|
25
|
+
providerName,
|
|
26
|
+
providerSlug
|
|
27
|
+
} = _ref;
|
|
28
|
+
this.providerName = providerName;
|
|
29
|
+
this.providerSlug = providerSlug;
|
|
30
|
+
this.chainService = chainService;
|
|
31
|
+
this.balanceService = balanceService;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// public abstract getSwapQuote(request: SwapRequest): Promise<SwapQuote | SwapError>;
|
|
35
|
+
async generateOptimalProcess(params, genStepFuncList) {
|
|
36
|
+
const result = {
|
|
37
|
+
totalFee: [_utils2.MOCK_SWAP_FEE],
|
|
38
|
+
steps: [_utils2.DEFAULT_SWAP_FIRST_STEP]
|
|
39
|
+
};
|
|
40
|
+
try {
|
|
41
|
+
for (const genStepFunc of genStepFuncList) {
|
|
42
|
+
const step = await genStepFunc.bind(this, params)();
|
|
43
|
+
if (step) {
|
|
44
|
+
result.steps.push({
|
|
45
|
+
id: result.steps.length,
|
|
46
|
+
...step[0]
|
|
47
|
+
});
|
|
48
|
+
result.totalFee.push(step[1]);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
} catch (e) {
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async validateXcmStep(params, stepIndex) {
|
|
57
|
+
const bnAmount = new _bignumber.default(params.selectedQuote.fromAmount);
|
|
58
|
+
const swapPair = params.selectedQuote.pair;
|
|
59
|
+
const alternativeAssetSlug = (0, _utils2.getSwapAlternativeAsset)(swapPair);
|
|
60
|
+
if (!alternativeAssetSlug) {
|
|
61
|
+
return [new _TransactionError.TransactionError(_KoniTypes.BasicTxErrorType.INTERNAL_ERROR)];
|
|
62
|
+
}
|
|
63
|
+
const alternativeAsset = this.chainService.getAssetBySlug(alternativeAssetSlug);
|
|
64
|
+
const fromAsset = this.chainService.getAssetBySlug(swapPair.from);
|
|
65
|
+
const [alternativeAssetBalance, fromAssetBalance] = await Promise.all([this.balanceService.getTokenFreeBalance(params.address, alternativeAsset.originChain, alternativeAssetSlug), this.balanceService.getTokenFreeBalance(params.address, fromAsset.originChain, fromAsset.slug)]);
|
|
66
|
+
const bnAlternativeAssetBalance = new _bignumber.default(alternativeAssetBalance.value);
|
|
67
|
+
const bnFromAssetBalance = new _bignumber.default(fromAssetBalance.value);
|
|
68
|
+
const xcmFeeComponent = params.process.totalFee[stepIndex].feeComponent[0]; // todo: can do better than indexing
|
|
69
|
+
const xcmFee = new _bignumber.default(xcmFeeComponent.amount || '0');
|
|
70
|
+
let xcmAmount = bnAmount.minus(bnFromAssetBalance);
|
|
71
|
+
if ((0, _utils._isNativeToken)(alternativeAsset)) {
|
|
72
|
+
xcmAmount = xcmAmount.plus(xcmFee);
|
|
73
|
+
}
|
|
74
|
+
const alternativeTokenMinAmount = new _bignumber.default(alternativeAsset.minAmount || '0');
|
|
75
|
+
if (!bnAlternativeAssetBalance.minus(xcmAmount).gte(alternativeTokenMinAmount)) {
|
|
76
|
+
const maxBn = bnFromAssetBalance.plus(new _bignumber.default(alternativeAssetBalance.value)).minus(xcmFee).minus(alternativeTokenMinAmount);
|
|
77
|
+
const maxValue = (0, _utils3.formatNumber)(maxBn.toString(), fromAsset.decimals || 0);
|
|
78
|
+
const altInputTokenInfo = this.chainService.getAssetBySlug(alternativeAssetSlug);
|
|
79
|
+
const symbol = altInputTokenInfo.symbol;
|
|
80
|
+
const alternativeChain = this.chainService.getChainInfoByKey(altInputTokenInfo.originChain);
|
|
81
|
+
const chain = this.chainService.getChainInfoByKey(fromAsset.originChain);
|
|
82
|
+
const inputNetworkName = chain.name;
|
|
83
|
+
const altNetworkName = alternativeChain.name;
|
|
84
|
+
const currentValue = (0, _utils3.formatNumber)(bnFromAssetBalance.toString(), fromAsset.decimals || 0);
|
|
85
|
+
const bnMaxXCM = new _bignumber.default(alternativeAssetBalance.value).minus(xcmFee).minus(alternativeTokenMinAmount);
|
|
86
|
+
const maxXCMValue = (0, _utils3.formatNumber)(bnMaxXCM.toString(), fromAsset.decimals || 0);
|
|
87
|
+
if (maxBn.lte(0) || bnFromAssetBalance.lte(0) || bnMaxXCM.lte(0)) {
|
|
88
|
+
return [new _TransactionError.TransactionError(_KoniTypes.BasicTxErrorType.NOT_ENOUGH_BALANCE, (0, _i18next.t)(`Insufficient balance. Deposit ${fromAsset.symbol} and try again.`))];
|
|
89
|
+
}
|
|
90
|
+
return [new _TransactionError.TransactionError(_KoniTypes.BasicTxErrorType.NOT_ENOUGH_BALANCE, (0, _i18next.t)('You can only enter a maximum of {{maxValue}} {{symbol}}, which is {{currentValue}} {{symbol}} ({{inputNetworkName}}) and {{maxXCMValue}} {{symbol}} ({{altNetworkName}}). Lower your amount and try again.', {
|
|
91
|
+
replace: {
|
|
92
|
+
symbol,
|
|
93
|
+
maxValue,
|
|
94
|
+
inputNetworkName,
|
|
95
|
+
altNetworkName,
|
|
96
|
+
currentValue,
|
|
97
|
+
maxXCMValue
|
|
98
|
+
}
|
|
99
|
+
}))];
|
|
100
|
+
}
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
async validateTokenApproveStep(params, stepIndex) {
|
|
104
|
+
return Promise.resolve([]);
|
|
105
|
+
}
|
|
106
|
+
async validateSetFeeTokenStep(params, stepIndex) {
|
|
107
|
+
if (!params.selectedQuote) {
|
|
108
|
+
return Promise.resolve([new _TransactionError.TransactionError(_KoniTypes.BasicTxErrorType.INTERNAL_ERROR)]);
|
|
109
|
+
}
|
|
110
|
+
const feeInfo = params.process.totalFee[stepIndex];
|
|
111
|
+
const feeAmount = feeInfo.feeComponent[0];
|
|
112
|
+
const feeTokenInfo = this.chainService.getAssetBySlug(feeInfo.defaultFeeToken);
|
|
113
|
+
const feeTokenBalance = await this.balanceService.getTokenFreeBalance(params.address, feeTokenInfo.originChain, feeTokenInfo.slug);
|
|
114
|
+
const bnFeeTokenBalance = new _bignumber.default(feeTokenBalance.value);
|
|
115
|
+
const bnFeeAmount = new _bignumber.default(feeAmount.amount);
|
|
116
|
+
if (bnFeeAmount.gte(bnFeeTokenBalance)) {
|
|
117
|
+
return Promise.resolve([new _TransactionError.TransactionError(_KoniTypes.BasicTxErrorType.NOT_ENOUGH_BALANCE)]);
|
|
118
|
+
}
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
async validateSwapStep(params, isXcmOk, stepIndex) {
|
|
122
|
+
if (!params.selectedQuote) {
|
|
123
|
+
return Promise.resolve([new _TransactionError.TransactionError(_KoniTypes.BasicTxErrorType.INTERNAL_ERROR)]);
|
|
124
|
+
}
|
|
125
|
+
const selectedQuote = params.selectedQuote;
|
|
126
|
+
const currentTimestamp = +Date.now();
|
|
127
|
+
if (selectedQuote.aliveUntil <= currentTimestamp) {
|
|
128
|
+
return Promise.resolve([new _TransactionError.TransactionError(_swap.SwapErrorType.QUOTE_TIMEOUT)]);
|
|
129
|
+
}
|
|
130
|
+
const bnAmount = new _bignumber.default(params.selectedQuote.fromAmount);
|
|
131
|
+
const fromAsset = this.chainService.getAssetBySlug(params.selectedQuote.pair.from);
|
|
132
|
+
const stepFee = params.process.totalFee[stepIndex].feeComponent;
|
|
133
|
+
const networkFee = stepFee.find(fee => fee.feeType === _swap.SwapFeeType.NETWORK_FEE);
|
|
134
|
+
if (!networkFee) {
|
|
135
|
+
return Promise.resolve([new _TransactionError.TransactionError(_KoniTypes.BasicTxErrorType.INTERNAL_ERROR)]);
|
|
136
|
+
}
|
|
137
|
+
const feeTokenInfo = this.chainService.getAssetBySlug(networkFee.tokenSlug);
|
|
138
|
+
const feeTokenChain = this.chainService.getChainInfoByKey(feeTokenInfo.originChain);
|
|
139
|
+
const [feeTokenBalance, fromAssetBalance] = await Promise.all([this.balanceService.getTokenFreeBalance(params.address, feeTokenInfo.originChain, feeTokenInfo.slug), this.balanceService.getTokenFreeBalance(params.address, fromAsset.originChain, fromAsset.slug)]);
|
|
140
|
+
const bnFeeTokenBalance = new _bignumber.default(feeTokenBalance.value);
|
|
141
|
+
const bnFromAssetBalance = new _bignumber.default(fromAssetBalance.value);
|
|
142
|
+
const bnFeeAmount = new _bignumber.default(networkFee.amount);
|
|
143
|
+
if (bnFeeTokenBalance.lte(bnFeeAmount)) {
|
|
144
|
+
return Promise.resolve([new _TransactionError.TransactionError(_KoniTypes.BasicTxErrorType.NOT_ENOUGH_BALANCE, `You don't have enough ${feeTokenInfo.symbol} (${feeTokenChain.name}) to pay transaction fee`)]);
|
|
145
|
+
}
|
|
146
|
+
if (fromAsset.slug === feeTokenInfo.slug) {
|
|
147
|
+
if (bnFromAssetBalance.lte(bnFeeAmount.plus(bnAmount))) {
|
|
148
|
+
return Promise.resolve([new _TransactionError.TransactionError(_KoniTypes.BasicTxErrorType.NOT_ENOUGH_BALANCE, `Insufficient balance. Deposit ${fromAsset.symbol} and try again.`)]);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (params.selectedQuote.minSwap) {
|
|
152
|
+
const minProtocolSwap = new _bignumber.default(params.selectedQuote.minSwap);
|
|
153
|
+
if (!isXcmOk && bnFromAssetBalance.lte(minProtocolSwap)) {
|
|
154
|
+
const parsedMinSwapValue = (0, _utils3.formatNumber)(minProtocolSwap, (0, _utils._getAssetDecimals)(fromAsset));
|
|
155
|
+
return Promise.resolve([new _TransactionError.TransactionError(_swap.SwapErrorType.SWAP_NOT_ENOUGH_BALANCE, `Insufficient balance. You need more than ${parsedMinSwapValue} ${fromAsset.symbol} to start swapping. Deposit ${fromAsset.symbol} and try again.`)]); // todo: min swap or amount?
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const bnSrcAssetMinAmount = new _bignumber.default((0, _utils._getTokenMinAmount)(fromAsset));
|
|
160
|
+
const bnMaxBalanceSwap = bnFromAssetBalance.minus(bnSrcAssetMinAmount);
|
|
161
|
+
if (!isXcmOk && bnAmount.gte(bnMaxBalanceSwap)) {
|
|
162
|
+
const parsedMaxBalanceSwap = (0, _utils3.formatNumber)(bnMaxBalanceSwap, (0, _utils._getAssetDecimals)(fromAsset));
|
|
163
|
+
return Promise.resolve([new _TransactionError.TransactionError(_swap.SwapErrorType.SWAP_EXCEED_ALLOWANCE, `Amount too high. Lower your amount ${bnMaxBalanceSwap.gt(0) ? `below ${parsedMaxBalanceSwap} ${fromAsset.symbol}` : ''} and try again`)]);
|
|
164
|
+
}
|
|
165
|
+
if (params.recipient) {
|
|
166
|
+
const toAsset = this.chainService.getAssetBySlug(params.selectedQuote.pair.to);
|
|
167
|
+
const destChainInfo = this.chainService.getChainInfoByKey(toAsset.originChain);
|
|
168
|
+
const isEvmAddress = (0, _utilCrypto.isEthereumAddress)(params.recipient);
|
|
169
|
+
const isEvmDestChain = (0, _utils._isChainEvmCompatible)(destChainInfo);
|
|
170
|
+
if (isEvmAddress && !isEvmDestChain || !isEvmAddress && isEvmDestChain) {
|
|
171
|
+
return Promise.resolve([new _TransactionError.TransactionError(_swap.SwapErrorType.INVALID_RECIPIENT)]);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return Promise.resolve([]);
|
|
175
|
+
}
|
|
176
|
+
get name() {
|
|
177
|
+
return this.providerName;
|
|
178
|
+
}
|
|
179
|
+
get slug() {
|
|
180
|
+
return this.providerSlug;
|
|
181
|
+
}
|
|
182
|
+
get providerInfo() {
|
|
183
|
+
return {
|
|
184
|
+
id: this.providerSlug,
|
|
185
|
+
name: this.providerName
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
exports.SwapBaseHandler = SwapBaseHandler;
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.ChainflipSwapHandler = void 0;
|
|
8
|
+
var _swap = require("@chainflip/sdk/swap");
|
|
9
|
+
var _chainList = require("@subwallet/chain-list");
|
|
10
|
+
var _SwapError = require("@subwallet/extension-base/background/errors/SwapError");
|
|
11
|
+
var _TransactionError = require("@subwallet/extension-base/background/errors/TransactionError");
|
|
12
|
+
var _KoniTypes = require("@subwallet/extension-base/background/KoniTypes");
|
|
13
|
+
var _transfer = require("@subwallet/extension-base/koni/api/dotsama/transfer");
|
|
14
|
+
var _transfer2 = require("@subwallet/extension-base/koni/api/tokens/evm/transfer");
|
|
15
|
+
var _utils = require("@subwallet/extension-base/services/chain-service/utils");
|
|
16
|
+
var _baseHandler = require("@subwallet/extension-base/services/swap-service/handler/base-handler");
|
|
17
|
+
var _utils2 = require("@subwallet/extension-base/services/swap-service/utils");
|
|
18
|
+
var _swap2 = require("@subwallet/extension-base/types/swap");
|
|
19
|
+
var _bignumber = _interopRequireDefault(require("bignumber.js"));
|
|
20
|
+
// Copyright 2019-2022 @subwallet/extension-base
|
|
21
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
22
|
+
var ChainflipFeeType;
|
|
23
|
+
(function (ChainflipFeeType) {
|
|
24
|
+
ChainflipFeeType["INGRESS"] = "INGRESS";
|
|
25
|
+
ChainflipFeeType["NETWORK"] = "NETWORK";
|
|
26
|
+
ChainflipFeeType["EGRESS"] = "EGRESS";
|
|
27
|
+
ChainflipFeeType["LIQUIDITY"] = "LIQUIDITY";
|
|
28
|
+
})(ChainflipFeeType || (ChainflipFeeType = {}));
|
|
29
|
+
const INTERMEDIARY_MAINNET_ASSET_SLUG = _chainList.COMMON_ASSETS.USDC_ETHEREUM;
|
|
30
|
+
const INTERMEDIARY_TESTNET_ASSET_SLUG = _chainList.COMMON_ASSETS.USDC_SEPOLIA;
|
|
31
|
+
var CHAINFLIP_QUOTE_ERROR;
|
|
32
|
+
(function (CHAINFLIP_QUOTE_ERROR) {
|
|
33
|
+
CHAINFLIP_QUOTE_ERROR["InsufficientLiquidity"] = "InsufficientLiquidity";
|
|
34
|
+
CHAINFLIP_QUOTE_ERROR["InsufficientEgress"] = "is lower than minimum egress amount";
|
|
35
|
+
CHAINFLIP_QUOTE_ERROR["InsufficientIngress"] = "amount is lower than estimated ingress fee";
|
|
36
|
+
})(CHAINFLIP_QUOTE_ERROR || (CHAINFLIP_QUOTE_ERROR = {}));
|
|
37
|
+
class ChainflipSwapHandler {
|
|
38
|
+
constructor(chainService, balanceService) {
|
|
39
|
+
let isTestnet = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
|
40
|
+
this.swapBaseHandler = new _baseHandler.SwapBaseHandler({
|
|
41
|
+
chainService,
|
|
42
|
+
balanceService,
|
|
43
|
+
providerName: isTestnet ? 'Chainflip Testnet' : 'Chainflip',
|
|
44
|
+
providerSlug: isTestnet ? _swap2.SwapProviderId.CHAIN_FLIP_TESTNET : _swap2.SwapProviderId.CHAIN_FLIP_MAINNET
|
|
45
|
+
});
|
|
46
|
+
this.isTestnet = isTestnet;
|
|
47
|
+
this.swapSdk = new _swap.SwapSDK({
|
|
48
|
+
network: isTestnet ? 'perseverance' : 'mainnet'
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
get chainService() {
|
|
52
|
+
return this.swapBaseHandler.chainService;
|
|
53
|
+
}
|
|
54
|
+
get balanceService() {
|
|
55
|
+
return this.swapBaseHandler.balanceService;
|
|
56
|
+
}
|
|
57
|
+
get providerInfo() {
|
|
58
|
+
return this.swapBaseHandler.providerInfo;
|
|
59
|
+
}
|
|
60
|
+
get name() {
|
|
61
|
+
return this.swapBaseHandler.name;
|
|
62
|
+
}
|
|
63
|
+
get slug() {
|
|
64
|
+
return this.swapBaseHandler.slug;
|
|
65
|
+
}
|
|
66
|
+
get assetMapping() {
|
|
67
|
+
if (this.isTestnet) {
|
|
68
|
+
return _utils2.CHAIN_FLIP_SUPPORTED_TESTNET_ASSET_MAPPING;
|
|
69
|
+
} else {
|
|
70
|
+
return _utils2.CHAIN_FLIP_SUPPORTED_MAINNET_ASSET_MAPPING;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
get chainMapping() {
|
|
74
|
+
if (this.isTestnet) {
|
|
75
|
+
return _utils2.CHAIN_FLIP_SUPPORTED_TESTNET_MAPPING;
|
|
76
|
+
} else {
|
|
77
|
+
return _utils2.CHAIN_FLIP_SUPPORTED_MAINNET_MAPPING;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
get intermediaryAssetSlug() {
|
|
81
|
+
if (this.isTestnet) {
|
|
82
|
+
return INTERMEDIARY_TESTNET_ASSET_SLUG;
|
|
83
|
+
} else {
|
|
84
|
+
return INTERMEDIARY_MAINNET_ASSET_SLUG;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async validateSwapRequest(request) {
|
|
88
|
+
try {
|
|
89
|
+
// todo: risk of matching wrong chain, asset can lead to loss of funds
|
|
90
|
+
|
|
91
|
+
const fromAsset = this.chainService.getAssetBySlug(request.pair.from);
|
|
92
|
+
const toAsset = this.chainService.getAssetBySlug(request.pair.to);
|
|
93
|
+
const srcChain = fromAsset.originChain;
|
|
94
|
+
const destChain = toAsset.originChain;
|
|
95
|
+
const srcChainInfo = this.chainService.getChainInfoByKey(srcChain);
|
|
96
|
+
const srcChainId = this.chainMapping[srcChain];
|
|
97
|
+
const destChainId = this.chainMapping[destChain];
|
|
98
|
+
const fromAssetId = this.assetMapping[fromAsset.slug];
|
|
99
|
+
const toAssetId = this.assetMapping[toAsset.slug];
|
|
100
|
+
if (!srcChainId || !destChainId || !fromAssetId || !toAssetId) {
|
|
101
|
+
return {
|
|
102
|
+
error: _swap2.SwapErrorType.ASSET_NOT_SUPPORTED
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const [supportedDestChains, srcAssets, destAssets] = await Promise.all([this.swapSdk.getChains(srcChainId), this.swapSdk.getAssets(srcChainId), this.swapSdk.getAssets(destChainId)]);
|
|
106
|
+
const supportedDestChainId = supportedDestChains.find(c => c.chain === destChainId);
|
|
107
|
+
const srcAssetData = srcAssets.find(a => a.asset === fromAssetId);
|
|
108
|
+
const destAssetData = destAssets.find(a => a.asset === toAssetId);
|
|
109
|
+
if (!destAssetData || !srcAssetData || !supportedDestChainId) {
|
|
110
|
+
return {
|
|
111
|
+
error: _swap2.SwapErrorType.UNKNOWN
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const bnAmount = new _bignumber.default(request.fromAmount);
|
|
115
|
+
const bnMinSwap = new _bignumber.default(srcAssetData.minimumSwapAmount);
|
|
116
|
+
if (srcAssetData.maximumSwapAmount) {
|
|
117
|
+
const bnMaxProtocolSwap = new _bignumber.default(srcAssetData.maximumSwapAmount);
|
|
118
|
+
if (bnMinSwap.gte(bnMaxProtocolSwap)) {
|
|
119
|
+
return {
|
|
120
|
+
error: _swap2.SwapErrorType.UNKNOWN
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
if (bnAmount.gte(bnMaxProtocolSwap)) {
|
|
124
|
+
return {
|
|
125
|
+
error: _swap2.SwapErrorType.SWAP_EXCEED_ALLOWANCE,
|
|
126
|
+
metadata: {
|
|
127
|
+
minSwap: {
|
|
128
|
+
value: srcAssetData.minimumSwapAmount,
|
|
129
|
+
decimals: (0, _utils._getAssetDecimals)(fromAsset),
|
|
130
|
+
symbol: fromAsset.symbol
|
|
131
|
+
},
|
|
132
|
+
maxSwap: {
|
|
133
|
+
value: bnMaxProtocolSwap.toString(),
|
|
134
|
+
decimals: (0, _utils._getAssetDecimals)(fromAsset),
|
|
135
|
+
symbol: fromAsset.symbol
|
|
136
|
+
},
|
|
137
|
+
chain: srcChainInfo
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (bnAmount.lt(bnMinSwap)) {
|
|
143
|
+
// might miss case when minSwap is 0
|
|
144
|
+
return {
|
|
145
|
+
error: _swap2.SwapErrorType.NOT_MEET_MIN_SWAP,
|
|
146
|
+
metadata: {
|
|
147
|
+
minSwap: {
|
|
148
|
+
value: srcAssetData.minimumSwapAmount,
|
|
149
|
+
decimals: (0, _utils._getAssetDecimals)(fromAsset),
|
|
150
|
+
symbol: fromAsset.symbol
|
|
151
|
+
},
|
|
152
|
+
maxSwap: {
|
|
153
|
+
value: srcAssetData.maximumSwapAmount,
|
|
154
|
+
decimals: (0, _utils._getAssetDecimals)(fromAsset),
|
|
155
|
+
symbol: fromAsset.symbol
|
|
156
|
+
},
|
|
157
|
+
chain: srcChainInfo
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
metadata: {
|
|
163
|
+
minSwap: {
|
|
164
|
+
value: srcAssetData.minimumSwapAmount,
|
|
165
|
+
decimals: (0, _utils._getAssetDecimals)(fromAsset),
|
|
166
|
+
symbol: fromAsset.symbol
|
|
167
|
+
},
|
|
168
|
+
maxSwap: {
|
|
169
|
+
value: srcAssetData.maximumSwapAmount,
|
|
170
|
+
decimals: (0, _utils._getAssetDecimals)(fromAsset),
|
|
171
|
+
symbol: fromAsset.symbol
|
|
172
|
+
},
|
|
173
|
+
chain: srcChainInfo
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
} catch (e) {
|
|
177
|
+
return {
|
|
178
|
+
error: _swap2.SwapErrorType.UNKNOWN
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
parseSwapPath(fromAsset, toAsset) {
|
|
183
|
+
if (toAsset.slug !== this.intermediaryAssetSlug && fromAsset.slug !== this.intermediaryAssetSlug) {
|
|
184
|
+
// Chainflip always use USDC as intermediary
|
|
185
|
+
return [fromAsset.slug, this.intermediaryAssetSlug, toAsset.slug]; // todo: generalize this
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return [fromAsset.slug, toAsset.slug];
|
|
189
|
+
}
|
|
190
|
+
async getSwapQuote(request) {
|
|
191
|
+
const fromAsset = this.chainService.getAssetBySlug(request.pair.from);
|
|
192
|
+
const toAsset = this.chainService.getAssetBySlug(request.pair.to);
|
|
193
|
+
const fromChain = this.chainService.getChainInfoByKey(fromAsset.originChain);
|
|
194
|
+
const fromChainNativeTokenSlug = (0, _utils._getChainNativeTokenSlug)(fromChain);
|
|
195
|
+
if (!fromAsset || !toAsset) {
|
|
196
|
+
return new _SwapError.SwapError(_swap2.SwapErrorType.UNKNOWN);
|
|
197
|
+
}
|
|
198
|
+
const earlyValidation = await this.validateSwapRequest(request);
|
|
199
|
+
const metadata = earlyValidation.metadata;
|
|
200
|
+
if (earlyValidation.error) {
|
|
201
|
+
return (0, _utils2.getChainflipEarlyValidationError)(earlyValidation.error, metadata);
|
|
202
|
+
}
|
|
203
|
+
const srcChainId = this.chainMapping[fromAsset.originChain];
|
|
204
|
+
const destChainId = this.chainMapping[toAsset.originChain];
|
|
205
|
+
const fromAssetId = this.assetMapping[fromAsset.slug];
|
|
206
|
+
const toAssetId = this.assetMapping[toAsset.slug];
|
|
207
|
+
try {
|
|
208
|
+
var _metadata$maxSwap;
|
|
209
|
+
const quoteResponse = await this.swapSdk.getQuote({
|
|
210
|
+
srcChain: srcChainId,
|
|
211
|
+
destChain: destChainId,
|
|
212
|
+
srcAsset: fromAssetId,
|
|
213
|
+
destAsset: toAssetId,
|
|
214
|
+
amount: request.fromAmount
|
|
215
|
+
});
|
|
216
|
+
const feeComponent = [];
|
|
217
|
+
quoteResponse.quote.includedFees.forEach(fee => {
|
|
218
|
+
switch (fee.type) {
|
|
219
|
+
case ChainflipFeeType.INGRESS:
|
|
220
|
+
|
|
221
|
+
// eslint-disable-next-line no-fallthrough
|
|
222
|
+
case ChainflipFeeType.EGRESS:
|
|
223
|
+
{
|
|
224
|
+
const tokenSlug = Object.keys(this.assetMapping).find(assetSlug => this.assetMapping[assetSlug] === fee.asset);
|
|
225
|
+
feeComponent.push({
|
|
226
|
+
tokenSlug,
|
|
227
|
+
amount: fee.amount,
|
|
228
|
+
feeType: _swap2.SwapFeeType.NETWORK_FEE
|
|
229
|
+
});
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
case ChainflipFeeType.NETWORK:
|
|
233
|
+
|
|
234
|
+
// eslint-disable-next-line no-fallthrough
|
|
235
|
+
case ChainflipFeeType.LIQUIDITY:
|
|
236
|
+
{
|
|
237
|
+
const tokenSlug = Object.keys(this.assetMapping).find(assetSlug => this.assetMapping[assetSlug] === fee.asset);
|
|
238
|
+
feeComponent.push({
|
|
239
|
+
tokenSlug,
|
|
240
|
+
amount: fee.amount,
|
|
241
|
+
feeType: _swap2.SwapFeeType.PLATFORM_FEE
|
|
242
|
+
});
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
const defaultFeeToken = (0, _utils._isNativeToken)(fromAsset) ? fromAsset.slug : fromChainNativeTokenSlug;
|
|
248
|
+
return {
|
|
249
|
+
pair: request.pair,
|
|
250
|
+
fromAmount: request.fromAmount,
|
|
251
|
+
toAmount: quoteResponse.quote.egressAmount.toString(),
|
|
252
|
+
rate: (0, _utils2.calculateSwapRate)(request.fromAmount, quoteResponse.quote.egressAmount.toString(), fromAsset, toAsset),
|
|
253
|
+
provider: this.providerInfo,
|
|
254
|
+
aliveUntil: +Date.now() + (_utils2.SWAP_QUOTE_TIMEOUT_MAP[this.slug] || _utils2.SWAP_QUOTE_TIMEOUT_MAP.default),
|
|
255
|
+
minSwap: metadata.minSwap.value,
|
|
256
|
+
maxSwap: (_metadata$maxSwap = metadata.maxSwap) === null || _metadata$maxSwap === void 0 ? void 0 : _metadata$maxSwap.value,
|
|
257
|
+
estimatedArrivalTime: quoteResponse.quote.estimatedDurationSeconds,
|
|
258
|
+
// in seconds
|
|
259
|
+
isLowLiquidity: quoteResponse.quote.lowLiquidityWarning,
|
|
260
|
+
feeInfo: {
|
|
261
|
+
feeComponent: feeComponent,
|
|
262
|
+
defaultFeeToken,
|
|
263
|
+
feeOptions: [defaultFeeToken]
|
|
264
|
+
},
|
|
265
|
+
route: {
|
|
266
|
+
path: this.parseSwapPath(fromAsset, toAsset)
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
} catch (e) {
|
|
270
|
+
var _error$response;
|
|
271
|
+
const error = e;
|
|
272
|
+
const errorObj = error === null || error === void 0 ? void 0 : (_error$response = error.response) === null || _error$response === void 0 ? void 0 : _error$response.data;
|
|
273
|
+
if (errorObj && errorObj.error && errorObj.error.includes(CHAINFLIP_QUOTE_ERROR.InsufficientLiquidity)) {
|
|
274
|
+
// todo: Chainflip will improve this
|
|
275
|
+
return new _SwapError.SwapError(_swap2.SwapErrorType.NOT_ENOUGH_LIQUIDITY);
|
|
276
|
+
}
|
|
277
|
+
if (errorObj && errorObj.message && errorObj.message.includes(CHAINFLIP_QUOTE_ERROR.InsufficientLiquidity)) {
|
|
278
|
+
return new _SwapError.SwapError(_swap2.SwapErrorType.NOT_ENOUGH_LIQUIDITY);
|
|
279
|
+
}
|
|
280
|
+
return new _SwapError.SwapError(_swap2.SwapErrorType.ERROR_FETCHING_QUOTE);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
async validateSwapProcess(params) {
|
|
284
|
+
const amount = params.selectedQuote.fromAmount;
|
|
285
|
+
const bnAmount = new _bignumber.default(amount);
|
|
286
|
+
if (bnAmount.lte(0)) {
|
|
287
|
+
return [new _TransactionError.TransactionError(_KoniTypes.BasicTxErrorType.INVALID_PARAMS, 'Amount must be greater than 0')];
|
|
288
|
+
}
|
|
289
|
+
let isXcmOk = false;
|
|
290
|
+
for (const [index, step] of params.process.steps.entries()) {
|
|
291
|
+
const getErrors = async () => {
|
|
292
|
+
switch (step.type) {
|
|
293
|
+
case _swap2.SwapStepType.DEFAULT:
|
|
294
|
+
return Promise.resolve([]);
|
|
295
|
+
case _swap2.SwapStepType.TOKEN_APPROVAL:
|
|
296
|
+
return Promise.reject(new _TransactionError.TransactionError(_KoniTypes.BasicTxErrorType.UNSUPPORTED));
|
|
297
|
+
default:
|
|
298
|
+
return this.swapBaseHandler.validateSwapStep(params, isXcmOk, index);
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
const errors = await getErrors();
|
|
302
|
+
if (errors.length) {
|
|
303
|
+
return errors;
|
|
304
|
+
} else if (step.type === _swap2.SwapStepType.XCM) {
|
|
305
|
+
isXcmOk = true;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return [];
|
|
309
|
+
}
|
|
310
|
+
async handleSubmitStep(params) {
|
|
311
|
+
const {
|
|
312
|
+
address,
|
|
313
|
+
quote,
|
|
314
|
+
recipient
|
|
315
|
+
} = params;
|
|
316
|
+
const pair = quote.pair;
|
|
317
|
+
const fromAsset = this.chainService.getAssetBySlug(pair.from);
|
|
318
|
+
const toAsset = this.chainService.getAssetBySlug(pair.to);
|
|
319
|
+
const chainInfo = this.chainService.getChainInfoByKey(fromAsset.originChain);
|
|
320
|
+
const chainType = (0, _utils._isSubstrateChain)(chainInfo) ? _KoniTypes.ChainType.SUBSTRATE : _KoniTypes.ChainType.EVM;
|
|
321
|
+
const receiver = recipient !== null && recipient !== void 0 ? recipient : address;
|
|
322
|
+
const srcChainId = this.chainMapping[fromAsset.originChain];
|
|
323
|
+
const destChainId = this.chainMapping[toAsset.originChain];
|
|
324
|
+
const fromAssetId = this.assetMapping[fromAsset.slug];
|
|
325
|
+
const toAssetId = this.assetMapping[toAsset.slug];
|
|
326
|
+
const depositAddressResponse = await this.swapSdk.requestDepositAddress({
|
|
327
|
+
srcChain: srcChainId,
|
|
328
|
+
destChain: destChainId,
|
|
329
|
+
srcAsset: fromAssetId,
|
|
330
|
+
destAsset: toAssetId,
|
|
331
|
+
destAddress: receiver,
|
|
332
|
+
amount: quote.fromAmount
|
|
333
|
+
});
|
|
334
|
+
const txData = {
|
|
335
|
+
address,
|
|
336
|
+
provider: this.providerInfo,
|
|
337
|
+
quote: params.quote,
|
|
338
|
+
slippage: params.slippage,
|
|
339
|
+
recipient,
|
|
340
|
+
depositChannelId: depositAddressResponse.depositChannelId,
|
|
341
|
+
depositAddress: depositAddressResponse.depositAddress,
|
|
342
|
+
process: params.process
|
|
343
|
+
};
|
|
344
|
+
let extrinsic;
|
|
345
|
+
if (chainType === _KoniTypes.ChainType.SUBSTRATE) {
|
|
346
|
+
const chainApi = this.chainService.getSubstrateApi(chainInfo.slug);
|
|
347
|
+
const substrateApi = await chainApi.isReady;
|
|
348
|
+
const [submittableExtrinsic] = await (0, _transfer.createTransferExtrinsic)({
|
|
349
|
+
from: address,
|
|
350
|
+
networkKey: chainInfo.slug,
|
|
351
|
+
substrateApi,
|
|
352
|
+
to: depositAddressResponse.depositAddress,
|
|
353
|
+
tokenInfo: fromAsset,
|
|
354
|
+
transferAll: false,
|
|
355
|
+
// always false, because we do not allow swapping all the balance
|
|
356
|
+
value: quote.fromAmount
|
|
357
|
+
});
|
|
358
|
+
extrinsic = submittableExtrinsic;
|
|
359
|
+
} else {
|
|
360
|
+
if ((0, _utils._isNativeToken)(fromAsset)) {
|
|
361
|
+
const [transactionConfig] = await (0, _transfer2.getEVMTransactionObject)(chainInfo, address, depositAddressResponse.depositAddress, quote.fromAmount, false, this.chainService.getEvmApi(chainInfo.slug));
|
|
362
|
+
extrinsic = transactionConfig;
|
|
363
|
+
} else {
|
|
364
|
+
const [transactionConfig] = await (0, _transfer2.getERC20TransactionObject)((0, _utils._getContractAddressOfToken)(fromAsset), chainInfo, address, depositAddressResponse.depositAddress, quote.fromAmount, false, this.chainService.getEvmApi(chainInfo.slug));
|
|
365
|
+
extrinsic = transactionConfig;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return {
|
|
369
|
+
txChain: fromAsset.originChain,
|
|
370
|
+
txData,
|
|
371
|
+
extrinsic,
|
|
372
|
+
transferNativeAmount: (0, _utils._isNativeToken)(fromAsset) ? quote.fromAmount : '0',
|
|
373
|
+
// todo
|
|
374
|
+
extrinsicType: _KoniTypes.ExtrinsicType.SWAP,
|
|
375
|
+
chainType
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
async handleSwapProcess(params) {
|
|
379
|
+
const {
|
|
380
|
+
currentStep,
|
|
381
|
+
process
|
|
382
|
+
} = params;
|
|
383
|
+
const type = process.steps[currentStep].type;
|
|
384
|
+
switch (type) {
|
|
385
|
+
case _swap2.SwapStepType.DEFAULT:
|
|
386
|
+
return Promise.reject(new _TransactionError.TransactionError(_KoniTypes.BasicTxErrorType.UNSUPPORTED));
|
|
387
|
+
case _swap2.SwapStepType.SWAP:
|
|
388
|
+
return this.handleSubmitStep(params);
|
|
389
|
+
default:
|
|
390
|
+
return this.handleSubmitStep(params);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
async getSubmitStep(params) {
|
|
394
|
+
if (params.selectedQuote) {
|
|
395
|
+
const submitStep = {
|
|
396
|
+
name: 'Swap',
|
|
397
|
+
type: _swap2.SwapStepType.SWAP
|
|
398
|
+
};
|
|
399
|
+
return Promise.resolve([submitStep, params.selectedQuote.feeInfo]);
|
|
400
|
+
}
|
|
401
|
+
return Promise.resolve(undefined);
|
|
402
|
+
}
|
|
403
|
+
generateOptimalProcess(params) {
|
|
404
|
+
return this.swapBaseHandler.generateOptimalProcess(params, [this.getSubmitStep]);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
exports.ChainflipSwapHandler = ChainflipSwapHandler;
|