@subwallet/extension-base 1.0.7-1 → 1.0.8-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/background/KoniTypes.d.ts +6 -4
  2. package/background/KoniTypes.js +1 -0
  3. package/background/errors/TransactionError.js +5 -1
  4. package/cjs/background/KoniTypes.js +1 -0
  5. package/cjs/background/errors/TransactionError.js +4 -0
  6. package/cjs/constants/index.js +6 -3
  7. package/cjs/koni/api/dotsama/balance.js +2 -1
  8. package/cjs/koni/api/dotsama/crowdloan.js +1 -1
  9. package/cjs/koni/api/dotsama/transfer.js +2 -2
  10. package/cjs/koni/api/staking/bonding/astar.js +5 -4
  11. package/cjs/koni/api/staking/bonding/relayChain.js +12 -3
  12. package/cjs/koni/api/staking/bonding/utils.js +7 -0
  13. package/cjs/koni/api/xcm/polkadotXcm.js +18 -37
  14. package/cjs/koni/api/xcm/utils.js +78 -11
  15. package/cjs/koni/api/xcm/xTokens.js +4 -33
  16. package/cjs/koni/api/xcm/xcmPallet.js +4 -36
  17. package/cjs/koni/background/handlers/Extension.js +179 -97
  18. package/cjs/koni/background/handlers/State.js +1 -1
  19. package/cjs/packageInfo.js +1 -1
  20. package/cjs/services/chain-service/constants.js +8 -6
  21. package/cjs/services/chain-service/index.js +19 -15
  22. package/cjs/services/chain-service/utils.js +1 -5
  23. package/cjs/services/transaction-service/helpers/index.js +45 -2
  24. package/cjs/services/transaction-service/index.js +58 -24
  25. package/cjs/utils/number.js +112 -0
  26. package/constants/index.d.ts +1 -0
  27. package/constants/index.js +1 -0
  28. package/koni/api/dotsama/balance.js +2 -1
  29. package/koni/api/dotsama/crowdloan.js +2 -2
  30. package/koni/api/dotsama/transfer.js +2 -2
  31. package/koni/api/staking/bonding/astar.js +5 -4
  32. package/koni/api/staking/bonding/relayChain.js +13 -4
  33. package/koni/api/staking/bonding/utils.d.ts +5 -0
  34. package/koni/api/staking/bonding/utils.js +6 -0
  35. package/koni/api/xcm/polkadotXcm.js +20 -39
  36. package/koni/api/xcm/utils.d.ts +36 -3
  37. package/koni/api/xcm/utils.js +72 -11
  38. package/koni/api/xcm/xTokens.js +6 -35
  39. package/koni/api/xcm/xcmPallet.js +5 -35
  40. package/koni/background/handlers/Extension.js +109 -29
  41. package/koni/background/handlers/State.js +2 -2
  42. package/package.json +13 -8
  43. package/packageInfo.js +1 -1
  44. package/services/chain-service/constants.d.ts +1 -0
  45. package/services/chain-service/constants.js +8 -7
  46. package/services/chain-service/index.js +13 -8
  47. package/services/chain-service/types.d.ts +8 -0
  48. package/services/chain-service/utils.d.ts +0 -1
  49. package/services/chain-service/utils.js +1 -4
  50. package/services/transaction-service/helpers/index.d.ts +2 -0
  51. package/services/transaction-service/helpers/index.js +42 -0
  52. package/services/transaction-service/index.js +54 -20
  53. package/services/transaction-service/types.d.ts +2 -2
  54. package/utils/number.d.ts +9 -0
  55. package/utils/number.js +100 -0
@@ -3,7 +3,7 @@
3
3
 
4
4
  import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
5
5
  import { StakingStatus, StakingTxErrorType, StakingType, UnstakingStatus } from '@subwallet/extension-base/background/KoniTypes';
6
- import { calculateAlephZeroValidatorReturn, calculateChainStakedReturn, calculateInflation, calculateValidatorStakedReturn, getCommission, parseIdentity, parsePoolStashAddress, transformPoolName } from '@subwallet/extension-base/koni/api/staking/bonding/utils';
6
+ import { calculateAlephZeroValidatorReturn, calculateChainStakedReturn, calculateInflation, calculateTernoaValidatorReturn, calculateValidatorStakedReturn, getCommission, parseIdentity, parsePoolStashAddress, transformPoolName } from '@subwallet/extension-base/koni/api/staking/bonding/utils';
7
7
  import { _STAKING_CHAIN_GROUP, _STAKING_ERA_LENGTH_MAP } from '@subwallet/extension-base/services/chain-service/constants';
8
8
  import { _getChainSubstrateAddressPrefix } from '@subwallet/extension-base/services/chain-service/utils';
9
9
  import { reformatAddress } from '@subwallet/extension-base/utils';
