@subwallet/extension-base 1.3.39-0 → 1.3.41-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/cjs/constants/environment.js +4 -2
- package/cjs/koni/background/handlers/Extension.js +114 -105
- package/cjs/packageInfo.js +1 -1
- package/cjs/services/balance-service/helpers/subscribe/evm.js +6 -1
- package/cjs/services/balance-service/transfer/xcm/index.js +26 -29
- package/cjs/services/balance-service/transfer/xcm/utils.js +52 -56
- package/cjs/services/chain-service/constants.js +6 -5
- package/cjs/services/chain-service/handler/CardanoApi.js +25 -35
- package/cjs/services/chain-service/index.js +4 -0
- package/cjs/services/chain-service/utils/patch.js +1 -1
- package/cjs/services/earning-service/handlers/native-staking/tao.js +4 -38
- package/cjs/services/earning-service/handlers/special.js +28 -36
- package/cjs/services/swap-service/handler/base-handler.js +58 -53
- package/cjs/services/swap-service/handler/chainflip-handler.js +29 -18
- package/cjs/services/swap-service/handler/kyber-handler.js +46 -34
- package/cjs/services/swap-service/handler/simpleswap-handler.js +79 -43
- package/cjs/services/swap-service/handler/uniswap-handler.js +5 -12
- package/cjs/services/swap-service/utils.js +48 -37
- package/cjs/types/environment.js +19 -0
- package/cjs/utils/environment.js +30 -2
- package/cjs/utils/fee/transfer.js +41 -33
- package/constants/environment.d.ts +1 -0
- package/constants/environment.js +2 -1
- package/koni/background/handlers/Extension.js +52 -43
- package/package.json +12 -7
- package/packageInfo.js +1 -1
- package/services/balance-service/helpers/subscribe/evm.js +6 -1
- package/services/balance-service/transfer/xcm/index.d.ts +1 -2
- package/services/balance-service/transfer/xcm/index.js +23 -26
- package/services/balance-service/transfer/xcm/utils.d.ts +38 -6
- package/services/balance-service/transfer/xcm/utils.js +51 -55
- package/services/chain-service/constants.d.ts +1 -0
- package/services/chain-service/constants.js +6 -5
- package/services/chain-service/handler/CardanoApi.d.ts +1 -5
- package/services/chain-service/handler/CardanoApi.js +26 -34
- package/services/chain-service/index.js +4 -0
- package/services/chain-service/utils/patch.js +1 -1
- package/services/earning-service/handlers/native-staking/tao.d.ts +0 -11
- package/services/earning-service/handlers/native-staking/tao.js +4 -24
- package/services/earning-service/handlers/special.js +12 -20
- package/services/swap-service/handler/base-handler.js +11 -6
- package/services/swap-service/handler/chainflip-handler.d.ts +0 -2
- package/services/swap-service/handler/chainflip-handler.js +25 -13
- package/services/swap-service/handler/kyber-handler.d.ts +0 -1
- package/services/swap-service/handler/kyber-handler.js +46 -33
- package/services/swap-service/handler/simpleswap-handler.d.ts +0 -1
- package/services/swap-service/handler/simpleswap-handler.js +80 -43
- package/services/swap-service/handler/uniswap-handler.js +6 -13
- package/services/swap-service/utils.d.ts +0 -13
- package/services/swap-service/utils.js +48 -34
- package/types/environment.d.ts +9 -0
- package/types/environment.js +13 -0
- package/utils/environment.d.ts +2 -0
- package/utils/environment.js +27 -1
- package/utils/fee/transfer.js +11 -3
|
@@ -17,11 +17,12 @@ var _acrossBridge = require("@subwallet/extension-base/services/balance-service/
|
|
|
17
17
|
var _availBridge = require("@subwallet/extension-base/services/balance-service/transfer/xcm/availBridge");
|
|
18
18
|
var _polygonBridge = require("@subwallet/extension-base/services/balance-service/transfer/xcm/polygonBridge");
|
|
19
19
|
var _posBridge = require("@subwallet/extension-base/services/balance-service/transfer/xcm/posBridge");
|
|
20
|
-
var _utils = require("@subwallet/extension-base/services/
|
|
21
|
-
var _utils2 = require("@subwallet/extension-base/services/
|
|
20
|
+
var _utils = require("@subwallet/extension-base/services/balance-service/transfer/xcm/utils");
|
|
21
|
+
var _utils2 = require("@subwallet/extension-base/services/chain-service/utils");
|
|
22
|
+
var _utils3 = require("@subwallet/extension-base/services/fee-service/utils");
|
|
22
23
|
var _tokenPayFee = require("@subwallet/extension-base/services/fee-service/utils/tokenPayFee");
|
|
23
24
|
var _helpers = require("@subwallet/extension-base/services/transaction-service/helpers");
|
|
24
|
-
var
|
|
25
|
+
var _utils4 = require("@subwallet/extension-base/utils");
|
|
25
26
|
var _keyring = require("@subwallet/keyring");
|
|
26
27
|
var _bignumber = _interopRequireDefault(require("bignumber.js"));
|
|
27
28
|
var _util = require("@polkadot/util");
|
|
@@ -33,18 +34,18 @@ var _combine = require("./combine");
|
|
|
33
34
|
const detectTransferTxType = (srcToken, srcChain, destChain) => {
|
|
34
35
|
const isXcmTransfer = srcChain.slug !== destChain.slug;
|
|
35
36
|
if (isXcmTransfer) {
|
|
36
|
-
const isAvailBridgeFromEvm = (0,
|
|
37
|
-
const isSnowBridgeEvmTransfer = (0,
|
|
37
|
+
const isAvailBridgeFromEvm = (0, _utils2._isPureEvmChain)(srcChain) && (0, _availBridge.isAvailChainBridge)(destChain.slug);
|
|
38
|
+
const isSnowBridgeEvmTransfer = (0, _utils2._isPureEvmChain)(srcChain) && (0, _xcmParser._isSnowBridgeXcm)(srcChain, destChain) && !isAvailBridgeFromEvm;
|
|
38
39
|
const isPolygonBridgeTransfer = (0, _polygonBridge._isPolygonChainBridge)(srcChain.slug, destChain.slug);
|
|
39
40
|
const isPosBridgeTransfer = (0, _posBridge._isPosChainBridge)(srcChain.slug, destChain.slug);
|
|
40
41
|
const isAcrossBridgeTransfer = (0, _acrossBridge._isAcrossChainBridge)(srcChain.slug, destChain.slug);
|
|
41
42
|
return isAvailBridgeFromEvm || isSnowBridgeEvmTransfer || isPolygonBridgeTransfer || isPosBridgeTransfer || isAcrossBridgeTransfer ? 'evm' : 'substrate';
|
|
42
43
|
} else {
|
|
43
|
-
if ((0,
|
|
44
|
+
if ((0, _utils2._isChainEvmCompatible)(srcChain) && (0, _utils2._isTokenTransferredByEvm)(srcToken)) {
|
|
44
45
|
return 'evm';
|
|
45
|
-
} else if ((0,
|
|
46
|
+
} else if ((0, _utils2._isChainTonCompatible)(srcChain) && (0, _utils2._isTokenTransferredByTon)(srcToken)) {
|
|
46
47
|
return 'ton';
|
|
47
|
-
} else if ((0,
|
|
48
|
+
} else if ((0, _utils2._isChainCardanoCompatible)(srcChain) && (0, _utils2._isTokenTransferredByCardano)(srcToken)) {
|
|
48
49
|
return 'cardano';
|
|
49
50
|
} else {
|
|
50
51
|
return 'substrate';
|
|
@@ -70,7 +71,7 @@ const calculateMaxTransferable = async (id, request, freeBalance, fee) => {
|
|
|
70
71
|
} else {
|
|
71
72
|
maxTransferableAmount = await calculateTransferMaxTransferable(id, request, freeBalance, fee);
|
|
72
73
|
}
|
|
73
|
-
maxTransferableAmount.feePercentageSpecialCase =
|
|
74
|
+
maxTransferableAmount.feePercentageSpecialCase = _utils3.FEE_COVERAGE_PERCENTAGE_SPECIAL_CASE;
|
|
74
75
|
return maxTransferableAmount;
|
|
75
76
|
};
|
|
76
77
|
exports.calculateMaxTransferable = calculateMaxTransferable;
|
|
@@ -100,15 +101,15 @@ const calculateTransferMaxTransferable = async (id, request, freeBalance, fee) =
|
|
|
100
101
|
const substrateAddress = fakeAddress; // todo: move this
|
|
101
102
|
const evmAddress = (0, _util.u8aToHex)((0, _utilCrypto.addressToEvm)(fakeAddress)); // todo: move this
|
|
102
103
|
|
|
103
|
-
const recipient = (0,
|
|
104
|
+
const recipient = (0, _utils2._isChainEvmCompatible)(destChain) ? evmAddress : substrateAddress;
|
|
104
105
|
try {
|
|
105
106
|
let transaction;
|
|
106
|
-
if ((0, _utilCrypto.isEthereumAddress)(address) && (0, _utilCrypto.isEthereumAddress)(recipient) && (0,
|
|
107
|
+
if ((0, _utilCrypto.isEthereumAddress)(address) && (0, _utilCrypto.isEthereumAddress)(recipient) && (0, _utils2._isTokenTransferredByEvm)(srcToken)) {
|
|
107
108
|
// todo: refactor: merge getERC20TransactionObject & getEVMTransactionObject
|
|
108
109
|
// Estimate with EVM API
|
|
109
|
-
if ((0,
|
|
110
|
+
if ((0, _utils2._isTokenEvmSmartContract)(srcToken) || (0, _utils2._isLocalToken)(srcToken)) {
|
|
110
111
|
[transaction,, error] = await (0, _smartContract.getERC20TransactionObject)({
|
|
111
|
-
assetAddress: (0,
|
|
112
|
+
assetAddress: (0, _utils2._getContractAddressOfToken)(srcToken),
|
|
112
113
|
chain: srcChain.slug,
|
|
113
114
|
evmApi,
|
|
114
115
|
feeCustom,
|
|
@@ -134,7 +135,7 @@ const calculateTransferMaxTransferable = async (id, request, freeBalance, fee) =
|
|
|
134
135
|
fallbackFee: true
|
|
135
136
|
});
|
|
136
137
|
}
|
|
137
|
-
} else if ((0, _keyring.isTonAddress)(address) && (0,
|
|
138
|
+
} else if ((0, _keyring.isTonAddress)(address) && (0, _utils2._isTokenTransferredByTon)(srcToken)) {
|
|
138
139
|
[transaction] = await (0, _tonTransfer.createTonTransaction)({
|
|
139
140
|
tokenInfo: srcToken,
|
|
140
141
|
from: address,
|
|
@@ -145,7 +146,7 @@ const calculateTransferMaxTransferable = async (id, request, freeBalance, fee) =
|
|
|
145
146
|
// currently not used
|
|
146
147
|
tonApi
|
|
147
148
|
});
|
|
148
|
-
} else if ((0, _keyring.isCardanoAddress)(address) && (0,
|
|
149
|
+
} else if ((0, _keyring.isCardanoAddress)(address) && (0, _utils2._isTokenTransferredByCardano)(srcToken)) {
|
|
149
150
|
[transaction] = await (0, _cardanoTransfer.createCardanoTransaction)({
|
|
150
151
|
tokenInfo: srcToken,
|
|
151
152
|
from: address,
|
|
@@ -252,8 +253,8 @@ const calculateTransferMaxTransferable = async (id, request, freeBalance, fee) =
|
|
|
252
253
|
}
|
|
253
254
|
if (isTransferLocalTokenAndPayThatTokenAsFee && feeChainType === 'substrate') {
|
|
254
255
|
if (_constants._SUPPORT_TOKEN_PAY_FEE_GROUP.assetHub.includes(srcChain.slug)) {
|
|
255
|
-
const estimatedFeeNative = (BigInt(estimatedFee) * BigInt(
|
|
256
|
-
const estimatedFeeLocal = await (0,
|
|
256
|
+
const estimatedFeeNative = (BigInt(estimatedFee) * BigInt(_utils3.FEE_COVERAGE_PERCENTAGE_SPECIAL_CASE) / BigInt(100)).toString();
|
|
257
|
+
const estimatedFeeLocal = await (0, _utils3.calculateToAmountByReservePool)(substrateApi.api, nativeToken, srcToken, estimatedFeeNative);
|
|
257
258
|
maxTransferable = (0, _bignumber.default)(freeBalance.value).minus(estimatedFeeLocal);
|
|
258
259
|
} else if (_constants._SUPPORT_TOKEN_PAY_FEE_GROUP.hydration.includes(srcChain.slug)) {
|
|
259
260
|
const rate = await (0, _tokenPayFee.getHydrationRate)(address, nativeToken, srcToken);
|
|
@@ -269,14 +270,14 @@ const calculateTransferMaxTransferable = async (id, request, freeBalance, fee) =
|
|
|
269
270
|
} else if (isTransferNativeTokenAndPayLocalTokenAsFee) {
|
|
270
271
|
maxTransferable = (0, _bignumber.default)(freeBalance.value);
|
|
271
272
|
} else {
|
|
272
|
-
if (!(0,
|
|
273
|
+
if (!(0, _utils2._isNativeToken)(srcToken)) {
|
|
273
274
|
maxTransferable = (0, _bignumber.default)(freeBalance.value);
|
|
274
275
|
} else {
|
|
275
276
|
maxTransferable = (0, _bignumber.default)(freeBalance.value).minus(new _bignumber.default(estimatedFee));
|
|
276
277
|
}
|
|
277
278
|
}
|
|
278
279
|
return {
|
|
279
|
-
maxTransferable: maxTransferable.gt(
|
|
280
|
+
maxTransferable: maxTransferable.gt(_utils4.BN_ZERO) ? maxTransferable.toFixed(0) || '0' : '0',
|
|
280
281
|
feeOptions: feeOptions,
|
|
281
282
|
feeType: feeChainType,
|
|
282
283
|
id: id,
|
|
@@ -305,9 +306,9 @@ const calculateXcmMaxTransferable = async (id, request, freeBalance, fee) => {
|
|
|
305
306
|
let feeOptions;
|
|
306
307
|
let maxTransferable;
|
|
307
308
|
let error;
|
|
308
|
-
const isAvailBridgeFromEvm = (0,
|
|
309
|
-
const isAvailBridgeFromAvail = (0, _availBridge.isAvailChainBridge)(srcChain.slug) && (0,
|
|
310
|
-
const isSnowBridgeEvmTransfer = (0,
|
|
309
|
+
const isAvailBridgeFromEvm = (0, _utils2._isPureEvmChain)(srcChain) && (0, _availBridge.isAvailChainBridge)(destChain.slug);
|
|
310
|
+
const isAvailBridgeFromAvail = (0, _availBridge.isAvailChainBridge)(srcChain.slug) && (0, _utils2._isPureEvmChain)(destChain);
|
|
311
|
+
const isSnowBridgeEvmTransfer = (0, _utils2._isPureEvmChain)(srcChain) && (0, _xcmParser._isSnowBridgeXcm)(srcChain, destChain) && !isAvailBridgeFromEvm;
|
|
311
312
|
const isPolygonBridgeTransfer = (0, _polygonBridge._isPolygonChainBridge)(srcChain.slug, destChain.slug);
|
|
312
313
|
const isPosBridgeTransfer = (0, _posBridge._isPosChainBridge)(srcChain.slug, destChain.slug);
|
|
313
314
|
const isAcrossBridgeTransfer = (0, _acrossBridge._isAcrossChainBridge)(srcChain.slug, destChain.slug);
|
|
@@ -316,7 +317,7 @@ const calculateXcmMaxTransferable = async (id, request, freeBalance, fee) => {
|
|
|
316
317
|
const substrateAddress = fakeAddress; // todo: move this
|
|
317
318
|
const evmAddress = (0, _util.u8aToHex)((0, _utilCrypto.addressToEvm)(fakeAddress)); // todo: move this
|
|
318
319
|
const bnFreeBalance = new _bignumber.default(freeBalance.value);
|
|
319
|
-
const recipient = (0,
|
|
320
|
+
const recipient = (0, _utils2._isChainEvmCompatible)(destChain) ? evmAddress : substrateAddress;
|
|
320
321
|
if (!destToken) {
|
|
321
322
|
throw Error('Destination token is not available');
|
|
322
323
|
}
|
|
@@ -342,9 +343,9 @@ const calculateXcmMaxTransferable = async (id, request, freeBalance, fee) => {
|
|
|
342
343
|
} else if (isAcrossBridgeTransfer) {
|
|
343
344
|
funcCreateExtrinsic = _xcm.createAcrossBridgeExtrinsic;
|
|
344
345
|
if ((0, _acrossBridge._isAcrossTestnetBridge)(srcChain.slug)) {
|
|
345
|
-
params.sendingValue = (0, _bignumber.default)(0.0037).shiftedBy((0,
|
|
346
|
+
params.sendingValue = (0, _bignumber.default)(0.0037).shiftedBy((0, _utils2._getAssetDecimals)(srcToken)).toFixed(0, 1);
|
|
346
347
|
} else {
|
|
347
|
-
params.sendingValue = (0, _bignumber.default)(1).shiftedBy((0,
|
|
348
|
+
params.sendingValue = (0, _bignumber.default)(1).shiftedBy((0, _utils2._getAssetDecimals)(srcToken)).toFixed(0, 1);
|
|
348
349
|
}
|
|
349
350
|
} else if (isSnowBridgeEvmTransfer) {
|
|
350
351
|
funcCreateExtrinsic = _xcm.createSnowBridgeExtrinsic;
|
|
@@ -379,8 +380,15 @@ const calculateXcmMaxTransferable = async (id, request, freeBalance, fee) => {
|
|
|
379
380
|
} else if (feeChainType === 'substrate') {
|
|
380
381
|
// Calculate fee for substrate transaction
|
|
381
382
|
if (isSubstrateXcm) {
|
|
382
|
-
const
|
|
383
|
-
|
|
383
|
+
const xcmFeeInfo = await (0, _utils.estimateXcmFee)({
|
|
384
|
+
fromChainInfo: params.originChain,
|
|
385
|
+
fromTokenInfo: params.originTokenInfo,
|
|
386
|
+
toChainInfo: params.destinationChain,
|
|
387
|
+
recipient: params.recipient,
|
|
388
|
+
sender: params.sender,
|
|
389
|
+
value: params.sendingValue
|
|
390
|
+
});
|
|
391
|
+
estimatedFee = (xcmFeeInfo === null || xcmFeeInfo === void 0 ? void 0 : xcmFeeInfo.origin.fee) || '0';
|
|
384
392
|
} else {
|
|
385
393
|
try {
|
|
386
394
|
var _paymentInfo$partialF2;
|
|
@@ -423,11 +431,11 @@ const calculateXcmMaxTransferable = async (id, request, freeBalance, fee) => {
|
|
|
423
431
|
console.warn('Unable to estimate fee', e);
|
|
424
432
|
}
|
|
425
433
|
if (!destToken) {
|
|
426
|
-
maxTransferable =
|
|
434
|
+
maxTransferable = _utils4.BN_ZERO;
|
|
427
435
|
} else if (isTransferLocalTokenAndPayThatTokenAsFee && feeChainType === 'substrate') {
|
|
428
436
|
if (_constants._SUPPORT_TOKEN_PAY_FEE_GROUP.assetHub.includes(srcChain.slug)) {
|
|
429
|
-
const estimatedFeeNative = (BigInt(estimatedFee) * BigInt(
|
|
430
|
-
const estimatedFeeLocal = await (0,
|
|
437
|
+
const estimatedFeeNative = (BigInt(estimatedFee) * BigInt(_utils3.FEE_COVERAGE_PERCENTAGE_SPECIAL_CASE) / BigInt(100)).toString();
|
|
438
|
+
const estimatedFeeLocal = await (0, _utils3.calculateToAmountByReservePool)(substrateApi.api, nativeToken, srcToken, estimatedFeeNative);
|
|
431
439
|
maxTransferable = bnFreeBalance.minus(estimatedFeeLocal);
|
|
432
440
|
} else if (_constants._SUPPORT_TOKEN_PAY_FEE_GROUP.hydration.includes(srcChain.slug)) {
|
|
433
441
|
const rate = await (0, _tokenPayFee.getHydrationRate)(address, nativeToken, srcToken);
|
|
@@ -443,18 +451,18 @@ const calculateXcmMaxTransferable = async (id, request, freeBalance, fee) => {
|
|
|
443
451
|
} else if (isTransferNativeTokenAndPayLocalTokenAsFee) {
|
|
444
452
|
maxTransferable = bnFreeBalance;
|
|
445
453
|
} else {
|
|
446
|
-
if (!(0,
|
|
454
|
+
if (!(0, _utils2._isNativeToken)(srcToken)) {
|
|
447
455
|
maxTransferable = bnFreeBalance;
|
|
448
456
|
} else {
|
|
449
457
|
maxTransferable = bnFreeBalance.minus((0, _bignumber.default)(estimatedFee).multipliedBy(_constants.XCM_FEE_RATIO));
|
|
450
458
|
}
|
|
451
459
|
}
|
|
452
460
|
if (isAvailBridgeFromAvail) {
|
|
453
|
-
const addedAmount = (0, _bignumber.default)(1).shiftedBy((0,
|
|
461
|
+
const addedAmount = (0, _bignumber.default)(1).shiftedBy((0, _utils2._getAssetDecimals)(srcToken));
|
|
454
462
|
maxTransferable = maxTransferable.minus(addedAmount);
|
|
455
463
|
}
|
|
456
464
|
return {
|
|
457
|
-
maxTransferable: maxTransferable.gt(
|
|
465
|
+
maxTransferable: maxTransferable.gt(_utils4.BN_ZERO) ? maxTransferable.toFixed(0) : '0',
|
|
458
466
|
feeOptions: feeOptions,
|
|
459
467
|
feeType: feeChainType,
|
|
460
468
|
id: id,
|
package/constants/environment.js
CHANGED
|
@@ -5,4 +5,5 @@ const PRODUCTION_BRANCHES = ['master', 'webapp', 'webapp-dev'];
|
|
|
5
5
|
const branchName = process.env.BRANCH_NAME || 'subwallet-dev';
|
|
6
6
|
export const isProductionMode = PRODUCTION_BRANCHES.indexOf(branchName) > -1;
|
|
7
7
|
export const BACKEND_API_URL = process.env.SUBWALLET_API || (isProductionMode ? 'https://sw-services.subwallet.app/api' : 'https://be-dev.subwallet.app/api');
|
|
8
|
-
export const BACKEND_PRICE_HISTORY_URL = process.env.SUBWALLET_PRICE_HISTORY_API || (isProductionMode ? 'https://price-history.subwallet.app/api' : 'https://price-history-dev.subwallet.app/api');
|
|
8
|
+
export const BACKEND_PRICE_HISTORY_URL = process.env.SUBWALLET_PRICE_HISTORY_API || (isProductionMode ? 'https://price-history.subwallet.app/api' : 'https://price-history-dev.subwallet.app/api');
|
|
9
|
+
export const SW_EXTERNAL_SERVICES_API = process.env.SW_EXTERNAL_SERVICES_API || (isProductionMode ? 'https://external-services.subwallet.app' : 'https://external-services-dev.subwallet.app');
|
|
@@ -34,6 +34,7 @@ import { _isAcrossChainBridge, getAcrossQuote } from '@subwallet/extension-base/
|
|
|
34
34
|
import { getClaimTxOnAvail, getClaimTxOnEthereum, isAvailChainBridge } from '@subwallet/extension-base/services/balance-service/transfer/xcm/availBridge';
|
|
35
35
|
import { _isPolygonChainBridge, getClaimPolygonBridge, isClaimedPolygonBridge } from '@subwallet/extension-base/services/balance-service/transfer/xcm/polygonBridge';
|
|
36
36
|
import { _isPosChainBridge, getClaimPosBridge } from '@subwallet/extension-base/services/balance-service/transfer/xcm/posBridge';
|
|
37
|
+
import { estimateXcmFee } from '@subwallet/extension-base/services/balance-service/transfer/xcm/utils';
|
|
37
38
|
import { _DEFAULT_MANTA_ZK_CHAIN, _MANTA_ZK_CHAIN_GROUP, _ZK_ASSET_PREFIX } from '@subwallet/extension-base/services/chain-service/constants';
|
|
38
39
|
import { _ChainConnectionStatus } from '@subwallet/extension-base/services/chain-service/types';
|
|
39
40
|
import { _getAssetDecimals, _getAssetSymbol, _getChainNativeTokenBasicInfo, _getContractAddressOfToken, _getEvmChainId, _isAssetSmartContractNft, _isChainEnabled, _isChainEvmCompatible, _isChainSubstrateCompatible, _isCustomAsset, _isLocalToken, _isMantaZkAsset, _isNativeToken, _isNativeTokenBySlug, _isPureEvmChain, _isTokenEvmSmartContract, _isTokenTransferredByCardano, _isTokenTransferredByEvm, _isTokenTransferredByTon } from '@subwallet/extension-base/services/chain-service/utils';
|
|
@@ -1451,7 +1452,7 @@ export default class KoniExtension {
|
|
|
1451
1452
|
const isPosBridgeTransfer = _isPosChainBridge(originNetworkKey, destinationNetworkKey);
|
|
1452
1453
|
const isAcrossBridgeTransfer = _isAcrossChainBridge(originNetworkKey, destinationNetworkKey);
|
|
1453
1454
|
const extrinsicType = ExtrinsicType.TRANSFER_XCM;
|
|
1454
|
-
const isSubstrateXcm = !(isAvailBridgeFromEvm || isAvailBridgeFromAvail || isSnowBridgeEvmTransfer || isPolygonBridgeTransfer || isPosBridgeTransfer);
|
|
1455
|
+
const isSubstrateXcm = !(isAvailBridgeFromEvm || isAvailBridgeFromAvail || isSnowBridgeEvmTransfer || isPolygonBridgeTransfer || isPosBridgeTransfer || isAcrossBridgeTransfer);
|
|
1455
1456
|
const isTransferNative = this.#koniState.getNativeTokenInfo(originNetworkKey).slug === tokenSlug;
|
|
1456
1457
|
const isTransferLocalTokenAndPayThatTokenAsFee = !isTransferNative && tokenSlug === tokenPayFeeSlug;
|
|
1457
1458
|
let xcmFeeDryRun;
|
|
@@ -1497,10 +1498,16 @@ export default class KoniExtension {
|
|
|
1497
1498
|
feeInfo
|
|
1498
1499
|
};
|
|
1499
1500
|
extrinsic = await funcCreateExtrinsic(params);
|
|
1500
|
-
let dryRunInfo;
|
|
1501
1501
|
if (isSubstrateXcm) {
|
|
1502
|
-
|
|
1503
|
-
|
|
1502
|
+
const xcmFeeInfo = await estimateXcmFee({
|
|
1503
|
+
fromChainInfo: params.originChain,
|
|
1504
|
+
fromTokenInfo: params.originTokenInfo,
|
|
1505
|
+
toChainInfo: params.destinationChain,
|
|
1506
|
+
recipient: params.recipient,
|
|
1507
|
+
sender: params.sender,
|
|
1508
|
+
value: params.sendingValue
|
|
1509
|
+
});
|
|
1510
|
+
xcmFeeDryRun = (xcmFeeInfo === null || xcmFeeInfo === void 0 ? void 0 : xcmFeeInfo.origin.fee) || '0';
|
|
1504
1511
|
}
|
|
1505
1512
|
if (isAcrossBridgeTransfer) {
|
|
1506
1513
|
const data = await getAcrossQuote(params);
|
|
@@ -1527,50 +1534,52 @@ export default class KoniExtension {
|
|
|
1527
1534
|
}
|
|
1528
1535
|
let isSendingTokenSufficient = false;
|
|
1529
1536
|
let receiverSystemAccountInfo;
|
|
1530
|
-
if (
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
value: _receiverDestinationTokenKeepAliveBalance
|
|
1548
|
-
} = await this.getAddressTotalBalance({
|
|
1549
|
-
address: to,
|
|
1550
|
-
networkKey: destinationNetworkKey,
|
|
1551
|
-
token: destinationTokenInfo.slug,
|
|
1552
|
-
extrinsicType
|
|
1553
|
-
});
|
|
1554
|
-
const receiverDestinationTokenKeepAliveBalance = BigInt(_receiverDestinationTokenKeepAliveBalance);
|
|
1555
|
-
if (!_isNativeToken(destinationTokenInfo)) {
|
|
1556
|
-
const _receiverNativeTotal = await this.getAddressTotalBalance({
|
|
1537
|
+
if (_isChainSubstrateCompatible(chainInfoMap[destinationNetworkKey])) {
|
|
1538
|
+
const setting = {
|
|
1539
|
+
visible: true
|
|
1540
|
+
};
|
|
1541
|
+
await this.#koniState.chainService.updateAssetSetting(destinationTokenInfo.slug, setting, true);
|
|
1542
|
+
const {
|
|
1543
|
+
value: _senderTransferable
|
|
1544
|
+
} = await this.getAddressTransferableBalance({
|
|
1545
|
+
address: from,
|
|
1546
|
+
networkKey: originNetworkKey,
|
|
1547
|
+
token: originTokenInfo.slug
|
|
1548
|
+
});
|
|
1549
|
+
const senderTransferable = BigInt(_senderTransferable);
|
|
1550
|
+
const sendingAmount = BigInt(value);
|
|
1551
|
+
const {
|
|
1552
|
+
value: _receiverDestinationTokenKeepAliveBalance
|
|
1553
|
+
} = await this.getAddressTotalBalance({
|
|
1557
1554
|
address: to,
|
|
1558
1555
|
networkKey: destinationNetworkKey,
|
|
1559
|
-
token:
|
|
1556
|
+
token: destinationTokenInfo.slug,
|
|
1560
1557
|
extrinsicType
|
|
1561
1558
|
});
|
|
1562
|
-
|
|
1559
|
+
const receiverDestinationTokenKeepAliveBalance = BigInt(_receiverDestinationTokenKeepAliveBalance);
|
|
1560
|
+
if (!_isNativeToken(destinationTokenInfo)) {
|
|
1561
|
+
const _receiverNativeTotal = await this.getAddressTotalBalance({
|
|
1562
|
+
address: to,
|
|
1563
|
+
networkKey: destinationNetworkKey,
|
|
1564
|
+
token: destinationNativeTokenSlug,
|
|
1565
|
+
extrinsicType
|
|
1566
|
+
});
|
|
1567
|
+
receiverSystemAccountInfo = _receiverNativeTotal.metadata;
|
|
1568
|
+
}
|
|
1569
|
+
const substrateApi = this.#koniState.getSubstrateApi(destinationNetworkKey);
|
|
1570
|
+
const sufficientChain = this.#koniState.chainService.value.sufficientChains;
|
|
1571
|
+
isSendingTokenSufficient = await _isSufficientToken(destinationTokenInfo, substrateApi, sufficientChain);
|
|
1572
|
+
const [warning, error] = additionalValidateTransferForRecipient(destinationTokenInfo, destinationNativeTokenInfo, extrinsicType, receiverDestinationTokenKeepAliveBalance, sendingAmount, senderTransferable,
|
|
1573
|
+
// different from sendingTokenInfo being passed in
|
|
1574
|
+
receiverSystemAccountInfo, isSendingTokenSufficient);
|
|
1575
|
+
warning.length && inputTransaction.warnings.push(...warning);
|
|
1576
|
+
error.length && inputTransaction.errors.push(...error);
|
|
1563
1577
|
}
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
receiverSystemAccountInfo, isSendingTokenSufficient);
|
|
1570
|
-
warning.length && inputTransaction.warnings.push(...warning);
|
|
1571
|
-
error.length && inputTransaction.errors.push(...error);
|
|
1572
|
-
if (isSubstrateXcm && !dryRunInfo.success) {
|
|
1573
|
-
inputTransaction.errors.push(new TransactionError(BasicTxErrorType.UNABLE_TO_SEND, 'Unable to perform transaction. Select another token or destination chain and try again'));
|
|
1578
|
+
if (isSubstrateXcm) {
|
|
1579
|
+
const isDryRunSuccess = await dryRunXcmExtrinsicV2(params);
|
|
1580
|
+
if (!isDryRunSuccess) {
|
|
1581
|
+
inputTransaction.errors.push(new TransactionError(BasicTxErrorType.UNABLE_TO_SEND, 'Unable to perform transaction. Select another token or destination chain and try again'));
|
|
1582
|
+
}
|
|
1574
1583
|
}
|
|
1575
1584
|
};
|
|
1576
1585
|
eventsHandler = eventEmitter => {
|
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.41-0",
|
|
21
21
|
"main": "./cjs/index.js",
|
|
22
22
|
"module": "./index.js",
|
|
23
23
|
"types": "./index.d.ts",
|
|
@@ -2241,6 +2241,11 @@
|
|
|
2241
2241
|
"require": "./cjs/types/common/storage.js",
|
|
2242
2242
|
"default": "./types/common/storage.js"
|
|
2243
2243
|
},
|
|
2244
|
+
"./types/environment": {
|
|
2245
|
+
"types": "./types/environment.d.ts",
|
|
2246
|
+
"require": "./cjs/types/environment.js",
|
|
2247
|
+
"default": "./types/environment.js"
|
|
2248
|
+
},
|
|
2244
2249
|
"./types/error": {
|
|
2245
2250
|
"types": "./types/error.d.ts",
|
|
2246
2251
|
"require": "./cjs/types/error.js",
|
|
@@ -2709,13 +2714,13 @@
|
|
|
2709
2714
|
"@reduxjs/toolkit": "^1.9.1",
|
|
2710
2715
|
"@sora-substrate/type-definitions": "^1.17.7",
|
|
2711
2716
|
"@substrate/connect": "^0.8.9",
|
|
2712
|
-
"@subwallet/chain-list": "0.2.
|
|
2713
|
-
"@subwallet/extension-base": "^1.3.
|
|
2714
|
-
"@subwallet/extension-chains": "^1.3.
|
|
2715
|
-
"@subwallet/extension-dapp": "^1.3.
|
|
2716
|
-
"@subwallet/extension-inject": "^1.3.
|
|
2717
|
+
"@subwallet/chain-list": "0.2.105",
|
|
2718
|
+
"@subwallet/extension-base": "^1.3.41-0",
|
|
2719
|
+
"@subwallet/extension-chains": "^1.3.41-0",
|
|
2720
|
+
"@subwallet/extension-dapp": "^1.3.41-0",
|
|
2721
|
+
"@subwallet/extension-inject": "^1.3.41-0",
|
|
2717
2722
|
"@subwallet/keyring": "^0.1.12",
|
|
2718
|
-
"@subwallet/subwallet-api-sdk": "^1.3.
|
|
2723
|
+
"@subwallet/subwallet-api-sdk": "^1.3.41-0",
|
|
2719
2724
|
"@subwallet/ui-keyring": "^0.1.12",
|
|
2720
2725
|
"@ton/core": "^0.56.3",
|
|
2721
2726
|
"@ton/crypto": "^3.2.0",
|
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.41-0'
|
|
11
11
|
};
|
|
@@ -5,6 +5,7 @@ import { _AssetType } from '@subwallet/chain-list/types';
|
|
|
5
5
|
import { APIItemState } from '@subwallet/extension-base/background/KoniTypes';
|
|
6
6
|
import { ASTAR_REFRESH_BALANCE_INTERVAL, SUB_TOKEN_REFRESH_BALANCE_INTERVAL } from '@subwallet/extension-base/constants';
|
|
7
7
|
import { getERC20Contract } from '@subwallet/extension-base/koni/api/contract-handler/evm/web3';
|
|
8
|
+
import { _BALANCE_CHAIN_GROUP } from '@subwallet/extension-base/services/chain-service/constants';
|
|
8
9
|
import { _getContractAddressOfToken } from '@subwallet/extension-base/services/chain-service/utils';
|
|
9
10
|
import { filterAssetsByChainAndType } from '@subwallet/extension-base/utils';
|
|
10
11
|
import { BN } from '@polkadot/util';
|
|
@@ -16,7 +17,11 @@ export function subscribeERC20Interval({
|
|
|
16
17
|
evmApi
|
|
17
18
|
}) {
|
|
18
19
|
const chain = chainInfo.slug;
|
|
19
|
-
|
|
20
|
+
let tokenList = filterAssetsByChainAndType(assetMap, chain, [_AssetType.ERC20]);
|
|
21
|
+
if (_BALANCE_CHAIN_GROUP.moonbeam.includes(chain)) {
|
|
22
|
+
const moonbeamLocal = filterAssetsByChainAndType(assetMap, chain, [_AssetType.LOCAL]);
|
|
23
|
+
tokenList = Object.assign({}, tokenList, moonbeamLocal);
|
|
24
|
+
}
|
|
20
25
|
const erc20ContractMap = {};
|
|
21
26
|
Object.entries(tokenList).forEach(([slug, tokenInfo]) => {
|
|
22
27
|
erc20ContractMap[slug] = getERC20Contract(_getContractAddressOfToken(tokenInfo), evmApi);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { _ChainAsset, _ChainInfo } from '@subwallet/chain-list/types';
|
|
2
|
-
import { DryRunInfo } from '@subwallet/extension-base/services/balance-service/transfer/xcm/utils';
|
|
3
2
|
import { _EvmApi, _SubstrateApi } from '@subwallet/extension-base/services/chain-service/types';
|
|
4
3
|
import { FeeInfo, TransactionFee } from '@subwallet/extension-base/types';
|
|
5
4
|
import { TransactionConfig } from 'web3-core';
|
|
@@ -23,5 +22,5 @@ export declare const createAvailBridgeTxFromEth: ({ evmApi, feeCustom, feeInfo,
|
|
|
23
22
|
export declare const createAvailBridgeExtrinsicFromAvail: ({ recipient, sendingValue, substrateApi }: CreateXcmExtrinsicProps) => Promise<SubmittableExtrinsic<'promise'>>;
|
|
24
23
|
export declare const createPolygonBridgeExtrinsic: ({ destinationChain, evmApi, feeCustom, feeInfo, feeOption, originChain, originTokenInfo, recipient, sender, sendingValue }: CreateXcmExtrinsicProps) => Promise<TransactionConfig>;
|
|
25
24
|
export declare const createXcmExtrinsicV2: (request: CreateXcmExtrinsicProps) => Promise<SubmittableExtrinsic<'promise'> | undefined>;
|
|
26
|
-
export declare const dryRunXcmExtrinsicV2: (request: CreateXcmExtrinsicProps) => Promise<
|
|
25
|
+
export declare const dryRunXcmExtrinsicV2: (request: CreateXcmExtrinsicProps) => Promise<boolean>;
|
|
27
26
|
export declare const createAcrossBridgeExtrinsic: ({ destinationChain, destinationTokenInfo, evmApi, feeCustom, feeInfo, feeOption, originChain, originTokenInfo, recipient, sender, sendingValue }: CreateXcmExtrinsicProps) => Promise<TransactionConfig>;
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
// Copyright 2019-2022 @subwallet/extension-base
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
-
import { XCM_MIN_AMOUNT_RATIO } from '@subwallet/extension-base/constants';
|
|
5
4
|
import { _isAcrossBridgeXcm, _isPolygonBridgeXcm, _isPosBridgeXcm, _isSnowBridgeXcm } from '@subwallet/extension-base/core/substrate/xcm-parser';
|
|
6
5
|
import { getAvailBridgeExtrinsicFromAvail, getAvailBridgeTxFromEth } from '@subwallet/extension-base/services/balance-service/transfer/xcm/availBridge';
|
|
7
6
|
import { getExtrinsicByPolkadotXcmPallet } from '@subwallet/extension-base/services/balance-service/transfer/xcm/polkadotXcm';
|
|
8
7
|
import { _createPolygonBridgeL1toL2Extrinsic, _createPolygonBridgeL2toL1Extrinsic } from '@subwallet/extension-base/services/balance-service/transfer/xcm/polygonBridge';
|
|
9
8
|
import { getSnowBridgeEvmTransfer } from '@subwallet/extension-base/services/balance-service/transfer/xcm/snowBridge';
|
|
10
|
-
import { buildXcm,
|
|
9
|
+
import { buildXcm, dryRunXcm, isChainNotSupportDryRun, isChainNotSupportPolkadotApi } from '@subwallet/extension-base/services/balance-service/transfer/xcm/utils';
|
|
11
10
|
import { getExtrinsicByXcmPalletPallet } from '@subwallet/extension-base/services/balance-service/transfer/xcm/xcmPallet';
|
|
12
11
|
import { getExtrinsicByXtokensPallet } from '@subwallet/extension-base/services/balance-service/transfer/xcm/xTokens';
|
|
13
12
|
import { _XCM_CHAIN_GROUP } from '@subwallet/extension-base/services/chain-service/constants';
|
|
@@ -39,6 +38,8 @@ export const createSnowBridgeExtrinsic = async ({
|
|
|
39
38
|
}
|
|
40
39
|
return getSnowBridgeEvmTransfer(originTokenInfo, originChain, destinationChain, sender, recipient, sendingValue, evmApi, feeInfo, feeCustom, feeOption);
|
|
41
40
|
};
|
|
41
|
+
|
|
42
|
+
// deprecated
|
|
42
43
|
export const createXcmExtrinsic = async ({
|
|
43
44
|
destinationChain,
|
|
44
45
|
originChain,
|
|
@@ -121,37 +122,33 @@ export const createXcmExtrinsicV2 = async request => {
|
|
|
121
122
|
return await buildXcm(request);
|
|
122
123
|
} catch (e) {
|
|
123
124
|
console.log('createXcmExtrinsicV2 error: ', e);
|
|
124
|
-
const errorMessage = e instanceof Error ? e.message : 'Unknown error occurred';
|
|
125
|
-
if (isChainNotSupportPolkadotApi(errorMessage)) {
|
|
126
|
-
return createXcmExtrinsic(request);
|
|
127
|
-
}
|
|
128
125
|
return undefined;
|
|
129
126
|
}
|
|
130
127
|
};
|
|
131
128
|
export const dryRunXcmExtrinsicV2 = async request => {
|
|
132
129
|
try {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
130
|
+
const dryRunResult = await dryRunXcm(request);
|
|
131
|
+
const originDryRunRs = dryRunResult.origin;
|
|
132
|
+
if (originDryRunRs.success) {
|
|
133
|
+
const {
|
|
134
|
+
assetHub,
|
|
135
|
+
bridgeHub,
|
|
136
|
+
destination
|
|
137
|
+
} = dryRunResult;
|
|
138
|
+
if ((assetHub === null || assetHub === void 0 ? void 0 : assetHub.success) === false || (bridgeHub === null || bridgeHub === void 0 ? void 0 : bridgeHub.success) === false || (destination === null || destination === void 0 ? void 0 : destination.success) === false) {
|
|
139
|
+
if ((destination === null || destination === void 0 ? void 0 : destination.success) === false) {
|
|
140
|
+
// pass dry-run in these cases
|
|
141
|
+
return isChainNotSupportDryRun(destination.failureReason) || isChainNotSupportPolkadotApi(destination.failureReason);
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
142
144
|
}
|
|
143
|
-
|
|
144
|
-
const xcmFeeInfo = _xcmFeeInfo.toPrimitive();
|
|
145
|
-
|
|
146
|
-
// skip dry run in this case
|
|
147
|
-
return {
|
|
148
|
-
success: true,
|
|
149
|
-
fee: Math.round(xcmFeeInfo.partialFee * XCM_MIN_AMOUNT_RATIO).toString()
|
|
150
|
-
};
|
|
145
|
+
return true;
|
|
151
146
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
147
|
+
|
|
148
|
+
// pass dry-run in these cases
|
|
149
|
+
return isChainNotSupportDryRun(originDryRunRs.failureReason) || isChainNotSupportPolkadotApi(originDryRunRs.failureReason);
|
|
150
|
+
} catch (e) {
|
|
151
|
+
return false;
|
|
155
152
|
}
|
|
156
153
|
};
|
|
157
154
|
export const createAcrossBridgeExtrinsic = async ({
|
|
@@ -1,14 +1,46 @@
|
|
|
1
|
-
import { _ChainInfo } from '@subwallet/chain-list/types';
|
|
1
|
+
import { _ChainAsset, _ChainInfo } from '@subwallet/chain-list/types';
|
|
2
2
|
import { CreateXcmExtrinsicProps } from '@subwallet/extension-base/services/balance-service/transfer/xcm/index';
|
|
3
3
|
import { SubmittableExtrinsic } from '@polkadot/api/types';
|
|
4
|
-
export
|
|
5
|
-
success:
|
|
6
|
-
|
|
4
|
+
export declare type DryRunNodeFailure = {
|
|
5
|
+
success: false;
|
|
6
|
+
failureReason: string;
|
|
7
|
+
};
|
|
8
|
+
export declare type DryRunNodeSuccess = {
|
|
9
|
+
success: true;
|
|
10
|
+
fee: string;
|
|
11
|
+
forwardedXcms: any;
|
|
12
|
+
};
|
|
13
|
+
export declare type DryRunNodeResult = DryRunNodeSuccess | DryRunNodeFailure;
|
|
14
|
+
export declare type DryRunResult = {
|
|
15
|
+
origin: DryRunNodeResult;
|
|
16
|
+
destination?: DryRunNodeResult;
|
|
17
|
+
assetHub?: DryRunNodeResult;
|
|
18
|
+
bridgeHub?: DryRunNodeResult;
|
|
19
|
+
};
|
|
20
|
+
interface GetXcmFeeRequest {
|
|
21
|
+
sender: string;
|
|
22
|
+
recipient: string;
|
|
23
|
+
value: string;
|
|
24
|
+
fromChainInfo: _ChainInfo;
|
|
25
|
+
toChainInfo: _ChainInfo;
|
|
26
|
+
fromTokenInfo: _ChainAsset;
|
|
7
27
|
}
|
|
28
|
+
export declare type XcmFeeType = 'dryRun' | 'paymentInfo';
|
|
29
|
+
export interface XcmFeeDetail {
|
|
30
|
+
fee: string;
|
|
31
|
+
currency: string;
|
|
32
|
+
feeType: XcmFeeType;
|
|
33
|
+
dryRunError?: string;
|
|
34
|
+
}
|
|
35
|
+
export declare type GetXcmFeeResult = {
|
|
36
|
+
origin: XcmFeeDetail;
|
|
37
|
+
destination: XcmFeeDetail;
|
|
38
|
+
};
|
|
8
39
|
export declare function buildXcm(request: CreateXcmExtrinsicProps): Promise<SubmittableExtrinsic<"promise", import("@polkadot/types/types").ISubmittableResult>>;
|
|
9
|
-
export declare function dryRunXcm(request: CreateXcmExtrinsicProps): Promise<
|
|
10
|
-
export declare function
|
|
40
|
+
export declare function dryRunXcm(request: CreateXcmExtrinsicProps): Promise<DryRunResult>;
|
|
41
|
+
export declare function estimateXcmFee(request: GetXcmFeeRequest): Promise<GetXcmFeeResult | undefined>;
|
|
11
42
|
export declare function isChainNotSupportPolkadotApi(str: string): boolean;
|
|
12
43
|
export declare function isChainNotSupportDryRun(str: string): boolean;
|
|
13
44
|
export declare const STABLE_XCM_VERSION = 3;
|
|
14
45
|
export declare function isUseTeleportProtocol(originChainInfo: _ChainInfo, destChainInfo: _ChainInfo, tokenSlug?: string): boolean;
|
|
46
|
+
export {};
|