Skip to content

feat: transaction scanning patch 1 - refactor transaction object and add get transaction(s) network utils#87

Merged
stanleyyconsensys merged 12 commits into
mainfrom
feat/transaction-scannning-1
Jun 4, 2026
Merged

feat: transaction scanning patch 1 - refactor transaction object and add get transaction(s) network utils#87
stanleyyconsensys merged 12 commits into
mainfrom
feat/transaction-scannning-1

Conversation

@stanleyyconsensys
Copy link
Copy Markdown
Collaborator

@stanleyyconsensys stanleyyconsensys commented Jun 2, 2026

Explanation

This PR refactors transaction deserialization into the Transaction model (via factory methods) and
adds Horizon-backed transaction fetch/scan utilities to NetworkService, s
upporting transaction scanning and richer on-chain metadata (e.g., fee_charged).

References

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
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors transaction deserialization into the Transaction model (via factory methods) and adds Horizon-backed transaction fetch/scan utilities to NetworkService, supporting transaction scanning and richer on-chain metadata (e.g., fee_charged).

Changes:

  • Moved XDR deserialization from TransactionBuilder into Transaction (fromXdr, fromHorizon, fromRaw) and added feeCharged + id.
  • Added NetworkService.getTransaction and NetworkService.getTransactions (cursor pagination, bounded scanning).
  • Updated keyring signing flow and tests to use Transaction.fromXdr, plus new Horizon test fixtures/constants.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/snap/src/services/transaction/TransactionService.ts Uses Transaction.fromXdr for swap XDR validation flow.
packages/snap/src/services/transaction/TransactionBuilder.ts Removes deserialize method (deserialization moved to Transaction).
packages/snap/src/services/transaction/TransactionBuilder.test.ts Removes tests for the removed deserialize method.
packages/snap/src/services/transaction/Transaction.ts Adds factory constructors, feeCharged, and id accessors.
packages/snap/src/services/transaction/Transaction.test.ts Adds tests for fromXdr, fromHorizon, and invalid XDR behavior.
packages/snap/src/services/transaction/exceptions.ts Introduces TransactionDeserializationException.
packages/snap/src/services/transaction/mocks/transaction.fixtures.ts Adds Horizon transaction/page fixtures for tests.
packages/snap/src/services/network/NetworkService.ts Adds getTransaction/getTransactions and maps Horizon records to Transaction.
packages/snap/src/services/network/NetworkService.test.ts Adds test coverage for new Horizon transaction fetch/scan utilities.
packages/snap/src/handlers/keyring/signTransaction.ts Switches signing flow deserialization to Transaction.fromXdr.
packages/snap/src/handlers/keyring/signTransaction.test.ts Updates tests to align with new deserialization path.
packages/snap/src/handlers/keyring/exceptions.ts Maps TransactionDeserializationException to SEP-43 InvalidRequest.
packages/snap/src/context.ts Removes transactionBuilder injection into SignTransactionHandler.
packages/snap/src/constants.ts Adds scan/page-size constants for Horizon transaction pagination.
packages/snap/src/api/xdr.ts Uses Transaction.fromXdr for op-type inspection; adjusts networkId hashing logic.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/snap/src/api/xdr.ts Outdated
Comment on lines +871 to +877
const initialTransactionsResponse = await client
.transactions()
.forAccount(accountAddress)
.order(order)
.cursor(lastScanToken)
.limit(pageSize)
.call();
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread packages/snap/src/services/network/NetworkService.ts Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Comment thread packages/snap/src/services/network/NetworkService.ts Outdated
Comment on lines +870 to +873
let maxScanRemaining = maxScan;
let nextScanToken: string = lastScanToken;

try {
Copy link
Copy Markdown
Collaborator Author

@stanleyyconsensys stanleyyconsensys Jun 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will use default page = 1 , instead of return empty

Comment on lines +944 to +952
includeSelfTransactionsOnly: boolean,
): Transaction[] {
const result: Transaction[] = [];

for (const transaction of transactions) {
if (
(includeSelfTransactionsOnly &&
transaction.source_account === accountAddress) ||
!includeSelfTransactionsOnly
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This filters self transactions only by Horizon source_account. That can skip valid wallet transactions where the wallet is the fee source or an operation source. Since the scan cursor still advances, those skipped records won’t be picked up later.

I’d deserialize first, then filter with transaction.isInvokedByAccount(accountAddress), or include fee_account in the check

Copy link
Copy Markdown
Collaborator Author

@stanleyyconsensys stanleyyconsensys Jun 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

im not sure yet

for fee bump txn
the txn can be not belong to me, should i show them, i think we didnt do that for other network

for sponser transaction
the txn is made for others, but im the one who pay the fee and execute it, should i show it on my broad?

for now, i only consider if the txn is execute by me

let reconfirm this later with others, and create a ticket to follow up?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, i agree let's defer and open a follow-up ticket. Just let's reuse Transaction.isInvokedByAccount when we revisit so the scan matches the rest of the codebase


// Non-empty page from `next()` should always expose a paging token on the last row.
const lastPageRecord = currentResponse.records.at(-1);
nextScanToken = lastPageRecord?.paging_token ?? '';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We support order: 'desc', but nextScanToken is always set to the last record in the page. For descending scans, that token points to the oldest fetched transaction, so using it for the next scan can skip newer transactions.
If desc is only for loading older history, we should return a separate cursor or document that clearly

Copy link
Copy Markdown
Collaborator Author

@stanleyyconsensys stanleyyconsensys Jun 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is very confusing if the nextScanToken has different position by order

i would suggest we store the horizon data in the txn object
and remove the return of last scan token from the func response

and the caller decide which scan token to be store

so if there is a case that the caller need continue scanning from desc order, it works
so if there is a case that the caller need scanning once from desc order, and then scan in asc order base on the first record return from desc order, it works too

the caller decide, rather than the function decide

we should make the func more general

wdyt?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, sounds good just one thing: make sure the returned records keep their paging_token so the caller can actually choose where to resume from, and let's document which token to use for incremental (asc) vs backfill (desc)

Copy link
Copy Markdown
Contributor

@wantedsystem wantedsystem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@stanleyyconsensys stanleyyconsensys merged commit e811dd1 into main Jun 4, 2026
10 checks passed
@stanleyyconsensys stanleyyconsensys deleted the feat/transaction-scannning-1 branch June 4, 2026 10:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants