Skip to content

Add a configurable maximum vault balance (deposit cap) to bound custody exposure #404

@greatest0fallt1me

Description

@greatest0fallt1me

Description

The vault accepts deposits up to i128::MAX: deposit (contracts/vault/src/lib.rs:572) only checks amount > 0, the allowlist, and min_deposit, then does meta.balance.checked_add(amount). There is no upper bound on the tracked balance. For a prepaid-USDC custody contract, an unbounded balance is an unnecessary concentration of funds at risk. The vault already has a per-call ceiling (max_deduct) and a per-deposit floor (min_deposit) — a per-vault ceiling is the missing third control.

This issue adds an opt-in, owner-configurable max_vault_balance: deposits that would push the tracked balance above the cap are rejected. Disabled by default so existing deployments are unaffected.

Requirements and context

Functional

  • Persist a new max_vault_balance: i128 in instance storage. Disabled by default (0 or unset means "no cap").
  • Add an owner-only setter and a view:
    • set_max_vault_balance(caller: Address, max_balance: i128) -> Result<(), VaultError> (reject max_balance < 0).
    • get_max_vault_balance(env: Env) -> i128 (returns 0 / i128::MAX sentinel when unset — pick one and document it).
  • Enforce in deposit: after computing the prospective new balance, reject when it exceeds the cap with a new VaultError::ExceedsMaxVaultBalance. The check must run before the token transfer (a Check), consistent with the CEI structure documented on deposit.
  • Optionally guard init's initial_balance against a cap if one is supplied in the same call (document the chosen ordering).
  • Emit a set_max_vault_balance event (old, new) consistent with the set_max_deduct event pattern (contracts/vault/src/lib.rs:489).

Non-functional / repo conventions

  • Use checked_add for the prospective-balance computation (already present at contracts/vault/src/lib.rs:593).
  • Backward compatible: the cap is opt-in; existing tests must pass unchanged.
  • Update docs/interfaces/vault.json (new function + new error code), EVENT_SCHEMA.md, SECURITY.md (Vault-Specific Risks checklist), and contracts/vault/STORAGE.md.

Acceptance criteria

  • set_max_vault_balance / get_max_vault_balance implemented, owner-gated, with positive/zero validation.
  • deposit rejects deposits that would exceed the cap with VaultError::ExceedsMaxVaultBalance, before any transfer.
  • Cap disabled by default; existing deposit tests pass unchanged.
  • set_max_vault_balance event emitted and documented in EVENT_SCHEMA.md.
  • New error code added to docs/interfaces/vault.json; SECURITY.md and STORAGE.md updated.
  • Line coverage stays >= 95% (scripts/coverage.sh).

Suggested execution

1. Fork the repo and create a branch

git checkout -b feature/vault-max-balance-cap

2. Implement changes

  • contracts/vault/src/lib.rs — new storage key, setter/view, VaultError::ExceedsMaxVaultBalance, enforcement in deposit, and the event.
  • Update docs/interfaces/vault.json, EVENT_SCHEMA.md, SECURITY.md, contracts/vault/STORAGE.md.

3. Write/extend tests in contracts/vault/src/test.rs (or test_setter_validation.rs):

  • deposit up to exactly the cap succeeds; one stroop over is rejected;
  • cap unset preserves unbounded behavior;
  • non-owner setter call rejected;
  • negative cap rejected;
  • a sequence of deposits that cumulatively reaches the cap is bounded correctly.

4. Test and commit

cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --workspace
./scripts/check-wasm-size.sh
./scripts/coverage.sh

Example commit message

feat(vault): add configurable max_vault_balance deposit cap

Guidelines

  • Coverage must stay >= 95% (scripts/coverage.sh), CI-enforced via .github/workflows/coverage.yml.
  • NatSpec-style /// docs on the new entrypoints; keep EVENT_SCHEMA.md, SECURITY.md, and docs/interfaces/vault.json authoritative.
  • Backward compatible: cap is opt-in and disabled by default.
  • Timeframe: 96 hours.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions