Risk and Security Model
AUM Change Limits
The vault enforces both per-call and cumulative-window rate limits on every move of deployed_assets. Limits are asymmetric (increases and decreases are configured separately), denominated in basis points of the pre-move deployed_assets, and applied by the same shared helper used by every aggregate-mutating function (update_deployed_assets, update_wallet_deployed, update_wallet_deployed_batch, and remove_wallet_and_reconcile).
Per-call limits:
set_aum_limits(increase_bps, decrease_bps)caps how much deployed_assets can change in a single call. Decreases are typically configured tighter than increasesCumulative window limits: s
et_aum_window_limits(window_duration, cumulative_increase_bps, cumulative_decrease_bps)caps the sum of changes within a fixed tumbling window (minimum 1 hour, maximum 7 days; default 24 hours). The window resets entirely on expiry, and changing limits resets it immediately so stale counters never interact with new configuration. Cumulative limits are measured against a base_deployed snapshot captured at window start, not the live valueZero-boundary behavior: when deployed_assets == 0, percentage-based limits are undefined and skipped. The admin controls risk at this boundary through operator key management and the subaccount whitelist
Calls that exceed the per-call limit revert with AumChangeExceedsLimit; calls that exceed the cumulative limit revert with AumCumulativeChangeExceedsLimit.
Pause Mechanism
The admin can instantly halt user-facing operations via pause(). While paused:
deposit,mint,withdraw, andredeemall revert;max_deposit,max_mint,max_withdraw, andmax_redeemall return 0add_subaccountreverts (no new strategies or wallets can be onboarded mid-incident)Operator increases to deployed_assets (and any per-wallet tracker increase, including a single positive entry inside an otherwise net-negative update_wallet_deployed_batch) revert with VaultPaused. Decreases remain permitted so the operator can recognize losses and pull capital back
withdraw_from_subaccountcontinues to operate so the curator can return capitalThe admin can unpause at any time via unpause()
The asymmetry is deliberate: a pause must not let a compromised operator inflate share prices by booking phantom gains, but it must still allow loss recognition and capital recovery during an emergency.
Admin Transfer Safety
Admin role transfers use a two-step nomination process with an explicit deadline:
Current admin calls
propose_admin(new_admin, deadline). This records a pending nominee and an absolute ledger-timestamp expiryNominated admin calls
accept_admin(). The pending nominee must require_auth and the call must arrive before deadline
If the nomination expires (AdminProposalExpired) it must be re-issued. The current admin can cancel a pending proposal at any time via cancel_admin_proposal. Self-nominations (new_admin == admin) and zero/past deadlines are rejected at proposal time. This prevents accidental or malicious ownership changes; the deadline also prevents a long-lived pending nomination from being exploited later.
Operator changes are a single-step admin action via set_operator.
Wallet-Attribution Invariant
For Wallet subaccounts the vault maintains a per-wallet WalletNetDeployed tracker that is moved in lockstep with the aggregate deployed_assets on every routine operator action.
Σ WalletNetDeployed[w] == deployed_assets
The invariant is enforced by deposit_to_subaccount, withdraw_from_subaccount, update_wallet_deployed, update_wallet_deployed_batch, and remove_wallet_and_reconcile. The vault also enforces a per-wallet over-pull guard: withdraw_from_subaccount rejects pulls that exceed the wallet's tracker (WalletOverWithdraw), so an operator cannot silently consume another wallet's share of deployed_assets while informally recognizing a gain or recovering dust.
Two escape hatches can break the invariant by design — update_deployed_assets (aggregate-only) and seed_wallet_net_deployed (per-wallet, admin-only, with a WalletSeedBelowTracker guard that prevents zeroing a tracker without a matching aggregate write-down). Both are reserved for emergency storage corrections; off-chain monitoring compares get_deployed_assets() against get_wallet_deployed_assets() and alerts on divergence.
Contract Upgradeability
The vault implements OpenZeppelin's UpgradeableInternal trait, gated on require_admin.
Audit History
A separate audit has been conducted for the Stellar contracts and is available here.
Last updated
