@subwallet/extension-base 1.2.2-0 → 1.2.3-1

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 (72) hide show
  1. package/background/KoniTypes.d.ts +4 -0
  2. package/cjs/core/logic-validation/earning.js +47 -0
  3. package/cjs/core/logic-validation/swap.js +99 -0
  4. package/cjs/core/logic-validation/transfer.js +219 -0
  5. package/cjs/core/substrate/nominationpools-pallet.js +12 -0
  6. package/cjs/core/substrate/system-pallet.js +78 -0
  7. package/cjs/koni/api/dotsama/transfer.js +49 -4
  8. package/cjs/koni/api/staking/bonding/utils.js +1 -1
  9. package/cjs/koni/api/xcm/index.js +30 -2
  10. package/cjs/koni/background/handlers/Extension.js +135 -245
  11. package/cjs/packageInfo.js +1 -1
  12. package/cjs/services/balance-service/helpers/group.js +4 -27
  13. package/cjs/services/balance-service/helpers/subscribe/index.js +2 -30
  14. package/cjs/services/balance-service/helpers/subscribe/substrate/index.js +41 -80
  15. package/cjs/services/balance-service/index.js +11 -9
  16. package/cjs/services/chain-service/index.js +0 -1
  17. package/cjs/services/chain-service/utils/index.js +6 -0
  18. package/cjs/services/earning-service/handlers/base.js +1 -1
  19. package/cjs/services/earning-service/handlers/special.js +11 -12
  20. package/cjs/services/swap-service/handler/base-handler.js +28 -44
  21. package/cjs/services/swap-service/handler/chainflip-handler.js +23 -21
  22. package/cjs/services/swap-service/handler/hydradx-handler.js +40 -38
  23. package/cjs/services/swap-service/index.js +6 -0
  24. package/cjs/services/swap-service/utils.js +8 -49
  25. package/cjs/services/transaction-service/index.js +66 -155
  26. package/core/logic-validation/earning.d.ts +10 -0
  27. package/core/logic-validation/earning.js +37 -0
  28. package/core/logic-validation/swap.d.ts +8 -0
  29. package/core/logic-validation/swap.js +89 -0
  30. package/core/logic-validation/transfer.d.ts +16 -0
  31. package/core/logic-validation/transfer.js +206 -0
  32. package/core/substrate/nominationpools-pallet.d.ts +7 -0
  33. package/core/substrate/nominationpools-pallet.js +6 -0
  34. package/core/substrate/system-pallet.d.ts +27 -0
  35. package/core/substrate/system-pallet.js +71 -0
  36. package/koni/api/dotsama/transfer.d.ts +3 -1
  37. package/koni/api/dotsama/transfer.js +44 -1
  38. package/koni/api/staking/bonding/relayChain.d.ts +2 -1
  39. package/koni/api/staking/bonding/utils.js +1 -1
  40. package/koni/api/xcm/index.d.ts +2 -0
  41. package/koni/api/xcm/index.js +27 -1
  42. package/koni/background/handlers/Extension.d.ts +5 -5
  43. package/koni/background/handlers/Extension.js +111 -221
  44. package/package.json +34 -9
  45. package/packageInfo.js +1 -1
  46. package/services/balance-service/helpers/group.js +4 -27
  47. package/services/balance-service/helpers/subscribe/index.d.ts +2 -1
  48. package/services/balance-service/helpers/subscribe/index.js +2 -30
  49. package/services/balance-service/helpers/subscribe/substrate/index.d.ts +2 -1
  50. package/services/balance-service/helpers/subscribe/substrate/index.js +26 -64
  51. package/services/balance-service/index.d.ts +7 -6
  52. package/services/balance-service/index.js +12 -10
  53. package/services/chain-service/index.js +0 -1
  54. package/services/chain-service/utils/index.d.ts +1 -0
  55. package/services/chain-service/utils/index.js +4 -0
  56. package/services/earning-service/handlers/base.js +1 -1
  57. package/services/earning-service/handlers/nomination-pool/index.d.ts +2 -1
  58. package/services/earning-service/handlers/special.js +11 -12
  59. package/services/swap-service/handler/base-handler.d.ts +3 -2
  60. package/services/swap-service/handler/base-handler.js +26 -42
  61. package/services/swap-service/handler/chainflip-handler.d.ts +2 -1
  62. package/services/swap-service/handler/chainflip-handler.js +4 -2
  63. package/services/swap-service/handler/hydradx-handler.d.ts +2 -1
  64. package/services/swap-service/handler/hydradx-handler.js +7 -5
  65. package/services/swap-service/index.js +7 -1
  66. package/services/swap-service/utils.d.ts +2 -4
  67. package/services/swap-service/utils.js +7 -47
  68. package/services/transaction-service/index.d.ts +1 -1
  69. package/services/transaction-service/index.js +30 -119
  70. package/services/transaction-service/types.d.ts +1 -0
  71. package/types/balance/index.d.ts +6 -10
  72. package/types/yield/info/pallet.d.ts +0 -6
