Skip to content

feat: wallet improvements and send command#50

Merged
blakecduncan merged 8 commits intoreleasefrom
blake/wallet-and-send
Apr 7, 2026
Merged

feat: wallet improvements and send command#50
blakecduncan merged 8 commits intoreleasefrom
blake/wallet-and-send

Conversation

@blakecduncan
Copy link
Copy Markdown

@blakecduncan blakecduncan commented Apr 7, 2026

Summary

  • wallet create: New primary command (replaces wallet generate as recommended), swaps @alchemy/x402 imports for direct viem usage
  • wallet qr: Displays wallet address as a scannable terminal QR code
  • Gas mode config: Adds gas_mode (sponsored/wallet-paid) and gas_policy_id config keys, env vars (ALCHEMY_GAS_MODE, ALCHEMY_GAS_POLICY_ID), and --gas-mode/--gas-policy-id CLI flags
  • send command: 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 modes
  • Shared smart wallet infra: src/lib/smart-wallet.ts (client factory) and src/lib/chains.ts (network→viem chain mapping) for reuse by upcoming execution commands (swap, bridge, approve, wrap, unwrap)

Test plan

  • alchemy wallet create generates wallet and saves to config
  • alchemy wallet generate still works as alias
  • alchemy wallet qr displays scannable QR code
  • alchemy config set gas-sponsored true / alchemy config get gas-sponsored works
  • alchemy send <to> <amount> -n eth-sepolia sends native ETH (wallet-paid)
  • alchemy send <to> <amount> -n eth-sepolia --gas-sponsored --gas-policy-id <id> sends with sponsorship
  • alchemy send <to> <amount> --token <erc20> -n eth-sepolia sends ERC-20 tokens
  • All 273 tests pass

🤖 Generated with Claude Code

Non sponsored eth send
Screenshot 2026-04-07 at 2 23 33 PM

sponsored eth send
Screenshot 2026-04-07 at 2 22 08 PM

sponsored usdc send
Screenshot 2026-04-07 at 2 19 57 PM

blakecduncan and others added 4 commits April 7, 2026 10:52
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>
@blakecduncan blakecduncan requested a review from a team as a code owner April 7, 2026 17:51
blakecduncan and others added 3 commits April 7, 2026 13:54
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>
@blakecduncan blakecduncan removed the request for review from a team April 7, 2026 18:10
import { exitWithError, errInvalidArgs } from "../lib/errors.js";
import { withSpinner, printKeyValueBox, green } from "../lib/ui.js";

const NATIVE_DECIMALS = 18;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Native decimal for solana is 9 right? Is this send only for eth?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

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 {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

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 });
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

dumb question - where does this get displayed?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

just right in the terminal after calling the command

Screenshot 2026-04-07 at 3 25 30 PM

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.

@blakecduncan
Copy link
Copy Markdown
Author

@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>
@blakecduncan blakecduncan merged commit 26e88fb into release Apr 7, 2026
4 checks passed
@blakecduncan blakecduncan deleted the blake/wallet-and-send branch April 7, 2026 20:34
Sign up for free to 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.

2 participants