Skip to main content

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.

User-facing display

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

ParameterTypeRequiredDescription
walletAddressstringSmart 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>;
}
Amounts are strings

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)

ComponentPending fee applied?
current earningsYes — pending fee = current × 0.1
lifetime earningsNo — fee already crystallised
unrealized earningsNo — 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