@subwallet/extension-base 1.0.2-3 → 1.0.3-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 (43) hide show
  1. package/background/KoniTypes.d.ts +10 -2
  2. package/background/KoniTypes.js +3 -1
  3. package/cjs/background/KoniTypes.js +3 -1
  4. package/cjs/koni/background/handlers/Extension.js +9 -0
  5. package/cjs/koni/background/handlers/State.js +8 -1
  6. package/cjs/koni/background/subscription.js +24 -2
  7. package/cjs/packageInfo.js +1 -1
  8. package/cjs/services/base/types.js +20 -0
  9. package/cjs/services/chain-service/index.js +14 -4
  10. package/cjs/services/history-service/index.js +84 -39
  11. package/cjs/services/migration-service/scripts/MigrateImportedToken.js +2 -1
  12. package/cjs/services/price-service/index.js +71 -23
  13. package/cjs/services/storage-service/DatabaseService.js +10 -0
  14. package/cjs/services/storage-service/db-stores/Transaction.js +6 -10
  15. package/cjs/services/transaction-service/index.js +78 -33
  16. package/cjs/services/transaction-service/utils.js +10 -8
  17. package/cjs/utils/promise.js +26 -0
  18. package/koni/background/handlers/Extension.d.ts +1 -0
  19. package/koni/background/handlers/Extension.js +9 -0
  20. package/koni/background/handlers/State.js +8 -1
  21. package/koni/background/subscription.d.ts +2 -0
  22. package/koni/background/subscription.js +23 -2
  23. package/package.json +17 -7
  24. package/packageInfo.js +1 -1
  25. package/services/base/types.d.ts +34 -0
  26. package/services/base/types.js +15 -0
  27. package/services/chain-service/index.d.ts +2 -0
  28. package/services/chain-service/index.js +15 -5
  29. package/services/history-service/index.d.ts +24 -5
  30. package/services/history-service/index.js +84 -39
  31. package/services/migration-service/scripts/MigrateImportedToken.js +2 -1
  32. package/services/price-service/index.d.ts +22 -1
  33. package/services/price-service/index.js +71 -23
  34. package/services/storage-service/DatabaseService.d.ts +1 -0
  35. package/services/storage-service/DatabaseService.js +10 -0
  36. package/services/storage-service/db-stores/Transaction.d.ts +2 -0
  37. package/services/storage-service/db-stores/Transaction.js +6 -10
  38. package/services/transaction-service/index.d.ts +2 -0
  39. package/services/transaction-service/index.js +60 -17
  40. package/services/transaction-service/types.d.ts +2 -0
  41. package/services/transaction-service/utils.js +10 -8
  42. package/utils/promise.d.ts +6 -0
  43. package/utils/promise.js +20 -0
@@ -1,10 +1,11 @@
1
1
  import { TransactionHistoryItem } from '@subwallet/extension-base/background/KoniTypes';
2
+ import { CronServiceInterface, PersistDataServiceInterface, ServiceStatus, StoppableServiceInterface } from '@subwallet/extension-base/services/base/types';
2
3
  import { ChainService } from '@subwallet/extension-base/services/chain-service';
3
4
  import { EventService } from '@subwallet/extension-base/services/event-service';
4
5
  import { KeyringService } from '@subwallet/extension-base/services/keyring-service';
5
6
  import DatabaseService from '@subwallet/extension-base/services/storage-service/DatabaseService';
6
7
  import { BehaviorSubject } from 'rxjs';
7
- export declare class HistoryService {
8
+ export declare class HistoryService implements StoppableServiceInterface, PersistDataServiceInterface, CronServiceInterface {
8
9
  private dbService;
9
10
  private chainService;
10
11
  private eventService;
@@ -12,15 +13,33 @@ export declare class HistoryService {
12
13
  private historySubject;
13
14
  constructor(dbService: DatabaseService, chainService: ChainService, eventService: EventService, keyringService: KeyringService);
14
15
  private fetchPromise;
15
- private nextFetch;
16
+ private interval;
16
17
  private fetchAndLoadHistories;
17
- fetchHistories(addresses: string[]): Promise<TransactionHistoryItem<import("@subwallet/extension-base/background/KoniTypes").ExtrinsicType.TRANSFER_BALANCE>[]>;
18
- invalidCache(): void;
19
- refreshHistoryInterval(): void;
20
18
  getHistories(): Promise<TransactionHistoryItem<import("@subwallet/extension-base/background/KoniTypes").ExtrinsicType.TRANSFER_BALANCE>[]>;
21
19
  getHistorySubject(): Promise<BehaviorSubject<TransactionHistoryItem<import("@subwallet/extension-base/background/KoniTypes").ExtrinsicType.TRANSFER_BALANCE>[]>>;
22
20
  updateHistories(chain: string, extrinsicHash: string, updateData: Partial<TransactionHistoryItem>): Promise<void>;
21
+ updateHistoryByExtrinsicHash(extrinsicHash: string, updateData: Partial<TransactionHistoryItem>): Promise<void>;
23
22
  insertHistories(historyItems: TransactionHistoryItem[]): Promise<void>;
24
23
  addHistoryItems(historyItems: TransactionHistoryItem[]): Promise<void>;
25
24
  removeHistoryByAddress(address: string): Promise<void>;
25
+ status: ServiceStatus;
26
+ loadData(): Promise<void>;
27
+ persistData(): Promise<void>;
28
+ startCron(): Promise<void>;
29
+ stopCron(): Promise<void>;
30
+ startPromiseHandler: {
31
+ resolve: (value: void) => void;
32
+ reject: (reason?: unknown) => void;
33
+ promise: Promise<void>;
34
+ };
35
+ init(): Promise<void>;
36
+ start(): Promise<void>;
37
+ waitForStarted(): Promise<void>;
38
+ stopPromiseHandler: {
39
+ resolve: (value: void) => void;
40
+ reject: (reason?: unknown) => void;
41
+ promise: Promise<void>;
42
+ };
43
+ stop(): Promise<void>;
44
+ waitForStopped(): Promise<void>;
26
45
  }
@@ -2,6 +2,8 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import { CRON_REFRESH_HISTORY_INTERVAL } from '@subwallet/extension-base/constants';
5
+ import { ServiceStatus } from '@subwallet/extension-base/services/base/types';
6
+ import { createPromiseHandler } from '@subwallet/extension-base/utils/promise';
5
7
  import { keyring } from '@subwallet/ui-keyring';
6
8
  import { BehaviorSubject } from 'rxjs';
7
9
  import { fetchMultiChainHistories } from "./subsquid-multi-chain-history.js";
@@ -12,24 +14,10 @@ export class HistoryService {
12
14
  this.chainService = chainService;
13
15
  this.eventService = eventService;
14
16
  this.keyringService = keyringService;
15
- // Load history from database
16
- this.dbService.getHistories().then(histories => {
17
- this.historySubject.next(histories);
18
- }).catch(console.error);
19
-
20
- // Wait for keyring and chain ready and start
21
- Promise.all([this.eventService.waitKeyringReady, this.eventService.waitChainReady]).then(() => {
22
- this.getHistories().catch(console.log);
23
- this.eventService.on('account.add', () => {
24
- this.refreshHistoryInterval();
25
- });
26
- this.eventService.on('account.remove', address => {
27
- this.removeHistoryByAddress(address).catch(console.error);
28
- });
29
- }).catch(console.error);
17
+ this.init().catch(console.error);
30
18
  }
31
19
  fetchPromise = null;
32
- nextFetch = undefined;
20
+ interval = undefined;
33
21
  async fetchAndLoadHistories(addresses) {
34
22
  if (!addresses || addresses.length === 0) {
35
23
  return [];
@@ -52,32 +40,16 @@ export class HistoryService {
52
40
  await this.addHistoryItems(historyRecords);
53
41
  return historyRecords;
54
42
  }
55
- async fetchHistories(addresses) {
56
- if (!this.fetchPromise) {
57
- // Fetch another histories data data indexer and merge it with stored in database
58
- this.fetchPromise = this.fetchAndLoadHistories(addresses);
59
- }
60
- return this.fetchPromise;
61
- }
62
- invalidCache() {
63
- this.fetchPromise = null;
64
- }
65
- refreshHistoryInterval() {
66
- clearTimeout(this.nextFetch);
67
- this.invalidCache();
68
- this.getHistories().catch(console.error);
69
- this.nextFetch = setTimeout(() => {
70
- this.refreshHistoryInterval();
71
- }, CRON_REFRESH_HISTORY_INTERVAL);
72
- }
73
43
  async getHistories() {
74
44
  const addressList = keyring.getAccounts().map(a => a.address);
75
- const currentHistories = this.historySubject.value;
76
- if (!this.fetchPromise || currentHistories.length === 0) {
77
- await this.fetchHistories(addressList);
78
- this.historySubject.next(await this.dbService.getHistories());
45
+ if (!this.fetchPromise) {
46
+ this.fetchPromise = (async () => {
47
+ await this.fetchAndLoadHistories(addressList);
48
+ const histories = await this.dbService.getHistories();
49
+ this.historySubject.next(histories);
50
+ })();
79
51
  }
80
- return this.historySubject.getValue();
52
+ return Promise.resolve(this.historySubject.getValue());
81
53
  }
82
54
  async getHistorySubject() {
83
55
  await this.getHistories();
@@ -96,6 +68,10 @@ export class HistoryService {
96
68
  });
97
69
  await this.addHistoryItems(updatedRecords);
98
70
  }
71
+ async updateHistoryByExtrinsicHash(extrinsicHash, updateData) {
72
+ await this.dbService.updateHistoryByNewExtrinsicHash(extrinsicHash, updateData);
73
+ this.historySubject.next(await this.dbService.getHistories());
74
+ }
99
75
 
100
76
  // Insert history without check override origin 'app'
101
77
  async insertHistories(historyItems) {
@@ -124,4 +100,73 @@ export class HistoryService {
124
100
  await this.dbService.stores.transaction.removeAllByAddress(address);
125
101
  this.historySubject.next(await this.dbService.getHistories());
126
102
  }
103
+ status = ServiceStatus.NOT_INITIALIZED;
104
+ async loadData() {
105
+ const histories = await this.dbService.getHistories();
106
+ this.historySubject.next(histories);
107
+ }
108
+ async persistData() {
109
+ await this.dbService.upsertHistory(this.historySubject.value);
110
+ }
111
+ async startCron() {
112
+ await this.getHistories();
113
+ this.interval = setInterval(() => {
114
+ this.getHistories().catch(console.error);
115
+ }, CRON_REFRESH_HISTORY_INTERVAL);
116
+ }
117
+ stopCron() {
118
+ clearTimeout(this.interval);
119
+ this.fetchPromise = null;
120
+ return Promise.resolve();
121
+ }
122
+ startPromiseHandler = createPromiseHandler();
123
+ async init() {
124
+ this.status = ServiceStatus.INITIALIZING;
125
+ await this.loadData();
126
+ Promise.all([this.eventService.waitKeyringReady, this.eventService.waitChainReady]).then(() => {
127
+ this.getHistories().catch(console.log);
128
+ this.eventService.on('account.add', () => {
129
+ (async () => {
130
+ await this.stopCron();
131
+ await this.startCron();
132
+ })().catch(console.error);
133
+ });
134
+ this.eventService.on('account.remove', address => {
135
+ this.removeHistoryByAddress(address).catch(console.error);
136
+ });
137
+ }).catch(console.error);
138
+ this.status = ServiceStatus.INITIALIZED;
139
+ }
140
+ async start() {
141
+ try {
142
+ console.debug('Start history service');
143
+ this.startPromiseHandler = createPromiseHandler();
144
+ this.status = ServiceStatus.STARTING;
145
+ await this.startCron();
146
+ this.status = ServiceStatus.STARTED;
147
+ this.startPromiseHandler.resolve();
148
+ } catch (e) {
149
+ this.startPromiseHandler.reject(e);
150
+ }
151
+ }
152
+ waitForStarted() {
153
+ return this.startPromiseHandler.promise;
154
+ }
155
+ stopPromiseHandler = createPromiseHandler();
156
+ async stop() {
157
+ console.debug('Stop history service');
158
+ try {
159
+ this.stopPromiseHandler = createPromiseHandler();
160
+ this.status = ServiceStatus.STOPPING;
161
+ await this.persistData();
162
+ await this.stopCron();
163
+ this.stopPromiseHandler.resolve();
164
+ this.status = ServiceStatus.STOPPED;
165
+ } catch (e) {
166
+ this.stopPromiseHandler.reject(e);
167
+ }
168
+ }
169
+ waitForStopped() {
170
+ return this.stopPromiseHandler.promise;
171
+ }
127
172
  }
@@ -25,7 +25,8 @@ export default class MigrateImportedToken extends BaseMigrationJob {
25
25
  originChain: item.chain,
26
26
  priceId: null,
27
27
  slug: '',
28
- symbol: item.symbol || ''
28
+ symbol: item.symbol || '',
29
+ icon: ''
29
30
  });
30
31
  } catch (e) {
31
32
  console.log(e);
@@ -1,9 +1,11 @@
1
1
  import { PriceJson } from '@subwallet/extension-base/background/KoniTypes';
2
+ import { CronServiceInterface, PersistDataServiceInterface, ServiceStatus, StoppableServiceInterface } from '@subwallet/extension-base/services/base/types';
2
3
  import { ChainService } from '@subwallet/extension-base/services/chain-service';
3
4
  import { EventService } from '@subwallet/extension-base/services/event-service';
4
5
  import DatabaseService from '@subwallet/extension-base/services/storage-service/DatabaseService';
5
6
  import { BehaviorSubject } from 'rxjs';
6
- export declare class PriceService {
7
+ export declare class PriceService implements StoppableServiceInterface, PersistDataServiceInterface, CronServiceInterface {
8
+ status: ServiceStatus;
7
9
  private dbService;
8
10
  private eventService;
9
11
  private chainService;
@@ -15,4 +17,23 @@ export declare class PriceService {
15
17
  getPriceSubject(): BehaviorSubject<PriceJson>;
16
18
  getPriceIds(): Set<string>;
17
19
  refreshPriceData(priceIds?: Set<string>): void;
20
+ init(): Promise<void>;
21
+ loadData(): Promise<void>;
22
+ persistData(): Promise<void>;
23
+ startPromiseHandler: {
24
+ resolve: (value: void) => void;
25
+ reject: (reason?: unknown) => void;
26
+ promise: Promise<void>;
27
+ };
28
+ start(): Promise<void>;
29
+ startCron(): Promise<void>;
30
+ stopPromiseHandler: {
31
+ resolve: (value: void) => void;
32
+ reject: (reason?: unknown) => void;
33
+ promise: Promise<void>;
34
+ };
35
+ stop(): Promise<void>;
36
+ stopCron(): Promise<void>;
37
+ waitForStarted(): Promise<void>;
38
+ waitForStopped(): Promise<void>;
18
39
  }
@@ -2,7 +2,9 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import { CRON_REFRESH_PRICE_INTERVAL } from '@subwallet/extension-base/constants';
5
+ import { ServiceStatus } from '@subwallet/extension-base/services/base/types';
5
6
  import { getTokenPrice } from '@subwallet/extension-base/services/price-service/coingecko';
7
+ import { createPromiseHandler } from '@subwallet/extension-base/utils/promise';
6
8
  import { BehaviorSubject } from 'rxjs';
7
9
  const DEFAULT_PRICE_SUBJECT = {
8
10
  ready: false,
@@ -14,34 +16,14 @@ export class PriceService {
14
16
  priceSubject = new BehaviorSubject(DEFAULT_PRICE_SUBJECT);
15
17
  priceIds = new Set();
16
18
  constructor(dbService, eventService, chainService) {
19
+ this.status = ServiceStatus.NOT_INITIALIZED;
17
20
  this.dbService = dbService;
18
21
  this.eventService = eventService;
19
22
  this.chainService = chainService;
20
-
21
- // Fetch data from storage
22
- this.getPrice().catch(console.error);
23
- const eventHandler = () => {
24
- const newPriceIds = this.getPriceIds();
25
-
26
- // Compare two set newPriceIds and this.priceIds
27
- if (newPriceIds.size !== this.priceIds.size || !Array.from(newPriceIds).every(v => this.priceIds.has(v))) {
28
- this.priceIds = newPriceIds;
29
- this.refreshPriceData(this.priceIds);
30
- }
31
- };
32
- this.eventService.waitAssetReady.then(() => {
33
- this.refreshPriceData();
34
- this.eventService.on('asset.updateState', eventHandler);
35
- this.eventService.on('asset.updateState', eventHandler);
36
- }).catch(console.error);
23
+ this.init().catch(console.error);
37
24
  }
38
25
  async getPrice() {
39
- const isReady = this.priceSubject.value.ready;
40
- if (!isReady) {
41
- const data = await this.dbService.getPriceStore();
42
- this.priceSubject.next(data || DEFAULT_PRICE_SUBJECT);
43
- }
44
- return this.priceSubject.value;
26
+ return Promise.resolve(this.priceSubject.value);
45
27
  }
46
28
  getPriceSubject() {
47
29
  return this.priceSubject;
@@ -65,4 +47,70 @@ export class PriceService {
65
47
  }).catch(console.error);
66
48
  this.refreshTimeout = setTimeout(this.refreshPriceData.bind(this), CRON_REFRESH_PRICE_INTERVAL);
67
49
  }
50
+ async init() {
51
+ this.status = ServiceStatus.INITIALIZING;
52
+ // Fetch data from storage
53
+ await this.loadData();
54
+ const eventHandler = () => {
55
+ const newPriceIds = this.getPriceIds();
56
+
57
+ // Compare two set newPriceIds and this.priceIds
58
+ if (newPriceIds.size !== this.priceIds.size || !Array.from(newPriceIds).every(v => this.priceIds.has(v))) {
59
+ this.priceIds = newPriceIds;
60
+ this.refreshPriceData(this.priceIds);
61
+ }
62
+ };
63
+ await this.eventService.waitAssetReady;
64
+ this.status = ServiceStatus.INITIALIZED;
65
+ this.eventService.on('asset.updateState', eventHandler);
66
+ this.eventService.on('asset.updateState', eventHandler);
67
+ }
68
+ async loadData() {
69
+ const data = await this.dbService.getPriceStore();
70
+ this.priceSubject.next(data || DEFAULT_PRICE_SUBJECT);
71
+ }
72
+ async persistData() {
73
+ await this.dbService.updatePriceStore(this.priceSubject.value).catch(console.error);
74
+ }
75
+ startPromiseHandler = createPromiseHandler();
76
+ async start() {
77
+ console.debug('Start price service');
78
+ try {
79
+ this.startPromiseHandler = createPromiseHandler();
80
+ this.status = ServiceStatus.STARTING;
81
+ await this.startCron();
82
+ this.status = ServiceStatus.STARTED;
83
+ this.startPromiseHandler.resolve();
84
+ } catch (e) {
85
+ this.startPromiseHandler.reject(e);
86
+ }
87
+ }
88
+ async startCron() {
89
+ this.refreshPriceData();
90
+ return Promise.resolve();
91
+ }
92
+ stopPromiseHandler = createPromiseHandler();
93
+ async stop() {
94
+ console.debug('Stop price service');
95
+ try {
96
+ this.status = ServiceStatus.STOPPING;
97
+ this.stopPromiseHandler = createPromiseHandler();
98
+ await this.stopCron();
99
+ await this.persistData();
100
+ this.status = ServiceStatus.STOPPED;
101
+ this.stopPromiseHandler.resolve();
102
+ } catch (e) {
103
+ this.stopPromiseHandler.reject(e);
104
+ }
105
+ }
106
+ stopCron() {
107
+ clearTimeout(this.refreshTimeout);
108
+ return Promise.resolve(undefined);
109
+ }
110
+ waitForStarted() {
111
+ return this.startPromiseHandler.promise;
112
+ }
113
+ waitForStopped() {
114
+ return this.stopPromiseHandler.promise;
115
+ }
68
116
  }
@@ -41,6 +41,7 @@ export default class DatabaseService {
41
41
  subscribeNominatorMetadata(callback: (data: NominatorMetadata[]) => void): void;
42
42
  getHistories(query?: HistoryQuery): Promise<import("@subwallet/extension-base/services/storage-service/databases").ITransactionHistoryItem[]>;
43
43
  upsertHistory(histories: TransactionHistoryItem[]): Promise<unknown>;
44
+ updateHistoryByNewExtrinsicHash(extrinsicHash: string, updateData: Partial<TransactionHistoryItem>): Promise<unknown>;
44
45
  addNftCollection(collection: NftCollection): Promise<unknown>;
45
46
  deleteNftCollection(chain: string, collectionId: string): Promise<void>;
46
47
  getAllNftCollection(chainHashes?: string[]): import("dexie").PromiseExtended<NftCollection[]>;
@@ -126,6 +126,16 @@ export default class DatabaseService {
126
126
  const cleanedHistory = histories.filter(x => x && x.address && x.chain && x.extrinsicHash);
127
127
  return this.stores.transaction.bulkUpsert(cleanedHistory);
128
128
  }
129
+ async updateHistoryByNewExtrinsicHash(extrinsicHash, updateData) {
130
+ // this.logger.log('Updating transaction histories');
131
+ const canUpdate = updateData && extrinsicHash;
132
+ if (!canUpdate) {
133
+ return;
134
+ }
135
+ return this.stores.transaction.updateWithQuery({
136
+ extrinsicHash
137
+ }, updateData);
138
+ }
129
139
 
130
140
  // NFT Collection
131
141
  async addNftCollection(collection) {
@@ -4,9 +4,11 @@ export interface HistoryQuery {
4
4
  chain?: string;
5
5
  address?: string;
6
6
  extrinsicHash?: string;
7
+ transactionId?: string;
7
8
  }
8
9
  export default class TransactionStore extends BaseStoreWithAddressAndChain<ITransactionHistoryItem> {
9
10
  getHistoryByAddressAsObject(address: string): Promise<ITransactionHistoryItem[]>;
10
11
  queryHistory(query?: HistoryQuery): Promise<ITransactionHistoryItem[]>;
11
12
  bulkUpsert(records: ITransactionHistoryItem[]): Promise<unknown>;
13
+ updateWithQuery(query: HistoryQuery, update: Partial<ITransactionHistoryItem>): Promise<unknown>;
12
14
  }
@@ -29,16 +29,12 @@ export default class TransactionStore extends BaseStoreWithAddressAndChain {
29
29
  }
30
30
  async bulkUpsert(records) {
31
31
  await this.table.bulkPut(records);
32
-
33
- // await Promise.all(records.map((record) => {
34
- // return this.table.where({
35
- // chain: record.chain,
36
- // address: record.address,
37
- // extrinsicHash: record.extrinsicHash
38
- // }).filter((item) => (item.origin === 'app' && record.origin !== 'app'))
39
- // .delete();
40
- // }));
41
-
32
+ return true;
33
+ }
34
+ async updateWithQuery(query, update) {
35
+ await this.table.where(query).modify(record => {
36
+ return Object.assign(record, update);
37
+ });
42
38
  return true;
43
39
  }
44
40
  }
@@ -36,6 +36,8 @@ export default class TransactionService {
36
36
  private updateTransaction;
37
37
  private getTransactionLink;
38
38
  private transactionToHistories;
39
+ private onSigned;
40
+ private onSend;
39
41
  private onHasTransactionHash;
40
42
  private handlePostProcessing;
41
43
  private onSuccess;
@@ -37,7 +37,7 @@ export default class TransactionService {
37
37
  return Object.values(this.transactions);
38
38
  }
39
39
  get processingTransactions() {
40
- return this.allTransactions.filter(t => t.status === ExtrinsicStatus.PENDING || t.status === ExtrinsicStatus.PROCESSING);
40
+ return this.allTransactions.filter(t => t.status === ExtrinsicStatus.QUEUED || t.status === ExtrinsicStatus.PROCESSING);
41
41
  }
42
42
  getTransaction(id) {
43
43
  return this.transactions[id];
@@ -157,6 +157,7 @@ export default class TransactionService {
157
157
  }
158
158
  fillTransactionDefaultInfo(transaction) {
159
159
  const isInternal = !transaction.url;
160
+ const transactionId = getTransactionId(transaction.chainType, transaction.chain, isInternal);
160
161
  return {
161
162
  ...transaction,
162
163
  createdAt: new Date(),
@@ -164,10 +165,10 @@ export default class TransactionService {
164
165
  errors: transaction.errors || [],
165
166
  warnings: transaction.warnings || [],
166
167
  url: transaction.url || EXTENSION_REQUEST_URL,
167
- status: ExtrinsicStatus.PENDING,
168
+ status: ExtrinsicStatus.QUEUED,
168
169
  isInternal,
169
- id: getTransactionId(transaction.chainType, transaction.chain, isInternal),
170
- extrinsicHash: ''
170
+ id: transactionId,
171
+ extrinsicHash: transactionId
171
172
  };
172
173
  }
173
174
  async addTransaction(inputTransaction) {
@@ -180,8 +181,6 @@ export default class TransactionService {
180
181
  this.transactionSubject.next({
181
182
  ...transactions
182
183
  });
183
-
184
- // Send transaction
185
184
  return await this.sendTransaction(transaction);
186
185
  }
187
186
  generateBeforeHandleResponseErrors(errors) {
@@ -208,7 +207,8 @@ export default class TransactionService {
208
207
  validatedTransaction.warnings = [];
209
208
  const emitter = await this.addTransaction(validatedTransaction);
210
209
  await new Promise(resolve => {
211
- emitter.on('extrinsicHash', data => {
210
+ emitter.on('signed', data => {
211
+ validatedTransaction.id = data.id;
212
212
  validatedTransaction.extrinsicHash = data.extrinsicHash;
213
213
  resolve();
214
214
  });
@@ -224,6 +224,12 @@ export default class TransactionService {
224
224
  async sendTransaction(transaction) {
225
225
  // Send Transaction
226
226
  const emitter = transaction.chainType === 'substrate' ? this.signAndSendSubstrateTransaction(transaction) : await this.signAndSendEvmTransaction(transaction);
227
+ emitter.on('signed', data => {
228
+ this.onSigned(data);
229
+ });
230
+ emitter.on('send', data => {
231
+ this.onSend(data);
232
+ });
227
233
  emitter.on('extrinsicHash', data => {
228
234
  this.onHasTransactionHash(data);
229
235
  });
@@ -276,7 +282,8 @@ export default class TransactionService {
276
282
  to: '',
277
283
  chainType: transaction.chainType,
278
284
  address: transaction.address,
279
- status: ExtrinsicStatus.PROCESSING,
285
+ status: transaction.status,
286
+ transactionId: transaction.id,
280
287
  extrinsicHash: transaction.extrinsicHash,
281
288
  time: transaction.createdAt.getTime(),
282
289
  fee: transaction.estimateFee,
@@ -419,8 +426,14 @@ export default class TransactionService {
419
426
  break;
420
427
  }
421
428
  case ExtrinsicType.EVM_EXECUTE:
422
- // Todo: Update historyItem.to
423
- break;
429
+ {
430
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
431
+ const data = parseTransactionData(transaction.data);
432
+
433
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
434
+ historyItem.to = (data === null || data === void 0 ? void 0 : data.to) || '';
435
+ break;
436
+ }
424
437
  case ExtrinsicType.UNKNOWN:
425
438
  break;
426
439
  }
@@ -439,17 +452,36 @@ export default class TransactionService {
439
452
  }
440
453
  return [historyItem];
441
454
  }
455
+ onSigned({
456
+ id
457
+ }) {
458
+ console.log(`Transaction "${id}" is signed`);
459
+ }
460
+ onSend({
461
+ id
462
+ }) {
463
+ // Update transaction status
464
+ this.updateTransaction(id, {
465
+ status: ExtrinsicStatus.SUBMITTING
466
+ });
467
+
468
+ // Create Input History Transaction History
469
+ this.historyService.insertHistories(this.transactionToHistories(id)).catch(console.error);
470
+ console.log(`Transaction "${id}" is sent`);
471
+ }
442
472
  onHasTransactionHash({
443
- eventLogs,
444
473
  extrinsicHash,
445
474
  id
446
475
  }) {
447
476
  // Write processing transaction history
448
- this.updateTransaction(id, {
477
+ const updateData = {
449
478
  extrinsicHash,
450
479
  status: ExtrinsicStatus.PROCESSING
451
- });
452
- this.historyService.insertHistories(this.transactionToHistories(id, eventLogs)).catch(console.error);
480
+ };
481
+ this.updateTransaction(id, updateData);
482
+
483
+ // In this case transaction id is the same as extrinsic hash and will change after below update
484
+ this.historyService.updateHistoryByExtrinsicHash(id, updateData).catch(console.error);
453
485
  console.log(`Transaction "${id}" is submitted with hash ${extrinsicHash || ''}`);
454
486
  }
455
487
  handlePostProcessing(id) {
@@ -513,16 +545,17 @@ export default class TransactionService {
513
545
  id
514
546
  }) {
515
547
  const transaction = this.getTransaction(id);
548
+ const nextStatus = ExtrinsicStatus.FAIL;
516
549
  if (transaction) {
517
550
  this.updateTransaction(id, {
518
- status: ExtrinsicStatus.FAIL,
551
+ status: nextStatus,
519
552
  errors
520
553
  });
521
554
  console.log('Transaction failed', id, transaction.extrinsicHash);
522
555
 
523
556
  // Write failed transaction history
524
557
  this.historyService.updateHistories(transaction.chain, transaction.extrinsicHash, {
525
- status: ExtrinsicStatus.FAIL,
558
+ status: nextStatus,
526
559
  blockNumber: blockNumber || 0,
527
560
  blockHash: blockHash || ''
528
561
  }).catch(console.error);
@@ -642,6 +675,12 @@ export default class TransactionService {
642
675
  }
643
676
  signedTransaction = signed;
644
677
  }
678
+
679
+ // Emit signed event
680
+ emitter.emit('signed', eventData);
681
+
682
+ // Send transaction
683
+ emitter.emit('send', eventData); // This event is needed after sending transaction with queue
645
684
  signedTransaction && web3Api.eth.sendSignedTransaction(signedTransaction).once('transactionHash', hash => {
646
685
  eventData.extrinsicHash = hash;
647
686
  emitter.emit('extrinsicHash', eventData);
@@ -680,7 +719,6 @@ export default class TransactionService {
680
719
  errors: [],
681
720
  warnings: []
682
721
  };
683
- console.debug(address, transaction);
684
722
  transaction.signAsync(address, {
685
723
  signer: {
686
724
  signPayload: async payload => {
@@ -692,6 +730,11 @@ export default class TransactionService {
692
730
  }
693
731
  }
694
732
  }).then(rs => {
733
+ // Emit signed event
734
+ emitter.emit('signed', eventData);
735
+
736
+ // Send transaction
737
+ emitter.emit('send', eventData); // This event is needed after sending transaction with queue
695
738
  rs.send(txState => {
696
739
  // handle events, logs, history
697
740
  if (!txState || !txState.status) {
@@ -41,6 +41,8 @@ export interface TransactionEventResponse extends ValidateTransactionResponse {
41
41
  eventLogs?: EventRecord[];
42
42
  }
43
43
  export interface TransactionEventMap {
44
+ send: (response: TransactionEventResponse) => void;
45
+ signed: (response: TransactionEventResponse) => void;
44
46
  extrinsicHash: (response: TransactionEventResponse) => void;
45
47
  error: (response: TransactionEventResponse) => void;
46
48
  success: (response: TransactionEventResponse) => void;
@@ -9,15 +9,17 @@ export function parseTransactionData(data) {
9
9
  return data;
10
10
  }
11
11
  export function getTransactionLink(chainInfo, extrinsicHash) {
12
- const explorerLink = _getBlockExplorerFromChain(chainInfo);
13
- if (_isPureEvmChain(chainInfo)) {
14
- if (explorerLink) {
15
- return `${explorerLink}${explorerLink.endsWith('/') ? '' : '/'}tx/${extrinsicHash}`;
16
- }
17
- } else {
12
+ if (extrinsicHash.startsWith('0x')) {
18
13
  const explorerLink = _getBlockExplorerFromChain(chainInfo);
19
- if (explorerLink) {
20
- return `${explorerLink}${explorerLink.endsWith('/') ? '' : '/'}extrinsic/${extrinsicHash}`;
14
+ if (_isPureEvmChain(chainInfo)) {
15
+ if (explorerLink) {
16
+ return `${explorerLink}${explorerLink.endsWith('/') ? '' : '/'}tx/${extrinsicHash}`;
17
+ }
18
+ } else {
19
+ const explorerLink = _getBlockExplorerFromChain(chainInfo);
20
+ if (explorerLink) {
21
+ return `${explorerLink}${explorerLink.endsWith('/') ? '' : '/'}extrinsic/${extrinsicHash}`;
22
+ }
21
23
  }
22
24
  }
23
25
  return undefined;
@@ -0,0 +1,6 @@
1
+ export declare function createPromiseHandler<T>(): {
2
+ resolve: (value: T) => void;
3
+ reject: (reason?: unknown) => void;
4
+ promise: Promise<T>;
5
+ };
6
+ export declare type PromiseHandler<T> = ReturnType<typeof createPromiseHandler<T>>;