@@ -0,0 +1,89 @@
1
+ // Copyright 2019-2022 @subwallet/extension-base
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { SwapError } from '@subwallet/extension-base/background/errors/SwapError';
5
+ import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
6
+ import { BasicTxErrorType } from '@subwallet/extension-base/background/KoniTypes';
7
+ import { _getAssetDecimals, _isChainEvmCompatible } from '@subwallet/extension-base/services/chain-service/utils';
8
+ import { SwapErrorType } from '@subwallet/extension-base/types/swap';
9
+ import { formatNumber } from '@subwallet/extension-base/utils';
10
+ import BigN from 'bignumber.js';
11
+ import { isEthereumAddress } from '@polkadot/util-crypto';
12
+ export function _validateBalanceToSwap(fromToken, feeToken, feeTokenChainInfo, feeAmount, fromTokenBalance, feeTokenBalance, swapAmount, isXcmOk, minSwap) {
13
+ if (new BigN(feeTokenBalance).lte(feeAmount)) {
14
+ return new TransactionError(BasicTxErrorType.NOT_ENOUGH_BALANCE, `You don't have enough ${feeToken.symbol} (${feeTokenChainInfo.name}) to pay transaction fee`);
15
+ }
16
+ if (fromToken.slug === feeToken.slug) {
17
+ if (new BigN(fromTokenBalance).lte(new BigN(feeAmount).plus(swapAmount))) {
18
+ return new TransactionError(BasicTxErrorType.NOT_ENOUGH_BALANCE, `Insufficient balance. Deposit ${fromToken.symbol} and try again.`);
19
+ }
20
+ }
21
+ if (isXcmOk) {
22
+ // assume that the swap is valid if XCM is in the process and it was successful
23
+ return undefined;
24
+ }
25
+ if (minSwap) {
26
+ if (new BigN(fromTokenBalance).lte(minSwap)) {
27
+ const parsedMinSwapValue = formatNumber(minSwap, _getAssetDecimals(fromToken));
28
+ return new TransactionError(SwapErrorType.SWAP_NOT_ENOUGH_BALANCE, `Insufficient balance. You need more than ${parsedMinSwapValue} ${fromToken.symbol} to start swapping. Deposit ${fromToken.symbol} and try again.`); // todo: min swap or amount?
29
+ }
30
+ }
31
+
32
+ if (new BigN(swapAmount).gte(fromTokenBalance)) {
33
+ const parsedMaxBalanceSwap = formatNumber(fromTokenBalance, _getAssetDecimals(fromToken));
34
+ return new TransactionError(SwapErrorType.SWAP_EXCEED_ALLOWANCE, `Amount too high. Lower your amount ${new BigN(fromTokenBalance).gt(0) ? `below ${parsedMaxBalanceSwap} ${fromToken.symbol}` : ''} and try again`);
35
+ }
36
+ return undefined;
37
+ }
38
+ export function _validateSwapRecipient(destChainInfo, recipient) {
39
+ const isEvmAddress = isEthereumAddress(recipient);
40
+ const isEvmDestChain = _isChainEvmCompatible(destChainInfo);
41
+ if (isEvmAddress && !isEvmDestChain || !isEvmAddress && isEvmDestChain) {
42
+ return new TransactionError(SwapErrorType.INVALID_RECIPIENT);
43
+ }
44
+ return undefined;
45
+ }
46
+ export function _getChainflipEarlyValidationError(error, metadata) {
47
+ // todo: support more providers
48
+ switch (error) {
49
+ case SwapErrorType.NOT_MEET_MIN_SWAP:
50
+ {
51
+ const parsedMinSwapValue = formatNumber(metadata.minSwap.value, metadata.minSwap.decimals);
52
+ const message = `Amount too low. Increase your amount above ${parsedMinSwapValue} ${metadata.minSwap.symbol} and try again`;
53
+ return new SwapError(error, message);
54
+ }
55
+ case SwapErrorType.SWAP_EXCEED_ALLOWANCE:
56
+ {
57
+ if (metadata.maxSwap) {
58
+ const parsedMaxSwapValue = formatNumber(metadata.maxSwap.value, metadata.maxSwap.decimals);
59
+ return new SwapError(error, `Amount too high. Lower your amount below ${parsedMaxSwapValue} ${metadata.maxSwap.symbol} and try again`);
60
+ } else {
61
+ return new SwapError(error, 'Amount too high. Lower your amount and try again');
62
+ }
63
+ }
64
+ case SwapErrorType.ASSET_NOT_SUPPORTED:
65
+ return new SwapError(error, 'This swap pair is not supported');
66
+ case SwapErrorType.UNKNOWN:
67
+ return new SwapError(error, `Undefined error. Check your Internet and ${metadata.chain.slug} connection or contact support`);
68
+ case SwapErrorType.ERROR_FETCHING_QUOTE:
69
+ return new SwapError(error, 'No swap quote found. Adjust your amount or try again later.');
70
+ default:
71
+ return new SwapError(error);
72
+ }
73
+ }
74
+ export function _getEarlyHydradxValidationError(error, metadata) {
75
+ switch (error) {
76
+ case SwapErrorType.AMOUNT_CANNOT_BE_ZERO:
77
+ {
78
+ return new SwapError(error, 'Amount too low. Increase your amount above 0 and try again');
79
+ }
80
+ case SwapErrorType.ASSET_NOT_SUPPORTED:
81
+ return new SwapError(error, 'This swap pair is not supported');
82
+ case SwapErrorType.UNKNOWN:
83
+ return new SwapError(error, `Undefined error. Check your Internet and ${metadata.chain.slug} connection or contact support`);
84
+ case SwapErrorType.ERROR_FETCHING_QUOTE:
85
+ return new SwapError(error, 'No swap quote found. Adjust your amount or try again later.');
86
+ default:
87
+ return new SwapError(error);
88
+ }
89
+ }
@@ -0,0 +1,16 @@
1
+ import { _ChainAsset, _ChainInfo } from '@subwallet/chain-list/types';
2
+ import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
3
+ import { _Address, AmountData, ExtrinsicType, FeeData } from '@subwallet/extension-base/background/KoniTypes';
4
+ import { TransactionWarning } from '@subwallet/extension-base/background/warnings/TransactionWarning';
5
+ import { _EvmApi } from '@subwallet/extension-base/services/chain-service/types';
6
+ import { OptionalSWTransaction, SWTransactionInput, SWTransactionResponse } from '@subwallet/extension-base/services/transaction-service/types';
7
+ import { KeyringPair } from '@subwallet/keyring/types';
8
+ import BigN from 'bignumber.js';
9
+ export declare function validateTransferRequest(tokenInfo: _ChainAsset, from: _Address, to: _Address, value: string | undefined, transferAll: boolean | undefined): [TransactionError[], KeyringPair | undefined, BigN | undefined];
10
+ export declare function additionalValidateTransfer(tokenInfo: _ChainAsset, extrinsicType: ExtrinsicType, receiverTransferTokenFreeBalance: string, transferAmount: string, senderTransferTokenTransferable?: string): [TransactionWarning | undefined, TransactionError | undefined];
11
+ export declare function validateXcmTransferRequest(destTokenInfo: _ChainAsset | undefined, sender: _Address, sendingValue: string): [TransactionError[], KeyringPair | undefined, BigN | undefined];
12
+ export declare function additionalValidateXcmTransfer(originTokenInfo: _ChainAsset, destinationTokenInfo: _ChainAsset, sendingAmount: string, senderTransferable: string): [TransactionWarning | undefined, TransactionError | undefined];
13
+ export declare function checkSupportForTransaction(validationResponse: SWTransactionResponse, transaction: OptionalSWTransaction): void;
14
+ export declare function estimateFeeForTransaction(validationResponse: SWTransactionResponse, transaction: OptionalSWTransaction, chainInfo: _ChainInfo, evmApi: _EvmApi): Promise<FeeData>;
15
+ export declare function checkSigningAccountForTransaction(validationResponse: SWTransactionResponse): void;
16
+ export declare function checkBalanceWithTransactionFee(validationResponse: SWTransactionResponse, transactionInput: SWTransactionInput, nativeTokenInfo: _ChainAsset, nativeTokenAvailable: AmountData): void;
@@ -0,0 +1,206 @@
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
+
174
+ // if (!nativeTokenAvailable.metadata) {
175
+ // validationResponse.errors.push(new TransactionError(BasicTxErrorType.INTERNAL_ERROR));
176
+ //
177
+ // return;
178
+ // }
179
+
180
+ const {
181
+ edAsWarning,
182
+ extrinsicType,
183
+ isTransferAll,
184
+ skipFeeValidation
185
+ } = transactionInput;
186
+ if (skipFeeValidation) {
187
+ return;
188
+ }
189
+ const bnFee = new BigN(validationResponse.estimateFee.value);
190
+ const bnNativeTokenAvailable = new BigN(nativeTokenAvailable.value);
191
+ const bnNativeTokenTransferAmount = new BigN(validationResponse.transferNativeAmount || '0');
192
+ if (!bnNativeTokenAvailable.gt(0)) {
193
+ validationResponse.errors.push(new TransactionError(BasicTxErrorType.NOT_ENOUGH_BALANCE));
194
+ }
195
+ const isChainNotSupportTransferAll = [..._TRANSFER_CHAIN_GROUP.acala, ..._TRANSFER_CHAIN_GROUP.genshiro, ..._TRANSFER_CHAIN_GROUP.bitcountry, ..._TRANSFER_CHAIN_GROUP.statemine].includes(nativeTokenInfo.originChain);
196
+ if (bnNativeTokenTransferAmount.plus(bnFee).gt(bnNativeTokenAvailable) && (!isTransferAll || isChainNotSupportTransferAll)) {
197
+ validationResponse.errors.push(new TransactionError(BasicTxErrorType.NOT_ENOUGH_BALANCE)); // todo: should be generalized and reused in all features
198
+ }
199
+
200
+ // todo: only system.pallet has metadata, we should add for other pallets and mechanisms as well
201
+ const isNeedCheckRemainingBalance = !isTransferAll && extrinsicType === ExtrinsicType.TRANSFER_BALANCE && nativeTokenAvailable.metadata && _canAccountBeReaped(nativeTokenAvailable.metadata);
202
+ const isRemainingBalanceValid = bnNativeTokenAvailable.minus(bnNativeTokenTransferAmount).minus(bnFee).lt(_getTokenMinAmount(nativeTokenInfo));
203
+ if (isNeedCheckRemainingBalance && isRemainingBalanceValid) {
204
+ edAsWarning ? validationResponse.warnings.push(new TransactionWarning(BasicTxWarningCode.NOT_ENOUGH_EXISTENTIAL_DEPOSIT)) : validationResponse.errors.push(new TransactionError(BasicTxErrorType.NOT_ENOUGH_EXISTENTIAL_DEPOSIT));
205
+ }
206
+ }
@@ -0,0 +1,7 @@
1
+ export declare type PalletNominationPoolsPoolMember = {
2
+ poolId: number;
3
+ points: number;
4
+ lastRecordedRewardCounter: number;
5
+ unbondingEras: Record<string, number>;
6
+ };
7
+ export declare function _getActiveStakeInNominationPool(memberInfo: PalletNominationPoolsPoolMember): string;
@@ -0,0 +1,6 @@
1
+ // Copyright 2019-2022 @subwallet/extension-base
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export function _getActiveStakeInNominationPool(memberInfo) {
5
+ return memberInfo.points.toString();
6
+ }
@@ -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,71 @@
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 bnTotalBalance = new BigN(_getSystemPalletTotalBalanceV1(accountInfo));
66
+ const bnTransferableBalance = bnTotalBalance.minus(BigN.max(bnAppliedFrozen, accountInfo.data.reserved, bnAppliedExistentialDeposit));
67
+ return BigN.max(bnTransferableBalance, 0).toFixed();
68
+ }
69
+ function _getSystemPalletTotalBalanceV1(accountInfo) {
70
+ return new BigN(accountInfo.data.free).toFixed();
71
+ }
@@ -1,6 +1,7 @@
1
1
  import { _ChainAsset, _ChainInfo } from '@subwallet/chain-list/types';
