Skip to main content

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

DataOn-ChainAPI
Real-time vault statereadContract()GET /api/v1/vaults/{address}
Historical NAV updatesEvent logsGET /api/v1/vaults/{address}/nav
User deposit historyEvent logsGET /api/v1/users/{address}/deposits
Withdrawal statusgetWithdrawalRequest()GET /api/v1/users/{address}/withdrawals
Pending queuependingCount()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.