@azeth/provider 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +70 -0
  3. package/dist/agreement-cache.d.ts +20 -0
  4. package/dist/agreement-cache.d.ts.map +1 -0
  5. package/dist/agreement-cache.js +133 -0
  6. package/dist/agreement-cache.js.map +1 -0
  7. package/dist/agreement-keeper.d.ts +43 -0
  8. package/dist/agreement-keeper.d.ts.map +1 -0
  9. package/dist/agreement-keeper.js +102 -0
  10. package/dist/agreement-keeper.js.map +1 -0
  11. package/dist/examples/index.d.ts +4 -0
  12. package/dist/examples/index.d.ts.map +1 -0
  13. package/dist/examples/index.js +3 -0
  14. package/dist/examples/index.js.map +1 -0
  15. package/dist/examples/price-feed.d.ts +22 -0
  16. package/dist/examples/price-feed.d.ts.map +1 -0
  17. package/dist/examples/price-feed.js +148 -0
  18. package/dist/examples/price-feed.js.map +1 -0
  19. package/dist/examples/pricing-routes.d.ts +15 -0
  20. package/dist/examples/pricing-routes.d.ts.map +1 -0
  21. package/dist/examples/pricing-routes.js +77 -0
  22. package/dist/examples/pricing-routes.js.map +1 -0
  23. package/dist/extensions/payment-agreement.d.ts +37 -0
  24. package/dist/extensions/payment-agreement.d.ts.map +1 -0
  25. package/dist/extensions/payment-agreement.js +39 -0
  26. package/dist/extensions/payment-agreement.js.map +1 -0
  27. package/dist/index.d.ts +12 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +15 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/middleware/pre-settled.d.ts +29 -0
  32. package/dist/middleware/pre-settled.d.ts.map +1 -0
  33. package/dist/middleware/pre-settled.js +80 -0
  34. package/dist/middleware/pre-settled.js.map +1 -0
  35. package/dist/stack.d.ts +75 -0
  36. package/dist/stack.d.ts.map +1 -0
  37. package/dist/stack.js +169 -0
  38. package/dist/stack.js.map +1 -0
  39. package/dist/storage.d.ts +48 -0
  40. package/dist/storage.d.ts.map +1 -0
  41. package/dist/storage.js +96 -0
  42. package/dist/storage.js.map +1 -0
  43. package/package.json +66 -0
