A TokenEntry defines one accepted deposit token within an ExponentStrategyVault. Each token entry specifies how the token is priced, where its escrow accounts live, and which yield protocol it is deployed into.
TokenEntry
pub struct TokenEntry {
/// SPL mint of the accepted token
pub mint: Pubkey,
/// Price calculation method for this token's AUM contribution
pub price_id: PriceId,
/// Token account owned by the Squads smart account (squads_vault)
/// Deposited tokens land here and are deployed from here
pub token_squads_account: Pubkey,
/// Policy seeds used for force-deallocate emergency flows
pub force_deallocate_policy_ids: Vec<u64>,
}
The deserialized TypeScript TokenEntry type includes additional fields (tokenEscrow, currentLiquidity, mintFlag, interfaceType) that are derived at fetch time but are not stored directly in the Rust struct.
InterfaceType
The interface_type determines which yield protocol the token entry’s capital is deployed through.
InterfaceType is not stored on-chain in the TokenEntry struct. It is derived by the SDK’s fetcher during deserialization based on the token entry’s configuration.
pub enum InterfaceType {
/// No specific protocol — managed generically
Generic,
/// Deployed into Kamino Lending reserves
Kamino,
/// Deployed into Marginfi lending
Marginfi,
/// Deployed into Jito Restaking
JitoRestaking,
/// Deployed into Perena yield
Perena,
}
PriceId
The PriceId determines how the vault computes the base-unit value of this token for AUM calculations:
pub enum PriceId {
/// Single price feed: token value = price[price_id]
Simple { price_id: u64 },
/// Product of multiple price feeds: value = price[0] × price[1] × ...
/// Useful for LST pricing: LST/SOL rate × SOL/USD rate
Multiply { price_ids: Vec<u64> },
}
Prices are read from the global ExponentPrices account (["exponent_prices"]).
Token Account Layout
Each token entry uses two token accounts:
| Account | Owner | Purpose |
|---|
token_squads_account | Squads smart account (squads_vault) | Receives deposits; capital is deployed from here |
token_escrow | Vault PDA (self_address) | Holds tokens allocated for pending withdrawal fills |
The escrow PDA is derived as:
seeds = ["token_entry_escrow", vault, mint]
TypeScript Interface
When constructing a vault via the SDK, token entries are specified as TokenEntryInput:
import { TokenEntryInput } from "@exponent-labs/exponent-sdk";
import { ExponentVaultsPDA } from "@exponent-labs/exponent-vaults-pda";
import { PublicKey } from "@solana/web3.js";
const tokenEntry: TokenEntryInput = {
// The token mint (e.g., USDC)
mint: new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"),
// Price source: simple oracle reference
priceId: { simple: { priceId: 1n, underlyingMint: new PublicKey("...") } },
// Squads-controlled token account for this entry
tokenSquadsAccount: squadsTokenAccount,
// Vault PDA-controlled escrow for withdrawals
tokenEscrow: new ExponentVaultsPDA().tokenEntryEscrow({ vault, mint })[0],
// Target allocation in basis points (e.g., 5000 = 50%)
shareBp: 5000,
// Initial liquidity (set to 0 for new entries)
currentLiquidity: 0n,
// 0 = standard SPL token, 1 = Token-2022
mintFlag: 0,
// Protocol type for this entry (optional)
interfaceType: { kamino: {} },
// Policy IDs for emergency force-deallocate (optional, defaults to [])
forceDeallocatePolicyIds: [],
};
Reading Token Entries
Fetch a vault’s token entries after loading:
import { ExponentVault } from "@exponent-labs/exponent-sdk";
const vault = await ExponentVault.load({ connection, address: vaultAddress });
for (const entry of vault.state.tokenEntries) {
console.log("Mint:", entry.mint.toBase58());
console.log("Interface:", entry.interfaceType);
console.log("Liquidity:", entry.currentLiquidity.toString());
}