Skip to content

feat: subgraph retry with backoff, split validation, immutable query cache & safe event parsing (Closes #277, #278, #279, #280) - #476

Merged
Cjay-Cyber-2 merged 1 commit into
ASTROIDX556:mainfrom
graceshaba93:feat/stellar-wave-277-280
Oct 22, 2026
Merged

Cjay-Cyber-2 merged 1 commit into
ASTROIDX556:mainfrom
graceshaba93:feat/stellar-wave-277-280

Conversation

@graceshaba93

Copy link
Copy Markdown

Summary

Single PR delivering all four issues assigned to @graceshaba93 for the Stellar Wave (10th wave). Each issue is described and verified below; the change set is utility-first, follows the repo's module conventions, and ships unit tests for every new behaviour.


#277 — Add input validation for split configurations and receiver weights

Malformed split configurations with invalid receiver weights or duplicate addresses can lead to unexpected transaction reverts on-chain. Validation now runs before any split payload is built so invalid configurations never reach the submission stage.

Implemented

  • src/common/validators/split-configuration.schema.ts
    • splitReceiverSchema — Stellar account address (G…/M…, full checksum validation via the existing stellarPublicKeySchema) + integer weight (> 0, ≤ precision limit).
    • splitConfigurationSchema — rejects empty splits; flags duplicate receiver addresses with a descriptive message including the offending address and its index; rejects weight totals above MAX_SPLIT_WEIGHT_TOTAL (10⁷, so a full split maps onto Stellar's 7-decimal stroop precision).
    • validateSplitConfiguration(input) — dedicated validation function returning typed data or throwing the canonical ValidationException with structured ValidationErrorDetail[].
    • Single-pass O(n) checks, synchronous and allocation-light — no blocking I/O, no measurable event-loop impact.
  • Exported from src/common/validators/index.ts.

Acceptance criteria

  • Validation checks for receiver weight allocations and address formats
  • Descriptive validation errors for duplicate receivers / invalid weight sums
  • Unit tests for edge cases: empty splits, maximum weight boundaries, malformed addresses (20 tests)
  • Validation executes efficiently without blocking the main execution thread

#278 — Implement caching layer for immutable stream metadata queries

Repeatedly querying immutable metadata strains external subgraph endpoints. An in-memory, bounded cache now serves immutable reads directly from the query layer.

Implemented

  • src/utils/ttl-cache.util.ts — TtlCache with per-entry TTL, size-bounded LRU eviction (maxEntries, least-recently-used evicted first, so memory can never leak), hit/miss stats for observability, injectable clock for deterministic tests, and getOrSet(key, loader, { bypass, ttlMs }).
    • bypass: true skips the cache read and refreshes the entry with fresh data.
    • Loader failures are never cached — the error propagates and the next call retries.
  • Integrated into the subgraph query wrapper (src/utils/subgraph-query.util.ts): opt-in via cacheTtlMs (only for immutable reads), keyed by url + query + variables, with bypassCache to force fresh data. Only successful responses are cached; the cache is bounded (250 entries shared, or inject a custom TtlCache).

Acceptance criteria

  • In-memory cache utility with bounded size and expiration
  • Integrated into subgraph/metadata retrieval (executeSubgraphQuery)
  • Unit tests for cache hits, misses and eviction behaviour (17 utility tests + 5 wrapper tests)
  • Option to bypass the cache when fresh data is required

#279 — Refactor event parsing utilities to handle malformed log payloads safely

A single malformed contract event log (missing topic, null payload, corrupted object) could throw during parsing and abort batch processing of otherwise valid events.

Implemented

  • src/integrations/stellar/contract-event.parser.ts — parseContractEventLogs(logs, logger?)
    • Validates every entry inside its own try/catch — even property access on corrupted objects (throwing getters/proxies) degrades to a warning.
    • Skips invalid entries with one warn per entry (index + reason) and continues parsing the rest of the batch.
    • Non-array input (missing events field) yields []; valid entries are returned unchanged, in order, with the exact SorobanContractEvent type.
  • Integrated into both simulation services (SorobanSimulationService, StellarSimulationService): events are parsed defensively before risk scoring and before being returned, so eventCount only counts well-formed logs and the response keeps its exact shape.

Acceptance criteria

  • Log decoding wrapped in safe try/catch that logs warnings for malformed entries
  • Invalid logs skipped while valid events continue to be parsed in batches
  • Unit tests with corrupted/incomplete log fixtures (11 parser tests + 2 service-level tests)
  • Exact return types and behaviour preserved for valid event logs

#280 — Add retry mechanism with exponential backoff for transient subgraph queries

Temporary subgraph outages or rate limits caused queries to fail unnecessarily.

Implemented

  • src/utils/retry.util.ts
    • isTransientNetworkError(error) — classifies failures by walking the cause chain: 5xx responses and transport errors (ECONNRESET, ETIMEDOUT, ECONNABORTED, undici timeouts, …) are retryable; 4xx responses, GraphQL execution errors (retryable: false) and deterministic failures fail immediately without retrying.
    • retryWithBackoff(fn, options) — configurable maxAttempts, baseDelayMs, maxDelayMs cap, isRetryable predicate, onRetry hook; exponential backoff with jitter via the existing exponentialBackoffWithJitter (src/utils/backoff.util.ts); no sleep after the final attempt.
  • src/utils/subgraph-query.util.ts — executeSubgraphQuery applies the retry wrapper to subgraph executions (injectable fetchImpl for tests); idempotent reads only.

Acceptance criteria

  • Exponential backoff retry helper with configurable max attempts and jitter
  • Retry wrapper applied to subgraph query executions
  • Non-transient errors (syntax errors, 400 responses) fail immediately without retrying
  • Unit tests simulate transient network failures and verify backoff timing, eventual success, max-attempt exhaustion and delay capping (13 retry tests + 13 wrapper tests, fake timers)

Testing & Validation

All run locally against this branch:

Check Result
npm run typecheck ✅ pass
npm run lint ✅ pass (0 warnings/errors)
npm test ✅ 1964 tests / 186 files passed (includes all new suites)
npm run docs:check ✅ pass
npm run db:verify ✅ pass (0 warnings; no schema changes in this PR)

New/updated test files: split-configuration.schema.spec.ts, ttl-cache.util.spec.ts, subgraph-query.util.spec.ts, retry.util.spec.ts, contract-event.parser.spec.ts, soroban-simulation.service.spec.ts, stellar-simulation.service.spec.ts.

No database migrations, no new endpoints, no dependency changes. Branch is rebased on the latest main — no conflicts.

Closes #277
Closes #278
Closes #279
Closes #280

… safe event parsing

Implements the four assigned Stellar Wave issues in one change:

- ASTROIDX556#280: exponential backoff retry helper with configurable attempts and
  jitter (retryWithBackoff), transient-error classification
  (isTransientNetworkError: 5xx/network timeouts retryable, 4xx and
  GraphQL errors fail fast), and executeSubgraphQuery wrapping subgraph
  reads with the retry policy.
- ASTROIDX556#277: split configuration validation (split-configuration.schema)
  rejecting empty splits, duplicate receivers, malformed addresses and
  weight totals beyond the precision limit, with descriptive
  ValidationException details.
- ASTROIDX556#278: bounded in-memory TtlCache (TTL + LRU eviction, bypass option,
  hit/miss stats) wired into executeSubgraphQuery as an opt-in cache for
  immutable metadata reads.
- ASTROIDX556#279: defensive parseContractEventLogs skipping malformed contract
  event logs with warnings, integrated into both simulation services so
  one bad log never aborts batch processing.

Closes ASTROIDX556#277, closes ASTROIDX556#278, closes ASTROIDX556#279, closes ASTROIDX556#280

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
@Cjay-Cyber-2
Cjay-Cyber-2 merged commit e337c93 into ASTROIDX556:main Oct 22, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants