Skip to content
LogoLogo

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.

HyperliquidUniswap
Write operationsplace-order, cancel-order, update-leverageexecute-swap, wrap-eth, mint-test-tokens
Operation kindverified-writebuild
Calls Guardian itselfYes: the operation requests verification internallyNo
Can reach the venueYes, in direct mode after an approving verdict (or a terminal UNDECIDED one with --allow-undecided)Never
ReturnsA venue resultUnsigned {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.

OperationKindCLIMCP tool
request-verificationoperationalnava request-verificationrequest-verification
await-verificationoperationalnava await-verificationawait-verification
check-verification-statusoperationalnava check-verification-statuscheck-verification-status
get-user-addressoperationalnava get-user-addressget-user-address

request-verification

Submit one proposed transaction to Guardian. Returns a requestHash and does not wait.

CLIMCPRequiredNotes
--reasonpromptyesShort audit label. Non-empty string.
--proposed-tx-jsonproposedTxyesA 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-jsonmetadatanoFree-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:

CLIMCPRequiredNotes
--interval-msintervalMsnoPoll interval, default 2000. MCP bounds it to 250–30000; the CLI accepts any positive integer.
--max-attemptsmaxAttemptsnoPoll 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" with success: false and isError: true.
  • The CLI and the TypeScript client throw NavaTimeoutError. On the CLI that is exit 1 with the error on stderr; there is no PENDING payload 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.

CLIMCPRequiredNotes
--request-hashrequestHashyesMCP 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.

OperationKindCLIMCP tool
get-marketsreadnava hyperliquid get-marketshyperliquid.get-markets
get-account-summaryreadnava hyperliquid get-account-summaryhyperliquid.get-account-summary
get-positionsreadnava hyperliquid get-positionshyperliquid.get-positions
get-open-ordersreadnava hyperliquid get-open-ordershyperliquid.get-open-orders
get-pricereadnava hyperliquid get-pricehyperliquid.get-price
place-orderverified-writenava hyperliquid place-orderhyperliquid.place-order
cancel-orderverified-writenava hyperliquid cancel-orderhyperliquid.cancel-order
update-leverageverified-writenava hyperliquid update-leveragehyperliquid.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

OperationCLIMCPRequiredNotes
get-markets--filterfilternoCase-insensitive substring match on the symbol. Omit to list the whole universe.
get-account-summary--addressaddressnoDefaults to the configured wallet address; fails if none is set.
get-positions--address, --assetaddress, assetnoOmit the asset to list every open position.
get-open-orders--address, --assetaddress, assetnoOmit the asset to list every open order.
get-price--assetassetyesSymbol, 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:

CLIMCPRequiredNotes
--reasonreasonyesThe audit prompt recorded against the verdict. Sent as prompt. State the intent of the action.
--modemodenodirect (default) or dry-run. dry-run verifies and stops, returning the verdict and status: "dry-run" without signing or reaching the exchange.
--allow-undecidedallowUndecidedExecutionnoDefault 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:

DefaultEnvironment overrideEffect when set
mode = directHYPERLIQUID_MODE, else HL_MODEdry-run makes every unflagged write verify-only.
allowUndecidedExecution = falseHYPERLIQUID_ALLOW_UNDECIDED_EXECUTION, else HL_ALLOW_UNDECIDED_EXECUTIONFail-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

CLIMCPRequiredNotes
--assetassetyesSymbol, e.g. BTC.
--sidesideyesbuy or sell, not long or short.
--sizesizeyesPositive decimal string in whole contracts, e.g. 0.01.
--order-typeorderTypeyesmarket or limit.
--limit-pricelimitPricesee noteRequired 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.
--tiftifnoGtc (default), Ioc, or Alo, capitalized exactly. Ignored on market, which is always Ioc.
--reduce-onlyreduceOnlynoDefault 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

CLIMCPRequiredNotes
--assetassetyesSymbol the order is on.
--order-idorderIdyesPositive integer venue order id, from get-open-orders.

Plus --reason, --mode, and --allow-undecided.

update-leverage

CLIMCPRequiredNotes
--assetassetyesSymbol to update leverage for.
--leverageleverageyesPositive integer, e.g. 3. Not a decimal.
--margin-modemarginModeyescross or isolated.

Plus --reason, --mode, and --allow-undecided.

Uniswap

6 operations. Requires @navalabs/uniswap-adapter.

OperationKindCLIMCP tool
get-balancereadnava uniswap get-balanceuniswap.get-balance
get-quotereadnava uniswap get-quoteuniswap.get-quote
find-all-poolsreadnava uniswap find-all-poolsuniswap.find-all-pools
execute-swapbuildnava uniswap execute-swapuniswap.execute-swap
wrap-ethbuildnava uniswap wrap-ethuniswap.wrap-eth
mint-test-tokensbuildnava uniswap mint-test-tokensuniswap.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

OperationCLIMCPRequiredNotes
get-balance--wallet-addresswalletAddressnoDefaults to UNISWAP_WALLET_ADDRESS, then WALLET_ADDRESS. MCP requires ^0x[a-fA-F0-9]{40}$; the CLI does not check the shape.
--token-symbolstokenSymbolsnoComma-separated on the CLI (TKA,TKB), an array of strings over MCP.
find-all-pools--protocolprotocolnov3 or v4. Omit to list both; the excluded version is not queried.
get-quote--protocolprotocolyesv3 or v4.
--currency-incurrencyInyesSymbol or contract address of the token being sold.
--currency-outcurrencyOutyesSymbol or contract address of the token being bought.
--amount-inamountInyesDecimal string in whole units, e.g. 1.
execute-swap--protocol, --currency-in, --currency-out, --amount-inas for get-quoteyesSame shapes as get-quote.
--slippage-percentslippagePercentnoDefault 0.5. Greater than 0 and at most 50, enforced on both transports.
wrap-eth--amountamountyesDecimal string of ETH to wrap, e.g. 0.1.
mint-test-tokens--token-symboltokenSymbolyesOne of TKA, TKB, TestTokenA, TestTokenB. Any other value is rejected.
--amountamountnoDecimal 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.5

The 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:

CLIMCP
Hyperliquid networkNo --network flag; refused outright. Environment-only, via HYPERLIQUID_NETWORK or HL_NETWORK.Per-call network field on all 8 tools.
requestHash shapeNot validated; sent to the API as given.^0x[a-fA-F0-9]{64}$.
Proposed transactionAccepts '{}'.Requires protocol and chainId.
Poll boundsAny positive integer.250–30000 ms and 1–120 attempts.
Uniswap wallet addressNo shape check.Address regex.
limitPrice on a limit orderRequired, enforced as a cross-field rule.Not required. No cross-field rules exist over MCP.
Extra argumentsget-user-address refuses them.Not applicable.
Hyperliquid endpoint overridesHYPERLIQUID_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 direct mode and only on a terminal verdict that permits it: an approval, or UNDECIDED with --allow-undecided set.
  • 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.