@subwallet/extension-base 1.2.3-0 → 1.2.4-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 +4 -153
- package/background/KoniTypes.js +0 -21
- package/cjs/background/KoniTypes.js +1 -22
- package/cjs/core/logic-validation/earning.js +47 -0
- package/cjs/core/logic-validation/swap.js +99 -0
- package/cjs/core/logic-validation/transfer.js +212 -0
- package/cjs/core/substrate/nominationpools-pallet.js +22 -0
- package/cjs/core/substrate/system-pallet.js +77 -0
- package/cjs/core/substrate/xcm-parser.js +190 -0
- package/cjs/koni/api/dotsama/transfer.js +58 -122
- package/cjs/koni/api/staking/bonding/utils.js +1 -1
- package/cjs/koni/api/xcm/index.js +30 -2
- package/cjs/koni/api/xcm/polkadotXcm.js +12 -20
- package/cjs/koni/api/xcm/utils.js +8 -126
- package/cjs/koni/api/xcm/xTokens.js +10 -8
- package/cjs/koni/api/xcm/xcmPallet.js +6 -6
- package/cjs/koni/background/handlers/Extension.js +223 -356
- package/cjs/packageInfo.js +1 -1
- package/cjs/services/balance-service/helpers/group.js +4 -27
- package/cjs/services/balance-service/helpers/subscribe/index.js +2 -30
- package/cjs/services/balance-service/helpers/subscribe/substrate/index.js +46 -82
- package/cjs/services/balance-service/index.js +11 -9
- package/cjs/services/chain-service/constants.js +2 -2
- package/cjs/services/chain-service/index.js +0 -1
- package/cjs/services/chain-service/utils/index.js +13 -2
- package/cjs/services/earning-service/handlers/base.js +1 -1
- package/cjs/services/earning-service/handlers/special.js +11 -12
- package/cjs/services/migration-service/scripts/MigrateTransactionHistoryBySymbol.js +2 -1
- package/cjs/services/migration-service/scripts/databases/MigrateAssetSetting.js +2 -1
- package/cjs/services/migration-service/scripts/index.js +2 -2
- package/cjs/services/swap-service/handler/base-handler.js +30 -44
- package/cjs/services/swap-service/handler/chainflip-handler.js +23 -21
- package/cjs/services/swap-service/handler/hydradx-handler.js +41 -39
- package/cjs/services/swap-service/index.js +6 -0
- package/cjs/services/swap-service/utils.js +8 -49
- package/cjs/services/transaction-service/index.js +66 -155
- package/core/logic-validation/earning.d.ts +10 -0
- package/core/logic-validation/earning.js +37 -0
- package/core/logic-validation/swap.d.ts +8 -0
- package/core/logic-validation/swap.js +89 -0
- package/core/logic-validation/transfer.d.ts +16 -0
- package/core/logic-validation/transfer.js +199 -0
- package/core/substrate/nominationpools-pallet.d.ts +10 -0
- package/core/substrate/nominationpools-pallet.js +13 -0
- package/core/substrate/system-pallet.d.ts +27 -0
- package/core/substrate/system-pallet.js +70 -0
- package/core/substrate/xcm-parser.d.ts +49 -0
- package/core/substrate/xcm-parser.js +181 -0
- package/koni/api/dotsama/transfer.d.ts +3 -4
- package/koni/api/dotsama/transfer.js +54 -118
- package/koni/api/staking/bonding/relayChain.d.ts +2 -1
- package/koni/api/staking/bonding/utils.js +1 -1
- package/koni/api/xcm/index.d.ts +2 -0
- package/koni/api/xcm/index.js +27 -1
- package/koni/api/xcm/polkadotXcm.js +14 -22
- package/koni/api/xcm/utils.d.ts +3 -48
- package/koni/api/xcm/utils.js +5 -114
- package/koni/api/xcm/xTokens.js +12 -10
- package/koni/api/xcm/xcmPallet.js +7 -7
- package/koni/background/handlers/Extension.d.ts +6 -8
- package/koni/background/handlers/Extension.js +117 -247
- package/package.json +39 -9
- package/packageInfo.js +1 -1
- package/services/balance-service/helpers/group.js +4 -27
- package/services/balance-service/helpers/subscribe/index.d.ts +2 -1
- package/services/balance-service/helpers/subscribe/index.js +2 -30
- package/services/balance-service/helpers/subscribe/substrate/index.d.ts +2 -1
- package/services/balance-service/helpers/subscribe/substrate/index.js +31 -66
- package/services/balance-service/index.d.ts +7 -6
- package/services/balance-service/index.js +12 -10
- package/services/chain-service/constants.js +2 -2
- package/services/chain-service/index.js +0 -1
- package/services/chain-service/utils/index.d.ts +4 -2
- package/services/chain-service/utils/index.js +9 -2
- package/services/earning-service/handlers/base.js +1 -1
- package/services/earning-service/handlers/nomination-pool/index.d.ts +2 -1
- package/services/earning-service/handlers/special.js +11 -12
- package/services/migration-service/scripts/MigrateTransactionHistoryBySymbol.js +2 -1
- package/services/migration-service/scripts/databases/MigrateAssetSetting.js +2 -1
- package/services/migration-service/scripts/index.js +2 -2
- package/services/swap-service/handler/base-handler.d.ts +3 -2
- package/services/swap-service/handler/base-handler.js +28 -42
- package/services/swap-service/handler/chainflip-handler.d.ts +2 -1
- package/services/swap-service/handler/chainflip-handler.js +4 -2
- package/services/swap-service/handler/hydradx-handler.d.ts +2 -1
- package/services/swap-service/handler/hydradx-handler.js +8 -6
- package/services/swap-service/index.js +7 -1
- package/services/swap-service/utils.d.ts +2 -4
- package/services/swap-service/utils.js +7 -47
- package/services/transaction-service/index.d.ts +1 -1
- package/services/transaction-service/index.js +30 -119
- package/services/transaction-service/types.d.ts +1 -0
- package/types/balance/index.d.ts +6 -10
- package/types/yield/info/pallet.d.ts +0 -6
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// Copyright 2019-2022 @subwallet/extension-base authors & contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
|
|
5
|
+
import { BasicTxErrorType, BasicTxWarningCode, ExtrinsicType, TransferTxErrorType } from '@subwallet/extension-base/background/KoniTypes';
|
|
6
|
+
import { TransactionWarning } from '@subwallet/extension-base/background/warnings/TransactionWarning';
|
|
7
|
+
import { XCM_MIN_AMOUNT_RATIO } from '@subwallet/extension-base/constants';
|
|
8
|
+
import { _canAccountBeReaped } from '@subwallet/extension-base/core/substrate/system-pallet';
|
|
9
|
+
import { _TRANSFER_CHAIN_GROUP } from '@subwallet/extension-base/services/chain-service/constants';
|
|
10
|
+
import { _getChainNativeTokenBasicInfo, _getContractAddressOfToken, _getTokenMinAmount, _isNativeToken, _isTokenEvmSmartContract } from '@subwallet/extension-base/services/chain-service/utils';
|
|
11
|
+
import { calculateGasFeeParams } from '@subwallet/extension-base/services/fee-service/utils';
|
|
12
|
+
import { isSubstrateTransaction } from '@subwallet/extension-base/services/transaction-service/helpers';
|
|
13
|
+
import { balanceFormatter, formatNumber } from '@subwallet/extension-base/utils';
|
|
14
|
+
import { keyring } from '@subwallet/ui-keyring';
|
|
15
|
+
import BigN from 'bignumber.js';
|
|
16
|
+
import { t } from 'i18next';
|
|
17
|
+
import { isEthereumAddress } from '@polkadot/util-crypto';
|
|
18
|
+
|
|
19
|
+
// normal transfer
|
|
20
|
+
export function validateTransferRequest(tokenInfo, from, to, value, transferAll) {
|
|
21
|
+
const errors = [];
|
|
22
|
+
const keypair = keyring.getPair(from);
|
|
23
|
+
let transferValue;
|
|
24
|
+
if (!transferAll) {
|
|
25
|
+
if (value === undefined) {
|
|
26
|
+
errors.push(new TransactionError(BasicTxErrorType.INVALID_PARAMS, t('Transfer amount is required')));
|
|
27
|
+
}
|
|
28
|
+
if (value) {
|
|
29
|
+
transferValue = new BigN(value);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (!tokenInfo) {
|
|
33
|
+
errors.push(new TransactionError(BasicTxErrorType.INVALID_PARAMS, t('Not found token from registry')));
|
|
34
|
+
}
|
|
35
|
+
if (isEthereumAddress(from) && isEthereumAddress(to) && _isTokenEvmSmartContract(tokenInfo) && _getContractAddressOfToken(tokenInfo).length === 0) {
|
|
36
|
+
errors.push(new TransactionError(BasicTxErrorType.INVALID_PARAMS, t('Not found ERC20 address for this token')));
|
|
37
|
+
}
|
|
38
|
+
return [errors, keypair, transferValue];
|
|
39
|
+
}
|
|
40
|
+
export function additionalValidateTransfer(tokenInfo, extrinsicType, receiverTransferTokenFreeBalance, transferAmount, senderTransferTokenTransferable) {
|
|
41
|
+
const minAmount = _getTokenMinAmount(tokenInfo);
|
|
42
|
+
let warning;
|
|
43
|
+
let error;
|
|
44
|
+
|
|
45
|
+
// Check ed of not native token for sender
|
|
46
|
+
if (extrinsicType === ExtrinsicType.TRANSFER_TOKEN && senderTransferTokenTransferable) {
|
|
47
|
+
if (new BigN(senderTransferTokenTransferable).minus(transferAmount).lt(minAmount)) {
|
|
48
|
+
warning = new TransactionWarning(BasicTxWarningCode.NOT_ENOUGH_EXISTENTIAL_DEPOSIT);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Check ed for receiver
|
|
53
|
+
if (new BigN(receiverTransferTokenFreeBalance).plus(transferAmount).lt(minAmount)) {
|
|
54
|
+
const atLeast = new BigN(minAmount).minus(receiverTransferTokenFreeBalance).plus((tokenInfo.decimals || 0) === 0 ? 0 : 1);
|
|
55
|
+
const atLeastStr = formatNumber(atLeast, tokenInfo.decimals || 0, balanceFormatter, {
|
|
56
|
+
maxNumberFormat: tokenInfo.decimals || 6
|
|
57
|
+
});
|
|
58
|
+
error = new TransactionError(TransferTxErrorType.RECEIVER_NOT_ENOUGH_EXISTENTIAL_DEPOSIT, t('You must transfer at least {{amount}} {{symbol}} to keep the destination account alive', {
|
|
59
|
+
replace: {
|
|
60
|
+
amount: atLeastStr,
|
|
61
|
+
symbol: tokenInfo.symbol
|
|
62
|
+
}
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
return [warning, error];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// xcm transfer
|
|
69
|
+
export function validateXcmTransferRequest(destTokenInfo, sender, sendingValue) {
|
|
70
|
+
const errors = [];
|
|
71
|
+
const keypair = keyring.getPair(sender);
|
|
72
|
+
const transferValue = new BigN(sendingValue);
|
|
73
|
+
if (!destTokenInfo) {
|
|
74
|
+
errors.push(new TransactionError(TransferTxErrorType.INVALID_TOKEN, t('Not found token from registry')));
|
|
75
|
+
}
|
|
76
|
+
return [errors, keypair, transferValue];
|
|
77
|
+
}
|
|
78
|
+
export function additionalValidateXcmTransfer(originTokenInfo, destinationTokenInfo, sendingAmount, senderTransferable) {
|
|
79
|
+
const destMinAmount = _getTokenMinAmount(destinationTokenInfo);
|
|
80
|
+
const minSendingRequired = new BigN(destMinAmount).multipliedBy(XCM_MIN_AMOUNT_RATIO);
|
|
81
|
+
let error;
|
|
82
|
+
let warning;
|
|
83
|
+
|
|
84
|
+
// Check ed for receiver
|
|
85
|
+
if (new BigN(sendingAmount).lt(minSendingRequired)) {
|
|
86
|
+
const atLeastStr = formatNumber(minSendingRequired, destinationTokenInfo.decimals || 0, balanceFormatter, {
|
|
87
|
+
maxNumberFormat: destinationTokenInfo.decimals || 6
|
|
88
|
+
});
|
|
89
|
+
error = new TransactionError(TransferTxErrorType.RECEIVER_NOT_ENOUGH_EXISTENTIAL_DEPOSIT, t('You must transfer at least {{amount}} {{symbol}} to keep the destination account alive', {
|
|
90
|
+
replace: {
|
|
91
|
+
amount: atLeastStr,
|
|
92
|
+
symbol: originTokenInfo.symbol
|
|
93
|
+
}
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Check ed for sender
|
|
98
|
+
if (!_isNativeToken(originTokenInfo)) {
|
|
99
|
+
if (new BigN(senderTransferable).minus(sendingAmount).lt(_getTokenMinAmount(originTokenInfo))) {
|
|
100
|
+
warning = new TransactionWarning(BasicTxWarningCode.NOT_ENOUGH_EXISTENTIAL_DEPOSIT);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return [warning, error];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// general validations
|
|
107
|
+
export function checkSupportForTransaction(validationResponse, transaction) {
|
|
108
|
+
const {
|
|
109
|
+
extrinsicType
|
|
110
|
+
} = validationResponse;
|
|
111
|
+
if (!transaction) {
|
|
112
|
+
if (extrinsicType === ExtrinsicType.SEND_NFT) {
|
|
113
|
+
validationResponse.errors.push(new TransactionError(BasicTxErrorType.UNSUPPORTED, t('This feature is not yet available for this NFT')));
|
|
114
|
+
} else {
|
|
115
|
+
validationResponse.errors.push(new TransactionError(BasicTxErrorType.UNSUPPORTED));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
export async function estimateFeeForTransaction(validationResponse, transaction, chainInfo, evmApi) {
|
|
120
|
+
const estimateFee = {
|
|
121
|
+
symbol: '',
|
|
122
|
+
decimals: 0,
|
|
123
|
+
value: '0',
|
|
124
|
+
tooHigh: false
|
|
125
|
+
};
|
|
126
|
+
const {
|
|
127
|
+
decimals,
|
|
128
|
+
symbol
|
|
129
|
+
} = _getChainNativeTokenBasicInfo(chainInfo);
|
|
130
|
+
estimateFee.decimals = decimals;
|
|
131
|
+
estimateFee.symbol = symbol;
|
|
132
|
+
if (transaction) {
|
|
133
|
+
try {
|
|
134
|
+
if (isSubstrateTransaction(transaction)) {
|
|
135
|
+
estimateFee.value = (await transaction.paymentInfo(validationResponse.address)).partialFee.toString();
|
|
136
|
+
} else {
|
|
137
|
+
const gasLimit = await evmApi.api.eth.estimateGas(transaction);
|
|
138
|
+
const priority = await calculateGasFeeParams(evmApi, chainInfo.slug);
|
|
139
|
+
if (priority.baseGasFee) {
|
|
140
|
+
const maxFee = priority.maxFeePerGas; // TODO: Need review
|
|
141
|
+
|
|
142
|
+
estimateFee.value = maxFee.multipliedBy(gasLimit).toFixed(0);
|
|
143
|
+
} else {
|
|
144
|
+
estimateFee.value = new BigN(priority.gasPrice).multipliedBy(gasLimit).toFixed(0);
|
|
145
|
+
}
|
|
146
|
+
estimateFee.tooHigh = priority.busyNetwork;
|
|
147
|
+
}
|
|
148
|
+
} catch (e) {
|
|
149
|
+
const error = e;
|
|
150
|
+
if (error.message.includes('gas required exceeds allowance') && error.message.includes('insufficient funds')) {
|
|
151
|
+
validationResponse.errors.push(new TransactionError(BasicTxErrorType.NOT_ENOUGH_BALANCE));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return estimateFee;
|
|
156
|
+
}
|
|
157
|
+
export function checkSigningAccountForTransaction(validationResponse) {
|
|
158
|
+
const pair = keyring.getPair(validationResponse.address);
|
|
159
|
+
if (!pair) {
|
|
160
|
+
validationResponse.errors.push(new TransactionError(BasicTxErrorType.INTERNAL_ERROR, t('Unable to find account')));
|
|
161
|
+
} else {
|
|
162
|
+
var _pair$meta;
|
|
163
|
+
if ((_pair$meta = pair.meta) !== null && _pair$meta !== void 0 && _pair$meta.isReadOnly) {
|
|
164
|
+
validationResponse.errors.push(new TransactionError(BasicTxErrorType.INTERNAL_ERROR, t('This account is watch-only')));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
export function checkBalanceWithTransactionFee(validationResponse, transactionInput, nativeTokenInfo, nativeTokenAvailable) {
|
|
169
|
+
if (!validationResponse.estimateFee) {
|
|
170
|
+
// todo: estimateFee should be must-have, need to refactor interface
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const {
|
|
174
|
+
edAsWarning,
|
|
175
|
+
extrinsicType,
|
|
176
|
+
isTransferAll,
|
|
177
|
+
skipFeeValidation
|
|
178
|
+
} = transactionInput;
|
|
179
|
+
if (skipFeeValidation) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const bnFee = new BigN(validationResponse.estimateFee.value);
|
|
183
|
+
const bnNativeTokenAvailable = new BigN(nativeTokenAvailable.value);
|
|
184
|
+
const bnNativeTokenTransferAmount = new BigN(validationResponse.transferNativeAmount || '0');
|
|
185
|
+
if (!bnNativeTokenAvailable.gt(0)) {
|
|
186
|
+
validationResponse.errors.push(new TransactionError(BasicTxErrorType.NOT_ENOUGH_BALANCE));
|
|
187
|
+
}
|
|
188
|
+
const isChainNotSupportTransferAll = [..._TRANSFER_CHAIN_GROUP.acala, ..._TRANSFER_CHAIN_GROUP.genshiro, ..._TRANSFER_CHAIN_GROUP.bitcountry, ..._TRANSFER_CHAIN_GROUP.statemine].includes(nativeTokenInfo.originChain);
|
|
189
|
+
if (bnNativeTokenTransferAmount.plus(bnFee).gt(bnNativeTokenAvailable) && (!isTransferAll || isChainNotSupportTransferAll)) {
|
|
190
|
+
validationResponse.errors.push(new TransactionError(BasicTxErrorType.NOT_ENOUGH_BALANCE)); // todo: should be generalized and reused in all features
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// todo: only system.pallet has metadata, we should add for other pallets and mechanisms as well
|
|
194
|
+
const isNeedCheckRemainingBalance = !isTransferAll && extrinsicType === ExtrinsicType.TRANSFER_BALANCE && nativeTokenAvailable.metadata && _canAccountBeReaped(nativeTokenAvailable.metadata);
|
|
195
|
+
const isRemainingBalanceValid = bnNativeTokenAvailable.minus(bnNativeTokenTransferAmount).minus(bnFee).lt(_getTokenMinAmount(nativeTokenInfo));
|
|
196
|
+
if (isNeedCheckRemainingBalance && isRemainingBalanceValid) {
|
|
197
|
+
edAsWarning ? validationResponse.warnings.push(new TransactionWarning(BasicTxWarningCode.NOT_ENOUGH_EXISTENTIAL_DEPOSIT)) : validationResponse.errors.push(new TransactionError(BasicTxErrorType.NOT_ENOUGH_EXISTENTIAL_DEPOSIT));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import BigN from 'bignumber.js';
|
|
2
|
+
export declare type PalletNominationPoolsPoolMember = {
|
|
3
|
+
poolId: number;
|
|
4
|
+
points: number;
|
|
5
|
+
lastRecordedRewardCounter: number;
|
|
6
|
+
unbondingEras: Record<string, number>;
|
|
7
|
+
};
|
|
8
|
+
export declare function _getActiveStakeInNominationPool(memberInfo: PalletNominationPoolsPoolMember): BigN;
|
|
9
|
+
export declare function _getUnbondingStakeInNominationPool(memberInfo: PalletNominationPoolsPoolMember): BigN;
|
|
10
|
+
export declare function _getTotalStakeInNominationPool(memberInfo: PalletNominationPoolsPoolMember): BigN;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Copyright 2019-2022 @subwallet/extension-base
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import BigN from 'bignumber.js';
|
|
5
|
+
export function _getActiveStakeInNominationPool(memberInfo) {
|
|
6
|
+
return new BigN(memberInfo.points.toString());
|
|
7
|
+
}
|
|
8
|
+
export function _getUnbondingStakeInNominationPool(memberInfo) {
|
|
9
|
+
return new BigN(Object.values(memberInfo.unbondingEras).reduce((a, b) => a + b, 0));
|
|
10
|
+
}
|
|
11
|
+
export function _getTotalStakeInNominationPool(memberInfo) {
|
|
12
|
+
return _getActiveStakeInNominationPool(memberInfo).plus(_getUnbondingStakeInNominationPool(memberInfo));
|
|
13
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
|
|
2
|
+
export declare type FrameSystemAccountInfoV2 = Omit<FrameSystemAccountInfoV1, 'data'> & {
|
|
3
|
+
data: {
|
|
4
|
+
free: number;
|
|
5
|
+
reserved: number;
|
|
6
|
+
frozen: number;
|
|
7
|
+
flags: number;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export declare type FrameSystemAccountInfoV1 = {
|
|
11
|
+
nonce: number;
|
|
12
|
+
consumers: number;
|
|
13
|
+
providers: number;
|
|
14
|
+
sufficients: number;
|
|
15
|
+
data: {
|
|
16
|
+
free: number | string;
|
|
17
|
+
reserved: number;
|
|
18
|
+
miscFrozen: number;
|
|
19
|
+
feeFrozen: number;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
export declare type FrameSystemAccountInfo = FrameSystemAccountInfoV1 | FrameSystemAccountInfoV2;
|
|
23
|
+
export declare function _getSystemPalletTransferable(accountInfo: FrameSystemAccountInfo, existentialDeposit: string, extrinsicType?: ExtrinsicType): string;
|
|
24
|
+
export declare function _canAccountBeReaped(accountInfo: FrameSystemAccountInfo): boolean;
|
|
25
|
+
export declare function _isAccountActive(accountInfo: FrameSystemAccountInfo): boolean;
|
|
26
|
+
export declare function _getSystemPalletTotalBalance(accountInfo: FrameSystemAccountInfo): string;
|
|
27
|
+
export declare function _getAppliedExistentialDepositWithExtrinsicType(accountInfo: FrameSystemAccountInfo, existentialDeposit: string, extrinsicType?: ExtrinsicType): string;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Copyright 2019-2022 @subwallet/extension-base
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
|
|
5
|
+
import BigN from 'bignumber.js';
|
|
6
|
+
|
|
7
|
+
// https://crates.parity.io/frame_system/struct.AccountInfo.html
|
|
8
|
+
// https://wiki.polkadot.network/docs/learn-account-balances
|
|
9
|
+
|
|
10
|
+
function isV1(accountInfo) {
|
|
11
|
+
return accountInfo.data.miscFrozen !== undefined && accountInfo.data.feeFrozen !== undefined;
|
|
12
|
+
}
|
|
13
|
+
export function _getSystemPalletTransferable(accountInfo, existentialDeposit, extrinsicType) {
|
|
14
|
+
const strictMode = !extrinsicType || ![ExtrinsicType.TRANSFER_BALANCE].includes(extrinsicType); // always apply strict mode to keep account alive unless explicitly specified otherwise
|
|
15
|
+
|
|
16
|
+
if (isV1(accountInfo)) {
|
|
17
|
+
return _getSystemPalletTransferableV1(accountInfo, existentialDeposit, strictMode);
|
|
18
|
+
} else {
|
|
19
|
+
return _getSystemPalletTransferableV2(accountInfo, existentialDeposit, strictMode);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function _canAccountBeReaped(accountInfo) {
|
|
23
|
+
return accountInfo.consumers === 0; // might need to check refCount
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function _isAccountActive(accountInfo) {
|
|
27
|
+
return accountInfo.providers === 0 && accountInfo.consumers === 0;
|
|
28
|
+
}
|
|
29
|
+
export function _getSystemPalletTotalBalance(accountInfo) {
|
|
30
|
+
if (isV1(accountInfo)) {
|
|
31
|
+
return _getSystemPalletTotalBalanceV1(accountInfo);
|
|
32
|
+
} else {
|
|
33
|
+
return _getSystemPalletTotalBalanceV2(accountInfo);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function _getAppliedExistentialDepositWithExtrinsicType(accountInfo, existentialDeposit, extrinsicType) {
|
|
37
|
+
const strictMode = !extrinsicType || ![ExtrinsicType.TRANSFER_BALANCE].includes(extrinsicType); // always apply strict mode to keep account alive unless explicitly specified otherwise
|
|
38
|
+
|
|
39
|
+
return _getAppliedExistentialDeposit(accountInfo, existentialDeposit, strictMode);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ----------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
function _getAppliedExistentialDeposit(accountInfo, existentialDeposit, strictMode) {
|
|
45
|
+
// strict mode will always apply existential deposit to keep account alive
|
|
46
|
+
if (strictMode) {
|
|
47
|
+
return existentialDeposit;
|
|
48
|
+
}
|
|
49
|
+
return _canAccountBeReaped(accountInfo) ? '0' : existentialDeposit; // account for ED here will go better with max transfer logic
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function _getSystemPalletTransferableV2(accountInfo, existentialDeposit, strictMode) {
|
|
53
|
+
const bnFree = new BigN(accountInfo.data.free);
|
|
54
|
+
const bnLocked = new BigN(accountInfo.data.frozen).minus(accountInfo.data.reserved); // locked can go below 0 but this shouldn't matter
|
|
55
|
+
const bnAppliedExistentialDeposit = new BigN(_getAppliedExistentialDeposit(accountInfo, existentialDeposit, strictMode));
|
|
56
|
+
const bnTransferableBalance = bnFree.minus(BigN.max(bnLocked, bnAppliedExistentialDeposit));
|
|
57
|
+
return BigN.max(bnTransferableBalance, 0).toFixed();
|
|
58
|
+
}
|
|
59
|
+
function _getSystemPalletTotalBalanceV2(accountInfo) {
|
|
60
|
+
return new BigN(accountInfo.data.free).plus(accountInfo.data.reserved).toFixed();
|
|
61
|
+
}
|
|
62
|
+
function _getSystemPalletTransferableV1(accountInfo, existentialDeposit, strictMode) {
|
|
63
|
+
const bnAppliedExistentialDeposit = new BigN(_getAppliedExistentialDeposit(accountInfo, existentialDeposit, strictMode));
|
|
64
|
+
const bnAppliedFrozen = BigN.max(accountInfo.data.feeFrozen, accountInfo.data.miscFrozen);
|
|
65
|
+
const bnTransferableBalance = new BigN(accountInfo.data.free).minus(BigN.max(bnAppliedFrozen, bnAppliedExistentialDeposit));
|
|
66
|
+
return BigN.max(bnTransferableBalance, 0).toFixed();
|
|
67
|
+
}
|
|
68
|
+
function _getSystemPalletTotalBalanceV1(accountInfo) {
|
|
69
|
+
return new BigN(accountInfo.data.free).plus(accountInfo.data.reserved).toFixed();
|
|
70
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { _ChainAsset, _ChainInfo } from '@subwallet/chain-list/types';
|
|
2
|
+
import { _Address } from '@subwallet/extension-base/background/KoniTypes';
|
|
3
|
+
export declare function _getXcmDestWeight(originChainInfo: _ChainInfo): "Unlimited" | 5000000000;
|
|
4
|
+
export declare function _getXcmBeneficiary(destChainInfo: _ChainInfo, recipient: _Address, version: number): {
|
|
5
|
+
[x: string]: {
|
|
6
|
+
parents: number;
|
|
7
|
+
interior: {
|
|
8
|
+
X1: {
|
|
9
|
+
AccountId32: {
|
|
10
|
+
network: string | undefined;
|
|
11
|
+
id: Uint8Array;
|
|
12
|
+
};
|
|
13
|
+
AccountKey20?: undefined;
|
|
14
|
+
} | {
|
|
15
|
+
AccountKey20: {
|
|
16
|
+
network: string | undefined;
|
|
17
|
+
key: string;
|
|
18
|
+
};
|
|
19
|
+
AccountId32?: undefined;
|
|
20
|
+
} | ({
|
|
21
|
+
AccountId32: {
|
|
22
|
+
network: string | undefined;
|
|
23
|
+
id: Uint8Array;
|
|
24
|
+
};
|
|
25
|
+
AccountKey20?: undefined;
|
|
26
|
+
} | {
|
|
27
|
+
AccountKey20: {
|
|
28
|
+
network: string | undefined;
|
|
29
|
+
key: string;
|
|
30
|
+
};
|
|
31
|
+
AccountId32?: undefined;
|
|
32
|
+
})[];
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
export declare function _getXcmMultiAssets(tokenInfo: _ChainAsset, value: string, version: number): {
|
|
37
|
+
[x: string]: {
|
|
38
|
+
id: Record<string, any>;
|
|
39
|
+
fun: {
|
|
40
|
+
Fungible: string;
|
|
41
|
+
};
|
|
42
|
+
}[];
|
|
43
|
+
};
|
|
44
|
+
export declare function _getXcmMultiLocation(originChainInfo: _ChainInfo, destChainInfo: _ChainInfo, version: number, recipient?: _Address): {
|
|
45
|
+
[x: string]: {
|
|
46
|
+
parents: number;
|
|
47
|
+
interior: unknown;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// Copyright 2019-2022 @subwallet/extension-base
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { COMMON_CHAIN_SLUGS } from '@subwallet/chain-list';
|
|
5
|
+
import { _getChainSubstrateAddressPrefix, _getEvmChainId, _getSubstrateParaId, _getSubstrateRelayParent, _getXcmAssetMultilocation, _isChainEvmCompatible, _isSubstrateParaChain } from '@subwallet/extension-base/services/chain-service/utils';
|
|
6
|
+
import { decodeAddress, evmToAddress } from '@polkadot/util-crypto';
|
|
7
|
+
const FOUR_INSTRUCTIONS_WEIGHT = 5000000000;
|
|
8
|
+
const UNLIMITED_WEIGHT = 'Unlimited';
|
|
9
|
+
export function _getXcmDestWeight(originChainInfo) {
|
|
10
|
+
if (['pioneer'].includes(originChainInfo.slug)) {
|
|
11
|
+
return FOUR_INSTRUCTIONS_WEIGHT;
|
|
12
|
+
}
|
|
13
|
+
return UNLIMITED_WEIGHT;
|
|
14
|
+
}
|
|
15
|
+
export function _getXcmBeneficiary(destChainInfo, recipient, version) {
|
|
16
|
+
const receiverLocation = version < 4 // from V4, X1 is also an array
|
|
17
|
+
? _getRecipientLocation(destChainInfo, recipient, version) : [_getRecipientLocation(destChainInfo, recipient, version)];
|
|
18
|
+
return {
|
|
19
|
+
[`V${version}`]: {
|
|
20
|
+
parents: 0,
|
|
21
|
+
interior: {
|
|
22
|
+
X1: receiverLocation
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function _getXcmMultiAssets(tokenInfo, value, version) {
|
|
28
|
+
const assetId = _getAssetIdentifier(tokenInfo, version);
|
|
29
|
+
return {
|
|
30
|
+
[`V${version}`]: [{
|
|
31
|
+
id: assetId,
|
|
32
|
+
fun: {
|
|
33
|
+
Fungible: value
|
|
34
|
+
}
|
|
35
|
+
}]
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function _getXcmMultiLocation(originChainInfo, destChainInfo, version, recipient) {
|
|
39
|
+
const isWithinSameConsensus = _isXcmWithinSameConsensus(originChainInfo, destChainInfo);
|
|
40
|
+
const parents = _getMultiLocationParent(originChainInfo, isWithinSameConsensus);
|
|
41
|
+
const interior = _getMultiLocationInterior(destChainInfo, isWithinSameConsensus, version, recipient);
|
|
42
|
+
return {
|
|
43
|
+
[`V${version}`]: {
|
|
44
|
+
parents,
|
|
45
|
+
interior
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
function _isXcmWithinSameConsensus(originChainInfo, destChainInfo) {
|
|
53
|
+
return _getSubstrateRelayParent(originChainInfo) === destChainInfo.slug || _getSubstrateRelayParent(destChainInfo) === originChainInfo.slug || _getSubstrateRelayParent(originChainInfo) === _getSubstrateRelayParent(destChainInfo);
|
|
54
|
+
}
|
|
55
|
+
function _getMultiLocationParent(originChainInfo, isWithinSameConsensus) {
|
|
56
|
+
let parent = 0; // how many hops up the hierarchy
|
|
57
|
+
|
|
58
|
+
if (_isSubstrateParaChain(originChainInfo)) {
|
|
59
|
+
parent += 1;
|
|
60
|
+
}
|
|
61
|
+
if (!isWithinSameConsensus) {
|
|
62
|
+
parent += 1;
|
|
63
|
+
}
|
|
64
|
+
return parent;
|
|
65
|
+
}
|
|
66
|
+
function _getMultiLocationInterior(destChainInfo, isWithinSameConsensus, version, recipient) {
|
|
67
|
+
const junctions = [];
|
|
68
|
+
if (isWithinSameConsensus) {
|
|
69
|
+
if (_isSubstrateParaChain(destChainInfo)) {
|
|
70
|
+
junctions.push({
|
|
71
|
+
Parachain: _getSubstrateParaId(destChainInfo)
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
junctions.push({
|
|
76
|
+
GlobalConsensus: _getGlobalConsensusJunction(destChainInfo, version)
|
|
77
|
+
});
|
|
78
|
+
if (_isSubstrateParaChain(destChainInfo)) {
|
|
79
|
+
junctions.push({
|
|
80
|
+
Parachain: _getSubstrateParaId(destChainInfo)
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (recipient) {
|
|
85
|
+
junctions.push(_getRecipientLocation(destChainInfo, recipient, version));
|
|
86
|
+
}
|
|
87
|
+
if (junctions.length === 0 && !recipient) {
|
|
88
|
+
return 'Here';
|
|
89
|
+
}
|
|
90
|
+
if (version < 4 && junctions.length === 1) {
|
|
91
|
+
return {
|
|
92
|
+
X1: junctions[0]
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
[`X${junctions.length}`]: junctions
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function _getGlobalConsensusJunction(destChainInfo, version) {
|
|
100
|
+
let chainSlug = destChainInfo.slug;
|
|
101
|
+
let evmChainId;
|
|
102
|
+
if (_isSubstrateParaChain(destChainInfo)) {
|
|
103
|
+
const relaySlug = _getSubstrateRelayParent(destChainInfo);
|
|
104
|
+
if (!relaySlug) {
|
|
105
|
+
throw Error('Parachain must have a parent chainSlug');
|
|
106
|
+
}
|
|
107
|
+
chainSlug = relaySlug;
|
|
108
|
+
} else {
|
|
109
|
+
evmChainId = _getEvmChainId(destChainInfo);
|
|
110
|
+
}
|
|
111
|
+
if (evmChainId) {
|
|
112
|
+
return {
|
|
113
|
+
Ethereum: {
|
|
114
|
+
chainId: evmChainId
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
switch (chainSlug) {
|
|
119
|
+
case COMMON_CHAIN_SLUGS.POLKADOT:
|
|
120
|
+
return version < 4 ? {
|
|
121
|
+
Polkadot: null
|
|
122
|
+
} : 'Polkadot';
|
|
123
|
+
case COMMON_CHAIN_SLUGS.KUSAMA:
|
|
124
|
+
return version < 4 ? {
|
|
125
|
+
Kusama: null
|
|
126
|
+
} : 'Kusama';
|
|
127
|
+
default:
|
|
128
|
+
return version < 4 ? {
|
|
129
|
+
Rococo: null
|
|
130
|
+
} : 'Rococo';
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function _getRecipientLocation(destChainInfo, recipient, version) {
|
|
134
|
+
const network = _getNetworkByVersion(version);
|
|
135
|
+
if (destChainInfo.slug === COMMON_CHAIN_SLUGS.ASTAR_EVM) {
|
|
136
|
+
const ss58Address = evmToAddress(recipient, _getChainSubstrateAddressPrefix(destChainInfo)); // TODO: shouldn't pass addressPrefix directly
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
AccountId32: {
|
|
140
|
+
network,
|
|
141
|
+
id: decodeAddress(ss58Address)
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
if (_isChainEvmCompatible(destChainInfo)) {
|
|
146
|
+
return {
|
|
147
|
+
AccountKey20: {
|
|
148
|
+
network,
|
|
149
|
+
key: recipient
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
AccountId32: {
|
|
155
|
+
network,
|
|
156
|
+
id: decodeAddress(recipient)
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function _getAssetIdentifier(tokenInfo, version) {
|
|
161
|
+
const assetIdentifier = _getXcmAssetMultilocation(tokenInfo);
|
|
162
|
+
if (!assetIdentifier) {
|
|
163
|
+
throw new Error('Asset must have multilocation');
|
|
164
|
+
}
|
|
165
|
+
return version >= 4 // from V4, Concrete is removed
|
|
166
|
+
? assetIdentifier : {
|
|
167
|
+
Concrete: assetIdentifier
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function _getNetworkByVersion(version) {
|
|
171
|
+
switch (version) {
|
|
172
|
+
case 1:
|
|
173
|
+
case 2:
|
|
174
|
+
return 'Any';
|
|
175
|
+
case 3:
|
|
176
|
+
case 4:
|
|
177
|
+
return undefined;
|
|
178
|
+
default:
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { _ChainAsset, _ChainInfo } from '@subwallet/chain-list/types';
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import { _EvmApi, _SubstrateApi } from '@subwallet/extension-base/services/chain-service/types';
|
|
3
|
+
import BigN from 'bignumber.js';
|
|
4
4
|
import { SubmittableExtrinsic } from '@polkadot/api/promise/types';
|
|
5
|
-
export declare function checkReferenceCount(networkKey: string, address: string, substrateApiMap: Record<string, _SubstrateApi>, chainInfo: _ChainInfo): Promise<boolean>;
|
|
6
|
-
export declare function checkSupportTransfer(networkKey: string, tokenInfo: _ChainAsset, substrateApiMap: Record<string, _SubstrateApi>, chainInfo: _ChainInfo): Promise<SupportTransferResponse>;
|
|
7
5
|
interface CreateTransferExtrinsicProps {
|
|
8
6
|
substrateApi: _SubstrateApi;
|
|
9
7
|
networkKey: string;
|
|
@@ -14,4 +12,5 @@ interface CreateTransferExtrinsicProps {
|
|
|
14
12
|
tokenInfo: _ChainAsset;
|
|
15
13
|
}
|
|
16
14
|
export declare const createTransferExtrinsic: ({ from, networkKey, substrateApi, to, tokenInfo, transferAll, value }: CreateTransferExtrinsicProps) => Promise<[SubmittableExtrinsic | null, string]>;
|
|
15
|
+
export declare const getTransferMockTxFee: (address: string, chainInfo: _ChainInfo, tokenInfo: _ChainAsset, api: _SubstrateApi | _EvmApi) => Promise<BigN>;
|
|
17
16
|
export {};
|