@@ -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