Skip to content

Critical: liquidity pool blindly pays any merchant address the creditline names — no recipient validation or exposure cap #106

Description

@grantfox-oss

Problem

fund_loan() in liquidity-pool-contract (lib.rs:220–256) honors transfer(pool → merchant, amount) for whichever merchant address the registered creditline supplies. The pool performs zero independent verification: it cannot confirm the merchant is registered/approved in the vendor registry, that a real loan object exists, or that cumulative exposure to this merchant/borrower pair is sane. Its only guards are available-liquidity arithmetic and the creditline address check.

Trust model consequence: the pool's entire solvency rests on the creditline contract being bug-free forever. The unfunded-approval defect filed separately shows creditline lifecycle bugs are real. A single creditline bug or compromised creditline admin (who can repoint set_creditline() — lib.rs:50–54 — to any contract they deploy) converts the pool into an open faucet: fund_loan() repeatedly to attacker addresses up to available liquidity, instantly, with absorb_loss() afterwards erasing the accounting trace.

Defense-in-depth requirements: the pool should enforce its own caps and sanity checks so a misbehaving upstream degrades gracefully instead of draining.

Ground Rules

  1. Read context/architecture-context.md, context/code-standards.md, context/progress-tracker.md in full
  2. Read liquidity-pool-contract/src/lib.rs in full and vendor-registry-contract/src/lib.rs is_active()
  3. Respect the existing require_creditline pattern — this issue adds layered defenses, it does not replace caller restriction

What To Build

  1. Per-ledger outflow cap: bound total fund_loan outflows within a single ledger to a configurable fraction of available liquidity, enforced with a rolling window reset.
  2. Single-recipient concentration cap: track cumulative funded-per-merchant and reject transfers exceeding a configurable ceiling.
  3. Optional vendor cross-check: accept an optional registered vendor-registry address in initialize()/admin setter and, when present, require is_active(merchant) before transfer — keeping backward compatibility when unset.
  4. Emit granular events (already exists: emit_loan_funded) extended with merchant and remaining-cap fields for indexer monitoring.
  5. Tests proving caps hold under repeated calls, window resets work, admin setters are guarded, and honest single-loan flows are unaffected.

Files To Touch

  • contracts/liquidity-pool-contract/src/lib.rs
  • contracts/liquidity-pool-contract/src/storage.rs
  • contracts/liquidity-pool-contract/src/types.rs
  • contracts/liquidity-pool-contract/src/tests.rs
  • context/progress-tracker.md

Acceptance Criteria

  • Outflow and concentration caps enforced on-chain with tests
  • Optional vendor validation wired and tested in both enabled/disabled modes
  • Honest flows unchanged (regression tests)
  • Suite green

Mandatory Checks Before Opening PR

Standard Grantfox checklist applies.

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 rewardThird CampaignCampaign: Third Campaignhardhelp wantedExtra attention is needed

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions