Skip to main content

Vault Overview

The vault is an ERC-4626 style tokenized vault built on OpenZeppelin Stellar Contracts (stellar-tokens::vault). It is modeled on the ERC-4626 interface but ported to Soroban, not a direct implementation of the Ethereum standard. It holds a single collateral token (e.g., USDC) and issues share tokens to liquidity providers. The vault acts as the counterparty to all trading positions: it absorbs losses from traders and pays out profits.

For standard vault behavior (deposit, withdraw, mint, redeem, share-price math, decimals offset, inflation-attack protection), refer to the OpenZeppelin Fungible Token Vault documentation. This page documents only Zenex-specific extensions and design decisions.

Deposit Lock

Every deposit or mint adds a per-user lock that prevents the receiver from withdrawing, redeeming, or transferring their newly-minted shares until lock_time has elapsed. The lock duration is set globally at vault construction. See Deposit Lock for the full lock semantics, transfer-side behavior, and unlock-time computation.

Minimum Deposit

A min_deposit: i128 floor (in asset token decimals) is enforced on every deposit and mint, rejecting calls below it with BelowMinDeposit. It exists to prevent a hostage-taking attack on the deposit lock. Because deposit takes a separate receiver argument and each new deposit resets the receiver's lock timestamp, an attacker without a floor could repeatedly dust-deposit one token unit into a victim's address, perpetually extending the victim's lock and trapping their shares. Requiring each deposit to cross a meaningful floor makes the attack uneconomic. The floor is set at construction and is not mutable post-deployment. Setting it to 0 disables the check.

Interaction with Trading

Token Flow

DirectionWhenMechanism
Fees to VaultEvery trade (open/close/fill)token.transfer(trading, vault, fee_amount)
Collateral to VaultTrader losstoken.transfer(trading, vault, collateral_remainder)
Vault to TradingTrader profitstrategy_withdraw(trading, payout_amount)

The trading contract never holds vault shares. It interacts purely through strategy_withdraw and direct token transfers.

Strategy Withdraw

The only function added on top of the standard vault interface is strategy_withdraw, the privileged path the trading contract uses to pull collateral for profitable trader payouts. It bypasses share accounting (no shares are burned) and is gated to a single registered strategy address. See Strategy Withdraw for the authorization model and error codes.

Storage Layout

Instance Storage

KeyTypeDescription
LockTimeu64Global lock duration in seconds
StrategyAddressAuthorized trading contract
MinDepositi128Minimum asset amount per deposit/mint (token decimals); 0 disables the floor

Persistent Storage

KeyTypeDescription
DepositLock(Address)DepositLock { timestamp: u64, shares: i128 }Per-user deposit lock tracking the most recent deposit timestamp and the number of shares locked within that window

OpenZeppelin's token library manages share balances, allowances, and metadata in its own storage namespace.