Skip to content

#103 — Has No Per-Payer Cap: Receipt Counters Grow Unbounded #884

Description

@Topmatrixmor2014

Labels: Official Campaign | FWC26 GrantFox OSS Maybe Rewarded contract soroban receipts performance bug

This is a smart-contract issue for the GrantFox FWC26 campaign. Bound per-payer receipt counts so a single account cannot bloat storage and slow the TTL sweep.

Requirements and Context

contracts/finchippay-contract/src/lib.rsmint_receipt (≈ line 2110):

let count: u32 = env
    .storage()
    .persistent()
    .get(&DataKey::ReceiptCount(from.clone()))
    .unwrap_or(0);

There is no MAX_USER_RECEIPTS cap (unlike MAX_USER_ESCROWS/MAX_USER_STREAMS). A single payer can mint an unbounded number of receipts; each mints two persistent entries (ReceiptRecord(from, count) + ReceiptByIndex(global)). Consequences:

  • Storage bloat: one account can drive the contract's storage footprint (and ledger fees) far beyond any other user.
  • TTL sweep slowdown: bump_all_ttls walks every receipt via TotalReceiptCount; unbounded receipts make each sweep pass progressively more expensive and eventually exceed a single transaction's budget, so the sweep can no longer complete a class.
  • ReceiptCount is u32 — overflow at 4.29B mints (remote, but the pattern is already bounded elsewhere).

Objectives

  1. Add MAX_USER_RECEIPTS (e.g. 1_000, consistent with the escrow/stream caps) and reject ReceiptCount(from) >= MAX_USER_RECEIPTS with a descriptive panic before minting.
  2. Document the cap in rustdoc and the README.
  3. Add a test: minting past the cap reverts; boundary value passes.

Suggested Execution

  1. Fork and branch: git checkout -b fix/receipt-cap.
  2. Patch mint_receipt in src/lib.rs.
  3. Add boundary tests; run cargo fmt --check && cargo clippy -- -D warnings && cargo test.

Acceptance Criteria

  • Receipt minting past the cap reverts cleanly.
  • Boundary (exactly at cap) succeeds.
  • ≥2 tests; cargo test + wasm32v1-none build pass.

Guidelines

  • Pick the cap to be generous but bounded; document it as a contract-level constant.

Timeframe: 12 hours

Activity

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions