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
- Read context/architecture-context.md, context/code-standards.md, context/progress-tracker.md in full
- Read liquidity-pool-contract/src/lib.rs in full and vendor-registry-contract/src/lib.rs
is_active()
- Respect the existing
require_creditline pattern — this issue adds layered defenses, it does not replace caller restriction
What To Build
- 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.
- Single-recipient concentration cap: track cumulative funded-per-merchant and reject transfers exceeding a configurable ceiling.
- 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.
- Emit granular events (already exists:
emit_loan_funded) extended with merchant and remaining-cap fields for indexer monitoring.
- 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
Mandatory Checks Before Opening PR
Standard Grantfox checklist applies.
Problem
fund_loan()in liquidity-pool-contract (lib.rs:220–256) honorstransfer(pool → merchant, amount)for whichevermerchantaddress 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, withabsorb_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
is_active()require_creditlinepattern — this issue adds layered defenses, it does not replace caller restrictionWhat To Build
fund_loanoutflows within a single ledger to a configurable fraction of available liquidity, enforced with a rolling window reset.initialize()/admin setter and, when present, requireis_active(merchant)before transfer — keeping backward compatibility when unset.emit_loan_funded) extended with merchant and remaining-cap fields for indexer monitoring.Files To Touch
contracts/liquidity-pool-contract/src/lib.rscontracts/liquidity-pool-contract/src/storage.rscontracts/liquidity-pool-contract/src/types.rscontracts/liquidity-pool-contract/src/tests.rscontext/progress-tracker.mdAcceptance Criteria
Mandatory Checks Before Opening PR
Standard Grantfox checklist applies.