For the complete documentation index, see llms.txt. This page is also available as Markdown.

Stellar Vaults

Upshift Stellar Vaults bring the same institutional-grade vault infrastructure to the Stellar ecosystem. Built as a native Soroban smart contract on top of OpenZeppelin's stellar-tokens library, Stellar vaults offer fast, non-custodial yield generation transparently on-chain, with actively managed strategies.

Contract

AugustVault (upgradeable Soroban contract)

Framework

Soroban + OpenZeppelin stellar-tokens (ERC-4626 pattern)

Deposit Asset

Any SEP-41 token (native SAC or custom contract)

Share Token

Fungible share token issued by the vault contract itself

Redemptions

Instant, no lock-ups (bounded by the vault's local balance)

Vault Architecture

Each Upshift Stellar vault is a deployment of the AugustVault Soroban contract. The constructor binds the vault to a single underlying token, sets the share token's name and symbol, fixes a virtual decimals offset (used for inflation-attack protection).

The contract follows the ERC-4626 Tokenized Vault Standard. Share-pricing logic is reimplemented on top of OpenZeppelin's primitives so that NAV reflects not only the tokens physically held by the vault but also capital that has been deployed off-vault. total_assets, convert_to_shares, convert_to_assets, all four preview_* functions, and max_deposit / max_mint / max_withdraw / max_redeem On deployment the contract initializes:

  • Vault instance storage holding the asset address, share-token metadata, decimals offset, admin, operator, paused flag, deployed-assets aggregate, AUM limits, and the subaccount whitelist

  • The share token issued by the vault itself (the same contract that custodies the underlying asset is also the share issuer)

  • An on-chain TTL bump on every state-changing entrypoint so the instance never expires under normal traffic; a permissionless extend_ttl() is provided as a fallback for quiet periods

Deposit Flow

  1. User calls deposit(assets, receiver, from, operator) with their underlying tokens

  2. The operator (the address authorizing the deposit) is require_auth'd and the vault verifies it is not paused

  3. The vault converts the asset amount to shares against current NAV using ERC-4626 math with the configured decimals offset:

shares_minted = assets * (total_supply + 10^offset) / (total_assets + 1)

  1. The underlying tokens are pulled from from into the vault's contract balance, share tokens are minted to receiver, and the contract's instance TTL is extended

  2. A standard ERC-4626 Deposit event is emitted

mint(shares, receiver, from, operator) is the symmetric entrypoint: callers specify exact shares to mint and the vault computes the assets required (rounding up).

Redemption Flow

  1. User calls redeem(shares, receiver, owner, operator) or withdraw(assets, receiver, owner, operator)

  2. The operator is authorized and the vault verifies it is not paused

  3. The vault computes the asset payout against current NAV:

assets_out = shares * (total_assets + 1) / (total_supply + 10^offset)

  1. The redemption is capped at the vault's local token balance — max_withdraw and max_redeem are the minimum of the user's entitlement and the assets physically present in the vault. If the user's pro-rata share exceeds what's currently liquid, they must wait for the operator to return capital

  2. Share tokens are burned from owner and the underlying is transferred to receiver

A standard ERC-4626 Withdraw event is emitted

Vault Share Price Mechanics

The share token price is fully derivable on-chain:

share_price = total_assets / total_supply

total_assets = local_balance

+ Σ IStrategy::get_balance() over all Strategy subaccounts

+ deployed_assets

  • local_balance: the vault contract's own balance of the underlying token. Fully verifiable on-chain via a direct token.balance(vault) read

  • Σ get_balance(): each Strategy subaccount self-reports its full position (idle + deployed) on every NAV query. A strategy that traps, returns a non-i128, or returns a negative value causes the vault to halt the calculation with a StrategyUnreachable (or NegativeStrategyBalance) error rather than silently zero out — under-reporting NAV would let a buggy strategy crash the share price

  • deployed_assets: the aggregate of capital attributed to Wallet subaccounts (the value the operator has moved off-vault to addresses whose balances the vault cannot trustlessly read). The invariant Σ WalletNetDeployed == deployed_assets is maintained by every routine path; get_wallet_deployed_assets() exposes the Σ side for monitoring

  • total_supply: the share token's supply, readable directly from the vault contract

Any Soroban contract can compute the share price by calling total_assets() and total_supply() on the vault. No external oracle is required.

The vault uses ERC-4626's virtual-shares-and-virtual-assets formula with a configurable decimals offset (minimum 3, maximum 10, default 6).

Refer to this link for Upshift Stellar public repo.

Last updated