Skip to main content
vTokens are standard fungible tokens on Soroban that represent a proportional share of a Vanna lending pool’s total assets. Three vToken contracts exist — one per pool.
TokenPoolAddress
vXLMXLM Lending PoolCCQAAPNBYF6I7PRM2NZ4NRDYZUVJJANMX3RZ4ZLMQH6Z5WAUL2MHU2RZ
vUSDCUSDC Lending PoolCDAJHQEJ26EBBGV2UYSR5S5LLA6F3A7KQISLPY5JMOL77RUDBEWG3T6Y
vAQUSDCAquarius USDC PoolCATGJWK22YGAQX4DH6BXUMQYRZRGOQ6MJNJVZP67WZEE4PHSROU763KN
vSOUSDCSoroswap USDC PoolCDW5YJOBAR5KUPYQ5LQICSLKWRUMDEGSZVS5A4QNBMNKEZEARR6EJFYT
All vToken contracts share the same interface.

Exchange Rate

vTokens appreciate against the underlying asset as interest accrues. The exchange rate is:
exchange_rate = total_assets / vtoken_supply
Where total_assets = pool_balance + outstanding_borrows. As borrowers repay interest, total_assets grows, and each vToken is redeemable for more of the underlying. Holding vTokens is how LPs earn yield. No staking, claiming, or compounding required — yield accrues in the exchange rate.

Functions

initialize

fn initialize(
    env: Env,
    admin: Address,
    decimals: u32,
    name: String,
    symbol: String,
) -> ()
One-time initialization. Sets the token metadata and admin. Can only be called once.

mint

fn mint(env: Env, to: Address, amount: i128) -> ()
Increases to’s vToken balance by amount. Only callable by the admin (the LendingPool contract). Called by the LendingPool on every deposit.

burn

fn burn(env: Env, from: Address, amount: i128) -> ()
Decreases from’s vToken balance by amount. Only callable by the admin (the LendingPool contract). Called by the LendingPool on every vToken redemption.

transfer

fn transfer(env: Env, from: Address, to: Address, amount: i128) -> ()
Standard token transfer. Requires from.require_auth(). vTokens are freely transferable — they can be held in any wallet or used in other DeFi protocols.

transfer_from

fn transfer_from(
    env: Env,
    spender: Address,
    from: Address,
    to: Address,
    amount: i128,
) -> ()
Allowance-based transfer. Requires spender.require_auth() and sufficient allowance from from to spender.

approve

fn approve(
    env: Env,
    from: Address,
    spender: Address,
    amount: i128,
    expiration_ledger: u32,
) -> ()
Grants spender permission to transfer up to amount of from’s vTokens. Standard Soroban token approval with an expiration ledger.

balance

fn balance(env: Env, id: Address) -> i128
Returns the vToken balance of id.

total_supply

fn total_supply(env: Env) -> i128
Returns the total number of vTokens currently in circulation.

allowance

fn allowance(env: Env, from: Address, spender: Address) -> i128
Returns the remaining allowance that spender can transfer from from.

decimals / name / symbol

fn decimals(env: Env) -> u32
fn name(env: Env) -> String
fn symbol(env: Env) -> String
Standard token metadata.

admin

fn admin(env: Env) -> Address
Returns the admin address (the LendingPool contract that controls mint/burn).

set_admin

fn set_admin(env: Env, new_admin: Address) -> ()
Changes the admin. Requires current admin auth. Used for protocol upgrades.

Events

MintEvent {
    admin: Address,
    to: Address,
    amount: i128
}

BurnEvent {
    admin: Address,
    from: Address,
    amount: i128
}

TransferEvent {
    from: Address,
    to: Address,
    amount: i128
}

ApprovalEvent {
    from: Address,
    spender: Address,
    amount: i128,
    expiration_ledger: u32
}

Storage Layout

KeyTypeDescription
TokenInfo{decimals, name, symbol}Token metadata
AdminAddressAdmin (LendingPool)
TotalSupplyi128Total vTokens in circulation
Balance(Address)i128Per-user balance
Allowance({from, spender}){amount, expiration_ledger}Transfer allowances
Frozen(Address)boolWhether account is frozen (admin action)

Integration Notes

vTokens follow the Stellar Asset Interface (SEP-41) and can be held, transferred, and used as collateral in other protocols on Stellar.
To compute the current redemption value of a vToken balance:
const vtokenBalance = await vtokenContract.balance(userAddress);
const totalSupply = await vtokenContract.total_supply();
const totalAssets = await lendingPool.total_assets(); // liquidity + borrows in WAD

const underlyingWad = (vtokenBalance * totalAssets) / totalSupply;