@subwallet/extension-base 1.1.54-0 → 1.1.56-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 (112) hide show
  1. package/background/KoniTypes.d.ts +30 -2
  2. package/background/KoniTypes.js +6 -0
  3. package/background/errors/SwapError.d.ts +6 -0
  4. package/background/errors/SwapError.js +57 -0
  5. package/background/errors/TransactionError.js +9 -0
  6. package/background/types.d.ts +2 -0
  7. package/cjs/background/KoniTypes.js +8 -1
  8. package/cjs/background/errors/SwapError.js +64 -0
  9. package/cjs/background/errors/TransactionError.js +9 -0
  10. package/cjs/koni/api/nft/{statemint_nft → assethub_nft}/index.js +5 -3
  11. package/cjs/koni/api/nft/{statemine_nft → assethub_unique}/index.js +5 -3
  12. package/cjs/koni/api/nft/index.js +13 -13
  13. package/cjs/koni/api/nft/nft.js +1 -1
  14. package/cjs/koni/api/nft/transfer.js +11 -15
  15. package/cjs/koni/api/staking/bonding/utils.js +35 -6
  16. package/cjs/koni/background/handlers/Extension.js +214 -102
  17. package/cjs/koni/background/handlers/State.js +5 -2
  18. package/cjs/packageInfo.js +1 -1
  19. package/cjs/services/balance-service/index.js +6 -3
  20. package/cjs/services/chain-service/constants.js +18 -4
  21. package/cjs/services/chain-service/index.js +39 -18
  22. package/cjs/services/chain-service/utils/index.js +15 -4
  23. package/cjs/services/chain-service/utils/patch.js +1 -1
  24. package/cjs/services/earning-service/constants/chains.js +4 -2
  25. package/cjs/services/earning-service/handlers/native-staking/amplitude.js +7 -9
  26. package/cjs/services/earning-service/handlers/native-staking/astar.js +4 -3
  27. package/cjs/services/earning-service/handlers/native-staking/para-chain.js +11 -8
  28. package/cjs/services/earning-service/handlers/native-staking/relay-chain.js +22 -3
  29. package/cjs/services/earning-service/handlers/nomination-pool/index.js +19 -5
  30. package/cjs/services/earning-service/service.js +0 -1
  31. package/cjs/services/migration-service/scripts/MigrateTransactionHistoryBySymbol.js +4 -17
  32. package/cjs/services/migration-service/scripts/databases/MigrateAssetSetting.js +4 -17
  33. package/cjs/services/migration-service/scripts/index.js +3 -3
  34. package/cjs/services/swap-service/handler/base-handler.js +189 -0
  35. package/cjs/services/swap-service/handler/chainflip-handler.js +407 -0
  36. package/cjs/services/swap-service/handler/hydradx-handler.js +531 -0
  37. package/cjs/services/swap-service/index.js +250 -0
  38. package/cjs/services/swap-service/utils.js +126 -0
  39. package/cjs/services/transaction-service/index.js +20 -0
  40. package/cjs/services/transaction-service/utils.js +6 -0
  41. package/cjs/types/fee/evm.js +1 -0
  42. package/cjs/types/fee/fee.js +70 -0
  43. package/cjs/types/fee/index.js +27 -1
  44. package/cjs/types/service-base.js +1 -0
  45. package/cjs/types/swap/index.js +50 -0
  46. package/cjs/utils/index.js +12 -0
  47. package/cjs/utils/swap.js +78 -0
  48. package/koni/api/nft/{statemint_nft → assethub_nft}/index.d.ts +1 -1
  49. package/koni/api/nft/{statemint_nft → assethub_nft}/index.js +4 -2
  50. package/koni/api/nft/{statemine_nft → assethub_unique}/index.d.ts +1 -1
  51. package/koni/api/nft/{statemine_nft → assethub_unique}/index.js +4 -2
  52. package/koni/api/nft/index.js +13 -13
  53. package/koni/api/nft/nft.js +1 -1
  54. package/koni/api/nft/transfer.d.ts +1 -2
  55. package/koni/api/nft/transfer.js +10 -13
  56. package/koni/api/staking/bonding/utils.d.ts +3 -1
  57. package/koni/api/staking/bonding/utils.js +32 -6
  58. package/koni/background/handlers/Extension.d.ts +6 -0
  59. package/koni/background/handlers/Extension.js +111 -0
  60. package/koni/background/handlers/State.d.ts +2 -0
  61. package/koni/background/handlers/State.js +5 -2
  62. package/package.json +85 -28
  63. package/packageInfo.js +1 -1
  64. package/services/balance-service/index.js +6 -3
  65. package/services/base/types.d.ts +4 -0
  66. package/services/chain-service/constants.js +18 -4
  67. package/services/chain-service/index.d.ts +4 -0
  68. package/services/chain-service/index.js +21 -1
  69. package/services/chain-service/utils/index.d.ts +7 -5
  70. package/services/chain-service/utils/index.js +9 -2
  71. package/services/chain-service/utils/patch.js +1 -1
  72. package/services/earning-service/constants/chains.d.ts +1 -0
  73. package/services/earning-service/constants/chains.js +1 -0
  74. package/services/earning-service/handlers/native-staking/amplitude.js +7 -9
  75. package/services/earning-service/handlers/native-staking/astar.js +4 -3
  76. package/services/earning-service/handlers/native-staking/para-chain.js +12 -9
  77. package/services/earning-service/handlers/native-staking/relay-chain.js +24 -5
  78. package/services/earning-service/handlers/nomination-pool/index.js +19 -5
  79. package/services/earning-service/service.js +0 -1
  80. package/services/event-service/types.d.ts +1 -0
  81. package/services/migration-service/scripts/MigrateTransactionHistoryBySymbol.js +4 -17
  82. package/services/migration-service/scripts/databases/MigrateAssetSetting.js +4 -17
  83. package/services/migration-service/scripts/index.js +3 -3
  84. package/services/swap-service/handler/base-handler.d.ts +38 -0
  85. package/services/swap-service/handler/base-handler.js +180 -0
  86. package/services/swap-service/handler/chainflip-handler.d.ts +30 -0
  87. package/services/swap-service/handler/chainflip-handler.js +399 -0
  88. package/services/swap-service/handler/hydradx-handler.d.ts +36 -0
  89. package/services/swap-service/handler/hydradx-handler.js +522 -0
  90. package/services/swap-service/index.d.ts +32 -0
  91. package/services/swap-service/index.js +241 -0
  92. package/services/swap-service/utils.d.ts +18 -0
  93. package/services/swap-service/utils.js +105 -0
  94. package/services/transaction-service/index.js +20 -0
  95. package/services/transaction-service/utils.d.ts +2 -0
  96. package/services/transaction-service/utils.js +6 -2
  97. package/types/fee/evm.d.ts +49 -0
  98. package/types/fee/evm.js +1 -0
  99. package/types/fee/fee.d.ts +32 -0
  100. package/types/fee/fee.js +63 -0
  101. package/types/fee/index.d.ts +2 -49
  102. package/types/fee/index.js +5 -1
  103. package/types/service-base.d.ts +10 -0
  104. package/types/service-base.js +1 -0
  105. package/types/swap/index.d.ts +168 -0
  106. package/types/swap/index.js +41 -0
  107. package/types/yield/info/chain/target.d.ts +2 -0
  108. package/types/yield/info/pallet.d.ts +8 -0
  109. package/utils/index.d.ts +1 -0
  110. package/utils/index.js +2 -1
  111. package/utils/swap.d.ts +3 -0
  112. package/utils/swap.js +70 -0
