Skip to content

feat: acp trade (swap + Hyperliquid) with copy polish - #34

Merged
Zuhwa merged 24 commits into
mainfrom
claude/competent-raman-aac988
Jun 9, 2026
Merged

feat: acp trade (swap + Hyperliquid) with copy polish#34
Zuhwa merged 24 commits into
mainfrom
claude/competent-raman-aac988

Conversation

@psmiratisu

@psmiratisupsmiratisu commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Brings the unified acp trade command (swaps + Hyperliquid deposit/spot/perp/withdraw, chain aliases, Treasures tokenized stocks, --dry-run) onto the latest main, plus a round of agent-facing copy polish.

This branch = main + feat/trade-command merged in, with the documentation/UX edits below on top. Since feat/trade-command had no PR of its own, this lands it.

Copy changes

  • acp trade statusacp trade hl-status. Renamed and clarified: it's the Hyperliquid account view only (HL perp positions, margin, HL spot balances). For on-chain token balances on any EVM chain, use acp wallet balance. Updated command, help text, examples, and comments.
  • add-signer policy. Help text and docs now urge picking an explicit --policy rather than relying on the restricted fallback (and note that changing it later is manual). unrestricted is documented as the choice for transacting outside Virtuals-approved contracts. Applied to both agent add-signer and agent create --signer.
  • Spot copy trimmed. Removed the concrete HL-spot how-to (BUY/SELL examples, the "USDC-quoted, one side must be usdc" note, and the command-table row) since spot routing is resolved on the backend; kept spot as a conceptual venue in the intent table.
  • Trimmed backend-plumbing prose (routing-service internals) down to observable behavior (auto-sign, no per-tx prompt, keys never leave the keystore).

Mirrored across README.md and SKILL.md. Companion docs PR in whitepaper-economyOS.

🤖 Generated with Claude Code


Note

High Risk
New auto-signing trade flows move real funds across chains and Hyperliquid; incorrect routing or signer policy could cause unintended transactions despite HTTPS and backend-built calldata.

Overview
Adds acp trade as a unified trading surface: DEX swaps (same/cross-chain), Hyperliquid deposit/spot/perp/withdraw (chain 1337), Treasures tokenized stocks, --dry-run, plus subcommands hl-status and withdraw-from-hl. The CLI proxies /trade/plan and /trade/next, runs a runTradeLoop that signs/broadcasts send legs and posts sign results (EIP-712 / personal) for Treasures; trade HTTP calls require HTTPS. createProviderAdapter gains extra mainnet viem chains for multi-chain legs; getApiContext and parseChainArg support trade routing.

Docs / agent UX: Large README and SKILL sections for trade; skill description extended for trading. acp trade status is renamed hl-status (HL account only vs wallet balance). add-signer / create --signer help and docs push an explicit --policy (not the restricted default) and clarify unrestricted.

Other: CLI entry registers trade + trading help blurb; SLIPPAGE_TOO_LOW / INSUFFICIENT_GAS error codes; scripts/tradeLoopSign.test.ts for the sign branch; Node >=20.19.0 in package.json / lockfile.

Reviewed by Cursor Bugbot for commit 49a9b65. Bugbot is set up for automated code reviews on this repo. Configure here.

