diff --git a/bunny_js/README.md b/bunny_js/README.md
index a7463a9b8..a3aa464b1 100644
--- a/bunny_js/README.md
+++ b/bunny_js/README.md
@@ -11,9 +11,13 @@ Four Bunny Edge Scripts, each deployed as **one bundled file**:
All four are live. Three of them are **sellers** — unpaid requests get the honest `402` +
`Payment-Required` challenge; retry with the payment attached settles and returns the
-result. The channel state lives in the shared S3 bucket, claimed by `scw_js`'s
-`llmx402cron` (the edge has no cron), which is also why `x402_facilitator/` keeps only
-`walletreportcron`.
+result. The sellers' 402 lists **two schemes**: batch-settlement channel entries first
+(the preferred way to pay), plus an appended **exact alternative** — one EIP-3009 signature
+per call, flat $0.01 USDC on Base mainnet — for stock clients without channel wiring; the
+flat price is decoupled from the channel prices (see `withExactAlternative` in
+[`src/x402_server.ts`](./src/x402_server.ts) for the fee rationale). The channel state lives
+in the shared S3 bucket, claimed by `scw_js`'s `llmx402cron` (the edge has no cron), which is
+also why `x402_facilitator/` keeps only `walletreportcron`.
The sellers are ERC-8004-registered agents on Base. Every origin serves:
diff --git a/bunny_js/src/discovery.ts b/bunny_js/src/discovery.ts
index 4b488eb95..9197c4eae 100644
--- a/bunny_js/src/discovery.ts
+++ b/bunny_js/src/discovery.ts
@@ -40,7 +40,7 @@ export function buildRootPage(input: RootPageInput): string {
-
+
${escapeHtml(input.title)}
diff --git a/bunny_js/src/favicon.ts b/bunny_js/src/favicon.ts
index 6693e3ff5..922b31605 100644
--- a/bunny_js/src/favicon.ts
+++ b/bunny_js/src/favicon.ts
@@ -1,3 +1,3 @@
export const faviconBase64 =
- "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAQABAADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDLopTSV9EeSJS0UUxBSUtFABRRRQAUUUUAJRS4ooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACkpaKAEopaKACilpKACiiigAoopcUAJRRRQAUUUUAFJS0UAFFFFACUUtFABRRRQAlFLRQAUUUUAFJS0UAFFFFABRRRQAlLRRQAUUUUAFFFFABRRRQAlFLRQAUUUUAFFFFABRRRQAlFLRQAUUUUAJS0lLQAUUUUAFFFFABRRRQAUUUUAFJS0UAJRS0UAFFFFACUtFFACUUtFACUtFFABRRRQAUUUUAJRS0UAJS0UUAFFFFABRRRQAUUUUAFJS0UAJRS0UAJRS0UAFFFKKQAaSlNFACUUUUwCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKWgBKKWkoAKKKKACiiigAooooAWkoooAKKKWgBKKKKAFpKWg0AJRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRS0AJRRRQAUUUtACUUGigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKWiigAooopABpKKKYBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFGaKKAFpKKKACiiigAooooAKKKWgApKWigBKKKKACilooAKQ0tJQAUUUUAFFFFABRRRQAUUUUAFFFLQAlFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFLQAlLRRQAlFFFABRS0lABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUtJQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUooAKSlNFIBKKKKYBRRRQAUUUUAFFFFABRRRQAUUUtABSUtJQAUUUUAFFFFABRRRQAUUUUAFFFFABRRS0AJRRRQAUUtJQAUUUtABRRRQAUUUUAFJS0lABRRS0AJS0UUAJRRRigAopaMUAJRS0UAJRRRQAUUUUAFFFFAAKWiigBKKKWgBKKKKACiiigAoopcUAFJS0lABRRS0AJRS0lABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAAKWikoAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigApaSloAKKKKQCUUUUwCiiigAooooAKKKKAClpKWgBKKKKACiiigAooooAKKKKACiiloASilpKACilooASilpDQAUUUUAFBopaAEopaKAEopaKAEopaSgBaSlooAKSlpKACiiigApaSloAKKSloAM0UlLQAUlLSUAFLSUtABSUtJQAUUUUAFFFFABRRRQMKKKKAFooooEFJS0lAC0UUUAFJS0lABRRRQAUtJRQAUUUUALSUtIaACiiigApaKKACkpaKAEooooAWikooAKKKKACiiigAooooAKKKKAClpKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKWkooAKKKKACiiigAooooAKKKWgBKWilFACUUUUgEooopgFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUtJRQAtJS0lABRRRQAZooooAKKKKACiiigApaSigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACilooASloooAKSlpKACiiigAooooAKKWigBKKWkoAWikpaACkpaSgAooooAKKKKACiiigAooooAKWkpaACkpaKAEooooGFLSUUCCiiigAooooAKKWigBKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooASloooAKKKKACiiigBaKSloAKKKKQCUUUUwCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKBhRRRQIKKWkoAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAopaKAEooooAWkoooAWkpaMUAJRS0UAJRRRQAtFJS0AFFFFACUUUUAFFLRQAlFFFABRRRQAUUUUAFFFFABS0lFAC0lLRQAlFFFABRRRQAUUUYoAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKWkNABRRRQAUUUUAFFFFABRRRQAUtJSigBKWiikAlFFFMAooooAKKKKACiiigAooooAKWkpaACiikoAWkpaSgAopcUlABS0lLQAlFFFABRRRQAUUUUAFFGKKACiiigAooooAKKKKACilooASloooASilooASiiigAoopaACiiigAopKKACilpKACiiigYtFJS0hBSGlooASiilpgJRRRQAUUUUAFFFFAC0UUlAC0lLSUAFFFFABRRRQAUUUUAFFFFAC0lFFABRRRQAUUUtACUtJRQAtJS0lABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUALSUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABS0lKKACiiikAlFLSUwCiiigAooooAKKKKACiiigAooooAKKKKACiiigBaKKKAEopaSgAooooAKKKKACiiigBaSiigAooooAKKKKACiiloAKKKKACiiigAooooASiiigAopaKAEoopaAEopaSgBaSiigAooooGFLSUtAgpKKKACjNFFABRRRQAUUUUAFFFFAC0GiigBKKKKACiiigAooooAKKWjFACUUUUAFFFFABRRRQMKKKKBBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRS0AFFFFIAopKKYBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAC0lFLQAlFLSUALmkoooAKKKKACiiigAooooAKWkooAWkpaKAEopaSgAooooAWiiigAooooAKKKKAEooooAWiiigBKWkooAWkpaSgAooooAKKWigBKKWkNABRRRQAUUUYoAKKWigBKWkpaACkoooAWkoooAKKKKACiiigAooooAWikooAWkpaSgAopaSgAooooAKKWkoAKKKKACiiigAoopaACkpaSgAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAClpKUUAFFFFIBKKKKYBRRRQAopKWkoAKKKKACiiigAooooAKKKKAClpKKACiiigAooooAKKKKACiiigAooooAKKKKAFopKXNABSUtJQAUUUUAFFFFABRRRQAUUUUAFFLSUAFFFFABRRRQAUUUUAFLRRQAUUUlAC0lLRQAlLRRQAUUUGgBKKKWgBKKKKACiiigYUUuKSgQUUUUAFFFFABRRRQAUUUUAFFBooAWkpaQ0AFFFFABRRRQAUUUUAFFFFABRRRQAUUtJQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAwpwpKKBBRRRSASiiimAUUUUAFFFFABRRRQAUUUUAFLSUtABSUUUAFFLRQAlFFFABRRRQAUUUUAFFFFABRRS0AFFFFACUUtJQAtJS0lABRRRQAUUUUALRSUtACUUUUAFFFFABRS0lABRRS0AJRRRQAtFFFAAaSlpKAFooooASilpKACiiloASilpKACilpKBhRRRQIWkoooAKWkooAKKKKAFopKWgAoopDQAUUUUALRSUtACUUtJQAUUUUDCiiigQUtFFABSUtJQAtJRRQAUUUUAFFLSUAFFFFABRRRQAUUUUAFFFFABRRS0AJRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUtJS0AFFFFIBKKWkpgFFFFABRRRQAUUtJQAUUUUAFFFFABRRS0AFFFJQAUUUUAFLSUUAFFFFABRS0UAJRS0hoAWikpaAEooooAUUUUUAFJS0UAJRSmkoAKKKKACiiigYUUUtAgooooAKKKKACiiigAooooAKSlooAKSlooASilpKACiiloASiiigBaSiigAopaKAEooooAKKKKAFpKWkoAKKKKAFpKKKAFoNFFACUtJRQAtJS0UAJRS0mKACilooASiiigBaSiigAooooAKKKKAFpDRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUtJS0AFFFFIBKKKWmAlFFFABRRRQAUUUtACUUUUAFFLSUAFLSUUALSUtJQAUUUUAFFFFABRRRQAUUUUAGaKKKAClpKKACiiigAopaKACiikoAKKKWgAooooAKKKKACiikNAC0UlLQAUUlFAC0lLRQAUUUUAFFFJQAtFFFABSUUUALRSZpc0AJRRRQAUtAooAKKKKACkpaQ0ALSUUUAFFFFABRRRQAUUUtABRRSUAFFFFABRRRQAtFJS0AIaKKKACiiloASiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigApaKKACiiikAUlFFMAooooAKKKKACilooASiiigAooooAWiiigAoNFFACUUUUAFFFFABQaKWgBKKKKACiiigAooooAKWkooAKKKKAFpKKKACiiigBaSlooAKQ0tJQAUUUUALSUUtACUUUUALRRRQAUlLRQAUlFFAC0lLRQAlFFFABRRRQAUUUUAFFFFAC0UUUAJRRRQAUUUUAFFFFABRRRQAUtJS0AFJS0UAJRS0lABRRRQAUtFIaACiiigAooooAKKKKACiiigApaSloASiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAFpKKKACiiigAooooAKKKKACiiigAooooAKWkooGLRRRQIKKKKQCUUUUwCiiigAooooAKKKKACiiigAoopaACikpaACg0UlABRRSigBKKWigBKKWkoAKKKKACiiigAooooAKKKKACiiigAooooAKKKWgAooooAKKKKAEopaSgAooooAKKKKACiiigBaKKKAEopaSgBRRSUUAFFLSUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFLRQAlFFFABRRRQMWkpaKBBSUtJQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUALSUUUAFFFFABRS0UAJRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUtJS0AKaSlNJSAKDRRTASiiigAooooAKKBRQAUUUUAFLRRQAUlLSUAFFFFABS0lFAC0UlFAC0lLQaAEooooAKKKKACiiigAooooAKKKKACiiloASlopKAClpKKAFopKKAFpKKWgBBS0UUAJRRRQAUUUUALRRRQAUlLSUAFFFFABRRRQAUUlFADiCMZBGelJV/UohFbaaQPv2oY/99tVCkndXGFFFFMQUUUUAFFFFABRRRQAtJRRQAUUUUALSUUtACUuaSigBaSiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKWigBKWkpaAA0UUUgCiiimAlFFLQAlFFFAC0lFFABRRRQAUUUUAFFFFABRRS0AFJS0lABRRRQAtFJS0AJRRS0AJRRRQAtJRRQAUUUUAFFFFABS0lLQAUlLRQAlFFFABRRS0AJS0UUAFFFJQAUUUUAFFFFAC0lFFABRRRQAUUUUAFFFSRxGVtoZAf9tgo/M0AR0YrSh0SefBF1YID/fu0H9a2NO8ET3s6q2q6YoPa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iigAooooAKKKKAFpKKKACiiigAooooAWkoooAKKKKACiiigBaSiloASiiigAooooAKKKKACiiigAooooAKKKKACiiigAopaSgAooooAKKKKACiiigAooooAKKKKACiiigAooooAKWkpaACkpaKQCUUUUwCiiigAooooAKKKKACilpKACiiigAooooAKKWigBKWkpaACg0UlABRRS0AJRS0lABRRRQAUUtFABSUtFACUUUtABSUUUDCiiigQUUUUALQaSigApaSjNABRRRQAUUUUAFFFFABRRRQAUUUUAFKBSYq3p9jNqV7HawDLuevYDuTSbsrsaVzT8NaEdYvN0gItYiDIf7x7KK9PjRY41RFCqowAOgFVtO0+HTLKO1gGEQcnux7k1YmlSCF5ZGCogLMx6AV5dWo6kjrhHlRT1bVIdJsXuZiMjhEzyzdhXlF9ezahdyXM7bpHOT7ew9qu+INak1rUDJysCfLEnoPU+5rJrtoUeRXe5z1J8zCiiiugzCiiigAooooAKKKKAOq8Ia/8AYbgWNy3+jyn5WJ+43+B/z3r0QHNeIivQ/CGv/bIBY3T5njHyMTy6/wCI/wA964cTRt7yOilPozT8Q6JHrNkVAC3CZMT+/ofY15ZLG8MjRyIUdSQynqD6V7XXI+MdBFxC2pWy/vYx+9UfxL6/UfyqcPWs+Vjqwvqjz+kzSmkr0DmFooooAKKKKACiiigAooooAKKKKACiiigAoopaAEopaSgAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigApaKKAEooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAFoopRQAlJS0lIYUUUUxBRRRQAUUtFACUUUUAFFFFABRRiloAKSiigBaKSloAKSlooASiiigAooooAWiiigBKKKKAFoopKAFoNFFACUtFFACUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUoFADkVnYKoJYnAA716d4a0JdIs98oBupRmQ/3f9kVj+DvD+Nup3Sf9cFI/8e/wrtOlefia13yo6KUOrFzXn/jHxAbqZtOtX/cRn96wP329PoP51t+Lde/s6zNrbvi6mGMjqi9z9fSvN8VWGo/bYVZ/ZQlFLRXcc4lFLilEbnorH8KLgNop/kyf883/AO+TS+RL/wA83/75NK4WI6KkMMg/5Zv/AN8mkMbjqjflRcLDKKUjFJTAKkhmkt5klicpIhDKwPINRUtFrgereHtaTWbDecLcR8SoPX1Hsa1yAwINeQaTqc+k3yXMB5HDKejDuDXrFjeRX9nFdQHMcgyPUex968yvS5HdbHXTlzKx574r0A6ddfarePFpKeg/gb0+lc3ivaLu1hvLWS3nQPFIMMK8p1rSZdH1B7d8sh5jfH3lrpw9bmXK9zGpDld0ZtFFLiuoyEooooAKKKKACiiigAooooAKKKKACiiigAopaSgAopaDQAlFFFAC0lFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFLSGgAooooAKKKKACiiigAooooAKKKKACiiigApaSloAKKKKQCUUGimAUUUUALRSUtACUUUUAFFFFAC0UlLQAlFFFAC0lFFABRRRQAUUUtACUUUUAFFFFABRRRQAUUtJQAUUUUAFFFFABRRS0AJRS0UAJRRilxQAlFFGKAClpMUuKACkpwVmOAM1Zi0y+n/ANVZzv8A7sZNJtIdmVKXFbEPhXWpuli6D1chf51oQ+BtUfmR7eP6uT/IVDqwXUahLscvijFdtD4AY/66/UeyR5/mavxeBNOTHmTXEn4hf6Vm8TTXUr2UjzrFLivUovCejRf8uYc+rux/rV2HRtNt/wDVWNup9fLBP61DxceiKVFnkUcMkpxGjOfRRmui8O+Gbi9vlku4JIrWP5m3qRv9hXo6oqDCqqj0UYpwrKeLbVki40UtwVQqhVAAAwAOgqnqF4tnbM/BkIIRT3P+FWpZVijZ3OFUZJrk728e7nMjZC9FX0FYQg5O5pJ2Vjn59Jub26e4uroNJIcsQuafHoFuv35JG/ECtTNIXVRlmAHvXYpPZGPKimukWSf8sc/ViamSytk+7BGP+AimyahaRnDTpn0Bz/Kq0mt2qfd3v9Fx/On77D3UaAjReiKPoKXGKx28QJ/DbsfqwFRN4gk/ht1H1YmjkkHNE3aK57+37jP+qi/Wj+37n/nnF+R/xp+zkLnR0X40Vzv9vXP/ADzi/WnL4gmH3oIz9CaPZyDnRvFVPUA/hTGtoH+9DGfqorIXxD/etvyb/wCtUya/bn70Ui/TBpckw5olxtNs3626fgMfyqu+i2bdEZfo3+NPTWLJ/wDlqV/3lNWUu7eT7k8bfRhR7yH7rMt/D8Z/1c7L/vDNbXhr7To1w8UsyyWkpyRyCh9f8aaGB6U8Gpm3JWY0kndHajBFZPiDRU1nT2iGFnT5onPY+n0NM0e/zi2kP+4T/KtquLWEro2+JHj02kajbE+bZXCgcZMZx+dVSpU4IIPvXteBTHghl/1kUb/7yg11LGd0YuieKYpcV65NoGkz/f0+3+qpt/lVGXwdo0mcQPH/ALkh/rmtFi4dSXRZ5hRXoE3gKybPk3Uyf7wDf4VQl8AXI/1N9E3s6Ef41osRTfUn2UjjqK6GbwTrMediQyj/AGZMfzxVKXw3rMWd2nzH/dAb+VWqsH1JcGjKpasS2F5B/rrSeP8A3oyKgIq00xWG5opcUYpiEooooAKKKKAFpKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAWkoooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigBaKKBQAUUUUgCkpaSmAUUUUAFFFFABRRRQAUUUUAFFFLQAlFFFABRRRQAUUUUALRRQaAEoopaAEooooAKKKMUAFFLSUAFFFFABRRRQAUtJS0AFFFFACir2n6Te6pLstIGfH3m6Kv1NWvDuiSaze4OVt4sGRx/Ie5r1C2tobSBYYI1jjUYCqK5q1dQ0W5rCnzanEwfD+4YA3F7Gh9EQt+pxV6LwDZqR5t5M/sqhf8a66jNcbxNR9TdUonPReC9GjOWilk/wB+Q/0xV6Lw7o8P3NPhz/tDd/OtPNNZ0QZdlUepOKh1Kj6lcsURx2lvCMRQRIP9lAKlwKpTa1pcH+tv7ZcdvMBqhN4v0WIcXRkPpGhP9KFCpLoHNFG7ikNcnN4+slz5VrO/uxC/41Qm+IE5/wBTYxr7u5P8sVaw9R9CfaxR3VLmvNZfG2ryE7XhjHosef55qlN4k1efIa/lAPZML/LFaLCT6k+2ieqlgOScD3qvJqFnDnzLuBMf3pAK8ilu7iY/vZ5H/wB5yan06yN5Pkj5F5Y/0qvqiW7J9s3sj1D+2rAruWcOp6FQTmon162X7qu36VzYAVQAMAVm6te/ZofLQ/vXHHsPWkqEWynUaRb13xc88v2e3iURofmJOcmsGTWr1+kir/uqKoIjyuFRSzE4AAySa63SPh9ql8Flu8WcJ5w4y5H+72/Gul+zpLUyXPN6HMteXUn3p5D/AMCx/KkjguLpsRxyTMeyqWNevad4H0aw2s0BuJB/FOd36dK6CG3igUJFEiKOyqAK5p42K+FGscNJ/EzxKLwvrswyml3OP9pNv86n/wCEO8QYz/Zc35r/AI17ZtHpRgelZfXpdjT6tHueFS+GtagyZNLugB3EZb+VZ8tvLC22WN0b0ZSDX0IVHpUctvDOm2aGORfR1BH61Ucc+qJeFXRnzyRzRivZdQ8D6HfZItfs7n+KA7f06fpXHaz8Pb6xjeaxkF3EvO0DDgfTv+H5V008XTlpsYyoSicUaKDkHB60V1GIlLRRQAUZoooAek8sf3JGX6MRVhNUvE6Tsfrg1TopWQ7s1YtfvI2BOxseox/Kuz0nxYt5bK0sGJF4cKe/rXm9WbK7ezuBIvI6MvqKyqUYyWxcajR6qmt2jfeLp9V/wp41vTd2w3sKt6O20/rXKwypPEskZyrDINVdTsheQZUfvV5U+vtXJ7GN7G/O7HfRXEE4zFNHIP8AZYGpa8TbdGxxkMPwqxFqmoQf6q+uU+krY/nVPB9mR7buj2SlrymHxZrUP/L4XH+2in+laEPj3Uo8eZDbyj/dIP6GoeEmtilWiejUmBXEx/EEcebp/wD3xJ/iKvw+O9Lkx5kdxGfdQf5Gs3QqLoUqkWdPioJbG1nH722hk/3kBrNh8V6LMcC9VD6OpX+laEOp2Fx/qbyB/wDdkFRyziVeLKc3hrR5s77GMf7mV/kaozeCNIk+4s0Z/wBmTP8APNdGCGGRyPalqlVmuouSLOOk+H9sc+Xeyr/vID/hWRqHgrULSMyW7LcqOSFGG/LvXpFFXHEzT3JdKLPEGUqxVgQQcEHtSYr0vxN4Zi1OF7q2QLeKM8ceZ7H39682IKsVYEEHBB7V30qqqK5zzg4sbRRRWpAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFKKKAEooooAKKKKACiiigAooooAKKKKACiiigBaBSUtACUUUtACUUUUAFFFFABRRRQAUUUtABRRRQAUUUUAFJS0UAFJS0UAJRS0lAC0lApaAEopaSgAoopaACiiigAooooAKSlooASilpKACiiloAKWkpc0mB6v4asFsNCtkxh5FEr/AFbn+WB+Fa/So7fabeLb93YMfTFVtYMq6JfGHPmiByuOucHpXjyvKep3LSJy2t+N2huXttNRGCHa0z8gn2H9awZfF2tyD/j82/7kaj+lYQpa9ONGCWxyOpJl+XXdVn/1moXLD08wgfpVKSaSU5kdnP8AtMTTKK0UUtkTdiiiiiqEFFFFACilptKKAHxRPNIscYyzHAFdZZ2q2lusa8nqT6mqWkWHkR+fIP3jjgegrSkkWONnY4VRkmuepK+iNYRtqyG9uktLdpXPToPU+lY+l6TqHiTUCkCEknLyH7qD3NSadYXfizW1ihUrCvVj0RfU+9ex6XpdtpNilpaptRRyT1Y+p9656tZUlZbmkKbqO/QztA8J6foaK6J511jmZxz+A7Vv4opa86c3J3Z2KKirIMUUUVJQUUUUAFJVDUda07Sk3Xt3FD6KTlj9AOa43VPibCm5NMtDIe0kxwP++Rz+orWFGc/hREqkY7s789ay9W13TdJt3e7uo1YDiMEF2PsK8j1HxVrOpkia9dUP8ER2L+nX8axySSSeprshgXvJnPLEroiS7n+1Xs9xtC+bIz7R2yc1DRRXopWVjjeoUUUUwCiiigAooooASiiloA1NHvzbzCGQ/unPBP8ACa6XqK4aui0fUDNH5Mh+dBwfUVhUh1RpCXQr63YlT9pjHB++B/OsWu1cK6FWAKkYIPeuV1CzNpcFRny25U+1VTlfRhONinRRS1qZiUUYpaAEozS0lAE0d1cQnMc8iH/ZcirsXiHV4cbNQnx/tPu/nWZRUuEXuhptHQQ+NNZiILTJKB2eMc/liu60HXoNctS6L5c8eBJETnHuPUV5NXSeBfN/4SD5M7PJbf8ATjH64rnr0YcraNadR3sel15r4109bPWfOjGEuF38f3uh/wAfxr0uuI+IZG3T/wC9+8/9lrlwzaqI1qq8ThqKKK9Q5AooooAWkpaDQAlFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAtFFFABSUtJQAUUUUAFFFFABRRRQAUUUUAFFFFABS0lLQAlFLRSASiiimAUUUUAFFFFABRRRQAtFJRmgBaKKDQAlFFFABRRRQAtFJS0AJRmiigAooooAKKKKAFzSUUtACUZpaKACiiigBKKKKAClpKWgAooooA9T8Kaj/aGhxbj+8hHlP+HQ/littgCpB5BFeZeENT/s/V1jc4huMRt7Hsfz4/GvTu1eVXhyTOunLmieRa/ph0vWJoAuIyd8f+6en+H4Vm16P4z0v7Xpv2uNcy2/J907/l1/OvOa76M+eJzzjysbRQaK2ICiiigAoopaADFaekWH2iYTSD90h/M1Rt4HuZ0iTqx/KuughS3gSJB8qjFZ1JW0LhG7HnpWNqE0t7eRadagu7sF2r1LHoKtapfC0tyFP71+FHp71u/DnQcs+tXC5PKQZ/8AHm/p+dc05KEXJmqXNLlR2Xh/Q4dC0uO2jCmQjMrgfeb/AA9K1qSlrypScndnckkrIKKKKQxKM1DdXcFnbvPcypFEgyzOcAV5v4i+IU0++20jMUfQ3DD5m+g7fz+la0qMqjtFETqRgtTtdZ8TaZoi/wClTgy4yIU5c/h2/GvP9Z+IWpX+6OyH2OE8ZU5c/j2/D86495HkdndyzsclmOST7mm5r06WEhDV6s4p15S2HSyyTSGSR2d25LMck0yiiuu1jAKKKKACiiigAooooAKKKKACiiigAooooAKfFI0MiyIcMpyDTKKQHW2V0t3Arjg9GHoaL60W7tyh+8OVPoa52wvDaThuSh4YV1SMHUMpBBGQa55LlZsnzI4x0aNyrDDA4INNrf1qyDRm5QcqPnHqPWsA1vGV0ZtWYUUlLVEhRRRQAUUUooAMV6N4J0v7HpZvHX97c8jPZB0/Pr+VcRo+nnU9TgtRnazfMR2Ucn9K9cjRY41RFCqoAUDsBXFip2XKb0Y3dx5NeY+MdSXUNaaOM5ith5QPq38R/Pj8K7nxDqf9laPNOpHmt8kX+8e/4da8mJJOTyfepwkNeZjrS6CUUUV3nOFFFFABRRRQAUUUUAFFFFABRS0lABRRRQAUUUUAFFFFABRRRQAUUUUALSUUUAFFLSUAFFFLQAlFFFABRRRQAUUUUAFFFFABS0lLQAUUUUgEooNFMAooooAWkoooAKKKKACiiigAooooAKKKKACiiloASilooASiiigYUUtFAhKKWigBKKWkoAWikooAKWiigBKKWkoAKKKKACiiigBwYjkcH2r1nw9qf9q6PDcMcygbJP8AeH+PX8a8lrp/BOqfZNTazkbEVz93PZx0/Pp+Vc2JhzRuuhrSlZnosiLIjI4BVgQQe4ryHV9PbTdUntTnajfKfVTyD+Vewdq4/wAc6b5trHfovzR/I/8Aunofz/nXLhp8srPqa1Y3VzgKKKK9M5QooooAKWirVhbfartUx8o5b6Um7AjZ0WzEUHnsPnkHHsK0JpFijZ3OFUZNPACqABgCsPW7ss4tkPA5b6+lc6vORvpFFXZLrGpRQoP3kzhEHpk17lY2cdhZQ2sQwkSBF+gFeW/Dyx+0+IjOwyttGXH+8eB/M161XFjJ3ly9jfDR05mOoooriOkKydc1+y0Kzaa5kBkI/dxKfmc+3t71B4k8SWvh+zLyEPcOP3UIPLe59BXjWo6ldapeyXd3IXlc/gB2A9AK68PhnU1lsc9Wty6Lcua54hvdeuvNuX2xj7kKk7U/+v71kk0lLXrRioqyOFtt3YlFFFUIKKKKACiiigAooooAKKKKAClNJRQAUUUUAFFFFABRRRQAVt6Le8/ZnP8Auf4ViU9HaNw6nBByCKmSurDTsztGAZSpAIIwRXJ6hafZLpkA+Q8r9K6Syuhd2ySDg9GHoah1W1FxaMQPnT5h/hWEHyysayV1c5Y0gpaK6TEKKKKAClpKsWdrJe3kVtEMvIwUUm7K4HbeBtN8u3k1CRfml+SP/dHU/n/Kuw7VDaWyWdpFbxDCRqFH4VBq+oLpmmT3bY+RflHqx4A/OvJm3UmdsVyxOF8a6mLvVRaRtmO2GD/vnr/QVzFK7tJI0jnc7EsxPcmkr1IRUYpHJJ3dwoooqyQooooAKKKKACiiigAooooAKKKKACigUtACUUUUAFLSUtABSUtJQAUUUUAFFFFAC0UlFABRRRQAUUUUAFFFFABRRRQAUUUUAFLRRQAUUUUgEooopgFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFLSUUALRRRQAUlLSYoAWkpaSgBaKKKAEooooAKKKKAFooooATNFFFABRRRQAUUUUALTo3aKRZEOGUggjsRTKWgD2HSb9NT0uC7XGXX5h6MOCPzqa8tkvLSW3k5SRSp/GuJ8C6n5dzLp8jfLKN8ef7w6j8R/Ku8ryaseSZ2RfNE8Yu7aSzu5beUYeNipqGuy8daXsmi1KNflfEcmPXsfy4/CuNr0qc+eKZyyVnYSiiitCRa6PRLXyrXzWHzSHP4dqwbaE3FxHEP4jiuwQKiBVGABgCsqktLGkF1I7mdbe3eVuij865J3aR2djlmOSa1teucslup4HzN/SsXNFONlcU3d2PUvhnahNKu7oj5pJdgPso/+vXc1y3w9QL4ThPdpHP64/pXU149d3qM9CkrQQZrI8Q+ILbQdPaeU7pWBEUQPLn/AA9TVzUtRt9LsZbu6cJHGMn1J7AepNeJa5rdxrupSXc/yqfljjzwi9hV4ah7SV3sTWqcqstytqOoXGqX0t3dOXlkOT6D0A9hVSiivZSSVkee3cKKKKYgooooAWikpc0AFJS5pKACiiigAooooAKKKKACiiigAooooAKKKKACiiigDT0a68m58pj8knH0PaujPIrilJByDyK62yuBc2iSdyOfr3rCpHW5rB9DndStvs14wAwjfMtU66PWrYS2nmAfNGc/h3rnK0hK6IkrMKWkpRVkigV2fgXTd002oyLwn7uP69z/AE/GuPijaWRI0Us7EKoHcmvXtMsU07T4bVMfu1wT6nufzrlxM7Rsa0o3dy52rgfHep+bcxadG3yxfPJ/vHoPy/nXb3t3HY2c1zKcJEpY/wCFeO3dw93dS3Ehy8jFj+NYYWF5cz6GtaVlYhooor0TlCiiigAooooAKKKKACiiigAooooAKKKKACiiigAoopaAEooooAKKKKACiilNACUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFLSUooAKKKKQCUUUUwCiiigAooooAKKKKAFopKWgApKWkoAKKWigBKKKKAFpKWigAooooAKSlFFABRSUtABSUtJQAUUUUALRSUtACUUUUAFFFFABRRRQAUUUooAmtbmS0uoriI4eNgy/UV7BZXUd9ZQ3UX3JUDD29q8aruvAup74JdOkPzR/vI8+h6j8/51yYqF48yNqMrOx1GpWKajp09pJjEi4B9D2P515BNC8ErxSKVdGKsD2Ir2mvPPG+m/ZtSW8RcR3A+b2cdfzH9aywtSz5WXWj1OUpRRSgV6BzGxoNvukknI+78q/XvW27BEZieAMmq+nW/wBnsIkIwxG4/U1DrM3lWDqD80hCj+tcz96RstInO3MxuLh5W6sajpKBXRsYnsXw+cP4ThA/hkcH/vquoJABJOAOprzb4e+IbSxhn069nWEM/mRO5wvTBGe3SrXjfxjAbN9M0yZZGlGJpozkKvdQfU/y/TyKlGUqzSR3wqJU0znPGfiQ65qXkwOfsUBIjx0c92/w9vrXMUlFepTgoR5UcUpOTuwooorQkKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAWkNFFABRRRQAtbOgz/NJAe43D+v9Kxans5zbXUcvYHn6VM1dDi7M62RQ6FSMgjBrj54jBM8Z/hYiuyHIB7Guf12DZcJMBw4wfqP8isabs7Gs1pcyaUUlOUFiABkmtzE6nwTpf2nUWvZBmO3+77uen5dfyr0TGKzNA03+zNGggYYkI3yf7x6/wCFaE8yW8Ek0hwkalmPsK8qtNzmdkFyxON8eaptSLTYzycSS49Ow/r+VcLVrUr2TUdRnu5PvSNnHoOw/KqtejShyRSOacrsKKKK1ICiiigAooooAKKKWgBKKKKACiiigAooooAKKKKACiiigAooooAKKWkoAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAoopRQMKKKKQhKKKKYBRRRQAUUUUAFFFGKACilooAKKSloAKKKSgAooooAWiiigBKKKKAClpKWgApKKKACiiigAooooAKKKKACiiloGJRS0UCEooooAKWkooAXNXNMvn07UYLpM5jbJHqO4/KqVKKTV1Yadnc9rhlSeFJY23I6hlPqDWdr+mDVNImgAzIBvjP+0On+H41k+CNT+06c1lIf3lufl91P+B/pXV15Mk6czsTUoniDAqxBGCOxq1p0H2m9jQj5c5b6Ctbxhpn2DWGlRcRXGXGOx7j+v403QLfbHJcEfe+Vf616XOnG6OTl96xs9q53XJvMuViHRB+proZGCIWJwAMmuOnlM07yH+Jiaikru5c3pYhopTSV0GQZooooAKKKKACiiigAooooAKKKWgBKKKKACiiigAooooAKKKKACiiigAooooAKKKKACkoooAWiiigAooooA6vS5/PsIyeoG0/hSarb+fYvgfMnzD8P8ms7QZ8SyQHow3D6/5/lW+QCMGuWXuyN1qjh66Hwhpv2/WFkdcw248xvc9h+f8AKsW9tzbXkkXQA8fSvS/Cel/2do6F1xNP+8f29B+X86qvU5YaEU43kb3auU8b6n9n05bJD+8uDlvZR/if611LsFUknAA5NeSa9qR1XVprgHMYOyMf7I6fn1/GuTDQ5p37G9WVlYzTSUUV6ZyBRRRQAUUUUAFFFFABS0lFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAwooooEFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUCgBaKKKQBSUtJTAKKKKACiiigApaKSgBaKSloASilpKACiiigAooooAWiiigBKKKKACiiigAopaTFABRRRQAUtJRQAUUUtACUtFFABRRRQAlFFFABRRRQAUUUtAGnoWonS9VhuMny87ZB6qev8Aj+FesKwZQwIIIyCK8VFek+D9T+3aSIHbMtt8h917H+n4VxYuGnMjooy6FvxJpY1TS2QD97Gd6f1H5VgRQLbxLEowFGBXbEZFctqUH2e6ZcfKeVrGlN25S5LW5h6zceTYsoPzSfKP61zFXtWuvtN6wU/u4/lX+pqhXoU1ZHNJ3YUUUVZIUUUUAFFFFABRRRQAUtJRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFACUUUUALRRRQAUUUUATWk5trmOUfwnJ+neuxQh1BByCMg1xFdLoV151v5DH54+nutY1Vpc0g+hqQaNFqWp27yLlYzucf3gOg/PFdqOBxWbpFt5Vv5pHzP0+laROBXnVZuTsdMYpanPeMdT+xaM0KNiW4+QY7L3P8AT8a8zrZ8Tan/AGnrErI2YYv3cf0HU/iaxq9ChDkgc1SV2JRRRW5mFFFFABRRRQAUUUUAFFFLQAlFFFABRRRQAUUUUAFFFFABRRRQMKKWkoEFFFFABRRRQAUUUUAFFFFABS0lLQAlFFFABRRRQAUUUUAFFFFABRRS0AFFFFIBKKKKYBRRRQAUUUUAFFFFABS0lFAC0lFFABRRS0AJRS0UAFJS0UAJRRRQAUUUUAFLmkooAKKKKACiiigApaSloAKSlooAKKKKAEooooAKKKKAFpKWkoAUVv8Ag+aePxBCkILLICsg/wBnufwrAr0HwPpnkWL37r+8n+VPZB/if5VhXklB3LppuR1lc/4sXbpDSx/65eFx6d66A1zesT+fcMmcog2j+tefSXvXOqex5tSVb1C2+y3jxgfL1X6VVr1k7o4mJRRRTAKKKKACiiigBaSlpKACiiigAooooAKKKKACiilxQAlFFFABRRRQAUUUUAJRS0UAFdL4X8IT+I1lmacW9tG23ft3Fm9AMj865qux8HeMINAt5bS7hkeB38xWjwSpxg8HtxWNdzUPc3NKai5e8ZnijwxN4cuYgZRNBMD5cm3ByOoIrBrpPGHigeI7qFYImitYM7A/3mJ6k+n0rm6dHn5Fz7inZS90KKKK1ICiiigAq9o5kGqwbMkbvm/3e/6VRrovD9qI42uWHzPwv0/z/KoqP3WVFanpSABAFGFAwPpWd4hlng0G7ktx+8CfkOhP4DJqbS7kT2oB+8nyn+lXZEWSNkdQysMEHuK8n4ZanZujxKitDW9PbTNVntiuFByh9VPT/PtWca9eLTV0cT0YUUUVQgooooAKKKKACiiigAooooAKKKKACiiigAopaSgAooooAKKKKACiiigAooooAKKKKBhRRRQIKWkooAKKKKBhRRRQIKKKKACiiigAoopaACiiigANFFFIBKKU0lMAooooAKKKKACiiigAooooAKKKKAClpKWgAoopKAClpKWgAooooASilpKACiiloASilpKACilpKACilpMUALRRRQAUUUUAJRRRQMKKKKACgUtKBQIt6bYvqOoQ2qdZGwT6Dufyr16CFLeCOGNdqIoVR6AVx/gbTNscuoSLyTsj/qf6fnXZ15uJnzS5TqoxsrlDW9RTS9KmuWxuA2oPVj0/x/CuYhmW4hWUdHGap+OtT+0X0djGf3cAy/ux/wABVXQLjdbNAx5jOR9D/k1pTpWhzEud5WJNctRJbeco+aPr9K5qu2lUSRMjDhgQa4qRGjlZG6qSDXRSeljOohtFFFbGYUUUUAFFFFABRS0lABRRRQAUUUtABSUtFABRRQaAEooooAKKKKACiiigBRQaVFLsFVSzMcAAZJNd1pPw1urq2WbULr7KWGREi7mA9znA/WsqlWNP4mXGDlscHRXba38Or3T4GuLGb7XGoyybcOB7DvXFcjg06dSNRXixShKO4lFFFaEhRS0lABRRQKAJIITPPHEvVmxXZRRiKJUUYVRgCsDQYN9y0x6IOPqf8muhPArnqvWxrBdSWx1dLDVreCRsJP8AKx9PQ/nXZZrxu+ufPvZJAeAcL9BXpvh3Uv7T0eGZjmVfkk+o/wARg1zV6VkpGtOd7oy/G2mfadOW9jX95bn5vdD/AIHH6155ivapolmheJ1DI6lWB7g15Fqlg+m6jNauD8jfKfVex/KtcLO65WRVjrco0UppK7DAKKKKAFFFJRQAUUUUALRRRQAlFFFABS0lLQAUlLSUAFFFFABRRRQAUUUUAFFFFABRRS0AJRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAtFFAoAKKKKQBSUUUwCiiigAooooAKKKKACiiigAooooAKWiigBKKDRQAUUUUALRRRQAUlFFABS0lLQ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JPAAptdr4N0Ddt1S6T5R/qFPf/a/wrOpNQV2VGLk7Gz4Z0IaVaebMo+1Sj5v9kf3f8a36MYrnfFeu/2XYmCB8XcwwuOqL3b/AA/+tXme9VmdekImN4x18TyNplq/7tD++YfxN/d+g/nXGmgkk5PNJXp04KEbI5JS5ncKKKK0JCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKWiigApKKKACiiigAooooAKKKKACiiigApaSigBaSiigAooooAKKKKACiiigAooooAKKKKACiiigAoopaAEooooAKKKKAClFJSigYUUtJSEJRRRTAKKKKACiiigAooooAKWkpaACiiigApKWigBKKKXFABRRRQAUlLRQAlFFFAC0UUUAJRRRQAtFFFABSUtJQAUUUUALRRRQAlLRRQAUUUUAFFFFACGilooASilxSUAFLSUtABSUtJQAUUUUAFFFFABRRRQB1HhDxB9guPsVy+LaU/Kx6I3+B/z3r0YHNeI16F4Q8Qfa4Rp9y37+Mfu2J++o7fUfy/GuHE0ftI6KU/ss19f0WPWbAxkATplon9D6fQ15ZPDJbyvFKhR0OGU9Qa9orlfF+gfbLc39sn+kRj94oH319fqP89qjD1uV8r2HVhfVHnlJQaK9E5gooooAKKKKAClpKu6Zp02qX0drCOWPLdlHcmk3ZXY0rmn4Y0A6veebMp+yRHLn++f7v+NenIqogVQFUDAAHSq9jZQ6fZx20C7Y0GB6k9yfc1NNNHBC8srBY0UszHsBXl1ajqSOuEeVFPWNUg0mwe5mPThF7s3YV5PfX02oXkl1O2ZJDk+g9APar3iHW5Nb1Aycrbx/LEh7D1Puaya7aFLkV3uc9SfMwoooroMwooooAKKKKACiiigAooooAKKWigBKKWigBKKWigBKKKKACiiigApaSloASiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigApRSUooAXNJRRSASig0UwCiiigAooooAKKKKACilooASiiigAooooAKWkpaACiiigApKWigAooooAKSiigApaTFLQAUUUlAC0lLSUAFLSUtABRRRQAlLRRQAUUUUAFFFFABRRRQAUlLRQAUUUUAFFFFACUUtJQAUUUUAFFFFABUkE0lvMk0TlJEIZWHY1HS0PXQD1fQNYj1iwWUYEyfLKnof8DWseRXkWkanNpN8lzFyBw6Z4Yeleq2V7Df2kdzA26Nxke3sfevLr0uSV1sddOfMjgvF3h/7DP9utk/0aQ/Oo/gb/A/57Vy1e1XFvFdW7wTIHjcbWU9xXlOu6PLo9+0LAmJuYn/ALy/4104atzLle5lVhZ3Rl0UUV1mIUopKUUASRxPLIqIpZmOAB1Jr0/w5oaaPZDcAbmQZlb09h7CsjwhoIijXUrlf3jD9ypHQf3v8K7GvPxNa/uo6aULasWuB8Y+IPtMjabav+5Q/vWH8bDt9B/OtrxXr39mWn2aB/8ASphwR1RfX/D/AOtXmxOTTw1G/vsVWp0Q2iiiu85wooooAKKKKACiiigApaKKAEpaKSgBaKSigAzS0lFAC0UlFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABS0lFABRRRQAUUUUAFFFFAwooooAKKKKBBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQMKWkpRQIKKKKQBSUUUwCiiigApaSloASilpKAFopKWgApKKKACiiigApaSigBaSlpKAFooooAKSlpKACiiigApaSigAooooAWkpaKAEpaSloAKKDSUALRRRQAUhpaSgAoopaACiiigAooooAKTNLRQAUUlFAC0lFFAC0UUlAC0UlLQAUUUUAKDXQ+F9eOl3fkTt/osp+Yn+A+v8AjXO0ucGonBTVmVF2d0e2AggEdDWbrekRaxYPbvhZB80b/wB1v8KwPBuv+co0y5f51H7lj3H938K7GvLlF05nWmpo8Wubaa0uHgnQpIhwwNRV6J4x0L7bam/t0/0iEfOAPvr/AIj/AD2rzzFelSqKcbnLOPKxK6XwroH9p3X2m4X/AESI8g/8tG9Pp61iafYT6nfR2sC5ZzyeyjuTXrllaRWNnFbQriONdo/xrLEVeVWW5VKF3dk4AAwBgVS1fU4tJ097mXkjhVHVm7CrrMFUsxAAGST2rzzxHq39raa86f6hL3y4uOqhOv4nJrjpQ55a7G85cqOdvbyW/vJLmdsySHJ9vYe1QUmaK9VKysjjCiiimAUUUUAFFFFABRRSmgBKKWkoAWkoooAWkopaAEopaSgAooooAKKKKACilooASilooASiiigAooooAKKKKACiig0ALSUUUAFFFGKACiiigAooooGFFFFAgooooAKKKKBhRS0UCEopaKAEooooAKKKKACiiigAoFFAoAWiiikAlFFFMAooooAKKKWgApKWkoAKKBS0AJRRRQAUUUUAFFFFABRS0UAFFFFABRRRQAUUUUAJRRRQAtFFFABRRRQAUlFFABRRRQAtFIKWgAooooAKKKKACiiigAooooAKKKKACkpaSgAopaKAEoNFFAC0UUUAFJS0lABRRRQA+KV4ZUkjYq6EMpHUEV6xoOrprOmJOMCVfllUdm/wPWvJK0dF1ifRb0TxfMh4kjJwHH+PvWFelzx03NKc+V6nrxGaxLvwjpF5cGZ4XRm6iJ9oP4Vb0zWbLVoRJbSgtj5o24ZfqK0M15t5QfY6rKRTsNJstMjK2kCx56t1Y/UmrmKXsSeAOp9Kx9U1u3it3js9R09bnpmWcYX34zk0kpSYNqKMHxr4h8hG0u1f94w/fMOw/u/4/wD66wfs2fAouCcAX4UD1+Sp4/Cct9MXOs6W0kjZZjd7iSe/Su+/4Q23PhBdGaf5gfN8/HHmdc49O30rq9pCkkkY8kp3Z49ilrprzwc9oxB1nSj7NcbT+VZE+kvDnN/pz4/uXIP9K7I1Iy2Zi4tbmfRQRtYjIOO4OaK0JCiiigAooooAKKKKBhRRRQIKKWkoAKKKKADNLSUtABSUUUAFFFFAC0UZooAKSlooAKSlpKAFpKKKACiiigAooooAKKKKAFopKM0AFFFFABRRRQAUUUUAFFLSUAFFFFABS0UhoAWikpaAEopaSgAooooAKKKKACloooAKKU9aSkAlFFFMAooooAKKKKAFpKKKAClpKKACiiigAoopaAEopaKACiiigApKKKAFopKWgApKWkoAKKWigBKKXFFACUUUUAFFFFAC0UUUAFFFFABSUtFABRRRQAUUUUAFFFFABRRRQAmaKWkoAWikpaACiiigAooooAKSlooASilooASiiigByO8bh0Yqw6EHBFaKeIdXjXauo3GPd8/zrMoqXFPdDu0W7nVL68GLm8nlH915CR+XSqmaKKaSWwXErul164/4VkyNKTL532QN32dcf988VwtannH/AIRXyc8fbt2P+2dZVYKVr9yoSauZmaM0UVqQFFFFMAooooAKKKWgBKKWigAooooAKSlpKACiiloASiiigAooooAKKKKACiijFAC0UlLQAUlLSUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABS0lLQAUUUUAJRRRQAUtJS0AJRS0lABRRRQAUUUUAFFFFAC0UUUAFFFFIBKKKKYBRRRQAUUUUAFFLRQAlFFFABRRRQAtFFJQAtFFFABSUUUAFFFLQAlFLRQAUlLSUAFLRRQAUlFFABRRRQAUUUUAFLSUUAFLRRQAlFLRQAUUUUAFFFJQAtFFFABRRRQAUlFFABRS0UAFFFIaADNFFFABRRRQAZopaKAEoopaAEopaSgAooooAKm84fYvIwc+Zv8A0x/jUNFABRRRQAUUUUAFFFFABRRmigAozRS0AFFFFABSUtJQAtJRRQAtJS0UAJRRRQAUUUUAFFLSUAFLSUUAFFFFABRRRQAUUUUAFFFFABRRRQMKKKKBBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABS0lFABRRRQAUUUUAFFFLQAUUUUAFFFFIBKKKKYBRRRQAUUUUALSUUtACUUUUAFFLSUAFFFFAC0UlLQAlFFFABRRS0AFFFFACUUtFABSUtJQAUUtJQAUUUUAFFFFABRRRQAtFFFABRRSUALRSUtABSUtJQAtFIKWgAoopKACiiigAooooAWkoooAKKKKACiiigBaQ0UGgApaSigBaSiigAooooAKKKKACiiigAooooAKKKKACiiloAKSiigAooooAKKKKACiiloAKKKKAEpaKKACkoooAKKWigBKKKKACiiigAopaSgAooooAKKKKACiiigAooooAKWkpaACkpaKAEopaKAEooooAWkopaAEopaSgAooooAKKKKACiiigAooooAKUUlKKACiiikAlFFFMAooooAKKKKACiiigAoopaACkpaSgAopaSgAooooAWkoooAKWkpaACiiigAooooAKSlpKACiiigAopaKACkpaKAEooooAKWiigBKKWkoAKWiigApKWigBKWiigApKWkoAKKKKACiiigApaKKACiiigAooooAKSlooASilpKAClpKWgApKWigBKKWkNABRRRQAUUUtACUUUUDCiilxQISlpKKACiiigAopaSgAooooAKWkFLQAUUUUAJS0UUAFFFFACUUUUAFFLSUAFFFFABRRRigAooooAKKKKACiiigAooooAKWikNABRRRQAUUUUAFFLRQAUUUUAJRRRQAUUUUAFFFFABRRRQAUtJSigAooopAFJRRTAKKKKACiiigAooooAKKKKACiiigBaSlooASiiigAooooAKKKKACiiigAooooAKKKKACiiigAopaKACiiigBKWiigBKWkpaACiiigAooooAKKKKACiiigAooooASilpKAClpKKAFooooAKKKKACiiigAozRRQAlFLSUAFFLRQAUUUUAFJS0lABRRS0AFFFFABSGlooASlpKWgBKKWigBKKKKACilooASiiloABRSUtABSUUtACUuaSigBc0lFFABS0UlAC0UZooASiiigAzS0lFABRRRQAUUUUAFFFFABRRRQMWkoooEFFFFABRRRQAtFFJQAUUUUAFFFFABRRRQAUUUUAFFFFABS0UUAFFFFIBKKWkpgFFFFABRRRQAYopaSgAopcUlABQKKKAFpKWigBKKKWgBKKKKACiiloASiiigAopaSgAooooAKKKKACiiigBaKKKACiiigApKWigApKWigBKKKKACloooAKKKKAEpaKKACkpaKAEopaKAAUUUUAFFFFABRRRQAUUUUAJRS0UAFFFFABRRRQAUUUUAFFFFABRRSUALSUUUAFFFFAC0lLSUALSUtJQAtFFFACGloooAKSlooASilooASilooASlpKKAFpKKKACiiigAooooAKKKWgBKKKKACiiigAooooAKKKKACiiigAopaKAEooooAKKKKACiiigAooooAKKKKACiiigAooooAWikpaACiiikMSilpKYgooooAKKWigBKKKKACiiloAKSlpKAFopKKAFooooASiiigBaKKKAA0lLSUAFFFFABRRRQAUUUYoAKWiigBKWiigAooooAKKKKACigUUAFJilpKAFooooAKKKKACkpaSgApaSloAKKSigBaKKKACiiigAooooAKKKKAEooooAWkpaKACiiigAooooAKKSloAKSlpKAFpKKKACiiloAKKKKACkpaKACkpaSgBaSiigBaSlpKAFopKKAFoopDQAUUUUAFFFFABRRS0AJRRRQAUUUUALSUtFACUUUUAFFFFABRRRQAUtJRQAtJRRQAUUUUAFFFFABRRRQAtJS0lABRRRQAUUUUAFFFFABS0UUAFFLSUgCkoopgFFFFAC0lLSUAFFFFABRRS0AJRS0lABRRRQAtFJS0AFJS0UAFJS0UAFJS0UAJRS0UAFJS0lAC0UlLQAUUUUAJmlpKKAFooooAKKKKACiiigApKKKAFpKWigBKKWigBKKKWgAooooAQ0UUUALSUUYoAKWiigAooooAKKKQ0AFFFLQAUUUlAC5pKKKACiiigAooooAWiiigApKWkoAKKKWgAooooAKSlooAKSlpKACiiloASloooAKSlooAKKKKAEooooAKKKKACiiigBaSlpKACiiigAooooAKKKKACiiigAooooAKKKKBhRRRQIKKKKACilooASiiigAooooAKKKKACiiigApRSUUALRRRQAUUUUAIaKDRQAUUUUAFAoooAWkpaSgAoFFFAC0UUUAFFJRQAUUUUAFLSUtABRRRQAUlLRQAlLmkooAKKKWgBKWiigAooooASloooAKKKKACiiigAooooAKKKKACiikoAWiiigAooooAKKKKAEopaSgBaKSloAKKKKACiiigAooooAKSlooAKSlooASloooAKKKKAEoFFLQAUUUUAFJQaKAFpKWkoAKWig0AFFJS0AFJQaKACiiigApc0lFAC0UlLQAUUUUAJRS0UAJS0UUAFFFFACUUtJQAtJRRQAUUUUAFFLSUAFFFLQAUUUhoAKKKKACiiigAooooAWikooAKKKKACiiigAooooAKKKKAClpKKAFooooAKKKKQAaSg0UwCiiigYUUUUCCiiloASilooAKKKSgAooooAWkoooAKWkpaAEpaKKACiiigBKKWkoAKWkpaACkNLRQAlFFLQAlFLRQAlFLRQAUUlLQAUUUUAFFFFABRRRQAUUUUAFFFFACUtFFABSUtFACUUuKSgBaKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigApKWigBKKKKACilooAKKKKAEopaKAEpaKKAEopaSgApaSloAKKKKACkpaKACiiigAooooASiiigBaSiloASiiigAooooAKKKKAFooooAKSlooASiiigAopcUUAJRS0lABRRRQAUUtFACUUUUAFFFFABRRRQAUUUUAFLSUtABRRRQAhopaSgAooooAKKWigBKKWkoAKWkooAKKKKACilooASiiigAoopaACiiigAooooAKSlooASiiigAooooAKKKKAFpKKKACijFFABRRRQAtFFFABSUUUALRRSUALRSUtABRRRQAUUUUAFJS0UAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAJS0lLQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFJS0lAC0lFFAC0UUUAFFFFABRRRQAUUUUAFFJS0AJRS0lABRRRQAUUCloASilooASilooASilxRQAUUUUAFJS0UAFFFFABSGlpKACiiigAooooAKKKKAFpKKKACiiloASijFLQAUUUUAFFKaSgApKKKACiiigBaKKKACkpaKAEopaKAEooooAKKKKACiiigApaSloAKKKKACkpaSgApaSloASiiigAopaKAEopaKAEpaSigBaSlooASilooASilooAKMUUUAFJS0UAJRS0YoASlpKKAFooooAKSlooAKKKSgBaSiigApaSloAKSlooAKKKKACiiigApKWigAooooAKKKKACiiigAooooAKKKKADNFJS0AFFFFABRRRQAUmKWigApKWigBKWkooAWkzRRQAtFFFACUUtFACUUtJQAUUUUAFFFFABRRRQAtFJS0AFJRRQAUUUUAFFFFABS0lFABRRRQAUUUtACUUUUAFFFFABRRRQAUUUUALRRRQAUCilFACUUUUgEopaSmAUUUUAFGaKKAClpKWgAooooAKSlooASilooASlxRRQAlLSUtABRRRQAUlLRQAlFLSUAFFLiigBKWiigAoopKACiiigApaSigBaKKKACiiigAooooAKKKKACkpaKACiiigAooooAKSlooAKKKKACiiigBKKWigAooooAKKKKACiiigAooooAKKKKACiiigBKWiigAooooASiiigAooooAWkpaTFABRRRQAUtJS0AFFFFACUUtFABRRRQAUUUUAFJRRQAUtJS0AFFFFACUUUUAFFLSUAFLSUtACUUUtACUUUUAFFFFAwooooEFFFFABRRRQAUUUUAFFKKKAEooooAKKKKAClpKWgAopKUUAFJRRSGFFFFMQUUUUAFFFFAC0lFFABS0lFABRRRQAtFJRQAtFJS0AFFFFABSUtFABSUtJQAUUUUALmkoooAWiikoAKKKKACiiigBaSiigAFLSUtABRRRQAUZopKAFooooAKKKKACiiigAooooAKKKKACiiigAooooAKSlooASloooAKSlpKADNFFFABRRS0AFJS0UAFFFFABRRRQAUUUUABpKKWgBKKWigAooooAKSiigBaSlpDQAUtJS0AFFFJQAtFFFABRRRQAlFFFABRRS0AFJRRQAUUUUAFFFLQAlFLSUAFFFFABRRRQAUUUtACUUtJQAUUUUDCiiigQUUUUALSUtJQAUUUUAFFFFABRRRQAUtFFAAetFFFIBKKKKYBRRRQAUUUUAFFFFABRRRQAUUtFACUUtJQAUUUUAFFFFABRRRQAUUtFACUUUtABRRRQAlFFFABRRRQAUUUUDFpKKKBBS0UUAFFFFABSUtIaAClpKWgAooozQAUUUlAC0UUUAJS0lFAC0lFLQAUUUlAC0UUUAIaWkooAWkpaKAEooooAKWkpaACiiigAoopKAFopKKAFpKWigAFFFFACUUtFABRRSUALRSCloAKKKKACikzS0AFJS0lABRRRQAtFJRQAUUUtABSUtIaACiiigAooooAKKKKACiiigAooooAKKKKAClpKWgBKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigApaKKAA0UlLQAlFFFABRRRQAUUUUAFFFFABRRRQAtJRRQAUUUUAFFFFABRRRQMKKKKBC0UlFABRRRQAUUUUAFFFFABRRRQAtJS0lABRRRQAUtJRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAxaSiigBaQ0UUCCiiigYUtJRQAtFJS0CEooooAXNJRRQMKKKKACg0UUCCiiigYtFFFAhKKKKACiiigYUUUUCCiiigAooooAKKKKACiiigBaSiigApaSigBaSiigAooooAKKKKACiiigAooooAKKKKACiiigApaSigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAClpKKAFopKWkAlFFGKYBRRRigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiijFABRRS0AJRRRQAUUUUAFFFFABRRRQAUUtFACUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUuKAEooooAKKKKACiiigAoopaAEpaKKAEpaKKAEopaKAEopaTFABRS4ooASiiigAooooAM0UUUAFFFFAwooooEFLRRQAlFLRQAlFFFABRRRQAUUUtACUUtJQAUUUUAFLSUtACUUtJQAUUUUAFFFFABRRS0AJRRRQAUUUtACUUUtACUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFLQAlFLRQAlKKSnYpDP/9k=";
-export const faviconContentType = "image/jpeg";
+ "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";
+export const faviconContentType = "image/png";
diff --git a/bunny_js/src/llm_routes.ts b/bunny_js/src/llm_routes.ts
index cc56c0624..72392d285 100644
--- a/bunny_js/src/llm_routes.ts
+++ b/bunny_js/src/llm_routes.ts
@@ -36,20 +36,26 @@ import {
MAX_PRICE_ATOMIC,
getSettleAmount,
} from "./llm_schemas.js";
-import { isTestnet } from "@fretchen/chain-utils";
+import { isTestnet, type SettlementTokenInfo } from "@fretchen/chain-utils";
import { logger } from "./logger.js";
import { offeredStablecoins, resolvePaidStablecoin } from "./stablecoin_pricing.js";
import { toResponse } from "./response.js";
import {
createLLMResourceServer,
+ EXACT_ALTERNATIVE_AMOUNT_ATOMIC,
+ EXACT_ALTERNATIVE_NETWORK,
createBatchSettlementPaymentRequirements,
create402Response,
+ createExactAlternative,
+ createResourceServer,
extractPaymentPayload,
createSettlementHeaders,
+ formatParallelPrices,
getBatchSettlementNetworks,
LLM_MAX_TIMEOUT_SECONDS,
formatUsdcAtomicAsDecimalUsd,
releaseLock,
+ withExactAlternative,
type SdkPaymentPayload,
type SdkPaymentRequirements,
} from "./x402_server.js";
@@ -103,10 +109,9 @@ const ROOT_PAGE = buildRootPage({
},
],
price: [
- `POST / — metered by real token usage, at most $${formatUsdcAtomicAsDecimalUsd(MAX_PRICE_ATOMIC.USDC)} per request`,
+ `POST / — metered per request, ceiling ${formatParallelPrices(MAX_PRICE_ATOMIC)}; or exact: $${formatUsdcAtomicAsDecimalUsd(EXACT_ALTERNATIVE_AMOUNT_ATOMIC)} flat (USDC, Base mainnet)`,
],
- payment:
- "x402 (HTTP 402): no account, no API key. An unpaid request answers 402 with the machine-readable Payment-Required challenge — exact terms (scheme batch-settlement, network, token, ceiling) are in the challenge, then retry the same request with the payment attached.",
+ payment: `x402 (HTTP 402): no account, no API key. The 402 lists both schemes — batch-settlement channels first (the preferred way to pay: metered by real token usage under the ceiling) and an exact alternative at the end (one EIP-3009 signature per call, unmetered, for clients without channel wiring). Pay in USDC (Base or Optimism) or EURC/EURe (Base); the challenge carries every token's per-network terms. Retry the same request with the payment attached.`,
tryIt: `curl -X POST '${openapiSpec.servers[0].url}/' -H 'Content-Type: application/json' -d '{"model":"${advertisedModelIds()[0]}","messages":[{"role":"user","content":"ping"}]}'\n# → HTTP 402 + Payment-Required header: the terms. No wallet needed for this first look.\n# Testnet note: on Base Sepolia the payment settles and the completion is a mocked answer — a free end-to-end test.`,
errors:
"402 = unpaid or invalid payment (challenge in the Payment-Required header); 400 = invalid request (OpenAI-shaped error body); 404 = unknown model. Response schemas: openapi.json.",
@@ -249,7 +254,12 @@ export async function handleLLMRequest(request: Request): Promise {
tags: ["llm", "chat", "x402"],
iconUrl: `${openapiSpec.servers[0].url}/favicon.png`,
});
- return toResponse(create402Response(paymentRequirements));
+ // Append the exact alternative AFTER the batch entries — order is the contract (see
+ // withExactAlternative): channel buyers keep their preferred entries, channel-less
+ // clients fall through to the exact one.
+ return toResponse(
+ create402Response(withExactAlternative(paymentRequirements, receiverAddressForChallenge)),
+ );
}
// OpenAI chat-completions request body — everything we do not consume is FORWARDED to the
@@ -345,74 +355,115 @@ export async function handleLLMRequest(request: Request): Promise {
);
}
- let resourceServer: ReturnType["resourceServer"];
- let scheme: ReturnType["scheme"];
- try {
- ({ resourceServer, scheme } = createLLMResourceServer(receiverAddress));
- } catch (err) {
- logger.error({ err }, "Failed to configure batch-settlement resource server");
- return jsonError(500, "Server configuration error");
- }
-
// Non-null: the unpaid case already returned the 402 challenge above, before validation.
const paymentPayload = paymentPayloadEarly;
+ const accepted = (paymentPayload as Record)["accepted"] as
+ Record | undefined;
+ const clientNetwork = accepted?.["network"] as string | undefined;
- const clientNetwork = (paymentPayload as Record)["accepted"]
- ? (((paymentPayload as Record)["accepted"] as Record)[
- "network"
- ] as string | undefined)
- : undefined;
+ // ─── Scheme fork ────────────────────────────────────────────────────────────────────
+ // The 402 lists the batch-settlement entries first and the exact alternative last (see
+ // withExactAlternative); the payment names which one it pays against. The exact path is
+ // channel-free — no enhancePaymentRequirements, no channel lock — which is also why an
+ // invalid exact payment answers a plain 402 below instead of the batch path's corrective
+ // resync (there is no channel state to resync).
+ const isExact = accepted?.["scheme"] === "exact";
- if (!clientNetwork || !getBatchSettlementNetworks().includes(clientNetwork)) {
- return jsonError(402, "Unsupported or missing network for batch-settlement payment");
- }
+ let resourceServer: ReturnType["resourceServer"];
+ let paymentRequirements: SdkPaymentRequirements;
+ let useMock: boolean;
+ let coin: SettlementTokenInfo;
+
+ if (isExact) {
+ // The exact entry was offered on exactly one (network, token); anything else is a
+ // caller paying against terms we never issued.
+ const exactRequirements = createExactAlternative(receiverAddress);
+ if (
+ clientNetwork !== EXACT_ALTERNATIVE_NETWORK ||
+ accepted?.["asset"] !== exactRequirements.asset
+ ) {
+ return jsonError(
+ 402,
+ `Unsupported exact payment terms. This endpoint's exact scheme accepts ${EXACT_ALTERNATIVE_NETWORK} and ${exactRequirements.asset} only`,
+ );
+ }
+ // Mainnet-only offer, so the testnet mock branch cannot apply; only an explicit
+ // useDummyData still selects the stand-in.
+ useMock = useDummyData === true;
+ // Guaranteed by the terms check above (the asset IS ours), but stay symmetric with the
+ // batch branch rather than trust an invariant across two modules.
+ const resolvedCoin = resolvePaidStablecoin(EXACT_ALTERNATIVE_NETWORK, exactRequirements.asset);
+ if (!resolvedCoin) {
+ logger.error(
+ { network: EXACT_ALTERNATIVE_NETWORK, asset: exactRequirements.asset },
+ "Config error: exact-alternative asset not offered",
+ );
+ return jsonError(500, "Server configuration error");
+ }
+ coin = resolvedCoin;
+ resourceServer = createResourceServer();
+ paymentRequirements = exactRequirements;
+ } else {
+ let scheme: ReturnType["scheme"];
+ try {
+ ({ resourceServer, scheme } = createLLMResourceServer(receiverAddress));
+ } catch (err) {
+ logger.error({ err }, "Failed to configure batch-settlement resource server");
+ return jsonError(500, "Server configuration error");
+ }
- // SECURITY: testnet must never reach the real Mistral API (scw parity). An explicit
- // `useDummyData: false` on a testnet network is always a caller error — reject loudly.
- if (isTestnet(clientNetwork) && useDummyData === false) {
- return jsonError(
- 400,
- "Real inference is not available on testnet — this network always returns a mocked LLM response. Omit useDummyData or set it to true, or use a mainnet network for a real completion.",
- );
- }
- // Never spend real inference budget on a valueless testnet payment (scw parity).
- const useMock = useDummyData === true || isTestnet(clientNetwork);
+ if (!clientNetwork || !getBatchSettlementNetworks().includes(clientNetwork)) {
+ return jsonError(402, "Unsupported or missing network for batch-settlement payment");
+ }
- // Price the stablecoin the buyer's channel is in, not a fixed one (scw parity).
- const accepted = (paymentPayload as Record)["accepted"] as
- Record | undefined;
- const coin = resolvePaidStablecoin(clientNetwork, accepted?.["asset"]);
- if (!coin) {
- logger.warn({ clientNetwork, asset: accepted?.["asset"] }, "Payment asset not offered");
- return jsonError(
- 402,
- `Unsupported payment asset on ${clientNetwork} — offered: ${offeredStablecoins(clientNetwork)
- .map((offered) => `${offered.symbol} ${offered.address}`)
- .join(", ")}`,
- );
- }
- const baseRequirements: SdkPaymentRequirements = {
- scheme: "batch-settlement",
- network: clientNetwork as `${string}:${string}`,
- amount: MAX_PRICE_ATOMIC[coin.symbol],
- asset: coin.address,
- payTo: receiverAddress,
- maxTimeoutSeconds: LLM_MAX_TIMEOUT_SECONDS,
- extra: { name: coin.name, version: coin.version },
- };
- // Must be the SAME enhanced requirements the client signed its channelConfig against —
- // a raw object here makes the facilitator reject every deposit with
- // receiver_authorizer_mismatch (scw parity; confirmed via a real Base Sepolia run).
- const paymentRequirements = await scheme.enhancePaymentRequirements(
- baseRequirements,
- {
- x402Version: 2,
+ // SECURITY: testnet must never reach the real Mistral API (scw parity). An explicit
+ // `useDummyData: false` on a testnet network is always a caller error — reject loudly.
+ if (isTestnet(clientNetwork) && useDummyData === false) {
+ return jsonError(
+ 400,
+ "Real inference is not available on testnet — this network always returns a mocked LLM response. Omit useDummyData or set it to true, or use a mainnet network for a real completion.",
+ );
+ }
+ // Never spend real inference budget on a valueless testnet payment (scw parity).
+ useMock = useDummyData === true || isTestnet(clientNetwork);
+
+ // Price the stablecoin the buyer's channel is in, not a fixed one (scw parity).
+ const resolvedCoin = resolvePaidStablecoin(clientNetwork, accepted?.["asset"]);
+ if (!resolvedCoin) {
+ logger.warn({ clientNetwork, asset: accepted?.["asset"] }, "Payment asset not offered");
+ return jsonError(
+ 402,
+ `Unsupported payment asset on ${clientNetwork} — offered: ${offeredStablecoins(
+ clientNetwork,
+ )
+ .map((offered) => `${offered.symbol} ${offered.address}`)
+ .join(", ")}`,
+ );
+ }
+ coin = resolvedCoin;
+ const baseRequirements: SdkPaymentRequirements = {
scheme: "batch-settlement",
network: clientNetwork as `${string}:${string}`,
- extra: baseRequirements.extra,
- },
- [],
- );
+ amount: MAX_PRICE_ATOMIC[coin.symbol],
+ asset: coin.address,
+ payTo: receiverAddress,
+ maxTimeoutSeconds: LLM_MAX_TIMEOUT_SECONDS,
+ extra: { name: coin.name, version: coin.version },
+ };
+ // Must be the SAME enhanced requirements the client signed its channelConfig against —
+ // a raw object here makes the facilitator reject every deposit with
+ // receiver_authorizer_mismatch (scw parity; confirmed via a real Base Sepolia run).
+ paymentRequirements = await scheme.enhancePaymentRequirements(
+ baseRequirements,
+ {
+ x402Version: 2,
+ scheme: "batch-settlement",
+ network: clientNetwork as `${string}:${string}`,
+ extra: baseRequirements.extra,
+ },
+ [],
+ );
+ }
let verification: {
isValid: boolean;
@@ -431,13 +482,21 @@ export async function handleLLMRequest(request: Request): Promise {
}
if (!verification.isValid) {
- // Through createPaymentRequiredResponse (not a hand-rolled body) so the SDK's corrective
- // enrichment runs — channelState/voucherState for cumulative mismatches, which the client
- // SDK's processCorrectivePaymentRequired needs to resync and retry (scw parity).
logger.warn(
{ reason: verification.invalidReason, message: verification.invalidMessage },
"Payment verification failed",
);
+ if (isExact) {
+ // Plain 402, not the batch corrective re-challenge: exact holds no channel state to
+ // resync — the client retries with a fresh signature against the challenge it has.
+ return jsonError(
+ 402,
+ `Payment verification failed: ${verification.invalidReason ?? "unknown"}`,
+ );
+ }
+ // Through createPaymentRequiredResponse (not a hand-rolled body) so the SDK's corrective
+ // enrichment runs — channelState/voucherState for cumulative mismatches, which the client
+ // SDK's processCorrectivePaymentRequired needs to resync and retry (scw parity).
const paymentRequired = await resourceServer.createPaymentRequiredResponse(
[paymentRequirements],
{
@@ -485,16 +544,22 @@ export async function handleLLMRequest(request: Request): Promise {
logger.error({ err: error }, "Error during answer generation");
// Verified but never settled: release the channel lock, or the next message waits out
// LLM_MAX_TIMEOUT_SECONDS (the lock TTL) behind `channel_busy` for an answer that was
- // never charged.
- await releaseLock(resourceServer, paymentPayload as SdkPaymentPayload, paymentRequirements);
+ // never charged. Exact holds no lock, so there is nothing to release.
+ if (!isExact) {
+ await releaseLock(resourceServer, paymentPayload as SdkPaymentPayload, paymentRequirements);
+ }
return jsonError(500, (error as Error).message);
}
// Settling with a DIFFERENT (smaller, real-usage) amount than verifyPayment() used is
// intentional (scw parity — see getSettleAmount() in llm_schemas.ts): handleBeforeSettle
- // only checks against the voucher's signed ceiling, not against what verify saw.
- const settleAmount = getSettleAmount(llmData.usage, coin.symbol);
- const settleRequirements = { ...paymentRequirements, amount: settleAmount };
+ // only checks against the voucher's signed ceiling, not against what verify saw. The
+ // exact alternative has no ceiling concept — its price is the flat per-call amount, so
+ // the settle amount equals the verified one.
+ const settleRequirements = isExact
+ ? paymentRequirements
+ : { ...paymentRequirements, amount: getSettleAmount(llmData.usage, coin.symbol) };
+ const settleAmount = settleRequirements.amount;
try {
const settlement = await resourceServer.settlePayment(
diff --git a/bunny_js/src/mcp_routes.ts b/bunny_js/src/mcp_routes.ts
index 29d187938..75eb1439d 100644
--- a/bunny_js/src/mcp_routes.ts
+++ b/bunny_js/src/mcp_routes.ts
@@ -54,19 +54,23 @@ const ROOT_PAGE = buildRootPage({
},
{
href: "https://web-agent.fretchen.eu/",
- label: "web_search — the web seller's page, spec and terms",
+ label:
+ "the web seller — home of the web_search tool; it also serves GET /fetch (single-page fetch, one URL's clean machine-readable content), a REST route priced on its page",
},
{
href: `https://mcp-agent.fretchen.eu/${AGENT_REGISTRATION_PATH}`,
label: "this origin's registration document (domain control proof for both agents)",
},
],
- price: MCP_TOOLS.map(
- (tool) =>
- `${tool.name} — priced at the seller it relays to; the unpaid call's tool result carries the exact 402 terms`,
- ),
+ price: [
+ ...MCP_TOOLS.map(
+ (tool) =>
+ `${tool.name} — priced at the seller it relays to; the unpaid call's tool result carries the exact 402 terms`,
+ ),
+ "GET /fetch — single-page fetch, the web seller's most-called route: REST only (no MCP tool — call the web seller directly), priced on its page",
+ ],
payment:
- "per tool, via x402 at the relaying seller: no account, no API key. initialize and tools/list are free; an unpaid tools/call returns the 402 demands as structuredContent in an isError tool result — retry with the payment attached.",
+ "per tool, via x402 at the relaying seller: no account, no API key. Each seller's 402 lists both schemes — batch-settlement channels first (USDC, or EURC/EURe on Base) and an exact alternative (one signature per call, flat in USDC on Base mainnet); the challenge carries every token's terms. initialize and tools/list are free; an unpaid tools/call returns the 402 demands as structuredContent in an isError tool result — retry with the payment attached.",
tryIt: `curl -X POST '${MCP_SERVICE.endpoint}' -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'\n# → the tools and their input schemas, free (as is initialize). tools/call carries the x402 payment.`,
errors:
"JSON-RPC errors: -32601 unknown method (a stateless server: initialize, tools/list, tools/call only), -32602 unknown tool. GET /mcp is 405 Allow: POST (the optional GET stream is not offered). Unpaid tools/call is NOT an error — it is a tools result with isError and the 402 demands in structuredContent.",
diff --git a/bunny_js/src/openapi.llm.json b/bunny_js/src/openapi.llm.json
index 6dc1b498b..ab7dde8a5 100644
--- a/bunny_js/src/openapi.llm.json
+++ b/bunny_js/src/openapi.llm.json
@@ -2,9 +2,9 @@
"openapi": "3.1.0",
"info": {
"title": "Fretchen LLM Inference — pay-per-call chat completions for agents",
- "description": "OpenAI-shaped chat completions from Mistral models, sold per request to autonomous agents via x402 batch-settlement payment channels — no account, no API key. Metered by real token usage under a fixed per-request ceiling; settled in USDC (Base or Optimism), EURC or EURe (Base), while Base Sepolia settles for free and answers with a mocked completion.",
+ "description": "OpenAI-shaped chat completions from Mistral models, sold per request to autonomous agents via x402 — batch-settlement channels (metered by real token usage under a per-request ceiling; USDC, EURC or EURe, the euro tokens on Base) or a flat per-call exact alternative (unmetered, USDC on Base mainnet). No account, no API key; Base Sepolia settles for free and answers with a mocked completion.",
"version": "1.0.0",
- "x-guidance": "OpenAI chat-completions body. POST / with { model, messages: [{ role, content }, ...] } and no payment header to receive a 402 with x402 batch-settlement payment requirements (accepts[]). Open/top up a payment channel per the requirements, retry with the payment header, and the service returns a standard OpenAI chat.completion object. Streaming (stream: true) is not supported. Each message is metered and settled up to a per-message price ceiling; the real cost is usage-derived and typically lower. Other standard OpenAI chat params (temperature, top_p, stop, seed, the penalties, response_format) are forwarded to the upstream model and work as normal; they are not enumerated here because the upstream owns that contract. The exceptions are stream, n and max_tokens, which are rejected with a 400 rather than ignored, because each would move cost past the fixed per-message ceiling this endpoint meters against. Tool calling is supported (see x-capabilities): send OpenAI-shaped tools, and a tool call comes back as choices[].message.tool_calls with finish_reason: \"tool_calls\" and content: null. Execute it yourself and send the result back as a role:\"tool\" message with the matching tool_call_id — this endpoint never calls a tool on your behalf, and accepts no callback or MCP server URL. Note: payment uses x402 batch-settlement (a stateful channel), so a stock OpenAI SDK cannot pay this endpoint without batch-settlement client wiring — the OpenAI shape is for body legibility, not drop-in SDK use.",
+ "x-guidance": "OpenAI chat-completions body. POST / with { model, messages: [{ role, content }, ...] } and no payment header to receive a 402 with x402 batch-settlement payment requirements (accepts[]). Open/top up a payment channel per the requirements, retry with the payment header, and the service returns a standard OpenAI chat.completion object. Streaming (stream: true) is not supported. Each message is metered and settled up to a per-message price ceiling; the real cost is usage-derived and typically lower. Other standard OpenAI chat params (temperature, top_p, stop, seed, the penalties, response_format) are forwarded to the upstream model and work as normal; they are not enumerated here because the upstream owns that contract. The exceptions are stream, n and max_tokens, which are rejected with a 400 rather than ignored, because each would move cost past the fixed per-message ceiling this endpoint meters against. Tool calling is supported (see x-capabilities): send OpenAI-shaped tools, and a tool call comes back as choices[].message.tool_calls with finish_reason: \"tool_calls\" and content: null. Execute it yourself and send the result back as a role:\"tool\" message with the matching tool_call_id — this endpoint never calls a tool on your behalf, and accepts no callback or MCP server URL. Note: payment uses x402 batch-settlement (a stateful channel), so a stock OpenAI SDK cannot pay this endpoint without batch-settlement client wiring — the OpenAI shape is for body legibility, not drop-in SDK use. After the channel entries, the 402 also carries one exact-scheme alternative: a single EIP-3009 signature per call, flat 0.01 USDC on Base mainnet, unmetered — the usage-derived settlement and the per-message ceiling apply to channel payments only — for clients that cannot open channels at all. The live challenge is the source of truth for its terms.",
"contact": {
"name": "fretchen",
"url": "https://www.fretchen.eu",
diff --git a/bunny_js/src/openapi.search.json b/bunny_js/src/openapi.search.json
index fe2f87558..d148e061d 100644
--- a/bunny_js/src/openapi.search.json
+++ b/bunny_js/src/openapi.search.json
@@ -2,9 +2,9 @@
"openapi": "3.1.0",
"info": {
"title": "Fretchen Web Retrieval — pay-per-call web access for agents",
- "description": "Web search and single-page fetch for agents, paid per call via x402 batch-settlement payment channels in USDC (or EURC on Base).",
+ "description": "Web search and single-page fetch for agents, paid per call via x402 — batch-settlement channels in USDC, EURC or EURe (the euro tokens on Base, USDC also on Optimism), or a flat per-call exact alternative in USDC on Base mainnet.",
"version": "1.0.0",
- "x-guidance": "GET /search?q=... or GET /fetch?url=... with no payment header to receive a 402 carrying x402 batch-settlement payment requirements (accepts[]). Open or top up a payment channel per those requirements, retry with the payment header, and the service returns JSON. Payment travels in the header only — these are GET routes, so there is no body fallback. Both routes bill onto the SAME channel as llm-agent.fretchen.eu when the payer, voucher signer and receiver match, so an agent already chatting there pays for web access without a second deposit. Prices are fixed per route and settled only when the call succeeds: an upstream failure returns 500 and takes no money. Mainnet only — testnet USDC is free and these routes spend real money on the caller's behalf.",
+ "x-guidance": "GET /search?q=... or GET /fetch?url=... with no payment header to receive a 402 carrying x402 batch-settlement payment requirements (accepts[]). Open or top up a payment channel per those requirements, retry with the payment header, and the service returns JSON. Payment travels in the header only — these are GET routes, so there is no body fallback. Both routes bill onto the SAME channel as llm-agent.fretchen.eu when the payer, voucher signer and receiver match, so an agent already chatting there pays for web access without a second deposit. Prices are fixed per route and settled only when the call succeeds: an upstream failure returns 500 and takes no money. Base Sepolia is offered for end-to-end testing: a testnet payment settles in free test tokens and the answer is a marked stand-in (no web search or fetch is performed) — the metered upstreams run for mainnet payments only. After the channel entries, the 402 also carries one exact-scheme alternative: a single EIP-3009 signature per call, flat 0.01 USDC on Base mainnet, priced independently of the channel prices — for clients that cannot open batch-settlement channels. The live challenge is the source of truth for its terms.",
"contact": {
"name": "fretchen",
"url": "https://www.fretchen.eu",
diff --git a/bunny_js/src/search_stand_in.ts b/bunny_js/src/search_stand_in.ts
new file mode 100644
index 000000000..cfb6f5322
--- /dev/null
+++ b/bunny_js/src/search_stand_in.ts
@@ -0,0 +1,66 @@
+/**
+ * The testnet stand-ins for the web seller's two routes — the twin's counterpart of
+ * llm_service's mock LLM (scw parity in intent, not in shape).
+ *
+ * A paid request on a testnet network (Base Sepolia) exercises the FULL x402 machinery —
+ * challenge, validation, channel verification, settlement, the Payment-Response header —
+ * but must never touch the metered upstreams: Brave bills per query and /fetch is
+ * egress, and a testnet payment is free. `run()` in searchapi_routes.ts therefore swaps
+ * these in for searchWeb/fetchExternalHtml whenever the payment's network is a testnet.
+ *
+ * The output types are the SERVICES' own return types (SearchResult / FetchedPage), so the
+ * rest of the response path is untouched. Content rules, deliberately strict:
+ * - deterministic — same input, same bytes; no clock, no randomness, no drift;
+ * - loud — every string carries the MOCKER marker, so no caller can mistake a stand-in
+ * for real data (a mock that looks real is worse than no mock);
+ * - echoing — the input query/url appears in the output, so the caller sees their own
+ * request flow through the whole pipeline.
+ */
+
+import type { SearchResult } from "./search_service.js";
+import type { FetchedPage } from "./web_fetch_service.js";
+
+/** The marker every stand-in string carries — tests pin the exact phrase. */
+export const TESTNET_MOCK_MARKER = "testnet mock";
+
+const note =
+ "This is a testnet mock answer: your payment's challenge, verification, settlement and " +
+ "response all ran, but no real web search was performed. Pay on a mainnet network " +
+ "(eip155:10 or eip155:8453) for real results.";
+
+export function standInSearch(query: string): { results: SearchResult[] } {
+ return {
+ results: [
+ {
+ url: "https://web-agent.fretchen.eu/mock-result-1",
+ title: `testnet mock result for "${query}"`,
+ text: note,
+ },
+ {
+ url: "https://web-agent.fretchen.eu/mock-result-2",
+ title: "testnet mock: payment pipeline verified",
+ text: `Your query "${query}" reached the seller and a signed, settled payment was accepted for it.`,
+ },
+ {
+ url: "https://web-agent.fretchen.eu/mock-result-3",
+ title: "testnet mock: how to get real results",
+ text: "Aim the same client at a mainnet network entry of the 402 challenge and the live upstream runs.",
+ },
+ ],
+ };
+}
+
+export function standInFetch(url: string): FetchedPage {
+ return {
+ finalUrl: url,
+ contentType: "text/html; charset=utf-8",
+ html: `\n\ntestnet mock page: ${escapeHtml(
+ url,
+ )}\n\n
testnet mock page
\n
${note}
\n
You requested ${escapeHtml(
+ url,
+ )}; it was never fetched.
\n\n`,
+ };
+}
+
+const escapeHtml = (text: string): string =>
+ text.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """);
diff --git a/bunny_js/src/searchapi_routes.ts b/bunny_js/src/searchapi_routes.ts
index d311f1827..f3921aebd 100644
--- a/bunny_js/src/searchapi_routes.ts
+++ b/bunny_js/src/searchapi_routes.ts
@@ -12,9 +12,11 @@
* fails after it never settles, orphaning the lock for MAX_TIMEOUT_SECONDS).
*/
import { logger } from "./logger.js";
+import { isTestnet } from "@fretchen/chain-utils";
import { offeredStablecoins, resolvePaidStablecoin } from "./stablecoin_pricing.js";
import { searchWeb, QueryError } from "./search_service.js";
import { fetchExternalHtml, FetchUrlError, parseHttpsUrl } from "./web_fetch_service.js";
+import { standInFetch, standInSearch } from "./search_stand_in.js";
import {
DESCRIPTION,
FetchQuerySchema,
@@ -37,24 +39,33 @@ import openapiSpec from "./openapi.search.json" with { type: "json" };
import { buildSearchBazaarExtension } from "./bazaar_discovery.js";
import { toResponse } from "./response.js";
import {
+ EXACT_ALTERNATIVE_AMOUNT_ATOMIC,
+ EXACT_ALTERNATIVE_NETWORK,
create402Response,
createBatchSettlementPaymentRequirements,
+ createExactAlternative,
createLLMResourceServer,
+ createResourceServer,
createSettlementHeaders,
extractPaymentPayload,
+ formatParallelPrices,
+ formatUsdcAtomicAsDecimalUsd,
+ getBatchSettlementNetworks,
releaseLock,
+ withExactAlternative,
type SdkPaymentPayload,
type SdkPaymentRequirements,
} from "./x402_server.js";
/**
- * Mainnet only, unlike the chat, which also accepts Base Sepolia. Testnet USDC is free, and
- * both routes spend real money on the caller's behalf — Brave bills per query, and /fetch is
- * egress. Advertising a testnet would hand anyone a metered API for nothing. The cost is
- * that the paid path cannot be exercised on a testnet at all: at $0.001 a call, a real
- * run is cheaper than the setup it replaces. (Copied from scw_js/search_api.ts.)
+ * The batch-settlement networks, mainnet PLUS Base Sepolia — unlike the scw original, which
+ * was mainnet-only. Adverting a testnet is safe now BECAUSE the paid path never touches the
+ * metered upstreams there: a testnet payment gets the marked stand-in answers instead
+ * (search_stand_in.ts), while the full challenge/verify/settle machinery runs for real on
+ * free test tokens — the try-before-buy the agents asked for. Metered Brave/egress only
+ * run for mainnet payments; the regression test pins that forever.
*/
-const MAINNET_NETWORKS = ["eip155:10", "eip155:8453"];
+const OFFERED_NETWORKS = getBatchSettlementNetworks();
/**
* Advertised and verified lock TTL, a quarter of the chat's LLM_MAX_TIMEOUT_SECONDS. It
@@ -102,18 +113,12 @@ const ROOT_PAGE = buildRootPage({
label: "stateless MCP endpoint — same tools as JSON-RPC 2.0 over POST (web_search)",
},
],
- price: ROUTES.map((route) => {
- const info = (
- openapiSpec.paths as Record<
- string,
- Record
- >
- )[`/${route}`].get["x-payment-info"];
- return `GET /${route} — $${info?.price.amount ?? "?"} per call`;
- }),
- payment:
- "x402 (HTTP 402): no account, no API key. An unpaid request answers 402 with the machine-readable Payment-Required challenge — exact terms (scheme batch-settlement, network, token, amount) are in the challenge, then retry the same request with the payment attached.",
- tryIt: `curl '${SERVICE_URL}/search?q=erc-8004'\n# → HTTP 402 + Payment-Required header: the terms. No wallet needed for this first look.`,
+ price: ROUTES.map(
+ (route) =>
+ `GET /${route} — channel call: ${formatParallelPrices(PRICE_ATOMIC[route])}; or exact: $${formatUsdcAtomicAsDecimalUsd(EXACT_ALTERNATIVE_AMOUNT_ATOMIC)} flat (USDC, Base mainnet)`,
+ ),
+ payment: `x402 (HTTP 402): no account, no API key. The 402 lists both schemes — batch-settlement channels first (the preferred way to pay) and an exact alternative at the end (one EIP-3009 signature per call, for clients without channel wiring). Pay in USDC (Base or Optimism) or EURC/EURe (Base); the challenge carries every token's per-network terms. Retry the same request with the payment attached.`,
+ tryIt: `curl '${SERVICE_URL}/search?q=erc-8004'\n# → HTTP 402 + Payment-Required header: the terms. No wallet needed for this first look.\n# Testnet note: a payment on Base Sepolia settles in free test tokens and the answer is a marked mock — the full flow, real results on mainnet only.`,
errors:
"402 = unpaid (challenge in the Payment-Required header); 404 = no such route; everything else is standard HTTP. Response schemas: openapi.json.",
});
@@ -165,7 +170,16 @@ function parseArgs(route: Route, queryParams: Record): Args {
return { route, url: parseHttpsUrl(parsed.data.url).toString() };
}
-function run(args: Args): Promise {
+/**
+ * The upstream call for one paid request — the ONE place the metered services are touched,
+ * which is why the testnet gate lives here and nowhere else: a testnet payment gets the
+ * marked stand-in answers (search_stand_in.ts) and the metered upstreams never run.
+ */
+async function run(args: Args, useMock: boolean): Promise {
+ if (useMock) {
+ logger.info({ route: args.route }, "Serving stand-in answer (testnet payment)");
+ return args.route === "search" ? standInSearch(args.q) : standInFetch(args.url);
+ }
return args.route === "search" ? searchWeb(args.q) : fetchExternalHtml(args.url);
}
@@ -191,8 +205,17 @@ function acceptedNetwork(payload: Record): string | null {
return typeof network === "string" ? network : null;
}
+/** The scheme the payer built their payload against, or null if they named none. */
+function acceptedScheme(payload: Record): string | null {
+ const accepted = payload["accepted"] as Record | undefined;
+ const scheme = accepted?.["scheme"];
+ return typeof scheme === "string" ? scheme : null;
+}
+
/**
* Serve one route against a payment: verify, run, and settle **only if the run succeeded**.
+ * The batch-settlement path below is the original; payments carrying the exact alternative
+ * (the last accepts entry of the 402, see x402_server.ts) take their own, channel-free flow.
*
* Settle-after-success is what makes an upstream failure free for the caller. It leaves a
* verify without a matching settle, which holds the channel lock for MAX_TIMEOUT_SECONDS —
@@ -203,13 +226,124 @@ async function servePaid(
args: Args,
payload: Record,
receiverAddress: `0x${string}`,
+): Promise {
+ if (acceptedScheme(payload) === "exact") {
+ return serveExact(route, args, payload, receiverAddress);
+ }
+ return serveBatchSettlement(route, args, payload, receiverAddress);
+}
+
+/**
+ * The exact alternative's paid flow: one EIP-3009 signature per call, Base mainnet USDC,
+ * flat EXACT_ALTERNATIVE_AMOUNT_ATOMIC (see x402_server.ts for why the price is what it is).
+ * Deliberately resembles the batch flow (verify → run → settle-on-success → Payment-Response)
+ * while touching NONE of the channel machinery: no enhancePaymentRequirements, no channel
+ * lock, no releaseLock — there is no state for any of it to act on.
+ */
+async function serveExact(
+ route: Route,
+ args: Args,
+ payload: Record,
+ receiverAddress: `0x${string}`,
+): Promise {
+ // The exact entry was offered on exactly one network/token; anything else is the caller
+ // paying against terms we never issued.
+ const network = acceptedNetwork(payload);
+ const paidAsset = (payload["accepted"] as Record | undefined)?.["asset"];
+ const exactRequirements = createExactAlternative(receiverAddress);
+ if (network !== EXACT_ALTERNATIVE_NETWORK || paidAsset !== exactRequirements.asset) {
+ return jsonResponse(402, {
+ error: `Unsupported exact payment terms. This endpoint's exact scheme accepts ${
+ EXACT_ALTERNATIVE_NETWORK
+ } and ${exactRequirements.asset} only`,
+ });
+ }
+
+ const resourceServer = createResourceServer();
+
+ let verification: {
+ isValid: boolean;
+ invalidReason?: string;
+ invalidMessage?: string;
+ payer?: string;
+ };
+ try {
+ verification = await resourceServer.verifyPayment(
+ payload as SdkPaymentPayload,
+ exactRequirements,
+ );
+ } catch (err) {
+ logger.error({ err }, "Payment verification error");
+ return jsonResponse(402, { error: `Payment verification failed: ${(err as Error).message}` });
+ }
+
+ if (!verification.isValid) {
+ logger.warn(
+ { reason: verification.invalidReason, message: verification.invalidMessage },
+ "Payment verification failed",
+ );
+ // A plain 402, not the batch path's corrective re-challenge: exact has no channel state
+ // to resync — the client retries with a fresh signature against the challenge it has.
+ return jsonResponse(402, {
+ error: `Payment verification failed: ${verification.invalidReason ?? "unknown"}`,
+ ...(verification.payer ? { payer: verification.payer } : {}),
+ });
+ }
+
+ let data: unknown;
+ try {
+ // Exact is offered on Base mainnet only — the metered upstream always runs there.
+ data = await run(args, false);
+ } catch (err) {
+ // Nothing settled and nothing to release — exact holds no locks.
+ logger.warn({ err, route }, "Run failed after exact verify");
+ return errorResponseFor(err);
+ }
+
+ let settlement: Record & { success: boolean; errorReason?: string };
+ try {
+ settlement = await resourceServer.settlePayment(
+ payload as SdkPaymentPayload,
+ exactRequirements,
+ );
+ } catch (err) {
+ logger.error({ err }, "Settlement error");
+ return jsonResponse(402, { error: `Settlement failed: ${(err as Error).message}` });
+ }
+
+ if (!settlement.success) {
+ logger.error({ settlement }, "Settlement failed");
+ return jsonResponse(402, {
+ error: `Settlement failed: ${settlement.errorReason ?? "unknown"}`,
+ });
+ }
+
+ logger.info(
+ { route, network, scheme: "exact", amount: exactRequirements.amount },
+ "Served and settled",
+ );
+ return new Response(JSON.stringify(data), {
+ status: 200,
+ headers: { ...CORS_HEADERS, ...createSettlementHeaders(settlement) },
+ });
+}
+
+/** The original batch-settlement flow (channels, vouchers, shared locks with the chat). */
+async function serveBatchSettlement(
+ route: Route,
+ args: Args,
+ payload: Record,
+ receiverAddress: `0x${string}`,
): Promise {
const network = acceptedNetwork(payload);
- if (!network || !MAINNET_NETWORKS.includes(network)) {
+ if (!network || !OFFERED_NETWORKS.includes(network)) {
return jsonResponse(402, {
- error: `Unsupported or missing network. This endpoint accepts ${MAINNET_NETWORKS.join(", ")}`,
+ error: `Unsupported or missing network. This endpoint accepts ${OFFERED_NETWORKS.join(", ")}`,
});
}
+ // A testnet payment settles free test tokens and answers with the marked stand-in
+ // (see run()); the metered upstreams run for mainnet payments only.
+ const useMock = isTestnet(network);
// Price the stablecoin the buyer's channel is in (see sc_llm_x402.ts) — checked before any
// server setup, since an unoffered asset is the caller's error, not ours.
@@ -295,7 +429,7 @@ async function servePaid(
let data: unknown;
try {
- data = await run(args);
+ data = await run(args, useMock);
} catch (err) {
// Nothing is settled: an upstream that failed is not something to charge for. But verify took
// the channel lock, which the chat shares — release it, or the user's next message is
@@ -353,7 +487,7 @@ async function challenge(route: Route, receiverAddress: `0x${string}`): Promise<
price: PRICE_ATOMIC[route],
payTo: receiverAddress,
scheme,
- networks: MAINNET_NETWORKS,
+ networks: OFFERED_NETWORKS,
maxTimeoutSeconds: MAX_TIMEOUT_SECONDS,
// One bazaar block per route: the block names the method and the query parameters, and the
// two routes sell different things at different prices. (Copied.)
@@ -362,7 +496,10 @@ async function challenge(route: Route, receiverAddress: `0x${string}`): Promise<
tags: ["search", "web", "x402"],
iconUrl: `${openapiSpec.servers[0].url}/favicon.png`,
});
- return toResponse(create402Response(paymentRequirements));
+ // Append the exact alternative AFTER the batch entries — order is the contract (see
+ // withExactAlternative): channel buyers keep their preferred entries, channel-less
+ // clients fall through to the exact one.
+ return toResponse(create402Response(withExactAlternative(paymentRequirements, receiverAddress)));
}
function isHexAddress(addr: unknown): addr is `0x${string}` {
diff --git a/bunny_js/src/x402_server.ts b/bunny_js/src/x402_server.ts
index 9155796fa..368521859 100644
--- a/bunny_js/src/x402_server.ts
+++ b/bunny_js/src/x402_server.ts
@@ -491,6 +491,63 @@ export async function createBatchSettlementPaymentRequirements({
};
}
+/**
+ * The exact-scheme alternative the sellers append to their batch-settlement 402s.
+ *
+ * Why it exists: a stock x402 client can sign an EIP-3009 authorization but cannot open
+ * batch-settlement channels — for those callers the channel-only challenge was unpayable.
+ * Terms are deliberately the flattest possible: one network (Base mainnet), one token
+ * (USDC), one price per call (10000 atomic = $0.01).
+ *
+ * The price IS the facilitator's per-settle fee (0.01), so a settled exact call nets the
+ * seller zero minus the upstream cost — an accepted trade for the simplicity it buys
+ * (decided 2026-11). It also means an exact price below $0.01 would lose money on every
+ * call, which is why /fetch quotes 10000 under exact despite quoting 1000 under
+ * batch-settlement — the two schemes price independently.
+ */
+export const EXACT_ALTERNATIVE_NETWORK = "eip155:8453" as const;
+export const EXACT_ALTERNATIVE_AMOUNT_ATOMIC = "10000";
+
+/**
+ * The one accepts entry the exact alternative consists of — also the per-call requirements
+ * the paid path verifies and settles against (single source; see genimg_x402_token.ts for
+ * the production exact flow this mirrors).
+ */
+export function createExactAlternative(payTo: string): SdkPaymentRequirements {
+ const usdc = offeredStablecoins(EXACT_ALTERNATIVE_NETWORK).find((coin) => coin.symbol === "USDC");
+ if (!usdc) {
+ // Config error (chain-utils' token list changed), not a caller error — throws into the
+ // 402 path's existing try/catch and surfaces as the same "Server configuration error".
+ throw new Error("USDC is not offered on the exact-alternative network");
+ }
+ return {
+ scheme: "exact",
+ network: EXACT_ALTERNATIVE_NETWORK as `${string}:${string}`,
+ amount: EXACT_ALTERNATIVE_AMOUNT_ATOMIC,
+ asset: usdc.address,
+ payTo,
+ maxTimeoutSeconds: 60,
+ extra: tokenExtra(usdc),
+ };
+}
+
+/**
+ * Appends the exact alternative AFTER the batch-settlement entries — the order is the
+ * contract: existing channel buyers read accepts[0..n-1] as the preferred (EURe-first)
+ * way to pay, and a client that cannot wire batch-settlement falls through to the exact
+ * entry at the end. The extensions stay the batch builder's own (Permit2/bazaar).
+ */
+export function withExactAlternative<
+ T extends {
+ x402Version: number;
+ resource: { url: string };
+ accepts: unknown[];
+ extensions?: Record;
+ },
+>(challenge: T, payTo: string): T {
+ return { ...challenge, accepts: [...challenge.accepts, createExactAlternative(payTo)] };
+}
+
export interface PaymentRequirementsOptions {
resourceUrl: string;
description: string;
@@ -687,6 +744,22 @@ export function formatUsdcAtomicAsDecimalUsd(atomicAmount: string, decimals: big
return trimmedFraction ? `${whole}.${trimmedFraction}` : whole.toString();
}
+/**
+ * A PriceList as the root pages display it: every accepted token priced in its own units, in
+ * the seller's preference order (EURe, EURC, USDC on Base — the same order the 402 lists),
+ * € for the euro tokens and $ for USDC, decimals read from the token registry so EURe's
+ * 18-decimal prices format correctly. Pages built with this can never quote fewer tokens
+ * (or a different order) than the challenge does.
+ */
+export function formatParallelPrices(price: PriceList): string {
+ return offeredStablecoins("eip155:8453")
+ .map((coin) => {
+ const display = formatUsdcAtomicAsDecimalUsd(price[coin.symbol], BigInt(coin.decimals));
+ return `${coin.symbol === "USDC" ? "$" : "€"}${display} in ${coin.symbol}`;
+ })
+ .join(", ");
+}
+
export function createSettlementHeaders(
settlementResult: Record,
): Record {
diff --git a/bunny_js/test/llm.test.ts b/bunny_js/test/llm.test.ts
index 046f63051..66f7e0cc3 100644
--- a/bunny_js/test/llm.test.ts
+++ b/bunny_js/test/llm.test.ts
@@ -8,6 +8,9 @@ const {
mockCreateLLMResourceServer,
mockCreateBatchSettlementPaymentRequirements,
mockCreate402Response,
+ mockCreateResourceServer,
+ mockExactVerifyPayment,
+ mockExactSettlePayment,
mockExtractPaymentPayload,
mockCreateSettlementHeaders,
mockGetBatchSettlementNetworks,
@@ -63,6 +66,10 @@ const {
mockCreateLLMResourceServer: vi.fn(),
mockCreateBatchSettlementPaymentRequirements: vi.fn(),
mockCreate402Response: vi.fn(),
+ // The exact alternative's server (createResourceServer — no channels, no scheme object).
+ mockCreateResourceServer: vi.fn(),
+ mockExactVerifyPayment: vi.fn(),
+ mockExactSettlePayment: vi.fn(),
mockExtractPaymentPayload: vi.fn(),
mockCreateSettlementHeaders: vi.fn(),
mockGetBatchSettlementNetworks: vi.fn(),
@@ -89,28 +96,65 @@ vi.mock("../src/llm_service.js", () => ({
advertisedModelIds: () => ["mistral-large-latest"],
}));
-vi.mock("../src/x402_server.js", () => ({
- createLLMResourceServer: mockCreateLLMResourceServer,
- createBatchSettlementPaymentRequirements: mockCreateBatchSettlementPaymentRequirements,
- create402Response: mockCreate402Response,
- extractPaymentPayload: mockExtractPaymentPayload,
- createSettlementHeaders: mockCreateSettlementHeaders,
- getBatchSettlementNetworks: mockGetBatchSettlementNetworks,
- releaseLock: mockReleaseLock,
- // Real value (the scw-verbatim advertised maxTimeoutSeconds / lock TTL), imported by
- // llm_routes.ts — see the constant's twin of this comment there.
- LLM_MAX_TIMEOUT_SECONDS: 120,
- // Real implementation (pure bigint math, no side effects) — keep in sync with
- // x402_server.ts's actual formatUsdcAtomicAsDecimalUsd.
- formatUsdcAtomicAsDecimalUsd: (atomicAmount: string) => {
- const atomic = BigInt(atomicAmount);
- const divisor = 1_000_000n;
- const whole = atomic / divisor;
- const fraction = (atomic % divisor).toString().padStart(6, "0");
- const trimmedFraction = fraction.replace(/0+$/, "");
- return trimmedFraction ? `${whole}.${trimmedFraction}` : whole.toString();
- },
-}));
+vi.mock("../src/x402_server.js", async () => {
+ // The real registry — which asset is offered where is part of what these tests check, so
+ // the exact-alternative helpers mirror the real implementation against it.
+ const { USDC_ADDRESSES } = await import("@fretchen/chain-utils");
+ const exactAlternativeEntry = (payTo: string) => ({
+ scheme: "exact",
+ network: "eip155:8453",
+ amount: "10000",
+ asset: USDC_ADDRESSES["eip155:8453"],
+ payTo,
+ maxTimeoutSeconds: 60,
+ extra: { name: "USD Coin", version: "2" },
+ });
+ return {
+ createLLMResourceServer: mockCreateLLMResourceServer,
+ createBatchSettlementPaymentRequirements: mockCreateBatchSettlementPaymentRequirements,
+ create402Response: mockCreate402Response,
+ extractPaymentPayload: mockExtractPaymentPayload,
+ createSettlementHeaders: mockCreateSettlementHeaders,
+ getBatchSettlementNetworks: mockGetBatchSettlementNetworks,
+ releaseLock: mockReleaseLock,
+ // Real values, as the routes import them.
+ EXACT_ALTERNATIVE_NETWORK: "eip155:8453",
+ EXACT_ALTERNATIVE_AMOUNT_ATOMIC: "10000",
+ // Real semantics for the pure helpers — the tests below pin the entry they produce.
+ createExactAlternative: exactAlternativeEntry,
+ withExactAlternative: (challenge: { accepts: unknown[] }, payTo: string) => ({
+ ...challenge,
+ accepts: [...(challenge.accepts ?? []), exactAlternativeEntry(payTo)],
+ }),
+ createResourceServer: mockCreateResourceServer,
+ // Real value (the scw-verbatim advertised maxTimeoutSeconds / lock TTL), imported by
+ // llm_routes.ts — see the constant's twin of this comment there.
+ LLM_MAX_TIMEOUT_SECONDS: 120,
+ // Real implementation (pure bigint math, no side effects) — keep in sync with
+ // x402_server.ts's actual formatUsdcAtomicAsDecimalUsd.
+ formatUsdcAtomicAsDecimalUsd: (atomicAmount: string) => {
+ const atomic = BigInt(atomicAmount);
+ const divisor = 1_000_000n;
+ const whole = atomic / divisor;
+ const fraction = (atomic % divisor).toString().padStart(6, "0");
+ const trimmedFraction = fraction.replace(/0+$/, "");
+ return trimmedFraction ? `${whole}.${trimmedFraction}` : whole.toString();
+ },
+ // Real semantics, EURe-first as the production preference orders it: every token priced
+ // in its own units, so the page can never quote fewer tokens than the challenge.
+ formatParallelPrices: (price: { EURe: string; EURC: string; USDC: string }) => {
+ const fmt = (atomic: string, decimals: bigint, symbol: "EURe" | "EURC" | "USDC") =>
+ `${symbol === "USDC" ? "$" : "€"}${(() => {
+ const a = BigInt(atomic);
+ const div = 10n ** decimals;
+ const whole = a / div;
+ const frac = (a % div).toString().padStart(Number(decimals), "0").replace(/0+$/, "");
+ return frac ? `${whole}.${frac}` : whole.toString();
+ })()} in ${symbol}`;
+ return `${fmt(price.EURe, 18n, "EURe")}, ${fmt(price.EURC, 6n, "EURC")}, ${fmt(price.USDC, 6n, "USDC")}`;
+ },
+ };
+});
// The real stablecoin registry — which asset a seller prices is the thing under test here.
vi.mock("@fretchen/chain-utils", async (importOriginal) => ({
@@ -240,6 +284,18 @@ describe("sc_llm_x402", () => {
transaction: "",
network: "eip155:84532",
});
+ // The exact alternative's server: same shapes, its own mocks, so the two schemes' call
+ // paths can be asserted apart.
+ mockCreateResourceServer.mockImplementation(() => ({
+ verifyPayment: mockExactVerifyPayment,
+ settlePayment: mockExactSettlePayment,
+ }));
+ mockExactVerifyPayment.mockResolvedValue({ isValid: true, payer: "0xpayer" });
+ mockExactSettlePayment.mockResolvedValue({
+ success: true,
+ transaction: "",
+ network: "eip155:8453",
+ });
});
describe("basic request validation", () => {
@@ -375,11 +431,11 @@ describe("sc_llm_x402", () => {
it("serves the favicon as raw bytes on GET /favicon.png", async () => {
const res = await handleLLMRequest(makeEvent({ httpMethod: "GET", path: "/favicon.png" }));
expect(res.status).toBe(200);
- expect(res.headers.get("content-type")).toBe("image/jpeg");
+ expect(res.headers.get("content-type")).toBe("image/png");
const bytes = new Uint8Array(await res.arrayBuffer());
expect(bytes.length).toBeGreaterThan(0);
- expect(bytes[0]).toBe(0xff);
- expect(bytes[1]).toBe(0xd8);
+ expect(bytes[0]).toBe(0x89);
+ expect(bytes[1]).toBe(0x50);
});
it("answers a HEAD /favicon.png probe with 200 and an image content-type", async () => {
@@ -388,7 +444,7 @@ describe("sc_llm_x402", () => {
{},
);
expect(res.statusCode).toBe(200);
- expect(res.headers["content-type"]).toBe("image/jpeg");
+ expect(res.headers["content-type"]).toBe("image/png");
expect(res.body).toBe("");
});
@@ -1403,4 +1459,120 @@ describe("sc_llm_x402", () => {
expect(mockVerifyPayment).not.toHaveBeenCalled();
});
});
+
+ describe("the exact alternative", () => {
+ const exactPayload = {
+ accepted: {
+ scheme: "exact",
+ network: "eip155:8453",
+ asset: USDC_ADDRESSES["eip155:8453"],
+ },
+ payload: { transferWithAuthorization: "0xsigned" },
+ };
+
+ /** One accepts entry appended AFTER the batch-settlement ones — the ordering is the
+ * contract (channel buyers read their entries first, channel-less clients fall
+ * through); the mock's batch accepts are empty, so appended-last also means
+ * index-0-of-one here. */
+ it("appends the exact alternative after the batch-settlement accepts", async () => {
+ const res = await handle(makeEvent() as never, {});
+
+ expect(res.statusCode).toBe(402);
+ const challenge = (mockCreate402Response.mock.calls[0] as unknown[])[0] as {
+ accepts: Record[];
+ };
+ const exactEntries = challenge.accepts.filter((entry) => entry["scheme"] === "exact");
+ expect(exactEntries).toHaveLength(1);
+ expect(challenge.accepts[challenge.accepts.length - 1]).toEqual({
+ scheme: "exact",
+ network: "eip155:8453",
+ amount: "10000",
+ asset: USDC_ADDRESSES["eip155:8453"],
+ payTo: VALID_ADDRESS,
+ maxTimeoutSeconds: 60,
+ extra: { name: "USD Coin", version: "2" },
+ });
+ });
+
+ it("serves and settles an exact payment at the flat price, metering bypassed", async () => {
+ mockExtractPaymentPayload.mockReturnValue(exactPayload);
+
+ const res = await handle(makeEvent() as never, {});
+
+ expect(res.statusCode).toBe(200);
+ // The exact server, for both payment steps; the batch pipeline never runs.
+ expect(mockCreateResourceServer).toHaveBeenCalledOnce();
+ expect(mockExactVerifyPayment).toHaveBeenCalledTimes(1);
+ expect(mockExactVerifyPayment).toHaveBeenCalledWith(
+ exactPayload,
+ expect.objectContaining({
+ scheme: "exact",
+ network: "eip155:8453",
+ amount: "10000",
+ asset: USDC_ADDRESSES["eip155:8453"],
+ payTo: VALID_ADDRESS,
+ }),
+ );
+ // Flat price: the settle amount is exactly what was verified, NOT the usage-derived
+ // getSettleAmount of the batch path.
+ expect(mockExactSettlePayment).toHaveBeenCalledTimes(1);
+ expect((mockExactSettlePayment.mock.calls[0] as unknown[])[1]).toEqual(
+ expect.objectContaining({ scheme: "exact", amount: "10000" }),
+ );
+ expect(res.headers?.["Payment-Response"] ?? res.headers?.["payment-response"]).toBe(
+ "encoded",
+ );
+ expect(mockCreateLLMResourceServer).not.toHaveBeenCalled();
+ expect(mockEnhancePaymentRequirements).not.toHaveBeenCalled();
+ expect(mockVerifyPayment).not.toHaveBeenCalled();
+ expect(mockSettlePayment).not.toHaveBeenCalled();
+ // Mistral really ran (mainnet offer — no mock completion unless asked).
+ expect(mockCallLLMAPI).toHaveBeenCalledTimes(1);
+ expect(mockCallLLMAPI.mock.calls[0][1]).toBe(false); // useMock
+ });
+
+ it("rejects an exact payment naming a network it never offered", async () => {
+ mockExtractPaymentPayload.mockReturnValue({
+ accepted: {
+ scheme: "exact",
+ network: "eip155:84532", // Base Sepolia — batch-settlement testnet, never exact
+ asset: USDC_ADDRESSES["eip155:84532"],
+ },
+ payload: { transferWithAuthorization: "0xsigned" },
+ });
+
+ const res = await handle(makeEvent() as never, {});
+
+ expect(res.statusCode).toBe(402);
+ expect(mockExactVerifyPayment).not.toHaveBeenCalled();
+ expect(mockExactSettlePayment).not.toHaveBeenCalled();
+ expect(mockCallLLMAPI).not.toHaveBeenCalled();
+ });
+
+ it("answers an invalid exact payment with a plain 402, not the batch corrective", async () => {
+ mockExtractPaymentPayload.mockReturnValue(exactPayload);
+ mockExactVerifyPayment.mockResolvedValue({ isValid: false, invalidReason: "expired" });
+
+ const res = await handle(makeEvent() as never, {});
+
+ expect(res.statusCode).toBe(402);
+ expect(JSON.parse(res.body).error).toMatch(/expired/);
+ // No channel state to resync — no SDK corrective re-challenge.
+ expect(mockCreatePaymentRequiredResponse).not.toHaveBeenCalled();
+ expect(mockExactSettlePayment).not.toHaveBeenCalled();
+ expect(mockCallLLMAPI).not.toHaveBeenCalled();
+ });
+
+ it("does not settle an exact payment when the model call fails", async () => {
+ mockExtractPaymentPayload.mockReturnValue(exactPayload);
+ mockCallLLMAPI.mockRejectedValue(new Error("mistral down"));
+
+ const res = await handle(makeEvent() as never, {});
+
+ expect(res.statusCode).toBe(500);
+ expect(mockExactSettlePayment).not.toHaveBeenCalled();
+ // Exact holds no lock, so the batch path's lock release must not run either.
+ expect(mockReleaseLock).not.toHaveBeenCalled();
+ });
+ });
});
diff --git a/bunny_js/test/mcp.test.ts b/bunny_js/test/mcp.test.ts
index 2fc276c9d..133cb827b 100644
--- a/bunny_js/test/mcp.test.ts
+++ b/bunny_js/test/mcp.test.ts
@@ -231,6 +231,10 @@ describe("mcp_routes (stateless paid MCP endpoint)", () => {
// the free first look: initialize and tools/list respond unpaid.
expect(body).toContain("https://llm-agent.fretchen.eu/");
expect(body).toContain("https://web-agent.fretchen.eu/");
+ // /fetch is REST-only (no MCP tool) — the page must still name the seller's
+ // most-called route and where it lives, or an MCP-only agent never finds it.
+ expect(body).toContain("GET /fetch");
+ expect(body).toContain("single-page fetch");
expect(body).toContain(".well-known/agent-registration.json");
expect(body).toContain("https://mcp-agent.fretchen.eu/mcp");
expect(body).toContain("tools/list");
@@ -339,7 +343,7 @@ describe("mcp_routes (stateless paid MCP endpoint)", () => {
// ════════════════════════════════════════════════════════════════════════════
describe("tools/call — the payment gate (no free path, by the real handlers)", () => {
- it("answers an unpaid web_search with the real mainnet-only demands and never touches Brave", async () => {
+ it("answers an unpaid web_search with the real demands and never touches Brave", async () => {
const res = await rpc({
jsonrpc: "2.0",
id: 3,
@@ -355,11 +359,11 @@ describe("mcp_routes (stateless paid MCP endpoint)", () => {
expect(result.isError).toBe(true);
expect(result.structuredContent.x402Version).toBe(2);
expect(result.structuredContent.accepts.length).toBeGreaterThan(0);
- // The security pin, on the real challenge path: search is mainnet-only — testnet
- // coins must never buy metered Brave queries. A translation layer that minted its
- // own accepts broke this once; it cannot anymore.
+ // Mainnets plus Base Sepolia — testnet is offered because a testnet payment buys the
+ // MARKED STAND-IN, never metered Brave. A translation layer that minted its own
+ // accepts broke the network pin once; it cannot anymore.
for (const accept of result.structuredContent.accepts) {
- expect(["eip155:10", "eip155:8453"]).toContain(accept.network);
+ expect(["eip155:10", "eip155:8453", "eip155:84532"]).toContain(accept.network);
}
expect(mockSearchWeb).not.toHaveBeenCalled();
});
@@ -425,7 +429,7 @@ describe("mcp_routes (stateless paid MCP endpoint)", () => {
expect(result._meta["x402/payment-response"]).toMatchObject({ success: true });
});
- it("rejects a paid web_search on testnet with the handler's own network refusal", async () => {
+ it("runs and settles a paid web_search on testnet via the handler's own stand-in rule", async () => {
const res = await rpc(
{
jsonrpc: "2.0",
@@ -437,15 +441,21 @@ describe("mcp_routes (stateless paid MCP endpoint)", () => {
);
if ("error" in res)
throw new Error(`expected result, got: ${JSON.stringify(res).slice(0, 300)}`);
+ expect(mockVerifyPayment).toHaveBeenCalledTimes(1);
+ expect(mockSettlePayment).toHaveBeenCalledTimes(1);
+ // The REAL testnet-stand-in rule, exercised through MCP: a worthless-coin payment
+ // must buy the marked mock answer, never metered Brave.
+ expect(mockSearchWeb).not.toHaveBeenCalled();
const result = res.result as {
- isError: boolean;
- structuredContent: { error: string };
+ isError?: boolean;
+ structuredContent: { results: Array<{ title: string; text: string }> };
+ _meta: Record;
};
- expect(result.isError).toBe(true);
- expect(result.structuredContent.error).toContain("Unsupported or missing network");
- // Refused before anything metered runs.
- expect(mockSearchWeb).not.toHaveBeenCalled();
- expect(mockVerifyPayment).not.toHaveBeenCalled();
+ expect(result.isError).not.toBe(true);
+ expect(result.structuredContent.results[0].title).toContain("testnet mock");
+ expect(result.structuredContent.results[0].text).toContain("testnet mock");
+ // The settlement receipt, decoded from the handler's Payment-Response header.
+ expect(result._meta["x402/payment-response"]).toMatchObject({ success: true });
});
it("runs and settles a paid web_search on mainnet and returns the receipt", async () => {
diff --git a/bunny_js/test/routes.test.ts b/bunny_js/test/routes.test.ts
index bb8972b9c..8b38d0838 100644
--- a/bunny_js/test/routes.test.ts
+++ b/bunny_js/test/routes.test.ts
@@ -7,6 +7,7 @@ import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
// being asserted is that even a CORRECT signature is ignored.
import { privateKeyToAccount } from "viem/accounts";
import { USDC_ADDRESSES, EURC_ADDRESSES } from "@fretchen/chain-utils";
+import { TESTNET_MOCK_MARKER } from "../src/search_stand_in.js";
// DNS is stubbed so these stay hermetic: /fetch resolves every hostname before connecting, and a
// test that needs a working resolver fails on an offline machine for reasons that have nothing to
@@ -29,6 +30,9 @@ const {
mockCreatePaymentRequiredResponse,
mockEnhancePaymentRequirements,
mockReleaseLock,
+ mockCreateResourceServer,
+ mockExactVerifyPayment,
+ mockExactSettlePayment,
} = vi.hoisted(() => ({
mockCreateLLMResourceServer: vi.fn(),
mockCreateBatchSettlementPaymentRequirements: vi.fn(),
@@ -40,16 +44,69 @@ const {
mockCreatePaymentRequiredResponse: vi.fn(),
mockEnhancePaymentRequirements: vi.fn(),
mockReleaseLock: vi.fn(),
+ // The exact alternative's server (createResourceServer — no channels, no scheme object).
+ mockCreateResourceServer: vi.fn(),
+ mockExactVerifyPayment: vi.fn(),
+ mockExactSettlePayment: vi.fn(),
}));
-vi.mock("../src/x402_server.js", () => ({
- createLLMResourceServer: mockCreateLLMResourceServer,
- createBatchSettlementPaymentRequirements: mockCreateBatchSettlementPaymentRequirements,
- create402Response: mockCreate402Response,
- extractPaymentPayload: mockExtractPaymentPayload,
- createSettlementHeaders: mockCreateSettlementHeaders,
- releaseLock: mockReleaseLock,
-}));
+vi.mock("../src/x402_server.js", async () => {
+ // The real registry — which asset is offered where is part of what these tests check, so
+ // the exact-alternative helpers mirror the real implementation against it.
+ const { USDC_ADDRESSES } = await import("@fretchen/chain-utils");
+ const exactAlternativeEntry = (payTo: string) => ({
+ scheme: "exact",
+ network: "eip155:8453",
+ amount: "10000",
+ asset: USDC_ADDRESSES["eip155:8453"],
+ payTo,
+ maxTimeoutSeconds: 60,
+ extra: { name: "USD Coin", version: "2" },
+ });
+ return {
+ createLLMResourceServer: mockCreateLLMResourceServer,
+ createBatchSettlementPaymentRequirements: mockCreateBatchSettlementPaymentRequirements,
+ create402Response: mockCreate402Response,
+ extractPaymentPayload: mockExtractPaymentPayload,
+ createSettlementHeaders: mockCreateSettlementHeaders,
+ releaseLock: mockReleaseLock,
+ // Real values, as the routes import them.
+ EXACT_ALTERNATIVE_NETWORK: "eip155:8453",
+ EXACT_ALTERNATIVE_AMOUNT_ATOMIC: "10000",
+ // Real value (x402_server.ts's batch networks — mainnets plus the testnet mock path).
+ getBatchSettlementNetworks: () => ["eip155:10", "eip155:8453", "eip155:84532"],
+ // Real implementation (pure bigint math, no side effects) — keep in sync with
+ // x402_server.ts's actual formatUsdcAtomicAsDecimalUsd.
+ formatUsdcAtomicAsDecimalUsd: (atomicAmount: string) => {
+ const atomic = BigInt(atomicAmount);
+ const divisor = 1_000_000n;
+ const whole = atomic / divisor;
+ const fraction = (atomic % divisor).toString().padStart(6, "0");
+ const trimmedFraction = fraction.replace(/0+$/, "");
+ return trimmedFraction ? `${whole}.${trimmedFraction}` : whole.toString();
+ },
+ // Real semantics, EURe-first as the production preference orders it: every token priced
+ // in its own units, so the page can never quote fewer tokens than the challenge.
+ formatParallelPrices: (price: { EURe: string; EURC: string; USDC: string }) => {
+ const fmt = (atomic: string, decimals: bigint, symbol: "EURe" | "EURC" | "USDC") =>
+ `${symbol === "USDC" ? "$" : "€"}${(() => {
+ const a = BigInt(atomic);
+ const div = 10n ** decimals;
+ const whole = a / div;
+ const frac = (a % div).toString().padStart(Number(decimals), "0").replace(/0+$/, "");
+ return frac ? `${whole}.${frac}` : whole.toString();
+ })()} in ${symbol}`;
+ return `${fmt(price.EURe, 18n, "EURe")}, ${fmt(price.EURC, 6n, "EURC")}, ${fmt(price.USDC, 6n, "USDC")}`;
+ },
+ // Real semantics for the pure helpers — the tests below pin the entry they produce.
+ createExactAlternative: exactAlternativeEntry,
+ withExactAlternative: (challenge: { accepts: unknown[] }, payTo: string) => ({
+ ...challenge,
+ accepts: [...(challenge.accepts ?? []), exactAlternativeEntry(payTo)],
+ }),
+ createResourceServer: mockCreateResourceServer,
+ };
+});
/** The seller's own address — `NFT_WALLET_PUBLIC_KEY`, shared with the chat so both land on one
* payment channel. Anvil account #2, standing in for it. */
@@ -180,6 +237,18 @@ beforeEach(async () => {
mockExtractPaymentPayload.mockReturnValue(null);
mockVerifyPayment.mockResolvedValue({ isValid: true, payer: "0xpayer" });
mockSettlePayment.mockResolvedValue({ success: true, transaction: "", network: "eip155:10" });
+ // The exact alternative's server: same shapes, its own mocks, so the two schemes' call
+ // paths can be asserted apart.
+ mockCreateResourceServer.mockImplementation(() => ({
+ verifyPayment: mockExactVerifyPayment,
+ settlePayment: mockExactSettlePayment,
+ }));
+ mockExactVerifyPayment.mockResolvedValue({ isValid: true, payer: "0xpayer" });
+ mockExactSettlePayment.mockResolvedValue({
+ success: true,
+ transaction: "",
+ network: "eip155:8453",
+ });
mockCreatePaymentRequiredResponse.mockResolvedValue({
x402Version: 2,
error: "channel_busy",
@@ -509,7 +578,7 @@ describe("discovery", () => {
const res = await handleRequest(discoveryRequest("GET", "favicon.png"));
expect(res.status).toBe(200);
- expect(res.headers.get("content-type")).toBe("image/jpeg");
+ expect(res.headers.get("content-type")).toBe("image/png");
expect((await res.arrayBuffer()).byteLength).toBeGreaterThan(0);
});
@@ -541,9 +610,15 @@ describe("discovery", () => {
expect(res.body).toContain(".well-known/agent-registration.json");
expect(res.body).toContain(".well-known/oasf.json");
expect(res.body).toContain("https://mcp-agent.fretchen.eu/mcp");
- // Prices restated on the page must be the spec's x-payment-info values.
- expect(res.body).toContain("GET /search — $0.01 per call");
- expect(res.body).toContain("GET /fetch — $0.001 per call");
+ // Prices on the page are generated from PRICE_ATOMIC — every accepted token in its
+ // own units, EURe first as the seller's preference orders it — plus the flat exact
+ // alternative; never a single-token restatement.
+ expect(res.body).toContain(
+ "GET /search — channel call: €0.01 in EURe, €0.01 in EURC, $0.01 in USDC; or exact: $0.01 flat (USDC, Base mainnet)",
+ );
+ expect(res.body).toContain(
+ "GET /fetch — channel call: €0.001 in EURe, €0.001 in EURC, $0.001 in USDC; or exact: $0.01 flat (USDC, Base mainnet)",
+ );
expect(res.body).toContain("curl 'https://web-agent.fretchen.eu/search?q=erc-8004'");
expect(res.body).toContain("402");
expect(mockCreateBatchSettlementPaymentRequirements).not.toHaveBeenCalled();
@@ -567,9 +642,15 @@ describe("the 402 challenge", () => {
])("quotes %s at %s atomic units", async (route, amount) => {
const res = await handle(makeRequest("GET", route, { auth: null }));
- const { accepts } = JSON.parse(res.body) as { accepts: { amount: string }[] };
+ const { accepts } = JSON.parse(res.body) as {
+ accepts: { amount: string; scheme?: string }[];
+ };
expect(res.statusCode).toBe(402);
- expect(accepts.every((entry) => entry.amount === amount)).toBe(true);
+ // The route's own price applies to every entry except the exact alternative (whose
+ // flat per-call price is pinned in its own test below).
+ const routeEntries = accepts.filter((entry) => entry.scheme !== "exact");
+ expect(routeEntries.length).toBeGreaterThan(0);
+ expect(routeEntries.every((entry) => entry.amount === amount)).toBe(true);
});
/**
@@ -588,12 +669,26 @@ describe("the 402 challenge", () => {
});
/** Testnet USDC is free and both routes spend real money, so a testnet payment would buy
- * metered Brave queries for nothing. */
- test("offers mainnet only", async () => {
+ * metered Brave queries for nothing. The exact alternative is Base MAINNET — it joins the
+ * mainnet-only fold rather than widening it. */
+ test("offers the batch networks: mainnets plus the testnet mock path", async () => {
const res = await handle(makeRequest("GET", "search", { auth: null }));
- const { accepts } = JSON.parse(res.body) as { accepts: { network: string }[] };
- expect(accepts.map((entry) => entry.network).sort()).toEqual(["eip155:10", "eip155:8453"]);
+ const { accepts } = JSON.parse(res.body) as { accepts: { network: string; scheme?: string }[] };
+ expect(
+ accepts
+ .filter((entry) => entry.scheme !== "exact")
+ .map((entry) => entry.network)
+ .sort(),
+ ).toEqual(["eip155:10", "eip155:8453", "eip155:84532"]);
+ expect(
+ accepts.every(
+ (entry) =>
+ entry.network === "eip155:10" ||
+ entry.network === "eip155:8453" ||
+ entry.network === "eip155:84532",
+ ),
+ ).toBe(true);
});
test("quotes the route, not the query string, as the resource", async () => {
@@ -816,7 +911,9 @@ describe("the paid path", () => {
});
test("refuses a payment on a network it does not sell on", async () => {
- mockExtractPaymentPayload.mockReturnValue(payment("eip155:84532"));
+ // Optimism Sepolia: in no network list here — not batch (no contract deployment there)
+ // and not the exact alternative (Base mainnet only). The refusal stays a plain 402.
+ mockExtractPaymentPayload.mockReturnValue(payment("eip155:11155420"));
const res = await handle(makeRequest("GET", "search", { auth: null, query: { q: "x402" } }));
@@ -859,3 +956,189 @@ describe("the paid path", () => {
expect(mockSettlePayment).toHaveBeenCalled();
});
});
+
+/**
+ * The exact alternative: the accepts entry appended AFTER the batch-settlement entries —
+ * one EIP-3009 signature per call, flat $0.01 USDC on Base mainnet, no channels. It exists
+ * for stock x402 clients that cannot open channels at all.
+ */
+describe("the exact alternative", () => {
+ const exactPayload = {
+ accepted: {
+ scheme: "exact",
+ network: "eip155:8453",
+ asset: USDC_ADDRESSES["eip155:8453"],
+ },
+ payload: { transferWithAuthorization: "0xsigned" },
+ };
+
+ /** One accepts entry, appended last, exact terms; the batch entries stay untouched ahead
+ * of it — the ordering IS the contract (channel buyers read their entries first). */
+ test("appends one exact entry after the batch-settlement entries", async () => {
+ for (const route of ["search", "fetch"] as const) {
+ const res = await handle(makeRequest("GET", route, { auth: null }));
+
+ const { accepts } = JSON.parse(res.body) as {
+ accepts: Record[];
+ };
+ const exactEntries = accepts.filter((entry) => entry["scheme"] === "exact");
+ expect(exactEntries).toHaveLength(1);
+ expect(accepts[accepts.length - 1]["scheme"]).toBe("exact");
+ expect(exactEntries[0]).toEqual({
+ scheme: "exact",
+ network: "eip155:8453",
+ amount: "10000",
+ asset: USDC_ADDRESSES["eip155:8453"],
+ payTo: RECEIVER,
+ maxTimeoutSeconds: 60,
+ extra: { name: "USD Coin", version: "2" },
+ });
+ }
+ });
+
+ test("serves and settles an exact payment without touching channel machinery", async () => {
+ mockExtractPaymentPayload.mockReturnValue(exactPayload);
+
+ const res = await handle(makeRequest("GET", "search", { auth: null, query: { q: "x402" } }));
+
+ expect(res.statusCode).toBe(200);
+ // The exact server, for both payment steps; the batch server is never built.
+ expect(mockCreateResourceServer).toHaveBeenCalledOnce();
+ expect(mockExactVerifyPayment).toHaveBeenCalledTimes(1);
+ expect(mockExactVerifyPayment).toHaveBeenCalledWith(
+ exactPayload,
+ expect.objectContaining({
+ scheme: "exact",
+ network: "eip155:8453",
+ amount: "10000",
+ asset: USDC_ADDRESSES["eip155:8453"],
+ payTo: RECEIVER,
+ maxTimeoutSeconds: 60,
+ }),
+ );
+ // Flat price: settle verifies and settles the same amount (no metering, no enhancement).
+ expect(mockExactSettlePayment).toHaveBeenCalledTimes(1);
+ expect((mockExactSettlePayment.mock.calls[0] as unknown[])[1]).toEqual(
+ expect.objectContaining({ scheme: "exact", amount: "10000" }),
+ );
+ expect(res.headers["payment-response"]).toBe("encoded");
+ // None of the channel pipeline may run.
+ expect(mockCreateLLMResourceServer).not.toHaveBeenCalled();
+ expect(mockEnhancePaymentRequirements).not.toHaveBeenCalled();
+ expect(mockVerifyPayment).not.toHaveBeenCalled();
+ expect(mockSettlePayment).not.toHaveBeenCalled();
+ expect(fetch).toHaveBeenCalledTimes(1); // the paid Brave call did run
+ });
+
+ test("rejects an exact payment on network terms it never offered", async () => {
+ mockExtractPaymentPayload.mockReturnValue({
+ accepted: {
+ scheme: "exact",
+ network: "eip155:10", // Optimism USDC — offered for batch-settlement, never for exact
+ asset: USDC_ADDRESSES["eip155:10"],
+ },
+ payload: { transferWithAuthorization: "0xsigned" },
+ });
+
+ const res = await handle(makeRequest("GET", "search", { auth: null, query: { q: "x402" } }));
+
+ expect(res.statusCode).toBe(402);
+ expect(mockExactVerifyPayment).not.toHaveBeenCalled();
+ expect(mockExactSettlePayment).not.toHaveBeenCalled();
+ expect(fetch).not.toHaveBeenCalled();
+ });
+
+ test("answers an invalid exact payment with a plain 402, not the batch corrective", async () => {
+ mockExtractPaymentPayload.mockReturnValue(exactPayload);
+ mockExactVerifyPayment.mockResolvedValue({ isValid: false, invalidReason: "expired" });
+
+ const res = await handle(makeRequest("GET", "search", { auth: null, query: { q: "x402" } }));
+
+ expect(res.statusCode).toBe(402);
+ expect(JSON.parse(res.body).error).toMatch(/expired/);
+ // No channel state to resync — so no SDK corrective and no advert of the failed payment.
+ expect(mockCreatePaymentRequiredResponse).not.toHaveBeenCalled();
+ expect(mockExactSettlePayment).not.toHaveBeenCalled();
+ expect(fetch).not.toHaveBeenCalled();
+ });
+
+ test("does not settle an exact payment when the upstream fails", async () => {
+ mockExtractPaymentPayload.mockReturnValue(exactPayload);
+ vi.stubGlobal(
+ "fetch",
+ vi.fn().mockResolvedValue({ ok: false, status: 502, statusText: "Bad Gateway" }),
+ );
+
+ const res = await handle(makeRequest("GET", "search", { auth: null, query: { q: "x402" } }));
+
+ expect(res.statusCode).toBe(500);
+ expect(mockExactSettlePayment).not.toHaveBeenCalled();
+ // Exact holds no lock, so the batch path's lock release must not run either.
+ expect(mockReleaseLock).not.toHaveBeenCalled();
+ });
+});
+
+/**
+ * The testnet stand-in: a paid request on Base Sepolia runs the FULL x402 machinery —
+ * challenge, validation, channel verify, settle-on-success, the Payment-Response header —
+ * but answers with the marked stand-in (search_stand_in.ts) instead of the metered
+ * upstreams. The invariant is one-sided and absolute: testnet money (free) must never
+ * buy Brave queries or /fetch egress; mainnet money must always get the real thing.
+ */
+describe("the testnet stand-in", () => {
+ beforeEach(() => {
+ mockExtractPaymentPayload.mockReturnValue(payment("eip155:84532"));
+ });
+
+ test("serves and settles a paid /search with the marked stand-in, upstream untouched", async () => {
+ const res = await handle(makeRequest("GET", "search", { auth: null, query: { q: "x402" } }));
+
+ expect(res.statusCode).toBe(200);
+ expect(mockVerifyPayment).toHaveBeenCalledTimes(1);
+ expect(mockSettlePayment).toHaveBeenCalledTimes(1);
+ expect(res.headers["payment-response"]).toBe("encoded");
+ // The security invariant: testnet money never reaches Brave.
+ expect(fetch).not.toHaveBeenCalled();
+
+ const body = JSON.parse(res.body) as {
+ results: { title: string; text: string; url: string }[];
+ };
+ expect(body.results).toHaveLength(3);
+ for (const result of body.results) {
+ expect(result.title).toContain(TESTNET_MOCK_MARKER);
+ expect(result.url).toContain("web-agent.fretchen.eu");
+ }
+ // Echo: the caller's own query arrives in the stand-in answer.
+ expect(body.results[0].title).toContain("x402");
+ });
+
+ test("serves and settles a paid /fetch with the marked stand-in, upstream untouched", async () => {
+ const res = await handle(
+ makeRequest("GET", "fetch", { auth: null, query: { url: "https://example.com/" } }),
+ );
+
+ expect(res.statusCode).toBe(200);
+ expect(mockVerifyPayment).toHaveBeenCalledTimes(1);
+ expect(mockSettlePayment).toHaveBeenCalledTimes(1);
+ expect(res.headers["payment-response"]).toBe("encoded");
+ // The security invariant: testnet money never triggers egress.
+ expect(fetch).not.toHaveBeenCalled();
+
+ const body = JSON.parse(res.body) as { finalUrl: string; html: string; contentType: string };
+ expect(body.finalUrl).toBe("https://example.com/");
+ expect(body.html).toContain(TESTNET_MOCK_MARKER);
+ expect(body.html).toContain("no real web"); // loud it is
+ });
+
+ /** The other side of the invariant, pinned where it is defined: the same caller, the same
+ * route, only the network flipped — and the metered upstream runs. */
+ test("flips back to the real upstream for a mainnet payment", async () => {
+ mockExtractPaymentPayload.mockReturnValue(payment("eip155:10"));
+
+ const res = await handle(makeRequest("GET", "search", { auth: null, query: { q: "x402" } }));
+
+ expect(res.statusCode).toBe(200);
+ expect(fetch).toHaveBeenCalledTimes(1); // the paid Brave call did run
+ expect(JSON.stringify(res.body)).not.toContain(TESTNET_MOCK_MARKER);
+ });
+});
diff --git a/scw_js/discovery.ts b/scw_js/discovery.ts
index af0e5fc48..f08885767 100644
--- a/scw_js/discovery.ts
+++ b/scw_js/discovery.ts
@@ -43,7 +43,7 @@ export function buildRootPage(input: RootPageInput): string {
-
+
${escapeHtml(input.title)}
diff --git a/scw_js/favicon.ts b/scw_js/favicon.ts
index 6693e3ff5..922b31605 100644
--- a/scw_js/favicon.ts
+++ b/scw_js/favicon.ts
@@ -1,3 +1,3 @@
export const faviconBase64 =
- "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAQABAADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDLopTSV9EeSJS0UUxBSUtFABRRRQAUUUUAJRS4ooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACkpaKAEopaKACilpKACiiigAoopcUAJRRRQAUUUUAFJS0UAFFFFACUUtFABRRRQAlFLRQAUUUUAFJS0UAFFFFABRRRQAlLRRQAUUUUAFFFFABRRRQAlFLRQAUUUUAFFFFABRRRQAlFLRQAUUUUAJS0lLQAUUUUAFFFFABRRRQAUUUUAFJS0UAJRS0UAFFFFACUtFFACUUtFACUtFFABRRRQAUUUUAJRS0UAJS0UUAFFFFABRRRQAUUUUAFJS0UAJRS0UAJRS0UAFFFKKQAaSlNFACUUUUwCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKWgBKKWkoAKKKKACiiigAooooAWkoooAKKKWgBKKKKAFpKWg0AJRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRS0AJRRRQAUUUtACUUGigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKWiigAooopABpKKKYBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFGaKKAFpKKKACiiigAooooAKKKWgApKWigBKKKKACilooAKQ0tJQAUUUUAFFFFABRRRQAUUUUAFFFLQAlFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFLQAlLRRQAlFFFABRS0lABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUtJQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUooAKSlNFIBKKKKYBRRRQAUUUUAFFFFABRRRQAUUUtABSUtJQAUUUUAFFFFABRRRQAUUUUAFFFFABRRS0AJRRRQAUUtJQAUUUtABRRRQAUUUUAFJS0lABRRS0AJS0UUAJRRRigAopaMUAJRS0UAJRRRQAUUUUAFFFFAAKWiigBKKKWgBKKKKACiiigAoopcUAFJS0lABRRS0AJRS0lABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAAKWikoAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigApaSloAKKKKQCUUUUwCiiigAooooAKKKKAClpKWgBKKKKACiiigAooooAKKKKACiiloASilpKACilooASilpDQAUUUUAFBopaAEopaKAEopaKAEopaSgBaSlooAKSlpKACiiigApaSloAKKSloAM0UlLQAUlLSUAFLSUtABSUtJQAUUUUAFFFFABRRRQMKKKKAFooooEFJS0lAC0UUUAFJS0lABRRRQAUtJRQAUUUUALSUtIaACiiigApaKKACkpaKAEooooAWikooAKKKKACiiigAooooAKKKKAClpKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKWkooAKKKKACiiigAooooAKKKWgBKWilFACUUUUgEooopgFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUtJRQAtJS0lABRRRQAZooooAKKKKACiiigApaSigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACilooASloooAKSlpKACiiigAooooAKKWigBKKWkoAWikpaACkpaSgAooooAKKKKACiiigAooooAKWkpaACkpaKAEooooGFLSUUCCiiigAooooAKKWigBKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooASloooAKKKKACiiigBaKSloAKKKKQCUUUUwCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKBhRRRQIKKWkoAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAopaKAEooooAWkoooAWkpaMUAJRS0UAJRRRQAtFJS0AFFFFACUUUUAFFLRQAlFFFABRRRQAUUUUAFFFFABS0lFAC0lLRQAlFFFABRRRQAUUUYoAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKWkNABRRRQAUUUUAFFFFABRRRQAUtJSigBKWiikAlFFFMAooooAKKKKACiiigAooooAKWkpaACiikoAWkpaSgAopcUlABS0lLQAlFFFABRRRQAUUUUAFFGKKACiiigAooooAKKKKACilooASloooASilooASiiigAoopaACiiigAopKKACilpKACiiigYtFJS0hBSGlooASiilpgJRRRQAUUUUAFFFFAC0UUlAC0lLSUAFFFFABRRRQAUUUUAFFFFAC0lFFABRRRQAUUUtACUtJRQAtJS0lABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUALSUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABS0lKKACiiikAlFLSUwCiiigAooooAKKKKACiiigAooooAKKKKACiiigBaKKKAEopaSgAooooAKKKKACiiigBaSiigAooooAKKKKACiiloAKKKKACiiigAooooASiiigAopaKAEoopaAEopaSgBaSiigAooooGFLSUtAgpKKKACjNFFABRRRQAUUUUAFFFFAC0GiigBKKKKACiiigAooooAKKWjFACUUUUAFFFFABRRRQMKKKKBBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRS0AFFFFIAopKKYBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAC0lFLQAlFLSUALmkoooAKKKKACiiigAooooAKWkooAWkpaKAEopaSgAooooAWiiigAooooAKKKKAEooooAWiiigBKWkooAWkpaSgAooooAKKWigBKKWkNABRRRQAUUUYoAKKWigBKWkpaACkoooAWkoooAKKKKACiiigAooooAWikooAWkpaSgAopaSgAooooAKKWkoAKKKKACiiigAoopaACkpaSgAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAClpKUUAFFFFIBKKKKYBRRRQAopKWkoAKKKKACiiigAooooAKKKKAClpKKACiiigAooooAKKKKACiiigAooooAKKKKAFopKXNABSUtJQAUUUUAFFFFABRRRQAUUUUAFFLSUAFFFFABRRRQAUUUUAFLRRQAUUUlAC0lLRQAlLRRQAUUUGgBKKKWgBKKKKACiiigYUUuKSgQUUUUAFFFFABRRRQAUUUUAFFBooAWkpaQ0AFFFFABRRRQAUUUUAFFFFABRRRQAUUtJQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAwpwpKKBBRRRSASiiimAUUUUAFFFFABRRRQAUUUUAFLSUtABSUUUAFFLRQAlFFFABRRRQAUUUUAFFFFABRRS0AFFFFACUUtJQAtJS0lABRRRQAUUUUALRSUtACUUUUAFFFFABRS0lABRRS0AJRRRQAtFFFAAaSlpKAFooooASilpKACiiloASilpKACilpKBhRRRQIWkoooAKWkooAKKKKAFopKWgAoopDQAUUUUALRSUtACUUtJQAUUUUDCiiigQUtFFABSUtJQAtJRRQAUUUUAFFLSUAFFFFABRRRQAUUUUAFFFFABRRS0AJRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUtJS0AFFFFIBKKWkpgFFFFABRRRQAUUtJQAUUUUAFFFFABRRS0AFFFJQAUUUUAFLSUUAFFFFABRS0UAJRS0hoAWikpaAEooooAUUUUUAFJS0UAJRSmkoAKKKKACiiigYUUUtAgooooAKKKKACiiigAooooAKSlooAKSlooASilpKACiiloASiiigBaSiigAopaKAEooooAKKKKAFpKWkoAKKKKAFpKKKAFoNFFACUtJRQAtJS0UAJRS0mKACilooASiiigBaSiigAooooAKKKKAFpDRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUtJS0AFFFFIBKKKWmAlFFFABRRRQAUUUtACUUUUAFFLSUAFLSUUALSUtJQAUUUUAFFFFABRRRQAUUUUAGaKKKAClpKKACiiigAopaKACiikoAKKKWgAooooAKKKKACiikNAC0UlLQAUUlFAC0lLRQAUUUUAFFFJQAtFFFABSUUUALRSZpc0AJRRRQAUtAooAKKKKACkpaQ0ALSUUUAFFFFABRRRQAUUUtABRRSUAFFFFABRRRQAtFJS0AIaKKKACiiloASiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigApaKKACiiikAUlFFMAooooAKKKKACilooASiiigAooooAWiiigAoNFFACUUUUAFFFFABQaKWgBKKKKACiiigAooooAKWkooAKKKKAFpKKKACiiigBaSlooAKQ0tJQAUUUUALSUUtACUUUUALRRRQAUlLRQAUlFFAC0lLRQAlFFFABRRRQAUUUUAFFFFAC0UUUAJRRRQAUUUUAFFFFABRRRQAUtJS0AFJS0UAJRS0lABRRRQAUtFIaACiiigAooooAKKKKACiiigApaSloASiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAFpKKKACiiigAooooAKKKKACiiigAooooAKWkooGLRRRQIKKKKQCUUUUwCiiigAooooAKKKKACiiigAoopaACikpaACg0UlABRRSigBKKWigBKKWkoAKKKKACiiigAooooAKKKKACiiigAooooAKKKWgAooooAKKKKAEopaSgAooooAKKKKACiiigBaKKKAEopaSgBRRSUUAFFLSUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFLRQAlFFFABRRRQMWkpaKBBSUtJQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUALSUUUAFFFFABRS0UAJRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUtJS0AKaSlNJSAKDRRTASiiigAooooAKKBRQAUUUUAFLRRQAUlLSUAFFFFABS0lFAC0UlFAC0lLQaAEooooAKKKKACiiigAooooAKKKKACiiloASlopKAClpKKAFopKKAFpKKWgBBS0UUAJRRRQAUUUUALRRRQAUlLSUAFFFFABRRRQAUUlFADiCMZBGelJV/UohFbaaQPv2oY/99tVCkndXGFFFFMQUUUUAFFFFABRRRQAtJRRQAUUUUALSUUtACUuaSigBaSiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKWigBKWkpaAA0UUUgCiiimAlFFLQAlFFFAC0lFFABRRRQAUUUUAFFFFABRRS0AFJS0lABRRRQAtFJS0AJRRS0AJRRRQAtJRRQAUUUUAFFFFABS0lLQAUlLRQAlFFFABRRS0AJS0UUAFFFJQAUUUUAFFFFAC0lFFABRRRQAUUUUAFFFSRxGVtoZAf9tgo/M0AR0YrSh0SefBF1YID/fu0H9a2NO8ET3s6q2q6YoPa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iigAooooAKKKKAFpKKKACiiigAooooAWkoooAKKKKACiiigBaSiloASiiigAooooAKKKKACiiigAooooAKKKKACiiigAopaSgAooooAKKKKACiiigAooooAKKKKACiiigAooooAKWkpaACkpaKQCUUUUwCiiigAooooAKKKKACilpKACiiigAooooAKKWigBKWkpaACg0UlABRRS0AJRS0lABRRRQAUUtFABSUtFACUUUtABSUUUDCiiigQUUUUALQaSigApaSjNABRRRQAUUUUAFFFFABRRRQAUUUUAFKBSYq3p9jNqV7HawDLuevYDuTSbsrsaVzT8NaEdYvN0gItYiDIf7x7KK9PjRY41RFCqowAOgFVtO0+HTLKO1gGEQcnux7k1YmlSCF5ZGCogLMx6AV5dWo6kjrhHlRT1bVIdJsXuZiMjhEzyzdhXlF9ezahdyXM7bpHOT7ew9qu+INak1rUDJysCfLEnoPU+5rJrtoUeRXe5z1J8zCiiiugzCiiigAooooAKKKKAOq8Ia/8AYbgWNy3+jyn5WJ+43+B/z3r0QHNeIivQ/CGv/bIBY3T5njHyMTy6/wCI/wA964cTRt7yOilPozT8Q6JHrNkVAC3CZMT+/ofY15ZLG8MjRyIUdSQynqD6V7XXI+MdBFxC2pWy/vYx+9UfxL6/UfyqcPWs+Vjqwvqjz+kzSmkr0DmFooooAKKKKACiiigAooooAKKKKACiiigAoopaAEopaSgAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigApaKKAEooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAFoopRQAlJS0lIYUUUUxBRRRQAUUtFACUUUUAFFFFABRRiloAKSiigBaKSloAKSlooASiiigAooooAWiiigBKKKKAFoopKAFoNFFACUtFFACUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUoFADkVnYKoJYnAA716d4a0JdIs98oBupRmQ/3f9kVj+DvD+Nup3Sf9cFI/8e/wrtOlefia13yo6KUOrFzXn/jHxAbqZtOtX/cRn96wP329PoP51t+Lde/s6zNrbvi6mGMjqi9z9fSvN8VWGo/bYVZ/ZQlFLRXcc4lFLilEbnorH8KLgNop/kyf883/AO+TS+RL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U7pWBEUQPLn/AA9TVzUtRt9LsZbu6cJHGMn1J7AepNeJa5rdxrupSXc/yqfljjzwi9hV4ah7SV3sTWqcqstytqOoXGqX0t3dOXlkOT6D0A9hVSiivZSSVkee3cKKKKYgooooAWikpc0AFJS5pKACiiigAooooAKKKKACiiigAooooAKKKKACiiigDT0a68m58pj8knH0PaujPIrilJByDyK62yuBc2iSdyOfr3rCpHW5rB9DndStvs14wAwjfMtU66PWrYS2nmAfNGc/h3rnK0hK6IkrMKWkpRVkigV2fgXTd002oyLwn7uP69z/AE/GuPijaWRI0Us7EKoHcmvXtMsU07T4bVMfu1wT6nufzrlxM7Rsa0o3dy52rgfHep+bcxadG3yxfPJ/vHoPy/nXb3t3HY2c1zKcJEpY/wCFeO3dw93dS3Ehy8jFj+NYYWF5cz6GtaVlYhooor0TlCiiigAooooAKKKKACiiigAooooAKKKKACiiigAoopaAEooooAKKKKACiilNACUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFLSUooAKKKKQCUUUUwCiiigAooooAKKKKAFopKWgApKWkoAKKWigBKKKKAFpKWigAooooAKSlFFABRSUtABSUtJQAUUUUALRSUtACUUUUAFFFFABRRRQAUUUooAmtbmS0uoriI4eNgy/UV7BZXUd9ZQ3UX3JUDD29q8aruvAup74JdOkPzR/vI8+h6j8/51yYqF48yNqMrOx1GpWKajp09pJjEi4B9D2P515BNC8ErxSKVdGKsD2Ir2mvPPG+m/ZtSW8RcR3A+b2cdfzH9aywtSz5WXWj1OUpRRSgV6BzGxoNvukknI+78q/XvW27BEZieAMmq+nW/wBnsIkIwxG4/U1DrM3lWDqD80hCj+tcz96RstInO3MxuLh5W6sajpKBXRsYnsXw+cP4ThA/hkcH/vquoJABJOAOprzb4e+IbSxhn069nWEM/mRO5wvTBGe3SrXjfxjAbN9M0yZZGlGJpozkKvdQfU/y/TyKlGUqzSR3wqJU0znPGfiQ65qXkwOfsUBIjx0c92/w9vrXMUlFepTgoR5UcUpOTuwooorQkKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAWkNFFABRRRQAtbOgz/NJAe43D+v9Kxans5zbXUcvYHn6VM1dDi7M62RQ6FSMgjBrj54jBM8Z/hYiuyHIB7Guf12DZcJMBw4wfqP8isabs7Gs1pcyaUUlOUFiABkmtzE6nwTpf2nUWvZBmO3+77uen5dfyr0TGKzNA03+zNGggYYkI3yf7x6/wCFaE8yW8Ek0hwkalmPsK8qtNzmdkFyxON8eaptSLTYzycSS49Ow/r+VcLVrUr2TUdRnu5PvSNnHoOw/KqtejShyRSOacrsKKKK1ICiiigAooooAKKKWgBKKKKACiiigAooooAKKKKACiiigAooooAKKWkoAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAoopRQMKKKKQhKKKKYBRRRQAUUUUAFFFGKACilooAKKSloAKKKSgAooooAWiiigBKKKKAClpKWgApKKKACiiigAooooAKKKKACiiloGJRS0UCEooooAKWkooAXNXNMvn07UYLpM5jbJHqO4/KqVKKTV1Yadnc9rhlSeFJY23I6hlPqDWdr+mDVNImgAzIBvjP+0On+H41k+CNT+06c1lIf3lufl91P+B/pXV15Mk6czsTUoniDAqxBGCOxq1p0H2m9jQj5c5b6Ctbxhpn2DWGlRcRXGXGOx7j+v403QLfbHJcEfe+Vf616XOnG6OTl96xs9q53XJvMuViHRB+proZGCIWJwAMmuOnlM07yH+Jiaikru5c3pYhopTSV0GQZooooAKKKKACiiigAooooAKKKWgBKKKKACiiigAooooAKKKKACiiigAooooAKKKKACkoooAWiiigAooooA6vS5/PsIyeoG0/hSarb+fYvgfMnzD8P8ms7QZ8SyQHow3D6/5/lW+QCMGuWXuyN1qjh66Hwhpv2/WFkdcw248xvc9h+f8AKsW9tzbXkkXQA8fSvS/Cel/2do6F1xNP+8f29B+X86qvU5YaEU43kb3auU8b6n9n05bJD+8uDlvZR/if611LsFUknAA5NeSa9qR1XVprgHMYOyMf7I6fn1/GuTDQ5p37G9WVlYzTSUUV6ZyBRRRQAUUUUAFFFFABS0lFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAwooooEFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUCgBaKKKQBSUtJTAKKKKACiiigApaKSgBaKSloASilpKACiiigAooooAWiiigBKKKKACiiigAopaTFABRRRQAUtJRQAUUUtACUtFFABRRRQAlFFFABRRRQAUUUtAGnoWonS9VhuMny87ZB6qev8Aj+FesKwZQwIIIyCK8VFek+D9T+3aSIHbMtt8h917H+n4VxYuGnMjooy6FvxJpY1TS2QD97Gd6f1H5VgRQLbxLEowFGBXbEZFctqUH2e6ZcfKeVrGlN25S5LW5h6zceTYsoPzSfKP61zFXtWuvtN6wU/u4/lX+pqhXoU1ZHNJ3YUUUVZIUUUUAFFFFABRRRQAUtJRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFACUUUUALRRRQAUUUUATWk5trmOUfwnJ+neuxQh1BByCMg1xFdLoV151v5DH54+nutY1Vpc0g+hqQaNFqWp27yLlYzucf3gOg/PFdqOBxWbpFt5Vv5pHzP0+laROBXnVZuTsdMYpanPeMdT+xaM0KNiW4+QY7L3P8AT8a8zrZ8Tan/AGnrErI2YYv3cf0HU/iaxq9ChDkgc1SV2JRRRW5mFFFFABRRRQAUUUUAFFFLQAlFFFABRRRQAUUUUAFFFFABRRRQMKKWkoEFFFFABRRRQAUUUUAFFFFABS0lLQAlFFFABRRRQAUUUUAFFFFABRRS0AFFFFIBKKKKYBRRRQAUUUUAFFFFABS0lFAC0lFFABRRS0AJRS0UAFJS0UAJRRRQAUUUUAFLmkooAKKKKACiiigApaSloAKSlooAKKKKAEooooAKKKKAFpKWkoAUVv8Ag+aePxBCkILLICsg/wBnufwrAr0HwPpnkWL37r+8n+VPZB/if5VhXklB3LppuR1lc/4sXbpDSx/65eFx6d66A1zesT+fcMmcog2j+tefSXvXOqex5tSVb1C2+y3jxgfL1X6VVr1k7o4mJRRRTAKKKKACiiigBaSlpKACiiigAooooAKKKKACiilxQAlFFFABRRRQAUUUUAJRS0UAFdL4X8IT+I1lmacW9tG23ft3Fm9AMj865qux8HeMINAt5bS7hkeB38xWjwSpxg8HtxWNdzUPc3NKai5e8ZnijwxN4cuYgZRNBMD5cm3ByOoIrBrpPGHigeI7qFYImitYM7A/3mJ6k+n0rm6dHn5Fz7inZS90KKKK1ICiiigAq9o5kGqwbMkbvm/3e/6VRrovD9qI42uWHzPwv0/z/KoqP3WVFanpSABAFGFAwPpWd4hlng0G7ktx+8CfkOhP4DJqbS7kT2oB+8nyn+lXZEWSNkdQysMEHuK8n4ZanZujxKitDW9PbTNVntiuFByh9VPT/PtWca9eLTV0cT0YUUUVQgooooAKKKKACiiigAooooAKKKKACiiigAopaSgAooooAKKKKACiiigAooooAKKKKBhRRRQIKWkooAKKKKBhRRRQIKKKKACiiigAoopaACiiigANFFFIBKKU0lMAooooAKKKKACiiigAooooAKKKKAClpKWgAoopKAClpKWgAooooASilpKACiiloASilpKACilpKACilpMUALRRRQAUUUUAJRRRQMKKKKACgUtKBQIt6bYvqOoQ2qdZGwT6Dufyr16CFLeCOGNdqIoVR6AVx/gbTNscuoSLyTsj/qf6fnXZ15uJnzS5TqoxsrlDW9RTS9KmuWxuA2oPVj0/x/CuYhmW4hWUdHGap+OtT+0X0djGf3cAy/ux/wABVXQLjdbNAx5jOR9D/k1pTpWhzEud5WJNctRJbeco+aPr9K5qu2lUSRMjDhgQa4qRGjlZG6qSDXRSeljOohtFFFbGYUUUUAFFFFABRS0lABRRRQAUUUtABSUtFABRRQaAEooooAKKKKACiiigBRQaVFLsFVSzMcAAZJNd1pPw1urq2WbULr7KWGREi7mA9znA/WsqlWNP4mXGDlscHRXba38Or3T4GuLGb7XGoyybcOB7DvXFcjg06dSNRXixShKO4lFFFaEhRS0lABRRQKAJIITPPHEvVmxXZRRiKJUUYVRgCsDQYN9y0x6IOPqf8muhPArnqvWxrBdSWx1dLDVreCRsJP8AKx9PQ/nXZZrxu+ufPvZJAeAcL9BXpvh3Uv7T0eGZjmVfkk+o/wARg1zV6VkpGtOd7oy/G2mfadOW9jX95bn5vdD/AIHH6155ivapolmheJ1DI6lWB7g15Fqlg+m6jNauD8jfKfVex/KtcLO65WRVjrco0UppK7DAKKKKAFFFJRQAUUUUALRRRQAlFFFABS0lLQAUlLSUAFFFFABRRRQAUUUUAFFFFABRRS0AJRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAtFFAoAKKKKQBSUUUwCiiigAooooAKKKKACiiigAooooAKWiigBKKDRQAUUUUALRRRQAUlFFABS0lLQ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JPAAptdr4N0Ddt1S6T5R/qFPf/a/wrOpNQV2VGLk7Gz4Z0IaVaebMo+1Sj5v9kf3f8a36MYrnfFeu/2XYmCB8XcwwuOqL3b/AA/+tXme9VmdekImN4x18TyNplq/7tD++YfxN/d+g/nXGmgkk5PNJXp04KEbI5JS5ncKKKK0JCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKWiigApKKKACiiigAooooAKKKKACiiigApaSigBaSiigAooooAKKKKACiiigAooooAKKKKACiiigAoopaAEooooAKKKKAClFJSigYUUtJSEJRRRTAKKKKACiiigAooooAKWkpaACiiigApKWigBKKKXFABRRRQAUlLRQAlFFFAC0UUUAJRRRQAtFFFABSUtJQAUUUUALRRRQAlLRRQAUUUUAFFFFACGilooASilxSUAFLSUtABSUtJQAUUUUAFFFFABRRRQB1HhDxB9guPsVy+LaU/Kx6I3+B/z3r0YHNeI16F4Q8Qfa4Rp9y37+Mfu2J++o7fUfy/GuHE0ftI6KU/ss19f0WPWbAxkATplon9D6fQ15ZPDJbyvFKhR0OGU9Qa9orlfF+gfbLc39sn+kRj94oH319fqP89qjD1uV8r2HVhfVHnlJQaK9E5gooooAKKKKAClpKu6Zp02qX0drCOWPLdlHcmk3ZXY0rmn4Y0A6veebMp+yRHLn++f7v+NenIqogVQFUDAAHSq9jZQ6fZx20C7Y0GB6k9yfc1NNNHBC8srBY0UszHsBXl1ajqSOuEeVFPWNUg0mwe5mPThF7s3YV5PfX02oXkl1O2ZJDk+g9APar3iHW5Nb1Aycrbx/LEh7D1Puaya7aFLkV3uc9SfMwoooroMwooooAKKKKACiiigAooooAKKWigBKKWigBKKWigBKKKKACiiigApaSloASiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigApRSUooAXNJRRSASig0UwCiiigAooooAKKKKACilooASiiigAooooAKWkpaACiiigApKWigAooooAKSiigApaTFLQAUUUlAC0lLSUAFLSUtABRRRQAlLRRQAUUUUAFFFFABRRRQAUlLRQAUUUUAFFFFACUUtJQAUUUUAFFFFABUkE0lvMk0TlJEIZWHY1HS0PXQD1fQNYj1iwWUYEyfLKnof8DWseRXkWkanNpN8lzFyBw6Z4Yeleq2V7Df2kdzA26Nxke3sfevLr0uSV1sddOfMjgvF3h/7DP9utk/0aQ/Oo/gb/A/57Vy1e1XFvFdW7wTIHjcbWU9xXlOu6PLo9+0LAmJuYn/ALy/4104atzLle5lVhZ3Rl0UUV1mIUopKUUASRxPLIqIpZmOAB1Jr0/w5oaaPZDcAbmQZlb09h7CsjwhoIijXUrlf3jD9ypHQf3v8K7GvPxNa/uo6aULasWuB8Y+IPtMjabav+5Q/vWH8bDt9B/OtrxXr39mWn2aB/8ASphwR1RfX/D/AOtXmxOTTw1G/vsVWp0Q2iiiu85wooooAKKKKACiiigApaKKAEpaKSgBaKSigAzS0lFAC0UlFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABS0lFABRRRQAUUUUAFFFFAwooooAKKKKBBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQMKWkpRQIKKKKQBSUUUwCiiigApaSloASilpKAFopKWgApKKKACiiigApaSigBaSlpKAFooooAKSlpKACiiigApaSigAooooAWkpaKAEpaSloAKKDSUALRRRQAUhpaSgAoopaACiiigAooooAKTNLRQAUUlFAC0lFFAC0UUlAC0UlLQAUUUUAKDXQ+F9eOl3fkTt/osp+Yn+A+v8AjXO0ucGonBTVmVF2d0e2AggEdDWbrekRaxYPbvhZB80b/wB1v8KwPBuv+co0y5f51H7lj3H938K7GvLlF05nWmpo8Wubaa0uHgnQpIhwwNRV6J4x0L7bam/t0/0iEfOAPvr/AIj/AD2rzzFelSqKcbnLOPKxK6XwroH9p3X2m4X/AESI8g/8tG9Pp61iafYT6nfR2sC5ZzyeyjuTXrllaRWNnFbQriONdo/xrLEVeVWW5VKF3dk4AAwBgVS1fU4tJ097mXkjhVHVm7CrrMFUsxAAGST2rzzxHq39raa86f6hL3y4uOqhOv4nJrjpQ55a7G85cqOdvbyW/vJLmdsySHJ9vYe1QUmaK9VKysjjCiiimAUUUUAFFFFABRRSmgBKKWkoAWkoooAWkopaAEopaSgAooooAKKKKACilooASilooASiiigAooooAKKKKACiig0ALSUUUAFFFGKACiiigAooooGFFFFAgooooAKKKKBhRS0UCEopaKAEooooAKKKKACiiigAoFFAoAWiiikAlFFFMAooooAKKKWgApKWkoAKKBS0AJRRRQAUUUUAFFFFABRS0UAFFFFABRRRQAUUUUAJRRRQAtFFFABRRRQAUlFFABRRRQAtFIKWgAooooAKKKKACiiigAooooAKKKKACkpaSgAopaKAEoNFFAC0UUUAFJS0lABRRRQA+KV4ZUkjYq6EMpHUEV6xoOrprOmJOMCVfllUdm/wPWvJK0dF1ifRb0TxfMh4kjJwHH+PvWFelzx03NKc+V6nrxGaxLvwjpF5cGZ4XRm6iJ9oP4Vb0zWbLVoRJbSgtj5o24ZfqK0M15t5QfY6rKRTsNJstMjK2kCx56t1Y/UmrmKXsSeAOp9Kx9U1u3it3js9R09bnpmWcYX34zk0kpSYNqKMHxr4h8hG0u1f94w/fMOw/u/4/wD66wfs2fAouCcAX4UD1+Sp4/Cct9MXOs6W0kjZZjd7iSe/Su+/4Q23PhBdGaf5gfN8/HHmdc49O30rq9pCkkkY8kp3Z49ilrprzwc9oxB1nSj7NcbT+VZE+kvDnN/pz4/uXIP9K7I1Iy2Zi4tbmfRQRtYjIOO4OaK0JCiiigAooooAKKKKBhRRRQIKKWkoAKKKKADNLSUtABSUUUAFFFFAC0UZooAKSlooAKSlpKAFpKKKACiiigAooooAKKKKAFopKM0AFFFFABRRRQAUUUUAFFLSUAFFFFABS0UhoAWikpaAEopaSgAooooAKKKKACloooAKKU9aSkAlFFFMAooooAKKKKAFpKKKAClpKKACiiigAoopaAEopaKACiiigApKKKAFopKWgApKWkoAKKWigBKKXFFACUUUUAFFFFAC0UUUAFFFFABSUtFABRRRQAUUUUAFFFFABRRRQAmaKWkoAWikpaACiiigAooooAKSlooASilooASiiigByO8bh0Yqw6EHBFaKeIdXjXauo3GPd8/zrMoqXFPdDu0W7nVL68GLm8nlH915CR+XSqmaKKaSWwXErul164/4VkyNKTL532QN32dcf988VwtannH/AIRXyc8fbt2P+2dZVYKVr9yoSauZmaM0UVqQFFFFMAooooAKKKWgBKKWigAooooAKSlpKACiiloASiiigAooooAKKKKACiijFAC0UlLQAUlLSUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABS0lLQAUUUUAJRRRQAUtJS0AJRS0lABRRRQAUUUUAFFFFAC0UUUAFFFFIBKKKKYBRRRQAUUUUAFFLRQAlFFFABRRRQAtFFJQAtFFFABSUUUAFFFLQAlFLRQAUlLSUAFLRRQAUlFFABRRRQAUUUUAFLSUUAFLRRQAlFLRQAUUUUAFFFJQAtFFFABRRRQAUlFFABRS0UAFFFIaADNFFFABRRRQAZopaKAEoopaAEopaSgAooooAKm84fYvIwc+Zv8A0x/jUNFABRRRQAUUUUAFFFFABRRmigAozRS0AFFFFABSUtJQAtJRRQAtJS0UAJRRRQAUUUUAFFLSUAFLSUUAFFFFABRRRQAUUUUAFFFFABRRRQMKKKKBBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABS0lFABRRRQAUUUUAFFFLQAUUUUAFFFFIBKKKKYBRRRQAUUUUALSUUtACUUUUAFFLSUAFFFFAC0UlLQAlFFFABRRS0AFFFFACUUtFABSUtJQAUUtJQAUUUUAFFFFABRRRQAtFFFABRRSUALRSUtABSUtJQAtFIKWgAoopKACiiigAooooAWkoooAKKKKACiiigBaQ0UGgApaSigBaSiigAooooAKKKKACiiigAooooAKKKKACiiloAKSiigAooooAKKKKACiiloAKKKKAEpaKKACkoooAKKWigBKKKKACiiigAopaSgAooooAKKKKACiiigAooooAKWkpaACkpaKAEopaKAEooooAWkopaAEopaSgAooooAKKKKACiiigAooooAKUUlKKACiiikAlFFFMAooooAKKKKACiiigAoopaACkpaSgAopaSgAooooAWkoooAKWkpaACiiigAooooAKSlpKACiiigAopaKACkpaKAEooooAKWiigBKKWkoAKWiigApKWigBKWiigApKWkoAKKKKACiiigApaKKACiiigAooooAKSlooASilpKAClpKWgApKWigBKKWkNABRRRQAUUUtACUUUUDCiilxQISlpKKACiiigAopaSgAooooAKWkFLQAUUUUAJS0UUAFFFFACUUUUAFFLSUAFFFFABRRRigAooooAKKKKACiiigAooooAKWikNABRRRQAUUUUAFFLRQAUUUUAJRRRQAUUUUAFFFFABRRRQAUtJSigAooopAFJRRTAKKKKACiiigAooooAKKKKACiiigBaSlooASiiigAooooAKKKKACiiigAooooAKKKKACiiigAopaKACiiigBKWiigBKWkpaACiiigAooooAKKKKACiiigAooooASilpKAClpKKAFooooAKKKKACiiigAozRRQAlFLSUAFFLRQAUUUUAFJS0lABRRS0AFFFFABSGlooASlpKWgBKKWigBKKKKACilooASiiloABRSUtABSUUtACUuaSigBc0lFFABS0UlAC0UZooASiiigAzS0lFABRRRQAUUUUAFFFFABRRRQMWkoooEFFFFABRRRQAtFFJQAUUUUAFFFFABRRRQAUUUUAFFFFABS0UUAFFFFIBKKWkpgFFFFABRRRQAYopaSgAopcUlABQKKKAFpKWigBKKKWgBKKKKACiiloASiiigAopaSgAooooAKKKKACiiigBaKKKACiiigApKWigApKWigBKKKKACloooAKKKKAEpaKKACkpaKAEopaKAAUUUUAFFFFABRRRQAUUUUAJRS0UAFFFFABRRRQAUUUUAFFFFABRRSUALSUUUAFFFFAC0lLSUALSUtJQAtFFFACGloooAKSlooASilooASilooASlpKKAFpKKKACiiigAooooAKKKWgBKKKKACiiigAooooAKKKKACiiigAopaKAEooooAKKKKACiiigAooooAKKKKACiiigAooooAWikpaACiiikMSilpKYgooooAKKWigBKKKKACiiloAKSlpKAFopKKAFooooASiiigBaKKKAA0lLSUAFFFFABRRRQAUUUYoAKWiigBKWiigAooooAKKKKACigUUAFJilpKAFooooAKKKKACkpaSgApaSloAKKSigBaKKKACiiigAooooAKKKKAEooooAWkpaKACiiigAooooAKKSloAKSlpKAFpKKKACiiloAKKKKACkpaKACkpaSgBaSiigBaSlpKAFopKKAFoopDQAUUUUAFFFFABRRS0AJRRRQAUUUUALSUtFACUUUUAFFFFABRRRQAUtJRQAtJRRQAUUUUAFFFFABRRRQAtJS0lABRRRQAUUUUAFFFFABS0UUAFFLSUgCkoopgFFFFAC0lLSUAFFFFABRRS0AJRS0lABRRRQAtFJS0AFJS0UAFJS0UAFJS0UAJRS0UAFJS0lAC0UlLQAUUUUAJmlpKKAFooooAKKKKACiiigApKKKAFpKWigBKKWigBKKKWgAooooAQ0UUUALSUUYoAKWiigAooooAKKKQ0AFFFLQAUUUlAC5pKKKACiiigAooooAWiiigApKWkoAKKKWgAooooAKSlooAKSlpKACiiloASloooAKSlooAKKKKAEooooAKKKKACiiigBaSlpKACiiigAooooAKKKKACiiigAooooAKKKKBhRRRQIKKKKACilooASiiigAooooAKKKKACiiigApRSUUALRRRQAUUUUAIaKDRQAUUUUAFAoooAWkpaSgAoFFFAC0UUUAFFJRQAUUUUAFLSUtABRRRQAUlLRQAlLmkooAKKKWgBKWiigAooooASloooAKKKKACiiigAooooAKKKKACiikoAWiiigAooooAKKKKAEopaSgBaKSloAKKKKACiiigAooooAKSlooAKSlooASloooAKKKKAEoFFLQAUUUUAFJQaKAFpKWkoAKWig0AFFJS0AFJQaKACiiigApc0lFAC0UlLQAUUUUAJRS0UAJS0UUAFFFFACUUtJQAtJRRQAUUUUAFFLSUAFFFLQAUUUhoAKKKKACiiigAooooAWikooAKKKKACiiigAooooAKKKKAClpKKAFooooAKKKKQAaSg0UwCiiigYUUUUCCiiloASilooAKKKSgAooooAWkoooAKWkpaAEpaKKACiiigBKKWkoAKWkpaACkNLRQAlFFLQAlFLRQAlFLRQAUUlLQAUUUUAFFFFABRRRQAUUUUAFFFFACUtFFABSUtFACUUuKSgBaKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigApKWigBKKKKACilooAKKKKAEopaKAEpaKKAEopaSgApaSloAKKKKACkpaKACiiigAooooASiiigBaSiloASiiigAooooAKKKKAFooooAKSlooASiiigAopcUUAJRS0lABRRRQAUUtFACUUUUAFFFFABRRRQAUUUUAFLSUtABRRRQAhopaSgAooooAKKWigBKKWkoAKWkooAKKKKACilooASiiigAoopaACiiigAooooAKSlooASiiigAooooAKKKKAFpKKKACijFFABRRRQAtFFFABSUUUALRRSUALRSUtABRRRQAUUUUAFJS0UAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAJS0lLQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFJS0lAC0lFFAC0UUUAFFFFABRRRQAUUUUAFFJS0AJRS0lABRRRQAUUCloASilooASilooASilxRQAUUUUAFJS0UAFFFFABSGlpKACiiigAooooAKKKKAFpKKKACiiloASijFLQAUUUUAFFKaSgApKKKACiiigBaKKKACkpaKAEopaKAEooooAKKKKACiiigApaSloAKKKKACkpaSgApaSloASiiigAopaKAEopaKAEpaSigBaSlooASilooASilooAKMUUUAFJS0UAJRS0YoASlpKKAFooooAKSlooAKKKSgBaSiigApaSloAKSlooAKKKKACiiigApKWigAooooAKKKKACiiigAooooAKKKKADNFJS0AFFFFABRRRQAUmKWigApKWigBKWkooAWkzRRQAtFFFACUUtFACUUtJQAUUUUAFFFFABRRRQAtFJS0AFJRRQAUUUUAFFFFABS0lFABRRRQAUUUtACUUUUAFFFFABRRRQAUUUUALRRRQAUCilFACUUUUgEopaSmAUUUUAFGaKKAClpKWgAooooAKSlooASilooASlxRRQAlLSUtABRRRQAUlLRQAlFLSUAFFLiigBKWiigAoopKACiiigApaSigBaKKKACiiigAooooAKKKKACkpaKACiiigAooooAKSlooAKKKKACiiigBKKWigAooooAKKKKACiiigAooooAKKKKACiiigBKWiigAooooASiiigAooooAWkpaTFABRRRQAUtJS0AFFFFACUUtFABRRRQAUUUUAFJRRQAUtJS0AFFFFACUUUUAFFLSUAFLSUtACUUUtACUUUUAFFFFAwooooEFFFFABRRRQAUUUUAFFKKKAEooooAKKKKAClpKWgAopKUUAFJRRSGFFFFMQUUUUAFFFFAC0lFFABS0lFABRRRQAtFJRQAtFJS0AFFFFABSUtFABSUtJQAUUUUALmkoooAWiikoAKKKKACiiigBaSiigAFLSUtABRRRQAUZopKAFooooAKKKKACiiigAooooAKKKKACiiigAooooAKSlooASloooAKSlpKADNFFFABRRS0AFJS0UAFFFFABRRRQAUUUUABpKKWgBKKWigAooooAKSiigBaSlpDQAUtJS0AFFFJQAtFFFABRRRQAlFFFABRRS0AFJRRQAUUUUAFFFLQAlFLSUAFFFFABRRRQAUUUtACUUtJQAUUUUDCiiigQUUUUALSUtJQAUUUUAFFFFABRRRQAUtFFAAetFFFIBKKKKYBRRRQAUUUUAFFFFABRRRQAUUtFACUUtJQAUUUUAFFFFABRRRQAUUtFACUUUtABRRRQAlFFFABRRRQAUUUUDFpKKKBBS0UUAFFFFABSUtIaAClpKWgAooozQAUUUlAC0UUUAJS0lFAC0lFLQAUUUlAC0UUUAIaWkooAWkpaKAEooooAKWkpaACiiigAoopKAFopKKAFpKWigAFFFFACUUtFABRRSUALRSCloAKKKKACikzS0AFJS0lABRRRQAtFJRQAUUUtABSUtIaACiiigAooooAKKKKACiiigAooooAKKKKAClpKWgBKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigApaKKAA0UlLQAlFFFABRRRQAUUUUAFFFFABRRRQAtJRRQAUUUUAFFFFABRRRQMKKKKBC0UlFABRRRQAUUUUAFFFFABRRRQAtJS0lABRRRQAUtJRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAxaSiigBaQ0UUCCiiigYUtJRQAtFJS0CEooooAXNJRRQMKKKKACg0UUCCiiigYtFFFAhKKKKACiiigYUUUUCCiiigAooooAKKKKACiiigBaSiigApaSigBaSiigAooooAKKKKACiiigAooooAKKKKACiiigApaSigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAClpKKAFopKWkAlFFGKYBRRRigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiijFABRRS0AJRRRQAUUUUAFFFFABRRRQAUUtFACUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUuKAEooooAKKKKACiiigAoopaAEpaKKAEpaKKAEopaKAEopaTFABRS4ooASiiigAooooAM0UUUAFFFFAwooooEFLRRQAlFLRQAlFFFABRRRQAUUUtACUUtJQAUUUUAFLSUtACUUtJQAUUUUAFFFFABRRS0AJRRRQAUUUtACUUUtACUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFLQAlFLRQAlKKSnYpDP/9k=";
-export const faviconContentType = "image/jpeg";
+ "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";
+export const faviconContentType = "image/png";
diff --git a/scw_js/genimg_x402_token.ts b/scw_js/genimg_x402_token.ts
index fcdca287f..d131e94a2 100644
--- a/scw_js/genimg_x402_token.ts
+++ b/scw_js/genimg_x402_token.ts
@@ -33,8 +33,9 @@ import {
createResourceServer,
createPaymentRequirements,
create402Response,
- extractPaymentPayload,
createSettlementHeaders,
+ extractPaymentPayload,
+ formatParallelPrices,
type SdkPaymentPayload,
type SdkPaymentRequirements,
} from "./x402_server.js";
@@ -88,16 +89,9 @@ const ROOT_PAGE = buildRootPage({
},
],
price: [
- ((): string => {
- // Generated from the committed spec, not restated — the page and the 402 share it.
- const info = (
- openapiSpec.paths as Record<
- string,
- Record
- >
- )["/"].post["x-payment-info"];
- return `POST /imagegen — $${info?.price.amount ?? "?"} per image (USDC, EURC or EURe; each token's price is set in its own units)`;
- })(),
+ // Generated from the committed price list — every accepted token in its own units,
+ // EURe first as the seller's preference orders it; never a single-token restatement.
+ `POST /imagegen — ${formatParallelPrices(PRICE_ATOMIC)} per image`,
],
payment:
"x402 (HTTP 402), exact scheme — one signature moves the money per call: no account, no API key, no approve() needed. An unpaid request answers 402 with the machine-readable Payment-Required challenge — exact terms (network, token, amount) are in the challenge, then retry the same request with the payment attached.",
diff --git a/scw_js/test/genimg_x402_token.test.ts b/scw_js/test/genimg_x402_token.test.ts
index 0b7905651..b03d98f4f 100644
--- a/scw_js/test/genimg_x402_token.test.ts
+++ b/scw_js/test/genimg_x402_token.test.ts
@@ -612,14 +612,14 @@ describe("genimg_x402_token.js - x402 v2 Token Payment Tests", () => {
const response = await handle(event, {});
expect(response.statusCode).toBe(200);
- expect(response.headers["Content-Type"]).toBe("image/jpeg");
+ expect(response.headers["Content-Type"]).toBe("image/png");
expect(response.headers["Access-Control-Allow-Origin"]).toBe("*");
expect(response.isBase64Encoded).toBe(true);
- // Valid base64 that decodes to a JPEG (SOI marker 0xFFD8).
+ // Valid base64 that decodes to a PNG (signature 0x8950).
const bytes = Buffer.from(response.body, "base64");
expect(bytes.length).toBeGreaterThan(0);
- expect(bytes[0]).toBe(0xff);
- expect(bytes[1]).toBe(0xd8);
+ expect(bytes[0]).toBe(0x89);
+ expect(bytes[1]).toBe(0x50);
});
test("should answer a HEAD /favicon.png probe with 200 and an image content-type", async () => {
@@ -633,7 +633,7 @@ describe("genimg_x402_token.js - x402 v2 Token Payment Tests", () => {
const response = await handle(event, {});
expect(response.statusCode).toBe(200);
- expect(response.headers["Content-Type"]).toBe("image/jpeg");
+ expect(response.headers["Content-Type"]).toBe("image/png");
expect(response.body).toBe("");
});
@@ -666,7 +666,11 @@ describe("genimg_x402_token.js - x402 v2 Token Payment Tests", () => {
expect(response.body).toContain(".well-known/agent-registration.json");
expect(response.body).toContain(".well-known/oasf.json");
// The page price is the spec's x-payment-info value, not a hand-restated one.
- expect(response.body).toContain("POST /imagegen — $0.07 per image");
+ // Generated per-token prices (EURe first, each token's own units) — nominal EUR
+ // and USD prices differ here, which is exactly why the page must quote both.
+ expect(response.body).toContain(
+ "POST /imagegen — €0.06 in EURe, €0.06 in EURC, $0.07 in USDC per image",
+ );
expect(response.body).toContain(
"curl -X POST 'https://imagegen-agent.fretchen.eu/imagegen'",
);
diff --git a/scw_js/x402_server.ts b/scw_js/x402_server.ts
index 9155796fa..d8df04d10 100644
--- a/scw_js/x402_server.ts
+++ b/scw_js/x402_server.ts
@@ -687,6 +687,22 @@ export function formatUsdcAtomicAsDecimalUsd(atomicAmount: string, decimals: big
return trimmedFraction ? `${whole}.${trimmedFraction}` : whole.toString();
}
+/**
+ * A PriceList as the root pages display it: every accepted token priced in its own units, in
+ * the seller's preference order (EURe, EURC, USDC on Base — the same order the 402 lists),
+ * € for the euro tokens and $ for USDC, decimals read from the token registry so EURe's
+ * 18-decimal prices format correctly. Pages built with this can never quote fewer tokens
+ * (or a different order) than the challenge does. (Twin of bunny_js/x402_server.ts.)
+ */
+export function formatParallelPrices(price: PriceList): string {
+ return offeredStablecoins("eip155:8453")
+ .map((coin) => {
+ const display = formatUsdcAtomicAsDecimalUsd(price[coin.symbol], BigInt(coin.decimals));
+ return `${coin.symbol === "USDC" ? "$" : "€"}${display} in ${coin.symbol}`;
+ })
+ .join(", ");
+}
+
export function createSettlementHeaders(
settlementResult: Record,
): Record {