2
2
  import { SupportTransferResponse } from '@subwallet/extension-base/background/KoniTypes';
3
- import { _SubstrateApi } from '@subwallet/extension-base/services/chain-service/types';
3
+ import { _EvmApi, _SubstrateApi } from '@subwallet/extension-base/services/chain-service/types';
4
+ import BigN from 'bignumber.js';
4
5
  import { SubmittableExtrinsic } from '@polkadot/api/promise/types';
5
6
  export declare function checkReferenceCount(networkKey: string, address: string, substrateApiMap: Record<string, _SubstrateApi>, chainInfo: _ChainInfo): Promise<boolean>;
6
7
  export declare function checkSupportTransfer(networkKey: string, tokenInfo: _ChainAsset, substrateApiMap: Record<string, _SubstrateApi>, chainInfo: _ChainInfo): Promise<SupportTransferResponse>;
@@ -14,4 +15,5 @@ interface CreateTransferExtrinsicProps {
14
15
  tokenInfo: _ChainAsset;
15
16
  }
16
17
  export declare const createTransferExtrinsic: ({ from, networkKey, substrateApi, to, tokenInfo, transferAll, value }: CreateTransferExtrinsicProps) => Promise<[SubmittableExtrinsic | null, string]>;
18
+ export declare const getTransferMockTxFee: (address: string, chainInfo: _ChainInfo, tokenInfo: _ChainAsset, api: _SubstrateApi | _EvmApi) => Promise<BigN>;
17
19
  export {};
@@ -5,8 +5,10 @@ import { GearApi } from '@gear-js/api';
5
5
  import { getPSP22ContractPromise } from '@subwallet/extension-base/koni/api/tokens/wasm';
6
6
  import { getWasmContractGasLimit } from '@subwallet/extension-base/koni/api/tokens/wasm/utils';
7
7
  import { _BALANCE_TOKEN_GROUP, _MANTA_ZK_CHAIN_GROUP, _TRANSFER_CHAIN_GROUP, _TRANSFER_NOT_SUPPORTED_CHAINS, _ZK_ASSET_PREFIX } from '@subwallet/extension-base/services/chain-service/constants';
8
- import { _getContractAddressOfToken, _getTokenOnChainAssetId, _getTokenOnChainInfo, _isChainEvmCompatible, _isNativeToken, _isTokenGearSmartContract, _isTokenWasmSmartContract } from '@subwallet/extension-base/services/chain-service/utils';
8
+ import { _getContractAddressOfToken, _getTokenOnChainAssetId, _getTokenOnChainInfo, _isChainEvmCompatible, _isNativeToken, _isTokenGearSmartContract, _isTokenTransferredByEvm, _isTokenWasmSmartContract } from '@subwallet/extension-base/services/chain-service/utils';
9
+ import { calculateGasFeeParams } from '@subwallet/extension-base/services/fee-service/utils';
9
10
  import { getGRC20ContractPromise } from '@subwallet/extension-base/utils';
11
+ import BigN from 'bignumber.js';
10
12
  import { BN, u8aToHex } from '@polkadot/util';
11
13
  import { decodeAddress } from '@polkadot/util-crypto';
12
14
  function isRefCount(accountInfo) {
@@ -211,4 +213,45 @@ export const createTransferExtrinsic = async ({
211
213
  }
212
214
  }
213
215
  return [transfer, transferAmount || value];
216
+ };
217
+ export const getTransferMockTxFee = async (address, chainInfo, tokenInfo, api) => {
218
+ try {
219
+ let estimatedFee;
220
+ if (_isChainEvmCompatible(chainInfo) && _isTokenTransferredByEvm(tokenInfo)) {
221
+ const web3 = api;
222
+ const transaction = {
223
+ value: 0,
224
+ to: '0x0000000000000000000000000000000000000000',
225
+ // null address
226
+ from: address
227
+ };
228
+ const gasLimit = await web3.api.eth.estimateGas(transaction);
229
+ const priority = await calculateGasFeeParams(web3, chainInfo.slug);
230
+ if (priority.baseGasFee) {
231
+ const maxFee = priority.maxFeePerGas;
232
+ estimatedFee = maxFee.multipliedBy(gasLimit);
233
+ } else {
234
+ estimatedFee = new BigN(priority.gasPrice).multipliedBy(gasLimit);
235
+ }
236
+ } else {
237
+ var _paymentInfo$partialF;
238
+ const substrateApi = api;
239
+ const [mockTx] = await createTransferExtrinsic({
240
+ from: address,
241
+ networkKey: chainInfo.slug,
242
+ substrateApi,
243
+ to: address,
244
+ tokenInfo,
245
+ transferAll: true,
246
+ value: '1000000000000000000'
247
+ });
248
+ const paymentInfo = await (mockTx === null || mockTx === void 0 ? void 0 : mockTx.paymentInfo(address));
249
+ estimatedFee = new BigN((paymentInfo === null || paymentInfo === void 0 ? void 0 : (_paymentInfo$partialF = paymentInfo.partialFee) === null || _paymentInfo$partialF === void 0 ? void 0 : _paymentInfo$partialF.toString()) || '0'); // todo: should handle error case instead of setting fee to 0
250
+ }
251
+
252
+ return estimatedFee;
253
+ } catch (e) {
254
+ console.error('error mocking tx fee', e);
255
+ return new BigN(0);
256
+ }
214
257
  };
@@ -1,8 +1,9 @@
1
1
  import { _ChainInfo } from '@subwallet/chain-list/types';
2
2
  import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
3
3
  import { ChainStakingMetadata, NominatorMetadata, UnstakingInfo, ValidatorInfo } from '@subwallet/extension-base/background/KoniTypes';
4
+ import { PalletNominationPoolsPoolMember } from '@subwallet/extension-base/core/substrate/nominationpools-pallet';
4
5
  import { _SubstrateApi } from '@subwallet/extension-base/services/chain-service/types';
5
- import { NominationPoolInfo, PalletNominationPoolsPoolMember } from '@subwallet/extension-base/types';
6
+ import { NominationPoolInfo } from '@subwallet/extension-base/types';
6
7
  export interface PalletStakingNominations {
7
8
  targets: string[];
8
9
  submittedIn: number;
@@ -383,7 +383,7 @@ export function getTopValidatorByPoints(validatorPointsList) {
383
383
  export const getMinStakeErrorMessage = (chainInfo, bnMinStake) => {
384
384
  const tokenInfo = _getChainNativeTokenBasicInfo(chainInfo);
385
385
  const number = formatNumber(bnMinStake.toString(), tokenInfo.decimals || 0, balanceFormatter);
386
- return t('Insufficient stake. Please stake at least {{number}} {{tokenSymbol}} to get rewards', {
386
+ return t('Insufficient stake. You need to stake at least {{number}} {{tokenSymbol}} to earn rewards', {
387
387
  replace: {
388
388
  tokenSymbol: tokenInfo.symbol,
389
389
  number
@@ -1,5 +1,6 @@
1
1
  import { _ChainAsset, _ChainInfo } from '@subwallet/chain-list/types';
2
2
  import { _SubstrateApi } from '@subwallet/extension-base/services/chain-service/types';
3
+ import BigN from 'bignumber.js';
3
4
  import { SubmittableExtrinsic } from '@polkadot/api/types';
4
5
  interface CreateXcmExtrinsicProps {
5
6
  originTokenInfo: _ChainAsset;
@@ -10,4 +11,5 @@ interface CreateXcmExtrinsicProps {
10
11
  chainInfoMap: Record<string, _ChainInfo>;
11
12
  }
12
13
  export declare const createXcmExtrinsic: ({ chainInfoMap, destinationTokenInfo, originTokenInfo, recipient, sendingValue, substrateApi }: CreateXcmExtrinsicProps) => Promise<SubmittableExtrinsic<'promise'>>;
14
+ export declare const getXcmMockTxFee: (substrateApi: _SubstrateApi, chainInfoMap: Record<string, _ChainInfo>, address: string, originTokenInfo: _ChainAsset, destinationTokenInfo: _ChainAsset) => Promise<BigN>;
13
15
  export {};
@@ -5,7 +5,10 @@ import { getExtrinsicByPolkadotXcmPallet } from '@subwallet/extension-base/koni/
5
5
  import { getExtrinsicByXcmPalletPallet } from '@subwallet/extension-base/koni/api/xcm/xcmPallet';
6
6
  import { getExtrinsicByXtokensPallet } from '@subwallet/extension-base/koni/api/xcm/xTokens';
7
7
  import { _XCM_CHAIN_GROUP } from '@subwallet/extension-base/services/chain-service/constants';
8
- import { _isNativeToken } from '@subwallet/extension-base/services/chain-service/utils';
8
+ import { _isChainEvmCompatible, _isNativeToken } from '@subwallet/extension-base/services/chain-service/utils';
9
+ import BigN from 'bignumber.js';
10
+ import { u8aToHex } from '@polkadot/util';
11
+ import { addressToEvm, isEthereumAddress } from '@polkadot/util-crypto';
9
12
  export const createXcmExtrinsic = async ({
10
13
  chainInfoMap,
11
14
  destinationTokenInfo,
@@ -31,4 +34,27 @@ export const createXcmExtrinsic = async ({
31
34
  extrinsic = getExtrinsicByXtokensPallet(originTokenInfo, originChainInfo, destinationChainInfo, recipient, sendingValue, api);
32
35
  }
33
36
  return extrinsic;
37
+ };
38
+ export const getXcmMockTxFee = async (substrateApi, chainInfoMap, address, originTokenInfo, destinationTokenInfo) => {
39
+ try {
40
+ var _paymentInfo$partialF;
41
+ const destChainInfo = chainInfoMap[destinationTokenInfo.originChain];
42
+ const originChainInfo = chainInfoMap[originTokenInfo.originChain];
43
+
44
+ // mock receiving account from sender
45
+ const recipient = !isEthereumAddress(address) && _isChainEvmCompatible(destChainInfo) && !_isChainEvmCompatible(originChainInfo) ? u8aToHex(addressToEvm(address)) : address;
46
+ const mockTx = await createXcmExtrinsic({
47
+ chainInfoMap,
48
+ destinationTokenInfo,
49
+ originTokenInfo,
50
+ recipient: recipient,
51
+ sendingValue: '1000000000000000000',
52
+ substrateApi
53
+ });
54
+ const paymentInfo = await mockTx.paymentInfo(address);
55
+ return new BigN((paymentInfo === null || paymentInfo === void 0 ? void 0 : (_paymentInfo$partialF = paymentInfo.partialFee) === null || _paymentInfo$partialF === void 0 ? void 0 : _paymentInfo$partialF.toString()) || '0');
56
+ } catch (e) {
57
+ console.error('error mocking xcm tx fee', e);
58
+ return new BigN(0);
59
+ }
34
60
  };
@@ -124,9 +124,7 @@ export default class KoniExtension {
124
124
  private subscribeStaking;
125
125
  private subscribeHistory;
126
126
  private subscribeHistoryByChainAndAddress;
127
- private validateTransfer;
128
127
  private makeTransfer;
129
- private validateCrossChainTransfer;
130
128
  private makeCrossChainTransfer;
131
129
  private evmNftSubmitTransaction;
132
130
  private upsertChain;
@@ -140,9 +138,11 @@ export default class KoniExtension {
140
138
  private upsertCustomToken;
141
139
  private deleteCustomAsset;
142
140
  private validateCustomAsset;
143
- private getAddressFreeBalance;
144
- private transferGetMaxTransferable;
145
- private subscribeAddressFreeBalance;
141
+ private getAddressTransferableBalance;
142
+ private getMaxTransferable;
143
+ private getXcmMaxTransferable;
144
+ private getNativeTokenMaxTransferable;
145
+ private subscribeAddressTransferableBalance;
146
146
  private transferCheckReferenceCount;
147
147
  private transferCheckSupporting;
148
148
  private transferGetExistentialDeposit;