@subwallet/extension-base 1.0.7-0 → 1.0.7-2

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 (64) hide show
  1. package/background/KoniTypes.d.ts +7 -8
  2. package/cjs/constants/index.js +1 -1
  3. package/cjs/koni/api/staking/bonding/amplitude.js +83 -0
  4. package/cjs/koni/api/staking/bonding/astar.js +109 -5
  5. package/cjs/koni/api/staking/bonding/index.js +35 -0
  6. package/cjs/koni/api/staking/bonding/paraChain.js +97 -0
  7. package/cjs/koni/api/staking/bonding/relayChain.js +203 -15
  8. package/cjs/koni/api/staking/bonding/utils.js +7 -0
  9. package/cjs/koni/api/staking/index.js +11 -11
  10. package/cjs/koni/api/staking/paraChain.js +200 -130
  11. package/cjs/koni/api/staking/relayChain.js +66 -68
  12. package/cjs/koni/api/staking/subsquidStaking.js +6 -11
  13. package/cjs/koni/background/cron.js +0 -25
  14. package/cjs/koni/background/handlers/Extension.js +98 -74
  15. package/cjs/koni/background/handlers/State.js +17 -19
  16. package/cjs/koni/background/subscription.js +57 -12
  17. package/cjs/packageInfo.js +1 -1
  18. package/cjs/services/chain-service/constants.js +3 -1
  19. package/cjs/services/chain-service/utils.js +6 -1
  20. package/cjs/services/storage-service/DatabaseService.js +7 -3
  21. package/cjs/services/storage-service/db-stores/ChainStakingMetadata.js +5 -0
  22. package/cjs/services/storage-service/db-stores/NominatorMetadata.js +4 -4
  23. package/cjs/services/transaction-service/index.js +6 -1
  24. package/constants/index.d.ts +1 -1
  25. package/constants/index.js +1 -1
  26. package/koni/api/staking/bonding/amplitude.d.ts +4 -0
  27. package/koni/api/staking/bonding/amplitude.js +81 -0
  28. package/koni/api/staking/bonding/astar.d.ts +4 -0
  29. package/koni/api/staking/bonding/astar.js +107 -5
  30. package/koni/api/staking/bonding/index.d.ts +1 -0
  31. package/koni/api/staking/bonding/index.js +38 -4
  32. package/koni/api/staking/bonding/paraChain.d.ts +4 -0
  33. package/koni/api/staking/bonding/paraChain.js +95 -0
  34. package/koni/api/staking/bonding/relayChain.d.ts +5 -0
  35. package/koni/api/staking/bonding/relayChain.js +198 -14
  36. package/koni/api/staking/bonding/utils.d.ts +5 -0
  37. package/koni/api/staking/bonding/utils.js +6 -0
  38. package/koni/api/staking/index.d.ts +4 -4
  39. package/koni/api/staking/index.js +11 -11
  40. package/koni/api/staking/paraChain.d.ts +5 -5
  41. package/koni/api/staking/paraChain.js +201 -131
  42. package/koni/api/staking/relayChain.d.ts +4 -4
  43. package/koni/api/staking/relayChain.js +66 -67
  44. package/koni/api/staking/subsquidStaking.d.ts +1 -1
  45. package/koni/api/staking/subsquidStaking.js +6 -11
  46. package/koni/background/cron.js +1 -26
  47. package/koni/background/handlers/Extension.js +29 -7
  48. package/koni/background/handlers/State.d.ts +2 -2
  49. package/koni/background/handlers/State.js +17 -19
  50. package/koni/background/subscription.d.ts +2 -1
  51. package/koni/background/subscription.js +58 -13
  52. package/package.json +5 -5
  53. package/packageInfo.js +1 -1
  54. package/services/chain-service/constants.d.ts +1 -0
  55. package/services/chain-service/constants.js +3 -2
  56. package/services/chain-service/types.d.ts +8 -0
  57. package/services/chain-service/utils.js +6 -1
  58. package/services/storage-service/DatabaseService.d.ts +2 -2
  59. package/services/storage-service/DatabaseService.js +7 -3
  60. package/services/storage-service/db-stores/ChainStakingMetadata.d.ts +1 -0
  61. package/services/storage-service/db-stores/ChainStakingMetadata.js +3 -0
  62. package/services/storage-service/db-stores/NominatorMetadata.d.ts +2 -2
  63. package/services/storage-service/db-stores/NominatorMetadata.js +4 -4
  64. package/services/transaction-service/index.js +6 -1
@@ -1,3 +1,3 @@
1
1
  import { _ChainInfo } from '@subwallet/chain-list/types';
2
2
  import { StakingRewardItem } from '@subwallet/extension-base/background/KoniTypes';
3
- export declare const getAllSubsquidStaking: (accounts: string[], chainInfoMap: Record<string, _ChainInfo>) => Promise<StakingRewardItem[]>;
3
+ export declare const getAllSubsquidStaking: (accounts: string[], chainInfoMap: Record<string, _ChainInfo>, callback: (rewardItem: StakingRewardItem) => void) => Promise<void>;
@@ -32,9 +32,8 @@ const getSubsquidQuery = (account, chain) => {
32
32
  }
