POST to Wallet Snapshot
Send public wallet input to https://api.blockchainmoneymap.com/api/x402/wallet-snapshot without a payment header.
Node.js x402 guide
Start with an unpaid request that needs no wallet or packages. The optional paid retry checks the resource, network, token, receiver, and your spending limit before signing with a dedicated buyer wallet.
Send public wallet input to https://api.blockchainmoneymap.com/api/x402/wallet-snapshot without a payment header.
The response tells the buyer agent the exact price, receiver, network, and protected resource.
Before signing USDC on base, verify the exact resource, scheme, network, asset, receiver, and your spending limit. Never send private keys to BMM.
Keep the request body unchanged and retry with the signed x402 payment header.
The paid response returns the Wallet Snapshot result; outputs are research summaries, not financial or compliance advice.
Use Node.js 22 or later. Save the example below as bmm-wallet-snapshot.mjs and run node bmm-wallet-snapshot.mjs. It stops after HTTP 402 and never reads a private key. The request may create ordinary server logs, but it sends no payment.
Only the optional paid retry needs these packages. Install them in an isolated buyer project and retain its lockfile. The maintained repository example also supports a fully offline fixture.
npm install @x402/fetch @x402/evm viemHTTP 402 confirms that a payment challenge was received. It does not prove paid delivery. The default command exits here; running with --pay explicitly enables one paid retry.
Before using --pay, independently verify the published receiver and token contract for the configured payment network. Set BUYER_EXPECTED_ASSET to that token contract and BUYER_MAX_AMOUNT_ATOMIC to your integer spending limit. For USDC with six decimals, 10,000 atomic units is a $0.01 budget; this is an example budget, not the current service price. A missing limit or a challenge over that limit stops before signing.
Keep BUYER_PRIVATE_KEY only in your local buyer environment. Review multiple offers manually: this example accepts exactly one. After an uncertain paid response, check settlement before trying again.
// Save as bmm-wallet-snapshot.mjs. Node.js 22+.
// node bmm-wallet-snapshot.mjs (unpaid; no packages or wallet needed)
// Optional paid retry: node bmm-wallet-snapshot.mjs --pay
const args = process.argv.slice(2);
if (args.length && !(args.length === 1 && args[0] === '--pay')) throw new Error('Only --pay is supported');
const pay = args[0] === '--pay';
const endpoint = "https://api.blockchainmoneymap.com/api/x402/wallet-snapshot";
const body = JSON.stringify({
address: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
chain: "base"
});
const first = await fetch(endpoint, {
method: "POST",
redirect: 'error', signal: AbortSignal.timeout(15000),
headers: { "content-type": "application/json" },
body
});
if (first.status !== 402) {
throw new Error(`Expected 402 payment challenge, got ${first.status}`);
}
const header = first.headers.get('payment-required');
await first.body?.cancel();
if (!header || header.length > 32768) throw new Error('Missing or oversized payment-required header');
const paymentRequired = JSON.parse(Buffer.from(header, 'base64').toString('utf8'));
if (paymentRequired.x402Version !== 2 || paymentRequired.resource?.url !== endpoint ||
!Array.isArray(paymentRequired.accepts) || !paymentRequired.accepts.length || paymentRequired.accepts.length > 16)
throw new Error('Unexpected x402 version, resource, or offers');
console.log({ status: first.status, offerCount: paymentRequired.accepts.length, paymentAttempted: false });
// A 402 proves only that a challenge was received, not paid delivery or reliability.
if (!pay) process.exit(0);
const validateBuyerOffer = function c(a,b){let c=a=>{if(!a||"object"!=typeof a||Array.isArray(a))throw Error("Invalid payment object");return a},d=a=>{if("string"!=typeof a||!/^0x[0-9a-f]{40}$/i.test(a)||/^0x0{40}$/i.test(a))throw Error("Invalid or missing expected asset/receiver");return a.toLowerCase()},e=a=>{if("string"!=typeof a||!/^[1-9][0-9]{0,77}$/.test(a))throw Error("Invalid atomic amount or budget");let b=BigInt(a);if(b>(1n<<256n)-1n)throw Error("Atomic amount exceeds uint256");return b},f=d(b.asset),g=d(b.payTo),h=e(b.maxAmountAtomic),i=new URL(b.resource);if(!(!0===b.allowLoopbackHttp&&"http:"===i.protocol&&["localhost","127.0.0.1","[::1]"].includes(i.hostname))&&"https:"!==i.protocol||i.username||i.password||i.search||i.hash||!/^eip155:[1-9][0-9]*$/.test(b.network))throw Error("Invalid expected resource or network");let j=c(a);if(2!==j.x402Version||c(j.resource).url!==i.href||!Array.isArray(j.accepts)||j.accepts.length<1||j.accepts.length>16)throw Error("Unexpected x402 version, resource, or offers");if(1!==j.accepts.length)throw Error("Expected exactly one payment offer; review multiple offers manually");let k=c(j.accepts[0]),l=void 0===k.extra?{}:c(k.extra);if(void 0!==l.assetTransferMethod&&"eip3009"!==l.assetTransferMethod)throw Error("Token approval/Permit2 payment paths require separate review");if("exact"!==k.scheme||k.network!==b.network||d(k.asset)!==f||d(k.payTo)!==g)throw Error("Unexpected x402 scheme, network, asset, or receiver");if(e(k.amount)>h)throw Error("Payment exceeds the explicit buyer budget");if(void 0!==b.expectedAmountAtomic&&e(k.amount)!==e(b.expectedAmountAtomic))throw Error("Decoded payment amount differs from the approved route price");if(!Number.isInteger(k.maxTimeoutSeconds)||1>Number(k.maxTimeoutSeconds)||Number(k.maxTimeoutSeconds)>300)throw Error("Unexpected payment authorization lifetime");return Object.freeze({scheme:k.scheme,network:k.network,asset:k.asset,amount:k.amount,payTo:k.payTo,maxTimeoutSeconds:k.maxTimeoutSeconds})};
const validateBuyerPaymentPayload = function d(a,b,c){if(a?.x402Version!==2||a.resource?.url!==b||["scheme","network","asset","amount","payTo","maxTimeoutSeconds"].some(b=>a.accepted?.[b]!==c[b]))throw Error("Signed payload differs from the approved resource or offer; do not submit")};
const policy = {
resource: endpoint,
network: "eip155:8453",
payTo: "0x2211E12e6Bc8Da27B48B9DD66A55f5C3388D713a",
asset: process.env.BUYER_EXPECTED_ASSET, // Independently approved token contract, not copied from this challenge.
maxAmountAtomic: process.env.BUYER_MAX_AMOUNT_ATOMIC // Required integer budget; no automatic/default spend.
};
const approvedOffer = validateBuyerOffer(paymentRequired, policy); // BEFORE importing a signer or reading a private key.
const { x402Client, x402HTTPClient } = await import('@x402/fetch');
const { registerExactEvmScheme } = await import('@x402/evm/exact/client');
const { privateKeyToAccount } = await import('viem/accounts');
const privateKey = process.env.BUYER_PRIVATE_KEY;
if (!/^0x[0-9a-f]{64}$/i.test(privateKey ?? '')) throw new Error('A dedicated local buyer key is required for --pay');
const signer = privateKeyToAccount(privateKey);
const client = new x402Client();
registerExactEvmScheme(client, { signer, networks: [policy.network] });
const httpClient = new x402HTTPClient(client);
const paymentPayload = await httpClient.createPaymentPayload(paymentRequired);
validateBuyerPaymentPayload(paymentPayload, endpoint, approvedOffer);
const paymentHeaders = httpClient.encodePaymentSignatureHeader(paymentPayload);
const paid = await fetch(endpoint, {
method: "POST",
redirect: 'error', signal: AbortSignal.timeout(15000),
headers: { "content-type": "application/json", ...paymentHeaders },
body
});
const result = await paid.json();
const settlement = httpClient.getPaymentSettleResponse((name) => paid.headers.get(name));
if (!paid.ok || result.ok !== true || settlement.success !== true) throw new Error('Paid delivery or settlement failed; do not automatically retry');
console.log({ status: paid.status, settled: true, requestId: result.meta?.requestId ?? null });