11import { buildCommand , type CommandContext } from "@stricli/core" ;
2+ import type { PaymentRequired } from "@x402/fetch" ;
23import pc from "picocolors" ;
34import { createX402ProxyHandler , extractTxSignature } from "../handler.js" ;
45import { appendHistory , type TxRecord } from "../history.js" ;
@@ -91,7 +92,6 @@ Examples:
9192 console . log ( ` ${ pc . cyan ( "$ npx x402-proxy setup" ) } Reconfigure wallet` ) ;
9293 console . log ( ` ${ pc . cyan ( "$ npx x402-proxy wallet" ) } Addresses and balances` ) ;
9394 console . log ( ` ${ pc . cyan ( "$ npx x402-proxy wallet history" ) } Full payment history` ) ;
94- console . log ( ` ${ pc . cyan ( "$ npx x402-proxy wallet fund" ) } Funding instructions` ) ;
9595 console . log ( ) ;
9696 console . log (
9797 pc . dim ( " try: " ) +
@@ -113,7 +113,6 @@ Examples:
113113 ) ;
114114 console . log ( ` ${ pc . cyan ( "$ npx x402-proxy wallet" ) } Addresses and balances` ) ;
115115 console . log ( ` ${ pc . cyan ( "$ npx x402-proxy wallet history" ) } Payment history` ) ;
116- console . log ( ` ${ pc . cyan ( "$ npx x402-proxy wallet fund" ) } Funding instructions` ) ;
117116 console . log ( ` ${ pc . cyan ( "$ npx x402-proxy --help" ) } All options` ) ;
118117 console . log ( ) ;
119118 console . log ( pc . dim ( " try: " ) + pc . cyan ( "$ npx x402-proxy setup" ) ) ;
@@ -133,15 +132,29 @@ Examples:
133132 process . exit ( 1 ) ;
134133 }
135134
136- // Resolve wallet
137- const wallet = resolveWallet ( {
135+ // Resolve wallet - auto-setup on first use
136+ let wallet = resolveWallet ( {
138137 evmKey : flags . evmKey ,
139138 solanaKey : flags . solanaKey ,
140139 } ) ;
141140 if ( wallet . source === "none" ) {
142- error ( "No wallet configured." ) ;
143- console . error ( pc . dim ( `Run ${ pc . cyan ( "x402-proxy setup" ) } or set X402_PROXY_WALLET_MNEMONIC` ) ) ;
144- process . exit ( 1 ) ;
141+ if ( ! isTTY ( ) ) {
142+ error ( "No wallet configured." ) ;
143+ console . error (
144+ pc . dim (
145+ `Run:\n ${ pc . cyan ( "$ npx x402-proxy setup" ) } \n\nOr set X402_PROXY_WALLET_MNEMONIC` ,
146+ ) ,
147+ ) ;
148+ process . exit ( 1 ) ;
149+ }
150+ dim ( " No wallet found. Let's set one up first.\n" ) ;
151+ const { runSetup } = await import ( "./setup.js" ) ;
152+ await runSetup ( ) ;
153+ console . log ( ) ;
154+ wallet = resolveWallet ( ) ;
155+ if ( wallet . source === "none" ) {
156+ return ;
157+ }
145158 }
146159
147160 const config = loadConfig ( ) ;
@@ -183,8 +196,68 @@ Examples:
183196 const payment = shiftPayment ( ) ;
184197 const txSig = extractTxSignature ( response ) ;
185198
199+ // Payment failed - show funding instructions from the endpoint's actual requirements
200+ if ( response . status === 402 && isTTY ( ) ) {
201+ const prHeader =
202+ response . headers . get ( "PAYMENT-REQUIRED" ) ?? response . headers . get ( "X-PAYMENT-REQUIRED" ) ;
203+ let accepts : PaymentRequired [ "accepts" ] = [ ] ;
204+ if ( prHeader ) {
205+ try {
206+ const decoded = JSON . parse ( Buffer . from ( prHeader , "base64" ) . toString ( ) ) as PaymentRequired ;
207+ accepts = decoded . accepts ?? [ ] ;
208+ } catch {
209+ // Fall through with empty accepts
210+ }
211+ }
212+
213+ if ( accepts . length > 0 ) {
214+ const cheapest = accepts . reduce ( ( min , a ) =>
215+ Number ( a . amount ) < Number ( min . amount ) ? a : min ,
216+ ) ;
217+ const cost = ( Number ( cheapest . amount ) / 1_000_000 ) . toFixed ( 4 ) ;
218+ error ( `Payment required: ${ cost } USDC` ) ;
219+ } else {
220+ error ( "Payment required" ) ;
221+ }
222+
223+ const hasEvm = accepts . some ( ( a ) => a . network . startsWith ( "eip155:" ) ) ;
224+ const hasSolana = accepts . some ( ( a ) => a . network . startsWith ( "solana:" ) ) ;
225+ const hasOther = accepts . some (
226+ ( a ) => ! a . network . startsWith ( "eip155:" ) && ! a . network . startsWith ( "solana:" ) ,
227+ ) ;
228+
229+ if ( hasEvm || hasSolana ) {
230+ console . error ( ) ;
231+ dim ( " Fund your wallet with USDC:" ) ;
232+ if ( hasEvm && wallet . evmAddress ) {
233+ console . error ( ` Base: ${ pc . cyan ( wallet . evmAddress ) } ` ) ;
234+ }
235+ if ( hasSolana && wallet . solanaAddress ) {
236+ console . error ( ` Solana: ${ pc . cyan ( wallet . solanaAddress ) } ` ) ;
237+ }
238+ if ( hasEvm && ! wallet . evmAddress ) {
239+ dim ( " Base: endpoint accepts EVM but no EVM wallet configured" ) ;
240+ }
241+ if ( hasSolana && ! wallet . solanaAddress ) {
242+ dim ( " Solana: endpoint accepts Solana but no Solana wallet configured" ) ;
243+ }
244+ } else if ( hasOther ) {
245+ const networks = [ ...new Set ( accepts . map ( ( a ) => a . network ) ) ] . join ( ", " ) ;
246+ console . error ( ) ;
247+ error ( `This endpoint only accepts payment on unsupported networks: ${ networks } ` ) ;
248+ }
249+
250+ console . error ( ) ;
251+ dim ( " Then re-run:" ) ;
252+ console . error ( ` ${ pc . cyan ( `$ npx x402-proxy ${ url } ` ) } ` ) ;
253+ console . error ( ) ;
254+ return ;
255+ }
256+
186257 if ( payment && isTTY ( ) ) {
187- info ( ` Payment: ${ payment . amount ?? "?" } (${ payment . network ?? "unknown" } )` ) ;
258+ info (
259+ ` Payment: ${ payment . amount ? ( Number ( payment . amount ) / 1_000_000 ) . toFixed ( 4 ) : "?" } USDC (${ payment . network ?? "unknown" } )` ,
260+ ) ;
188261 if ( txSig ) dim ( ` Tx: ${ txSig } ` ) ;
189262 }
190263
0 commit comments