From a29a559779c2388a5b0841d2104b62df6aa494bd Mon Sep 17 00:00:00 2001 From: Ozgun Ozerk Date: Tue, 21 Oct 2025 15:34:22 +0300 Subject: [PATCH 1/9] vault --- .../stellar-contracts/tokens/vault/vault.mdx | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 content/stellar-contracts/tokens/vault/vault.mdx diff --git a/content/stellar-contracts/tokens/vault/vault.mdx b/content/stellar-contracts/tokens/vault/vault.mdx new file mode 100644 index 00000000..7c2ee082 --- /dev/null +++ b/content/stellar-contracts/tokens/vault/vault.mdx @@ -0,0 +1,141 @@ +--- +title: Fungible Vault Token +--- + +[Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/fungible/extensions/vault) + +The Fungible Vault Token implements the ERC-4626 tokenized vault standard, enabling fungible tokens to represent shares in an underlying asset pool. This module allows users to deposit underlying assets in exchange for vault shares, and later redeem those shares for the underlying assets. The vault maintains a dynamic conversion rate between shares and assets based on the total supply of shares and total assets held by the vault contract. + +## Overview + +The [Vault](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/fungible/extensions/vault) module provides a complete implementation of tokenized vaults following the ERC-4626 standard. Vaults are useful for: + +- **Yield-bearing tokens**: Represent shares in a yield-generating strategy +- **Liquidity pools**: Pool assets together with automatic share calculation +- **Asset management**: Manage a pool of assets with proportional ownership +- **Wrapped tokens**: Create wrapped versions of tokens with additional features + +The vault automatically handles: +- Share-to-asset conversion with configurable precision +- Deposit and withdrawal operations +- Minting and redemption of shares +- Preview functions for simulating operations + +## Key Concepts + +### Shares vs Assets + +- **Assets**: The underlying token that the vault manages (e.g., USDC, XLM) +- **Shares**: The vault tokens that represent proportional ownership of the assets + +When assets are deposited into a vault, shares are minted to the depositor. The number of shares minted depends on the current exchange rate, which is determined by: + +``` +shares = (assets × totalSupply) / totalAssets +``` + +When withdrawing or redeeming, the reverse calculation applies: + +``` +assets = (shares × totalAssets) / totalSupply +``` + +### Virtual Decimals Offset + +The vault uses a "virtual decimals offset" to add extra precision to share calculations. This helps prevent rounding errors and improves the accuracy of share-to-asset conversions, especially when the vault has few assets or shares. + +The offset adds virtual shares and assets to the conversion formula: + +``` +shares = (assets × (totalSupply + 10^offset)) / (totalAssets + 1) +``` + +The offset is bounded to a maximum of 10 for security and UX considerations. Values higher than 10 provide minimal practical benefits and may cause overflow errors. + +## Rounding Behavior + +The vault implements specific rounding behavior to protect the vault from being drained: + +- **Deposit/Redeem**: Rounds **down** (depositor receives slightly fewer shares/assets) +- **Mint/Withdraw**: Rounds **up** (depositor provides slightly more assets/shares) + +This ensures the vault always retains a slight advantage in conversions, preventing precision attacks. + +| Operation | Input | Output | Rounding Direction | +| ---------- | ------ | ------ | ---------------------------- | +| `deposit` | assets | shares | Down (fewer shares) | +| `mint` | shares | assets | Up (more assets required) | +| `withdraw` | assets | shares | Up (more shares burned) | +| `redeem` | shares | assets | Down (fewer assets received) | + +## Security Considerations + +### Initialization + +The vault **MUST** be properly initialized before use: + +1. Call `Vault::set_asset(e, asset)` to set the underlying asset +2. Call `Vault::set_decimals_offset(e, offset)` to set the decimals offset +3. Initialize metadata with `Base::set_metadata()` + +These should typically be done in the constructor. Once set, the asset address and decimals offset are **immutable**. + + + +### Decimal Offset Limits + +The decimals offset is limited to a maximum of 10 to prevent: +- Overflow errors in calculations +- Excessive precision that provides no practical benefit +- Poor user experience with unnecessarily large numbers + +If a higher offset is required, a custom version of `set_decimals_offset()` must be implemented. + +### Inflation Attacks + +The virtual decimals offset helps protect against inflation attacks where an attacker: +1. Deposits 1 wei to get the first share +2. Donates a large amount directly to the vault +3. Causes subsequent depositors to receive 0 shares due to rounding + +The offset adds virtual shares and assets to the conversion formula, making such attacks economically infeasible. + +### Custom Authorization + +Custom authorization logic can be implemented as needed: + +```rust +fn deposit( + e: &Env, + assets: i128, + receiver: Address, + from: Address, + operator: Address, +) -> i128 { + // Custom authorization: only allow deposits from whitelisted addresses + if !is_whitelisted(e, &from) { + panic_with_error!(e, Error::NotWhitelisted); + } + + operator.require_auth(); + Vault::deposit(e, assets, receiver, from, operator) +} +``` + +## Compatibility and Compliance + +The vault module implements the ERC-4626 tokenized vault standard with one minor deviation (see Security Considerations). + +### ERC-4626 Deviation + +⚠️ **DEVIATION FROM ERC-4626**: The `query_asset()` function will panic if the asset address is not set, whereas ERC-4626 requires it to never revert. + +**Rationale**: Soroban doesn't have a "zero address" concept like EVM. Returning `Option
` would break ERC-4626 compatibility. + +**Mitigation**: Always initialize the vault properly in the constructor. Once initialized, `query_asset()` will never panic during normal operations. + +Aside from this deviation, the vault implementation for Soroban provides: + +- **Cross-ecosystem familiarity**: Ethereum developers will recognize the interface +- **Standard compliance**: Compatible with ERC-4626 tooling and integrations +- **Stellar optimizations**: Leverages Soroban's unique capabilities From 34d113383f1f97bc8e4d6fb0fc71f63de4930cf4 Mon Sep 17 00:00:00 2001 From: Ozgun Ozerk Date: Tue, 21 Oct 2025 16:12:03 +0300 Subject: [PATCH 2/9] formatting --- content/stellar-contracts/tokens/vault/vault.mdx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/content/stellar-contracts/tokens/vault/vault.mdx b/content/stellar-contracts/tokens/vault/vault.mdx index 7c2ee082..afcaa77d 100644 --- a/content/stellar-contracts/tokens/vault/vault.mdx +++ b/content/stellar-contracts/tokens/vault/vault.mdx @@ -28,7 +28,8 @@ The vault automatically handles: - **Assets**: The underlying token that the vault manages (e.g., USDC, XLM) - **Shares**: The vault tokens that represent proportional ownership of the assets -When assets are deposited into a vault, shares are minted to the depositor. The number of shares minted depends on the current exchange rate, which is determined by: +When assets are deposited into a vault, shares are minted to the depositor. +The number of shares minted depends on the current exchange rate, which is determined by: ``` shares = (assets × totalSupply) / totalAssets @@ -42,7 +43,9 @@ assets = (shares × totalAssets) / totalSupply ### Virtual Decimals Offset -The vault uses a "virtual decimals offset" to add extra precision to share calculations. This helps prevent rounding errors and improves the accuracy of share-to-asset conversions, especially when the vault has few assets or shares. +The vault uses a "virtual decimals offset" to add extra precision to share calculations. +This helps prevent rounding errors and improves the accuracy of share-to-asset conversions, +especially when the vault has few assets or shares. The offset adds virtual shares and assets to the conversion formula: @@ -50,7 +53,8 @@ The offset adds virtual shares and assets to the conversion formula: shares = (assets × (totalSupply + 10^offset)) / (totalAssets + 1) ``` -The offset is bounded to a maximum of 10 for security and UX considerations. Values higher than 10 provide minimal practical benefits and may cause overflow errors. +The offset is bounded to a maximum of 10 with both security and UX taken into account. +Values higher than 10 provide minimal practical benefits and may cause overflow errors. ## Rounding Behavior @@ -80,8 +84,6 @@ The vault **MUST** be properly initialized before use: These should typically be done in the constructor. Once set, the asset address and decimals offset are **immutable**. - - ### Decimal Offset Limits The decimals offset is limited to a maximum of 10 to prevent: @@ -91,7 +93,7 @@ The decimals offset is limited to a maximum of 10 to prevent: If a higher offset is required, a custom version of `set_decimals_offset()` must be implemented. -### Inflation Attacks +### Inflation (Precision) Attacks The virtual decimals offset helps protect against inflation attacks where an attacker: 1. Deposits 1 wei to get the first share From b531176f0a6789ebb2a6d149c27d2a83f938a3d2 Mon Sep 17 00:00:00 2001 From: Ozgun Ozerk Date: Tue, 21 Oct 2025 18:17:30 +0300 Subject: [PATCH 3/9] link --- content/stellar-contracts/index.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/content/stellar-contracts/index.mdx b/content/stellar-contracts/index.mdx index bd6f8ab6..2ed24270 100644 --- a/content/stellar-contracts/index.mdx +++ b/content/stellar-contracts/index.mdx @@ -15,6 +15,7 @@ for access control and contract management. * **[Fungible Tokens](/stellar-contracts/tokens/fungible/fungible)**: Digital assets representing a fixed or dynamic supply of identical units. * **[Non-Fungible Tokens (NFTs)](/stellar-contracts/tokens/non-fungible/non-fungible)**: Unique digital assets with verifiable ownership. * **[Real World Assets (RWAs)](/stellar-contracts/tokens/rwa/rwa)**: Digital assets representing real-world assets. +* **[Vault](/stellar-contracts/tokens/vault/vault)**: Digital assets representing a fixed or dynamic supply of identical units. ## Access Control From a3976a1dd322b6f0a08fd3342c176898ea14f68a Mon Sep 17 00:00:00 2001 From: Ozgun Ozerk Date: Fri, 24 Oct 2025 14:44:11 +0300 Subject: [PATCH 4/9] few tweaks --- .../stellar-contracts/tokens/vault/vault.mdx | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/content/stellar-contracts/tokens/vault/vault.mdx b/content/stellar-contracts/tokens/vault/vault.mdx index afcaa77d..1ffd386e 100644 --- a/content/stellar-contracts/tokens/vault/vault.mdx +++ b/content/stellar-contracts/tokens/vault/vault.mdx @@ -4,7 +4,14 @@ title: Fungible Vault Token [Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/fungible/extensions/vault) -The Fungible Vault Token implements the ERC-4626 tokenized vault standard, enabling fungible tokens to represent shares in an underlying asset pool. This module allows users to deposit underlying assets in exchange for vault shares, and later redeem those shares for the underlying assets. The vault maintains a dynamic conversion rate between shares and assets based on the total supply of shares and total assets held by the vault contract. +The Fungible Vault Token implements the ERC-4626 tokenized vault standard, +enabling fungible tokens to represent shares in an underlying asset pool. The tokenized vault standard +is the formalized interface for yield-bearing vaults that hold underlying assets. Vault shares enable +hyperfungible collaterals in DeFi. + +This module allows users to deposit underlying assets in exchange for vault shares, and later redeem +those shares for the underlying assets. The vault maintains a dynamic conversion rate between shares and +assets based on the total supply of shares and total assets held by the vault contract. ## Overview @@ -96,11 +103,13 @@ If a higher offset is required, a custom version of `set_decimals_offset()` must ### Inflation (Precision) Attacks The virtual decimals offset helps protect against inflation attacks where an attacker: -1. Deposits 1 wei to get the first share -2. Donates a large amount directly to the vault -3. Causes subsequent depositors to receive 0 shares due to rounding +1. Deposits 1 stroop to get the first share (becoming the sole shareholder) +2. **Donates** (not deposits) an enormous amount of assets directly to the vault contract via a direct transfer, without receiving any shares in return. This inflates the vault's total assets while keeping total shares at 1, making that single share worth an enormous amount +3. When a legitimate user tries to deposit a normal amount (e.g., 1000 stroops), the share calculation rounds down to 0 shares because their deposit is negligible compared to the inflated vault balance. The user loses their deposit while receiving nothing + +For example: If the attacker donates 1,000,000 stroops after their initial 1 stroop deposit, the vault has 1,000,001 total assets and 1 total share. A user depositing 1000 stroops would receive `(1000 × 1) / 1,000,001 = 0.000999` shares, which rounds down to 0. -The offset adds virtual shares and assets to the conversion formula, making such attacks economically infeasible. +The offset adds virtual shares and assets to the conversion formula, making such attacks economically infeasible by ensuring the denominator is never so small that legitimate deposits round to zero. ### Custom Authorization From 64a47e01ff73f24fa73551f00f3e2150f7058025 Mon Sep 17 00:00:00 2001 From: Ozgun Ozerk Date: Tue, 4 Nov 2025 13:47:30 +0300 Subject: [PATCH 5/9] suggestions --- .../stellar-contracts/tokens/vault/vault.mdx | 27 ++++++++++++------- src/navigation/stellar.json | 5 ++++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/content/stellar-contracts/tokens/vault/vault.mdx b/content/stellar-contracts/tokens/vault/vault.mdx index 1ffd386e..c8a21d82 100644 --- a/content/stellar-contracts/tokens/vault/vault.mdx +++ b/content/stellar-contracts/tokens/vault/vault.mdx @@ -1,13 +1,13 @@ --- -title: Fungible Vault Token +title: Fungible Token Vault --- -[Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/fungible/extensions/vault) +[Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/fungible/vault) -The Fungible Vault Token implements the ERC-4626 tokenized vault standard, +The Fungible Token Vault extends the [Fungible Token](/stellar-contracts/tokens/fungible/fungible) and implements the ERC-4626 tokenized vault standard, enabling fungible tokens to represent shares in an underlying asset pool. The tokenized vault standard is the formalized interface for yield-bearing vaults that hold underlying assets. Vault shares enable -hyperfungible collaterals in DeFi. +hyperfungible collaterals in DeFi and remain fully compatible with standard fungible token operations. This module allows users to deposit underlying assets in exchange for vault shares, and later redeem those shares for the underlying assets. The vault maintains a dynamic conversion rate between shares and @@ -15,7 +15,7 @@ assets based on the total supply of shares and total assets held by the vault co ## Overview -The [Vault](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/fungible/extensions/vault) module provides a complete implementation of tokenized vaults following the ERC-4626 standard. Vaults are useful for: +The [Vault](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/fungible/vault) module provides a complete implementation of tokenized vaults following the ERC-4626 standard. Vaults are useful for: - **Yield-bearing tokens**: Represent shares in a yield-generating strategy - **Liquidity pools**: Pool assets together with automatic share calculation @@ -33,7 +33,7 @@ The vault automatically handles: ### Shares vs Assets - **Assets**: The underlying token that the vault manages (e.g., USDC, XLM) -- **Shares**: The vault tokens that represent proportional ownership of the assets +- **Shares**: The Token Vaults that represent proportional ownership of the assets When assets are deposited into a vault, shares are minted to the depositor. The number of shares minted depends on the current exchange rate, which is determined by: @@ -52,7 +52,8 @@ assets = (shares × totalAssets) / totalSupply The vault uses a "virtual decimals offset" to add extra precision to share calculations. This helps prevent rounding errors and improves the accuracy of share-to-asset conversions, -especially when the vault has few assets or shares. +especially when the vault has few assets or shares. It's also a key defense mechanism against +[inflation attacks](#inflation-precision-attacks). The offset adds virtual shares and assets to the conversion formula: @@ -65,12 +66,16 @@ Values higher than 10 provide minimal practical benefits and may cause overflow ## Rounding Behavior -The vault implements specific rounding behavior to protect the vault from being drained: +The vault implements specific rounding behavior to protect against being drained through repeated rounding exploits. +Without proper rounding, an attacker could exploit precision loss to extract more assets than they deposited by +performing many small operations where rounding errors accumulate in their favor. + +To prevent this: - **Deposit/Redeem**: Rounds **down** (depositor receives slightly fewer shares/assets) - **Mint/Withdraw**: Rounds **up** (depositor provides slightly more assets/shares) -This ensures the vault always retains a slight advantage in conversions, preventing precision attacks. +This ensures the vault always retains a slight advantage in conversions, making such attacks unprofitable. | Operation | Input | Output | Rounding Direction | | ---------- | ------ | ------ | ---------------------------- | @@ -105,12 +110,14 @@ If a higher offset is required, a custom version of `set_decimals_offset()` must The virtual decimals offset helps protect against inflation attacks where an attacker: 1. Deposits 1 stroop to get the first share (becoming the sole shareholder) 2. **Donates** (not deposits) an enormous amount of assets directly to the vault contract via a direct transfer, without receiving any shares in return. This inflates the vault's total assets while keeping total shares at 1, making that single share worth an enormous amount -3. When a legitimate user tries to deposit a normal amount (e.g., 1000 stroops), the share calculation rounds down to 0 shares because their deposit is negligible compared to the inflated vault balance. The user loses their deposit while receiving nothing +3. When a legitimate user tries to deposit (e.g., 1000 stroops), the share calculation rounds down to 0 shares because their deposit is negligible compared to the inflated vault balance. The user loses their deposit while receiving nothing For example: If the attacker donates 1,000,000 stroops after their initial 1 stroop deposit, the vault has 1,000,001 total assets and 1 total share. A user depositing 1000 stroops would receive `(1000 × 1) / 1,000,001 = 0.000999` shares, which rounds down to 0. The offset adds virtual shares and assets to the conversion formula, making such attacks economically infeasible by ensuring the denominator is never so small that legitimate deposits round to zero. +For more details about the mechanics of this attack, see the [OpenZeppelin ERC-4626 security documentation](https://docs.openzeppelin.com/contracts/5.x/erc4626#security-concern-inflation-attack). + ### Custom Authorization Custom authorization logic can be implemented as needed: diff --git a/src/navigation/stellar.json b/src/navigation/stellar.json index f5821d50..0a4a86bb 100644 --- a/src/navigation/stellar.json +++ b/src/navigation/stellar.json @@ -62,6 +62,11 @@ "type": "page", "name": "RWA", "url": "/stellar-contracts/tokens/rwa/rwa" + }, + { + "type": "page", + "name": "Vault", + "url": "/stellar-contracts/tokens/vault/vault" } ] }, From 20993f2adfb3ff5af696c6c372bac961fc0ac32c Mon Sep 17 00:00:00 2001 From: Ozgun Ozerk Date: Tue, 4 Nov 2025 13:50:42 +0300 Subject: [PATCH 6/9] typo --- content/stellar-contracts/tokens/vault/vault.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/stellar-contracts/tokens/vault/vault.mdx b/content/stellar-contracts/tokens/vault/vault.mdx index c8a21d82..efbdf24d 100644 --- a/content/stellar-contracts/tokens/vault/vault.mdx +++ b/content/stellar-contracts/tokens/vault/vault.mdx @@ -2,7 +2,7 @@ title: Fungible Token Vault --- -[Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/fungible/vault) +[Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/vault) The Fungible Token Vault extends the [Fungible Token](/stellar-contracts/tokens/fungible/fungible) and implements the ERC-4626 tokenized vault standard, enabling fungible tokens to represent shares in an underlying asset pool. The tokenized vault standard @@ -15,7 +15,7 @@ assets based on the total supply of shares and total assets held by the vault co ## Overview -The [Vault](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/fungible/vault) module provides a complete implementation of tokenized vaults following the ERC-4626 standard. Vaults are useful for: +The [Vault](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/vault) module provides a complete implementation of tokenized vaults following the ERC-4626 standard. Vaults are useful for: - **Yield-bearing tokens**: Represent shares in a yield-generating strategy - **Liquidity pools**: Pool assets together with automatic share calculation From c68ef7d83fb1ea883fddab06271986a3a6982c09 Mon Sep 17 00:00:00 2001 From: Ozgun Ozerk Date: Tue, 4 Nov 2025 13:52:53 +0300 Subject: [PATCH 7/9] use callout --- content/stellar-contracts/tokens/vault/vault.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/content/stellar-contracts/tokens/vault/vault.mdx b/content/stellar-contracts/tokens/vault/vault.mdx index efbdf24d..07e7dcb0 100644 --- a/content/stellar-contracts/tokens/vault/vault.mdx +++ b/content/stellar-contracts/tokens/vault/vault.mdx @@ -146,11 +146,13 @@ The vault module implements the ERC-4626 tokenized vault standard with one minor ### ERC-4626 Deviation -⚠️ **DEVIATION FROM ERC-4626**: The `query_asset()` function will panic if the asset address is not set, whereas ERC-4626 requires it to never revert. + +**DEVIATION FROM ERC-4626**: The `query_asset()` function will panic if the asset address is not set, whereas ERC-4626 requires it to never revert. **Rationale**: Soroban doesn't have a "zero address" concept like EVM. Returning `Option
` would break ERC-4626 compatibility. **Mitigation**: Always initialize the vault properly in the constructor. Once initialized, `query_asset()` will never panic during normal operations. + Aside from this deviation, the vault implementation for Soroban provides: From 78fd66b6da07c83a303a58e0677a864d6ef4dc66 Mon Sep 17 00:00:00 2001 From: Ozgun Ozerk Date: Tue, 4 Nov 2025 14:07:53 +0300 Subject: [PATCH 8/9] usage --- .../stellar-contracts/tokens/vault/vault.mdx | 217 +++++++++++++++++- 1 file changed, 216 insertions(+), 1 deletion(-) diff --git a/content/stellar-contracts/tokens/vault/vault.mdx b/content/stellar-contracts/tokens/vault/vault.mdx index 07e7dcb0..b10da0a2 100644 --- a/content/stellar-contracts/tokens/vault/vault.mdx +++ b/content/stellar-contracts/tokens/vault/vault.mdx @@ -147,7 +147,7 @@ The vault module implements the ERC-4626 tokenized vault standard with one minor ### ERC-4626 Deviation -**DEVIATION FROM ERC-4626**: The `query_asset()` function will panic if the asset address is not set, whereas ERC-4626 requires it to never revert. +The `query_asset()` function will **panic if the asset address is not set**, whereas ERC-4626 requires it to never revert. **Rationale**: Soroban doesn't have a "zero address" concept like EVM. Returning `Option
` would break ERC-4626 compatibility. @@ -159,3 +159,218 @@ Aside from this deviation, the vault implementation for Soroban provides: - **Cross-ecosystem familiarity**: Ethereum developers will recognize the interface - **Standard compliance**: Compatible with ERC-4626 tooling and integrations - **Stellar optimizations**: Leverages Soroban's unique capabilities + +## Usage + +### Basic Implementation + +To create a vault contract, implement both the `FungibleToken` and `FungibleVault` traits: + +```rust +use soroban_sdk::{contract, contractimpl, Address, Env, String}; +use stellar_macros::default_impl; +use stellar_tokens::{ + fungible::{Base, FungibleToken}, + vault::{FungibleVault, Vault}, +}; + +#[contract] +pub struct VaultContract; + +#[contractimpl] +impl VaultContract { + pub fn __constructor(e: &Env, asset: Address, decimals_offset: u32) { + // Set the underlying asset address (immutable after initialization) + Vault::set_asset(e, asset); + + // Set the decimals offset for precision (immutable after initialization) + Vault::set_decimals_offset(e, decimals_offset); + + // Initialize token metadata + // Note: Vault overrides the decimals function, so set offset first + Base::set_metadata( + e, + Vault::decimals(e), + String::from_str(e, "Vault Token"), + String::from_str(e, "VLT"), + ); + } +} + +#[default_impl] +#[contractimpl] +impl FungibleToken for VaultContract { + type ContractType = Vault; + + fn decimals(e: &Env) -> u32 { + Vault::decimals(e) + } +} + +#[contractimpl] +impl FungibleVault for VaultContract { + fn deposit( + e: &Env, + assets: i128, + receiver: Address, + from: Address, + operator: Address, + ) -> i128 { + operator.require_auth(); + Vault::deposit(e, assets, receiver, from, operator) + } + + fn withdraw( + e: &Env, + assets: i128, + receiver: Address, + owner: Address, + operator: Address, + ) -> i128 { + operator.require_auth(); + Vault::withdraw(e, assets, receiver, owner, operator) + } + + // Implement other required methods... +} +``` + +### Initialization + +The vault **must** be properly initialized in the constructor: + +1. **Set the underlying asset**: Call `Vault::set_asset(e, asset)` with the address of the token contract that the vault will manage +2. **Set the decimals offset**: Call `Vault::set_decimals_offset(e, offset)` to configure precision (0-10 recommended) +3. **Initialize metadata**: Call `Base::set_metadata()` with appropriate token information + +**Important**: The asset address and decimals offset are immutable once set and cannot be changed. + +### Core Operations + +#### Depositing Assets + +Users can deposit underlying assets to receive vault shares: + +```rust +// Deposit 1000 assets and receive shares +let shares_received = vault_client.deposit( + &1000, // Amount of assets to deposit + &user_address, // Address to receive shares + &user_address, // Address providing assets + &user_address, // Operator (requires auth) +); +``` + +Alternatively, mint a specific amount of shares: + +```rust +// Mint exactly 500 shares by depositing required assets +let assets_required = vault_client.mint( + &500, // Amount of shares to mint + &user_address, // Address to receive shares + &user_address, // Address providing assets + &user_address, // Operator (requires auth) +); +``` + +#### Withdrawing Assets + +Users can withdraw assets by burning their shares: + +```rust +// Withdraw 500 assets by burning required shares +let shares_burned = vault_client.withdraw( + &500, // Amount of assets to withdraw + &user_address, // Address to receive assets + &user_address, // Owner of shares + &user_address, // Operator (requires auth) +); +``` + +Or redeem a specific amount of shares: + +```rust +// Redeem 200 shares for underlying assets +let assets_received = vault_client.redeem( + &200, // Amount of shares to redeem + &user_address, // Address to receive assets + &user_address, // Owner of shares + &user_address, // Operator (requires auth) +); +``` + +### Preview Functions + +Preview functions allow you to simulate operations without executing them: + +```rust +let expected_shares = vault_client.preview_deposit(&1000); + +let required_assets = vault_client.preview_mint(&500); + +let shares_to_burn = vault_client.preview_withdraw(&500); + +let expected_assets = vault_client.preview_redeem(&200); +``` + +### Conversion Functions + +Convert between assets and shares at the current exchange rate: + +```rust +// Convert assets to shares +let shares = vault_client.convert_to_shares(&1000); + +// Convert shares to assets +let assets = vault_client.convert_to_assets(&500); +``` + +### Query Functions + +Check vault state and limits: + +```rust +// Get the underlying asset address +let asset_address = vault_client.query_asset(); + +// Get total assets held by the vault +let total_assets = vault_client.total_assets(); + +// Check maximum amounts for operations +let max_deposit = vault_client.max_deposit(&user_address); +let max_mint = vault_client.max_mint(&user_address); +let max_withdraw = vault_client.max_withdraw(&user_address); +let max_redeem = vault_client.max_redeem(&user_address); +``` + +### Operator Pattern + +The vault supports an operator pattern where one address can perform operations on behalf of another: + +```rust +// User approves operator to spend their assets on the underlying token +asset_client.approve(&user, &operator, &1000, &expiration_ledger); + +// Operator deposits user's assets to a receiver +vault_client.deposit( + &1000, + &receiver, // Receives the shares + &user, // Provides the assets + &operator, // Performs the operation (requires auth) +); +``` + +For withdrawals, the operator must have allowance on the **vault shares**: + +```rust +// User approves operator to spend their vault shares +vault_client.approve(&user, &operator, &500, &expiration_ledger); + +// Operator withdraws on behalf of user +vault_client.withdraw( + &500, + &receiver, // Receives the assets + &user, // Owns the shares + &operator, // Performs the operation (requires auth) +); +``` From 9dd2daaf51e124f2cb4565609a5076ca8929b397 Mon Sep 17 00:00:00 2001 From: Ozgun Ozerk Date: Tue, 4 Nov 2025 17:50:09 +0300 Subject: [PATCH 9/9] suggestion --- content/stellar-contracts/tokens/vault/vault.mdx | 1 - 1 file changed, 1 deletion(-) diff --git a/content/stellar-contracts/tokens/vault/vault.mdx b/content/stellar-contracts/tokens/vault/vault.mdx index b10da0a2..89345dd7 100644 --- a/content/stellar-contracts/tokens/vault/vault.mdx +++ b/content/stellar-contracts/tokens/vault/vault.mdx @@ -158,7 +158,6 @@ Aside from this deviation, the vault implementation for Soroban provides: - **Cross-ecosystem familiarity**: Ethereum developers will recognize the interface - **Standard compliance**: Compatible with ERC-4626 tooling and integrations -- **Stellar optimizations**: Leverages Soroban's unique capabilities ## Usage