Deposit amount of any whitelisted deposit asset and receive share tokens.
Multi-asset v2: if deposit_assets list is configured, any asset in that
list is accepted. Non-base assets are oracle-priced to compute the
base-asset-denominated deposit value used for share minting.
Legacy mode (no portfolio_assets configured): only base_asset accepted.
Flow (dHedge V2 model):
amount of asset from from into vault.deposit_value = oracle.price(asset) Γ amount (base-asset terms).total_shares = deposit_value Γ total_supply / nav_before.Number of share tokens minted to from.
VaultError::Paused]VaultError::InvalidAmount]VaultError::AssetNotInPortfolio] β not in deposit asset listVaultError::DepositCapExceeded]fn deposit(
env: soroban_sdk::Env,
amount: i128,
from: soroban_sdk::Address,
asset: soroban_sdk::Address,
min_shares_out: i128,
) -> i128
Return the current NAV (vault base-asset balance + strategy values).
fn get_nav(env: soroban_sdk::Env) -> i128
fn get_name(env: soroban_sdk::Env) -> soroban_sdk::String
Burn share_amount of share tokens and receive a proportional fraction
of every asset in the vault (multi-asset dHedge V2 withdrawal model).
Flow:
fraction Γ balance to to.withdraw_fraction(vault, num, den, to).
Guards send their underlying tokens directly to to.Legacy mode (no portfolio_assets configured): same proportional logic applied to base_asset only, plus legacy single-asset strategies.
Exit fee fraction stays in the vault β not transferred to manager.
Total value withdrawn in base-asset terms (for events / slippage check).
VaultError::Paused]VaultError::InvalidAmount]VaultError::InsufficientShares]VaultError::CooldownActive]fn withdraw(
env: soroban_sdk::Env,
share_amount: i128,
from: soroban_sdk::Address,
to: soroban_sdk::Address,
min_base_out: i128,
) -> i128
Return the vault admin address.
fn get_admin(env: soroban_sdk::Env) -> soroban_sdk::Address
fn is_paused(env: soroban_sdk::Env) -> bool
Add an allowlisted member for private-pool deposits. Admin only.
fn add_member(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
member: soroban_sdk::Address,
)
Execute any authorised operation through a whitelisted guard contract.
This is the single unified dispatch entry-point replacing the old
invest / unwind / invest_lp / unwind_lp / execute_trade
family of functions.
caller must be the registered manager or trader β both may call.guard must appear in ActiveGuards.fn_name must appear in AuthorizedOps(guard).args. This ensures the guard can only
act on behalf of the vault β the caller cannot substitute a different
source address.guard.<fn_name>(vault, argsβ¦) is called directly by name.max_loss_bps the
transaction reverts (TVL guard).caller β Manager or trader address (must require_auth()).guard β Active guard contract address.fn_name β Guard function name to call (e.g. "supply", `"swapfn execute_op(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
guard: soroban_sdk::Address,
fn_name: soroban_sdk::Symbol,
args: soroban_sdk::Vec,
) -> soroban_sdk::Val
fn get_trader(env: soroban_sdk::Env) -> soroban_sdk::Address
Register the oracle contract used to price strategy positions. Manager only.
Once set, nav() will call oracle.get_price(price_token) for each
strategy that has a price token registered via [set_strategy_oracle_token].
VaultError::NotAdmin]fn set_oracle(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
oracle: soroban_sdk::Address,
)
Transfer the trader role to a new address. Manager only.
After this call only new_trader can call execute_trade.
VaultError::NotManager]fn set_trader(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
new_trader: soroban_sdk::Address,
)
Return the vault's factory reference address.
fn get_factory(env: soroban_sdk::Env) -> Option
fn get_manager(env: soroban_sdk::Env) -> soroban_sdk::Address
Transfer the manager role to a new address.
The vault admin or the current manager may authorise this call.
VaultError::NotManager]fn set_manager(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
new_manager: soroban_sdk::Address,
)
Return the fee-recipient treasury address.
fn get_treasury(env: soroban_sdk::Env) -> soroban_sdk::Address
Return the PnL breakdown for a user.
All values are in base-asset terms (PRICE_PRECISION-scaled where noted).
fn get_user_pnl(env: soroban_sdk::Env, user: soroban_sdk::Address) -> UserPnLReport
Perform the one-time anti-inflation seed deposit. Factory only.
The factory calls this after transferring seed_amount of base_asset
directly into the vault. This function mints seed_amount shares to
a burn address (all-zeros), ensuring total_supply > 0 from day one
and eliminating the first-depositor inflation attack.
Can only be called once per vault (enforced via SeedDeposited flag).
VaultError::SeedAlreadyDeposited]fn seed_deposit(env: soroban_sdk::Env, caller: soroban_sdk::Address, seed_amount: i128)
Set or update the treasury address. Admin only.
fn set_treasury(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
treasury: soroban_sdk::Address,
)
fn is_ops_paused(env: soroban_sdk::Env) -> bool
Remove an allowlisted member for private-pool deposits. Admin only.
fn remove_member(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
member: soroban_sdk::Address,
)
Initialize the vault. Runs atomically at deployment via CreateContract.
VaultError::InvalidAmount] β if any fee exceeds its cap.fn __constructor(env: soroban_sdk::Env, params: VaultParams)
fn get_base_asset(env: soroban_sdk::Env) -> soroban_sdk::Address
Pause deposits and withdrawals. Admin only.
fn pause_deposits(env: soroban_sdk::Env, caller: soroban_sdk::Address)
Return the current share price (NAV / total_supply) in PRICE_PRECISION units.
fn get_share_price(env: soroban_sdk::Env) -> i128
fn get_share_token(env: soroban_sdk::Env) -> soroban_sdk::Address
fn is_private_pool(env: soroban_sdk::Env) -> bool
Set the vault's maximum deposit cap (in base-asset units). Manager only.
A cap of 0 means uncapped (no limit). Set to a positive value to
prevent the vault from accepting deposits beyond that NAV ceiling.
VaultError::NotManager]fn set_deposit_cap(env: soroban_sdk::Env, caller: soroban_sdk::Address, cap: i128)
Register a strategy guard contract as active for this vault.
VaultError::NotManager]VaultError::GuardNotAuthorized] β not in factory whitelistVaultError::GuardAlreadyActive]VaultError::TooManyGuards]fn add_active_guard(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
guard: soroban_sdk::Address,
)
Return the manager's display name, or None if not set.
fn get_manager_name(env: soroban_sdk::Env) -> Option
Pause manager operations (execute_op). Admin only.
fn pause_operations(env: soroban_sdk::Env, caller: soroban_sdk::Address)
Update the exit fee. Manager only. Capped at [MAX_ENTRY_EXIT_FEE_BPS].
VaultError::NotManager] / [VaultError::InvalidAmount]fn set_exit_fee_bps(env: soroban_sdk::Env, caller: soroban_sdk::Address, bps: u32)
Set or update the manager's display name. Admin or manager only.
fn set_manager_name(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
name: soroban_sdk::String,
)
Set the maximum NAV loss allowed per manager operation. Manager only.
After every invest, unwind, invest_lp, unwind_lp, and
execute_trade the vault checks:
nav_after β₯ nav_before Γ (1 β max_loss_bps / 10_000)
If the check fails the transaction reverts with
[VaultError::TvlGuardTripped], preventing the manager from silently
draining TVL through repeated high-slippage operations.
A typical production value is 100 (1 % per transaction).
Setting 0 is rejected because a zero tolerance disables the NAV-loss
guard entirely, which would allow a manager to execute a damaging
operation without any on-chain slippage protection. Use the default
of [DEFAULT_MAX_LOSS_BPS] (1 000 bps = 10 %) or another non-zero value.
VaultError::NotManager]VaultError::InvalidAmount] β if bps == 0 or bps > FEE_DENOMINATORfn set_max_loss_bps(env: soroban_sdk::Env, caller: soroban_sdk::Address, bps: u32)
Update the annual management fee. Manager only. Capped at [MAX_MGMT_FEE_BPS].
The timestamp is NOT reset; accrued-since-last-collection will use the old rate for the elapsed period and the new rate going forward (standard fund accounting).
VaultError::NotManager] / [VaultError::InvalidAmount]fn set_mgmt_fee_bps(env: soroban_sdk::Env, caller: soroban_sdk::Address, bps: u32)
Update the performance fee. Manager only. Capped at [MAX_PERF_FEE_BPS].
VaultError::NotManager] / [VaultError::InvalidAmount]fn set_perf_fee_bps(env: soroban_sdk::Env, caller: soroban_sdk::Address, bps: u32)
Enable or disable private-pool mode. Admin only.
fn set_private_pool(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
is_private: bool,
)
Unpause deposits and withdrawals. Admin only.
fn unpause_deposits(env: soroban_sdk::Env, caller: soroban_sdk::Address)
Add an asset to the deposit-allowed list. Must be in portfolio assets.
VaultError::NotManager]VaultError::AssetNotInPortfolio]VaultError::AssetAlreadyPresent]fn add_deposit_asset(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
asset: soroban_sdk::Address,
)
Return the list of active guard contracts for this vault.
fn get_active_guards(env: soroban_sdk::Env) -> soroban_sdk::Vec
fn is_member_allowed(env: soroban_sdk::Env, member: soroban_sdk::Address) -> bool
Update the entry fee. Manager only. Capped at [MAX_ENTRY_EXIT_FEE_BPS].
VaultError::NotManager] / [VaultError::InvalidAmount]fn set_entry_fee_bps(env: soroban_sdk::Env, caller: soroban_sdk::Address, bps: u32)
fn get_announced_fees(env: soroban_sdk::Env) -> AnnouncedFees
Return the authorized function names for a guard.
fn get_authorized_ops(
env: soroban_sdk::Env,
guard: soroban_sdk::Address,
) -> soroban_sdk::Vec
Return the vault's current deposit asset list.
fn get_deposit_assets(env: soroban_sdk::Env) -> soroban_sdk::Vec
Return portfolio assets currently included in bounded NAV scans.
fn get_tracked_assets(env: soroban_sdk::Env) -> soroban_sdk::Vec
Set the authorized function names for a guard contract. Manager only.
Only the listed function names may be dispatched by the trader through
execute_op for the given guard.
fn set_authorized_ops(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
guard: soroban_sdk::Address,
ops: soroban_sdk::Vec,
)
Synchronize a portfolio asset into or out of the bounded NAV index.
This is permissionless so unsolicited token transfers to the vault can be included in NAV without requiring a manager action. The asset must already be configured as a portfolio asset.
fn sync_asset_balance(env: soroban_sdk::Env, asset: soroban_sdk::Address)
Unpause manager operations (execute_op). Admin only.
fn unpause_operations(env: soroban_sdk::Env, caller: soroban_sdk::Address)
Add an asset to the vault's portfolio asset list.
The asset must be in the factory's global authorized asset list. The oracle must be set before adding non-base assets (needed for NAV).
VaultError::NotManager]VaultError::AssetNotAuthorized] β not in factory whitelistVaultError::AssetAlreadyPresent] β already in portfolioVaultError::TooManyAssets] β MAX_PORTFOLIO_ASSETS reachedfn add_portfolio_asset(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
asset: soroban_sdk::Address,
)
Commit the latest announced fee-increase schedule once delay elapsed.
fn commit_fee_increase(env: soroban_sdk::Env, caller: soroban_sdk::Address)
Return active guards that currently contribute non-zero strategy value to NAV and withdrawals.
fn get_position_guards(env: soroban_sdk::Env) -> soroban_sdk::Vec
Remove a strategy guard contract from the active list.
VaultError::NotManager]VaultError::StrategyNotWhitelisted] β not in active listVaultError::GuardHasActivePosition] β guard still holds positionsfn remove_active_guard(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
guard: soroban_sdk::Address,
)
Synchronize an active guard into or out of the bounded position index.
This is permissionless so strategy value changes caused by external yield, rewards, or testnet repair scripts can be reflected without forcing a full active-guard scan in every user action.
fn sync_guard_position(env: soroban_sdk::Env, guard: soroban_sdk::Address)
Permissionlessly settle pending management and performance fees.
Fees are deterministic from vault state: the caller cannot choose the fee amount, recipient, NAV, or timestamp. This lets keepers, users, UIs, or the manager sync fee accounting even when no deposit/withdraw occurs.
Returns the number of fee shares minted to the treasury.
fn collect_pending_fees(env: soroban_sdk::Env) -> i128
Return the vault's current portfolio asset list.
fn get_portfolio_assets(
env: soroban_sdk::Env,
) -> soroban_sdk::Vec
Remove an asset from the deposit-allowed list.
Does not remove from portfolio assets. No constraint on balance.
VaultError::NotManager]VaultError::AssetNotInPortfolio] β not in deposit listfn remove_deposit_asset(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
asset: soroban_sdk::Address,
)
Announce a fee-increase schedule that can be committed after timelock.
fn announce_fee_increase(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
entry_fee_bps: u32,
exit_fee_bps: u32,
mgmt_fee_bps: u32,
perf_fee_bps: u32,
)
Cancel any previously announced fee-increase schedule.
fn renounce_fee_increase(env: soroban_sdk::Env, caller: soroban_sdk::Address)
fn get_exit_cooldown_secs(env: soroban_sdk::Env) -> u64
fn is_value_guard_enabled(env: soroban_sdk::Env) -> bool
Remove an asset from the vault's portfolio asset list.
VaultError::NotManager]VaultError::AssetNotInPortfolio]VaultError::AssetHasBalance] β vault still holds this tokenVaultError::AssetInUseByGuard] β a guard has an active position using this assetfn remove_portfolio_asset(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
asset: soroban_sdk::Address,
)
Set per-user exit cooldown in seconds. Manager only.
fn set_exit_cooldown_secs(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
secs: u64,
)
Toggle same-ledger value-manipulation guard. Manager only.
fn set_value_guard_enabled(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
enabled: bool,
)
fn share_transfers_enabled(env: soroban_sdk::Env) -> bool
fn pnl_tracking_is_accurate(env: soroban_sdk::Env) -> bool
fn get_exit_remaining_cooldown(
env: soroban_sdk::Env,
user: soroban_sdk::Address,
) -> u64
Enable or disable transferable vault shares. Admin only.
Shares are non-transferable by default so exit cooldown and per-user PnL accounting remain accurate. If transfers are enabled, cooldown becomes a same-address friction control and PnL becomes informational unless a future transfer-aware accounting design is added.
fn set_share_transfers_enabled(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
enabled: bool,
)
fn exit_cooldown_is_hard_control(env: soroban_sdk::Env) -> bool