@@ -138,7 +138,7 @@ export async function getRelayChainStakingMetadata(chainInfo, substrateApi) {
138
138
  chain,
139
139
  type: StakingType.NOMINATED,
140
140
  era: parseInt(currentEra),
141
- expectedReturn,
141
+ expectedReturn: !_STAKING_CHAIN_GROUP.ternoa.includes(chain) ? expectedReturn : undefined,
142
142
  // in %, annually
143
143
  inflation,
144
144
  minStake: minStake.toString(),
@@ -477,7 +477,8 @@ export async function getRelayValidatorsInfo(chain, substrateApi, decimals, chai
477
477
  const currentEra = _era.toString();
478
478
  const allValidators = [];
479
479
  const validatorInfoList = [];
480
- const [_totalEraStake, _eraStakers, _minBond] = await Promise.all([chainApi.api.query.staking.erasTotalStake(parseInt(currentEra)), chainApi.api.query.staking.erasStakers.entries(parseInt(currentEra)), chainApi.api.query.staking.minNominatorBond()]);
480
+ const [_totalEraStake, _eraStakers, _minBond, _stakingRewards] = await Promise.all([chainApi.api.query.staking.erasTotalStake(parseInt(currentEra)), chainApi.api.query.staking.erasStakers.entries(parseInt(currentEra)), chainApi.api.query.staking.minNominatorBond(), chainApi.api.query.stakingRewards && chainApi.api.query.stakingRewards.data()]);
481
+ const stakingRewards = _stakingRewards === null || _stakingRewards === void 0 ? void 0 : _stakingRewards.toPrimitive();
481
482
  const maxNominatorRewarded = chainApi.api.consts.staking.maxNominatorRewardedPerValidator.toString();
482
483
  const bnTotalEraStake = new BN(_totalEraStake.toString());
483
484
  const eraStakers = _eraStakers;
@@ -540,7 +541,15 @@ export async function getRelayValidatorsInfo(chain, substrateApi, decimals, chai
540
541
  for (const validator of validatorInfoList) {
541
542
  const commission = extraInfoMap[validator.address].commission;
542
543
  const bnValidatorStake = totalStakeMap[validator.address].div(bnDecimals);
543
- validator.expectedReturn = _STAKING_CHAIN_GROUP.aleph.includes(chain) ? calculateAlephZeroValidatorReturn(chainStakingMetadata.expectedReturn, getCommission(commission)) : calculateValidatorStakedReturn(chainStakingMetadata.expectedReturn, bnValidatorStake, bnAvgStake, getCommission(commission));
544
+ if (_STAKING_CHAIN_GROUP.aleph.includes(chain)) {
545
+ validator.expectedReturn = calculateAlephZeroValidatorReturn(chainStakingMetadata.expectedReturn, getCommission(commission));
546
+ } else if (_STAKING_CHAIN_GROUP.ternoa.includes(chain)) {
547
+ const rewardPerValidator = new BN(stakingRewards.sessionExtraRewardPayout).divn(allValidators.length).div(bnDecimals);
548
+ const validatorStake = totalStakeMap[validator.address].div(bnDecimals).toNumber();
549
+ validator.expectedReturn = calculateTernoaValidatorReturn(rewardPerValidator.toNumber(), validatorStake, getCommission(commission));
550
+ } else {
551
+ validator.expectedReturn = calculateValidatorStakedReturn(chainStakingMetadata.expectedReturn, bnValidatorStake, bnAvgStake, getCommission(commission));
552
+ }
544
553
  validator.commission = parseFloat(commission.split('%')[0]);
545
554
  validator.blocked = extraInfoMap[validator.address].blocked;
546
555
  validator.identity = extraInfoMap[validator.address].identity;
@@ -102,6 +102,10 @@ export interface Unlocking {
102
102
  remainingEras: BN;
103
103
  value: BN;
104
104
  }
105
+ export interface TernoaStakingRewardsStakingRewardsData {
106
+ sessionEraPayout: string;
107
+ sessionExtraRewardPayout: string;
108
+ }
105
109
  export declare function parsePoolStashAddress(api: ApiPromise, index: number, poolId: number, poolsPalletId: string): string;
106
110
  export declare function transformPoolName(input: string): string;
107
111
  export declare function parseIdentity(identityInfo: PalletIdentityRegistration | null): string | undefined;
@@ -111,6 +115,7 @@ export declare function calcInflationRewardCurve(minInflation: number, stakedFra
111
115
  export declare function calculateInflation(totalEraStake: BN, totalIssuance: BN, numAuctions: number, networkKey: string): number;
112
116
  export declare function calculateChainStakedReturn(inflation: number, totalEraStake: BN, totalIssuance: BN, networkKey: string): number;
113
117
  export declare function calculateAlephZeroValidatorReturn(chainStakedReturn: number, commission: number): number;
118
+ export declare function calculateTernoaValidatorReturn(rewardPerValidator: number, validatorStake: number, commission: number): number;
114
119
  export declare function calculateValidatorStakedReturn(chainStakedReturn: number, totalValidatorStake: BN, avgStake: BN, commission: number): number;
115
120
  export declare function getCommission(commissionString: string): number;
116
121
  export interface InflationConfig {
@@ -86,6 +86,12 @@ export function calculateChainStakedReturn(inflation, totalEraStake, totalIssuan
86
86
  export function calculateAlephZeroValidatorReturn(chainStakedReturn, commission) {
87
87
  return chainStakedReturn * (100 - commission) / 100;
88
88
  }
89
+ export function calculateTernoaValidatorReturn(rewardPerValidator, validatorStake, commission) {
90
+ const percentRewardForNominators = (100 - commission) / 100;
91
+ const rewardForNominators = rewardPerValidator * percentRewardForNominators;
92
+ const stakeRatio = rewardForNominators / validatorStake;
93
+ return stakeRatio * 365 * 100;
94
+ }
89
95
  export function calculateValidatorStakedReturn(chainStakedReturn, totalValidatorStake, avgStake, commission) {
90
96
  const bnAdjusted = avgStake.mul(BN_HUNDRED).div(totalValidatorStake);
91
97
  const adjusted = bnAdjusted.toNumber() * chainStakedReturn;
@@ -1,49 +1,30 @@
1
1
  // Copyright 2019-2022 @subwallet/extension-base
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { getBeneficiary, getDestWeight } from '@subwallet/extension-base/koni/api/xcm/utils';
5
- import { _getSubstrateParaId, _getXcmAssetMultilocation, _isSubstrateParaChain, _isSubstrateRelayChain } from '@subwallet/extension-base/services/chain-service/utils';
6
- function getDestinationChainLocation(destinationChainInfo) {
7
- if (_isSubstrateParaChain(destinationChainInfo)) {
8
- return {
9
- V1: {
10
- parents: 1,
11
- interior: {
12
- X1: {
13
- Parachain: _getSubstrateParaId(destinationChainInfo)
14
- }
15
- }
16
- }
17
- };
18
- }
19
- return {
20
- // to relaychain by default
21
- V1: {
22
- parents: 1,
23
- interior: 'Here'
24
- }
25
- };
26
- }
27
- function getAssetLocation(tokenInfo, sendingValue) {
28
- const multilocation = _getXcmAssetMultilocation(tokenInfo);
29
- return {
30
- V1: [{
31
- id: multilocation,
32
- fun: {
33
- Fungible: sendingValue
34
- }
35
- }]
36
- };
37
- }
4
+ import { getBeneficiary, getDestinationChainLocation, getDestWeight, getTokenLocation } from '@subwallet/extension-base/koni/api/xcm/utils';
5
+ import { _isNativeToken, _isSubstrateRelayChain } from '@subwallet/extension-base/services/chain-service/utils';
38
6
  export function getExtrinsicByPolkadotXcmPallet(tokenInfo, originChainInfo, destinationChainInfo, recipientAddress, value, api) {
39
7
  const weightParam = getDestWeight();
40
- const beneficiary = getBeneficiary(originChainInfo, destinationChainInfo, recipientAddress);
41
- const destination = getDestinationChainLocation(destinationChainInfo);
42
- const assetLocation = getAssetLocation(tokenInfo, value);
8
+ const beneficiary = getBeneficiary(destinationChainInfo, recipientAddress);
9
+ const destination = getDestinationChainLocation(originChainInfo, destinationChainInfo);
10
+ let assetLocation = getTokenLocation(tokenInfo, value);
43
11
  let method = 'limitedReserveTransferAssets';
44
- if (['astar', 'shiden'].includes(originChainInfo.slug)) {
12
+ if (['astar', 'shiden'].includes(originChainInfo.slug) && !_isNativeToken(tokenInfo)) {
45
13
  method = 'limitedReserveWithdrawAssets';
46
- } else if (_isSubstrateRelayChain(destinationChainInfo)) {
14
+ } else if (['statemint', 'statemine'].includes(originChainInfo.slug) && _isSubstrateRelayChain(destinationChainInfo)) {
15
+ assetLocation = {
16
+ V1: [{
17
+ id: {
18
+ Concrete: {
19
+ parents: 1,
20
+ interior: 'Here'
21
+ }
22
+ },
23
+ fun: {
24
+ Fungible: value
25
+ }
26
+ }]
27
+ };
47
28
  method = 'limitedTeleportAssets';
48
29
  }
49
30
  return api.tx.polkadotXcm[method](destination, beneficiary, assetLocation, 0,
@@ -1,10 +1,10 @@
1
- import { _ChainInfo } from '@subwallet/chain-list/types';
1
+ import { _ChainAsset, _ChainInfo } from '@subwallet/chain-list/types';
2
2
  export declare const FOUR_INSTRUCTIONS_WEIGHT = 5000000000;
3
3
  export declare const FOUR_INSTRUCTIONS_LIMITED_WEIGHT: {
4
4
  Limited: number;
5
5
  };
6
- export declare function getReceiverLocation(originChainInfo: _ChainInfo, destinationChainInfo: _ChainInfo, toAddress: string): Record<string, any>;
7
- export declare function getBeneficiary(originChainInfo: _ChainInfo, destinationChainInfo: _ChainInfo, recipientAddress: string, version?: string): {
6
+ export declare function getReceiverLocation(destinationChainInfo: _ChainInfo, toAddress: string, version?: string): Record<string, any>;
7
+ export declare function getBeneficiary(destinationChainInfo: _ChainInfo, recipientAddress: string, version?: string): {
8
8
  [x: string]: {
9
9
  parents: number;
10
10
  interior: {
@@ -13,3 +13,36 @@ export declare function getBeneficiary(originChainInfo: _ChainInfo, destinationC
13
13
  };
14
14
  };
15
15
  export declare function getDestWeight(): string;
16
+ export declare function getTokenLocation(tokenInfo: _ChainAsset, sendingValue: string, version?: string): {
17
+ [x: string]: {
18
+ id: Record<string, any>;
19
+ fun: {
20
+ Fungible: string;
21
+ };
22
+ }[];
23
+ };
24
+ export declare function getDestMultilocation(destinationChainInfo: _ChainInfo, recipient: string, version?: string): {
25
+ [x: string]: {
26
+ parents: number;
27
+ interior: {
28
+ X2: Record<string, any>[];
29
+ };
30
+ };
31
+ } | {
32
+ [x: string]: {
33
+ parents: number;
34
+ interior: {
35
+ X1: Record<string, any>;
36
+ };
37
+ };
38
+ };
39
+ export declare function getDestinationChainLocation(originChainInfo: _ChainInfo, destinationChainInfo: _ChainInfo, version?: string): {
40
+ [x: string]: {
41
+ parents: number;
42
+ interior: string | {
43
+ X1: {
44
+ Parachain: number;
45
+ };
46
+ };
47
+ };
48
+ };
@@ -2,7 +2,7 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import { COMMON_CHAIN_SLUGS } from '@subwallet/chain-list';
5
- import { _isChainEvmCompatible } from '@subwallet/extension-base/services/chain-service/utils';
5
+ import { _getSubstrateParaId, _getXcmAssetMultilocation, _isChainEvmCompatible, _isNativeToken, _isSubstrateParaChain, _isSubstrateRelayChain } from '@subwallet/extension-base/services/chain-service/utils';
6
6
  import { decodeAddress, evmToAddress } from '@polkadot/util-crypto';
7
7
  export const FOUR_INSTRUCTIONS_WEIGHT = 5000000000;
8
8
  export const FOUR_INSTRUCTIONS_LIMITED_WEIGHT = {
@@ -11,13 +11,14 @@ export const FOUR_INSTRUCTIONS_LIMITED_WEIGHT = {
11
11
 
12
12
  // get multilocation for destination chain from a parachain
13
13
 
14
- export function getReceiverLocation(originChainInfo, destinationChainInfo, toAddress) {
14
+ export function getReceiverLocation(destinationChainInfo, toAddress, version) {
15
+ const network = version && version === 'V3' ? undefined : 'Any';
15
16
  if (destinationChainInfo.slug === COMMON_CHAIN_SLUGS.ASTAR_EVM) {
16
17
  const ss58Address = evmToAddress(toAddress, 2006); // TODO: shouldn't pass addressPrefix directly
17
18
 
18
19
  return {
19
20
  AccountId32: {
20
- network: 'Any',
21
+ network,
21
22
  id: decodeAddress(ss58Address)
22
23
  }
23
24
  };
@@ -25,20 +26,20 @@ export function getReceiverLocation(originChainInfo, destinationChainInfo, toAdd
25
26
  if (_isChainEvmCompatible(destinationChainInfo)) {
26
27
  return {
27
28
  AccountKey20: {
28
- network: 'Any',
29
+ network,
29
30
  key: toAddress
30
31
  }
31
32
  };
32
33
  }
33
34
  return {
34
35
  AccountId32: {
35
- network: 'Any',
36
+ network,
36
37
  id: decodeAddress(toAddress)
37
38
  }
38
39
  };
39
40
  }
40
- export function getBeneficiary(originChainInfo, destinationChainInfo, recipientAddress, version = 'V1') {
41
- const receiverLocation = getReceiverLocation(originChainInfo, destinationChainInfo, recipientAddress);
41
+ export function getBeneficiary(destinationChainInfo, recipientAddress, version = 'V1') {
42
+ const receiverLocation = getReceiverLocation(destinationChainInfo, recipientAddress, version);
42
43
  return {
43
44
  [version]: {
44
45
  parents: 0,
@@ -50,8 +51,68 @@ export function getBeneficiary(originChainInfo, destinationChainInfo, recipientA
50
51
  }
51
52
  export function getDestWeight() {
52
53
  return 'Unlimited';
53
- // return api.tx.xTokens.transfer.meta.args[3].type.toString() ===
54
- // 'XcmV2WeightLimit'
55
- // ? 'Unlimited'
56
- // : FOUR_INSTRUCTIONS_WEIGHT;
54
+ }
55
+ export function getTokenLocation(tokenInfo, sendingValue, version = 'V1') {
56
+ if (!_isNativeToken(tokenInfo)) {
57
+ const multilocation = _getXcmAssetMultilocation(tokenInfo);
58
+ return {
59
+ [version]: [{
60
+ id: multilocation,
61
+ fun: {
62
+ Fungible: sendingValue
63
+ }
64
+ }]
65
+ };
66
+ }
67
+ return {
68
+ [version]: [{
69
+ id: {
70
+ Concrete: {
71
+ parents: 0,
72
+ interior: 'Here'
73
+ }
74
+ },
75
+ fun: {
76
+ Fungible: sendingValue
77
+ }
78
+ }]
79
+ };
80
+ }
81
+ export function getDestMultilocation(destinationChainInfo, recipient, version = 'V1') {
82
+ const receiverLocation = getReceiverLocation(destinationChainInfo, recipient, version);
83
+ if (_isSubstrateParaChain(destinationChainInfo)) {
84
+ const interior = {
85
+ X2: [{
86
+ Parachain: _getSubstrateParaId(destinationChainInfo)
87
+ }, receiverLocation]
88
+ };
89
+ return {
90
+ [version]: {
91
+ parents: 1,
92
+ interior
93
+ }
94
+ };
95
+ }
96
+ return {
97
+ [version]: {
98
+ parents: 1,
99
+ interior: {
100
+ X1: receiverLocation
101
+ }
102
+ }
103
+ };
104
+ }
105
+ export function getDestinationChainLocation(originChainInfo, destinationChainInfo, version = 'V1') {
106
+ const parents = _isSubstrateRelayChain(originChainInfo) ? 0 : 1;
107
+ const interior = _isSubstrateParaChain(destinationChainInfo) ? {
108
+ X1: {
109
+ Parachain: _getSubstrateParaId(destinationChainInfo)
110
+ }
111
+ } : 'Here';
112
+ return {
113
+ [version]: {
114
+ parents,
115
+ interior
116
+ }
117
+ };
57
118
  }
@@ -1,9 +1,8 @@
1
1
  // Copyright 2019-2022 @subwallet/extension-base
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { FOUR_INSTRUCTIONS_WEIGHT, getDestWeight, getReceiverLocation } from '@subwallet/extension-base/koni/api/xcm/utils';
5
- import { _XCM_TYPE } from '@subwallet/extension-base/services/chain-service/constants';
6
- import { _getSubstrateParaId, _getTokenOnChainInfo, _getXcmAssetId, _getXcmAssetMultilocation, _getXcmAssetType, _getXcmTransferType, _isNativeToken } from '@subwallet/extension-base/services/chain-service/utils';
4
+ import { FOUR_INSTRUCTIONS_WEIGHT, getDestMultilocation, getDestWeight } from '@subwallet/extension-base/koni/api/xcm/utils';
5
+ import { _getTokenOnChainAssetId, _getTokenOnChainInfo, _getXcmAssetId, _getXcmAssetMultilocation, _getXcmAssetType, _isNativeToken } from '@subwallet/extension-base/services/chain-service/utils';
7
6
  function getCurrencyId(tokenInfo) {
8
7
  if (['acala', 'karura'].includes(tokenInfo.originChain) && _isNativeToken(tokenInfo)) {
9
8
  return _getXcmAssetMultilocation(tokenInfo);
@@ -16,38 +15,10 @@ function getCurrencyId(tokenInfo) {
16
15
  } else if (['pioneer'].includes(tokenInfo.originChain)) {
17
16
  return _getXcmAssetMultilocation(tokenInfo);
18
17
  }
19
- return _getTokenOnChainInfo(tokenInfo);
20
- }
21
- function getMultiLocationForXtokensPallet(originChainInfo, destinationChainInfo, toAddress) {
22
- const xcmType = _getXcmTransferType(originChainInfo, destinationChainInfo);
23
- const paraId = _getSubstrateParaId(destinationChainInfo);
24
- const receiverLocation = getReceiverLocation(originChainInfo, destinationChainInfo, toAddress);
25
- if (xcmType === _XCM_TYPE.PP) {
26
- // parachain -> parachain
27
- const interior = {
28
- X2: [{
29
- Parachain: paraId
30
- }, receiverLocation]
31
- };
32
- return {
33
- V1: {
34
- parents: 1,
35
- interior
36
- }
37
- };
38
- }
39
-
40
- // parachain -> relaychain by default
41
- return {
42
- V1: {
43
- parents: 1,
44
- interior: {
45
- X1: receiverLocation
46
- }
47
- }
48
- };
18
+ return _getTokenOnChainInfo(tokenInfo) || _getTokenOnChainAssetId(tokenInfo);
49
19
  }
50
20
  export function getExtrinsicByXtokensPallet(tokenInfo, originChainInfo, destinationChainInfo, recipientAddress, value, api) {
51
- const weightParam = ['pioneer'].includes(originChainInfo.slug) ? FOUR_INSTRUCTIONS_WEIGHT : getDestWeight();
52
- return api.tx.xTokens.transfer(getCurrencyId(tokenInfo), value, getMultiLocationForXtokensPallet(originChainInfo, destinationChainInfo, recipientAddress), weightParam);
21
+ const weightParam = ['pioneer', 'hydradx_main'].includes(originChainInfo.slug) ? FOUR_INSTRUCTIONS_WEIGHT : getDestWeight();
22
+ const destVersion = ['moonbeam', 'moonriver'].includes(originChainInfo.slug) ? 'V3' : undefined;
23
+ return api.tx.xTokens.transfer(getCurrencyId(tokenInfo), value, getDestMultilocation(destinationChainInfo, recipientAddress, destVersion), weightParam);
53
24
  }
@@ -1,44 +1,14 @@
1
1
  // Copyright 2019-2022 @subwallet/extension-base
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { getBeneficiary, getDestWeight } from '@subwallet/extension-base/koni/api/xcm/utils';
5
- import { _getSubstrateParaId } from '@subwallet/extension-base/services/chain-service/utils';
6
- function getDestinationChainLocation(destinationChainInfo, version = 'V1') {
7
- return {
8
- [version]: {
9
- parents: 0,
10
- interior: {
11
- X1: {
12
- Parachain: _getSubstrateParaId(destinationChainInfo)
13
- }
14
- }
15
- }
16
- };
17
- }
18
- function getTokenLocation(sendingValue, version = 'V2') {
19
- return {
20
- // always native token of relaychain
21
- [version]: [{
22
- id: {
23
- Concrete: {
24
- parents: 0,
25
- interior: 'Here'
26
- }
27
- },
28
- fun: {
29
- Fungible: sendingValue
30
- }
31
- }]
32
- };
33
- }
34
-
4
+ import { getBeneficiary, getDestinationChainLocation, getDestWeight, getTokenLocation } from '@subwallet/extension-base/koni/api/xcm/utils';
35
5
  // this pallet is only used by Relaychains
36
6
  export function getExtrinsicByXcmPalletPallet(tokenInfo, originChainInfo, destinationChainInfo, recipientAddress, value, api) {
37
7
  const weightParam = getDestWeight();
38
- const xcmVer = ['kusama'].includes(originChainInfo.slug) ? 'V2' : 'V1';
39
- const destination = getDestinationChainLocation(destinationChainInfo, xcmVer);
40
- const beneficiary = getBeneficiary(originChainInfo, destinationChainInfo, recipientAddress, xcmVer);
41
- const tokenLocation = getTokenLocation(value, xcmVer);
8
+ const xcmVer = ['kusama'].includes(originChainInfo.slug) ? 'V3' : 'V1';
9
+ const destination = getDestinationChainLocation(originChainInfo, destinationChainInfo, xcmVer);
10
+ const beneficiary = getBeneficiary(destinationChainInfo, recipientAddress, xcmVer);
11
+ const tokenLocation = getTokenLocation(tokenInfo, value, xcmVer);
42
12
  let method = 'limitedReserveTransferAssets';
43
13
  if (['statemint', 'statemine'].includes(destinationChainInfo.slug)) {
44
14
  method = 'limitedTeleportAssets';
@@ -2,12 +2,14 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import Common from '@ethereumjs/common';
5
+ import { _AssetType } from '@subwallet/chain-list/types';
5
6
  import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
6
7
  import { isJsonPayload, SEED_DEFAULT_LENGTH, SEED_LENGTHS } from '@subwallet/extension-base/background/handlers/Extension';
7
8
  import { withErrorLog } from '@subwallet/extension-base/background/handlers/helpers';
8
9
  import { createSubscription } from '@subwallet/extension-base/background/handlers/subscriptions';
9
- import { AccountExternalErrorCode, BasicTxErrorType, ChainType, ExternalRequestPromiseStatus, ExtrinsicType, StakingType, TransferTxErrorType } from '@subwallet/extension-base/background/KoniTypes';
10
- import { ALL_ACCOUNT_KEY, ALL_GENESIS_HASH } from '@subwallet/extension-base/constants';
10
+ import { AccountExternalErrorCode, BasicTxErrorType, BasicTxWarningCode, ChainType, ExternalRequestPromiseStatus, ExtrinsicType, StakingType, TransferTxErrorType } from '@subwallet/extension-base/background/KoniTypes';
11
+ import { TransactionWarning } from '@subwallet/extension-base/background/warnings/TransactionWarning';
12
+ import { ALL_ACCOUNT_KEY, ALL_GENESIS_HASH, XCM_MIN_AMOUNT_RATIO } from '@subwallet/extension-base/constants';
11
13
  import { ALLOWED_PATH } from '@subwallet/extension-base/defaults';
12
14
  import { parseSubstrateTransaction } from '@subwallet/extension-base/koni/api/dotsama/parseTransaction';
13
15
  import { checkReferenceCount, checkSupportTransfer, createTransferExtrinsic } from '@subwallet/extension-base/koni/api/dotsama/transfer';
@@ -25,6 +27,7 @@ import { reformatAddress } from '@subwallet/extension-base/utils';
25
27
  import { convertSubjectInfoToAddresses } from '@subwallet/extension-base/utils/address';
26
28
  import { createTransactionFromRLP, signatureToHex } from '@subwallet/extension-base/utils/eth';
27
29
  import { parseContractInput, parseEvmRlp } from '@subwallet/extension-base/utils/eth/parseTransaction';
30
+ import { balanceFormatter, formatNumber } from '@subwallet/extension-base/utils/number';
28
31
  import { createPair } from '@subwallet/keyring';
29
32
  import { keyring } from '@subwallet/ui-keyring';
30
33
  import BigN from 'bignumber.js';
@@ -1398,31 +1401,58 @@ export default class KoniExtension {
1398
1401
 
1399
1402
  // Get native token amount
1400
1403
  const freeBalance = await this.#koniState.balanceService.getTokenFreeBalance(from, networkKey, tokenSlug);
1401
- if (isEthereumAddress(from) && isEthereumAddress(to) && _isTokenTransferredByEvm(tokenInfo)) {
1402
- // TODO: review this
1403
- chainType = ChainType.EVM;
1404
- const txVal = transferAll ? freeBalance.value : value || '0';
1405
-
1406
- // Estimate with EVM API
1407
- if (_isTokenEvmSmartContract(tokenInfo) || _isLocalToken(tokenInfo)) {
1408
- [transaction, transferAmount.value] = await getERC20TransactionObject(_getContractAddressOfToken(tokenInfo), chainInfo, from, to, txVal, !!transferAll, evmApiMap);
1404
+ try {
1405
+ if (isEthereumAddress(from) && isEthereumAddress(to) && _isTokenTransferredByEvm(tokenInfo)) {
1406
+ // TODO: review this
1407
+ chainType = ChainType.EVM;
1408
+ const txVal = transferAll ? freeBalance.value : value || '0';
1409
+
1410
+ // Estimate with EVM API
1411
+ if (_isTokenEvmSmartContract(tokenInfo) || _isLocalToken(tokenInfo)) {
1412
+ [transaction, transferAmount.value] = await getERC20TransactionObject(_getContractAddressOfToken(tokenInfo), chainInfo, from, to, txVal, !!transferAll, evmApiMap);
1413
+ } else {
1414
+ [transaction, transferAmount.value] = await getEVMTransactionObject(chainInfo, to, txVal, !!transferAll, evmApiMap);
1415
+ }
1409
1416
  } else {
1410
- [transaction, transferAmount.value] = await getEVMTransactionObject(chainInfo, to, txVal, !!transferAll, evmApiMap);
1417
+ const substrateApi = this.#koniState.getSubstrateApi(networkKey);
1418
+ [transaction, transferAmount.value] = await createTransferExtrinsic({
1419
+ transferAll: !!transferAll,
1420
+ value: value || '0',
1421
+ from: from,
1422
+ networkKey,
1423
+ tokenInfo,
1424
+ to: to,
1425
+ substrateApi
1426
+ });
1411
1427
  }
1412
- } else {
1413
- const substrateApi = this.#koniState.getSubstrateApi(networkKey);
1414
- [transaction, transferAmount.value] = await createTransferExtrinsic({
1415
- transferAll: !!transferAll,
1416
- value: value || '0',
1417
- from: from,
1418
- networkKey,
1419
- tokenInfo,
1420
- to: to,
1421
- substrateApi
1422
- });
1428
+ } catch (e) {
1429
+ const error = e;
1430
+ if (error.message.includes('transfer amount exceeds balance')) {
1431
+ error.message = 'Not enough balance';
1432
+ }
1433
+ throw error;
1423
1434
  }
1424
1435
  const transferNativeAmount = isTransferNativeToken ? transferAmount.value : '0';
1425
1436
  this.addContact(to);
1437
+ const additionalValidator = async inputTransaction => {
1438
+ const minAmount = tokenInfo.minAmount || '0';
1439
+ if (!isTransferNativeToken) {
1440
+ const {
1441
+ value: balance
1442
+ } = await this.#koniState.balanceService.getTokenFreeBalance(from, networkKey, tokenSlug);
1443
+ if (new BigN(balance).minus(transferAmount.value).lt(minAmount)) {
1444
+ inputTransaction.warnings.push(new TransactionWarning(BasicTxWarningCode.NOT_ENOUGH_EXISTENTIAL_DEPOSIT, ''));
1445
+ }
1446
+ }
1447
+ const {
1448
+ value: receiverBalance
1449
+ } = await this.#koniState.balanceService.getTokenFreeBalance(to, networkKey, tokenSlug);
1450
+ if (new BigN(receiverBalance).plus(transferAmount.value).lt(minAmount)) {
1451
+ const atLeast = new BigN(minAmount).minus(receiverBalance).plus((tokenInfo.decimals || 0) === 0 ? 0 : 1);
1452
+ const atLeastStr = formatNumber(atLeast, tokenInfo.decimals || 0, balanceFormatter);
1453
+ inputTransaction.errors.push(new TransactionError(TransferTxErrorType.RECEIVER_NOT_ENOUGH_EXISTENTIAL_DEPOSIT, `You must transfer at least ${atLeastStr} ${tokenInfo.symbol} to keep the destination account alive`));
1454
+ }
1455
+ };
1426
1456
  return this.#koniState.transactionService.handleTransaction({
1427
1457
  errors,
1428
1458
  warnings,
@@ -1434,8 +1464,9 @@ export default class KoniExtension {
1434
1464
  data: inputData,
1435
1465
  extrinsicType: isTransferNativeToken ? ExtrinsicType.TRANSFER_BALANCE : ExtrinsicType.TRANSFER_TOKEN,
1436
1466
  ignoreWarnings: transferAll,
1437
- isTransferAll: transferAll,
1438
- edAsWarning: isTransferNativeToken
1467
+ isTransferAll: isTransferNativeToken ? transferAll : false,
1468
+ edAsWarning: isTransferNativeToken,
1469
+ additionalValidator: additionalValidator
1439
1470
  });
1440
1471
  }
1441
1472
  validateCrossChainTransfer(destinationNetworkKey, sendingTokenSlug, sender, sendingValue) {
@@ -1463,6 +1494,8 @@ export default class KoniExtension {
1463
1494
  if (errors.length > 0) {
1464
1495
  return this.#koniState.transactionService.generateBeforeHandleResponseErrors(errors);
1465
1496
  }
1497
+ let additionalValidator;
1498
+ let eventsHandler;
1466
1499
  if (fromKeyPair && destinationTokenInfo) {
1467
1500
  const substrateApi = this.#koniState.getSubstrateApi(originNetworkKey);
1468
1501
  const chainInfoMap = this.#koniState.getChainInfoMap();
@@ -1474,6 +1507,40 @@ export default class KoniExtension {
1474
1507
  chainInfoMap,
1475
1508
  substrateApi
1476
1509
  });
1510
+ additionalValidator = async inputTransaction => {
1511
+ const destMinAmount = destinationTokenInfo.minAmount || '0';
1512
+ const atLeast = new BigN(destMinAmount).multipliedBy(XCM_MIN_AMOUNT_RATIO);
1513
+ if (new BigN(value).lt(atLeast)) {
1514
+ const atLeastStr = formatNumber(atLeast, destinationTokenInfo.decimals || 0, balanceFormatter);
1515
+ inputTransaction.errors.push(new TransactionError(TransferTxErrorType.RECEIVER_NOT_ENOUGH_EXISTENTIAL_DEPOSIT, `You must transfer at least ${atLeastStr} ${originTokenInfo.symbol} to keep the destination account alive`));
1516
+ }
1517
+ const srcMinAmount = originTokenInfo.minAmount || '0';
1518
+ const isTransferNativeToken = originTokenInfo.assetType === _AssetType.NATIVE;
1519
+ if (!isTransferNativeToken) {
1520
+ const {
1521
+ value: balance
1522
+ } = await this.#koniState.balanceService.getTokenFreeBalance(from, originNetworkKey, originTokenInfo.slug);
1523
+ if (new BigN(balance).minus(value).lt(srcMinAmount)) {
1524
+ inputTransaction.warnings.push(new TransactionWarning(BasicTxWarningCode.NOT_ENOUGH_EXISTENTIAL_DEPOSIT, ''));
1525
+ }
1526
+ }
1527
+ };
1528
+ eventsHandler = eventEmitter => {
1529
+ eventEmitter.on('send', () => {
1530
+ try {
1531
+ const dest = keyring.getPair(to);
1532
+ if (dest) {
1533
+ this.updateAssetSetting({
1534
+ autoEnableNativeToken: false,
1535
+ tokenSlug: destinationTokenInfo.slug,
1536
+ assetSetting: {
1537
+ visible: true
1538
+ }
1539
+ }).catch(console.error);
1540
+ }
1541
+ } catch (e) {}
1542
+ });
1543
+ };
1477
1544
  }
1478
1545
  this.addContact(to);
1479
1546
  return await this.#koniState.transactionService.handleTransaction({
@@ -1485,8 +1552,11 @@ export default class KoniExtension {
1485
1552
  extrinsicType: ExtrinsicType.TRANSFER_XCM,
1486
1553
  chainType: ChainType.SUBSTRATE,
1487
1554
  transferNativeAmount: _isNativeToken(originTokenInfo) ? value : '0',
1555
+ ignoreWarnings: inputData.transferAll,
1488
1556
  isTransferAll: inputData.transferAll,
1489
- errors
1557
+ errors,
1558
+ additionalValidator: additionalValidator,
1559
+ eventsHandler: eventsHandler
1490
1560
  });
1491
1561
  }
1492
1562
  async evmNftSubmitTransaction(inputData) {
@@ -1524,8 +1594,11 @@ export default class KoniExtension {
1524
1594
  disableChain(networkKey) {
1525
1595
  return this.#koniState.disableChain(networkKey);
1526
1596
  }
1527
- async enableChain(networkKey) {
1528
- return await this.#koniState.enableChain(networkKey);
1597
+ async enableChain({
1598
+ chainSlug,
1599
+ enableTokens
1600
+ }) {
1601
+ return await this.#koniState.enableChain(chainSlug, enableTokens);
1529
1602
  }
1530
1603
  async validateNetwork({
1531
1604
  existedChainSlug,
@@ -1715,9 +1788,15 @@ export default class KoniExtension {
1715
1788
  isSendingSelf
1716
1789
  };
1717
1790
  }
1718
- async enableChains(targetKeys) {
1791
+ async enableChains({
1792
+ chainSlugs,
1793
+ enableTokens
1794
+ }) {
1719
1795
  try {
1720
- await Promise.all(targetKeys.map(networkKey => this.enableChain(networkKey)));
1796
+ await Promise.all(chainSlugs.map(chainSlug => this.enableChain({
1797
+ chainSlug,
1798
+ enableTokens
1799
+ })));
1721
1800
  } catch (e) {
1722
1801
  return false;
1723
1802
  }
@@ -2839,6 +2918,7 @@ export default class KoniExtension {
2839
2918
  return Object.fromEntries(Object.entries(rs).map(([key, value]) => {
2840
2919
  const {
2841
2920
  additionalValidator,
2921
+ eventsHandler,
2842
2922
  transaction,
2843
2923
  ...transactionResult
2844
2924
  } = value;