This document describes the technical architecture of the NeuroWealth Vault contract, including storage layout, data structures, and integration patterns.
The NeuroWealth Vault is a Soroban smart contract that implements a non-custodial yield vault on the Stellar blockchain. Users deposit USDC, and an AI agent automatically deploys those funds across various yield-generating protocols.
Instance storage is used for contract-wide configuration that is read frequently but changes infrequently.
| Key | Type | Description |
|---|---|---|
Agent |
Address | Authorized AI agent that can call rebalance() |
UsdcToken |
Address | USDC token contract address |
TotalDeposits |
i128 | Total USDC held in vault (excludes deployed yield) |
Paused |
bool | Emergency pause state |
Owner |
Address | Contract owner for administrative functions |
TvLCap |
i128 | Maximum total value locked |
UserDepositCap |
i128 | Maximum deposit per user |
Version |
u32 | Contract version for upgrade tracking |
Persistent storage is used for per-user data that requires efficient access.
| Key | Type | Description |
|---|---|---|
Balance(Address) |
i128 | Individual user USDC balance |
pub enum DataKey {
Balance(Address), // user -> usdc balance
TotalDeposits, // total USDC in vault
Agent, // authorized AI agent address
UsdcToken, // USDC token contract address
Paused, // emergency pause state
Owner, // contract owner address
TvLCap, // maximum TVL
UserDepositCap, // per-user deposit limit
Version, // contract version
}The vault currently uses a simple 1:1 asset accounting model:
1 deposited USDC = 1 vault balance unit
This means:
- Users receive exact balance matching their deposit
- No share tokens are minted
- Yield is tracked separately by the AI agent off-chain
Limitations:
- Cannot accurately track user's share of yield earned
- No proportional withdrawal during yield deployment
- Not ERC-4626 compliant
Will upgrade to proper share-based accounting:
shares = (assets * total_shares) / total_assets
assets = (shares * total_assets) / total_shares
This enables:
- Proportional claim on total vault assets
- Accurate yield distribution
- ERC-4626 compliance
- Proper rebalancing during withdrawals
- Deposits: 1:1 (no rounding)
- Withdrawals: 1:1 (no rounding)
- Minimum deposit: 1 USDC (1,000,000 in 7-decimal units)
- Deposits: Round down (favor vault, protect against dust)
- Withdrawals: Round down (favor vault, protect against reentrancy)
- Share minting: Round down
struct DepositEvent {
user: Address, // User who made the deposit
amount: i128, // Amount in 7-decimal USDC units
}Topics: SymbolShort("deposit")
struct WithdrawEvent {
user: Address, // User who made the withdrawal
amount: i128, // Amount in 7-decimal USDC units
}Topics: SymbolShort("withdraw")
struct RebalanceEvent {
strategy: Symbol, // Target strategy (e.g., "conservative", "balanced", "growth")
amount: i128, // Amount being rebalanced (0 for full rebalance)
}Topics: SymbolShort("rebalance")
struct PauseEvent {
paused: bool, // true = paused, false = unpaused
caller: Address, // Who triggered the pause
}Topics: SymbolShort("pause")
Vault Contract → USDC Token Contract (via token::Client)
↑
├── transfer() - receive user funds
└── transfer() - return funds to user
Integration Points:
deposit(): Callstoken.transfer(user, vault, amount)withdraw(): Callstoken.transfer(vault, user, amount)
Assumptions:
- USDC uses Stellar's Soroban Token interface
- 7 decimal places
- Standard token operations (transfer, balance, etc.)
AI Agent → Vault Contract
├── get_balance(user) - monitor positions
├── get_total_deposits() - monitor TVL
└── rebalance(strategy) - signal strategy changes
↓
DepositEvent / WithdrawEvent (via Soroban events)
Event Flow:
- User calls
deposit()orwithdraw() - Contract emits corresponding event
- AI agent monitors events via RPC/subscription
- Agent responds by calling
rebalance()or adjusting off-chain state
Vault Contract → Blend Protocol Contract
↑
├── lend() - deposit USDC for yield
├── redeem() - withdraw from lending
└── get_balance() - check yield earned
Future Integration:
- Phase 2 will add direct Blend protocol interactions
- Vault will call Blend's lending functions
- Yield earned will be tracked in total assets
┌─────────┐ ┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ User │───▶│ USDC Token │───▶│ Vault │───▶│ Event Emit │
│ Wallet │ │ (transfer) │ │ (balance++) │ │ (deposit) │
└─────────┘ └─────────────┘ └──────────────┘ └─────────────┘
↓
┌─────────────┐
│ AI Agent │
│ (monitors) │
└─────────────┘
- User authorizes deposit transaction
- USDC transferred from user to vault
- User balance updated in persistent storage
- Total deposits updated in instance storage
- DepositEvent emitted
- AI agent detects event, initiates yield deployment
┌─────────┐ ┌──────────────┐ ┌─────────────┐ ┌─────────────┐
│ User │───▶│ Vault │───▶│ Balance │───▶│ Event Emit │
│ Wallet │ │ (auth check) │ │ (balance--) │ │ (withdraw) │
└─────────┘ └──────────────┘ └─────────────┘ └─────────────┘
↓ ↓
┌─────────────┐ ┌─────────────┐
│ USDC Token │◀───────────────────────────│ AI Agent │
│ (transfer) │ │ (monitors) │
└─────────────┘ └─────────────┘
- User authorizes withdrawal transaction
- Vault verifies user balance
- User balance updated in persistent storage
- Total deposits updated in instance storage
- USDC transferred from vault to user
- WithdrawEvent emitted
- AI agent detects event, updates internal state
┌─────────────┐ ┌──────────────┐ ┌─────────────┐ ┌─────────────┐
│ AI Agent │───▶│ Vault │───▶│ Auth Check │───▶│ Event Emit │
│ (strategy) │ │ (rebalance) │ │ (agent) │ │ (rebalance) │
└─────────────┘ └──────────────┘ └─────────────┘ └─────────────┘
↓
┌─────────────┐
│ External │
│ Protocols │
│ (Blend/DEX) │
└─────────────┘
- AI agent evaluates market conditions
- Agent calls
rebalance(strategy)on vault - Vault verifies caller is agent
- RebalanceEvent emitted
- Agent proceeds to execute strategy via external protocols
When upgrading the contract, the following storage keys must be preserved:
- All
Balance(Address)entries TotalDepositsAgentUsdcTokenPausedOwnerTvLCapUserDepositCapVersion(incremented)
| Version | Changes |
|---|---|
| 1 | Initial implementation with basic deposit/withdraw |
| 2 | (Planned) ERC-4626 share accounting |
| 3 | (Planned) Blend protocol integration |
| 4 | (Planned) Multi-asset support |
When upgrading to share-based accounting (Phase 2):
- Snapshot all user balances
- Mint shares 1:1 to existing balances
- Track total shares = total deposits
- Future deposits/withdrawals use share math
| Function | Panic Condition |
|---|---|
initialize |
"Already initialized" |
deposit |
"Vault is paused", "Amount must be positive", "Minimum deposit is 1 USDC", "Exceeds user deposit cap", "Exceeds TVL cap" |
withdraw |
"Vault is paused", "Amount must be positive", "Insufficient balance" |
rebalance |
"Vault is paused" |
pause |
(requires owner auth) |
unpause |
"Vault is not paused" |
All read functions return the requested data or 0/default if not set.
- Deposit with valid amount
- Deposit with minimum amount (boundary)
- Deposit exceeding cap (should fail)
- Withdraw with sufficient balance
- Withdraw exceeding balance (should fail)
- Pause/unpause by owner
- Pause by non-owner (should fail)
- Full deposit → rebalance → withdraw flow
- Multiple users depositing and withdrawing
- TVL cap enforcement
- User deposit cap enforcement
- Emergency pause during active deposits
- Read: ~1-2 gas units
- Write: ~2-3 gas units
- Use for: Configuration, totals, flags
- Read: ~1 gas unit
- Write: ~1-2 gas units
- Use for: User balances
- Batch reads when possible
- Use instance storage for frequently accessed globals
- Use persistent storage for user-specific data
- Minimize state changes in single transaction