Skip to content

Commit 78fd66b

Browse files
committed
usage
1 parent c68ef7d commit 78fd66b

File tree

1 file changed

+216
-1
lines changed
  • content/stellar-contracts/tokens/vault

1 file changed

+216
-1
lines changed

content/stellar-contracts/tokens/vault/vault.mdx

Lines changed: 216 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ The vault module implements the ERC-4626 tokenized vault standard with one minor
147147
### ERC-4626 Deviation
148148

149149
<Callout type="warning">
150-
**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.
150+
The `query_asset()` function will **panic if the asset address is not set**, whereas ERC-4626 requires it to never revert.
151151

152152
**Rationale**: Soroban doesn't have a "zero address" concept like EVM. Returning `Option<Address>` would break ERC-4626 compatibility.
153153

@@ -159,3 +159,218 @@ Aside from this deviation, the vault implementation for Soroban provides:
159159
- **Cross-ecosystem familiarity**: Ethereum developers will recognize the interface
160160
- **Standard compliance**: Compatible with ERC-4626 tooling and integrations
161161
- **Stellar optimizations**: Leverages Soroban's unique capabilities
162+
163+
## Usage
164+
165+
### Basic Implementation
166+
167+
To create a vault contract, implement both the `FungibleToken` and `FungibleVault` traits:
168+
169+
```rust
170+
use soroban_sdk::{contract, contractimpl, Address, Env, String};
171+
use stellar_macros::default_impl;
172+
use stellar_tokens::{
173+
fungible::{Base, FungibleToken},
174+
vault::{FungibleVault, Vault},
175+
};
176+
177+
#[contract]
178+
pub struct VaultContract;
179+
180+
#[contractimpl]
181+
impl VaultContract {
182+
pub fn __constructor(e: &Env, asset: Address, decimals_offset: u32) {
183+
// Set the underlying asset address (immutable after initialization)
184+
Vault::set_asset(e, asset);
185+
186+
// Set the decimals offset for precision (immutable after initialization)
187+
Vault::set_decimals_offset(e, decimals_offset);
188+
189+
// Initialize token metadata
190+
// Note: Vault overrides the decimals function, so set offset first
191+
Base::set_metadata(
192+
e,
193+
Vault::decimals(e),
194+
String::from_str(e, "Vault Token"),
195+
String::from_str(e, "VLT"),
196+
);
197+
}
198+
}
199+
200+
#[default_impl]
201+
#[contractimpl]
202+
impl FungibleToken for VaultContract {
203+
type ContractType = Vault;
204+
205+
fn decimals(e: &Env) -> u32 {
206+
Vault::decimals(e)
207+
}
208+
}
209+
210+
#[contractimpl]
211+
impl FungibleVault for VaultContract {
212+
fn deposit(
213+
e: &Env,
214+
assets: i128,
215+
receiver: Address,
216+
from: Address,
217+
operator: Address,
218+
) -> i128 {
219+
operator.require_auth();
220+
Vault::deposit(e, assets, receiver, from, operator)
221+
}
222+
223+
fn withdraw(
224+
e: &Env,
225+
assets: i128,
226+
receiver: Address,
227+
owner: Address,
228+
operator: Address,
229+
) -> i128 {
230+
operator.require_auth();
231+
Vault::withdraw(e, assets, receiver, owner, operator)
232+
}
233+
234+
// Implement other required methods...
235+
}
236+
```
237+
238+
### Initialization
239+
240+
The vault **must** be properly initialized in the constructor:
241+
242+
1. **Set the underlying asset**: Call `Vault::set_asset(e, asset)` with the address of the token contract that the vault will manage
243+
2. **Set the decimals offset**: Call `Vault::set_decimals_offset(e, offset)` to configure precision (0-10 recommended)
244+
3. **Initialize metadata**: Call `Base::set_metadata()` with appropriate token information
245+
246+
**Important**: The asset address and decimals offset are immutable once set and cannot be changed.
247+
248+
### Core Operations
249+
250+
#### Depositing Assets
251+
252+
Users can deposit underlying assets to receive vault shares:
253+
254+
```rust
255+
// Deposit 1000 assets and receive shares
256+
let shares_received = vault_client.deposit(
257+
&1000, // Amount of assets to deposit
258+
&user_address, // Address to receive shares
259+
&user_address, // Address providing assets
260+
&user_address, // Operator (requires auth)
261+
);
262+
```
263+
264+
Alternatively, mint a specific amount of shares:
265+
266+
```rust
267+
// Mint exactly 500 shares by depositing required assets
268+
let assets_required = vault_client.mint(
269+
&500, // Amount of shares to mint
270+
&user_address, // Address to receive shares
271+
&user_address, // Address providing assets
272+
&user_address, // Operator (requires auth)
273+
);
274+
```
275+
276+
#### Withdrawing Assets
277+
278+
Users can withdraw assets by burning their shares:
279+
280+
```rust
281+
// Withdraw 500 assets by burning required shares
282+
let shares_burned = vault_client.withdraw(
283+
&500, // Amount of assets to withdraw
284+
&user_address, // Address to receive assets
285+
&user_address, // Owner of shares
286+
&user_address, // Operator (requires auth)
287+
);
288+
```
289+
290+
Or redeem a specific amount of shares:
291+
292+
```rust
293+
// Redeem 200 shares for underlying assets
294+
let assets_received = vault_client.redeem(
295+
&200, // Amount of shares to redeem
296+
&user_address, // Address to receive assets
297+
&user_address, // Owner of shares
298+
&user_address, // Operator (requires auth)
299+
);
300+
```
301+
302+
### Preview Functions
303+
304+
Preview functions allow you to simulate operations without executing them:
305+
306+
```rust
307+
let expected_shares = vault_client.preview_deposit(&1000);
308+
309+
let required_assets = vault_client.preview_mint(&500);
310+
311+
let shares_to_burn = vault_client.preview_withdraw(&500);
312+
313+
let expected_assets = vault_client.preview_redeem(&200);
314+
```
315+
316+
### Conversion Functions
317+
318+
Convert between assets and shares at the current exchange rate:
319+
320+
```rust
321+
// Convert assets to shares
322+
let shares = vault_client.convert_to_shares(&1000);
323+
324+
// Convert shares to assets
325+
let assets = vault_client.convert_to_assets(&500);
326+
```
327+
328+
### Query Functions
329+
330+
Check vault state and limits:
331+
332+
```rust
333+
// Get the underlying asset address
334+
let asset_address = vault_client.query_asset();
335+
336+
// Get total assets held by the vault
337+
let total_assets = vault_client.total_assets();
338+
339+
// Check maximum amounts for operations
340+
let max_deposit = vault_client.max_deposit(&user_address);
341+
let max_mint = vault_client.max_mint(&user_address);
342+
let max_withdraw = vault_client.max_withdraw(&user_address);
343+
let max_redeem = vault_client.max_redeem(&user_address);
344+
```
345+
346+
### Operator Pattern
347+
348+
The vault supports an operator pattern where one address can perform operations on behalf of another:
349+
350+
```rust
351+
// User approves operator to spend their assets on the underlying token
352+
asset_client.approve(&user, &operator, &1000, &expiration_ledger);
353+
354+
// Operator deposits user's assets to a receiver
355+
vault_client.deposit(
356+
&1000,
357+
&receiver, // Receives the shares
358+
&user, // Provides the assets
359+
&operator, // Performs the operation (requires auth)
360+
);
361+
```
362+
363+
For withdrawals, the operator must have allowance on the **vault shares**:
364+
365+
```rust
366+
// User approves operator to spend their vault shares
367+
vault_client.approve(&user, &operator, &500, &expiration_ledger);
368+
369+
// Operator withdraws on behalf of user
370+
vault_client.withdraw(
371+
&500,
372+
&receiver, // Receives the assets
373+
&user, // Owns the shares
374+
&operator, // Performs the operation (requires auth)
375+
);
376+
```

0 commit comments

Comments
 (0)