diff --git a/.changeset/fund-command-testnet-default.md b/.changeset/fund-command-testnet-default.md new file mode 100644 index 00000000..03d2a67b --- /dev/null +++ b/.changeset/fund-command-testnet-default.md @@ -0,0 +1,5 @@ +--- +'mppx': patch +--- + +Defaulted account faucet funding to testnet unless a network or RPC URL was specified. diff --git a/src/cli/cli.ts b/src/cli/cli.ts index 640be5b9..9d074b4e 100755 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -32,6 +32,7 @@ import { printResponseHeaders, prompt, resolveChain, + resolveFundingNetwork, resolveRpcUrl, } from './utils.js' @@ -704,8 +705,9 @@ const account = Cli.create('account', { const addrDisplay = explorerUrl ? link(`${explorerUrl}/address/${acct.address}`, acct.address) : acct.address - const rpcUrl = resolveRpcUrl(c.options.rpcUrl, { network: c.options.network }) - resolveChain({ network: c.options.network, rpcUrl }) + const fundingNetwork = resolveFundingNetwork(c.options) + const rpcUrl = resolveRpcUrl(c.options.rpcUrl, { network: fundingNetwork }) + resolveChain({ network: fundingNetwork, rpcUrl }) .then((chain) => createClient({ chain, transport: http(rpcUrl) })) .then((client) => import('viem/tempo').then(({ Actions }) => @@ -715,6 +717,11 @@ const account = Cli.create('account', { return outputResult(c, { address: acct.address, name: resolvedName }, () => { console.log(`Account "${resolvedName}" saved to keychain.`) console.log(pc.dim(`Address ${addrDisplay}`)) + console.log( + pc.dim( + `Fund testnet tokens: mppx account fund --account ${resolvedName} --network testnet`, + ), + ) }) }, }) @@ -840,8 +847,9 @@ const account = Cli.create('account', { return c.error({ code: 'ACCOUNT_NOT_FOUND', message: 'No account found.', exitCode: 69 }) } const acct = privateKeyToAccount(key as `0x${string}`) - const rpcUrl = resolveRpcUrl(c.options.rpcUrl, { network: c.options.network }) - const chain = await resolveChain({ network: c.options.network, rpcUrl }) + const fundingNetwork = resolveFundingNetwork(c.options) + const rpcUrl = resolveRpcUrl(c.options.rpcUrl, { network: fundingNetwork }) + const chain = await resolveChain({ network: fundingNetwork, rpcUrl }) const client = createClient({ chain, transport: http(rpcUrl) }) if (!structured) console.log(`Funding "${accountName}" on ${chainName(chain)}`) try { @@ -864,14 +872,18 @@ const account = Cli.create('account', { }, ) } catch (err) { + const message = err instanceof Error ? err.message : String(err) if (structured) return c.error({ code: 'FUNDING_FAILED', - message: err instanceof Error ? err.message : String(err), + message, exitCode: 1, }) - console.error('Funding failed:', err instanceof Error ? err.message : err) - return undefined as never + return c.error({ + code: 'FUNDING_FAILED', + message: `Funding failed: ${message}`, + exitCode: 1, + }) } }, }) diff --git a/src/cli/utils.test.ts b/src/cli/utils.test.ts index a14de91f..b942c292 100644 --- a/src/cli/utils.test.ts +++ b/src/cli/utils.test.ts @@ -1,7 +1,7 @@ import { tempo as tempoMainnet, tempoModerato } from 'viem/tempo/chains' import { afterEach, describe, expect, test } from 'vp/test' -import { networkRpcUrls, resolveChain, resolveRpcUrl } from './utils.js' +import { networkRpcUrls, resolveChain, resolveFundingNetwork, resolveRpcUrl } from './utils.js' describe('resolveRpcUrl', () => { afterEach(() => { @@ -52,6 +52,30 @@ describe('resolveRpcUrl', () => { }) }) +describe('resolveFundingNetwork', () => { + afterEach(() => { + delete process.env.MPPX_RPC_URL + delete process.env.RPC_URL + }) + + test('defaults faucet funding to testnet', () => { + expect(resolveFundingNetwork()).toBe('testnet') + }) + + test('keeps explicit network selection', () => { + expect(resolveFundingNetwork({ network: 'mainnet' })).toBe('mainnet') + }) + + test('does not override explicit rpc url', () => { + expect(resolveFundingNetwork({ rpcUrl: 'https://explicit.example.com' })).toBeUndefined() + }) + + test('does not override env rpc urls', () => { + process.env.MPPX_RPC_URL = 'https://env.example.com' + expect(resolveFundingNetwork()).toBeUndefined() + }) +}) + describe('resolveChain', () => { afterEach(() => { delete process.env.MPPX_RPC_URL diff --git a/src/cli/utils.ts b/src/cli/utils.ts index c18e1c63..6fbabe32 100644 --- a/src/cli/utils.ts +++ b/src/cli/utils.ts @@ -242,6 +242,16 @@ export function resolveRpcUrl( ) } +/** Select the default network for faucet funding without overriding explicit RPC configuration. */ +export function resolveFundingNetwork( + options: { network?: Network | undefined; rpcUrl?: string | undefined } = {}, +): Network | undefined { + if (options.network) return options.network + if (options.rpcUrl) return undefined + if (process.env.MPPX_RPC_URL?.trim() || process.env.RPC_URL?.trim()) return undefined + return 'testnet' +} + export async function resolveChain( opts: { network?: Network | undefined; rpcUrl?: string | undefined } = {}, ): Promise {