Vault Contract
The Vault contract is the core primitive of Nester's savings layer. It accepts stablecoin deposits, tracks share ownership, and manages yield source allocations.
Interface
rust
#![no_std]
use soroban_sdk::{contract, contractimpl, Address, Env, Vec};
#[contract]
pub struct Vault;
#[contractimpl]
impl Vault {
/// Initialize a new vault with admin, asset token, and strategy
pub fn initialize(
env: Env,
admin: Address,
asset: Address, // USDC token contract
share_token: Address, // nVault token contract
strategy: Address, // Allocation strategy contract
);
/// Deposit stablecoins and receive share tokens
pub fn deposit(
env: Env,
user: Address,
amount: i128,
) -> i128 {
user.require_auth();
let total_assets = Self::total_assets(&env);
let total_shares = Self::total_shares(&env);
// Calculate shares to mint
let shares = if total_shares == 0 {
amount // First depositor gets 1:1 shares
} else {
amount * total_shares / total_assets
};
// Transfer USDC from user to vault
token::transfer(&env, &user, &env.current_contract_address(), amount);
// Mint nVault share tokens to user
share_token::mint(&env, &user, shares);
// Update vault totals
Self::set_total_assets(&env, total_assets + amount);
Self::set_total_shares(&env, total_shares + shares);
// Emit deposit event
env.events().publish(
(symbol_short!("deposit"), user.clone()),
(amount, shares),
);
shares
}
/// Withdraw stablecoins by burning share tokens
pub fn withdraw(
env: Env,
user: Address,
shares: i128,
) -> i128 {
user.require_auth();
let total_assets = Self::total_assets(&env);
let total_shares = Self::total_shares(&env);
// Calculate underlying amount
let amount = shares * total_assets / total_shares;
// Check maturity and apply penalty if early
let penalty = Self::calculate_penalty(&env, &user, amount);
let net_amount = amount - penalty;
// Burn user's share tokens
share_token::burn(&env, &user, shares);
// Transfer USDC to user
token::transfer(&env, &env.current_contract_address(), &user, net_amount);
// Update vault totals (penalty stays in vault, boosting other holders)
Self::set_total_assets(&env, total_assets - net_amount);
Self::set_total_shares(&env, total_shares - shares);
env.events().publish(
(symbol_short!("withdraw"), user.clone()),
(net_amount, shares, penalty),
);
net_amount
}
/// Get user's current position
pub fn get_position(env: Env, user: Address) -> Position {
let shares = share_token::balance(&env, &user);
let total_assets = Self::total_assets(&env);
let total_shares = Self::total_shares(&env);
let value = shares * total_assets / total_shares;
Position { shares, value }
}
/// Get vault info (for frontend display)
pub fn get_info(env: Env) -> VaultInfo {
VaultInfo {
total_assets: Self::total_assets(&env),
total_shares: Self::total_shares(&env),
apy_estimate: Self::current_apy(&env),
allocations: Self::get_allocations(&env),
}
}
/// Rebalance vault allocations (admin only)
pub fn rebalance(env: Env, admin: Address, new_allocations: Vec<Allocation>) {
admin.require_auth();
Self::verify_admin(&env, &admin);
// Execute rebalance across yield sources
}
/// Emergency pause (admin only)
pub fn pause(env: Env, admin: Address) {
admin.require_auth();
Self::verify_admin(&env, &admin);
Self::set_paused(&env, true);
}
}Events
| Event | Data | When |
|---|---|---|
deposit | (amount, shares) | User deposits into vault |
withdraw | (net_amount, shares, penalty) | User withdraws from vault |
rebalance | (new_allocations) | Admin triggers rebalance |
pause | (admin) | Vault paused for emergency |
Storage Layout
rust
// Persistent storage keys
enum DataKey {
Admin, // Address
Asset, // Address (USDC token)
ShareToken, // Address (nVault token)
Strategy, // Address (allocation strategy)
TotalAssets, // i128
TotalShares, // i128
Paused, // bool
Maturity(Address), // Timestamp per user
}