Skip to content

x402: sign exact payments on nano:mainnet - #473

Open
dhyabi2 wants to merge 1 commit into
apify:mainfrom
dhyabi2:feat/x402-nano-exact
Open

dhyabi2 wants to merge 1 commit into
apify:mainfrom
dhyabi2:feat/x402-nano-exact

Conversation

@dhyabi2

@dhyabi2 dhyabi2 commented Oct 24, 2026

Copy link
Copy Markdown

Problem

mcpc can only pay x402 on Base. A server that also offers an exact accept on nano:mainnet cannot be paid in XNO. Selection also takes the first valid exact entry on any network, so a list that starts with a network mcpc cannot sign for (nano, or eip155:1) fails with "Unknown network" even when a Base entry follows it.

Change

  • src/lib/x402/nano.ts: nano_ addresses with checksum, state block hash (blake2b), signing (ed25519 with blake2b-512), and account_info / block_info / work_generate against MCPC_X402_NANO_RPC. In Nano the amount sent is the balance drop, so the RPC is not trusted for the balance: the frontier block's hash and signature are checked against the account key first. The signed block goes in PAYMENT-SIGNATURE as payload.block + payload.hash and is not broadcast. The server publishes it.
  • signer.ts: dispatch on nano:mainnet (exact only). Selection only picks accepts mcpc can sign for, and nano only when MCPC_X402_NANO_KEY is set. The no-match error lists networks, including nano.
  • Spend cap: no signing without MCPC_X402_NANO_MAX_RAW or above it. --amount (USD) is refused for nano.
  • Crypto: @noble/hashes 1.8.0 and @noble/curves 1.9.1 are already in the lockfile through viem. They are now devDependencies at those exact versions and exported from the viem boundary module, so they get bundled like viem. No new package is installed. Bundle: 690 -> 726 KiB.
  • README, SKILL.md, CHANGELOG. No help text changed (check:reference up to date).

Verification

Node v22.22.0, pnpm 10.33.4: pnpm install --frozen-lockfile && pnpm run build && pnpm run test:unit && pnpm run lint

  • main bc18da2: Test Files 58 passed (58), Tests 1345 passed (1345)
  • this branch: Test Files 59 passed (59), Tests 1363 passed (1363). Lint shows 0 errors and the same 24 warnings as main.

The new signer.test.ts run against main's source: Tests 8 failed | 30 passed (38). For example, skips nano accepts when no Nano key is configured: expected 'nano:mainnet' to be 'eip155:84532'. All of them pass on this branch.

Vectors in nano.test.ts:

  • seed 0 / index 0 key -> nano_3i1aq1cchnmbn9x5rsbap8b15akfh7wj7pwskuzi7ahz8oq6cobd99d4r3b7 (RPC docs deterministic_key)
  • the RPC docs block_hash example -> FF0144…4F17
  • signatures and a full send block via a fake RPC, matching nanocurrency-js 2.5.0 byte for byte
  • an RPC that misreports the balance is refused

Built CLI against a local fake RPC: x402 sign refused without the key, without the cap and above it, and with all three set printed the same block as the test.

Risks

  • There is no published x402 scheme spec for Nano. The payload shape (payload.block in node process JSON, payload.hash) is a proposal, and servers must verify it and broadcast it.
  • The config is env-only, with no x402 init/import --nano. A Base wallet is still required. Bridges read the env when they start.
  • The RPC is trusted for the frontier and the work. A stale frontier gives a fork that the network rejects, not an overpayment. An account whose frontier is an epoch block or a legacy block is refused. Pending funds are not received.
  • amountUsd is 0 for nano, so x402 sign --json prints amount: 0. Use amountAtomicUnits (raw).
  • The middleware's reused signature is a single-use block for nano. After it settles, the next call gets a 402 and signs again.

Add a Nano (XNO) leg next to the Base USDC rail. When a server offers an
exact accept on nano:mainnet and MCPC_X402_NANO_KEY is set, mcpc builds a
Nano state send block, signs it (ed25519 with blake2b), and sends it in
PAYMENT-SIGNATURE as payload.block. The block is not broadcast; the server
publishes it to settle.

- nano.ts: address encoding with checksum, state block hash, signing,
  account_info/block_info/work_generate over MCPC_X402_NANO_RPC. The
  frontier block's hash and signature are checked against the account key
  before its balance is used, since the send amount is the balance drop.
- Refuses to sign without MCPC_X402_NANO_MAX_RAW or above it.
- Accept selection only picks networks mcpc can sign for, so a Base-only
  setup is never handed a nano accept, and nano is exact-only.
- blake2b/ed25519 come from @noble/hashes and @noble/curves, already in the
  lockfile through viem, now listed as devDependencies at the same versions
  and bundled through the viem boundary module.

This branch has not been deployed

No deployments
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