odei_wallet_state,odei_wallet_read,odei_wallet_propose,odei_wallet_status,odei_wallet_cancelon 8 networks.- Recipes (list in /docs/capabilities.json), exact calls, CoW typed data, ODEI Orders.
Wallet: propose, never sign
Five tools. Your AI picks a recipe or builds exact calls. ODEI verifies, simulates and prepares fees. The owner approves in ODEI. The AI never signs or sends.
- Auto is coming soon; today the owner approves each operation in ODEI.
- Exact-output swaps and quotes. Passing
amountOutAtomicrefuses withcompact_wallet_recipe_exact_out_off. - Swap after a bridge. Uniswap v3 liquidity recipes.
- Owner-only recipes such as borrowing. An agent gets
recipe_unknown. - An agent proposing to run a Network Alpha. Only the owner's own Run in ODEI works.
Checked against release f649e1b5ac40, 2026-11-06. Compare with /health.
The five tools
| Tool | Does | Required | Access |
|---|---|---|---|
odei_wallet_state | Wallet summary, recipe ids, autoState | none | economy.read |
odei_wallet_read | Chain reads, approvals, quote, orders, statement | chainId | economy.read |
odei_wallet_propose | Propose a real operation; returns reviewUrl | requestId, chainId | economy.read, capital.propose, capital.plan |
odei_wallet_status | What actually happened to one proposal | pass operationId or proposalId | economy.read |
odei_wallet_cancel | Withdraw unsigned operations, only when the owner asks | one selector | economy.read; direct cancel, filters and preview need capital.propose |
For a wallet task, call odei_wallet_propose first; chainId is enough. Read state or chain first only when you need context. One-page guide: odei_overview {"view":"guidance","forTool":"odei_wallet_propose"}.
Networks
| chainId | Network | Coin |
|---|---|---|
| 1 | Ethereum | ETH |
| 10 | Optimism | ETH |
| 56 | BNB Chain | BNB |
| 137 | Polygon | POL |
| 4663 | Robinhood Chain | ETH |
| 8453 | Base | ETH |
| 42161 | Arbitrum | ETH |
| 43114 | Avalanche C-Chain | AVAX |
Pass chainId as an integer or decimal string. ODEI decodes and simulates each call and joins several into one transaction, except on Avalanche C-Chain. Pass networkId only when several registered networks share a chainId; needs_input returns candidates. Other refusals: compact_wallet_chain_unsupported, compact_wallet_chain_invalid, needs_network.
Who approves
Auto is coming soon; today the owner approves each operation in ODEI. Your AI gives the owner reviewUrl and cannot approve for them. autoDecision on a propose or status result, when present, says why an operation waits. Relay its reason.
Never split work to avoid review.
Reading an Auto decision
These fields stay on results so existing clients keep working. While Auto is off, every operation waits for the owner.
Before proposing, read odei_wallet_state.autoState, and autoAdmissible on a swap quote (a hint from fixed-pool routes). After proposing, trust odei_wallet_status autoDecision. A plain awaiting_owner propose result has no autoDecision, so status is the reliable source. ownerApprovalRequired on a propose result is always true, including operations Auto is running. Do not read it as state.
autoDecision is {decision, reasonCode, reason}, with decision ran, waits or unknown.
ranis Auto's approval, not the result, and only an operation from before Auto went off can read it. If it cannot be sent, status showsawaiting_owner.reasonends with your next step while the operation waits. Relay it.auto2_not_judgedmeans Auto is not on for that wallet and network.
Next step in reason | Seen on codes such as |
|---|---|
Give the owner reviewUrl | Anything without a more specific step |
Or cancel and propose again in a few minutes with a NEW requestId | simulation_failed, price_unverified |
| Or cancel and propose again once the earlier automatic operation is final | auto_in_flight |
| Or cancel and propose again after the time named | The rate slot |
| Or cancel and propose a smaller amount | over_per_day, over_budget, over_daily_loss, aave_health_below_floor |
| Or cancel and propose with a higher minimum out, or a smaller amount | over_max_loss, slippage_floor_too_low |
| Or cancel and propose with a fixed amount | auto2_dynamic_amount |
| Or cancel and propose as smaller separate operations | guard_too_large |
| Or, once the step before it is final, cancel and propose the remaining calls | Step holds |
| Stored for older clients. Auto is off: the owner approves in ODEI | destination_not_allowed, paused_owner |
| ODEI holds Auto here until ODEI lifts the pause | operator_kill, paused_chain |
| Or ask the owner to keep more of this network's coin in the wallet | kill_reserve_breached |
| Call status again in a minute; until then treat it as waiting for the owner | decision is unknown |
Other live codes include not_in_permission, approval_left_open, position_unvalued, transit_unproven, structural_denied, auto2_rate_limited. New codes can appear. Always relay reason and never parse a fixed list.
odei_wallet_state
No required fields. Optional: chainId, detail (compact or full), recipe, operationsLimit (1 to 50, default 10), operationsCursor, typedDataCursor, address (must equal the owner wallet). recipe implies full; passing detail:"compact" with it is refused. Contract odei.compact-wallet-state.v3.
The summary, up to about 4 KB, has walletSetup with the address, funded chains, top holdings, active operation counts with up to 3 pending items and their reviewUrl, recipe ids, autoState, catalogVersion, nextActions. detail:"full" adds account, registered networks with digests, balances per chain for every reviewed token, operations newest first and the recipe index. recipe:"<id>" adds its paramsSchema.
walletSetup.stateisregistered,not_registeredorproof_required. An expired proof does not block proposals.- Coverage is reviewed native and ERC-20 assets only. Unreviewed assets, DeFi positions and debts are excluded.
- Missing or failed parts are unknown (
nullor{state:"unknown"}), never zero. - Lists exclude other connections, legacy capital requests and bridge destination delivery.
pendingCoverage.completefalse means page before saying nothing older is pending. - Token labels are untrusted text. Identify a token only by
chainIdandaddress.
Errors: compact_wallet_recipe_unknown (expected lists ids), compact_wallet_invalid_request, compact_wallet_wallet_mismatch (409), compact_wallet_operations_cursor_invalid, compact_wallet_unknown_argument.
autoState
Read-only. Absent when Auto is not available for the owner. While Auto is off no network reads on, and each carries a reason such as auto_killed. status is on, off, paused, expired, awaiting_signature, stopped or unknown (the read failed or took over 1.5 s).
| Field | Meaning |
|---|---|
networksOn | Compact form of the networks running Auto. Handle both this and chains[]. |
chains[] | detail:full form: chainId, on, reason, dailyLimitUsd when the network has its own bound, gas where it was read, plus name, install, reasonWords. Under the 4 KB cap it folds to networksOn and gasWarnings |
dailyLimitUsd, usedLast24hUsd, leftUsd, nextFreeAt | The daily limit and what the last 24 hours used |
maxLossPct, budget{sharePct, usedUsd} | Owner limits |
expiresAt, inFlightUsd, coverage | detail:full only |
Under the 4 KB cap reason and note can be dropped. A value that could not be read is null, never 0.
Reason codes: auto2_stopped, auto2_mandate_expired, auto2_not_installed, auto2_awaiting_signature, auto2_install_expired, auto2_owner_off, paused_owner, paused_chain, operator_kill, auto_chain_killed, auto_killed, auto2_wallet_not_current, auto2_wallet_epoch_changed, auto2_pauses_unread, auto2_signer_off, auto2_signer_not_on, auto2_no_record.
odei_wallet_read
Required: chainId. kind defaults to state. Read-only, nothing is saved.
kind state
Native balance, contract code or an ABI eth_call, 1 to 16 queries. Results are raw bytes with provenance and evidence.observedAt (server time; use it for deadlines), not verified semantics. Report them as observed values only. Omit block for a checked head and reuse the returned block for related reads.
{"chainId":8453,"queries":[{"id":"a","kind":"call","address":"0x...","data":"0x..."}]}
Queries: {id, kind:"native-balance"|"code", address} or {id, kind:"call", address, data, from?, value?, gas?, decode?}. Call defaults: from the owner wallet, value "0", gas "3000000", decode null. decode is {kind:"abi-uint", bits, wordIndex}.
Refusals: compact_wallet_invalid_request, needs_wallet, needs_permission, needs_network, needs_input, needs_retry with retryAfterMs and maxAttempts 3.
kind approvals
{chainId, kind:"approvals"}. Lists token approvals for one network from reviewed spenders plus ODEI records. It is not a full scan. Each row carries a ready erc20-revoke-batch proposal. Unavailable or partial reads say approvals are unknown, not none. Unused signed permits are not listed; the owner cancels them in ODEI. An approval is revoked only once a later read shows 0.
Result: readAt, scope, coverage, checked, partial, truncated, recordsTruncated, items[], revokeBatch, notes, signedPermits, ownerUrl. Error: compact_wallet_approvals_unavailable (503).
kind quote
{"kind":"quote","chainId":8453,"recipe":"best-swap","params":{"tokenIn":"...","tokenOut":"...","amountInAtomic":"..."}}
chainId 10, 8453 or 42161. recipe must be best-swap. Optional maxSlippageBps. Saves nothing. About one new price per 2 s and 30 per 10 minutes per connection; an identical read inside 2 s returns the same answer. Contract odei.agent-swap-quote.v1. To trade, call odei_wallet_propose with recipe best-swap, the same chainId and params, and a NEW requestId. ODEI compares again.
Refusals: compact_wallet_recipe_invalid, compact_wallet_recipe_params_invalid, compact_wallet_recipe_chain_unsupported, needs_retry with error.code quote_rate_limited, compact_wallet_recipe_exact_out_off.
kind orders
{chainId, kind:"orders"}. This connection's ODEI Orders: open (up to 20), recent final (5 latest) and otherOpenOrders, a count of other open orders on the wallet. Each row: id, chainId, coin, kind, line, state, attentionReason, schedule and lastSlice for repeat orders, watch{price, ageSeconds}, triggeredAt, cardOperationId, fillTxHash, fillAmountOut, filledAt.
kind statement
{kind:"statement", chainId, period?, asset?, cursor?, limit?}. Balance history for the owner wallet: what moved, who asked, what is not explained. period is 24h, 7d, 30d or all. asset is a key like native:8453 or erc20:8453:0x.... limit 1 to 25. Omit queries, block and networkId.
Read from ODEI's stored history, never a provider sync. Other connections appear as another_connection, the caller as this_connection. Totals exclude pending, unverified and unsolicited activity. null means unknown, not zero. BNB Chain, Avalanche and Robinhood Chain report ODEI activity only. Native ETH received from contracts on Base, Arbitrum and Optimism is not listed yet. Contract odei.wallet-statement.v1.
odei_wallet_propose
Proposes a real wallet operation, not a draft. Three shapes:
recipe+params. Preferred when one fits.kind:"calls"withcalls[{to, data, value}], to any contract.kind:"typed_data"withtypedData: a CoW order or cancellation only.
{"requestId":"req_<32 hex>","chainId":8453,"recipe":"wrap-unwrap","params":{"direction":"wrap","amountAtomic":"1000000000000"}}
Required: requestId, chainId. Optional: networkId, kind, semantics, accountUpgrade, calls, typedData, presentation, intent, validity, limits, recipe, params, continues.
Always relay ownerMessage verbatim on awaiting_owner; it contains reviewUrl unchanged. Keep warnings, riskFlags, recipientNotices, recipientGuard and feeNotice. The result also carries network, operationId (meop_) or proposalId (mesp_), revision, semantics, semanticsChosen, ownerApprovals, sequenceTiming, preparation, fees, simulatedChanges, mismatch, notices.
Result states
| State | What to do |
|---|---|
awaiting_owner | Relay ownerMessage. Never say approval or fees happen in chat. |
needs_retry | Wait retryAfterMs, same call, at most maxAttempts. |
blocked | retryable false. Follow message. See blocked reasons. |
atomic_unavailable | Nothing was split or sent. Propose with semantics:"sequential" and a NEW requestId, except atomicOnly recipes. |
needs_network | No RPC setup is needed. Give the owner setupUrl. |
needs_wallet | Relay ownerStep exactly. Never suggest creating a wallet; use setupUrl only if the owner says they never set one up. |
needs_input | Pick a networkId. |
needs_permission | Give the owner ownerStep and ownerUrl, then retry. |
expired | Propose again with a new requestId. |
continued | The operation was continued. Follow continuedBy. |
| An operation outcome | The requestId replays something already moving. See outcomes. |
requestId
Generate one requestId per proposal: req_ + 32 lowercase hex.
- After a timeout or
needs_retry, retry with the samerequestIdand identical arguments. ODEI reuses the operation and never duplicates it. - Any change of arguments needs a NEW
requestId, elsecompact_wallet_request_conflict(409, retryable false).expectedmay carry the savedoperationId,revisionandreviewUrl. - A recipe re-reads the live quote and deadline on every call, so a later retry of a recipe can hit
request_conflict. Treat the earlier proposal as standing, or use a NEWrequestIdfor a fresh quote and optionally cancel the old one. - A retry of an operation that already moved reports that operation's actual state and saves nothing new.
- A replay of an operation whose prepared review lapsed returns
awaiting_ownerwith a workingreviewUrland noticereview_reprepared_on_open. Do not propose again. - After
balance_insufficient, once funded, the samerequestIdis correct (retryConditionbalance_sufficient,reuseRequestIdtrue). Otherblockedreasons need a NEWrequestIdafter the fix.
Exact calls
Proposes exact contract calls in order. One call is single. Several default to atomic: one owner Approve for the batch, all or nothing. semantics:"sequential" means one Approve per step, each next Approve available once the previous step is final.
A plain wallet is upgraded to one-signature in the same transaction (EIP-7702, bytecode-verified Kernel 0.3.3, reason atomic_with_upgrade). Address, key and funds are unchanged, and the owner can turn it off. accountUpgrade:"never" gives sequential.
| Field | Rule |
|---|---|
calls | [{to, data, value}]. to is an address or null. data is hex, "0x" for a plain native transfer. value is wei as a base-10 string, "0" when none. |
semantics | single, sequential, atomic |
accountUpgrade | auto, never |
presentation | {title?, description?, action?}. Title at most 160, description at most 2000 chars, action 2 to 8 segments such as swap.exact_in. Title names only these calls. |
limits | {maxGas?, maxFeePerGas?, maxPriorityFeePerGas?, maxGasPrice?, maxTotalFeeWei?}, only if the owner gave a cap |
validity | {expiresAt} in UTC with exactly three ms digits and Z. At least 3 minutes for the owner. It is the last time the owner can approve and cannot be extended. |
Refusals: compact_wallet_calls_invalid, compact_wallet_semantics_invalid, compact_wallet_calls_fields_invalid, compact_wallet_address_invalid, compact_wallet_address_checksum_invalid, compact_wallet_hex_invalid, compact_wallet_validity_window_too_short (carries minWindowMs), compact_wallet_presentation_invalid, compact_wallet_account_discovery_unavailable (503, retryable), compact_wallet_relay_raw_deposit (use relay-topup). Notices: recipe_available, approval_only, presentation_defaulted, presentation_mismatch, presentation_action_ignored, validity_from_quote.
Continuing an operation
continues:{operationId, revision?} with a NEW requestId proposes the next step of this connection's own earlier operation on the same chain, for example the swap after its approval. Exact calls only. The parent must be owner-signed with its signed steps final. If the parent used intent, this call must too. A signed parent that is not final answers needs_retry. An operation continued is final with outcome continued; its review link is no longer used. Follow continuedBy. Error: compact_wallet_continues_invalid.
Post-conditions
intent {queries (1 to 32), predicates (1 to 64), effects? (0 to 32), quote?}. Exact calls only, never with a recipe. Omit it rather than invent one. Never compute sourceDigest or plannedCallsDigest yourself.
Predicate kinds: uint-after, uint-interval-delta (signed; a debit is negative), bytes-after. Effect kinds: maximum-debit, minimum-credit, allowance-after, each naming supportingPredicateIds. intent.quote needs expiresAt. Full schema: page 1 of input-schema. Error: compact_wallet_intent_invalid.
Typed data
A CoW order or cancellation only. ODEI posts it to the CoW orderbook once the owner signs. The agent never receives a signature. Every other EIP-712 message, including permits and Permit2, is refused; propose exact transaction calls instead. Prefer recipe cow-limit-order.
typedData {domain, types, primaryType, message} as eth_signTypedData_v4 JSON: integers as decimal strings, domain.chainId equal to chainId, message owner equal to the wallet. One message per proposal. Returns proposalId (mesp_) and submission odei_posts_after_owner_signs. Track with odei_wallet_status {proposalId}: signatureState, signatureWithheld, orderSubmission, cowOrder.
Refusals: compact_wallet_typed_data_fields_invalid, compact_wallet_typed_data_invalid, compact_wallet_typed_data_unsupported, compact_wallet_flagged_address_refused. A past deadline is refused.
Recipes
Three lists differ. The propose tool description is a static index for every owner. odei_wallet_state recipes is the per-owner view, and can omit an engine recipe when a runtime switch is off; a recipe the index names can then refuse with recipe_unknown at call time. Some recipes are proposable by id only. Read paramsSchema with odei_wallet_state {"recipe":"<id>"}. Unknown params are rejected with the allowed list.
| Recipe | What it does | Chains | Required in params | Optional | Notes |
|---|---|---|---|---|---|
wrap-unwrap | Wrap the native coin, unwrap it, or both in one atomic batch. | All 8 | direction, amountAtomic | ||
erc20-transfer | Send an exact amount of an ERC-20 or the native coin to one recipient. | All 8 | token, to, amountAtomic (or amount) | to may be an ENS name or Basename. | |
uniswap-v3-swap | Single-pool swap: approve exactly the amount, swap, revoke, in one atomic batch. | All 8 | tokenIn, tokenOut, amountInAtomic | fee, minimumOutAtomic, slippageBps, deadlineSeconds | Output limited to the chain's reviewed native, wrapped-native and stablecoin assets. |
camelot-v3-swap | Exact-input ERC-20 single-pool swap, atomic only. A pool price boundary can leave a partial fill; unspent input stays in the wallet. | 42161 | tokenIn, tokenOut, amountInAtomic | slippageBps, deadlineSeconds | Output only Arbitrum's reviewed WETH or USDC. |
balancer-v2-swap | Exact-input Vault swap through an allowlisted pool, atomic only. | 1, 137, 8453, 42161 | poolId, tokenIn, tokenOut, amountInAtomic | slippageBps, deadlineSeconds | Registered ERC-20 only; use wrapped native. |
balancer-v2-batch-swap | Exact-input batch swap through verified allowlisted pools, atomic only. | 1, 137, 8453, 42161 | swaps (1 to 8) | slippageBps, deadlineSeconds | Base units only. |
best-swap | ODEI compares on-chain routes and aggregator quotes, decodes and re-simulates each, and proposes the best verified route as one atomic batch. Output goes to the owner wallet only. | 10, 8453, 42161 | tokenIn, tokenOut, amountInAtomic (or amountIn) | maxSlippageBps, minOutAtomic | See the section on best-swap. |
cow-limit-order | Sell-kind CoW Protocol limit order. ODEI posts it once the owner signs. | 1, 56, 137, 8453, 42161, 43114 | sellToken, buyToken, sellAmountAtomic, buyAmountAtomic | validForSeconds, partiallyFillable | ERC-20 only. No orderbook on Optimism. |
odei-order | Create or cancel an ODEI Order: limit, take profit, stop loss, trailing stop or repeat buy or sell. | 8453, 42161 | action (plus the create or cancel fields) | See the section on ODEI Orders. | |
cctp-bridge | Burn native USDC with Circle CCTP V2 forwarding; Circle mints to the same wallet on the destination. | 1, 10, 137, 8453, 42161, 43114 | destinationChainId, amountAtomic | maxFeeAtomic, finality | A burn is not delivery. |
cctp-receive | Mint on the destination when automatic forwarding has not delivered. | 1, 10, 137, 8453, 42161, 43114 | sourceChainId, sourceTransactionHash | nonce | Proposable by id. Offered from bridgeDelivery.manualCompletion. |
best-bridge | ODEI compares bridges for native USDC or native ETH and proposes the one that delivers the most after every fee, as one atomic batch. | 1, 10, 8453, 42161 | destinationChainId, amountAtomic | token, tokenOut, recipient, recipientAddress, provider, expectedProvider, expectedMinOutAtomic | atomicOnly. Listed only while the bridge comparison is on. |
relay-topup | Top up the gas token of BNB Chain, Polygon and Robinhood Chain through Relay, as one atomic card. | 1, 10, 8453, 42161 | legs (1 to 3) | token, recipient, recipientAddress | Up to $1,000 in one card. The recipient must be the owner's wallet. |
debridge-cancel | Cancel the owner's own deBridge order that no solver filled, refunding the owner on the source. | 1, 10, 56, 137, 8453, 42161 | sourceChainId, sourceTransactionHash, orderId | Proposable by id. Offered by the bridge delivery read. Manual only. | |
lido-stake | Stake ETH for stETH through Lido in one call. | 1 | amountAtomic | ||
aave-supply-withdraw | Supply to Aave v3, or withdraw an exact amount or everything. | 1, 8453, 42161 | action, asset, amountAtomic | The supplied aToken amount is an estimate. | |
aave-repay | Repay variable Aave v3 debt up to the cap. Debt may remain as interest accrues. | 8453, 42161 | asset, amountAtomic | max is the debt read now +0.1%, capped at the wallet balance. | |
spark-supply-withdraw | SparkLend supply and withdraw on Ethereum. | 1 | action, asset, amountAtomic | DAI, USDS and WETH only. | |
spark-repay | SparkLend repay on Ethereum. | 1 | asset, amountAtomic | DAI, USDS and WETH only. | |
erc4626-deposit-withdraw | Deposit or withdraw exact underlying assets through verified Fluid fTokens. | 1, 8453, 42161 | action, vault, amountAtomic | Despite the name, this is not a general ERC-4626 recipe. Base has no verified USDT fToken. | |
best-yield | ODEI reads USDC rates of every venue it can build on, simulates the deposit and exit, ranks by what is kept over the hold, and proposes one atomic batch. | 1, 8453, 42161 | asset, amountInAtomic | holdDays, choice, maxTier, preferAuto | atomicOnly. Variable on-chain APY; rewards are not counted. Listed only while its switches are on. |
yield-withdraw | Withdraw USDC from a supported deposit back to the owner wallet. | 1, 8453, 42161 | providerId, market, asset, amountAtomic | atomicOnly. max only where the source supports it now. | |
aave-hedge | Make part of an ETH position price-neutral in one transaction: supply, borrow WETH, sell it for USDC through ODEI Best Execution, supply the USDC. | 8453, 42161 | ratioBps | asset, ethAtomic, amountAtomic, maxSlippageBps | Refused if final Aave health would be below 1.50. atomicOnly. |
aave-hedge-adjust | Move an open hedge to 50%, 80% or 98%. | 8453, 42161 | targetRatioBps | asset, maxSlippageBps | atomicOnly. |
aave-hedge-unwind | Close a hedge, optionally buying back. | 8453, 42161 | none | asset, buyBack, unwrap, maxSlippageBps | atomicOnly. |
erc20-revoke | Set one ERC-20 or Permit2 allowance to zero. | All 8 | token, spender | amountAtomic (must be 0), approvalKind | Proposable by id. |
erc20-revoke-batch | Set up to 8 allowances to zero. | All 8 | items (1 to 8) | Proposable by id. Rows from kind approvals carry a ready proposal. |
atomicOnly recipes never split into sequential steps. erc20-permit is listed with proposable:false; connectors cannot use it. Recipe cards larger than ODEI can save are refused with no retry. Refusals carry a cause and a next step in message: compact_wallet_recipe_unknown, compact_wallet_recipe_chain_unsupported, compact_wallet_recipe_params_invalid, compact_wallet_recipe_fields_invalid (with a recipe omit calls, typedData, intent, continues), compact_wallet_recipe_kind_unsupported, compact_wallet_kind_invalid, compact_wallet_recipe_build_failed.
Amounts, symbols and names
Recipe params take base units in XAtomic fields, or X as "100" or "100 USDC" for amount, amountIn, minimumOut, sellAmount, buyAmount, maxFee. ODEI converts with on-chain decimals and never rounds. A token field may be a reviewed symbol for that chain. An unknown or ambiguous symbol is refused with candidates, never guessed. erc20-transfer to may be an ENS name or Basename; ODEI resolves and checks it and refuses on mismatch. Report result.recipe.params.
Human amounts: wrap-unwrap, erc20-transfer, cctp-bridge (amount, maxFee), lido-stake, aave-supply-withdraw, aave-repay, spark-supply-withdraw, spark-repay, erc4626-deposit-withdraw (amount); uniswap-v3-swap (amountIn, minimumOut); camelot-v3-swap, balancer-v2-swap, best-swap (amountIn); cow-limit-order (sellAmount, buyAmount). Base units only: balancer-v2-batch-swap, cctp-receive, erc20-revoke, erc20-revoke-batch.
best-swap
ODEI compares routes and proposes the best verified one. The minimum received is enforced in calldata. Name no fixed source list; read rows[].providerName from the quote. On swaps of 500 USD or more on Base and Arbitrum, ODEI Router also competes. Native ETH in keeps 0.001 ETH for fees, so balance_insufficient can appear with a nearby balance. Listed per owner; read odei_wallet_state recipes. detail:"full" status of a final confirmed swap adds fillReport.
maxSlippageBps 0 to 300, default 50. Refusals: compact_wallet_recipe_best_swap_refused, compact_wallet_recipe_best_swap_unavailable, compact_wallet_recipe_no_route, compact_wallet_recipe_offer_changed, compact_wallet_recipe_exact_out_off.
ODEI Orders
Recipe odei-order. Not the same thing as service orders in odei_orders. Chains 8453 and 42161. Answers are not compact_wallet_ states: order_saved, order_cancelled, order_unchanged.
Create: action:"create", chainId inside params as an integer (8453 or 42161, equal to the top-level chainId; without it the call is refused as order_chain_unsupported), amountAtomic (the token sold: the coin for sells, USDC for a buy), legs (1, or take profit plus a stop), optional coin (WETH or cbBTC on Base, WETH or WBTC on Arbitrum, default WETH), expiresInSeconds (3600 to 2592000, default 604800), maxSlippageBps (5 to 300, default 50). Cancel: action:"cancel", orderId (ord_ + 32 hex).
| Leg | Fields |
|---|---|
limit_sell, limit_buy, take_profit | price: USDC per coin, up to 6 decimals, 1 to 10000000 |
stop_loss | price, worstBps (9000 to 10000) |
trailing_stop | price, trailBps (200 to 5000), worstBps |
dca | side (buy or sell), everySeconds (300 to 604800), slices (2 to 48). No price; leave out expiresInSeconds. |
At most 5 open orders per connection and 10 per owner. Smallest order 1 USDC worth. A trigger must sit at least 0.2% from the market. A repeat order runs at most 30 days in all and is one order on its own. Stops need two consecutive checks. The owner's balance is never shown; a low balance is refused by reason only. An agent cannot cancel an order that already has a swap waiting; the owner cancels it in Trade.
States: draft, watching, triggered, card_ready, filled, needs_attention, paused, cancelled, expired. Final: filled, cancelled, expired. Track with odei_wallet_read {kind:"orders"}; once triggered, cardOperationId is the operation to track.
Refusals, with compact_wallet_ prefix: order_off, order_paused, order_agent_limit, order_order_limit, order_balance_low, order_pending, order_request_conflict, order_not_yours, order_card_open, order_changed, order_unavailable, orders_unread, order_chain_unsupported, order_pair_unsupported, order_amount_invalid, order_expiry_invalid, order_slippage_invalid, order_legs_invalid, order_price_invalid, order_side_invalid, order_schedule_invalid, order_kind_unsupported, order_request_invalid.
paramsSchema for odei-order is behind the server. The server validates with its own code, so send chainId, coin and dca legs as written here.Bridges
cctp-bridge destinations are 1, 10, 137, 8453, 42161, 43114. finality is standard (default; about 15 to 20 minutes from L2s, no protocol fee) or fast (soft finality in seconds, with a protocol fee, not offered from every chain). Omit maxFeeAtomic and ODEI fills it from a live Circle quote.
best-bridge destinations are 1, 10, 56, 137, 4663, 8453, 42161. BNB Chain and Polygon destinations vary by a runtime switch; read paramsSchema. token is USDC (default) or ETH, and ETH moves in steps of 0.000001 ETH. provider and expectedProvider take cctp-fast, cctp-standard, across, stargate, debridge, relay or cctp. expectedMinOutAtomic is a string minimum. recipient defaults to the owner wallet.
relay-topup legs are {destinationChainId: 56|137|4663, amountAtomic}, up to 3, distinct. A refund is Relay's action. Raw calls to Relay's depository are refused.
Lending, yield and hedges
aave-supply-withdraw and spark-supply-withdraw take action supply or withdraw and accept max on withdraw. yield-withdraw providerId: aave-v3, compound-v3, fluid-lending, morpho-vaults, morpho-vaults-2, morpho-vaults-3, morpho-v2-1, morpho-v2-2, spark-savings, moonwell, ethena-susde. best-yield optional holdDays 1 to 365, default 30; maxTier T1 or T2, default T2. aave-hedge ratioBps is 5000 or 9800; aave-hedge-adjust targetRatioBps is 5000, 8000 or 9800. asset is WETH by default, cbBTC on Base, WBTC on Arbitrum. These recipes are tools, and nothing on this page is financial advice.
Review links
On awaiting_owner give the owner reviewUrl unchanged, normally by relaying ownerMessage verbatim. The owner sees decoded calls, the simulation and the network fee and approves in ODEI, not in chat. The link carries account parameters; return it with all of them.
- A prepared review lasts at most 5 minutes. Opening the link re-prepares it.
validity.expiresAtis the real limit on approval. reviewUrlis null when the operation is final (except a reverted operation with a retry available), expired or blocked.- On a continued operation never give the old link. Follow
continuedBy. - On an operation Auto ran before it went off (
owner_signingorbroadcastwithautoDecision.decisionran), give noreviewUrland do not propose again.
odei_wallet_status
Pass exactly one of operationId (meop_ + 32 hex, plus revision?) or proposalId (mesp_ + 32 hex). Optional: refresh:true (calls only: re-reads receipt and finality from chain and records evidence, never signs or sends), detail (compact or full, calls only), waitSeconds (0 to 20, operationId only).
odei_wallet_status {"operationId":"meop_<32 hex>","waitSeconds":20}
waitSeconds holds the call and answers on the first change of state or once final, with waited{seconds, changed}. Call again at once until final:true. Without it wait retryAfterMs: about 15 s awaiting owner, 10 to 15 s while an operation is sending or confirming, 60 s for minutes-scale or unknown, null when there is nothing to poll. Finality usually follows inclusion by about 10 s on Base and OP, about 36 s on Ethereum.
The result has outcome, final, status{key, label}, transactionHash, receipt, fees, nextAction, retryAfterMs, autoDecision. A final result is compact unless detail:"full", which adds traces, historical receipt, provenance, preparations and, for a confirmed best-swap, fillReport. Errors: compact_wallet_selector_required, compact_wallet_operation_id_invalid, compact_wallet_proposal_id_invalid, compact_wallet_invalid_request.
Outcomes
Relay status.label and follow nextAction. Prepared, sent and executed differ. Only confirmed may be reported as executed.
outcome | Final | Meaning |
|---|---|---|
awaiting_owner | no | Ready for the owner |
owner_signing | no | Signing in progress |
broadcast | no | Sent, confirming |
included_not_final | no | In a block, not final |
confirmed | yes, when all steps confirm | Executed |
reverted | yes, but can stay not final; status says so | Failed on chain |
cancelled | yes | Cancelled |
replaced | yes | Another operation settled the nonce |
expired | yes | Past its window |
continued | yes | continuedBy carries the remaining steps |
reorged | yes, once ODEI resolved it | Never resend |
unknown | no | Unresolved, not failed |
Inclusion is not a verified financial result. financialOutcome reads unverified_unless_evidence_present. Amounts come from evidence; otherwise say unverified. A sequential operation confirms only when every step confirms, and a final step shows awaiting_owner for the next.
Typed data: signatureState is awaiting_owner, claimed, signing, released, cancelled, expired or unknown. signatureWithheld is never_returned, odei_posts_to_cow or owner_only. CoW order outcomes: open, partially_filled, filled, expired, cancelled, withdrawn, rejected, signed_not_posted_by_odei. A CoW order state is the orderbook's report. A fill is success only once effects are verified on chain.
Bridge delivery
For a bridge, confirmed means the source transaction is final, not that funds arrived. Status adds bridgeDelivery with a stage and summary, deliveryFinal and delivered. It is done when delivery is final or a refund is proven. manualCompletion offers cctp-receive, and the delivery read offers debridge-cancel for an unfilled order. A refund on the way: tell the owner, do not poll.
odei_wallet_cancel
Only when the owner asks. One selector:
| Selector | Effect |
|---|---|
operationId (+ revision) | Cancel one unsigned call operation |
state: unsigned, expired or all-unsigned, plus chainId?, olderThanMinutes? (0 to 525600), limit? (1 to 100, default 50), preview? | Filters preview by default |
operations [{operationId, revision}] (1 to 100) | From confirm.operations |
proposalId | Unsigned typed data gets ownerCancelUrl and state owner_action_required, whatever the scopes. Only a signed, placed CoW order goes to the off-chain cancel signature. |
A filter call previews: report would_cancel, then call again with operations: confirm.operations (or preview:false). unsigned means never opened for signing, and includes expired. all-unsigned also covers owner-opened unsigned operations.
Without capital.propose, filters and preview refuse, and one operationId or proposalId returns ownerCancelUrl and state owner_action_required.
Result: {kind, preview, selector, results, counts, complete, hasMore, confirm?, message}. Done only when complete:true. hasMore:true means more match. Per-item states: cancelled, already_cancelled, not_cancellable, would_cancel, not_found, not_saved (concurrent change; repeat the same call). signaturesRevoked is always false. CoW states: cancellation_awaiting_owner, cancellation_signed, cancellation_unavailable, not_cancellable, owner_action_required.
Errors: compact_wallet_selector_required, compact_wallet_state_required, compact_wallet_cancel_operations_invalid, compact_wallet_scope_required (403), compact_wallet_invalid_request.
Safety refusals
Refused before anything is saved. A same-requestId retry is judged again.
- Unlimited approve, increaseAllowance, setApprovalForAll or Permit2 allowance to a spender that is not a known protocol contract.
- Token access to a plain wallet that has no code.
- Any address on a public scam list.
- Non-standard approval calldata.
- Making an address an Aave position manager.
- Credit delegation to unverified delegatees.
- Delegatecall and uninspectable wallet envelopes.
- Calls to the wallet itself or its modules.
- Retired routers.
Approve exactly the amount the next call spends, inside the same atomic batch, and reset to 0. Recipes do this. Pinned bridge contracts accept only Across depositV3 and swapAndBridge, Stargate send and deBridge createOrder.
Codes: compact_wallet_unbounded_approval_refused (hint exact_amount_in_same_atomic_batch, canonical_abi_encoding, stop_and_tell_owner, known_delegatee_only or direct_calls), compact_wallet_eoa_spender_refused, compact_wallet_flagged_address_refused (hint stop_and_tell_owner: stop, tell the owner, do not retry or route around), compact_wallet_owner_only_call_refused, compact_wallet_retired_router_refused, compact_wallet_spender_check_limited, compact_wallet_spender_check_unavailable, compact_wallet_relay_raw_deposit, compact_wallet_typed_data_unsupported. Risks that are not refused appear as riskFlags.
Blocked reasons
After saving, preparation may stop. Follow message. A blocked result carries cancel{tool:"odei_wallet_cancel", arguments}. Contract revert text is quoted as untrusted data.
| Reason | Note |
|---|---|
simulation_failed | error.diagnosis names the call |
balance_insufficient | fundingShortfall gives the exact native amount. After funding retry the same requestId. |
block_gas_limit_exceeded, gas_limit_insufficient, gas_limit_invalid | Fix the gas settings |
total_fee_unbounded, total_fee_limit_exceeded | Fee limits |
prepare_failed, sequence_window_short | Fix the cause |
Except balance_insufficient, fix the cause and propose with a NEW requestId.