@@ -0,0 +1,522 @@
1
+ // Copyright 2019-2022 @subwallet/extension-base
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { PoolError, PoolService, TradeRouter } from '@galacticcouncil/sdk';
5
+ import { COMMON_CHAIN_SLUGS } from '@subwallet/chain-list';
6
+ import { _AssetType } from '@subwallet/chain-list/types';
7
+ import { SwapError } from '@subwallet/extension-base/background/errors/SwapError';
8
+ import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
9
+ import { BasicTxErrorType, ChainType, ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
10
+ import { createXcmExtrinsic } from '@subwallet/extension-base/koni/api/xcm';
11
+ import { _getAssetDecimals, _getChainNativeTokenSlug, _getTokenOnChainAssetId, _isNativeToken } from '@subwallet/extension-base/services/chain-service/utils';
12
+ import { SwapBaseHandler } from '@subwallet/extension-base/services/swap-service/handler/base-handler';
13
+ import { calculateSwapRate, getEarlyHydradxValidationError, getSwapAlternativeAsset, SWAP_QUOTE_TIMEOUT_MAP } from '@subwallet/extension-base/services/swap-service/utils';
14
+ import { SwapErrorType, SwapFeeType, SwapProviderId, SwapStepType } from '@subwallet/extension-base/types/swap';
15
+ import BigNumber from 'bignumber.js';
16
+ const HYDRADX_LOW_LIQUIDITY_THRESHOLD = 0.15;
17
+ const HYDRADX_SUBWALLET_REFERRAL_CODE = 'WALLET';
18
+ const HYDRADX_SUBWALLET_REFERRAL_ACCOUNT = '7PCsCpkgsHdNaZhv79wCCQ5z97uxVbSeSCtDMUa1eZHKXy4a';
19
+ const HYDRADX_TESTNET_SUBWALLET_REFERRAL_CODE = 'ASSETHUB';
20
+ const HYDRADX_TESTNET_SUBWALLET_REFERRAL_ACCOUNT = '7LCt6dFqtxzdKVB2648jWW9d85doiFfLSbZJDNAMVJNxh5rJ';
21
+ export class HydradxHandler {
22
+ isTestnet = true;
23
+ isReady = false;
24
+ constructor(chainService, balanceService, isTestnet = true) {
25
+ this.swapBaseHandler = new SwapBaseHandler({
26
+ balanceService,
27
+ chainService,
28
+ providerName: isTestnet ? 'HydraDX Testnet' : 'HydraDX',
29
+ providerSlug: isTestnet ? SwapProviderId.HYDRADX_TESTNET : SwapProviderId.HYDRADX_MAINNET
30
+ });
31
+ this.isTestnet = isTestnet;
32
+ }
33
+ async init() {
34
+ const chainState = this.chainService.getChainStateByKey(this.chain());
35
+ if (!chainState.active) {
36
+ await this.chainService.enableChain(this.chain());
37
+ }
38
+ const substrateApi = this.chainService.getSubstrateApi(this.chain());
39
+ await substrateApi.api.isReady;
40
+ const poolService = new PoolService(substrateApi.api);
41
+ this.tradeRouter = new TradeRouter(poolService);
42
+ this.isReady = true;
43
+ }
44
+ chain = () => {
45
+ // TODO: check origin chain of tokens in swap pair to determine support
46
+ if (!this.isTestnet) {
47
+ return COMMON_CHAIN_SLUGS.HYDRADX;
48
+ } else {
49
+ return COMMON_CHAIN_SLUGS.HYDRADX_TESTNET;
50
+ }
51
+ };
52
+ get chainService() {
53
+ return this.swapBaseHandler.chainService;
54
+ }
55
+ get balanceService() {
56
+ return this.swapBaseHandler.balanceService;
57
+ }
58
+ get providerInfo() {
59
+ return this.swapBaseHandler.providerInfo;
60
+ }
61
+ get name() {
62
+ return this.swapBaseHandler.name;
63
+ }
64
+ get slug() {
65
+ return this.swapBaseHandler.slug;
66
+ }
67
+ async getXcmStep(params) {
68
+ const bnAmount = new BigNumber(params.request.fromAmount);
69
+ const fromAsset = this.chainService.getAssetBySlug(params.request.pair.from);
70
+ const fromAssetBalance = await this.balanceService.getTokenFreeBalance(params.request.address, fromAsset.originChain, fromAsset.slug);
71
+ const bnFromAssetBalance = new BigNumber(fromAssetBalance.value);
72
+ if (bnFromAssetBalance.gte(bnAmount)) {
73
+ return undefined; // enough balance, no need to xcm
74
+ }
75
+
76
+ const alternativeAssetSlug = getSwapAlternativeAsset(params.request.pair);
77
+ if (!alternativeAssetSlug) {
78
+ return undefined;
79
+ }
80
+ const alternativeAsset = this.chainService.getAssetBySlug(alternativeAssetSlug);
81
+ const alternativeAssetBalance = await this.balanceService.getTokenFreeBalance(params.request.address, alternativeAsset.originChain, alternativeAsset.slug);
82
+ const bnAlternativeAssetBalance = new BigNumber(alternativeAssetBalance.value);
83
+ if (bnAlternativeAssetBalance.lte(0)) {
84
+ return undefined;
85
+ }
86
+ try {
87
+ const alternativeChainInfo = this.chainService.getChainInfoByKey(alternativeAsset.originChain);
88
+ const step = {
89
+ metadata: {
90
+ sendingValue: bnAmount.toString(),
91
+ originTokenInfo: alternativeAsset,
92
+ destinationTokenInfo: fromAsset
93
+ },
94
+ name: `Transfer ${alternativeAsset.symbol} from ${alternativeChainInfo.name}`,
95
+ type: SwapStepType.XCM
96
+ };
97
+ const xcmOriginSubstrateApi = await this.chainService.getSubstrateApi(alternativeAsset.originChain).isReady;
98
+ const xcmTransfer = await createXcmExtrinsic({
99
+ originTokenInfo: alternativeAsset,
100
+ destinationTokenInfo: fromAsset,
101
+ sendingValue: bnAmount.toString(),
102
+ recipient: params.request.address,
103
+ chainInfoMap: this.chainService.getChainInfoMap(),
104
+ substrateApi: xcmOriginSubstrateApi
105
+ });
106
+ const _xcmFeeInfo = await xcmTransfer.paymentInfo(params.request.address);
107
+ const xcmFeeInfo = _xcmFeeInfo.toPrimitive();
108
+ const fee = {
109
+ feeComponent: [{
110
+ feeType: SwapFeeType.NETWORK_FEE,
111
+ amount: Math.round(xcmFeeInfo.partialFee * 1.2).toString(),
112
+ tokenSlug: _getChainNativeTokenSlug(alternativeChainInfo)
113
+ }],
114
+ defaultFeeToken: _getChainNativeTokenSlug(alternativeChainInfo),
115
+ feeOptions: [_getChainNativeTokenSlug(alternativeChainInfo)]
116
+ };
117
+ return [step, fee];
118
+ } catch (e) {
119
+ return undefined;
120
+ }
121
+ }
122
+ async getFeeOptionStep(params) {
123
+ if (!params.selectedQuote) {
124
+ return Promise.resolve(undefined);
125
+ }
126
+ const selectedFeeToken = params.selectedQuote.feeInfo.selectedFeeToken;
127
+ if (!selectedFeeToken) {
128
+ return undefined;
129
+ }
130
+ const feeStep = {
131
+ name: 'Set fee token',
132
+ type: SwapStepType.SET_FEE_TOKEN
133
+ };
134
+ try {
135
+ const substrateApi = this.chainService.getSubstrateApi(this.chain());
136
+ const chainApi = await substrateApi.isReady;
137
+ const _currentFeeAssetId = await chainApi.api.query.multiTransactionPayment.accountCurrencyMap(params.request.address);
138
+ const currentFeeAssetId = _currentFeeAssetId.toString();
139
+ const selectedFeeAsset = this.chainService.getAssetBySlug(selectedFeeToken);
140
+ const assetId = _getTokenOnChainAssetId(selectedFeeAsset);
141
+ if (currentFeeAssetId === assetId) {
142
+ return;
143
+ }
144
+ const setFeeTx = chainApi.api.tx.multiTransactionPayment.setCurrency(assetId);
145
+ const _txFee = await setFeeTx.paymentInfo(params.request.address);
146
+ const txFee = _txFee.toPrimitive();
147
+ const fee = {
148
+ feeComponent: [{
149
+ feeType: SwapFeeType.NETWORK_FEE,
150
+ amount: Math.round(txFee.partialFee).toString(),
151
+ tokenSlug: selectedFeeAsset.slug
152
+ }],
153
+ selectedFeeToken: selectedFeeAsset.slug,
154
+ defaultFeeToken: selectedFeeAsset.slug,
155
+ feeOptions: [selectedFeeAsset.slug]
156
+ };
157
+ return [feeStep, fee];
158
+ } catch (e) {
159
+ return undefined;
160
+ }
161
+ }
162
+ async getSubmitStep(params) {
163
+ if (params.selectedQuote) {
164
+ const submitStep = {
165
+ name: 'Swap',
166
+ type: SwapStepType.SWAP
167
+ };
168
+ return Promise.resolve([submitStep, params.selectedQuote.feeInfo]);
169
+ }
170
+ return Promise.resolve(undefined);
171
+ }
172
+ generateOptimalProcess(params) {
173
+ return this.swapBaseHandler.generateOptimalProcess(params, [this.getXcmStep,
174
+ // this.getFeeOptionStep.bind(this),
175
+ this.getSubmitStep]);
176
+ }
177
+ getSwapPathErrors(swapList) {
178
+ return swapList.reduce((prev, current) => {
179
+ return [...prev, ...current.errors];
180
+ }, []);
181
+ }
182
+ parseSwapPath(swapList) {
183
+ const swapAssets = this.chainService.getAssetByChainAndType(this.chain(), [_AssetType.NATIVE, _AssetType.LOCAL]);
184
+ const swapAssetIdMap = Object.values(swapAssets).reduce((accumulator, asset) => {
185
+ return {
186
+ ...accumulator,
187
+ [_getTokenOnChainAssetId(asset)]: asset
188
+ };
189
+ }, {});
190
+ const path = [];
191
+ swapList.forEach(swap => {
192
+ var _swapAssetIdMap$swap$, _swapAssetIdMap$swap$2;
193
+ const swapAssetIn = (_swapAssetIdMap$swap$ = swapAssetIdMap[swap.assetIn]) === null || _swapAssetIdMap$swap$ === void 0 ? void 0 : _swapAssetIdMap$swap$.slug;
194
+ const swapAssetOut = (_swapAssetIdMap$swap$2 = swapAssetIdMap[swap.assetOut]) === null || _swapAssetIdMap$swap$2 === void 0 ? void 0 : _swapAssetIdMap$swap$2.slug;
195
+ if (swapAssetIn && !path.includes(swapAssetIn)) {
196
+ path.push(swapAssetIn);
197
+ }
198
+ if (swapAssetOut && !path.includes(swapAssetOut)) {
199
+ path.push(swapAssetOut);
200
+ }
201
+ });
202
+ return {
203
+ path
204
+ };
205
+ }
206
+ async getSwapQuote(request) {
207
+ const fromAsset = this.chainService.getAssetBySlug(request.pair.from);
208
+ const toAsset = this.chainService.getAssetBySlug(request.pair.to);
209
+ const fromChain = this.chainService.getChainInfoByKey(fromAsset.originChain);
210
+ const fromChainNativeTokenSlug = _getChainNativeTokenSlug(fromChain);
211
+ if (!this.isReady || !this.tradeRouter) {
212
+ return new SwapError(SwapErrorType.UNKNOWN);
213
+ }
214
+ const earlyValidation = await this.validateSwapRequest(request);
215
+ if (earlyValidation.error) {
216
+ const metadata = earlyValidation.metadata;
217
+ return getEarlyHydradxValidationError(earlyValidation.error, metadata);
218
+ }
219
+ try {
220
+ const fromAssetId = _getTokenOnChainAssetId(fromAsset);
221
+ const toAssetId = _getTokenOnChainAssetId(toAsset);
222
+ const parsedFromAmount = new BigNumber(request.fromAmount).shiftedBy(-1 * _getAssetDecimals(fromAsset)).toString();
223
+ const quoteResponse = await this.tradeRouter.getBestSell(fromAssetId, toAssetId, parsedFromAmount);
224
+ const toAmount = quoteResponse.amountOut;
225
+ const minReceive = toAmount.times(1 - request.slippage).integerValue();
226
+ const txHex = quoteResponse.toTx(minReceive).hex;
227
+ const substrateApi = this.chainService.getSubstrateApi(this.chain());
228
+ const extrinsic = substrateApi.api.tx(txHex);
229
+ const paymentInfo = await extrinsic.paymentInfo(request.address);
230
+ const networkFee = {
231
+ tokenSlug: fromChainNativeTokenSlug,
232
+ amount: paymentInfo.partialFee.toString(),
233
+ feeType: SwapFeeType.NETWORK_FEE
234
+ };
235
+ const tradeFee = {
236
+ tokenSlug: toAsset.slug,
237
+ // fee is subtracted from receiving amount
238
+ amount: quoteResponse.tradeFee.toString(),
239
+ feeType: SwapFeeType.PLATFORM_FEE
240
+ };
241
+ const swapRoute = this.parseSwapPath(quoteResponse.swaps);
242
+ const swapPathErrors = this.getSwapPathErrors(quoteResponse.swaps);
243
+ if (swapPathErrors.length > 0) {
244
+ const defaultError = swapPathErrors[0]; // might parse more errors
245
+
246
+ switch (defaultError) {
247
+ case PoolError.InsufficientTradingAmount:
248
+ return new SwapError(SwapErrorType.SWAP_NOT_ENOUGH_BALANCE);
249
+ case PoolError.TradeNotAllowed:
250
+ return new SwapError(SwapErrorType.ERROR_FETCHING_QUOTE);
251
+ case PoolError.MaxInRatioExceeded:
252
+ return new SwapError(SwapErrorType.NOT_ENOUGH_LIQUIDITY);
253
+ case PoolError.UnknownError:
254
+ return new SwapError(SwapErrorType.ERROR_FETCHING_QUOTE);
255
+ case PoolError.MaxOutRatioExceeded:
256
+ return new SwapError(SwapErrorType.NOT_ENOUGH_LIQUIDITY);
257
+ }
258
+ }
259
+
260
+ // const feeTokenOptions = this.chainService.getFeeTokensByChain(this.chain());
261
+ const feeTokenOptions = [fromChainNativeTokenSlug];
262
+
263
+ // if (request.feeToken && !feeTokenOptions.includes(request.feeToken)) {
264
+ // return new SwapError(SwapErrorType.UNKNOWN);
265
+ // }
266
+
267
+ // const selectedFeeToken = request.feeToken || fromChainNativeTokenSlug;
268
+ const selectedFeeToken = fromChainNativeTokenSlug;
269
+ return {
270
+ pair: request.pair,
271
+ fromAmount: request.fromAmount,
272
+ toAmount: toAmount.toString(),
273
+ rate: calculateSwapRate(request.fromAmount, toAmount.toString(), fromAsset, toAsset),
274
+ provider: this.providerInfo,
275
+ aliveUntil: +Date.now() + (SWAP_QUOTE_TIMEOUT_MAP[this.slug] || SWAP_QUOTE_TIMEOUT_MAP.default),
276
+ feeInfo: {
277
+ feeComponent: [networkFee, tradeFee],
278
+ defaultFeeToken: fromChainNativeTokenSlug,
279
+ feeOptions: feeTokenOptions,
280
+ // TODO: enable fee options
281
+ selectedFeeToken
282
+ },
283
+ isLowLiquidity: Math.abs(quoteResponse.priceImpactPct) >= HYDRADX_LOW_LIQUIDITY_THRESHOLD,
284
+ route: swapRoute,
285
+ metadata: txHex
286
+ };
287
+ } catch (e) {
288
+ return new SwapError(SwapErrorType.ERROR_FETCHING_QUOTE);
289
+ }
290
+ }
291
+ async handleXcmStep(params) {
292
+ const pair = params.quote.pair;
293
+ const alternativeAssetSlug = getSwapAlternativeAsset(pair);
294
+ const originAsset = this.chainService.getAssetBySlug(alternativeAssetSlug);
295
+ const destinationAsset = this.chainService.getAssetBySlug(pair.from);
296
+ const substrateApi = this.chainService.getSubstrateApi(originAsset.originChain);
297
+ const chainApi = await substrateApi.isReady;
298
+ const destinationAssetBalance = await this.balanceService.getTokenFreeBalance(params.address, destinationAsset.originChain, destinationAsset.slug);
299
+ const xcmFee = params.process.totalFee[params.currentStep];
300
+ const bnAmount = new BigNumber(params.quote.fromAmount);
301
+ const bnDestinationAssetBalance = new BigNumber(destinationAssetBalance.value);
302
+ let bnTotalAmount = bnAmount.minus(bnDestinationAssetBalance);
303
+ if (_isNativeToken(originAsset)) {
304
+ const bnXcmFee = new BigNumber(xcmFee.feeComponent[0].amount); // xcm fee is paid in native token but swap token is not always native token
305
+
306
+ bnTotalAmount = bnTotalAmount.plus(bnXcmFee);
307
+ }
308
+ const xcmTransfer = await createXcmExtrinsic({
309
+ originTokenInfo: originAsset,
310
+ destinationTokenInfo: destinationAsset,
311
+ sendingValue: bnTotalAmount.toString(),
312
+ recipient: params.address,
313
+ chainInfoMap: this.chainService.getChainInfoMap(),
314
+ substrateApi: chainApi
315
+ });
316
+ const xcmData = {
317
+ originNetworkKey: originAsset.originChain,
318
+ destinationNetworkKey: destinationAsset.originChain,
319
+ from: params.address,
320
+ to: params.address,
321
+ value: bnTotalAmount.toString(),
322
+ tokenSlug: originAsset.slug,
323
+ showExtraWarning: true
324
+ };
325
+ return {
326
+ txChain: originAsset.originChain,
327
+ extrinsic: xcmTransfer,
328
+ transferNativeAmount: _isNativeToken(originAsset) ? bnTotalAmount.toString() : '0',
329
+ extrinsicType: ExtrinsicType.TRANSFER_XCM,
330
+ chainType: ChainType.SUBSTRATE,
331
+ txData: xcmData
332
+ };
333
+ }
334
+ async handleSetFeeStep(params) {
335
+ var _swapFeeInfo$selected;
336
+ const substrateApi = this.chainService.getSubstrateApi(this.chain());
337
+ const chainApi = await substrateApi.isReady;
338
+ const swapStepIndex = params.process.steps.findIndex(step => step.type === SwapStepType.SWAP);
339
+ if (swapStepIndex <= -1) {
340
+ return Promise.reject(new TransactionError(BasicTxErrorType.INTERNAL_ERROR));
341
+ }
342
+ const swapFeeInfo = params.process.totalFee[swapStepIndex];
343
+ const selectedFeeTokenSlug = (_swapFeeInfo$selected = swapFeeInfo.selectedFeeToken) !== null && _swapFeeInfo$selected !== void 0 ? _swapFeeInfo$selected : swapFeeInfo.defaultFeeToken;
344
+ const selectedFeeAsset = this.chainService.getAssetBySlug(selectedFeeTokenSlug);
345
+ const extrinsic = chainApi.api.tx.multiTransactionPayment.setCurrency(_getTokenOnChainAssetId(selectedFeeAsset));
346
+ const txData = {
347
+ selectedFeeToken: selectedFeeTokenSlug
348
+ };
349
+ return {
350
+ txChain: this.chain(),
351
+ extrinsic,
352
+ // extrinsicType: ExtrinsicType.SET_FEE_TOKEN,
353
+ extrinsicType: ExtrinsicType.SWAP,
354
+ chainType: ChainType.SUBSTRATE,
355
+ txData
356
+ };
357
+ }
358
+ async handleSubmitStep(params) {
359
+ const txHex = params.quote.metadata;
360
+ const fromAsset = this.chainService.getAssetBySlug(params.quote.pair.from);
361
+ const substrateApi = this.chainService.getSubstrateApi(this.chain());
362
+ const chainApi = await substrateApi.isReady;
363
+ const txData = {
364
+ provider: this.providerInfo,
365
+ quote: params.quote,
366
+ address: params.address,
367
+ slippage: params.slippage,
368
+ txHex,
369
+ process: params.process
370
+ };
371
+ let extrinsic;
372
+ const txList = [];
373
+ const swapTx = chainApi.api.tx(txHex);
374
+ const _referral = await chainApi.api.query.referrals.linkedAccounts(params.address);
375
+ const referral = _referral === null || _referral === void 0 ? void 0 : _referral.toString();
376
+ const needSetReferral = !referral || referral === '';
377
+ const steps = params.process.steps.map(step => step.type);
378
+ const needSetFeeToken = steps.includes(SwapStepType.SET_FEE_TOKEN);
379
+ if (!needSetReferral && !needSetFeeToken) {
380
+ extrinsic = swapTx;
381
+ } else {
382
+ if (needSetReferral) {
383
+ txList.push(chainApi.api.tx.referrals.linkCode(this.referralCode));
384
+ }
385
+ if (needSetFeeToken) {
386
+ const nativeTokenInfo = this.chainService.getNativeTokenInfo(this.chain());
387
+ txList.push(chainApi.api.tx.multiTransactionPayment.setCurrency(_getTokenOnChainAssetId(nativeTokenInfo)));
388
+ }
389
+ txList.push(swapTx);
390
+ extrinsic = chainApi.api.tx.utility.batchAll(txList);
391
+ }
392
+ return {
393
+ txChain: fromAsset.originChain,
394
+ txData,
395
+ extrinsic,
396
+ transferNativeAmount: _isNativeToken(fromAsset) ? params.quote.fromAmount : '0',
397
+ // todo
398
+ extrinsicType: ExtrinsicType.SWAP,
399
+ chainType: ChainType.SUBSTRATE
400
+ };
401
+ }
402
+ handleSwapProcess(params) {
403
+ const {
404
+ currentStep,
405
+ process
406
+ } = params;
407
+ const type = process.steps[currentStep].type;
408
+ switch (type) {
409
+ case SwapStepType.DEFAULT:
410
+ return Promise.reject(new TransactionError(BasicTxErrorType.UNSUPPORTED));
411
+ case SwapStepType.XCM:
412
+ return this.handleXcmStep(params);
413
+ case SwapStepType.SET_FEE_TOKEN:
414
+ return this.handleSetFeeStep(params);
415
+ case SwapStepType.SWAP:
416
+ return this.handleSubmitStep(params);
417
+ default:
418
+ return this.handleSubmitStep(params);
419
+ }
420
+ }
421
+ async validateSwapProcess(params) {
422
+ const amount = params.selectedQuote.fromAmount;
423
+ const bnAmount = new BigNumber(amount);
424
+ if (bnAmount.lte(0)) {
425
+ return [new TransactionError(BasicTxErrorType.INVALID_PARAMS, 'Amount must be greater than 0')];
426
+ }
427
+ let isXcmOk = false;
428
+ for (const [index, step] of params.process.steps.entries()) {
429
+ const getErrors = async () => {
430
+ switch (step.type) {
431
+ case SwapStepType.DEFAULT:
432
+ return Promise.resolve([]);
433
+ case SwapStepType.XCM:
434
+ return this.swapBaseHandler.validateXcmStep(params, index);
435
+ case SwapStepType.SET_FEE_TOKEN:
436
+ return this.swapBaseHandler.validateSetFeeTokenStep(params, index);
437
+ default:
438
+ return this.swapBaseHandler.validateSwapStep(params, isXcmOk, index);
439
+ }
440
+ };
441
+ const errors = await getErrors();
442
+ if (errors.length) {
443
+ return errors;
444
+ } else if (step.type === SwapStepType.XCM) {
445
+ isXcmOk = true;
446
+ }
447
+ }
448
+ return [];
449
+ }
450
+ async validateSwapRequest(request) {
451
+ const fromAsset = this.chainService.getAssetBySlug(request.pair.from);
452
+ const toAsset = this.chainService.getAssetBySlug(request.pair.to);
453
+ const fromAssetId = _getTokenOnChainAssetId(fromAsset);
454
+ const toAssetId = _getTokenOnChainAssetId(toAsset);
455
+ try {
456
+ var _this$tradeRouter, _this$tradeRouter2;
457
+ // todo: might need to optimize for performance, but prioritize safety for now
458
+ const allAssets = await ((_this$tradeRouter = this.tradeRouter) === null || _this$tradeRouter === void 0 ? void 0 : _this$tradeRouter.getAllAssets());
459
+ if (!allAssets) {
460
+ return {
461
+ error: SwapErrorType.UNKNOWN
462
+ };
463
+ }
464
+ const supportedFromAsset = allAssets.find(asset => asset.id === fromAssetId && asset.symbol === fromAsset.symbol);
465
+ const supportedToAsset = allAssets.find(asset => asset.id === toAssetId && asset.symbol === toAsset.symbol);
466
+ if (!supportedFromAsset || !supportedToAsset) {
467
+ return {
468
+ error: SwapErrorType.ASSET_NOT_SUPPORTED
469
+ };
470
+ }
471
+ const assetPairs = await ((_this$tradeRouter2 = this.tradeRouter) === null || _this$tradeRouter2 === void 0 ? void 0 : _this$tradeRouter2.getAssetPairs(fromAssetId));
472
+ if (!assetPairs) {
473
+ return {
474
+ error: SwapErrorType.UNKNOWN
475
+ };
476
+ }
477
+ const pairedToAsset = assetPairs.find(supportedToAsset => supportedToAsset.id === toAssetId && supportedToAsset.symbol === toAsset.symbol);
478
+ if (!pairedToAsset) {
479
+ return {
480
+ error: SwapErrorType.ASSET_NOT_SUPPORTED
481
+ };
482
+ }
483
+ if (!(fromAsset.originChain === this.chain() && toAsset.originChain === this.chain())) {
484
+ return {
485
+ error: SwapErrorType.ASSET_NOT_SUPPORTED
486
+ };
487
+ }
488
+ if (!fromAssetId || !toAssetId) {
489
+ return {
490
+ error: SwapErrorType.UNKNOWN
491
+ };
492
+ }
493
+ const bnAmount = new BigNumber(request.fromAmount);
494
+ if (bnAmount.lte(0)) {
495
+ return {
496
+ error: SwapErrorType.AMOUNT_CANNOT_BE_ZERO
497
+ };
498
+ }
499
+ return {
500
+ metadata: {
501
+ chain: this.chainService.getChainInfoByKey(this.chain())
502
+ }
503
+ };
504
+ } catch (e) {
505
+ return {
506
+ error: SwapErrorType.UNKNOWN
507
+ };
508
+ }
509
+ }
510
+ get referralCode() {
511
+ if (this.isTestnet) {
512
+ return HYDRADX_TESTNET_SUBWALLET_REFERRAL_CODE;
513
+ }
514
+ return HYDRADX_SUBWALLET_REFERRAL_CODE;
515
+ }
516
+ get referralAccount() {
517
+ if (this.isTestnet) {
518
+ return HYDRADX_TESTNET_SUBWALLET_REFERRAL_ACCOUNT;
519
+ }
520
+ return HYDRADX_SUBWALLET_REFERRAL_ACCOUNT;
521
+ }
522
+ }
@@ -0,0 +1,32 @@
1
+ import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
2
+ import KoniState from '@subwallet/extension-base/koni/background/handlers/State';
3
+ import { ServiceStatus, ServiceWithProcessInterface, StoppableServiceInterface } from '@subwallet/extension-base/services/base/types';
4
+ import { OptimalSwapPath, OptimalSwapPathParams, SwapPair, SwapQuoteResponse, SwapRequest, SwapRequestResult, SwapSubmitParams, SwapSubmitStepData, ValidateSwapProcessParams } from '@subwallet/extension-base/types/swap';
5
+ import { PromiseHandler } from '@subwallet/extension-base/utils';
6
+ export declare class SwapService implements ServiceWithProcessInterface, StoppableServiceInterface {
7
+ protected readonly state: KoniState;
8
+ private eventService;
9
+ private readonly chainService;
10
+ private swapPairSubject;
11
+ private handlers;
12
+ startPromiseHandler: PromiseHandler<void>;
13
+ stopPromiseHandler: PromiseHandler<void>;
14
+ status: ServiceStatus;
15
+ constructor(state: KoniState);
16
+ private askProvidersForQuote;
17
+ private getDefaultProcess;
18
+ generateOptimalProcess(params: OptimalSwapPathParams): Promise<OptimalSwapPath>;
19
+ handleSwapRequest(request: SwapRequest): Promise<SwapRequestResult>;
20
+ getLatestQuotes(request: SwapRequest): Promise<SwapQuoteResponse>;
21
+ private initHandlers;
22
+ init(): Promise<void>;
23
+ start(): Promise<void>;
24
+ stop(): Promise<void>;
25
+ waitForStarted(): Promise<void>;
26
+ waitForStopped(): Promise<void>;
27
+ getSwapPairs(): SwapPair[];
28
+ private getSwapPairMetadata;
29
+ validateSwapProcess(params: ValidateSwapProcessParams): Promise<TransactionError[]>;
30
+ handleSwapProcess(params: SwapSubmitParams): Promise<SwapSubmitStepData>;
31
+ subscribeSwapPairs(callback: (pairs: SwapPair[]) => void): import("rxjs").Subscription;
32
+ }