Skip to content

fix(lp): validate fund_loan recipients and cap outflows & merchant exposure - #111

Merged
EmeditWeb merged 2 commits into
StepFi-app:mainfrom
Emeka-12:fix/106-lp-fund-loan-recipient-validation-caps
Aug 30, 2026
Merged

fix(lp): validate fund_loan recipients and cap outflows & merchant exposure#111
EmeditWeb merged 2 commits into
StepFi-app:mainfrom
Emeka-12:fix/106-lp-fund-loan-recipient-validation-caps

Conversation

@Emeka-12

Copy link
Copy Markdown
Contributor

Closes #106

Summary

liquidity_pool::fund_loan previously paid any merchant address the registered creditline supplied, with no independent recipient verification and no bound on how fast a misbehaving or compromised creditline could drain pool liquidity. This PR layers defense-in-depth guards on top of the existing require_creditline caller restriction (kept):

  1. Per-ledger outflow cap — admin configurable set_outflow_cap_bps (1..=10_000 bps of available liquidity; 0 = disabled). Cumulative fund_loan outflows within a ledger are bounded, with a rolling window keyed to the ledger sequence that resets automatically on ledger change.
  2. Single-recipient concentration cap — admin configurable set_merchant_exposure_cap (token units; 0 = disabled) bounds cumulative funding to any one merchant. Cumulative exposure is tracked regardless (persistent with TTL) so admins can monitor get_merchant_funded before enabling a ceiling.
  3. Optional vendor cross-checkinitialize() accepts an optional vendor_registry address (also settable post-init via admin-only set_vendor_registry). When set, fund_loan requires the recipient to be an active (approved) vendor via the registry's is_active(), fail-closed (invocation errors block funding). Unset = backward-compatible legacy behavior.

Changes

  • contracts/liquidity-pool-contract/src/lib.rs — guards, admin setters, getters, internal vendor_is_active cross-check
  • contracts/liquidity-pool-contract/src/storage.rs — outflow window, exposure, vendor-registry storage + TTL
  • contracts/liquidity-pool-contract/src/types.rs — default cap constants (targeted, zero, default-disabled)
  • contracts/liquidity-pool-contract/src/errors.rsOutflowCapExceeded, MerchantExposureCapExceeded, VendorNotActive, InvalidCap
  • contracts/liquidity-pool-contract/src/events.rsLOAN_FUNDED now carries (merchant, amount, outflow_remaining, merchant_remaining); new CAPSUPD / VREGUPD events
  • contracts/liquidity-pool-contract/src/tests.rs — 16 new tests: cap enforcement under repeated calls, rolling window reset, guarded/validated admin setters, per-recipient independence, enabled/disabled vendor modes, legacy-restore, event payload
  • contracts/creditline-contract/src/tests.rs — updated for new initialize signature
  • scripts/deploy-testnet.sh — passes --vendor_registry, opt-in OUTFLOW_CAP_BPS / MERCHANT_EXPOSURE_CAP env-gated caps
  • context/progress-tracker.md — documented entry

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 — cargo test --locked: 401 passing (125 liquidity-pool, 143 creditline, 20 parameters, 60 reputation, 26 vendor-registry, 27 vouching)

@Emeka-12
Emeka-12 requested a review from EmeditWeb as a code owner August 30, 2026 13:19
…n-recipient-validation-caps

# Conflicts:
#	context/progress-tracker.md

@EmeditWeb EmeditWeb left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Automated Audit: solves

@Emeka-12 Excellent work, thank you! 🎉

The PR GENUINELY solves Issue #106. The root cause was fund_loan() honoring any merchant address with zero independent verification and no caps. The PR layers defense-in-depth: (1) optional vendor registry cross-check (fail-closed when set, skipped when unset for backward compatibility), (2) per-ledger outflow cap with rolling window reset keyed to ledger sequence, (3) cumulative merchant exposure cap. All three address the root causes: zero independent verification, unbounded drain rate, and unbounded concentration. Backward compatibility is maintained (default caps = 0 = disabled), all admin setters have proper auth guards, and 16 new tests plus 401 total passing tests cover the new behavior. PR title/description are substantive and of good quality. CI is intact and all 401 tests pass.


CI checks: ✅ PASSED: Build and Test Contracts
Merge conflicts: ✅ none — but the PR is blocked (failing/missing required checks or reviews).

Audited by stepfi-audit-bot 🤖

@EmeditWeb
EmeditWeb merged commit b04f761 into StepFi-app:main Aug 30, 2026
1 check 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

Development

Successfully merging this pull request may close these issues.

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

2 participants