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
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.
Description
The vault accepts deposits up to
i128::MAX:deposit(contracts/vault/src/lib.rs:572) only checksamount > 0, the allowlist, andmin_deposit, then doesmeta.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
max_vault_balance: i128in instance storage. Disabled by default (0or unset means "no cap").set_max_vault_balance(caller: Address, max_balance: i128) -> Result<(), VaultError>(rejectmax_balance < 0).get_max_vault_balance(env: Env) -> i128(returns0/i128::MAXsentinel when unset — pick one and document it).deposit: after computing the prospective new balance, reject when it exceeds the cap with a newVaultError::ExceedsMaxVaultBalance. The check must run before the token transfer (a Check), consistent with the CEI structure documented ondeposit.init'sinitial_balanceagainst a cap if one is supplied in the same call (document the chosen ordering).set_max_vault_balanceevent(old, new)consistent with theset_max_deductevent pattern (contracts/vault/src/lib.rs:489).Non-functional / repo conventions
checked_addfor the prospective-balance computation (already present atcontracts/vault/src/lib.rs:593).docs/interfaces/vault.json(new function + new error code),EVENT_SCHEMA.md,SECURITY.md(Vault-Specific Risks checklist), andcontracts/vault/STORAGE.md.Acceptance criteria
set_max_vault_balance/get_max_vault_balanceimplemented, owner-gated, with positive/zero validation.depositrejects deposits that would exceed the cap withVaultError::ExceedsMaxVaultBalance, before any transfer.set_max_vault_balanceevent emitted and documented inEVENT_SCHEMA.md.docs/interfaces/vault.json;SECURITY.mdandSTORAGE.mdupdated.scripts/coverage.sh).Suggested execution
1. Fork the repo and create a branch
2. Implement changes
contracts/vault/src/lib.rs— new storage key, setter/view,VaultError::ExceedsMaxVaultBalance, enforcement indeposit, and the event.docs/interfaces/vault.json,EVENT_SCHEMA.md,SECURITY.md,contracts/vault/STORAGE.md.3. Write/extend tests in
contracts/vault/src/test.rs(ortest_setter_validation.rs):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.shExample commit message
Guidelines
scripts/coverage.sh), CI-enforced via.github/workflows/coverage.yml.///docs on the new entrypoints; keepEVENT_SCHEMA.md,SECURITY.md, anddocs/interfaces/vault.jsonauthoritative.