Skip to content

Implement a distributed idempotency layer for buy and sell endpoints using Redis to prevent double-execution across concurrent retries #725

Description

@Chucks1093

Summary

When a client retries a buy or sell request due to a network timeout, the server may process the same transaction twice — charging the buyer or crediting the seller multiple times. A distributed idempotency layer backed by Redis should detect duplicate requests within a configurable window and return the original response without re-executing the transaction.

Scope

  • Accept an Idempotency-Key header on POST /buy and POST /sell endpoints
  • On first request: execute the transaction, store { statusCode, body, resolvedAt } in Redis under the key with a 24-hour TTL, then return the response
  • On duplicate request (same key within TTL): return the stored response immediately without touching the database or Soroban
  • If the first request is still in-flight when a duplicate arrives, block the duplicate for up to 5 seconds then either return the in-progress result or a 409 request_in_progress
  • Reject requests missing the Idempotency-Key header with 400 missing_idempotency_key
  • Namespace keys by wallet address to prevent cross-wallet collisions: idempotency:{walletAddress}:{key}
  • Add integration tests covering: first execution stores result, duplicate returns stored result, in-flight duplicate returns 409, missing header returns 400, expired key allows re-execution

Acceptance Criteria

  • First request executes and stores result in Redis with 24-hour TTL
  • Duplicate key within TTL returns stored response without re-executing
  • In-flight duplicate blocked up to 5 seconds then returns 409
  • Missing header returns 400 with missing_idempotency_key
  • Keys namespaced by wallet to prevent cross-wallet collisions
  • Expired key allows a fresh execution (TTL enforced correctly)
  • No Soroban transaction submitted on a duplicate request

ETA: 24 hours


Coordinate on Telegram

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial Campaign | FWC26Campaign: Official Campaign | FWC26

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions