// Un facilitator x402 v2 LOCAL, pentru probe fara retea (2026-09-29): aceleasi verificari ca facilitatorul de pe testnetul 28001 // (x402/facilitator/facilitator.mjs din depozitul de dezvoltare: forma, reteaua, activul, payTo, suma, fereastra, semnatura EIP-712 // recuperata, nonce nefolosit, soldul), dar soldurile si nonce-urile folosite stau in memorie, iar /settle le muta in memorie in loc // sa trimita o tranzactie. Nu e un facilitator de folosit in productie: nu atinge niciun lant. import http from 'node:http'; import { incarcaEthers, EIP3009_TYPES } from './wallet.mjs'; export function createLocalFacilitator({ network, token, balances = {} }) { const ethers = incarcaEthers(); const domeniu = { name: token.name, version: token.version, chainId: Number(network.split(':')[1]), verifyingContract: ethers.getAddress(token.address) }; const sold = new Map(Object.entries(balances).map(([a, v]) => [a.toLowerCase(), BigInt(v)])); const folosite = new Set(); function verifica(body) { const pp = body && body.paymentPayload, pr = body && body.paymentRequirements; if (!pp || !pr || body.x402Version !== 2 || pp.x402Version !== 2) return { ok: false, reason: 'invalid_payload' }; if (pr.scheme !== 'exact' || pr.network !== network) return { ok: false, reason: 'unsupported_network' }; if (String(pr.asset).toLowerCase() !== token.address.toLowerCase()) return { ok: false, reason: 'unsupported_asset' }; const a = pp.payload && pp.payload.authorization, sig = pp.payload && pp.payload.signature; if (!a || !sig) return { ok: false, reason: 'invalid_payload' }; if (String(a.to).toLowerCase() !== String(pr.payTo).toLowerCase()) return { ok: false, reason: 'invalid_payment_requirements', payer: a.from }; if (BigInt(a.value) < BigInt(pr.amount)) return { ok: false, reason: 'insufficient_amount', payer: a.from }; const acum = BigInt(Math.floor(Date.now() / 1000)); if (acum <= BigInt(a.validAfter)) return { ok: false, reason: 'authorization_not_yet_valid', payer: a.from }; if (acum + 6n >= BigInt(a.validBefore)) return { ok: false, reason: 'authorization_expired', payer: a.from }; let semnatar = null; try { semnatar = ethers.verifyTypedData(domeniu, EIP3009_TYPES, { ...a, value: BigInt(a.value), validAfter: BigInt(a.validAfter), validBefore: BigInt(a.validBefore) }, sig); } catch { semnatar = null; } if (!semnatar || semnatar.toLowerCase() !== String(a.from).toLowerCase()) return { ok: false, reason: 'invalid_signature', payer: a.from }; if (folosite.has(`${a.from.toLowerCase()}:${a.nonce}`)) return { ok: false, reason: 'nonce_already_used', payer: a.from }; if ((sold.get(a.from.toLowerCase()) || 0n) < BigInt(a.value)) return { ok: false, reason: 'insufficient_funds', payer: a.from }; return { ok: true, payer: a.from, a }; } let n = 0; const srv = http.createServer((req, res) => { let s = ''; req.on('data', (x) => { s += x; }); req.on('end', () => { const trimite = (o) => { const b = JSON.stringify(o); res.writeHead(200, { 'content-type': 'application/json' }); res.end(b); }; let body; try { body = JSON.parse(s || 'null'); } catch { body = null; } const url = (req.url || '').split('?')[0]; const v = verifica(body); if (url === '/verify') return trimite(v.ok ? { isValid: true, payer: v.payer } : { isValid: false, invalidReason: v.reason, payer: v.payer || '' }); if (url === '/settle') { if (!v.ok) return trimite({ success: false, errorReason: v.reason, payer: v.payer || '', transaction: '', network }); const a = v.a; folosite.add(`${a.from.toLowerCase()}:${a.nonce}`); sold.set(a.from.toLowerCase(), sold.get(a.from.toLowerCase()) - BigInt(a.value)); sold.set(a.to.toLowerCase(), (sold.get(a.to.toLowerCase()) || 0n) + BigInt(a.value)); return trimite({ success: true, payer: a.from, transaction: '0x' + (++n).toString(16).padStart(64, '0'), network }); } res.writeHead(404); res.end(); }); }); return { server: srv, balanceOf: (a) => sold.get(String(a).toLowerCase()) || 0n, used: folosite, fund: (a, v) => sold.set(String(a).toLowerCase(), (sold.get(String(a).toLowerCase()) || 0n) + BigInt(v)), listen: () => new Promise((r) => srv.listen(0, '127.0.0.1', () => r(`http://127.0.0.1:${srv.address().port}`))), close: () => srv.close() }; }