Operations reference
Developer Preview
Every operation the SDK ships, with its parameters, in one place. 18 operations: 4 base verification operations plus 14 contributed by the two adapters: 8 Hyperliquid and 6 Uniswap.
One kebab-case token names each operation on both transports. The CLI spells it
nava <protocol> <token>; MCP spells it <protocol>.<token>. The base four are
un-namespaced on both. There are no camelCase aliases; naming is enforced when
the surface descriptor loads, so an alias cannot exist.
Option and field names differ only in case: --currency-in on the CLI is
currencyIn over MCP. Where the two transports differ in behaviour, the row
says so, and the differences are collected under
Transport asymmetries.
The safety boundary is not the same for both adapters
Each adapter has an operation whose name sounds like it trades. Only one of them can reach a venue.
| Hyperliquid | Uniswap | |
|---|---|---|
| Write operations | place-order, cancel-order, update-leverage | execute-swap, wrap-eth, mint-test-tokens |
| Operation kind | verified-write | build |
| Calls Guardian itself | Yes: the operation requests verification internally | No |
| Can reach the venue | Yes, in direct mode after an approving verdict (or a terminal UNDECIDED one with --allow-undecided) | Never |
| Returns | A venue result | Unsigned {to, data, value} calldata |
execute-swap does not broadcast a swap. Despite the name, every Uniswap
write is a build operation returning unsigned calldata and nothing else.
An approving verdict permits the exact evaluated action under the returned policy
version; it is not itself an instruction to execute. What follows differs by
adapter: Hyperliquid's three writes do sign and send on an approving verdict
when the mode is direct, which is the point of a verified-write. Uniswap
never does, so signing and submission stay with your application.
Base verification
Only @navalabs/sdk is required. None of these sign or broadcast anything.
| Operation | Kind | CLI | MCP tool |
|---|---|---|---|
request-verification | operational | nava request-verification | request-verification |
await-verification | operational | nava await-verification | await-verification |
check-verification-status | operational | nava check-verification-status | check-verification-status |
get-user-address | operational | nava get-user-address | get-user-address |
request-verification
Submit one proposed transaction to Guardian. Returns a requestHash and does
not wait.
| CLI | MCP | Required | Notes |
|---|---|---|---|
--reason | prompt | yes | Short audit label. Non-empty string. |
--proposed-tx-json | proposedTx | yes | A JSON object, not an array or a scalar. MCP requires protocol (non-empty string) and chainId (positive integer) and passes extra venue-native keys through; the CLI checks only that it parses as an object. |
--metadata-json | metadata | no | Free-form audit context. Stored with the request; not evaluated by policy. |
The CLI flag is --reason, the API and MCP field is prompt. One value, three
spellings, and it is an input name only. A verdict comes back carrying
humanReason and reasonCode, never prompt.
await-verification
Submit, then poll until a terminal verdict. Everything request-verification
takes, plus:
| CLI | MCP | Required | Notes |
|---|---|---|---|
--interval-ms | intervalMs | no | Poll interval, default 2000. MCP bounds it to 250–30000; the CLI accepts any positive integer. |
--max-attempts | maxAttempts | no | Poll count, default 30. MCP bounds it to 1–120; the CLI accepts any positive integer. |
The default ceiling is 58 seconds of waiting, not 60. The loop polls
maxAttempts times and sleeps only between polls: 30 polls, 29 sleeps,
29 × 2000 ms. The timeout message multiplies interval by attempts and so reports
60 s.
Exhausting the attempts is neither a rejection nor an approval, and the two transports fail closed differently:
- MCP returns
status: "PENDING"withsuccess: falseandisError: true. - The CLI and the TypeScript client throw
NavaTimeoutError. On the CLI that is exit 1 with the error on stderr; there is noPENDINGpayload on stdout.
Either way, poll again with check-verification-status or raise the attempt
count. A timeout is never permission.
check-verification-status
Re-read the status and structured verdict for a requestHash.
| CLI | MCP | Required | Notes |
|---|---|---|---|
--request-hash | requestHash | yes | MCP requires ^0x[a-fA-F0-9]{64}$. The CLI does not validate the shape, so a malformed hash reaches the API and returns an HTTP error rather than a usage error. |
An unknown hash is not an error. It returns status: "NONE" with
canExecute: false. A status carrying no structured verdict is not approval.
get-user-address
Return the authenticated session wallet address. Takes no options or fields at all, and the CLI hard-errors on any argument.
Hyperliquid
8 operations. Requires @navalabs/hyperliquid-adapter.
| Operation | Kind | CLI | MCP tool |
|---|---|---|---|
get-markets | read | nava hyperliquid get-markets | hyperliquid.get-markets |
get-account-summary | read | nava hyperliquid get-account-summary | hyperliquid.get-account-summary |
get-positions | read | nava hyperliquid get-positions | hyperliquid.get-positions |
get-open-orders | read | nava hyperliquid get-open-orders | hyperliquid.get-open-orders |
get-price | read | nava hyperliquid get-price | hyperliquid.get-price |
place-order | verified-write | nava hyperliquid place-order | hyperliquid.place-order |
cancel-order | verified-write | nava hyperliquid cancel-order | hyperliquid.cancel-order |
update-leverage | verified-write | nava hyperliquid update-leverage | hyperliquid.update-leverage |
Assets are perp symbols (BTC, ETH, DYDX), matched
case-insensitively. Never a contract address and never the numeric asset index;
the adapter resolves the index itself. An unknown symbol fails with
unknown asset "<symbol>" on <network> and the first ten available names. Use
get-markets to list what the configured network carries.
Sizes and prices are positive decimal strings in whole units ("0.01",
"64250.5"), never base units and never scientific notation. Size is truncated
to the asset's szDecimals and price to the venue's tick rules; a value that
truncates to zero is rejected rather than rounded up.
Every Hyperliquid MCP tool takes an optional network field (mainnet or
testnet, default mainnet). It is omitted from the tables below because it
applies uniformly. The CLI has no such flag and refuses it; see
Transport asymmetries.
Reads
| Operation | CLI | MCP | Required | Notes |
|---|---|---|---|---|
get-markets | --filter | filter | no | Case-insensitive substring match on the symbol. Omit to list the whole universe. |
get-account-summary | --address | address | no | Defaults to the configured wallet address; fails if none is set. |
get-positions | --address, --asset | address, asset | no | Omit the asset to list every open position. |
get-open-orders | --address, --asset | address, asset | no | Omit the asset to list every open order. |
get-price | --asset | asset | yes | Symbol, e.g. BTC. |
The writes call Guardian themselves
place-order, cancel-order, and update-leverage build the venue-native
action, submit it to Guardian, and wait for a terminal verdict before signing or
sending anything to the exchange. Do not call request-verification separately
for a Hyperliquid action: these operations take no proposed transaction, the
EIP-712 payload is constructed internally, and a hand-rolled proposal would not
be the action that gets signed.
All three share these:
| CLI | MCP | Required | Notes |
|---|---|---|---|
--reason | reason | yes | The audit prompt recorded against the verdict. Sent as prompt. State the intent of the action. |
--mode | mode | no | direct (default) or dry-run. dry-run verifies and stops, returning the verdict and status: "dry-run" without signing or reaching the exchange. |
--allow-undecided | allowUndecidedExecution | no | Default false. Lets a terminal UNDECIDED verdict through to execution, and only that. REJECTED and any non-terminal status still fail regardless. Leave it off on direct; it is exactly the fail-open behaviour Guardian exists to prevent. |
A rejected verdict returns success: false, the message under error, and the
requestHash and decision under verification. Nothing is signed. Explain the
verdict; do not resubmit unchanged.
Both defaults are environment-overridable, and one fails open. They apply only when the flag or field is absent and the variable is unset:
| Default | Environment override | Effect when set |
|---|---|---|
mode = direct | HYPERLIQUID_MODE, else HL_MODE | dry-run makes every unflagged write verify-only. |
allowUndecidedExecution = false | HYPERLIQUID_ALLOW_UNDECIDED_EXECUTION, else HL_ALLOW_UNDECIDED_EXECUTION | Fail-open: true lets an undecided verdict reach execution even though nothing was passed. |
Booleans must be exactly true or false; any other value errors naming the
variable. An explicit flag always wins. Do not describe an action as
verified-before-execution on the strength of the documented default alone; pass
both explicitly when the answer matters.
place-order
| CLI | MCP | Required | Notes |
|---|---|---|---|
--asset | asset | yes | Symbol, e.g. BTC. |
--side | side | yes | buy or sell, not long or short. |
--size | size | yes | Positive decimal string in whole contracts, e.g. 0.01. |
--order-type | orderType | yes | market or limit. |
--limit-price | limitPrice | see note | Required for limit on the CLI. MCP has no such cross-field rule; supply it yourself. On market it is optional; omitted, the adapter derives a 2%-slippage-bounded price from the current mid. |
--tif | tif | no | Gtc (default), Ioc, or Alo, capitalized exactly. Ignored on market, which is always Ioc. |
--reduce-only | reduceOnly | no | Default false. On the CLI a bare flag, or an explicit true/false. |
Plus --reason, --mode, and --allow-undecided from above.
Verify a small BTC short without reaching the exchange:
nava hyperliquid get-markets --filter BTC
nava hyperliquid place-order --asset BTC --side sell --size 0.01 \
--order-type limit --limit-price 64000 --tif Gtc \
--mode dry-run --reason 'Trim BTC exposure per momentum signal'cancel-order
| CLI | MCP | Required | Notes |
|---|---|---|---|
--asset | asset | yes | Symbol the order is on. |
--order-id | orderId | yes | Positive integer venue order id, from get-open-orders. |
Plus --reason, --mode, and --allow-undecided.
update-leverage
| CLI | MCP | Required | Notes |
|---|---|---|---|
--asset | asset | yes | Symbol to update leverage for. |
--leverage | leverage | yes | Positive integer, e.g. 3. Not a decimal. |
--margin-mode | marginMode | yes | cross or isolated. |
Plus --reason, --mode, and --allow-undecided.
Uniswap
6 operations. Requires @navalabs/uniswap-adapter.
| Operation | Kind | CLI | MCP tool |
|---|---|---|---|
get-balance | read | nava uniswap get-balance | uniswap.get-balance |
get-quote | read | nava uniswap get-quote | uniswap.get-quote |
find-all-pools | read | nava uniswap find-all-pools | uniswap.find-all-pools |
execute-swap | build | nava uniswap execute-swap | uniswap.execute-swap |
wrap-eth | build | nava uniswap wrap-eth | uniswap.wrap-eth |
mint-test-tokens | build | nava uniswap mint-test-tokens | uniswap.mint-test-tokens |
All three build operations return unsigned calldata and nothing more.
Currencies take either a symbol or a contract address. Both resolve against
the same pool registry, and find-all-pools returns both spellings for every
token, so use whichever the surrounding code already has. A symbol resolves only
if it is configured on the chain in use; an unknown symbol, an unknown address,
or a token with no configured decimals is an error naming the token, never a
guess. Recover with find-all-pools.
Amounts are decimal strings in whole token units ("1.5"), never base units.
Native ETH is not an alias for WETH. Wrapping is a separate, explicitly
requested wrap-eth transaction and is never folded into a swap.
You must name the protocol version; the adapter does not infer V3 or V4. The
chain comes from UNISWAP_CHAIN_ID, else CHAIN_ID; it is required, and there
is no per-call override on either transport.
Options
| Operation | CLI | MCP | Required | Notes |
|---|---|---|---|---|
get-balance | --wallet-address | walletAddress | no | Defaults to UNISWAP_WALLET_ADDRESS, then WALLET_ADDRESS. MCP requires ^0x[a-fA-F0-9]{40}$; the CLI does not check the shape. |
--token-symbols | tokenSymbols | no | Comma-separated on the CLI (TKA,TKB), an array of strings over MCP. | |
find-all-pools | --protocol | protocol | no | v3 or v4. Omit to list both; the excluded version is not queried. |
get-quote | --protocol | protocol | yes | v3 or v4. |
--currency-in | currencyIn | yes | Symbol or contract address of the token being sold. | |
--currency-out | currencyOut | yes | Symbol or contract address of the token being bought. | |
--amount-in | amountIn | yes | Decimal string in whole units, e.g. 1. | |
execute-swap | --protocol, --currency-in, --currency-out, --amount-in | as for get-quote | yes | Same shapes as get-quote. |
--slippage-percent | slippagePercent | no | Default 0.5. Greater than 0 and at most 50, enforced on both transports. | |
wrap-eth | --amount | amount | yes | Decimal string of ETH to wrap, e.g. 0.1. |
mint-test-tokens | --token-symbol | tokenSymbol | yes | One of TKA, TKB, TestTokenA, TestTokenB. Any other value is rejected. |
--amount | amount | no | Decimal string in whole units. Default 1000. |
Building and then verifying a swap
The two currency forms are interchangeable:
nava uniswap find-all-pools --protocol v3
nava uniswap execute-swap --protocol v3 \
--currency-in 0x36b687FB3495D2CBB026DEeDAF480713c7554624 \
--currency-out 0xB256fD3f0B6d95a3bA308c3259F1Cc31C547d071 \
--amount-in 1 --slippage-percent 0.5
# identical, using symbols:
nava uniswap execute-swap --protocol v3 \
--currency-in TKA --currency-out TKB \
--amount-in 1 --slippage-percent 0.5The result carries every unsigned {order, to, data, value} descriptor in
data.allTransactions, with data.requiredApprovals and data.swap splitting
the same descriptors out by role. A first swap for a token returns two or three:
an ERC-20→Permit2 approval, a Permit2→Router approval, and the swap. Verify
each one separately, in order:
nava request-verification \
--proposed-tx-json '{"protocol":"uniswap","chainId":11155111,"to":"0x…","data":"0x…","value":"0"}' \
--reason 'Approve TKA spend for the router before the swap'Signing and submission stay with your application.
Transport asymmetries
Neither transport is a superset of the other. These are the differences that change what a call accepts or means:
| CLI | MCP | |
|---|---|---|
| Hyperliquid network | No --network flag; refused outright. Environment-only, via HYPERLIQUID_NETWORK or HL_NETWORK. | Per-call network field on all 8 tools. |
requestHash shape | Not validated; sent to the API as given. | ^0x[a-fA-F0-9]{64}$. |
| Proposed transaction | Accepts '{}'. | Requires protocol and chainId. |
| Poll bounds | Any positive integer. | 250–30000 ms and 1–120 attempts. |
| Uniswap wallet address | No shape check. | Address regex. |
limitPrice on a limit order | Required, enforced as a cross-field rule. | Not required. No cross-field rules exist over MCP. |
| Extra arguments | get-user-address refuses them. | Not applicable. |
| Hyperliquid endpoint overrides | HYPERLIQUID_API_URL, _INFO_URL, _EXCHANGE_URL are read. | Not read; always the public endpoints for the selected network. |
The CLI's --network refusal reports itself:
$ nava hyperliquid get-markets --network testnet
--network is not supported. Set HYPERLIQUID_NETWORK or HL_NETWORK instead.
One more asymmetry deserves its own treatment, because it produces a
plausible-looking wrong answer rather than an obvious failure:
isError does not mean rejected.
What no operation does
- No Uniswap operation signs or broadcasts anything; every one returns unsigned
calldata. The Hyperliquid writes do reach the exchange, but only in
directmode and only on a terminal verdict that permits it: an approval, orUNDECIDEDwith--allow-undecidedset. - A verdict permits the exact evaluated action under the returned policy version. A materially changed action needs its own verification.
- Never retry a rejected action unchanged, and never reshape one to get around a policy boundary. Explain the returned verdict.