getOnchainEarnings
Get onchain earnings for a wallet — total earnings by token and a per-chain breakdown.
The response includes gross totals and net-of-fee totals. Net fields apply the Zyfi performance fee (10%) to current earnings only:
totalEarnings*WithFee = lifetime + unrealized + current × 0.9
Lifetime and unrealized are never multiplied by the keep-rate. Equivalent form: totalWithFee = total − current × 0.1. Never display total × 0.9.
Show totalEarningsByTokenWithFee / totalEarningsByChainWithFee as the primary numbers in your product UI (parity with the Zyfai app). Gross fields remain available for debugging or advanced transparency.
Signature
getOnchainEarnings(walletAddress: string): Promise<OnchainEarningsResponse>
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
walletAddress | string | ✅ | Smart wallet address (not the EOA) |
Returns
Onchain earnings data with gross and net-of-fee totals by token and by chain.
Return Type
// Token-keyed earnings — amounts as decimal strings: { "USDC": "421.315354", "WETH": "0.000009" }
type TokenEarnings = Record<string, string>;
// Chain + token-keyed earnings: { "8453": { "USDC": "324.31" }, "42161": { "USDC": "97.00" } }
type ChainTokenEarnings = Record<string, TokenEarnings>;
interface OnchainEarningsResponse {
success: boolean;
data: OnchainEarnings;
}
interface OnchainEarnings {
walletAddress: string;
totalEarningsByToken: TokenEarnings; // gross
totalEarningsByTokenWithFee: TokenEarnings; // SHOW THIS
totalEarningsByChain?: ChainTokenEarnings; // gross
totalEarningsByChainWithFee?: ChainTokenEarnings; // SHOW THIS
lastCheckTimestamp?: string;
lastLogDate?: Record<string, string | null>;
}
All earnings values are decimal strings (e.g. "421.315354"), not numbers. Parse them with parseFloat() or a BigNumber library when doing arithmetic.
Fee model (short)
| Component | Pending fee applied? |
|---|---|
current earnings | Yes — pending fee = current × 0.1 |
lifetime earnings | No — fee already crystallised |
unrealized earnings | No — treat like lifetime for display |
Example
const earnings = await sdk.getOnchainEarnings("0x...");
const {
totalEarningsByToken,
totalEarningsByTokenWithFee,
totalEarningsByChain,
totalEarningsByChainWithFee,
} = earnings.data;
// Gross vs net (user-facing)
console.log("Total USDC (gross):", totalEarningsByToken["USDC"]);
console.log("Net USDC (show this):", totalEarningsByTokenWithFee["USDC"]);
// Per-chain breakdown (prefer *WithFee)
console.log("Base USDC net:", totalEarningsByChainWithFee?.["8453"]?.["USDC"]);
console.log("Arbitrum USDC net:", totalEarningsByChainWithFee?.["42161"]?.["USDC"]);
// Parse for display
const netUsdc = parseFloat(totalEarningsByTokenWithFee["USDC"] ?? "0");
console.log(`$${netUsdc.toFixed(2)} USDC earned (net of fee)`);
Refresh flow
If GET returns no V2 snapshot, call calculateOnchainEarnings first, then retry:
try {
const earnings = await sdk.getOnchainEarnings(smartWallet);
// use earnings.data.totalEarningsByTokenWithFee
} catch {
await sdk.calculateOnchainEarnings(smartWallet);
const earnings = await sdk.getOnchainEarnings(smartWallet);
}
Notes
- Input is the smart wallet address — resolve from EOA via
getSmartWalletAddress/getSmartWalletByEOA - For portfolio balances net of the same pending fee, see getPortfolio
- Prefer calculateOnchainEarnings to refresh before reading if data is missing or stale