Feat/solana wallet (conflict-resolved)#57
Open
psmiratisu wants to merge 36 commits into
Open
Conversation
…lance, message signing, and transfers
…ersioned txs Wires the trade loop to the backend's new Solana sign actions: - sigType 'solana-message': Privy signs the raw challenge bytes (no envelope); the adapter's base58 output is re-encoded to BASE64 — the Treasures ownership-proof contract (base58 is their #1 documented rejection cause). - sigType 'solana-tx': sign the serialized versioned tx WITHOUT broadcasting (server/venue broadcast the signed bytes), via the adapter's Privy signTransaction (base64 in/out). - solWallet rides every /plan that could route through Solana: explicit sol venue/source, or a tokenized-stock BUY with no venue pinned — the backend then quotes both venues and executes the better one. Sells stay explicit (the backend can't see which venue holds shares). Verified against the live Privy signer for Mochi3DSTest: the signature checks out as raw ed25519 over the exact challenge bytes (local ed25519.verify — the same check Treasures runs server-side). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s top-up) The backend emits a 'solana-instructions' action before a sol-venue trade when the wallet's native SOL is below the gas floor — a Jupiter USDC→SOL swap. Run it through the adapter's sponsored sendInstructions (Alchemy fee payer) so a zero-SOL wallet can bootstrap, mapping the backend's role strings to AccountRole bitflags and posting back the broadcast signature. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e-SOL gas top-up)" This reverts commit d561aef.
- package-lock: re-resolve @virtuals-protocol/acp-node-v2 to the published 0.1.4 registry tarball instead of the sibling ../acp-node-v2 link, so `npm ci` works on machines without that local path (was breaking CI/releases). - trade: recognize the Privy Solana chain ids (500 devnet / 501 mainnet) in isSolanaChainRef, so a swap with --chain-in 501 attaches solWallet. - trade: route opts.chain through isSolanaChainRef instead of an exact `=== "sol"` match, so a Treasures sell with --chain solana (or other casing) also attaches solWallet. isSolanaChainRef is now the single source of truth. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bugbot follow-up on PR #44: couldRouteViaSolana covered --chain and --chain-in but not --chain-out, so a swap/bridge whose destination is Solana reached /trade/plan without the agent's Solana pubkey — the recipient the backend needs to route/sign the destination leg. Add the symmetric isSolanaChainRef(opts.chainOut) check. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bugbot follow-up on PR #44: the unpinned-buy heuristic fired on --token alone, so `--token AAPL --amount-usdc 1 --chain eth` still attached solWallet. With solWallet present the backend quotes both venues and may pick sol, overriding the user's explicit --chain eth pin. Gate the buy clause on opts.chain === undefined; an explicit --chain sol still routes via the isSolanaChainRef(opts.chain) clause. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mount) Bugbot follow-up on PR #44: the negative formulation classified any --token without --side/--amount-shares/--chain as an unpinned buy, so a malformed perp like `--token BTC --size 0.01` (missing --side) attached solWallet. Define a buy positively: --token plus a spend amount (--amount-usdc on eth, or --amount-in funded from another chain). This covers both documented buy shapes and excludes perp/incomplete shapes that carry no spend signal. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bugbot follow-up on PR #44: the empty catch around getSolanaWalletAddress flattened every failure (network, auth, agent lookup) into "no wallet", silently dropping solWallet. Swallow only the NO_SOLANA_WALLET signal, and only for a speculative unpinned buy; real failures now surface, and an explicit Solana route (--chain/--chain-in/--chain-out sol) propagates any error rather than planning a route it can't sign. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Solana/Treasures work added spot tokenized-stock buy/sell, sol-venue auto-routing, and swaps in/out of Solana — none of which were in the docs. Adds to both README and SKILL.md: - intent-routing rows for Solana-source swaps and tokenized-stock spot - the stock-vs-perp rule (route by FLAG --amount-usdc/-shares vs --side, never by the ticker — AAPL is both a stock and an HL equity perp) - examples: buy with held USDC, buy funded from another chain, sell, USDC@sol → USDC@Base - command-table row + capability description for tokenized stocks Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Read-only discovery wrapping the backend's GET /trade/instruments:
- no symbol → spot markets { stocks, hlSpot } (tokenized stocks + HL spot)
- with a symbol → every route for that asset, each naming the exact
`token` ticker to pass (e.g. xyz:AAPL for the equity perp, AAPL spot)
Adds a GET helper (the trade client only had POST). Documents the command
and the funding model in README.md and SKILL.md — USDC is the settlement
currency, not a prerequisite; trades fund from any chain/token and the
backend auto-bridges, so the listing never implies pre-holding USDC on HL.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e.json and package-lock.json
…46) * feat: add wallet policy management commands and update documentation * refactor: update CLI commands to clarify dashboard approval requirements and improve user guidance for policy changes --------- Co-authored-by: Zuhwa <zuhwa@virtuals.io>
#47) * feat: enhance wallet balance command to support querying all supported chain * feat: add native currency resolution for token display in wallet commands --------- Co-authored-by: Zuhwa <zuhwa@virtuals.io>
…rove documentation
…n balance support for all sponsored EVM chains and Solana
The backend now returns the agent's Treasures stock portfolio under `data.stocks` on GET /agents/:id/assets. Surface it in the wallet balance views. - Add StockPosition type + data.stocks to AgentAssetsResponse (was silently dropped before). - `wallet balance` and `wallet sol balance` now emit `stocks` in --json output and render a "Tokenized Stocks" table (TICKER, TOKEN, TOKENS, SHARES, USD) in TTY. The full portfolio is shown regardless of the queried chain (it isn't tied to the network). - usd_value (precomputed upstream) preferred; falls back to tokens × usd_per_token; nulls render as "—". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extract stockUsd(): prefer usd_value, fall back to tokens × usd_per_token, "—" when unknown. Piped output previously used `usd_value ?? "0"`, showing $0 for unknown values and disagreeing with the TTY table. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The tokenized-stock table only displayed a computed USD value, hiding the per-position pricing the backend already returns. Surface $/share, $/token, average entry price, USD value, and unrealized PnL (signed) in both the TTY table and the piped output. --json was already complete (verbatim positions passthrough); this brings the human views in line. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bring the tokenized-stock balance work onto the Solana-wallet branch. Resolved src/commands/wallet.ts by folding stock rendering into the unified renderBalances() helper (the Solana-wallet refactor) instead of the old inline per-command rendering: stocks render once after the per-network token tables (TTY), are included in --json, and emitted in the piped output. Keeps the $/share, $/token, avg-entry, value, and PnL columns. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A dry run signs and submits nothing — the server returns a `preview` action and runTradeLoop returns before any send/sign — yet the CLI eagerly built the signer adapter (createProviderAdapter), so `--dry-run` failed with "No signer configured" on agents without a key. Pass `undefined` for the dry-run provider and assert it only in the execution branches (send / EVM sign) via requireSigner(), which a dry run never reaches. Live trades are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve conflicts to clear PR #44's merge conflicts with main: - package.json/lock: take acp-node-v2 ^0.1.6 (main); lock regenerated - chains.ts: keep Solana-aware getNativeCurrency (superset of main's) - walletGate.ts: keep withSolanaWallet alongside main's approval-url mirroring - trade.ts: keep --dry-run skip-signer behavior - wallet.ts/README: keep branch versions (superset of main's #47 wallet-balance work) Build + tsc pass (the pre-existing job.ts typecheck error exists on main too). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…de loop A live HL-exit froze indefinitely mid-trade: a /trade/next call stalled with the connection open, and since fetch had no timeout it never returned or threw — so the trade loop's existing transient-retry never fired and it hung past the backend's 10-min settle-timeout (the withdraw had settled on Arbitrum, but the bridge leg was never reached). - trade.ts post()/get(): add AbortSignal.timeout(120s) to the fetch. A stall now throws → `wait`-poll calls retry (existing path), others surface a clean REQUEST_TIMEOUT (code "TIMEOUT") instead of hanging. - api/client.ts: same timeout on the shared ApiClient fetch (every CLI command), via a fetchWithTimeout helper. 120s is generous for slow legitimate calls (e.g. a LiFi quote inside /trade/next) but bounded. Follow-up: auto-resume a timed-out /trade/next by tradeId once the backend's per-step idempotency is confirmed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A long trade (e.g. an HL-exit's multi-minute settle wait) can outlive the access token: the loop captures it once at the start and never refreshes, and resolveToken only refreshes when the token is ALREADY expired — so a token with a few minutes left passes the start check, then expires mid-loop and the next /trade/next returns 401, killing the trade (observed live: a ~7-min HL-exit died 401 after the withdraw settled but before the bridge). - client.ts: export forceTokenRefresh() — mint a new access token via the stored refresh token unconditionally (the local expiry check can disagree with the server, so re-resolving isn't enough). - trade.ts post(): on a 401, call the onAuthRefresh callback once and retry with the fresh token (not counted as a transient retry). - runTradeLoop: hold a mutable currentToken and pass an onAuthRefresh that force-refreshes and updates it, so the rest of the loop uses the new token. Pairs with the request-timeout fix: together, long-running trades no longer hang on a stalled connection or die on an expired token. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The trade routing tables in README and SKILL only covered Solana→EVM and treated --chain-out as always-required. Reflect the shipped behavior: - --chain-out is optional, defaulting to --chain-in (omit to stay on the source chain); single-chain tokens like `sol` infer their own chain. - Add the EVM→Solana (buy SOL/SPL) and Solana→Solana routing rows + a buy-SOL example; delivery recipient is auto-derived from the agent's Solana wallet, so --recipient is only for sending elsewhere. - Fix the SKILL swap flag-reference row (was "both chains EVM", --chain-out required) to cover Solana and mark --chain-out/--recipient optional. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Agents reading the trade docs could miss that a single `acp trade` is decomposed into a full multi-leg route (e.g. HL sell → transfer → withdraw → bridge+swap) and try to chain `deposit`/`spot`/`bridge`/`swap` calls themselves. Add an explicit "one command, never chain trades yourself" callout to both README and SKILL, a real multi-leg example (PURR@HL → ETH@Base), and a note that a multi-leg route can take minutes to settle (not a hang) so agents don't re-issue. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: trade routing — chain-out defaulting, Solana buys, and multi-leg
# Conflicts: # README.md # SKILL.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Conflict-resolved version of #56 (
feat/solana-wallet).This branch is
feat/solana-walletwith the latestmainmerged in and the merge conflicts resolved, so it merges cleanly intomain. It supersedes #56, which is showing as conflicted because its head branch couldn't be updated.Conflict resolution
Both conflicts were docs-only; in each case the feature branch carried a superset of content while
mainhad nothing competing, so the feature-branch content was kept and the markers removed:tradetable rows (EVM→Solana buy, Solana→Solana swap) plus the--chain-outdefaulting and one-command-route notes.acp tradeexamples (buy SOL from Base, multi-leg HL→Base exit).Code files (
package.json,package-lock.json,src/commands/job.ts) auto-merged frommainwith no conflict.🤖 Generated with Claude Code
Generated by Claude Code
Note
Low Risk
Markdown-only documentation changes with no runtime or API behavior modified in this diff.
Overview
Documentation-only updates to
README.mdandSKILL.mdforacp trade(no application code in this diff).The intent routing table gains EVM → Solana (buy SOL/SPL to the agent wallet) and Solana → Solana swaps, alongside the existing Solana → EVM row.
New callouts explain that
--chain-outdefaults to--chain-in, that--token-out solinfers Solana, that Solana buys auto-deliver to the agent’s Solana wallet unless--recipientis set, and that oneacp tradecall should express start→end while the backend plans and the CLI signs every leg (with timing expectations for multi-leg routes).Examples add buying SOL from Base without
--chain-outand (in SKILL.md) a multi-leg PURR@HL → ETH@Base example. The swap command reference row is relaxed:--chain-outis optional, coverage includes Solana↔EVM and Solana↔Solana, and--recipientis noted as auto-derived for Solana delivery.Reviewed by Cursor Bugbot for commit 0635ea4. Bugbot is set up for automated code reviews on this repo. Configure here.