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
User calls deposit(assets, receiver, from, operator) with their underlying tokens
The operator (the address authorizing the deposit) is require_auth'd and the vault verifies it is not paused
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)
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
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
User calls redeem(shares, receiver, owner, operator) or withdraw(assets, receiver, owner, operator)
The operator is authorized and the vault verifies it is not paused
The vault computes the asset payout against current NAV:
assets_out = shares * (total_assets + 1) / (total_supply + 10^offset)
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
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
