Reading Vault State
All vault state is available on-chain through view functions. No authentication required.
Essential Reads
Vault Overview
import { createPublicClient, http } from "viem";
import { mainnet } from "viem/chains";
import vaultAbi from "./abi/EmeraldVault.json";
const publicClient = createPublicClient({
chain: mainnet,
transport: http("https://eth.llamarpc.com"),
});
const VAULT = "0x..."; // vault address
const [totalAssets, managedAssets, paused, totalSupply] =
await Promise.all([
publicClient.readContract({
address: VAULT, abi: vaultAbi, functionName: "totalAssets",
}),
publicClient.readContract({
address: VAULT, abi: vaultAbi, functionName: "managedAssets",
}),
publicClient.readContract({
address: VAULT, abi: vaultAbi, functionName: "paused",
}),
publicClient.readContract({
address: VAULT, abi: vaultAbi, functionName: "totalSupply",
}),
]);
console.log({
totalAssets, // on-chain balance + managed assets
managedAssets, // managed assets held off-chain
onChain: totalAssets - managedAssets,
paused,
totalSupply, // total shares outstanding
});
Share Price
The share price isn't a single function call — derive it from convertToAssets:
import { parseUnits } from "viem";
// Shares inherit the asset's decimals (eUSD=6, eBTC=8, eETH/eGOLD=18) — read it.
const decimals = await publicClient.readContract({
address: VAULT, abi: vaultAbi, functionName: "decimals",
});
const oneShare = parseUnits("1", decimals);
const pricePerShare = await publicClient.readContract({
address: VAULT,
abi: vaultAbi,
functionName: "convertToAssets",
args: [oneShare],
});
console.log(`1 share = ${pricePerShare} asset units`);
User Position
const userAddress = "0x...";
const shareBalance = await publicClient.readContract({
address: VAULT,
abi: vaultAbi,
functionName: "balanceOf",
args: [userAddress],
});
// Convert shares to asset value
const assetValue = await publicClient.readContract({
address: VAULT,
abi: vaultAbi,
functionName: "convertToAssets",
args: [shareBalance],
});
console.log({
shares: shareBalance,
valueInAssets: assetValue,
});
Withdrawal Queue State
const [pendingCount, totalPendingShares, queueLength] = await Promise.all([
publicClient.readContract({
address: VAULT, abi: vaultAbi, functionName: "pendingCount",
}),
publicClient.readContract({
address: VAULT, abi: vaultAbi, functionName: "totalPendingShares",
}),
publicClient.readContract({
address: VAULT, abi: vaultAbi, functionName: "withdrawalQueueLength",
}),
]);
console.log({
pendingCount, // unresolved (not claimed/cancelled) queued requests
totalPendingShares, // shares locked in the queue (asset value via convertToAssets)
queueLength, // total requests ever (including resolved)
});
Single Withdrawal Request
const [user, shares, assetsOwed, timestamp, claimed, cancelled] =
await publicClient.readContract({
address: VAULT,
abi: vaultAbi,
functionName: "getWithdrawalRequest",
args: [0n], // request index
});
// `assetsOwed` is computed live from the current share price and is 0 once the
// request is resolved. Status is queued/claimed/cancelled — there is no
// time-based "claimable"/"expired" state; queued requests are paid by admin
// release or self-claim at the queue head.
let status: string;
if (cancelled) status = "cancelled";
else if (claimed) status = "claimed";
else status = "queued";
console.log({ user, shares, assetsOwed, timestamp, status });
All User Requests
const indices = await publicClient.readContract({
address: VAULT,
abi: vaultAbi,
functionName: "getUserRequests",
args: [userAddress],
});
// Fetch all request details
const requests = await Promise.all(
indices.map((index) =>
publicClient.readContract({
address: VAULT,
abi: vaultAbi,
functionName: "getWithdrawalRequest",
args: [index],
})
)
);
Multicall Pattern
For efficiency, batch multiple reads in a single RPC call using multicall:
import { getContract } from "viem";
const vault = getContract({
address: VAULT,
abi: vaultAbi,
client: publicClient,
});
const results = await publicClient.multicall({
contracts: [
{ ...vault, functionName: "totalAssets" },
{ ...vault, functionName: "managedAssets" },
{ ...vault, functionName: "paused" },
{ ...vault, functionName: "totalSupply" },
{ ...vault, functionName: "pendingCount" },
],
});
// results[0].result = totalAssets, results[1].result = managedAssets, etc.
On-Chain vs API
| Data | On-Chain | API |
|---|---|---|
| Real-time vault state | readContract() | GET /api/v1/vaults/{address} |
| Historical NAV updates | Event logs | GET /api/v1/vaults/{address}/nav |
| User deposit history | Event logs | GET /api/v1/users/{address}/deposits |
| Withdrawal status | getWithdrawalRequest() | GET /api/v1/users/{address}/withdrawals |
| Pending queue | pendingCount() | GET /api/v1/queues/withdrawals |
Use on-chain reads for real-time data (current balances, live share price). Use the API for historical data, aggregations, and paginated lists.