Skip to content

Commit 94de1cf

Browse files
feat: transaction scan 4 - add transacstion scan service (#101)
## Explanation This PR introduces transaction-history synchronization and richer Horizon-derived transaction mapping for the Stellar snap, including decoding result_xdr for more accurate path-payment (swap/receive) amounts and persisting per-account scan cursors. <!-- Thanks for your contribution! Take a moment to answer these questions so that reviewers have the information they need to properly understand your changes: * What is the current state of things and why does it need to change? * What is the solution your changes offer and how does it work? * Are there any changes whose purpose might not obvious to those unfamiliar with the domain? * If your primary goal was to update one package but you found you had to update another one along the way, why did you do so? * If you had to upgrade a dependency, why did you do so? --> ## References <!-- Are there any issues that this pull request is tied to? Are there other links that reviewers should consult to understand these changes better? Are there client or consumer pull requests to adopt any breaking changes? For example: * Fixes #12345 * Related to #67890 --> ## Checklist - [ ] I've updated the test suite for new or updated code as appropriate - [ ] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [ ] I've communicated my changes to consumers by [updating changelogs for packages I've changed](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md) - [ ] I've introduced [breaking changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md) in this PR and have prepared draft pull requests for clients and consumer packages to resolve them
1 parent fe9d5e3 commit 94de1cf

21 files changed

Lines changed: 1671 additions & 756 deletions

merged-packages/stellar-wallet-snap/src/context.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import {
4747
} from './services/on-chain-account';
4848
import { PriceService } from './services/price';
4949
import { State } from './services/state';
50+
import { SynchronizeService } from './services/sync/SynchronizeService';
5051
import {
5152
TransactionBuilder,
5253
TransactionRepository,
@@ -68,6 +69,7 @@ const state = new State({
6869
keyringAccounts: {},
6970
assets: {},
7071
transactions: {},
72+
lastScanTokens: {},
7173
onChainAccounts: {} as OnChainAccountState['onChainAccounts'],
7274
},
7375
});
@@ -111,6 +113,7 @@ const transactionService = new TransactionService({
111113
transactionRepository,
112114
networkService,
113115
transactionBuilder,
116+
assetMetadataService,
114117
});
115118

116119
const priceService = new PriceService({
@@ -125,6 +128,12 @@ const transactionScanService = new TransactionScanService({
125128
logger,
126129
});
127130

131+
const synchronizeService = new SynchronizeService({
132+
logger,
133+
onChainAccountService,
134+
transactionService,
135+
});
136+
128137
/** UX Controller */
129138
const confirmationUIController = new ConfirmationUXController({
130139
logger,
@@ -210,15 +219,14 @@ const refreshConfirmationContextHandler = new RefreshConfirmationContextHandler(
210219
const trackTransactionHandler = new TrackTransactionHandler({
211220
logger,
212221
networkService,
213-
onChainAccountService,
222+
synchronizeService,
214223
accountService,
215-
transactionService,
216224
});
217225

218226
const syncAccountsHandler = new SyncAccountsHandler({
219227
logger,
220228
accountService,
221-
onChainAccountService,
229+
synchronizeService,
222230
});
223231

224232
const cronjobMethodHandlers: Record<

merged-packages/stellar-wallet-snap/src/handlers/cronjob/syncAccounts.test.ts

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type {
66
StellarKeyringAccount,
77
} from '../../services/account';
88
import { generateMockStellarKeyringAccounts } from '../../services/account/__mocks__/account.fixtures';
9-
import type { OnChainAccountService } from '../../services/on-chain-account';
9+
import type { SynchronizeService } from '../../services/sync/SynchronizeService';
1010
import { Duration } from '../../utils';
1111
import { logger } from '../../utils/logger';
1212

@@ -27,23 +27,22 @@ describe('SyncAccountsHandler', () => {
2727
getAllSelected: jest.fn(),
2828
findByIds: jest.fn(),
2929
};
30-
const onChainAccountService: jest.Mocked<
31-
Pick<OnChainAccountService, 'synchronize'>
30+
const synchronizeService: jest.Mocked<
31+
Pick<SynchronizeService, 'synchronize'>
3232
> = {
3333
synchronize: jest.fn(),
3434
};
3535

3636
const handler = new SyncAccountsHandler({
3737
logger,
3838
accountService: accountService as unknown as AccountService,
39-
onChainAccountService:
40-
onChainAccountService as unknown as OnChainAccountService,
39+
synchronizeService: synchronizeService as unknown as SynchronizeService,
4140
});
4241

4342
return {
4443
handler,
4544
accountService,
46-
onChainAccountService,
45+
synchronizeService,
4746
};
4847
};
4948

@@ -89,7 +88,7 @@ describe('SyncAccountsHandler', () => {
8988
});
9089

9190
it('synchronizes selected accounts when accountIds is `selected`', async () => {
92-
const { handler, accountService, onChainAccountService } = setupTest();
91+
const { handler, accountService, synchronizeService } = setupTest();
9392
const selectedAccounts = [firstAccount, secondAccount];
9493
accountService.getAllSelected.mockResolvedValue(selectedAccounts);
9594

@@ -104,14 +103,14 @@ describe('SyncAccountsHandler', () => {
104103

105104
expect(accountService.getAllSelected).toHaveBeenCalledTimes(1);
106105
expect(accountService.findByIds).not.toHaveBeenCalled();
107-
expect(onChainAccountService.synchronize).toHaveBeenCalledWith(
106+
expect(synchronizeService.synchronize).toHaveBeenCalledWith(
108107
selectedAccounts,
109-
AppConfig.selectedNetwork,
108+
{ scope: AppConfig.selectedNetwork },
110109
);
111110
});
112111

113112
it('treats empty params object like selected accounts', async () => {
114-
const { handler, accountService, onChainAccountService } = setupTest();
113+
const { handler, accountService, synchronizeService } = setupTest();
115114
const selectedAccounts = [firstAccount];
116115
accountService.getAllSelected.mockResolvedValue(selectedAccounts);
117116

@@ -125,14 +124,14 @@ describe('SyncAccountsHandler', () => {
125124
await handler.handle(request);
126125

127126
expect(accountService.getAllSelected).toHaveBeenCalledTimes(1);
128-
expect(onChainAccountService.synchronize).toHaveBeenCalledWith(
127+
expect(synchronizeService.synchronize).toHaveBeenCalledWith(
129128
selectedAccounts,
130-
AppConfig.selectedNetwork,
129+
{ scope: AppConfig.selectedNetwork },
131130
);
132131
});
133132

134133
it('synchronizes accounts fetched by ids when accountIds is an array of account ids', async () => {
135-
const { handler, accountService, onChainAccountService } = setupTest();
134+
const { handler, accountService, synchronizeService } = setupTest();
136135
const accountIds = [firstAccount.id, secondAccount.id];
137136
const accountsByIds = [firstAccount];
138137
accountService.findByIds.mockResolvedValue(accountsByIds);
@@ -148,9 +147,8 @@ describe('SyncAccountsHandler', () => {
148147

149148
expect(accountService.findByIds).toHaveBeenCalledWith(accountIds);
150149
expect(accountService.getAllSelected).not.toHaveBeenCalled();
151-
expect(onChainAccountService.synchronize).toHaveBeenCalledWith(
152-
accountsByIds,
153-
AppConfig.selectedNetwork,
154-
);
150+
expect(synchronizeService.synchronize).toHaveBeenCalledWith(accountsByIds, {
151+
scope: AppConfig.selectedNetwork,
152+
});
155153
});
156154
});

merged-packages/stellar-wallet-snap/src/handlers/cronjob/syncAccounts.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type {
66
AccountService,
77
StellarKeyringAccount,
88
} from '../../services/account';
9-
import type { OnChainAccountService } from '../../services/on-chain-account';
9+
import type { SynchronizeService } from '../../services/sync/SynchronizeService';
1010
import { Duration, scheduleBackgroundEvent } from '../../utils';
1111
import { createPrefixedLogger } from '../../utils/logger';
1212
import type { ILogger } from '../../utils/logger';
@@ -23,17 +23,17 @@ export class SyncAccountsHandler extends CronjobBaseHandler<SyncAccountJsonRpcRe
2323
});
2424
}
2525

26-
readonly #onChainAccountService: OnChainAccountService;
26+
readonly #synchronizeService: SynchronizeService;
2727

2828
readonly #accountService: AccountService;
2929

3030
constructor({
3131
logger,
32-
onChainAccountService,
32+
synchronizeService,
3333
accountService,
3434
}: {
3535
logger: ILogger;
36-
onChainAccountService: OnChainAccountService;
36+
synchronizeService: SynchronizeService;
3737
accountService: AccountService;
3838
}) {
3939
const prefixedLogger = createPrefixedLogger(
@@ -44,7 +44,7 @@ export class SyncAccountsHandler extends CronjobBaseHandler<SyncAccountJsonRpcRe
4444
logger: prefixedLogger,
4545
requestStruct: SyncAccountJsonRpcRequestStruct,
4646
});
47-
this.#onChainAccountService = onChainAccountService;
47+
this.#synchronizeService = synchronizeService;
4848
this.#accountService = accountService;
4949
}
5050

@@ -75,8 +75,6 @@ export class SyncAccountsHandler extends CronjobBaseHandler<SyncAccountJsonRpcRe
7575
accounts = await this.#accountService.findByIds(accountIds);
7676
}
7777

78-
await this.#onChainAccountService.synchronize(accounts, scope);
79-
// TODO:
80-
// sync transaction history
78+
await this.#synchronizeService.synchronize(accounts, { scope });
8179
}
8280
}

0 commit comments

Comments
 (0)