💰 Wallet Skill
Multi-chain wallet for EVM (DeBank-supported chains) + Solana. Balances,
transfers, signing, and policy management. Script skill — call the functions
below via bash; no wallet tools are registered.
How to call
All read/transfer/sign operations are Python functions in core.skill_tools.wallet.
Run them from bash and read the JSON result:
python3 -c "from core.skill_tools import wallet; import json; print(json.dumps(wallet.wallet_balance(chain='base')))"
The one operation that is NOT a script function is proposing a wallet policy
— it needs to render a confirmation card in the UI, so it goes through the native
frontend_action tool (see Policy Management below).
Functions (from core.skill_tools import wallet)
| Function |
Description |
wallet_info() |
Get all wallet addresses |
wallet_balance(chain, address="", asset="") |
EVM balance on a chain (DeBank). chain required |
wallet_sol_balance(address="", asset="") |
Solana balance (Birdeye) |
wallet_get_all_balances(evm_address="", sol_address="") |
All chains at once |
wallet_transfer(to, amount, chain_id=1, data="", **kw) |
Broadcast EVM tx (gas sponsored by default) |
wallet_sign_transaction(to, amount, chain_id=1, data="", **kw) |
Sign EVM tx (no broadcast) |
wallet_sign(message) |
EIP-191 message signing |
wallet_sign_typed_data(domain, types, primaryType, message) |
EIP-712 typed data signing |
wallet_transactions(chain="ethereum", asset="", limit=20) |
EVM tx history |
wallet_sol_transfer(transaction, caip2=...) |
Broadcast Solana tx (base64) |
wallet_sol_sign_transaction(transaction) |
Sign Solana tx (no broadcast) |
wallet_sol_sign(message) |
Solana message signing |
wallet_sol_transactions(chain="solana", asset="sol", limit=20) |
Solana tx history |
wallet_get_policy(chain_type="ethereum") |
Check policy status |
validate_and_clean_rules(rules, chain_type) |
Pre-validate policy rules before proposing |
Key Facts
- Amounts are in wei for EVM (
wallet_transfer / wallet_sign_transaction). 0.01 ETH = 10000000000000000. For ERC-20 token sends, amount is 0 (native) and the transfer is encoded in data calldata.
- Gas is sponsored by default on EVM chains — user doesn't need native tokens for gas. Falls back to user-paid if unavailable. Pass
sponsor=False to pay gas from wallet balance.
- Policy default: OFF (allow-all). Only when policy is enabled do transactions need UI confirmation.
- Supported EVM chains: All DeBank-supported chains. Common names auto-mapped (e.g.
avalanche → avax, bsc → bsc, zksync → era). Fallback aliases include ethereum/base/arbitrum/optimism/polygon/linea/bsc/avalanche/fantom/gnosis/zksync/scroll/blast/mantle/celo/aurora plus monad/world/unichain/abstract/sonic/berachain.
- Balance sources: DeBank (EVM), Birdeye (Solana), wallet-service (fallback). DeBank/Birdeye keys are auto-injected by sc-proxy.
Workflows
Check balances
python3 -c "from core.skill_tools import wallet; import json; print(json.dumps(wallet.wallet_balance(chain='base')))"
python3 -c "from core.skill_tools import wallet; import json; print(json.dumps(wallet.wallet_get_all_balances()))"
Send a transaction (EVM)
Always verify balance before, and the result/history after.
# 1. check
python3 -c "from core.skill_tools import wallet; import json; print(json.dumps(wallet.wallet_balance(chain='base')))"
# 2. transfer (amount in wei)
python3 -c "from core.skill_tools import wallet; import json; print(json.dumps(wallet.wallet_transfer(to='0x...', amount='10000000000000000', chain_id=8453)))"
# 3. verify
python3 -c "from core.skill_tools import wallet; import json; print(json.dumps(wallet.wallet_transactions(chain='base')))"
Sign EIP-712 typed data
python3 -c "from core.skill_tools import wallet; import json; print(json.dumps(wallet.wallet_sign_typed_data(domain={...}, types={...}, primaryType='Permit', message={...})))"
Policy Management
Checking policy is a script function; proposing a policy uses the native
frontend_action tool (it renders a signature card in the UI — a script cannot).
- Check current policy:
python3 -c "from core.skill_tools import wallet; import json; print(json.dumps(wallet.wallet_get_policy(chain_type='ethereum')))"
- (Optional) pre-validate rules:
python3 -c "from core.skill_tools import wallet; import json; print(json.dumps(wallet.validate_and_clean_rules([...], 'ethereum')))"
- Propose — call the
frontend_action tool (not a script):frontend_action(action_type="update_wallet_policy", chain_type="ethereum", rules=[...])
The user confirms + signs in the UI. Call once per chain (EVM + Solana = two calls).
Standard Wildcard Policy (when needed)
rules = [
{"name": "Deny key export", "method": "exportPrivateKey", "conditions": [], "action": "DENY"},
{"name": "Allow all", "method": "*", "conditions": [], "action": "ALLOW"},
]
Policy Modes — CRITICAL DECISION TABLE
⚠️ DENY > ALLOW in Privy. DENY * overrides ALL ALLOW rules. NEVER mix them.
| Mode |
Rules |
Effect |
| Allow-all (default) |
DENY exportPrivateKey + ALLOW * |
Everything allowed except key export |
| Deny-all (lockdown) |
DENY exportPrivateKey + DENY * |
Nothing works. No ALLOW rules! |
| Whitelist (selective) |
DENY exportPrivateKey + specific ALLOW rules only |
Only whitelisted ops work, rest implicitly denied |
Mode 1: Allow-All (Standard Wildcard)
rules = [
{"name": "Deny key export", "method": "exportPrivateKey", "conditions": [], "action": "DENY"},
{"name": "Allow all", "method": "*", "conditions": [], "action": "ALLOW"},
]
Mode 2: Deny-All (Lockdown)
rules = [
{"name": "Deny key export", "method": "exportPrivateKey", "conditions": [], "action": "DENY"},
{"name": "Deny all actions", "method": "*", "conditions": [], "action": "DENY"},
]
# ⚠️ NO ALLOW rules here — DENY * would override them!
Mode 3: Whitelist (Selective Allow)
rules = [
{"name": "Deny key export", "method": "exportPrivateKey", "conditions": [], "action": "DENY"},
{"name": "Allow transfer to Uniswap", "method": "eth_sendTransaction", "conditions": [
{"field_source": "ethereum_transaction", "field": "to", "operator": "eq", "value": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"}
], "action": "ALLOW"},
]
# ⚠️ NO "DENY *" here! enabled=true already denies everything not ALLOWed.
# Adding DENY * would override the ALLOW rules above (DENY > ALLOW).
Privy Policy Rules — Key Constraints
| Rule |
Details |
| Default behavior |
enabled=true → deny-all unless explicitly ALLOWed |
| DENY > ALLOW |
DENY always wins when both match |
| Empty conditions |
Only exportPrivateKey and * (wildcard) allow conditions: [] |
| TX methods need conditions |
eth_sendTransaction, eth_signTransaction, eth_signTypedData_v4, eth_signUserOperation, signAndSendTransaction, etc. ALL require ≥1 condition |
| Valid field_sources |
EVM: ethereum_transaction (to/value/chain_id), ethereum_calldata (function_name), ethereum_typed_data_domain (chainId/verifyingContract), ethereum_typed_data_message, system |
| Valid operators |
eq, gt, gte, lt, lte, in (array, max 100 values) |
| Dual chain |
Call frontend_action(action_type="update_wallet_policy", ...) TWICE for EVM + Solana |
Gotchas
- Policy proposal goes through the
frontend_action tool — needs an active SSE session (won't work from a background task).
wallet_balance requires chain — use wallet_get_all_balances for discovery.
- For both EVM + Solana policy, call
frontend_action TWICE (one per chain_type).
1---2name: wallet3description: Multi-chain wallet: EVM and Solana balances, transfers, signing, and policy. Use when checking balances, sending tokens, signing typed data, or proposing a wallet policy (e.g. send 10 USDC on Base, sign EIP-712, Solana balance).4---56# 💰 Wallet Skill78Multi-chain wallet for EVM (DeBank-supported chains) + Solana. Balances,9transfers, signing, and policy management. **Script skill** — call the functions10below via bash; no wallet tools are registered.1112## How to call1314All read/transfer/sign operations are Python functions in `core.skill_tools.wallet`.15Run them from bash and read the JSON result:1617```bash18python3 -c "from core.skill_tools import wallet; import json; print(json.dumps(wallet.wallet_balance(chain='base')))"19```2021The **one** operation that is NOT a script function is proposing a wallet policy22— it needs to render a confirmation card in the UI, so it goes through the native23`frontend_action` tool (see Policy Management below).2425## Functions (`from core.skill_tools import wallet`)2627| Function | Description |28|----------|-------------|29| `wallet_info()` | Get all wallet addresses |30| `wallet_balance(chain, address="", asset="")` | EVM balance on a chain (DeBank). `chain` required |31| `wallet_sol_balance(address="", asset="")` | Solana balance (Birdeye) |32| `wallet_get_all_balances(evm_address="", sol_address="")` | All chains at once |33| `wallet_transfer(to, amount, chain_id=1, data="", **kw)` | **Broadcast** EVM tx (gas sponsored by default) |34| `wallet_sign_transaction(to, amount, chain_id=1, data="", **kw)` | Sign EVM tx (no broadcast) |35| `wallet_sign(message)` | EIP-191 message signing |36| `wallet_sign_typed_data(domain, types, primaryType, message)` | EIP-712 typed data signing |37| `wallet_transactions(chain="ethereum", asset="", limit=20)` | EVM tx history |38| `wallet_sol_transfer(transaction, caip2=...)` | **Broadcast** Solana tx (base64) |39| `wallet_sol_sign_transaction(transaction)` | Sign Solana tx (no broadcast) |40| `wallet_sol_sign(message)` | Solana message signing |41| `wallet_sol_transactions(chain="solana", asset="sol", limit=20)` | Solana tx history |42| `wallet_get_policy(chain_type="ethereum")` | Check policy status |43| `validate_and_clean_rules(rules, chain_type)` | Pre-validate policy rules before proposing |4445## Key Facts4647- **Amounts are in wei** for EVM (`wallet_transfer` / `wallet_sign_transaction`). 0.01 ETH = `10000000000000000`. For ERC-20 token sends, `amount` is `0` (native) and the transfer is encoded in `data` calldata.48- **Gas is sponsored by default** on EVM chains — user doesn't need native tokens for gas. Falls back to user-paid if unavailable. Pass `sponsor=False` to pay gas from wallet balance.49- **Policy default: OFF** (allow-all). Only when policy is enabled do transactions need UI confirmation.50- **Supported EVM chains**: All DeBank-supported chains. Common names auto-mapped (e.g. `avalanche` → `avax`, `bsc` → `bsc`, `zksync` → `era`). Fallback aliases include ethereum/base/arbitrum/optimism/polygon/linea/bsc/avalanche/fantom/gnosis/zksync/scroll/blast/mantle/celo/aurora **plus** monad/world/unichain/abstract/sonic/berachain.51- **Balance sources**: DeBank (EVM), Birdeye (Solana), wallet-service (fallback). DeBank/Birdeye keys are auto-injected by sc-proxy.5253## Workflows5455### Check balances56```bash57python3 -c "from core.skill_tools import wallet; import json; print(json.dumps(wallet.wallet_balance(chain='base')))"58python3 -c "from core.skill_tools import wallet; import json; print(json.dumps(wallet.wallet_get_all_balances()))"59```6061### Send a transaction (EVM)62Always verify balance before, and the result/history after.63```bash64# 1. check65python3 -c "from core.skill_tools import wallet; import json; print(json.dumps(wallet.wallet_balance(chain='base')))"66# 2. transfer (amount in wei)67python3 -c "from core.skill_tools import wallet; import json; print(json.dumps(wallet.wallet_transfer(to='0x...', amount='10000000000000000', chain_id=8453)))"68# 3. verify69python3 -c "from core.skill_tools import wallet; import json; print(json.dumps(wallet.wallet_transactions(chain='base')))"70```7172### Sign EIP-712 typed data73```bash74python3 -c "from core.skill_tools import wallet; import json; print(json.dumps(wallet.wallet_sign_typed_data(domain={...}, types={...}, primaryType='Permit', message={...})))"75```7677## Policy Management7879Checking policy is a script function; **proposing** a policy uses the native80`frontend_action` tool (it renders a signature card in the UI — a script cannot).81821. Check current policy:83 ```bash84 python3 -c "from core.skill_tools import wallet; import json; print(json.dumps(wallet.wallet_get_policy(chain_type='ethereum')))"85 ```862. (Optional) pre-validate rules:87 ```bash88 python3 -c "from core.skill_tools import wallet; import json; print(json.dumps(wallet.validate_and_clean_rules([...], 'ethereum')))"89 ```903. Propose — call the **`frontend_action` tool** (not a script):91 ```92 frontend_action(action_type="update_wallet_policy", chain_type="ethereum", rules=[...])93 ```94 The user confirms + signs in the UI. **Call once per chain** (EVM + Solana = two calls).9596### Standard Wildcard Policy (when needed)97```98rules = [99 {"name": "Deny key export", "method": "exportPrivateKey", "conditions": [], "action": "DENY"},100 {"name": "Allow all", "method": "*", "conditions": [], "action": "ALLOW"},101]102```103104### Policy Modes — CRITICAL DECISION TABLE105106⚠️ **DENY > ALLOW in Privy.** `DENY *` overrides ALL ALLOW rules. NEVER mix them.107108| Mode | Rules | Effect |109|------|-------|--------|110| **Allow-all** (default) | `DENY exportPrivateKey` + `ALLOW *` | Everything allowed except key export |111| **Deny-all** (lockdown) | `DENY exportPrivateKey` + `DENY *` | Nothing works. No ALLOW rules! |112| **Whitelist** (selective) | `DENY exportPrivateKey` + specific ALLOW rules only | Only whitelisted ops work, rest implicitly denied |113114### Mode 1: Allow-All (Standard Wildcard)115```116rules = [117 {"name": "Deny key export", "method": "exportPrivateKey", "conditions": [], "action": "DENY"},118 {"name": "Allow all", "method": "*", "conditions": [], "action": "ALLOW"},119]120```121122### Mode 2: Deny-All (Lockdown)123```124rules = [125 {"name": "Deny key export", "method": "exportPrivateKey", "conditions": [], "action": "DENY"},126 {"name": "Deny all actions", "method": "*", "conditions": [], "action": "DENY"},127]128# ⚠️ NO ALLOW rules here — DENY * would override them!129```130131### Mode 3: Whitelist (Selective Allow)132```133rules = [134 {"name": "Deny key export", "method": "exportPrivateKey", "conditions": [], "action": "DENY"},135 {"name": "Allow transfer to Uniswap", "method": "eth_sendTransaction", "conditions": [136 {"field_source": "ethereum_transaction", "field": "to", "operator": "eq", "value": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"}137 ], "action": "ALLOW"},138]139# ⚠️ NO "DENY *" here! enabled=true already denies everything not ALLOWed.140# Adding DENY * would override the ALLOW rules above (DENY > ALLOW).141```142143## Privy Policy Rules — Key Constraints144145| Rule | Details |146|------|---------|147| **Default behavior** | `enabled=true` → deny-all unless explicitly ALLOWed |148| **DENY > ALLOW** | DENY always wins when both match |149| **Empty conditions** | Only `exportPrivateKey` and `*` (wildcard) allow `conditions: []` |150| **TX methods need conditions** | `eth_sendTransaction`, `eth_signTransaction`, `eth_signTypedData_v4`, `eth_signUserOperation`, `signAndSendTransaction`, etc. ALL require ≥1 condition |151| **Valid field_sources** | EVM: `ethereum_transaction` (to/value/chain_id), `ethereum_calldata` (function_name), `ethereum_typed_data_domain` (chainId/verifyingContract), `ethereum_typed_data_message`, `system` |152| **Valid operators** | `eq`, `gt`, `gte`, `lt`, `lte`, `in` (array, max 100 values) |153| **Dual chain** | Call `frontend_action(action_type="update_wallet_policy", ...)` TWICE for EVM + Solana |154155## Gotchas156157- Policy proposal goes through the `frontend_action` tool — needs an active SSE session (won't work from a background task).158- `wallet_balance` requires `chain` — use `wallet_get_all_balances` for discovery.159- For both EVM + Solana policy, call `frontend_action` TWICE (one per chain_type).