33
33
  }`;
34
34
  };
35
- const getSubsquidStaking = async (accounts, chain, chainInfoMap) => {
35
+ const getSubsquidStaking = async (accounts, chain, chainInfoMap, callback) => {
36
36
  try {
37
- const result = [];
38
37
  await Promise.all(accounts.map(async account => {
39
38
  if (_isChainEvmCompatible(chainInfoMap[chain]) && isEthereumAddress(account) || !_isChainEvmCompatible(chainInfoMap[chain]) && !isEthereumAddress(account)) {
40
39
  const parsedAccount = reformatAddress(account, _getChainSubstrateAddressPrefix(chainInfoMap[chain]));
@@ -70,17 +69,15 @@ const getSubsquidStaking = async (accounts, chain, chainInfoMap) => {
70
69
  }
71
70
  }
72
71
  if (stakingRewardItem.totalReward && parseFloat(stakingRewardItem.totalReward) > 0) {
73
- result.push(stakingRewardItem);
72
+ callback(stakingRewardItem);
74
73
  }
75
74
  }
76
75
  }));
77
- return result;
78
76
  } catch (e) {
79
- return [];
77
+ console.debug(e);
80
78
  }
81
79
  };
82
- export const getAllSubsquidStaking = async (accounts, chainInfoMap) => {
83
- let rewardList = [];
80
+ export const getAllSubsquidStaking = async (accounts, chainInfoMap, callback) => {
84
81
  const filteredNetworks = [];
85
82
  Object.values(chainInfoMap).forEach(network => {
86
83
  if (INDEXER_SUPPORTED_STAKING_CHAINS.includes(network.slug)) {
@@ -89,11 +86,9 @@ export const getAllSubsquidStaking = async (accounts, chainInfoMap) => {
89
86
  });
90
87
  try {
91
88
  await Promise.all(filteredNetworks.map(async network => {
92
- const rewardItems = await getSubsquidStaking(accounts, network, chainInfoMap);
93
- rewardList = rewardList.concat(rewardItems);
89
+ await getSubsquidStaking(accounts, network, chainInfoMap, callback);
94
90
  }));
95
91
  } catch (e) {
96
- return rewardList;
92
+ console.debug(e);
97
93
  }
98
- return rewardList;
99
94
  };
@@ -1,7 +1,7 @@
1
1
  // Copyright 2019-2022 @subwallet/extension-koni authors & contributors
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { CRON_AUTO_RECOVER_DOTSAMA_INTERVAL, CRON_GET_API_MAP_STATUS, CRON_REFRESH_CHAIN_NOMINATOR_METADATA, CRON_REFRESH_CHAIN_STAKING_METADATA, CRON_REFRESH_NFT_INTERVAL, CRON_REFRESH_STAKING_REWARD_FAST_INTERVAL, CRON_REFRESH_STAKING_REWARD_INTERVAL } from '@subwallet/extension-base/constants';
4
+ import { CRON_AUTO_RECOVER_DOTSAMA_INTERVAL, CRON_GET_API_MAP_STATUS, CRON_REFRESH_CHAIN_STAKING_METADATA, CRON_REFRESH_NFT_INTERVAL, CRON_REFRESH_STAKING_REWARD_FAST_INTERVAL, CRON_REFRESH_STAKING_REWARD_INTERVAL } from '@subwallet/extension-base/constants';
5
5
  import { _isChainSupportEvmNft, _isChainSupportNativeNft, _isChainSupportSubstrateStaking, _isChainSupportWasmNft } from '@subwallet/extension-base/services/chain-service/utils';
6
6
  import { waitTimeout } from '@subwallet/extension-base/utils';
7
7
  import { Subject } from 'rxjs';
@@ -49,26 +49,6 @@ export class KoniCron {
49
49
  delete this.cronMap[key];
50
50
  });
51
51
  };
52
-
53
- // init = () => {
54
- // const currentAccountInfo = this.state.keyringService.currentAccount;
55
- //
56
- // if (!currentAccountInfo?.address) {
57
- // return;
58
- // }
59
- //
60
- // if (Object.keys(this.state.getSubstrateApiMap()).length !== 0 || Object.keys(this.state.getEvmApiMap()).length !== 0) {
61
- // this.refreshNft(currentAccountInfo.address, this.state.getApiMap(), this.state.getSmartContractNfts(), this.state.getActiveChainInfoMap());
62
- // this.updateApiMapStatus();
63
- // this.refreshStakingReward(currentAccountInfo.address);
64
- // this.refreshStakingRewardFastInterval(currentAccountInfo.address);
65
- // // this.updateChainStakingMetadata(this.state.getChainInfoMap(), this.state.getChainStateMap(), this.state.getSubstrateApiMap());
66
- // this.updateNominatorMetadata(currentAccountInfo.address, this.state.getChainInfoMap(), this.state.getChainStateMap(), this.state.getSubstrateApiMap());
67
- // } else {
68
- // this.setStakingRewardReady();
69
- // }
70
- // };
71
-
72
52
  start = () => {
73
53
  if (this.status === 'running') {
74
54
  return;
@@ -109,7 +89,6 @@ export class KoniCron {
109
89
  (commonReload || needUpdateStaking || stakingSubmitted) && this.resetStakingReward();
110
90
  (commonReload || needUpdateStaking || stakingSubmitted) && this.removeCron('refreshStakingReward');
111
91
  (commonReload || needUpdateStaking || stakingSubmitted) && this.removeCron('refreshPoolingStakingReward');
112
- (commonReload || needUpdateStaking || stakingSubmitted) && this.removeCron('updateNominatorMetadata');
113
92
  needUpdateStaking && this.removeCron('updateChainStakingMetadata');
114
93
 
115
94
  // Chains
@@ -122,7 +101,6 @@ export class KoniCron {
122
101
  chainUpdated && this.addCron('recoverApiMap', this.recoverApiMap, CRON_AUTO_RECOVER_DOTSAMA_INTERVAL, false);
123
102
  (commonReload || needUpdateStaking || stakingSubmitted) && this.addCron('refreshStakingReward', this.refreshStakingReward(address), CRON_REFRESH_STAKING_REWARD_INTERVAL);
124
103
  (commonReload || needUpdateStaking || stakingSubmitted) && this.addCron('refreshPoolingStakingReward', this.refreshStakingRewardFastInterval(address), CRON_REFRESH_STAKING_REWARD_FAST_INTERVAL);
125
- (commonReload || needUpdateStaking || stakingSubmitted) && this.addCron('updateNominatorMetadata', this.updateNominatorMetadata(address, serviceInfo.chainInfoMap, serviceInfo.chainStateMap, serviceInfo.chainApiMap.substrate), CRON_REFRESH_CHAIN_NOMINATOR_METADATA);
126
104
  needUpdateStaking && this.addCron('updateChainStakingMetadata', this.updateChainStakingMetadata(serviceInfo.chainInfoMap, serviceInfo.chainStateMap, serviceInfo.chainApiMap.substrate), CRON_REFRESH_CHAIN_STAKING_METADATA);
127
105
  } else {
128
106
  this.setStakingRewardReady();
@@ -140,7 +118,6 @@ export class KoniCron {
140
118
  this.addCron('refreshStakingReward', this.refreshStakingReward(currentAccountInfo.address), CRON_REFRESH_STAKING_REWARD_INTERVAL);
141
119
  this.addCron('refreshPoolingStakingReward', this.refreshStakingRewardFastInterval(currentAccountInfo.address), CRON_REFRESH_STAKING_REWARD_FAST_INTERVAL);
142
120
  this.addCron('updateChainStakingMetadata', this.updateChainStakingMetadata(this.state.getChainInfoMap(), this.state.getChainStateMap(), this.state.getSubstrateApiMap()), CRON_REFRESH_CHAIN_STAKING_METADATA);
143
- this.addCron('updateNominatorMetadata', this.updateNominatorMetadata(currentAccountInfo.address, this.state.getChainInfoMap(), this.state.getChainStateMap(), this.state.getSubstrateApiMap()), CRON_REFRESH_CHAIN_NOMINATOR_METADATA);
144
121
  } else {
145
122
  this.setStakingRewardReady();
146
123
  }
@@ -235,10 +212,8 @@ export class KoniCron {
235
212
  this.resetStakingReward();
236
213
  this.removeCron('refreshStakingReward');
237
214
  this.removeCron('refreshPoolingStakingReward');
238
- this.removeCron('updateNominatorMetadata');
239
215
  this.addCron('refreshStakingReward', this.refreshStakingReward(address), CRON_REFRESH_STAKING_REWARD_INTERVAL);
240
216
  this.addCron('refreshPoolingStakingReward', this.refreshStakingRewardFastInterval(address), CRON_REFRESH_STAKING_REWARD_FAST_INTERVAL);
241
- this.addCron('updateNominatorMetadata', this.updateNominatorMetadata(address, this.state.getChainInfoMap(), this.state.getChainStateMap(), this.state.getSubstrateApiMap()), CRON_REFRESH_CHAIN_NOMINATOR_METADATA);
242
217
  await waitTimeout(1800);
243
218
  return true;
244
219
  }
@@ -6,7 +6,8 @@ import { TransactionError } from '@subwallet/extension-base/background/errors/Tr
6
6
  import { isJsonPayload, SEED_DEFAULT_LENGTH, SEED_LENGTHS } from '@subwallet/extension-base/background/handlers/Extension';
7
7
  import { withErrorLog } from '@subwallet/extension-base/background/handlers/helpers';
8
8
  import { createSubscription } from '@subwallet/extension-base/background/handlers/subscriptions';
9
- import { AccountExternalErrorCode, BasicTxErrorType, ChainType, ExternalRequestPromiseStatus, ExtrinsicType, StakingType, TransferTxErrorType } from '@subwallet/extension-base/background/KoniTypes';
9
+ import { AccountExternalErrorCode, BasicTxErrorType, BasicTxWarningCode, ChainType, ExternalRequestPromiseStatus, ExtrinsicType, StakingType, TransferTxErrorType } from '@subwallet/extension-base/background/KoniTypes';
10
+ import { TransactionWarning } from '@subwallet/extension-base/background/warnings/TransactionWarning';
10
11
  import { ALL_ACCOUNT_KEY, ALL_GENESIS_HASH } from '@subwallet/extension-base/constants';
11
12
  import { ALLOWED_PATH } from '@subwallet/extension-base/defaults';
12
13
  import { parseSubstrateTransaction } from '@subwallet/extension-base/koni/api/dotsama/parseTransaction';
@@ -1423,6 +1424,17 @@ export default class KoniExtension {
1423
1424
  }
1424
1425
  const transferNativeAmount = isTransferNativeToken ? transferAmount.value : '0';
1425
1426
  this.addContact(to);
1427
+ const additionalValidator = async inputTransaction => {
1428
+ if (!isTransferNativeToken) {
1429
+ const {
1430
+ value: balance
1431
+ } = await this.#koniState.balanceService.getTokenFreeBalance(from, networkKey, tokenSlug);
1432
+ const minAmount = tokenInfo.minAmount || '0';
1433
+ if (new BigN(balance).minus(transferAmount.value).lt(minAmount)) {
1434
+ inputTransaction.warnings.push(new TransactionWarning(BasicTxWarningCode.NOT_ENOUGH_EXISTENTIAL_DEPOSIT, ''));
1435
+ }
1436
+ }
1437
+ };
1426
1438
  return this.#koniState.transactionService.handleTransaction({
1427
1439
  errors,
1428
1440
  warnings,
@@ -1434,8 +1446,9 @@ export default class KoniExtension {
1434
1446
  data: inputData,
1435
1447
  extrinsicType: isTransferNativeToken ? ExtrinsicType.TRANSFER_BALANCE : ExtrinsicType.TRANSFER_TOKEN,
1436
1448
  ignoreWarnings: transferAll,
1437
- isTransferAll: transferAll,
1438
- edAsWarning: isTransferNativeToken
1449
+ isTransferAll: isTransferNativeToken ? transferAll : false,
1450
+ edAsWarning: true,
1451
+ additionalValidator: additionalValidator
1439
1452
  });
1440
1453
  }
1441
1454
  validateCrossChainTransfer(destinationNetworkKey, sendingTokenSlug, sender, sendingValue) {
@@ -1524,8 +1537,11 @@ export default class KoniExtension {
1524
1537
  disableChain(networkKey) {
1525
1538
  return this.#koniState.disableChain(networkKey);
1526
1539
  }
1527
- async enableChain(networkKey) {
1528
- return await this.#koniState.enableChain(networkKey);
1540
+ async enableChain({
1541
+ chainSlug,
1542
+ enableTokens
1543
+ }) {
1544
+ return await this.#koniState.enableChain(chainSlug, enableTokens);
1529
1545
  }
1530
1546
  async validateNetwork({
1531
1547
  existedChainSlug,
@@ -1715,9 +1731,15 @@ export default class KoniExtension {
1715
1731
  isSendingSelf
1716
1732
  };
1717
1733
  }
1718
- async enableChains(targetKeys) {
1734
+ async enableChains({
1735
+ chainSlugs,
1736
+ enableTokens
1737
+ }) {
1719
1738
  try {
1720
- await Promise.all(targetKeys.map(networkKey => this.enableChain(networkKey)));
1739
+ await Promise.all(chainSlugs.map(chainSlug => this.enableChain({
1740
+ chainSlug,
1741
+ enableTokens
1742
+ })));
1721
1743
  } catch (e) {
1722
1744
  return false;
1723
1745
  }
@@ -101,7 +101,7 @@ export default class KoniState {
101
101
  subscribeNominatorMetadata(): Subject<NominatorMetadata[]>;
102
102
  ensureUrlAuthorizedV2(url: string): Promise<boolean>;
103
103
  setStakingItem(networkKey: string, item: StakingItem): void;
104
- updateChainStakingMetadata(item: ChainStakingMetadata): void;
104
+ updateChainStakingMetadata(item: ChainStakingMetadata, changes?: Record<string, unknown>): void;
105
105
  updateStakingNominatorMetadata(item: NominatorMetadata): void;
106
106
  setNftCollection(network: string, data: NftCollection, callback?: (data: NftCollection) => void): void;
107
107
  getNftCollection(): import("dexie").PromiseExtended<NftCollection[]>;
@@ -113,7 +113,7 @@ export default class KoniState {
113
113
  getNft(): Promise<NftJson | undefined>;
114
114
  subscribeNft(): Subject<NftJson>;
115
115
  resetStakingReward(): void;
116
- updateStakingReward(stakingRewardData: StakingRewardItem[], type: 'slowInterval' | 'fastInterval', callback?: (stakingRewardData: StakingRewardJson) => void): void;
116
+ updateStakingReward(stakingRewardData: StakingRewardItem, callback?: (stakingRewardData: StakingRewardJson) => void): void;
117
117
  updateStakingRewardReady(ready: boolean): void;
118
118
  getAccountRefMap(callback: (refMap: Record<string, Array<string>>) => void): void;
119
119
  addAccountRef(addresses: string[], callback: () => void): void;
@@ -69,8 +69,7 @@ export default class KoniState {
69
69
  stakingRewardSubject = new Subject();
70
70
  stakingRewardState = {
71
71
  ready: false,
72
- slowInterval: [],
73
- fastInterval: []
72
+ data: {}
74
73
  };
75
74
  lazyMap = {};
76
75
  ready = false;
@@ -193,18 +192,24 @@ export default class KoniState {
193
192
  async init() {
194
193
  await this.chainService.init();
195
194
  await this.migrationService.run();
196
- this.startSubscription();
197
195
  this.eventService.emit('chain.ready', true);
198
196
  this.onReady();
199
197
  this.onAccountAdd();
200
198
  this.onAccountRemove();
199
+ await this.startSubscription();
201
200
  }
202
- startSubscription() {
201
+ async startSubscription() {
202
+ await this.eventService.waitKeyringReady;
203
203
  this.dbService.subscribeChainStakingMetadata([], data => {
204
204
  this.chainStakingMetadataSubject.next(data);
205
205
  });
206
- this.dbService.subscribeNominatorMetadata(data => {
207
- this.stakingNominatorMetadataSubject.next(data);
206
+ let unsub;
207
+ this.keyringService.accountSubject.subscribe(accounts => {
208
+ // TODO: improve this
209
+ unsub && unsub.unsubscribe();
210
+ unsub = this.dbService.subscribeNominatorMetadata(Object.keys(accounts), data => {
211
+ this.stakingNominatorMetadataSubject.next(data);
212
+ });
208
213
  });
209
214
  }
210
215
  onReady() {
@@ -296,14 +301,6 @@ export default class KoniState {
296
301
  async getPooledStakingRecordsByAddress(addresses) {
297
302
  return this.dbService.getPooledStakings(addresses, this.activeChainSlugs);
298
303
  }
299
-
300
- // TODO: delete later
301
- // public async getStoredStaking (address: string) {
302
- // const items = await this.dbService.stores.staking.getDataByAddressAsObject(address);
303
- //
304
- // return items || {};
305
- // }
306
-
307
304
  subscribeStaking() {
308
305
  return this.stakingSubject;
309
306
  }
@@ -319,8 +316,8 @@ export default class KoniState {
319
316
  setStakingItem(networkKey, item) {
320
317
  this.dbService.updateStaking(networkKey, item.address, item).catch(e => this.logger.warn(e));
321
318
  }
322
- updateChainStakingMetadata(item) {
323
- this.dbService.updateChainStakingMetadata(item).catch(e => this.logger.warn(e));
319
+ updateChainStakingMetadata(item, changes) {
320
+ this.dbService.updateChainStakingMetadata(item, changes).catch(e => this.logger.warn(e));
324
321
  }
325
322
  updateStakingNominatorMetadata(item) {
326
323
  this.dbService.updateNominatorMetadata(item).catch(e => this.logger.warn(e));
@@ -373,12 +370,13 @@ export default class KoniState {
373
370
  return this.nftSubject;
374
371
  }
375
372
  resetStakingReward() {
376
- this.stakingRewardState.slowInterval = [];
373
+ this.stakingRewardState.data = {};
377
374
  this.stakingRewardSubject.next(this.stakingRewardState);
378
375
  }
379
- updateStakingReward(stakingRewardData, type, callback) {
376
+ updateStakingReward(stakingRewardData, callback) {
380
377
  this.stakingRewardState.ready = true;
381
- this.stakingRewardState[type] = stakingRewardData;
378
+ const key = `${stakingRewardData.chain}___${stakingRewardData.address}___${stakingRewardData.type}`;
379
+ this.stakingRewardState.data[key] = stakingRewardData;
382
380
  if (callback) {
383
381
  callback(this.stakingRewardState);
384
382
  }
@@ -3,7 +3,7 @@ import { ChainStakingMetadata } from '@subwallet/extension-base/background/KoniT
3
3
  import { _ChainState, _EvmApi, _SubstrateApi } from '@subwallet/extension-base/services/chain-service/types';
4
4
  import DatabaseService from '@subwallet/extension-base/services/storage-service/DatabaseService';
5
5
  import KoniState from './handlers/State';
6
- declare type SubscriptionName = 'balance' | 'crowdloan' | 'stakingOnChain';
6
+ declare type SubscriptionName = 'balance' | 'crowdloan' | 'stakingOnChain' | 'essentialChainStakingMetadata';
7
7
  export declare class KoniSubscription {
8
8
  private eventHandler?;
9
9
  private subscriptionMap;
@@ -20,6 +20,7 @@ export declare class KoniSubscription {
20
20
  subscribeBalancesAndCrowdloans(address: string, chainInfoMap: Record<string, _ChainInfo>, chainStateMap: Record<string, _ChainState>, substrateApiMap: Record<string, _SubstrateApi>, web3ApiMap: Record<string, _EvmApi>, onlyRunOnFirstTime?: boolean): void;
21
21
  subscribeStakingOnChain(address: string, substrateApiMap: Record<string, _SubstrateApi>, onlyRunOnFirstTime?: boolean): void;
22
22
  initStakingOnChainSubscription(addresses: string[], substrateApiMap: Record<string, _SubstrateApi>, onlyRunOnFirstTime?: boolean): (() => void) | undefined;
23
+ initEssentialChainStakingMetadataSubscription(substrateApiMap: Record<string, _SubstrateApi>, onlyRunOnFirstTime?: boolean): (() => void) | undefined;
23
24
  initBalanceSubscription(addresses: string[], chainInfoMap: Record<string, _ChainInfo>, chainStateMap: Record<string, _ChainState>, substrateApiMap: Record<string, _SubstrateApi>, evmApiMap: Record<string, _EvmApi>, onlyRunOnFirstTime?: boolean): (() => void) | undefined;
24
25
  initCrowdloanSubscription(addresses: string[], substrateApiMap: Record<string, _SubstrateApi>, onlyRunOnFirstTime?: boolean): (() => void) | undefined;
25
26
  subscribeNft(address: string, substrateApiMap: Record<string, _SubstrateApi>, evmApiMap: Record<string, _EvmApi>, smartContractNfts: _ChainAsset[], chainInfoMap: Record<string, _ChainInfo>): void;
@@ -5,7 +5,7 @@ import { ALL_ACCOUNT_KEY } from '@subwallet/extension-base/constants';
5
5
  import { subscribeBalance } from '@subwallet/extension-base/koni/api/dotsama/balance';
6
6
  import { subscribeCrowdloan } from '@subwallet/extension-base/koni/api/dotsama/crowdloan';
7
7
  import { getNominationStakingRewardData, getPoolingStakingRewardData, stakingOnChainApi } from '@subwallet/extension-base/koni/api/staking';
8
- import { getChainStakingMetadata, getNominatorMetadata } from '@subwallet/extension-base/koni/api/staking/bonding';
8
+ import { getChainStakingMetadata, getNominatorMetadata, subscribeEssentialChainStakingMetadata } from '@subwallet/extension-base/koni/api/staking/bonding';
9
9
  import { getRelayChainPoolMemberMetadata } from '@subwallet/extension-base/koni/api/staking/bonding/relayChain';
10
10
  import { getAmplitudeUnclaimedStakingReward } from '@subwallet/extension-base/koni/api/staking/paraChain';
11
11
  import { nftHandler } from '@subwallet/extension-base/koni/background/handlers';
@@ -19,7 +19,8 @@ export class KoniSubscription {
19
19
  subscriptionMap = {
20
20
  crowdloan: undefined,
21
21
  balance: undefined,
22
- stakingOnChain: undefined
22
+ stakingOnChain: undefined,
23
+ essentialChainStakingMetadata: undefined
23
24
  };
24
25
  constructor(state, dbService) {
25
26
  this.dbService = dbService;
@@ -98,11 +99,38 @@ export class KoniSubscription {
98
99
  return;
99
100
  }
100
101
  this.updateSubscription('stakingOnChain', this.initStakingOnChainSubscription(addresses, substrateApiMap, onlyRunOnFirstTime));
102
+ this.updateSubscription('essentialChainStakingMetadata', this.initEssentialChainStakingMetadataSubscription(substrateApiMap, onlyRunOnFirstTime)); // TODO: might not need to re-subscribe on changing account
101
103
  }
104
+
102
105
  initStakingOnChainSubscription(addresses, substrateApiMap, onlyRunOnFirstTime) {
103
- const unsub = stakingOnChainApi(addresses, substrateApiMap, (networkKey, rs) => {
106
+ const stakingCallback = (networkKey, rs) => {
104
107
  this.state.setStakingItem(networkKey, rs);
105
- }, this.state.getActiveChainInfoMap());
108
+ };
109
+ const nominatorStateCallback = nominatorMetadata => {
110
+ this.state.updateStakingNominatorMetadata(nominatorMetadata);
111
+ };
112
+ const unsub = stakingOnChainApi(addresses, substrateApiMap, this.state.getActiveChainInfoMap(), stakingCallback, nominatorStateCallback);
113
+ if (onlyRunOnFirstTime) {
114
+ unsub && unsub();
115
+ return;
116
+ }
117
+ return () => {
118
+ unsub && unsub();
119
+ };
120
+ }
121
+ initEssentialChainStakingMetadataSubscription(substrateApiMap, onlyRunOnFirstTime) {
122
+ const unsub = subscribeEssentialChainStakingMetadata(substrateApiMap, this.state.getActiveChainInfoMap(), (networkKey, rs) => {
123
+ this.state.updateChainStakingMetadata(rs, {
124
+ era: rs.era,
125
+ minStake: rs.minStake,
126
+ maxValidatorPerNominator: rs.maxValidatorPerNominator,
127
+ // temporary fix for Astar, there's no limit for now
128
+ maxWithdrawalRequestPerValidator: rs.maxWithdrawalRequestPerValidator,
129
+ // by default
130
+ allowCancelUnstaking: rs.allowCancelUnstaking,
131
+ unstakingPeriod: rs.unstakingPeriod
132
+ });
133
+ });
106
134
  if (onlyRunOnFirstTime) {
107
135
  unsub && unsub();
108
136
  return;
@@ -168,8 +196,9 @@ export class KoniSubscription {
168
196
  targetNetworkMap[key] = network;
169
197
  }
170
198
  });
171
- const result = await getNominationStakingRewardData(addresses, targetNetworkMap);
172
- this.state.updateStakingReward(result, 'slowInterval');
199
+ await getNominationStakingRewardData(addresses, targetNetworkMap, rewardItem => {
200
+ this.state.updateStakingReward(rewardItem);
201
+ });
173
202
  }
174
203
  async subscribeStakingRewardFastInterval(address) {
175
204
  const addresses = this.state.getDecodedAddresses(address);
@@ -195,9 +224,10 @@ export class KoniSubscription {
195
224
  Object.keys(targetChainMap).forEach(key => {
196
225
  activeNetworks.push(key);
197
226
  });
198
- const [poolingStakingRewards, amplitudeUnclaimedStakingRewards] = await Promise.all([getPoolingStakingRewardData(pooledAddresses, targetChainMap, this.state.getSubstrateApiMap()), getAmplitudeUnclaimedStakingReward(this.state.getSubstrateApiMap(), addresses, chainInfoMap, activeNetworks)]);
199
- const result = [...poolingStakingRewards, ...amplitudeUnclaimedStakingRewards];
200
- this.state.updateStakingReward(result, 'fastInterval');
227
+ const updateState = result => {
228
+ this.state.updateStakingReward(result);
229
+ };
230
+ await Promise.all([getPoolingStakingRewardData(pooledAddresses, targetChainMap, this.state.getSubstrateApiMap(), updateState), getAmplitudeUnclaimedStakingReward(this.state.getSubstrateApiMap(), addresses, chainInfoMap, activeNetworks, updateState)]);
201
231
  }
202
232
  async fetchingStakingFromApi() {
203
233
  try {
@@ -221,16 +251,31 @@ export class KoniSubscription {
221
251
  if (Object.values(filteredChainInfoMap).length === 0) {
222
252
  return;
223
253
  }
254
+ const timeout = new Promise(resolve => {
255
+ const id = setTimeout(() => {
256
+ clearTimeout(id);
257
+ resolve(null);
258
+ }, 3000);
259
+ });
224
260
 
225
261
  // Fetch data from helper API
226
- const dataFromApi = await this.fetchingStakingFromApi();
262
+ const _dataFromApi = await Promise.race([this.fetchingStakingFromApi(), timeout]);
263
+ const dataFromApi = _dataFromApi;
227
264
  await Promise.all(Object.values(filteredChainInfoMap).map(async chainInfo => {
228
265
  // Use fetch API data if available
229
- if (dataFromApi[chainInfo.slug]) {
230
- this.state.updateChainStakingMetadata(dataFromApi[chainInfo.slug]);
266
+ if (dataFromApi && dataFromApi[chainInfo.slug]) {
267
+ this.state.updateChainStakingMetadata(dataFromApi[chainInfo.slug], {
268
+ expectedReturn: dataFromApi[chainInfo.slug].expectedReturn,
269
+ inflation: dataFromApi[chainInfo.slug].inflation,
270
+ nominatorCount: dataFromApi[chainInfo.slug].nominatorCount
271
+ });
231
272
  } else {
232
273
  const chainStakingMetadata = await getChainStakingMetadata(chainInfo, substrateApiMap[chainInfo.slug]);
233
- this.state.updateChainStakingMetadata(chainStakingMetadata);
274
+ this.state.updateChainStakingMetadata(chainStakingMetadata, {
275
+ expectedReturn: chainStakingMetadata.expectedReturn,
276
+ inflation: chainStakingMetadata.inflation,
277
+ nominatorCount: chainStakingMetadata.nominatorCount
278
+ });
234
279
  }
235
280
  }));
236
281
  }
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "./cjs/detectPackage.js"
18
18
  ],
19
19
  "type": "module",
20
- "version": "1.0.7-0",
20
+ "version": "1.0.7-2",
21
21
  "main": "./cjs/index.js",
22
22
  "module": "./index.js",
23
23
  "types": "./index.d.ts",
@@ -1698,10 +1698,10 @@
1698
1698
  "@subsocial/types": "^0.6.8",
1699
1699
  "@substrate/connect": "^0.7.26",
1700
1700
  "@subwallet/chain-list": "^0.1.9",
1701
- "@subwallet/extension-base": "^1.0.7-0",
1702
- "@subwallet/extension-chains": "^1.0.7-0",
1703
- "@subwallet/extension-dapp": "^1.0.7-0",
1704
- "@subwallet/extension-inject": "^1.0.7-0",
1701
+ "@subwallet/extension-base": "^1.0.7-2",
1702
+ "@subwallet/extension-chains": "^1.0.7-2",
1703
+ "@subwallet/extension-dapp": "^1.0.7-2",
1704
+ "@subwallet/extension-inject": "^1.0.7-2",
1705
1705
  "@subwallet/keyring": "^0.0.9",
1706
1706
  "@subwallet/ui-keyring": "^0.0.9",
1707
1707
  "@unique-nft/types": "^0.6.0-4",
package/packageInfo.js CHANGED
@@ -7,5 +7,5 @@ export const packageInfo = {
7
7
  name: '@subwallet/extension-base',
8
8
  path: (import.meta && import.meta.url) ? new URL(import.meta.url).pathname.substring(0, new URL(import.meta.url).pathname.lastIndexOf('/') + 1) : 'auto',
9
9
  type: 'esm',
10
- version: '1.0.7-0'
10
+ version: '1.0.7-2'
11
11
  };
@@ -36,6 +36,7 @@ export declare const _STAKING_CHAIN_GROUP: {
36
36
  nominationPool: string[];
37
37
  bifrost: string[];
38
38
  aleph: string[];
39
+ ternoa: string[];
39
40
  };
40
41
  export declare const _STAKING_ERA_LENGTH_MAP: Record<string, number>;
41
42
  export declare const _PARACHAIN_INFLATION_DISTRIBUTION: Record<string, Record<string, number>>;
@@ -55,9 +55,10 @@ export const _STAKING_CHAIN_GROUP = {
55
55
  kilt: ['kilt', 'kilt_peregrine'],
56
56
  nominationPool: ['polkadot', 'kusama', 'westend', 'alephTest', 'aleph'],
57
57
  bifrost: ['bifrost', 'bifrost_testnet'],
58
- aleph: ['aleph', 'alephTest'] // A0 has distinct tokenomics
58
+ aleph: ['aleph', 'alephTest'],
59
+ // A0 has distinct tokenomics
60
+ ternoa: ['ternoa', 'ternoa_alphanet']
59
61
  };
60
-
61
62
  export const _STAKING_ERA_LENGTH_MAP = {
62
63
  // in hours
63
64
  alephTest: 24,
@@ -93,6 +93,14 @@ export declare type _NetworkUpsertParams = {
93
93
  providerError?: _CHAIN_VALIDATION_ERROR;
94
94
  };
95
95
  export declare const _CUSTOM_PREFIX = "custom-";
96
+ export interface EnableChainParams {
97
+ chainSlug: string;
98
+ enableTokens?: boolean;
99
+ }
100
+ export interface EnableMultiChainParams {
101
+ chainSlugs: string[];
102
+ enableTokens?: boolean;
103
+ }
96
104
  export interface _ValidateCustomAssetRequest {
97
105
  contractAddress: string;
98
106
  originChain: string;
@@ -53,7 +53,12 @@ export function _isPureSubstrateChain(chainInfo) {
53
53
  }
54
54
  export function _getOriginChainOfAsset(assetSlug) {
55
55
  if (assetSlug.startsWith(_CUSTOM_PREFIX)) {
56
- return assetSlug.split('-')[1];
56
+ const arr = assetSlug.split('-').slice(1);
57
+ if (arr[0] === 'custom') {
58
+ const end = arr.findIndex(str => Object.values(_AssetType).includes(str));
59
+ return arr.slice(0, end).join('-');
60
+ }
61
+ return arr[0];
57
62
  }
58
63
  return assetSlug.split('-')[0];
59
64
  }
@@ -38,7 +38,7 @@ export default class DatabaseService {
38
38
  getPooledStakings(addresses: string[], chainHashes?: string[]): Promise<StakingItem[]>;
39
39
  subscribeStaking(addresses: string[], chainList?: string[], callback?: (stakingItems: StakingItem[]) => void): Subscription;
40
40
  subscribeChainStakingMetadata(chains: string[], callback: (data: ChainStakingMetadata[]) => void): void;
41
- subscribeNominatorMetadata(callback: (data: NominatorMetadata[]) => void): void;
41
+ subscribeNominatorMetadata(addresses: string[], callback: (data: NominatorMetadata[]) => void): Subscription;
42
42
  getHistories(query?: HistoryQuery): Promise<import("@subwallet/extension-base/services/storage-service/databases").ITransactionHistoryItem[]>;
43
43
  upsertHistory(histories: TransactionHistoryItem[]): Promise<unknown>;
44
44
  updateHistoryByExtrinsicHash(extrinsicHash: string, updateData: Partial<TransactionHistoryItem>): Promise<unknown>;
@@ -58,7 +58,7 @@ export default class DatabaseService {
58
58
  updateAssetStore(item: _ChainAsset): Promise<unknown>;
59
59
  getAllAssetStore(): Promise<_ChainAsset[]>;
60
60
  removeFromAssetStore(items: string[]): Promise<number>;
61
- updateChainStakingMetadata(item: ChainStakingMetadata): Promise<unknown>;
61
+ updateChainStakingMetadata(item: ChainStakingMetadata, changes?: Record<string, unknown>): Promise<unknown>;
62
62
  getChainStakingMetadata(): Promise<ChainStakingMetadata[]>;
63
63
  getStakingMetadataByChain(chain: string, type?: StakingType): Promise<ChainStakingMetadata | undefined>;
64
64
  updateNominatorMetadata(item: NominatorMetadata): Promise<unknown>;
@@ -99,8 +99,8 @@ export default class DatabaseService {
99
99
  next: data => callback && callback(data)
100
100
  });
101
101
  }
102
- subscribeNominatorMetadata(callback) {
103
- this.stores.nominatorMetadata.subscribeAll().subscribe({
102
+ subscribeNominatorMetadata(addresses, callback) {
103
+ return this.stores.nominatorMetadata.subscribeByAddresses(addresses).subscribe({
104
104
  next: data => callback && callback(data)
105
105
  });
106
106
  }
@@ -191,7 +191,11 @@ export default class DatabaseService {
191
191
  }
192
192
 
193
193
  // Staking
194
- async updateChainStakingMetadata(item) {
194
+ async updateChainStakingMetadata(item, changes) {
195
+ const existingRecord = await this.stores.chainStakingMetadata.getByChainAndType(item.chain, item.type);
196
+ if (existingRecord && changes) {
197
+ return this.stores.chainStakingMetadata.updateByChainAndType(item.chain, item.type, changes);
198
+ }
195
199
  return this.stores.chainStakingMetadata.upsert(item);
196
200
  }
197
201
  async getChainStakingMetadata() {
@@ -6,4 +6,5 @@ export default class ChainStakingMetadataStore extends BaseStoreWithChain<ChainS
6
6
  getByChains(chains: string[]): Promise<ChainStakingMetadata[]>;
7
7
  getByChainAndType(chain: string, type?: StakingType): import("dexie").PromiseExtended<ChainStakingMetadata | undefined>;
8
8
  removeByChains(chains: string[]): Promise<number>;
9
+ updateByChainAndType(chain: string, type: StakingType | undefined, changes: Record<string, unknown>): import("dexie").PromiseExtended<number>;
9
10
  }
@@ -26,4 +26,7 @@ export default class ChainStakingMetadataStore extends BaseStoreWithChain {
26
26
  async removeByChains(chains) {
27
27
  return this.table.where('chain').anyOfIgnoreCase(chains).delete();
28
28
  }
29
+ updateByChainAndType(chain, type = StakingType.NOMINATED, changes) {
30
+ return this.table.update([chain, type], changes);
31
+ }
29
32
  }
@@ -2,8 +2,8 @@ import { NominatorMetadata } from '@subwallet/extension-base/background/KoniType
2
2
  import BaseStoreWithAddressAndChain from '@subwallet/extension-base/services/storage-service/db-stores/BaseStoreWithAddressAndChain';
3
3
  export default class NominatorMetadataStore extends BaseStoreWithAddressAndChain<NominatorMetadata> {
4
4
  getAll(): Promise<NominatorMetadata[]>;
5
- subscribeByAddress(address: string): import("dexie").Observable<NominatorMetadata[]>;
5
+ subscribeByAddresses(addresses: string[]): import("dexie").Observable<NominatorMetadata[]>;
6
6
  subscribeAll(): import("dexie").Observable<NominatorMetadata[]>;
7
- getByAddress(address: string): import("dexie").PromiseExtended<NominatorMetadata[]>;
7
+ getByAddress(addresses: string[]): import("dexie").PromiseExtended<NominatorMetadata[]>;
8
8
  removeByAddress(address: string): Promise<number>;
9
9
  }
@@ -7,14 +7,14 @@ export default class NominatorMetadataStore extends BaseStoreWithAddressAndChain
7
7
  async getAll() {
8
8
  return this.table.toArray();
9
9
  }
10
- subscribeByAddress(address) {
11
- return liveQuery(() => this.getByAddress(address));
10
+ subscribeByAddresses(addresses) {
11
+ return liveQuery(() => this.getByAddress(addresses));
12
12
  }
13
13
  subscribeAll() {
14
14
  return liveQuery(() => this.getAll());
15
15
  }
16
- getByAddress(address) {
17
- return this.table.where('address').anyOfIgnoreCase(address).toArray();
16
+ getByAddress(addresses) {
17
+ return this.table.where('address').anyOfIgnoreCase(addresses).toArray();
18
18
  }
19
19
  async removeByAddress(address) {
20
20
  return this.table.where('address').anyOfIgnoreCase(address).delete();
@@ -631,7 +631,12 @@ export default class TransactionService {
631
631
  if (!payload.parseData) {
632
632
  const isToContract = await isContractAddress(payload.to || '', evmApi);
633
633
  payload.isToContract = isToContract;
634
- payload.parseData = isToContract ? payload.data ? (await parseContractInput(payload.data || '', payload.to || '', chainInfo)).result : '' : payload.data || '';
634
+ try {
635
+ payload.parseData = isToContract ? payload.data ? (await parseContractInput(payload.data || '', payload.to || '', chainInfo)).result : '' : payload.data || '';
636
+ } catch (e) {
637
+ console.warn('Unable to parse contract input data');
638
+ payload.parseData = payload.data;
639
+ }
635
640
  }
636
641
  if ('data' in payload && payload.data === undefined) {
637
642
  delete payload.data;