Skip to content

feat: Four SDK enhancements — sponsorship reserve, expiry guard, cursor tracker, XDR decoder - #572

Open
lekescrew22 wants to merge 5 commits into
Stellar-split:mainfrom
lekescrew22:feature/four-sdk-enhancements
Open

feat: Four SDK enhancements — sponsorship reserve, expiry guard, cursor tracker, XDR decoder#572
lekescrew22 wants to merge 5 commits into
Stellar-split:mainfrom
lekescrew22:feature/four-sdk-enhancements

Conversation

@lekescrew22

@lekescrew22 lekescrew22 commented Jul 28, 2026

Copy link
Copy Markdown

Closes #502
Closes #504
Closes #506
Closes #507

Summary

This PR delivers four independent SDK enhancements that improve transaction safety, stream reliability, and developer observability.


1. Sponsorship Reserve Verifier

Problem: The SDK's sponsorship feature builds BeginSponsoringFutureReserves / EndSponsoringFutureReserves operation pairs but does not verify in advance that the sponsoring account has sufficient XLM reserve to cover the new ledger entries it will sponsor.

Solution: Added checkSponsorReserve() in src/preflightChecker.ts that queries Horizon's loadAccount() for subentry_count and num_sponsored, computes the required reserve increase against BASE_RESERVE, and throws InsufficientSponsorReserveError on shortfall. A convenience wrapper checkSponsorshipReserve() is exported from src/sponsorship.ts.

Files: src/errors.ts, src/types.ts, src/sponsorship.ts, src/preflightChecker.ts


2. Invoice Expiry Guard with Timebounds

Problem: Invoices carry an expiresAt timestamp but the SDK does not enforce this at the ledger level, only through SDK-side checks.

Solution: Extended StellarSplitTxBuilder.build() and .submit() to accept an optional { expiresAt, invoiceId } parameter. When provided, translates the invoice expiry into transaction timebounds via TransactionBuilder.setTimeout(), causing the ledger to reject submissions after the deadline has passed. Added checkInvoiceExpiry() pre-submission check and PaymentExpiredError.

Files: src/txBuilder.ts, src/preflightChecker.ts, src/errors.ts


3. SSE Cursor Position Tracker

Problem: Long-running SDK processes discard the SSE cursor on process exit, causing duplicate event processing on every restart.

Solution: New cursorTracker.ts with getCursor(streamId) / setCursor(streamId, token) for persisting the last-processed paging token. Integrated into stream.ts (polling resume from stored cursor), sse.ts (SSE URL with ?cursor= param), and subscription.ts (createInvoiceSubscription). Supports injectable CursorPersistence backend for production persistence.

Files: src/cursorTracker.ts (new), src/stream.ts, src/sse.ts, src/subscription.ts


4. XDR Decoder for Structured Logging

Problem: Stellar's XDR binary format is opaque to API consumers and log-aggregation tools. Engineers must use external converters to understand logged transactions.

Solution: Added decodeXDR() in src/xdrDecoder.ts that translates base64-encoded XDR into structured JSON using the @stellar/stellar-sdk xdr namespace. Decodes TransactionEnvelope, TransactionResult, TransactionMeta, and LedgerEntry. Integrated into auditLogger.ts via logWithXdr() and logAndDecodeXdr() methods that auto-detect and decode XDR payloads.

Files: src/xdrDecoder.ts (new), src/auditLogger.ts, src/types.ts


Typecheck

All changes pass npx tsc --noEmit with no new errors introduced (only pre-existing error in client.ts unrelated to these changes).

Extends StellarSplitTxBuilder.build() and .submit() to accept an optional
expiresAt timestamp. When provided, translates the invoice expiry into
transaction timebounds via TransactionBuilder.setTimeout(), causing the
ledger to reject submissions after the deadline has passed.

Adds checkInvoiceExpiry() pre-submission check to preflightChecker.ts
and PaymentExpiredError to errors.ts.
… check

Adds checkSponsorReserve() to verify sponsoring accounts have sufficient
XLM reserve before building sponsored-reserve onboarding transactions.
Queries Horizon loadAccount() for subentry_count and num_sponsored,
computes required reserve against BASE_RESERVE, and throws
InsufficientSponsorReserveError on shortfall.

Adds InsufficientSponsorReserveError to errors.ts and SponsorshipConfig /
SponsorReserveCheckResult to types.ts. Exposes checkSponsorshipReserve()
convenience wrapper in sponsorship.ts.
Adds cursorTracker.ts with getCursor(streamId) / setCursor(streamId, token)
for persisting the last-processed paging token across process restarts.
Integrates cursor persistence into stream.ts (polling), sse.ts (SSE URL
with ?cursor= param), and subscription.ts (createInvoiceSubscription).

Supports injectable CursorPersistence backend for production use.
Adds decodeXDR() that translates base64-encoded Stellar XDR into
structured JSON objects safe for logging, audit trails, and developer UIs.
Decodes TransactionEnvelope, TransactionResult, TransactionMeta, and
LedgerEntry using @stellar/stellar-sdk xdr namespace.

Integrates with auditLogger.ts via logWithXdr() and logAndDecodeXdr()
methods that auto-detect and decode XDR payloads in audit entries.
@drips-wave

drips-wave Bot commented Jul 28, 2026

Copy link
Copy Markdown

@lekescrew22 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

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.

XDR-to-JSON Human-Readable Decoder Ledger Cursor Position Tracker Payment Expiry Guard Sponsorship Reserve Verifier

1 participant