Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- QR code display for wallet address in `x402 init`, `x402 import`, and `x402 info` commands, allowing users to scan and fund the wallet directly from the terminal

### Changed

- Release process migrated from local `scripts/publish.sh` to GitHub Actions; `npm run release` now triggers the CI workflow instead of running locally
Expand Down
17 changes: 17 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"commander": "^14.0.2",
"ora": "^9.0.0",
"proper-lockfile": "^4.1.2",
"qrcode-terminal": "^0.12.0",
"undici": "^7.22.0",
"uuid": "^13.0.0",
"viem": "^2.46.3"
Expand All @@ -70,6 +71,7 @@
"@types/jest": "^30.0.0",
"@types/node": "^25.0.3",
"@types/proper-lockfile": "^4.1.4",
"@types/qrcode-terminal": "^0.12.2",
"@types/uuid": "^11.0.0",
"@typescript-eslint/eslint-plugin": "^8.50.0",
"@typescript-eslint/parser": "^8.50.0",
Expand Down
31 changes: 31 additions & 0 deletions src/cli/commands/x402.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import { Command } from 'commander';
import chalk from 'chalk';
import qrcode from 'qrcode-terminal';
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
import type { Hex } from 'viem';
import { formatSuccess, formatError, formatInfo, formatJson } from '../output.js';
Expand All @@ -18,6 +19,32 @@ import { signPayment, parsePaymentRequired } from '../../lib/x402/signer.js';

const USDC_DECIMALS = 6;

/**
* Generate a QR code string for the given text using small (half-block) mode.
*/
function generateQrCode(text: string): Promise<string> {
return new Promise((resolve) => {
qrcode.generate(text, { small: true }, (code) => {
resolve(code);
});
});
}

/**
* Print a QR code for an Ethereum address so the user can scan it to fund the wallet.
*/
async function printAddressQrCode(address: string): Promise<void> {
const qr = await generateQrCode(address);
console.log('');
console.log(chalk.bold(' Scan to fund this wallet:'));
console.log(
qr
.split('\n')
.map((line) => ` ${line}`)
.join('\n')
);
}

// ---------------------------------------------------------------------------
// Command: init
// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -45,6 +72,7 @@ async function initWallet(options: { outputMode: OutputMode }): Promise<void> {
console.log(formatSuccess('Wallet created'));
console.log(formatInfo(`Address: ${chalk.cyan(account.address)}`));
console.log(formatInfo('Fund this address with USDC on Base to use x402 payments.'));
await printAddressQrCode(account.address);
}
}

Expand Down Expand Up @@ -86,6 +114,8 @@ async function importWallet(options: {
} else {
console.log(formatSuccess('Wallet imported'));
console.log(formatInfo(`Address: ${chalk.cyan(account.address)}`));
console.log(formatInfo('Fund this address with USDC on Base to use x402 payments.'));
await printAddressQrCode(account.address);
}
}

Expand All @@ -110,6 +140,7 @@ async function walletInfo(options: { outputMode: OutputMode }): Promise<void> {

console.log(` ${chalk.bold('Address')} ${chalk.cyan(wallet.address)}`);
console.log(` ${chalk.bold('Created')} ${wallet.createdAt}`);
await printAddressQrCode(wallet.address);
}

// ---------------------------------------------------------------------------
Expand Down
Loading