psmiratisuand others added 24 commits June 4, 2026 18:12
A single `acp trade` command for token swaps (same-chain and cross-chain via
the trading-agent server's BondingV5 / LiFi routing) and Hyperliquid (deposit,
spot, perp, withdraw). Routes by the params you pass: Hyperliquid is chain 1337,
so the chains decide the venue; `--side long|short` is a perp. Auto-balances the
HL perp/spot wallets so `deposit → trade` just works. Signing stays client-side
via the keystore signer; HL orders are EIP-712 actions.
Adds @nktkas/hyperliquid and bumps @virtuals-protocol/acp-node-v2 to ^0.1.2
(required for the provider signTypedData / sendTransaction APIs the trade flow
uses). Requires Node >=20.19 (engines) for the HL SDK. Documents the command in
README.md and SKILL.md.
Addresses review:
- spot market orders look up mids by `@{pairIndex}` (PURR special-cased), not
the pair name, which returned no mid.
- detectIntent gates perp on --side long|short so a stray --token can't
pre-empt chain-based spot routing.
- declare engines.node >=20.19.0 to match @nktkas/hyperliquid.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Hyperliquid lists leveraged perp markets beyond crypto (equities,
currencies, commodities); the acp trade --side flags are identical
across asset classes. Surface this in the SKILL.md frontmatter +
trading section and the README perps section so agents know they
can open stock/FX/commodity positions, not just crypto.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…trade` (#30)
Adds a Treasures Finance route to `acp trade` — `--ticker` triggers a new
"treasures-stock" intent and runs ownership-proof → quote → per-leg EIP-712
sign → submit → poll-status in one command. New `src/lib/treasures/client.ts`
wraps the public Treasures API over `fetch` with HTTPS enforcement and
TREASURES_API_URL / IS_TESTNET host selection.
Also hardens the flow per review:
- poll status once more after the final sleep so a late fill isn't misreported
as TIMEOUT
- non-zero exit code on terminal partial_failed / all_failed
- validate --slippage-bps as a non-negative integer instead of sending null
A stock symbol is now named with --token everywhere; the companion flag
picks the venue: --side -> HL perp, --amount-usdc/--amount-shares -> Treasures
tokenized stock (spot). Drops the separate --ticker flag so an agent picks the
asset once and the mode second, matching the CLI's params-decide-the-venue model.
https://claude.ai/code/session_01C56LaYyFW7iRxtdY5tY3uA
Move the Treasures tokenized-stock flow out of the CLI and into the
trading-agent planner. The CLI is now a thin signer: it POSTs a plan with
a `treasures` block and drives the same /trade/plan + /trade/next loop as a
swap, signing whatever the server asks via the new `sign` action.
- runTradeLoop: handle `sign` actions (signTypedData / signMessage → post
signature back).
- runTreasuresStock: build a plan + run the loop; delete the ~150 lines of
direct quote/sign/submit/poll and the now-dead lib/treasures/client.ts.
- detectIntent: route a funded buy (--token ticker + --token-in/--chain-in/
--amount-in, no --token-out/--chain-out) to Treasures, so the server can
bridge any chain's funds to USDC@eth before buying.
- scripts/: tsx harnesses for the sign branch + detectIntent routing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rebasing the trade feature onto main surfaced two integration fixes:
- package-lock.json: regenerate to include @nktkas/hyperliquid (the lock
was taken from main during conflict resolution and lacked it).
- getAgentApi: drop the walletAddress arg — main simplified getClient to
(unauthenticated?: boolean), so passing a string no longer typechecks.
The param was always ignored (resolveToken uses the config owner wallet).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add chain ID legend to --help (1 ETH, 42161 ARB, 8453 Base, 1337 HL)
- Lead command description with cross-chain support
- Clarify --chain-in/--chain-out flag descriptions with named chains
- Clarify --amount-in is spend amount, not receive amount
- Add --size/--leverage note in perp routing table
- ensureHlFunds: actionable error with deposit command + leverage tip
- --size error: explicitly say 'token units, not USD', show --leverage example
- Progress labels: human-readable (Opening long / Buying / Selling vs Market long/buy/sell)
- Spot validation: plain English instead of internal jargon
- chains: parseChainArg() accepts named chain aliases (hyperliquid/hl, base,
arb, eth, optimism, …) alongside numeric ids, case-insensitive. trade
normalizes --chain-in/--chain-out up front so intent routing and the backend
both get a clean id — `--chain-out hyperliquid` now works.
- trade: new --dry-run flag previews any trade without signing/submitting.
Swap/deposit/Treasures send dryRun to /trade/plan and render the server's new
`preview` action; HL perp/spot/withdraw (signed client-side) compute and print
their preview locally — size, notional, and margin at the effective leverage —
without setting leverage, moving funds, or placing the order.
- help: clarify --size is in token units (not USD); document aliases + --dry-run.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Removes two "two-ways-to-do-one-thing" footguns flagged in review:
- Withdraw: collapse to one command. `acp trade withdraw-from-hl --amount <n>`
is now the ONLY way to move USDC off Hyperliquid. Dropped the duplicate
`--chain-in 1337 --chain-out <evm>` intent route and the ceremony it required
(an explicit --chain-out whose only legal value was 42161 — HL's withdraw3
always settles to Arbitrum, so it was never a chain choice). --chain-in 1337
now means spot only; anything else points to the command. Adds --dry-run.
- Slippage: one flag instead of two. `--slippage <pct>` (percent) now covers
every route; removed `--slippage-bps` (the duplicate with a different unit
that made `--slippage 300` mean wildly different things per route). Swaps/
Treasures convert pct→bps via slippageBpsFromPct; HL already used percent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…thdraw
Backward-compat: existing callers/agents that move USDC off Hyperliquid via the
chain-in/chain-out combo shouldn't break now that `withdraw-from-hl` is the
canonical command. detectIntent routes `1337 → 42161` to the same withdrawal.
HL's withdraw3 only ever settles to Arbitrum, so we honor 42161 and reject any
other chain-out (with a message pointing at withdraw-from-hl) rather than
silently sending to a chain HL can't reach. Docs/help updated to show the combo
as an alias of withdraw-from-hl.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…dge onward)
- Default: a bare HL withdraw (no --chain-out / --to-chain) settles to Arbitrum,
where HL's withdraw3 always lands.
- --chain-in 1337 --chain-out <evm> (or withdraw-from-hl --to-chain <id>) to a
NON-Arbitrum chain now withdraws to Arbitrum first, waits for the funds to
land (polls Arbitrum USDC balance, ~few min), then bridges the actual arrived
amount onward via the normal swap/LiFi route. On settle-timeout it returns the
exact follow-up bridge command — funds are never stranded without a next step.
- detectIntent: chain-in 1337 with a non-USDC token-out still errors as a spot
order missing --chain-out 1337, so a typo can't silently move funds off HL.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`acp trade` with no flags previously dropped into a readline wizard. Drop it:
the CLI is agent-first, and the wizard was a parallel input surface that could
drift from the flag flows. With no intent in the flags, the command now errors
with a pointer to `--help` instead. Removes runInteractive, the "interactive"
intent, and the now-unused readline/prompt imports (the prompt lib stays — other
commands use it).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The CLI is now a pure signer for Hyperliquid too. runPerp / runHlSpot /
runWithdraw no longer touch the @nktkas ExchangeClient — they POST hl / hlSpot /
hlWithdraw to /trade/plan and drive the existing runTradeLoop sign loop (the
server builds each EIP-712 action; the CLI signs with the agent wallet).
- Deletes the client-side execution: order placement, leverage, spot↔perp
auto-balance (ensureHlFunds), the client withdraw-then-bridge, and the
exitAfterOrder/placeHlOrder SDK-quirk workarounds.
- lib/hl/client.ts shrinks to a read-only InfoClient for `trade status`; the
ExchangeClient/signer wiring and order/price helpers are gone.
- --dry-run, chain aliases, and the unified --slippage flag all keep working,
now fully server-driven (the backend returns a `preview` action).
Net −495 lines. `trade status` stays a client-side read (no signing).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…s intent
The CLI no longer routes. `acp trade` collects whatever flags were passed into a
flat body and POSTs to /trade/plan; the backend detects the venue and drives the
sign loop. Deletes detectIntent + all per-venue run* functions (runSwap /
runHlSpot / runPerp / runWithdraw / runTreasuresStock) and their helpers
(isUsdcSymbol, validateTreasures*, parseSlippage, slippageBpsFromPct,
parseChainArg usage). `withdraw-from-hl` stays as a thin alias that forwards the
equivalent flat request; `status` stays a read-only HL query.
Net big deletion; CLI is now a pure flag-forwarder + signer.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`acp trade status` calls the new POST /trade/hl-status instead of reading HL
directly. Deletes src/lib/hl/client.ts (the last HL SDK usage) and removes
@nktkas/hyperliquid from package.json. The CLI now has zero Hyperliquid deps —
all HL access (trade + status) goes through the backend.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
detectIntent was deleted (intent detection moved to the backend), so its harness
no longer applies. tradeLoopSign.test.ts stays — runTradeLoop is still the CLI's
sign loop and it passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sync package-lock.json after dropping the HL dependency, so `npm ci` no longer
installs @nktkas/hyperliquid (or its transitive valibot).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…policy, trim spot copy
- Rename `acp trade status` to `acp trade hl-status`; clarify it's HL-account-only
and that on-chain balances come from `acp wallet balance`.
- add-signer: make the help/docs urge an explicit --policy (fallback is restricted,
changing it later is manual); note unrestricted is for non-Virtuals contracts.
- Remove concrete HL-spot how-to (examples + USDC-quoted note + table row); keep
spot as a conceptual venue. Trim backend-plumbing prose to observable behavior.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 49a9b65. Configure here.

Comment threadsrc/commands/trade.ts
} catch (err) {
outputError(json, err instanceof Error ? err : String(err));
}
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documented trade picker missing

Medium Severity

README and SKILL state that running acp trade with no flags in a TTY opens an interactive picker for humans. The command action always calls runTrade with Commander options only; there is no TTY or empty-args branch and no use of the existing prompt helpers used elsewhere in the CLI.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 49a9b65. Configure here.

@Zuhwa
Zuhwa merged commit 9f5b9be into mainJun 9, 2026
1 check passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@psmiratisu@Zuhwa@ai-virtual-b@claude