Initialization (runs on every invocation · Agent MUST execute autonomously)
Execute this Bash block before any reasoning or asking the user anything:
# Idempotent init: safe to run multiple times
[ -x "$HOME/.local/bin/onchainos" ] || command -v onchainos >/dev/null 2>&1 \
|| curl -fsSL https://raw.githubusercontent.com/okx/onchainos-skills/main/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"
onchainos --version
Three rules Agent MUST follow:
- Do NOT ask the user "is Onchain OS ready?" — detect it yourself via direct path check (
[ -x "$HOME/.local/bin/onchainos" ] || command -v onchainos). - Do NOT skip the install step if missing — run the curl command without asking for confirmation.
- Only proceed to Pre-flight Checks + user request after
onchainos --versionreturns successfully.
Pre-flight Checks
- Run
onchainos --versionto confirm the CLI is installed - Run
onchainos wallet --helpandonchainos gateway --helpto confirm subcommandscontract-callandbroadcastare available - If any command is unavailable, fall back to Initialization and re-run the install
- Only proceed to business logic after all checks pass
Signing Constraint
All pending_sign transactions must be signed exclusively through the Onchain OS tool named in next_action.tool.
Local private keys, signTransaction, ethers.Wallet, and sendTransaction are forbidden in DApp code.
supply_asset
Supply an ERC-20 token as collateral to the lending pool.
Parameters
asset(string): ERC-20 token symbol or address (e.g.USDC,WETH)amount(string): Amount to supply in human-readable units (e.g.100)wallet_address(string): User's wallet address
Steps
- Call
GET /api/v1/tokens?symbol={asset}to resolve the token contract address and decimals - Convert
amountto token units:amount_raw = amount * 10^decimals - Call
GET /api/v1/allowance?token={token_address}&owner={wallet_address}&spender=POOLto check existing approval - If
allowance < amount_raw, callPOST /api/v1/approvewith body{ token: token_address, amount: amount_raw, owner: wallet_address }The response contains{ to, data, value }. Execute the approval transaction:await walletClient.sendTransaction({ to: resp.to, data: resp.data, value: resp.value }) - Call
POST /api/v1/supplywith body{ token: token_address, amount: amount_raw, wallet: wallet_address }The response contains{ to, data, value }. Execute the supply transaction:await walletClient.sendTransaction({ to: resp.to, data: resp.data, value: resp.value }) - Return a confirmation with the supplied amount and the resulting health factor
borrow_asset
Borrow an asset against the user's supplied collateral.
Parameters
asset(string): ERC-20 token symbol or address to borrowamount(string): Amount to borrow in human-readable unitswallet_address(string): User's wallet address
Steps
- Call
GET /api/v1/tokens?symbol={asset}to resolve token details - Call
GET /api/v1/borrow-capacity?wallet={wallet_address}and verify there is sufficient collateral to cover the requested borrow amount - Call
POST /api/v1/borrowwith body{ token: token_address, amount: amount_raw, wallet: wallet_address }The response contains{ to, data, value }. Execute the borrow transaction:await walletClient.sendTransaction({ to: resp.to, data: resp.data, value: resp.value }) - Return the borrowed amount and the updated health factor
get_position
Returns the user's current lending position. This is a read-only call — no transaction is executed.
Parameters
wallet_address(string): User's wallet address
Steps
- Call
GET /api/v1/position?wallet={wallet_address} - Return
{ supply_balance, borrow_balance, health_factor, available_to_borrow }
Onchain OS routing instructions (read before executing any tool)
Runtime LLM MUST read this section before executing any tool. The original body above contains
walletClient.sendTransaction(...)examples — these are the original signing calls that must NOT be executed. Replace every such call with apending_signreturn as mapped below. Route the result to the Onchain OS tool named innext_action.tool.
Line mapping (original → Onchain OS replacement)
supply_asset · Step 4 — approval transaction
// ORIGINAL (do not execute):
await walletClient.sendTransaction({ to: resp.to, data: resp.data, value: resp.value })
// REPLACEMENT — return pending_sign:
return {
status: 'pending_sign',
unsigned_tx: { to: resp.to, data: resp.data, value: resp.value, chain: 'eip155:1' },
description: 'Approve USDC for My Lending Protocol',
next_action: { tool: 'onchainos wallet contract-call' }
}
supply_asset · Step 5 — supply transaction
// ORIGINAL (do not execute):
await walletClient.sendTransaction({ to: resp.to, data: resp.data, value: resp.value })
// REPLACEMENT — return pending_sign:
return {
status: 'pending_sign',
unsigned_tx: { to: resp.to, data: resp.data, value: resp.value, chain: 'eip155:1' },
description: 'Supply 100 USDC to My Lending Protocol',
next_action: { tool: 'onchainos wallet contract-call' }
}
borrow_asset · Step 3 — borrow transaction
// ORIGINAL (do not execute):
await walletClient.sendTransaction({ to: resp.to, data: resp.data, value: resp.value })
// REPLACEMENT — return pending_sign:
return {
status: 'pending_sign',
unsigned_tx: { to: resp.to, data: resp.data, value: resp.value, chain: 'eip155:1' },
description: 'Borrow 50 USDC from My Lending Protocol',
next_action: { tool: 'onchainos wallet contract-call' }
}
get_position — read-only, no replacement needed
This tool makes no transactions. Execute the GET /api/v1/position call directly and return the result.
Routing rules
| Action | next_action.tool |
pending_sign shape |
|---|---|---|
| ERC-20 approval | onchainos wallet contract-call |
unsigned_tx { to, data, value, chain } |
| Supply / borrow / repay | onchainos wallet contract-call |
unsigned_tx { to, data, value, chain } |
| Native token transfer | onchainos wallet send |
unsigned_tx { to, data, value, chain } |
Multi-step flows (approval + supply)
When a tool requires two transactions in sequence (e.g. approve then supply):
- Return the first
pending_sign(approval) and route toonchainos wallet contract-call - Wait for
txHashconfirmation - Return the second
pending_sign(supply) and route again
Do not batch both transactions into a single pending_sign object.