Balance endpoint
stridge.balance has one method. It returns a wallet's on-chain holdings — broken down per chain, including the native asset on each chain plus every ERC-20 the upstream feed knows about. Authenticated; goes through projectHttpClient.
| Method | HTTP | Auth | Returns |
|---|---|---|---|
balance.onchain | GET /balance/onchain/{wallet_address} | projectKey required | OnchainBalanceResponse |
balance.onchain
Returns the wallet's holdings, grouped by chain. Backed by the upstream Moralis token-balances feed; the Stridge proxy aggregates per-chain subtotals and a total_usd so callers don't need to roll their own math.
Signature
balance.onchain(
walletAddress: string,
options?: OnchainOptions,
): Promise<OnchainBalanceResponse>
interface OnchainOptions {
/** Comma-separated chain filter. Accepts eip155 ids, slip44 ids, network names, or moralis slugs. */
chains?: string
/** Include tokens with zero balance. Default false. */
includeZero?: boolean
/** Include tokens flagged as spam by the upstream feed. Default false. */
includeSpam?: boolean
/** Minimum per-token USD value to include, decimal-string-encoded. Default "0". */
minUsd?: string
signal?: AbortSignal
}walletAddress is treated as opaque — pass a 0x… EVM address for any EIP-155 chain or a Tron address for Tron-family chains. The gateway resolves the right upstream feed per chain.
Example — single call
const balance = await stridge.balance.onchain(walletAddress)
console.log(`${balance.wallet_address} — total $${balance.total_usd}`)
for (const chain of balance.chains) {
console.log(` ${chain.label}: $${chain.subtotal_usd}`)
for (const token of chain.tokens) {
console.log(` ${token.symbol} ${token.amount} ($${token.amount_usd})`)
}
}Example — filtered
// Only Arbitrum + Base, hide tokens worth < $1, hide spam, hide zero balances.
const balance = await stridge.balance.onchain(walletAddress, {
chains: "arbitrum,base",
includeZero: false,
includeSpam: false,
minUsd: "1",
})The filter is applied server-side — the response only carries the rows that survived. The chains field is forwarded verbatim to the upstream feed; the SDK doesn't parse or normalize the value.
Response shape
interface OnchainBalanceResponse {
wallet_address: string // lowercased server-side for 0x… values
fetched_at: string // ISO-8601 of the most recent upstream pull
total_usd: string // sum of chains[].subtotal_usd, decimal string
chains: OnchainChainDto[]
}
interface OnchainChainDto {
eip155_id: number // chain id (numeric)
stridge_network_id: string // stable Stridge id, e.g. "60" for Ethereum
network_name: string // slug, e.g. "ethereum"
label: string // display, e.g. "Ethereum"
scanner_url: string // upstream block-explorer URL for the wallet
native_symbol: string // gas-token ticker, e.g. "ETH"
subtotal_usd: string // sum of tokens[].amount_usd on this chain
tokens: OnchainTokenDto[]
}
interface OnchainTokenDto {
token_address: string // contract; empty string when is_native
symbol: string // e.g. "USDC"
name: string // display name
decimals: number
logo: string
is_native: boolean
is_spam: boolean // upstream-flagged spam — drivers skip in user-facing surfaces
raw_amount: string // smallest-unit, string-encoded for precision
amount: string // human-readable decimal string
amount_usd: string // USD value of amount; may be "0" for unpriced tokens
usd_price: string // per-unit USD price
price_change_24h_pct: string // 24h percentage change of usd_price
}Every numeric amount is decimal-string-encoded so values exceeding 2^53 keep precision through JSON.parse. BigInt(raw_amount) is safe for on-chain math. For display, format the decimal string with a precision-preserving library — dnum, Decimal.js, big.js — instead of Number(amount). The Number() coercion silently rounds anything past ~15 significant digits, which is fine for casual UI but bites on full-precision balances, fees, or per-unit prices.
See Models & types for the canonical type imports.
Spam vs routability
The upstream feed flags some tokens as spam, and the SDK forwards that flag through OnchainTokenDto.is_spam. The feed doesn't catch every case, though — some airdropped tokens fabricate a usd_price to bypass the spam heuristic. If you're driving a deposit picker, layer a routability filter on top by intersecting balance.onchain(...) against gateway.assets() — only show tokens the gateway can actually route.
const [{ chains }, { assets: routable }] = await Promise.all([
stridge.balance.onchain(walletAddress),
stridge.gateway.assets(),
])
const routableByNetwork = new Map(
routable.map((c) => [c.network_id, new Set(c.assets.map((a) => a.address.toLowerCase()))]),
)
const depositable = chains.flatMap((chain) =>
chain.tokens.filter((token) => {
if (token.is_spam) return false
const allowed = routableByNetwork.get(chain.stridge_network_id)
if (!allowed) return false
return token.is_native || allowed.has(token.token_address.toLowerCase())
}),
)Errors
balance.onchain is a thin proxy in front of an upstream feed, so transient BackendErrors are normal — upstream rate limits, RPC hiccups, occasional 5xx. Treat any BackendError with statusCode >= 500 as retryable with exponential backoff; surface 4xx errors to the caller as-is. See Errors & retries for the recommended retry pattern.
Related
- Gateway endpoints —
gateway.assets()for the merchant-scoped routable catalogue. - API client — why
balance.*requiresprojectKey. - Errors & retries — retry policy for the upstream-fed reads.
- Models & types —
OnchainBalanceResponse,OnchainChainDto,OnchainTokenDto.