@@ -0,0 +1,77 @@
1
+ import { Hono } from 'hono';
2
+ import { AzethError, TOKENS } from '@azeth/common';
3
+ import { paymentMiddlewareFromHTTPServer } from '@x402/hono';
4
+ import { isSupportedCoin, getPrice, getFreshPrice } from './price-feed.js';
5
+ import { preSettledPaymentMiddleware } from '../middleware/pre-settled.js';
6
+ /** Create the x402-gated pricing routes.
7
+ *
8
+ * Uses @x402/hono middleware for V2 protocol support including:
9
+ * - PAYMENT-SIGNATURE / PAYMENT-RESPONSE headers (v2)
10
+ * - SIWx wallet-based sessions (pay once, then access via wallet sig)
11
+ * - Payment agreement extension (subscription terms in 402 response)
12
+ * - Pre-settled smart account payments (X-Payment-Tx header bypass)
13
+ *
14
+ * When httpServer is null, all routes return 503 (graceful degradation).
15
+ */
16
+ export function createPricingRoutes(httpServer) {
17
+ const app = new Hono();
18
+ if (!httpServer) {
19
+ app.get('/:coinId', (c) => {
20
+ return c.json({
21
+ error: {
22
+ code: 'SERVICE_UNAVAILABLE',
23
+ message: 'Price feed requires x402 facilitator configuration',
24
+ },
25
+ }, 503);
26
+ });
27
+ return app;
28
+ }
29
+ // Pre-settled payment verification — checks X-Payment-Tx header for smart account payments.
30
+ // If a valid on-chain Transfer is found, sets paymentTxHash/paymentFrom/paymentAmount context
31
+ // and skips the x402 facilitator settlement (payment already happened via UserOp).
32
+ const payTo = (process.env['X402_PAY_TO'] ?? '');
33
+ const chainName = (process.env['AZETH_CHAIN'] ?? 'baseSepolia');
34
+ // Use chain-aware USDC address from TOKENS constants (not env var, which may be for a different chain)
35
+ const usdcAddress = TOKENS[chainName].USDC;
36
+ const priceStr = process.env['X402_PRICE_FEED_PRICE'] ?? '$0.01';
37
+ // Parse price string like "$0.01" to atomic USDC (6 decimals)
38
+ const priceNum = parseFloat(priceStr.replace('$', ''));
39
+ const priceAtomicAmount = BigInt(Math.round(priceNum * 1e6));
40
+ if (payTo && usdcAddress) {
41
+ app.use('*', preSettledPaymentMiddleware({
42
+ payTo,
43
+ usdcAddress,
44
+ priceAtomicAmount,
45
+ rpcUrl: process.env['AZETH_RPC_URL'] ?? process.env['BASE_RPC_URL'],
46
+ chainName,
47
+ }));
48
+ }
49
+ // x402 V2 middleware — handles 402, verify, settle, SIWx, extensions.
50
+ // Skipped when pre-settled payment was verified (preSettledVerified flag set).
51
+ app.use('*', async (c, next) => {
52
+ if (c['preSettledVerified']) {
53
+ return next();
54
+ }
55
+ return paymentMiddlewareFromHTTPServer(httpServer)(c, next);
56
+ });
57
+ // Price data handler — pure business logic, no payment context needed.
58
+ // Payment info (amount, settlement tx) is in response headers via standard x402 protocol:
59
+ // - PAYMENT-RESPONSE header contains { success, transaction, network }
60
+ app.get('/:coinId', async (c) => {
61
+ const coinId = c.req.param('coinId');
62
+ if (!isSupportedCoin(coinId)) {
63
+ throw new AzethError('Unsupported coin', 'INVALID_INPUT', {
64
+ coinId,
65
+ supported: [
66
+ 'bitcoin', 'ethereum', 'solana', 'usd-coin', 'chainlink',
67
+ 'aave', 'uniswap', 'maker', 'compound-governance-token',
68
+ ],
69
+ });
70
+ }
71
+ const fresh = c.req.query('fresh') === 'true';
72
+ const data = fresh ? await getFreshPrice(coinId) : await getPrice(coinId);
73
+ return c.json({ data });
74
+ });
75
+ return app;
76
+ }
77
+ //# sourceMappingURL=pricing-routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing-routes.js","sourceRoot":"","sources":["../../src/examples/pricing-routes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import type { ResourceServerExtension } from '@x402/core/types';
2
+ /** Payment agreement terms advertised in the 402 response.
3
+ * Tells clients they can create an on-chain agreement instead of paying per-request. */
4
+ export interface AgreementTerms {
5
+ /** Service payee address */
6
+ payee: `0x${string}`;
7
+ /** Payment token address (e.g., USDC) */
8
+ token: `0x${string}`;
9
+ /** PaymentAgreementModule contract address */
10
+ moduleAddress: `0x${string}`;
11
+ /** Minimum amount per interval in token smallest units */
12
+ minAmountPerInterval: string;
13
+ /** Suggested interval in seconds (e.g., 86400 for daily) */
14
+ suggestedInterval: number;
15
+ }
16
+ /** Extension key used in 402 response extensions object */
17
+ export declare const PAYMENT_AGREEMENT_KEY = "payment-agreement";
18
+ /** Create a custom ResourceServerExtension that adds agreement terms to the 402 response.
19
+ *
20
+ * Clients see in the 402 response:
21
+ * ```json
22
+ * {
23
+ * "extensions": {
24
+ * "payment-agreement": {
25
+ * "acceptsAgreements": true,
26
+ * "terms": { "payee": "0x...", "token": "0x...", ... }
27
+ * }
28
+ * }
29
+ * }
30
+ * ```
31
+ *
32
+ * This is a server-side-only extension — it adds metadata to the PaymentRequired
33
+ * response but does not validate incoming payment payloads. Agreement validation
34
+ * happens in AzethSIWxStorage.hasPaid().
35
+ */
36
+ export declare function createPaymentAgreementExtension(terms: AgreementTerms): ResourceServerExtension;
37
+ //# sourceMappingURL=payment-agreement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payment-agreement.d.ts","sourceRoot":"","sources":["../../src/extensions/payment-agreement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAEhE;yFACyF;AACzF,MAAM,WAAW,cAAc;IAC7B,4BAA4B;IAC5B,KAAK,EAAE,KAAK,MAAM,EAAE,CAAC;IACrB,yCAAyC;IACzC,KAAK,EAAE,KAAK,MAAM,EAAE,CAAC;IACrB,8CAA8C;IAC9C,aAAa,EAAE,KAAK,MAAM,EAAE,CAAC;IAC7B,0DAA0D;IAC1D,oBAAoB,EAAE,MAAM,CAAC;IAC7B,4DAA4D;IAC5D,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,2DAA2D;AAC3D,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AAEzD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,cAAc,GAAG,uBAAuB,CAkB9F"}
@@ -0,0 +1,39 @@
1
+ /** Extension key used in 402 response extensions object */
2
+ export const PAYMENT_AGREEMENT_KEY = 'payment-agreement';
3
+ /** Create a custom ResourceServerExtension that adds agreement terms to the 402 response.
4
+ *
5
+ * Clients see in the 402 response:
6
+ * ```json
7
+ * {
8
+ * "extensions": {
9
+ * "payment-agreement": {
10
+ * "acceptsAgreements": true,
11
+ * "terms": { "payee": "0x...", "token": "0x...", ... }
12
+ * }
13
+ * }
14
+ * }
15
+ * ```
16
+ *
17
+ * This is a server-side-only extension — it adds metadata to the PaymentRequired
18
+ * response but does not validate incoming payment payloads. Agreement validation
19
+ * happens in AzethSIWxStorage.hasPaid().
20
+ */
21
+ export function createPaymentAgreementExtension(terms) {
22
+ return {
23
+ key: PAYMENT_AGREEMENT_KEY,
24
+ /** Enrich the 402 PaymentRequired response with agreement terms */
25
+ async enrichPaymentRequiredResponse(_declaration, _context) {
26
+ return {
27
+ acceptsAgreements: true,
28
+ terms: {
29
+ payee: terms.payee,
30
+ token: terms.token,
31
+ moduleAddress: terms.moduleAddress,
32
+ minAmountPerInterval: terms.minAmountPerInterval,
33
+ suggestedInterval: terms.suggestedInterval,
34
+ },
35
+ };
36
+ },
37
+ };
38
+ }
39
+ //# sourceMappingURL=payment-agreement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payment-agreement.js","sourceRoot":"","sources":["../../src/extensions/payment-agreement.ts"],"names":[],"mappings":"AAiBA,2DAA2D;AAC3D,MAAM,CAAC,MAAM,qBAAqB,GAAG,mBAAmB,CAAC;AAEzD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,+BAA+B,CAAC,KAAqB;IACnE,OAAO;QACL,GAAG,EAAE,qBAAqB;QAE1B,mEAAmE;QACnE,KAAK,CAAC,6BAA6B,CAAC,YAAqB,EAAE,QAAiB;YAC1E,OAAO;gBACL,iBAAiB,EAAE,IAAI;gBACvB,KAAK,EAAE;oBACL,KAAK,EAAE,KAAK,CAAC,KAAK;oBAClB,KAAK,EAAE,KAAK,CAAC,KAAK;oBAClB,aAAa,EAAE,KAAK,CAAC,aAAa;oBAClC,oBAAoB,EAAE,KAAK,CAAC,oBAAoB;oBAChD,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;iBAC3C;aACF,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,12 @@
1
+ export { createX402Stack, createX402StackFromEnv, CAIP2_NETWORKS, LocalFacilitatorClient, declareSIWxExtension, paymentMiddlewareFromHTTPServer, } from './stack.js';
2
+ export type { X402StackConfig, X402Stack, RoutesConfig, x402HTTPResourceServer, } from './stack.js';
3
+ export { AzethSIWxStorage } from './storage.js';
4
+ export type { AzethSIWxStorageConfig } from './storage.js';
5
+ export { AgreementKeeper } from './agreement-keeper.js';
6
+ export type { AgreementKeeperConfig } from './agreement-keeper.js';
7
+ export { findActiveAgreementForPayee, clearAgreementCache, setAgreementCacheTtl } from './agreement-cache.js';
8
+ export { createPaymentAgreementExtension, PAYMENT_AGREEMENT_KEY } from './extensions/payment-agreement.js';
9
+ export type { AgreementTerms } from './extensions/payment-agreement.js';
10
+ export { preSettledPaymentMiddleware } from './middleware/pre-settled.js';
11
+ export type { ProviderEnv } from './middleware/pre-settled.js';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,cAAc,EACd,sBAAsB,EAEtB,oBAAoB,EACpB,+BAA+B,GAChC,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,eAAe,EACf,SAAS,EACT,YAAY,EACZ,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,YAAY,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAG3D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,YAAY,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAGnE,OAAO,EAAE,2BAA2B,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAG9G,OAAO,EAAE,+BAA+B,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC3G,YAAY,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAGxE,OAAO,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AAC1E,YAAY,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,15 @@
1
+ // x402 stack — core provider functionality
2
+ export { createX402Stack, createX402StackFromEnv, CAIP2_NETWORKS, LocalFacilitatorClient,
3
+ // Re-exported x402 types
4
+ declareSIWxExtension, paymentMiddlewareFromHTTPServer, } from './stack.js';
5
+ // Agreement-aware SIWx storage
6
+ export { AzethSIWxStorage } from './storage.js';
7
+ // Agreement keeper — periodic execution of due agreements
8
+ export { AgreementKeeper } from './agreement-keeper.js';
9
+ // Agreement cache — LRU cache with stale-on-error
10
+ export { findActiveAgreementForPayee, clearAgreementCache, setAgreementCacheTtl } from './agreement-cache.js';
11
+ // Payment agreement extension
12
+ export { createPaymentAgreementExtension, PAYMENT_AGREEMENT_KEY } from './extensions/payment-agreement.js';
13
+ // Pre-settled payment middleware
14
+ export { preSettledPaymentMiddleware } from './middleware/pre-settled.js';
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,cAAc,EACd,sBAAsB;AACtB,yBAAyB;AACzB,oBAAoB,EACpB,+BAA+B,GAChC,MAAM,YAAY,CAAC;AAQpB,+BAA+B;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGhD,0DAA0D;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGxD,kDAAkD;AAClD,OAAO,EAAE,2BAA2B,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE9G,8BAA8B;AAC9B,OAAO,EAAE,+BAA+B,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAG3G,iCAAiC;AACjC,OAAO,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { Context, Env, Next } from 'hono';
2
+ import { type SupportedChainName } from '@azeth/common';
3
+ /** Minimal Hono env for pre-settled payment middleware.
4
+ * Consumer apps can extend this with their own variables. */
5
+ export interface ProviderEnv extends Env {
6
+ Variables: {
7
+ paymentFrom: `0x${string}`;
8
+ paymentAmount: bigint;
9
+ paymentTxHash: `0x${string}`;
10
+ };
11
+ }
12
+ /** Pre-settled payment verification middleware.
13
+ *
14
+ * Checks for X-Payment-Tx header. If present, verifies the transaction on-chain
15
+ * by decoding USDC Transfer event logs and confirming the payment meets requirements.
16
+ *
17
+ * When valid: sets context variables (paymentFrom, paymentAmount, paymentTxHash)
18
+ * and calls next() — downstream x402 middleware should be skipped.
19
+ *
20
+ * When invalid: falls through to let x402 middleware handle normally.
21
+ */
22
+ export declare function preSettledPaymentMiddleware(config: {
23
+ payTo: `0x${string}`;
24
+ usdcAddress: `0x${string}`;
25
+ priceAtomicAmount: bigint;
26
+ rpcUrl?: string;
27
+ chainName?: SupportedChainName;
28
+ }): (c: Context<ProviderEnv>, next: Next) => Promise<void>;
29
+ //# sourceMappingURL=pre-settled.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pre-settled.d.ts","sourceRoot":"","sources":["../../src/middleware/pre-settled.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE/C,OAAO,EAAoB,KAAK,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAE1E;8DAC8D;AAC9D,MAAM,WAAW,WAAY,SAAQ,GAAG;IACtC,SAAS,EAAE;QACT,WAAW,EAAE,KAAK,MAAM,EAAE,CAAC;QAC3B,aAAa,EAAE,MAAM,CAAC;QACtB,aAAa,EAAE,KAAK,MAAM,EAAE,CAAC;KAC9B,CAAC;CACH;AASD;;;;;;;;;GASG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IAClD,KAAK,EAAE,KAAK,MAAM,EAAE,CAAC;IACrB,WAAW,EAAE,KAAK,MAAM,EAAE,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,kBAAkB,CAAC;CAChC,IAOe,GAAG,OAAO,CAAC,WAAW,CAAC,EAAE,MAAM,IAAI,mBA4DlD"}
@@ -0,0 +1,80 @@
1
+ import { createPublicClient, http, parseAbiItem } from 'viem';
2
+ import { resolveViemChain } from '@azeth/common';
3
+ /** USDC Transfer(address,address,uint256) event topic */
4
+ const TRANSFER_EVENT = parseAbiItem('event Transfer(address indexed from, address indexed to, uint256 value)');
5
+ /** Track consumed tx hashes to prevent replay (one payment = one access) */
6
+ const usedTxHashes = new Set();
7
+ const MAX_USED_TX_HASHES = 50_000;
8
+ /** Pre-settled payment verification middleware.
9
+ *
10
+ * Checks for X-Payment-Tx header. If present, verifies the transaction on-chain
11
+ * by decoding USDC Transfer event logs and confirming the payment meets requirements.
12
+ *
13
+ * When valid: sets context variables (paymentFrom, paymentAmount, paymentTxHash)
14
+ * and calls next() — downstream x402 middleware should be skipped.
15
+ *
16
+ * When invalid: falls through to let x402 middleware handle normally.
17
+ */
18
+ export function preSettledPaymentMiddleware(config) {
19
+ const chain = resolveViemChain(config.chainName ?? 'baseSepolia');
20
+ const publicClient = createPublicClient({
21
+ chain,
22
+ transport: http(config.rpcUrl),
23
+ });
24
+ return async (c, next) => {
25
+ const txHashHeader = c.req.header('X-Payment-Tx');
26
+ if (!txHashHeader || !/^0x[0-9a-fA-F]{64}$/.test(txHashHeader)) {
27
+ return next();
28
+ }
29
+ const txHash = txHashHeader;
30
+ // Reject already-consumed tx hashes before making RPC call
31
+ if (usedTxHashes.has(txHash)) {
32
+ return next(); // Fall through to x402
33
+ }
34
+ try {
35
+ const receipt = await publicClient.getTransactionReceipt({ hash: txHash });
36
+ if (receipt.status !== 'success') {
37
+ return next(); // Transaction failed, fall through to x402
38
+ }
39
+ // Find USDC Transfer event matching requirements
40
+ for (const log of receipt.logs) {
41
+ if (log.address.toLowerCase() !== config.usdcAddress.toLowerCase())
42
+ continue;
43
+ if (log.topics.length < 3)
44
+ continue;
45
+ // topics[0] = event sig, topics[1] = from, topics[2] = to
46
+ const eventSig = log.topics[0];
47
+ if (eventSig !== '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef')
48
+ continue;
49
+ const toAddress = `0x${log.topics[2].slice(26)}`;
50
+ if (toAddress.toLowerCase() !== config.payTo.toLowerCase())
51
+ continue;
52
+ // Decode value from log data
53
+ const value = BigInt(log.data);
54
+ if (value < config.priceAtomicAmount)
55
+ continue;
56
+ // Valid pre-settled payment found — extract `from` from the on-chain log,
57
+ // NOT the client-controlled header (which could be spoofed).
58
+ const logFrom = `0x${log.topics[1].slice(26)}`;
59
+ c.set('paymentFrom', logFrom);
60
+ c.set('paymentAmount', value);
61
+ c.set('paymentTxHash', txHash);
62
+ // Mark tx hash as consumed — prevents replay
63
+ usedTxHashes.add(txHash);
64
+ if (usedTxHashes.size > MAX_USED_TX_HASHES) {
65
+ const oldest = usedTxHashes.values().next().value;
66
+ if (oldest !== undefined)
67
+ usedTxHashes.delete(oldest);
68
+ }
69
+ // Set a flag for downstream to know payment is pre-settled
70
+ c['preSettledVerified'] = true;
71
+ return next();
72
+ }
73
+ }
74
+ catch {
75
+ // RPC error or tx not found — fall through to x402
76
+ }
77
+ return next();
78
+ };
79
+ }
80
+ //# sourceMappingURL=pre-settled.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pre-settled.js","sourceRoot":"","sources":["../../src/middleware/pre-settled.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ import { type PublicClient, type WalletClient, type Chain, type Transport, type Account } from 'viem';
2
+ import { type SupportedChainName } from '@azeth/common';
3
+ import { x402Facilitator } from '@x402/core/facilitator';
4
+ import { x402ResourceServer, x402HTTPResourceServer, type FacilitatorClient, type RoutesConfig } from '@x402/core/server';
5
+ import type { PaymentPayload, PaymentRequirements, VerifyResponse, SettleResponse, SupportedResponse } from '@x402/core/types';
6
+ import { AzethSIWxStorage } from './storage.js';
7
+ import { type AgreementTerms } from './extensions/payment-agreement.js';
8
+ /** CAIP-2 network identifiers for supported chains */
9
+ export declare const CAIP2_NETWORKS: Record<SupportedChainName, `eip155:${string}`>;
10
+ /** Configuration for the x402 stack */
11
+ export interface X402StackConfig {
12
+ /** Chain name */
13
+ chainName: SupportedChainName;
14
+ /** Network in CAIP-2 format */
15
+ network: `eip155:${string}`;
16
+ /** Payment recipient address */
17
+ payTo: `0x${string}`;
18
+ /** Wallet client for gas settlement */
19
+ walletClient: WalletClient<Transport, Chain, Account>;
20
+ /** Public client for on-chain reads */
21
+ publicClient: PublicClient<Transport, Chain>;
22
+ /** Agreement terms (null = agreements disabled) */
23
+ agreementTerms?: AgreementTerms | null;
24
+ }
25
+ /** The complete x402 V2 stack returned by createX402Stack */
26
+ export interface X402Stack {
27
+ /** Self-hosted facilitator */
28
+ facilitator: x402Facilitator;
29
+ /** Resource server wrapping the facilitator */
30
+ server: x402ResourceServer;
31
+ /** HTTP server wrapping the resource server with routes */
32
+ httpServer: x402HTTPResourceServer;
33
+ /** Agreement-aware SIWx storage */
34
+ storage: AzethSIWxStorage;
35
+ /** Public client for on-chain reads (needed by keeper) */
36
+ publicClient: PublicClient<Transport, Chain>;
37
+ /** Wallet client for gas settlement (needed by keeper) */
38
+ walletClient: WalletClient<Transport, Chain, Account>;
39
+ }
40
+ /** Wraps x402Facilitator as a FacilitatorClient for in-process use.
41
+ * Avoids HTTP round-trips by calling the facilitator directly. */
42
+ export declare class LocalFacilitatorClient implements FacilitatorClient {
43
+ private readonly f;
44
+ constructor(facilitator: x402Facilitator);
45
+ verify(paymentPayload: PaymentPayload, paymentRequirements: PaymentRequirements): Promise<VerifyResponse>;
46
+ settle(paymentPayload: PaymentPayload, paymentRequirements: PaymentRequirements): Promise<SettleResponse>;
47
+ getSupported(): Promise<SupportedResponse>;
48
+ }
49
+ /** Create the complete x402 V2 stack with self-hosted facilitator, SIWx sessions,
50
+ * and payment agreement support.
51
+ *
52
+ * Architecture:
53
+ * 1. Self-hosted facilitator (we control settlement, no external dependency)
54
+ * 2. Resource server with EVM price parsing
55
+ * 3. SIWx extension for wallet-based sessions
56
+ * 4. Payment-agreement extension for subscription terms
57
+ * 5. Agreement-aware SIWx storage for hybrid access
58
+ *
59
+ * @param config - Stack configuration
60
+ * @param routes - Route configurations for protected endpoints
61
+ * @returns Complete x402 stack
62
+ */
63
+ export declare function createX402Stack(config: X402StackConfig, routes: RoutesConfig): X402Stack;
64
+ /** Create a complete x402 stack from environment variables.
65
+ * Returns null if required keys are missing (graceful degradation).
66
+ *
67
+ * Unlike the server's version, this creates its own publicClient
68
+ * directly from the RPC URL — no dependency on external singletons.
69
+ */
70
+ export declare function createX402StackFromEnv(routes: RoutesConfig): X402Stack | null;
71
+ export { declareSIWxExtension } from '@x402/extensions/sign-in-with-x';
72
+ export { paymentMiddlewareFromHTTPServer } from '@x402/hono';
73
+ export type { RoutesConfig } from '@x402/core/server';
74
+ export type { x402HTTPResourceServer } from '@x402/core/server';
75
+ //# sourceMappingURL=stack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../src/stack.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,SAAS,EACd,KAAK,OAAO,EACb,MAAM,MAAM,CAAC;AAGd,OAAO,EAA6C,KAAK,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnG,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,KAAK,iBAAiB,EAAE,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC1H,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,cAAc,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAU/H,OAAO,EAAE,gBAAgB,EAA+B,MAAM,cAAc,CAAC;AAC7E,OAAO,EAAmC,KAAK,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAEzG,sDAAsD;AACtD,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,kBAAkB,EAAE,UAAU,MAAM,EAAE,CAKzE,CAAC;AAEF,uCAAuC;AACvC,MAAM,WAAW,eAAe;IAC9B,iBAAiB;IACjB,SAAS,EAAE,kBAAkB,CAAC;IAC9B,+BAA+B;IAC/B,OAAO,EAAE,UAAU,MAAM,EAAE,CAAC;IAC5B,gCAAgC;IAChC,KAAK,EAAE,KAAK,MAAM,EAAE,CAAC;IACrB,uCAAuC;IACvC,YAAY,EAAE,YAAY,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IACtD,uCAAuC;IACvC,YAAY,EAAE,YAAY,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC7C,mDAAmD;IACnD,cAAc,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;CACxC;AAED,6DAA6D;AAC7D,MAAM,WAAW,SAAS;IACxB,8BAA8B;IAC9B,WAAW,EAAE,eAAe,CAAC;IAC7B,+CAA+C;IAC/C,MAAM,EAAE,kBAAkB,CAAC;IAC3B,2DAA2D;IAC3D,UAAU,EAAE,sBAAsB,CAAC;IACnC,mCAAmC;IACnC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,0DAA0D;IAC1D,YAAY,EAAE,YAAY,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC7C,0DAA0D;IAC1D,YAAY,EAAE,YAAY,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;CACvD;AAED;mEACmE;AACnE,qBAAa,sBAAuB,YAAW,iBAAiB;IAC9D,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAkB;gBAExB,WAAW,EAAE,eAAe;IAIlC,MAAM,CAAC,cAAc,EAAE,cAAc,EAAE,mBAAmB,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,CAAC;IAIzG,MAAM,CAAC,cAAc,EAAE,cAAc,EAAE,mBAAmB,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,CAAC;IAIzG,YAAY,IAAI,OAAO,CAAC,iBAAiB,CAAC;CAMjD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,YAAY,GACnB,SAAS,CAgEX;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,YAAY,GACnB,SAAS,GAAG,IAAI,CA+DlB;AAGD,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAAE,+BAA+B,EAAE,MAAM,YAAY,CAAC;AAC7D,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,YAAY,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/stack.js ADDED
@@ -0,0 +1,169 @@
1
+ import { createPublicClient, createWalletClient, http, } from 'viem';
2
+ import { publicActions } from 'viem';
3
+ import { privateKeyToAccount } from 'viem/accounts';
4
+ import { TOKENS, AZETH_CONTRACTS, resolveViemChain } from '@azeth/common';
5
+ import { x402Facilitator } from '@x402/core/facilitator';
6
+ import { x402ResourceServer, x402HTTPResourceServer } from '@x402/core/server';
7
+ import { registerExactEvmScheme as registerFacilitatorEvm } from '@x402/evm/exact/facilitator';
8
+ import { registerExactEvmScheme as registerServerEvm } from '@x402/evm/exact/server';
9
+ import { toFacilitatorEvmSigner } from '@x402/evm';
10
+ import { siwxResourceServerExtension, createSIWxSettleHook, createSIWxRequestHook, } from '@x402/extensions/sign-in-with-x';
11
+ import { AzethSIWxStorage } from './storage.js';
12
+ import { createPaymentAgreementExtension } from './extensions/payment-agreement.js';
13
+ /** CAIP-2 network identifiers for supported chains */
14
+ export const CAIP2_NETWORKS = {
15
+ base: 'eip155:8453',
16
+ baseSepolia: 'eip155:84532',
17
+ ethereumSepolia: 'eip155:11155111',
18
+ ethereum: 'eip155:1',
19
+ };
20
+ /** Wraps x402Facilitator as a FacilitatorClient for in-process use.
21
+ * Avoids HTTP round-trips by calling the facilitator directly. */
22
+ export class LocalFacilitatorClient {
23
+ f;
24
+ constructor(facilitator) {
25
+ this.f = facilitator;
26
+ }
27
+ async verify(paymentPayload, paymentRequirements) {
28
+ return this.f.verify(paymentPayload, paymentRequirements);
29
+ }
30
+ async settle(paymentPayload, paymentRequirements) {
31
+ return this.f.settle(paymentPayload, paymentRequirements);
32
+ }
33
+ async getSupported() {
34
+ // x402Facilitator.getSupported() returns network as plain string,
35
+ // but FacilitatorClient expects Network (`${string}:${string}`).
36
+ // The values are always CAIP-2 formatted, so the cast is safe.
37
+ return this.f.getSupported();
38
+ }
39
+ }
40
+ /** Create the complete x402 V2 stack with self-hosted facilitator, SIWx sessions,
41
+ * and payment agreement support.
42
+ *
43
+ * Architecture:
44
+ * 1. Self-hosted facilitator (we control settlement, no external dependency)
45
+ * 2. Resource server with EVM price parsing
46
+ * 3. SIWx extension for wallet-based sessions
47
+ * 4. Payment-agreement extension for subscription terms
48
+ * 5. Agreement-aware SIWx storage for hybrid access
49
+ *
50
+ * @param config - Stack configuration
51
+ * @param routes - Route configurations for protected endpoints
52
+ * @returns Complete x402 stack
53
+ */
54
+ export function createX402Stack(config, routes) {
55
+ // 1. Create facilitator signer from wallet+public client
56
+ const combinedClient = config.walletClient.extend(publicActions);
57
+ // Cast needed: viem's verifyTypedData uses strict TypedDataParameter[] types
58
+ // while @x402's FacilitatorEvmSigner uses simplified Record<string, unknown>.
59
+ // Runtime behavior is identical — this is a type-level mismatch only.
60
+ const signer = toFacilitatorEvmSigner(combinedClient);
61
+ // 2. Self-hosted facilitator with EVM exact scheme
62
+ const facilitator = new x402Facilitator();
63
+ registerFacilitatorEvm(facilitator, {
64
+ signer,
65
+ networks: config.network,
66
+ deployERC4337WithEIP6492: true,
67
+ });
68
+ // 3. Local facilitator client (no HTTP round-trips)
69
+ const facilitatorClient = new LocalFacilitatorClient(facilitator);
70
+ // 4. Resource server with EVM scheme
71
+ const server = new x402ResourceServer(facilitatorClient);
72
+ registerServerEvm(server);
73
+ // 5. Register SIWx extension (wallet sessions)
74
+ server.registerExtension(siwxResourceServerExtension);
75
+ // 6. Register payment-agreement extension if configured
76
+ if (config.agreementTerms) {
77
+ server.registerExtension(createPaymentAgreementExtension(config.agreementTerms));
78
+ }
79
+ // 7. Agreement-aware SIWx storage
80
+ const storageConfig = {
81
+ publicClient: config.publicClient,
82
+ servicePayee: config.payTo,
83
+ serviceToken: config.agreementTerms?.token
84
+ ? config.agreementTerms.token
85
+ : undefined,
86
+ moduleAddress: config.agreementTerms?.moduleAddress
87
+ ? config.agreementTerms.moduleAddress
88
+ : undefined,
89
+ minAgreementAmount: config.agreementTerms?.minAmountPerInterval
90
+ ? BigInt(config.agreementTerms.minAmountPerInterval)
91
+ : undefined,
92
+ };
93
+ const storage = new AzethSIWxStorage(storageConfig);
94
+ // 8. SIWx hooks: session recording + request validation
95
+ facilitator.onAfterSettle(createSIWxSettleHook({ storage }));
96
+ // 9. HTTP server wraps resource server + routes
97
+ const httpServer = new x402HTTPResourceServer(server, routes);
98
+ httpServer.onProtectedRequest(createSIWxRequestHook({
99
+ storage,
100
+ verifyOptions: {
101
+ // publicClient.verifyMessage satisfies EVMMessageVerifier —
102
+ // enables EIP-1271 (deployed smart wallets) and EIP-6492 (counterfactual)
103
+ evmVerifier: config.publicClient.verifyMessage,
104
+ },
105
+ }));
106
+ return { facilitator, server, httpServer, storage, publicClient: config.publicClient, walletClient: config.walletClient };
107
+ }
108
+ /** Create a complete x402 stack from environment variables.
109
+ * Returns null if required keys are missing (graceful degradation).
110
+ *
111
+ * Unlike the server's version, this creates its own publicClient
112
+ * directly from the RPC URL — no dependency on external singletons.
113
+ */
114
+ export function createX402StackFromEnv(routes) {
115
+ const privateKey = process.env['X402_FACILITATOR_KEY'] ?? process.env['DEPLOYER_PRIVATE_KEY'];
116
+ const payTo = process.env['X402_PAY_TO'];
117
+ if (!privateKey || !payTo) {
118
+ return null;
119
+ }
120
+ // Validate address format
121
+ if (!/^0x[0-9a-fA-F]{40}$/.test(payTo)) {
122
+ console.error('[AzethProvider] Invalid X402_PAY_TO address format');
123
+ return null;
124
+ }
125
+ const chainName = (process.env['AZETH_CHAIN'] ?? 'baseSepolia');
126
+ const chain = resolveViemChain(chainName);
127
+ const network = CAIP2_NETWORKS[chainName];
128
+ const rpcUrl = process.env['AZETH_RPC_URL'] ?? process.env['BASE_RPC_URL'];
129
+ let formattedKey = privateKey;
130
+ if (!formattedKey.startsWith('0x')) {
131
+ formattedKey = `0x${formattedKey}`;
132
+ }
133
+ const account = privateKeyToAccount(formattedKey);
134
+ const walletClient = createWalletClient({
135
+ account,
136
+ chain,
137
+ transport: http(rpcUrl),
138
+ });
139
+ // Create publicClient directly — no dependency on external singletons
140
+ const publicClient = createPublicClient({
141
+ chain,
142
+ transport: http(rpcUrl),
143
+ });
144
+ // Build agreement terms from env + contract addresses
145
+ const contracts = AZETH_CONTRACTS[chainName];
146
+ const moduleAddress = contracts?.paymentAgreementModule;
147
+ const usdcAddress = TOKENS[chainName].USDC;
148
+ const agreementTerms = moduleAddress
149
+ ? {
150
+ payee: payTo,
151
+ token: usdcAddress,
152
+ moduleAddress: moduleAddress,
153
+ minAmountPerInterval: process.env['X402_AGREEMENT_MIN_AMOUNT'] ?? '10000',
154
+ suggestedInterval: 86400,
155
+ }
156
+ : null;
157
+ return createX402Stack({
158
+ chainName,
159
+ network,
160
+ payTo: payTo,
161
+ walletClient,
162
+ publicClient,
163
+ agreementTerms,
164
+ }, routes);
165
+ }
166
+ // Re-export x402 types so consumers don't need direct @x402/* dependencies
167
+ export { declareSIWxExtension } from '@x402/extensions/sign-in-with-x';
168
+ export { paymentMiddlewareFromHTTPServer } from '@x402/hono';
169
+ //# sourceMappingURL=stack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stack.js","sourceRoot":"","sources":["../src/stack.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import type { PublicClient, Chain, Transport } from 'viem';
2
+ import type { SIWxStorage } from '@x402/extensions/sign-in-with-x';
3
+ import type { AgreementKeeper } from './agreement-keeper.js';
4
+ /** Configuration for agreement-aware SIWx storage */
5
+ export interface AzethSIWxStorageConfig {
6
+ /** viem public client for on-chain reads */
7
+ publicClient: PublicClient<Transport, Chain>;
8
+ /** The service's payment recipient address */
9
+ servicePayee: `0x${string}`;
10
+ /** Token address the service accepts (e.g., USDC) */
11
+ serviceToken?: `0x${string}`;
12
+ /** PaymentAgreementModule contract address */
13
+ moduleAddress?: `0x${string}`;
14
+ /** Minimum agreement amount per interval */
15
+ minAgreementAmount?: bigint;
16
+ }
17
+ export declare class AzethSIWxStorage implements SIWxStorage {
18
+ private readonly paymentRecords;
19
+ private readonly usedNonces;
20
+ private readonly config;
21
+ private keeper;
22
+ constructor(config: AzethSIWxStorageConfig);
23
+ /** Inject the agreement keeper after construction.
24
+ * Called from server startup to avoid circular initialization. */
25
+ setKeeper(keeper: AgreementKeeper): void;
26
+ /** Check if an address has paid for a resource.
27
+ *
28
+ * Two-tier lookup:
29
+ * 1. Check in-memory payment records (x402 settlement — permanent grant)
30
+ * 2. Check on-chain agreements (subscription — re-verified every ~60s via cache TTL)
31
+ *
32
+ * Settlement grants (recordPayment) are permanent for the session.
33
+ * Agreement grants are NOT cached permanently — they are re-verified on each
34
+ * call via findActiveAgreementForPayee (which uses a 60s TTL cache internally).
35
+ * This ensures that if a payer's balance drops to zero, their access is revoked
36
+ * within ~60s instead of being permanently granted.
37
+ */
38
+ hasPaid(resource: string, address: string): Promise<boolean>;
39
+ /** Record that an address has paid for a resource */
40
+ recordPayment(resource: string, address: string): void;
41
+ /** Check if a nonce has been used (replay prevention) */
42
+ hasUsedNonce(nonce: string): boolean;
43
+ /** Record a nonce as used */
44
+ recordNonce(nonce: string): void;
45
+ /** Synchronous payment record helper */
46
+ private recordPaymentSync;
47
+ }
48
+ //# sourceMappingURL=storage.d.ts.map