feat: wallet improvements and send command#50
Conversation
Replace @alchemy/x402 wallet utilities with direct viem/accounts imports, add `wallet create` as the primary command, and keep `wallet generate` as a compatibility alias. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add gas_mode (sponsored|wallet-paid) and gas_policy_id to config, resolve functions with flag > env > config precedence, and root-level --gas-mode / --gas-policy-id CLI options for state-changing commands. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add the `send` command for transferring native tokens and ERC-20 tokens using Alchemy's smart wallet client (@alchemy/wallet-apis). Includes shared infrastructure for future execution commands: - src/lib/chains.ts: network slug → viem Chain mapping (40+ networks) - src/lib/smart-wallet.ts: shared smart wallet client factory with gas sponsorship via paymaster capabilities - src/commands/send.ts: send command with ENS resolution, human-readable amount parsing, ERC-20 token support, and sponsored/wallet-paid gas modes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add `wallet qr` to display wallet address as a scannable QR code - Show gas mode (Sponsored/Wallet-paid) in send output for both human and JSON modes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Simplify gas config from string enum ("sponsored"|"wallet-paid") to a
boolean, matching the existing x402/verbose pattern. Flag becomes
--gas-sponsored, env becomes ALCHEMY_GAS_SPONSORED.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| import { exitWithError, errInvalidArgs } from "../lib/errors.js"; | ||
| import { withSpinner, printKeyValueBox, green } from "../lib/ui.js"; | ||
|
|
||
| const NATIVE_DECIMALS = 18; |
There was a problem hiding this comment.
Native decimal for solana is 9 right? Is this send only for eth?
There was a problem hiding this comment.
Yeah I just realized I didn't cover any of the solana stuff in the doc. I'm actually thinking that we have a different code path for solana. And then we branch based on the network being passed in the command. Maybe I can add solana send next as a follow on pr if that makes sense?
| tokenAddress: string, | ||
| ): Promise<{ decimals: number; symbol: string }> { | ||
| const client = clientFromFlags(program); | ||
| const result = await client.call("alchemy_getTokenMetadata", [tokenAddress]) as { |
There was a problem hiding this comment.
Should we at least have some known assets <> decimal mapping so we don't query alchemy everytime? Maybe alchemy get token metadata should be a fallback instead of it being a call?
There was a problem hiding this comment.
Yeah that is a fair point - I was against hardcoding this because tokens have a different addresses on every chain. So we would need to key the look up by chain id and contract address. Then there are some tokens (like usdt) that are 6 decimals on some chains and 18 on others.
Alternatively we could call the contract directly to get the decimal if we don't want to call the data apis.
| if (isJSONMode()) { | ||
| printJSON({ address }); | ||
| } else { | ||
| const qr = await QRCode.toString(address, { type: "terminal", small: true }); |
There was a problem hiding this comment.
dumb question - where does this get displayed?
src/lib/chains.ts
Outdated
| import * as viemChains from "viem/chains"; | ||
| import { errInvalidArgs } from "./errors.js"; | ||
|
|
||
| const SLUG_TO_CHAIN: Record<string, Chain> = { |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
|
@codex review |
Register gas-sponsored and gas-policy-id under `config set`, add them to `config list` and `config get` defaults. Also rename chains.ts internal map variable for clarity. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Summary
wallet create: New primary command (replaceswallet generateas recommended), swaps@alchemy/x402imports for directviemusagewallet qr: Displays wallet address as a scannable terminal QR codegas_mode(sponsored/wallet-paid) andgas_policy_idconfig keys, env vars (ALCHEMY_GAS_MODE,ALCHEMY_GAS_POLICY_ID), and--gas-mode/--gas-policy-idCLI flagssendcommand: Transfer native tokens or ERC-20s via Alchemy smart wallet client (@alchemy/wallet-apis) with ENS resolution, human-readable amounts, and sponsored/wallet-paid gas modessrc/lib/smart-wallet.ts(client factory) andsrc/lib/chains.ts(network→viem chain mapping) for reuse by upcoming execution commands (swap, bridge, approve, wrap, unwrap)Test plan
alchemy wallet creategenerates wallet and saves to configalchemy wallet generatestill works as aliasalchemy wallet qrdisplays scannable QR codealchemy config set gas-sponsored true/alchemy config get gas-sponsoredworksalchemy send <to> <amount> -n eth-sepoliasends native ETH (wallet-paid)alchemy send <to> <amount> -n eth-sepolia --gas-sponsored --gas-policy-id <id>sends with sponsorshipalchemy send <to> <amount> --token <erc20> -n eth-sepoliasends ERC-20 tokens🤖 Generated with Claude Code
Non sponsored eth send

sponsored eth send

sponsored usdc send
