diff --git a/README.md b/README.md index f96e732..e2fc415 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,37 @@ See **[python/README.md](./python/README.md)** for the full guide. --- +## Verified end-to-end + +Live on-chain settlements through `https://facilitator.acedata.cloud`, run from +[`typescript/scripts`](./typescript/scripts) on **2026-04-25**: + +| Network | API endpoint | USDC paid | Settlement tx | +| ---------- | --------------------------- | --------- | ------------- | +| 🟦 Base | `POST /openai/chat/completions` | 0.020568 | [`0xa1697ee4…7c2708`](https://basescan.org/tx/0xa1697ee44d6c8d14c8a26c9a41b507fb718bac85c9153396b3e23b565a7c2708) | +| 🟦 Base | `POST /midjourney/imagine` (turbo) | 0.025708 | [`0x2d161b04…84539b2`](https://basescan.org/tx/0x2d161b04589aad026e6c575509f1867bbeeba6bff6f17064b1a423dd084539b2) | +| 🟨 SKALE | `POST /openai/chat/completions` | 0.020568 | [`0x621b361a…7b12979`](https://elated-tan-skat.explorer.mainnet.skalenodes.com/tx/0x621b361ad78e6bb6f910dba603a4267bca92ca8748894011cced803227b12979) | +| 🟨 SKALE | `POST /midjourney/imagine` (turbo) | 0.025708 | [`0x0e66f646…6b827d3`](https://elated-tan-skat.explorer.mainnet.skalenodes.com/tx/0x0e66f646bdfbf2e29ca8bc3bc19f252aa6109d8cf7aff1ab6836111e56b827d3) | +| 🟪 Solana | `POST /openai/chat/completions` | 0.095215 | [`4fsVAukg…D1Gd3t`](https://solscan.io/tx/4fsVAukgeFpGezcmu84xu4gYm1ANoAzgked8zhV78g2ffFL9AMpNP64Q1QkLoHtxgLuaPXcACBPZiLykwKD1Gd3t) | +| 🟪 Solana | `POST /midjourney/imagine` (turbo) | 0.115215 | [`5G438pwj…WUeBj`](https://solscan.io/tx/5G438pwjGBPjekkZZgHsqKgkV43nxCLoqMoue7J6aHhRVCYhtnR45EE2SzffnsbQMVxceb8BhdZFA3jTECNWUeBj) | + +Reproduce with the live e2e scripts (need a funded test wallet for each chain): + +```bash +cd typescript + +# Base / SKALE — set X402B_BASE_PAYER_PRIVATE_KEY or SKALE_BASE_PRIVATE_KEY +TEST_API_PATH='/openai/chat/completions' \ + TEST_BODY='{"model":"gpt-4o-mini","messages":[{"role":"user","content":"hi"}],"max_tokens":10}' \ + npx tsx scripts/test-real-e2e.ts # Base +npx tsx scripts/test-skale-e2e.ts # SKALE + +# Solana — set X402B_SOLANA_PAYER_PRIVATE_KEY (base58) +npx tsx scripts/test-solana-e2e.ts +``` + +--- + ## Repository layout ``` diff --git a/typescript/scripts/test-real-e2e.ts b/typescript/scripts/test-real-e2e.ts index 3e3a7f7..80c37d0 100644 --- a/typescript/scripts/test-real-e2e.ts +++ b/typescript/scripts/test-real-e2e.ts @@ -29,10 +29,14 @@ function loadEnvFile(envPath: string): void { } const scriptDir = dirname(fileURLToPath(import.meta.url)); -loadEnvFile(resolve(scriptDir, '../../.claude/.env')); -loadEnvFile(resolve(scriptDir, '../../PlatformBackend/.env')); +loadEnvFile(resolve(scriptDir, '../../../.claude/.env')); +loadEnvFile(resolve(scriptDir, '../../../PlatformBackend/.env')); const API_BASE = process.env.API_BASE || 'https://api.acedata.cloud'; +const TEST_API_PATH = process.env.TEST_API_PATH || '/suno/audios'; +const TEST_BODY = process.env.TEST_BODY + ? JSON.parse(process.env.TEST_BODY) + : { prompt: 'a short test beat', make_instrumental: true }; const rawKey = process.env.X402B_BASE_PAYER_PRIVATE_KEY?.trim(); if (!rawKey) { console.error('ERROR: X402B_BASE_PAYER_PRIVATE_KEY is missing. Add it to .claude/.env or PlatformBackend/.env.'); @@ -69,11 +73,11 @@ async function main() { console.log(`Payer wallet: ${wallet.address}\n`); // Step 1: Request without auth → 402 - console.log('--- Step 1: POST /suno/audios without auth ---'); - const res1 = await fetch(`${API_BASE}/suno/audios`, { + console.log(`--- Step 1: POST ${TEST_API_PATH} without auth ---`); + const res1 = await fetch(`${API_BASE}${TEST_API_PATH}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ prompt: 'a short test beat', make_instrumental: true }), + body: JSON.stringify(TEST_BODY), }); console.log(`Status: ${res1.status}`); if (res1.status !== 402) { @@ -143,14 +147,14 @@ async function main() { console.log(` X-Payment header length: ${xPayment.length} chars\n`); // Step 4: Retry with X-Payment header - console.log('--- Step 3: Retry POST /suno/audios with X-Payment ---'); - const res2 = await fetch(`${API_BASE}/suno/audios`, { + console.log(`--- Step 3: Retry POST ${TEST_API_PATH} with X-Payment ---`); + const res2 = await fetch(`${API_BASE}${TEST_API_PATH}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Payment': xPayment, }, - body: JSON.stringify({ prompt: 'a short test beat', make_instrumental: true }), + body: JSON.stringify(TEST_BODY), }); console.log(`Status: ${res2.status}`); diff --git a/typescript/scripts/test-skale-e2e.ts b/typescript/scripts/test-skale-e2e.ts index f3f5c62..a27933d 100644 --- a/typescript/scripts/test-skale-e2e.ts +++ b/typescript/scripts/test-skale-e2e.ts @@ -81,10 +81,14 @@ async function parseBody(res: Response): Promise { async function main() { const scriptDir = dirname(fileURLToPath(import.meta.url)); - loadEnvFile(resolve(scriptDir, '../../.claude/.env')); + loadEnvFile(resolve(scriptDir, '../../../.claude/.env')); + loadEnvFile(resolve(scriptDir, '../../../PlatformBackend/.env')); const apiBase = process.env.API_BASE || 'https://api.acedata.cloud'; - const prompt = process.env.SUNO_PROMPT || 'a short SKALE test beat'; + const testApiPath = process.env.TEST_API_PATH || '/suno/audios'; + const testBody = process.env.TEST_BODY + ? JSON.parse(process.env.TEST_BODY) + : { prompt: process.env.SUNO_PROMPT || 'a short SKALE test beat', make_instrumental: true }; const wallet = new Wallet(getPrivateKey()); console.log('=== SKALE X402 Real E2E Test ==='); @@ -92,11 +96,11 @@ async function main() { console.log(`Payer wallet: ${wallet.address}`); console.log(''); - console.log('--- Step 1: Request without auth ---'); - const res1 = await fetch(`${apiBase}/suno/audios`, { + console.log(`--- Step 1: Request ${testApiPath} without auth ---`); + const res1 = await fetch(`${apiBase}${testApiPath}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ prompt, make_instrumental: true }), + body: JSON.stringify(testBody), }); const body1 = await parseBody(res1) as { accepts?: PaymentRequirement[] } | string | null; @@ -174,14 +178,14 @@ async function main() { console.log(` header length: ${xPayment.length}`); console.log(''); - console.log('--- Step 3: Retry with X-Payment ---'); - const res2 = await fetch(`${apiBase}/suno/audios`, { + console.log(`--- Step 3: Retry ${testApiPath} with X-Payment ---`); + const res2 = await fetch(`${apiBase}${testApiPath}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Payment': xPayment, }, - body: JSON.stringify({ prompt, make_instrumental: true }), + body: JSON.stringify(testBody), }); const body2 = await parseBody(res2); diff --git a/typescript/scripts/test-solana-e2e.ts b/typescript/scripts/test-solana-e2e.ts index 65f84a8..30c10db 100644 --- a/typescript/scripts/test-solana-e2e.ts +++ b/typescript/scripts/test-solana-e2e.ts @@ -42,8 +42,8 @@ function loadEnvFile(envPath: string): void { } const scriptDir = dirname(fileURLToPath(import.meta.url)); -loadEnvFile(resolve(scriptDir, '../../.claude/.env')); -loadEnvFile(resolve(scriptDir, '../../PlatformBackend/.env')); +loadEnvFile(resolve(scriptDir, '../../../.claude/.env')); +loadEnvFile(resolve(scriptDir, '../../../PlatformBackend/.env')); const API_BASE = process.env.API_BASE || 'https://api.acedata.cloud'; const SOLANA_RPC = process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com'; @@ -57,12 +57,14 @@ if (!PAYER_PRIVATE_KEY) { } // Use a cheap API for testing (openai chat completion is fast + cheap) -const TEST_API_PATH = '/openai/chat/completions'; -const TEST_BODY = { - model: 'gpt-4o-mini', - messages: [{ role: 'user', content: 'Say hi in 3 words' }], - max_tokens: 10, -}; +const TEST_API_PATH = process.env.TEST_API_PATH || '/openai/chat/completions'; +const TEST_BODY = process.env.TEST_BODY + ? JSON.parse(process.env.TEST_BODY) + : { + model: 'gpt-4o-mini', + messages: [{ role: 'user', content: 'Say hi in 3 words' }], + max_tokens: 10, + }; interface PaymentRequirement { scheme: string;