okx-wallet-portfolio
Original:🇺🇸 English
Translated
This skill should be used when the user asks to 'check my wallet balance', 'show my token holdings', 'how much ETH do I have', 'what tokens do I have', 'check my portfolio value', 'view my assets', 'how much is my portfolio worth', 'what\'s in my wallet', or mentions checking wallet balance, total assets, token holdings, portfolio value, remaining funds, DeFi positions, or multi-chain balance lookup. Supports Solana, Ethereum, Base, BSC, Polygon, Arbitrum, and 20+ other chains. Do NOT use for general programming questions about balance variables or API documentation. Do NOT use when the user is asking how to build or integrate a balance feature into code.
122installs
Sourceokx/onchainos-skills
Added on
NPX Install
npx skill4agent add okx/onchainos-skills okx-wallet-portfolioTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →OKX DEX Balance API
4 endpoints for supported chains, wallet total value, all token balances, and specific token balances.
Base URL:
https://web3.okx.comBase path:
/api/v6/dex/balanceAuth: HMAC-SHA256 signature, 4 headers required (, , , )
OK-ACCESS-KEYOK-ACCESS-SIGNOK-ACCESS-PASSPHRASEOK-ACCESS-TIMESTAMPAuthentication & Credentials
API Key Application: OKX Developer Portal
Setup Guide: Developer Portal Docs
Read credentials from environment variables:
- → API key
OKX_API_KEY - → Secret key (system-generated)
OKX_SECRET_KEY - → Passphrase (developer-supplied)
OKX_PASSPHRASE
Never output the above credentials to logs, response content, or any user-visible interface.
typescript
import crypto from 'crypto';
const BASE = 'https://web3.okx.com';
// Signature rule:
// GET → body = "", requestPath includes query string (e.g., "/api/v6/dex/balance/all-token-balances-by-address?address=0x...&chains=1,56")
// POST → body = JSON string of request body, requestPath is path only (e.g., "/api/v6/dex/balance/token-balances-by-address")
async function okxFetch(method: 'GET' | 'POST', path: string, body?: object) {
const timestamp = new Date().toISOString();
const bodyStr = body ? JSON.stringify(body) : '';
const sign = crypto
.createHmac('sha256', process.env.OKX_SECRET_KEY!)
.update(timestamp + method + path + bodyStr)
.digest('base64');
const headers: Record<string, string> = {
'OK-ACCESS-KEY': process.env.OKX_API_KEY!,
'OK-ACCESS-SIGN': sign,
'OK-ACCESS-PASSPHRASE': process.env.OKX_PASSPHRASE!,
'OK-ACCESS-TIMESTAMP': timestamp,
'Content-Type': 'application/json',
};
const res = await fetch(`${BASE}${path}`, {
method,
headers,
...(body && { body: bodyStr }),
});
if (res.status === 429) throw { code: 'RATE_LIMITED', msg: 'Rate limited — retry with backoff', retryable: true };
if (res.status >= 500) throw { code: `HTTP_${res.status}`, msg: 'Server error', retryable: true };
const json = await res.json();
if (json.code !== '0') throw { code: json.code, msg: json.msg || 'API error', retryable: false };
return json.data;
}Response envelope: . = means success.
{ "code": "0", "data": [...], "msg": "" }code"0"Developer Quickstart
typescript
// Get all token balances (GET)
const balances = await okxFetch('GET', '/api/v6/dex/balance/all-token-balances-by-address?' + new URLSearchParams({
address: '0xYourWallet', chains: '1,56,137',
}));
// → balances[].tokenAssets[]: { symbol, balance (UI units), tokenPrice, rawBalance }
// Check specific tokens (POST)
const specific = await okxFetch('POST', '/api/v6/dex/balance/token-balances-by-address', {
address: '0xYourWallet',
tokenContractAddresses: [
{ chainIndex: '1', tokenContractAddress: '' }, // native ETH
{ chainIndex: '1', tokenContractAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' }, // USDC
],
});Common Chain IDs
| Chain | chainIndex | Chain | chainIndex |
|---|---|---|---|
| Ethereum | | Arbitrum | |
| BSC | | Base | |
| Polygon | | Solana | |
Address format note: The same parameter is only valid across chains of the same type. EVM addresses () work on Ethereum/BSC/Polygon/Arbitrum/Base etc. Solana addresses (Base58) and Bitcoin addresses (UTXO) have different formats. Do NOT mix — e.g., passing an EVM address with will return empty data for the Solana portion.
address0x...chains="1,501"Endpoint Index
| # | Method | Path | Docs |
|---|---|---|---|
| 1 | GET | | supported-chain |
| 2 | GET | | total-value-by-address |
| 3 | GET | | all-token-balances-by-address |
| 4 | POST | | token-balances-by-address |
Error Codes: Balance Error Codes
Cross-Skill Workflows
This skill is often used before swap (to verify sufficient balance) or as portfolio entry point. Below are typical end-to-end flows showing how data flows between skills.
Workflow A: Pre-Swap Balance Check
User: "Swap 1 SOL for BONK"
1. okx-dex-token /market/token/search?search=BONK → get tokenContractAddress
2. okx-wallet-portfolio /balance/all-token-balances-by-address → verify SOL balance >= 1
↓ balance field (UI units) → convert to minimal units for swap
3. okx-dex-swap /aggregator/quote → get quote
4. okx-dex-swap /aggregator/swap-instruction → execute (Solana)Data handoff:
- from token search → feeds into swap
tokenContractAddress/fromTokenAddresstoTokenAddress - from balance API is UI units; swap API needs minimal units → multiply by
balance10^decimal - If balance < required amount → inform user, do NOT proceed to swap
Workflow B: Portfolio Overview + Analysis
User: "Show my portfolio"
1. okx-wallet-portfolio /balance/total-value-by-address → total USD value
2. okx-wallet-portfolio /balance/all-token-balances-by-address → per-token breakdown
↓ top holdings by USD value
3. okx-dex-token /market/price-info → enrich with 24h change, market cap, liquidity
4. okx-dex-market /market/candles (optional) → price charts for tokens of interestWorkflow C: Sell Underperforming Tokens
1. okx-wallet-portfolio /balance/all-token-balances-by-address → list all holdings
↓ tokenContractAddress + chainIndex for each
2. okx-dex-token /market/price-info → get priceChange24H per token
3. Filter by negative change → user confirms which to sell
4. okx-dex-swap /aggregator/quote → /aggregator/swap → execute sellKey conversion: (UI units) × = (minimal units) for swap API.
balance10^decimalamountOperation Flow
Step 1: Identify Intent
- Check total assets ->
GET /balance/total-value-by-address - View all token holdings ->
GET /balance/all-token-balances-by-address - Check specific token balance ->
POST /balance/token-balances-by-address - Unsure which chains are supported -> first
GET /balance/supported/chain
Step 2: Collect Parameters
- Missing wallet address -> ask user
- Missing target chains -> ask user, or suggest common set ()
"1,56,137,42161,8453" - Need to filter risky tokens -> set (only works on ETH/BSC/SOL/BASE)
excludeRiskToken=true
Step 3: Call and Display
- Total value: display USD amount
- Token balances: show token name, amount (UI units), USD value
- Sort by USD value descending
Step 4: Suggest Next Steps
After displaying results, suggest 2-3 relevant follow-up actions based on the endpoint just called:
| Just called | Suggest |
|---|---|
| 1. View token-level breakdown → |
| 1. View detailed analytics (market cap, 24h change) for a token → |
| 1. View full portfolio across all tokens → |
Present conversationally, e.g.: "Would you like to see the price chart for your top holding, or swap any of these tokens?" — never expose skill names or endpoint paths to the user.
API Reference
1. GET /balance/supported/chain
No request parameters.
Response:
| Field | Type | Description |
|---|---|---|
| String | Chain name (e.g., "Ethereum") |
| String | Chain logo URL |
| String | Chain short name (e.g., "ETH") |
| String | Chain unique identifier (e.g., "1") |
json
{ "code": "0", "data": [{ "name": "Ethereum", "logoUrl": "...", "shortName": "ETH", "chainIndex": "1" }], "msg": "" }2. GET /balance/total-value-by-address
type warning: This endpoint usesexcludeRiskToken(Boolean/true), but endpoints #3 and #4 usefalse(String/"0"). This is an OKX API inconsistency — pay attention to the type per endpoint."1"
| Param | Type | Required | Description |
|---|---|---|---|
| String | Yes | Wallet address |
| String | Yes | Chain IDs, comma-separated, max 50. e.g., |
| String | No | |
| Boolean | No | |
Response:
| Field | Type | Description |
|---|---|---|
| String | Total asset value in USD |
json
{ "code": "0", "data": [{ "totalValue": "1172.895057177065864522" }], "msg": "success" }3. GET /balance/all-token-balances-by-address
| Param | Type | Required | Description |
|---|---|---|---|
| String | Yes | Wallet address |
| String | Yes | Chain IDs, comma-separated, max 50 |
| String | No | |
Response key fields: — , , , (UI units), (base units), (USD), . Full fields: see docs.
tokenAssets[]chainIndextokenContractAddresssymbolbalancerawBalancetokenPriceisRiskTokenjson
{
"code": "0",
"data": [{
"tokenAssets": [{
"chainIndex": "1", "tokenContractAddress": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
"symbol": "WBTC", "balance": "0.5", "rawBalance": "50000000",
"tokenPrice": "65000.00", "isRiskToken": false,
"address": "0xEd0C6079229E2d407672a117c22b62064f4a4312"
}]
}],
"msg": "success"
}4. POST /balance/token-balances-by-address
| Param | Type | Required | Description |
|---|---|---|---|
| String | Yes | Wallet address |
| Array | Yes | Max 20 items |
| String | Yes | Chain ID (e.g., |
| String | Yes | Token address ( |
| String | No | |
Response: Same schema as endpoint #3.
json
{
"code": "0",
"data": [{
"tokenAssets": [{
"chainIndex": "1", "tokenContractAddress": "",
"symbol": "eth", "balance": "1.5", "tokenPrice": "3640.43",
"isRiskToken": false, "rawBalance": "1500000000000000000", "address": "0x..."
}]
}],
"msg": "success"
}Input / Output Examples
User says: "Check my wallet 0xABC... total assets on Ethereum and BSC"
GET /api/v6/dex/balance/total-value-by-address?address=0xabc...&chains=1,56
-> Display: Total assets $12,345.67User says: "Show all tokens in my wallet"
GET /api/v6/dex/balance/all-token-balances-by-address?address=0xabc...&chains=1,56
-> Display:
ETH: 1.5 ($4,500.00)
USDC: 2,000 ($2,000.00)
BNB: 3.2 ($1,920.00)
...User says: "Only check USDC and USDT balances"
POST /api/v6/dex/balance/token-balances-by-address
Body: {
"address": "0xabc...",
"tokenContractAddresses": [
{ "chainIndex": "1", "tokenContractAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" },
{ "chainIndex": "1", "tokenContractAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7" }
]
}
-> Display: USDC: 2,000 ($2,000.00), USDT: 1,500 ($1,500.00)Edge Cases
- Zero balance: valid state — display , not an error
$0.00 - Unsupported chain: call first to confirm
/balance/supported/chain - chains exceeds 50: split into batches, max 50 per request
- not working: only supported on ETH/BSC/SOL/BASE
excludeRiskToken - DeFi positions: use to query DeFi holdings separately
assetType=2 - 429 rate limit: exponential backoff with jitter. See Rate Limit & Fee Docs for tier-specific RPS limits (Trial: 1 RPS, Start-up: 2-50 RPS, Enterprise: custom).
- Cross-skill pipeline rate limit: when chaining calls across multiple skills (e.g., token search → balance → swap), add 300-500ms delay between requests to avoid triggering rate limit (error code ).
50011 - Network error: retry once
- Request timeout: all API calls must set a 10-second timeout limit
Amount Display Rules
- Token amounts in UI units (), never base units (
1.5 ETH)1500000000000000000 - USD values with 2 decimal places
- Large amounts in shorthand ()
$1.2M - Sort by USD value descending
Global Notes
- supports up to 50 chain IDs (comma-separated strings)
chains - :
assetType=all0=tokens only1=DeFi only2 - only works on ETH(
excludeRiskToken)/BSC(1)/SOL(56)/BASE(501)8453 - is the only POST endpoint in this family
/token-balances-by-address - Total value is returned as a USD string — handle type conversion
- For token prices / K-lines -> use
okx-dex-market - For token search / metadata -> use
okx-dex-token - For swap execution -> use
okx-dex-swap