@dexterai/x402 1.8.2 → 1.9.1
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.
- package/README.md +101 -8
- package/dist/adapters/index.cjs +15 -3
- package/dist/adapters/index.cjs.map +1 -1
- package/dist/adapters/index.d.cts +5 -5
- package/dist/adapters/index.d.ts +5 -5
- package/dist/adapters/index.js +5 -3
- package/dist/adapters/index.js.map +1 -1
- package/dist/client/index.cjs +50 -9
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +7 -6
- package/dist/client/index.d.ts +7 -6
- package/dist/client/index.js +37 -9
- package/dist/client/index.js.map +1 -1
- package/dist/react/index.cjs +64 -12
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +13 -4
- package/dist/react/index.d.ts +13 -4
- package/dist/react/index.js +52 -12
- package/dist/react/index.js.map +1 -1
- package/dist/server/index.cjs +43 -18
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +12 -3
- package/dist/server/index.d.ts +12 -3
- package/dist/server/index.js +40 -16
- package/dist/server/index.js.map +1 -1
- package/dist/{solana-BeGAqPta.d.cts → solana-CfHuiW2H.d.cts} +2 -2
- package/dist/{solana-CQD9yMju.d.ts → solana-kZcwbUK9.d.ts} +2 -2
- package/dist/{x402-client-Dk9q2QQF.d.cts → sponsored-access-BCB2CxdG.d.cts} +70 -3
- package/dist/{x402-client-D9b3PHai.d.ts → sponsored-access-H1EX6zpi.d.ts} +70 -3
- package/dist/{types-DYLi7SuF.d.cts → types-BQvaF8lB.d.cts} +7 -5
- package/dist/{types-DYLi7SuF.d.ts → types-BQvaF8lB.d.ts} +7 -5
- package/dist/{types-B477nBpg.d.cts → types-DmqH9yD8.d.cts} +1 -1
- package/dist/{types-BWnUAPvD.d.ts → types-ENcnkof8.d.ts} +1 -1
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +2 -6
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as AdapterConfig, C as ChainAdapter, S as SignedTransaction } from './types-
|
|
2
|
-
import { P as PaymentAccept } from './types-
|
|
1
|
+
import { A as AdapterConfig, C as ChainAdapter, S as SignedTransaction } from './types-DmqH9yD8.cjs';
|
|
2
|
+
import { P as PaymentAccept } from './types-BQvaF8lB.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* EVM Chain Adapter
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as AdapterConfig, C as ChainAdapter, S as SignedTransaction } from './types-
|
|
2
|
-
import { P as PaymentAccept } from './types-
|
|
1
|
+
import { A as AdapterConfig, C as ChainAdapter, S as SignedTransaction } from './types-ENcnkof8.js';
|
|
2
|
+
import { P as PaymentAccept } from './types-BQvaF8lB.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* EVM Chain Adapter
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { C as ChainAdapter, W as WalletSet } from './types-
|
|
2
|
-
import { A as AccessPassClientConfig } from './types-
|
|
1
|
+
import { C as ChainAdapter, W as WalletSet } from './types-DmqH9yD8.cjs';
|
|
2
|
+
import { A as AccessPassClientConfig } from './types-BQvaF8lB.cjs';
|
|
3
|
+
import { SponsoredRecommendation, SponsoredAccessSettlementInfo } from '@dexterai/x402-ads-types';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* x402 v2 Client
|
|
@@ -109,4 +110,70 @@ interface X402Client {
|
|
|
109
110
|
*/
|
|
110
111
|
declare function createX402Client(config: X402ClientConfig): X402Client;
|
|
111
112
|
|
|
112
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Sponsored Access (Ads for Agents) — Client Helpers
|
|
115
|
+
*
|
|
116
|
+
* Extract sponsored recommendations from x402 payment receipts and
|
|
117
|
+
* fire impression beacons to confirm delivery to the ad network.
|
|
118
|
+
*
|
|
119
|
+
* Recommendations are injected by the facilitator after settlement
|
|
120
|
+
* via the `extensions["sponsored-access"]` field. Publishers who enable
|
|
121
|
+
* `sponsoredAccess: true` in their middleware also inject them into
|
|
122
|
+
* the JSON response body as `_x402_sponsored`.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```typescript
|
|
126
|
+
* import { wrapFetch, getSponsoredRecommendations, fireImpressionBeacon } from '@dexterai/x402/client';
|
|
127
|
+
*
|
|
128
|
+
* const x402Fetch = wrapFetch(fetch, { walletPrivateKey: key });
|
|
129
|
+
* const response = await x402Fetch('https://api.example.com/data');
|
|
130
|
+
*
|
|
131
|
+
* const recs = getSponsoredRecommendations(response);
|
|
132
|
+
* if (recs) {
|
|
133
|
+
* console.log('Sponsored:', recs.map(r => `${r.sponsor}: ${r.description}`));
|
|
134
|
+
* await fireImpressionBeacon(response); // Confirm delivery to ad network
|
|
135
|
+
* }
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Extract the full sponsored-access extension data from a payment receipt.
|
|
141
|
+
* Returns undefined if no sponsored-access extension is present.
|
|
142
|
+
*/
|
|
143
|
+
declare function getSponsoredAccessInfo(response: Response): SponsoredAccessSettlementInfo | undefined;
|
|
144
|
+
/**
|
|
145
|
+
* Extract sponsored recommendations from an x402 payment response.
|
|
146
|
+
* Returns the recommendations array, or undefined if none present.
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```typescript
|
|
150
|
+
* const recs = getSponsoredRecommendations(response);
|
|
151
|
+
* if (recs) {
|
|
152
|
+
* for (const rec of recs) {
|
|
153
|
+
* console.log(`${rec.sponsor}: ${rec.description} — ${rec.resourceUrl}`);
|
|
154
|
+
* }
|
|
155
|
+
* }
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
declare function getSponsoredRecommendations(response: Response): SponsoredRecommendation[] | undefined;
|
|
159
|
+
/**
|
|
160
|
+
* Fire the impression beacon to confirm recommendation delivery to the ad network.
|
|
161
|
+
* This is a fire-and-forget GET request — failures are silently ignored.
|
|
162
|
+
*
|
|
163
|
+
* Call this after you've read the recommendations to help the ad network
|
|
164
|
+
* track delivery rates and verify impressions.
|
|
165
|
+
*
|
|
166
|
+
* @returns true if the beacon was fired (regardless of response), false if no beacon URL
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```typescript
|
|
170
|
+
* const recs = getSponsoredRecommendations(response);
|
|
171
|
+
* if (recs) {
|
|
172
|
+
* // Process recommendations...
|
|
173
|
+
* await fireImpressionBeacon(response);
|
|
174
|
+
* }
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
declare function fireImpressionBeacon(response: Response): Promise<boolean>;
|
|
178
|
+
|
|
179
|
+
export { type PaymentReceipt as P, type X402ClientConfig as X, type X402Client as a, getSponsoredRecommendations as b, createX402Client as c, getSponsoredAccessInfo as d, fireImpressionBeacon as f, getPaymentReceipt as g };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { C as ChainAdapter, W as WalletSet } from './types-
|
|
2
|
-
import { A as AccessPassClientConfig } from './types-
|
|
1
|
+
import { C as ChainAdapter, W as WalletSet } from './types-ENcnkof8.js';
|
|
2
|
+
import { A as AccessPassClientConfig } from './types-BQvaF8lB.js';
|
|
3
|
+
import { SponsoredRecommendation, SponsoredAccessSettlementInfo } from '@dexterai/x402-ads-types';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* x402 v2 Client
|
|
@@ -109,4 +110,70 @@ interface X402Client {
|
|
|
109
110
|
*/
|
|
110
111
|
declare function createX402Client(config: X402ClientConfig): X402Client;
|
|
111
112
|
|
|
112
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Sponsored Access (Ads for Agents) — Client Helpers
|
|
115
|
+
*
|
|
116
|
+
* Extract sponsored recommendations from x402 payment receipts and
|
|
117
|
+
* fire impression beacons to confirm delivery to the ad network.
|
|
118
|
+
*
|
|
119
|
+
* Recommendations are injected by the facilitator after settlement
|
|
120
|
+
* via the `extensions["sponsored-access"]` field. Publishers who enable
|
|
121
|
+
* `sponsoredAccess: true` in their middleware also inject them into
|
|
122
|
+
* the JSON response body as `_x402_sponsored`.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```typescript
|
|
126
|
+
* import { wrapFetch, getSponsoredRecommendations, fireImpressionBeacon } from '@dexterai/x402/client';
|
|
127
|
+
*
|
|
128
|
+
* const x402Fetch = wrapFetch(fetch, { walletPrivateKey: key });
|
|
129
|
+
* const response = await x402Fetch('https://api.example.com/data');
|
|
130
|
+
*
|
|
131
|
+
* const recs = getSponsoredRecommendations(response);
|
|
132
|
+
* if (recs) {
|
|
133
|
+
* console.log('Sponsored:', recs.map(r => `${r.sponsor}: ${r.description}`));
|
|
134
|
+
* await fireImpressionBeacon(response); // Confirm delivery to ad network
|
|
135
|
+
* }
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Extract the full sponsored-access extension data from a payment receipt.
|
|
141
|
+
* Returns undefined if no sponsored-access extension is present.
|
|
142
|
+
*/
|
|
143
|
+
declare function getSponsoredAccessInfo(response: Response): SponsoredAccessSettlementInfo | undefined;
|
|
144
|
+
/**
|
|
145
|
+
* Extract sponsored recommendations from an x402 payment response.
|
|
146
|
+
* Returns the recommendations array, or undefined if none present.
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```typescript
|
|
150
|
+
* const recs = getSponsoredRecommendations(response);
|
|
151
|
+
* if (recs) {
|
|
152
|
+
* for (const rec of recs) {
|
|
153
|
+
* console.log(`${rec.sponsor}: ${rec.description} — ${rec.resourceUrl}`);
|
|
154
|
+
* }
|
|
155
|
+
* }
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
declare function getSponsoredRecommendations(response: Response): SponsoredRecommendation[] | undefined;
|
|
159
|
+
/**
|
|
160
|
+
* Fire the impression beacon to confirm recommendation delivery to the ad network.
|
|
161
|
+
* This is a fire-and-forget GET request — failures are silently ignored.
|
|
162
|
+
*
|
|
163
|
+
* Call this after you've read the recommendations to help the ad network
|
|
164
|
+
* track delivery rates and verify impressions.
|
|
165
|
+
*
|
|
166
|
+
* @returns true if the beacon was fired (regardless of response), false if no beacon URL
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```typescript
|
|
170
|
+
* const recs = getSponsoredRecommendations(response);
|
|
171
|
+
* if (recs) {
|
|
172
|
+
* // Process recommendations...
|
|
173
|
+
* await fireImpressionBeacon(response);
|
|
174
|
+
* }
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
declare function fireImpressionBeacon(response: Response): Promise<boolean>;
|
|
178
|
+
|
|
179
|
+
export { type PaymentReceipt as P, type X402ClientConfig as X, type X402Client as a, getSponsoredRecommendations as b, createX402Client as c, getSponsoredAccessInfo as d, fireImpressionBeacon as f, getPaymentReceipt as g };
|
|
@@ -89,10 +89,10 @@ interface PaymentAccept {
|
|
|
89
89
|
scheme: 'exact';
|
|
90
90
|
/** CAIP-2 network identifier (v1: 'solana', v2: 'solana:5eykt...') */
|
|
91
91
|
network: string;
|
|
92
|
-
/** Payment amount in atomic units (x402 spec field
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
|
|
92
|
+
/** Payment amount in atomic units (x402 v2 spec field) */
|
|
93
|
+
amount: string;
|
|
94
|
+
/** @deprecated v1 field — use `amount` instead. Kept for backwards compatibility with v1 data. */
|
|
95
|
+
maxAmountRequired?: string;
|
|
96
96
|
/** Token address */
|
|
97
97
|
asset: string;
|
|
98
98
|
/** Seller's address to receive payment */
|
|
@@ -100,7 +100,7 @@ interface PaymentAccept {
|
|
|
100
100
|
/** Maximum seconds until payment expires */
|
|
101
101
|
maxTimeoutSeconds: number;
|
|
102
102
|
/** Chain-specific extra data */
|
|
103
|
-
extra
|
|
103
|
+
extra?: AcceptsExtra;
|
|
104
104
|
}
|
|
105
105
|
/**
|
|
106
106
|
* Full PaymentRequired structure (sent in PAYMENT-REQUIRED header)
|
|
@@ -114,6 +114,8 @@ interface PaymentRequired {
|
|
|
114
114
|
accepts: PaymentAccept[];
|
|
115
115
|
/** Optional error message */
|
|
116
116
|
error?: string;
|
|
117
|
+
/** Protocol extensions */
|
|
118
|
+
extensions?: Record<string, unknown>;
|
|
117
119
|
}
|
|
118
120
|
/**
|
|
119
121
|
* Response from /verify endpoint
|
|
@@ -89,10 +89,10 @@ interface PaymentAccept {
|
|
|
89
89
|
scheme: 'exact';
|
|
90
90
|
/** CAIP-2 network identifier (v1: 'solana', v2: 'solana:5eykt...') */
|
|
91
91
|
network: string;
|
|
92
|
-
/** Payment amount in atomic units (x402 spec field
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
|
|
92
|
+
/** Payment amount in atomic units (x402 v2 spec field) */
|
|
93
|
+
amount: string;
|
|
94
|
+
/** @deprecated v1 field — use `amount` instead. Kept for backwards compatibility with v1 data. */
|
|
95
|
+
maxAmountRequired?: string;
|
|
96
96
|
/** Token address */
|
|
97
97
|
asset: string;
|
|
98
98
|
/** Seller's address to receive payment */
|
|
@@ -100,7 +100,7 @@ interface PaymentAccept {
|
|
|
100
100
|
/** Maximum seconds until payment expires */
|
|
101
101
|
maxTimeoutSeconds: number;
|
|
102
102
|
/** Chain-specific extra data */
|
|
103
|
-
extra
|
|
103
|
+
extra?: AcceptsExtra;
|
|
104
104
|
}
|
|
105
105
|
/**
|
|
106
106
|
* Full PaymentRequired structure (sent in PAYMENT-REQUIRED header)
|
|
@@ -114,6 +114,8 @@ interface PaymentRequired {
|
|
|
114
114
|
accepts: PaymentAccept[];
|
|
115
115
|
/** Optional error message */
|
|
116
116
|
error?: string;
|
|
117
|
+
/** Protocol extensions */
|
|
118
|
+
extensions?: Record<string, unknown>;
|
|
117
119
|
}
|
|
118
120
|
/**
|
|
119
121
|
* Response from /verify endpoint
|
package/dist/utils/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/index.ts","../../src/types.ts","../../src/utils.ts"],"sourcesContent":["/**\n * @dexterai/x402 Utils\n *\n * Helper functions for x402 payments.\n *\n * @example\n * ```typescript\n * import { toAtomicUnits, fromAtomicUnits } from '@dexterai/x402/utils';\n *\n * const atomic = toAtomicUnits(0.05, 6); // '50000'\n * const human = fromAtomicUnits('50000', 6); // 0.05\n * ```\n */\n\nexport {\n toAtomicUnits,\n fromAtomicUnits,\n getChainFamily,\n getChainName,\n getExplorerUrl,\n type ChainFamily,\n} from '../utils';\n","/**\n * x402 v2 SDK — Shared Types\n *\n * Chain-agnostic types for x402 v2 payments.\n * Works with Solana, Base, and any future x402-compatible networks.\n */\n\n// ============================================================================\n// Network Constants\n// ============================================================================\n\n/** CAIP-2 network identifier for Solana mainnet */\nexport const SOLANA_MAINNET_NETWORK = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp';\n\n/** CAIP-2 network identifier for Base mainnet */\nexport const BASE_MAINNET_NETWORK = 'eip155:8453';\n\n/** Alias for Solana mainnet */\nexport const SOLANA_MAINNET = SOLANA_MAINNET_NETWORK;\n\n/** Alias for Base mainnet */\nexport const BASE_MAINNET = BASE_MAINNET_NETWORK;\n\n// ============================================================================\n// Asset Constants\n// ============================================================================\n\n/** USDC mint on Solana mainnet */\nexport const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';\n\n/** USDC address on Base mainnet */\nexport const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';\n\n// ============================================================================\n// Facilitator Constants\n// ============================================================================\n\n/** Dexter's public x402 v2 facilitator URL */\nexport const DEXTER_FACILITATOR_URL = 'https://x402.dexter.cash';\n\n// ============================================================================\n// PayTo Provider Types (for dynamic address resolution, e.g. Stripe)\n// ============================================================================\n\n/**\n * Context passed to a PayToProvider function.\n * Contains request-scoped information for dynamic address resolution.\n */\nexport interface PayToContext {\n /** The PAYMENT-SIGNATURE header value (present on retry/verify, undefined on initial 402) */\n paymentHeader?: string;\n /** Amount in atomic units (e.g., '10000' for 0.01 USDC) */\n amountAtomic?: string;\n /** The resource URL being accessed */\n resourceUrl?: string;\n}\n\n/**\n * Optional defaults a PayToProvider can advertise for auto-configuration.\n * Attached as `_x402Defaults` on the provider function.\n */\nexport interface PayToProviderDefaults {\n /** Default CAIP-2 network (e.g., 'eip155:8453' for Base) */\n network?: string;\n /** Default facilitator URL */\n facilitatorUrl?: string;\n}\n\n/**\n * A function that dynamically resolves a payment address.\n * Used for providers like Stripe that generate per-request deposit addresses.\n *\n * @example\n * ```typescript\n * import { stripePayTo } from '@dexterai/x402/server';\n *\n * const provider = stripePayTo(process.env.STRIPE_SECRET_KEY);\n * const address = await provider({ amountAtomic: '10000' });\n * ```\n */\nexport type PayToProvider = ((context: PayToContext) => Promise<string>) & {\n /** Auto-configuration defaults (set by provider factories like stripePayTo) */\n _x402Defaults?: PayToProviderDefaults;\n};\n\n// ============================================================================\n// Payment Types\n// ============================================================================\n\n/**\n * Asset configuration for payments\n */\nexport interface AssetConfig {\n /** Token address (mint on Solana, contract on EVM) */\n address: string;\n /** Token decimals */\n decimals: number;\n /** Optional: Human-readable symbol */\n symbol?: string;\n}\n\n/**\n * Resource info included in payment requirements\n */\nexport interface ResourceInfo {\n /** Resource URL */\n url: string;\n /** Human-readable description */\n description?: string;\n /** MIME type of the resource */\n mimeType?: string;\n}\n\n/**\n * Extra fields in payment requirements\n * Chain-specific fields may vary\n */\nexport interface AcceptsExtra {\n /** Facilitator address that pays tx fees (required for Solana) */\n feePayer?: string;\n /** Token decimals (optional - defaults to 6 for USDC) */\n decimals?: number;\n /** EIP-712: Token name (EVM only) */\n name?: string;\n /** EIP-712: Token version (EVM only) */\n version?: string;\n /** Additional chain-specific fields */\n [key: string]: unknown;\n}\n\n/**\n * A single payment option in the accepts array\n */\nexport interface PaymentAccept {\n /** x402 version (1 or 2, defaults to 2 if not specified) */\n x402Version?: 1 | 2;\n /** Payment scheme (always 'exact' for x402 v2) */\n scheme: 'exact';\n /** CAIP-2 network identifier (v1: 'solana', v2: 'solana:5eykt...') */\n network: string;\n /** Payment amount in atomic units (x402 spec field - REQUIRED) */\n maxAmountRequired: string;\n /** Alias for maxAmountRequired (for convenience) */\n amount?: string;\n /** Token address */\n asset: string;\n /** Seller's address to receive payment */\n payTo: string;\n /** Maximum seconds until payment expires */\n maxTimeoutSeconds: number;\n /** Chain-specific extra data */\n extra: AcceptsExtra;\n}\n\n/**\n * Full PaymentRequired structure (sent in PAYMENT-REQUIRED header)\n */\nexport interface PaymentRequired {\n /** x402 version (always 2) */\n x402Version: 2;\n /** Resource being accessed */\n resource: ResourceInfo;\n /** Available payment options */\n accepts: PaymentAccept[];\n /** Optional error message */\n error?: string;\n}\n\n/**\n * PaymentSignature structure (sent in PAYMENT-SIGNATURE header)\n */\nexport interface PaymentSignature {\n /** x402 version (always 2) */\n x402Version: 2;\n /** Resource being accessed */\n resource: ResourceInfo;\n /** The payment option that was accepted */\n accepted: PaymentAccept;\n /** The signed payment */\n payload: {\n /** Signed transaction (base64 for Solana, JSON for EVM) */\n transaction: string;\n };\n}\n\n// ============================================================================\n// Facilitator Response Types\n// ============================================================================\n\n/**\n * Response from /verify endpoint\n */\nexport interface VerifyResponse {\n /** Whether the payment is valid */\n isValid: boolean;\n /** Reason for invalidity (if invalid) */\n invalidReason?: string;\n /** Payer address */\n payer?: string;\n}\n\n/**\n * Response from /settle endpoint\n */\nexport interface SettleResponse {\n /** Whether settlement succeeded */\n success: boolean;\n /** Transaction signature/hash */\n transaction?: string;\n /** Network the payment was made on */\n network: string;\n /** Error reason (if failed) */\n errorReason?: string;\n /** Error code (if failed) */\n errorCode?: string;\n /** Payer address */\n payer?: string;\n /** Protocol extensions returned by the facilitator (e.g., sponsored-access recommendations) */\n extensions?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Access Pass Types\n// ============================================================================\n\n/**\n * A single access pass tier offered by a seller\n */\nexport interface AccessPassTier {\n /** Tier ID (e.g., '1h', '24h') */\n id: string;\n /** Human-readable label (e.g., '1 hour') */\n label: string;\n /** Duration in seconds */\n seconds: number;\n /** Price in USD (e.g., '0.50') */\n price: string;\n /** Price in atomic units (e.g., '500000') */\n priceAtomic: string;\n}\n\n/**\n * Access pass info returned in X-ACCESS-PASS-TIERS header\n */\nexport interface AccessPassInfo {\n /** Available tiers (if tier-based pricing) */\n tiers?: AccessPassTier[];\n /** Rate per hour in USD (if custom duration pricing) */\n ratePerHour?: string;\n /** Pass issuer identifier */\n issuer?: string;\n}\n\n/**\n * JWT claims inside an access pass token\n */\nexport interface AccessPassClaims {\n /** Subject — always 'x402-access-pass' */\n sub: string;\n /** Tier ID or 'custom' */\n tier: string;\n /** Duration in seconds */\n duration: number;\n /** Issued at (unix seconds) */\n iat: number;\n /** Expires at (unix seconds) */\n exp: number;\n /** Payer wallet address */\n payer: string;\n /** Network used for payment */\n network: string;\n /** Issuer identifier */\n iss: string;\n}\n\n/**\n * Client-side access pass configuration\n */\nexport interface AccessPassClientConfig {\n /** Enable access pass mode (default: true when this config is present) */\n enabled?: boolean;\n /** Preferred tier ID (e.g., '1h') — pick this tier if available */\n preferTier?: string;\n /** Preferred custom duration in seconds (e.g., 3600) */\n preferDuration?: number;\n /** Maximum amount willing to spend in USD (e.g., '2.00') */\n maxSpend?: string;\n /** Auto-renew expired passes (default: true) */\n autoRenew?: boolean;\n}\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\n/**\n * SDK error codes\n */\nexport type X402ErrorCode =\n // Client errors\n | 'missing_payment_required_header'\n | 'invalid_payment_required'\n | 'unsupported_network'\n | 'no_matching_payment_option'\n | 'no_solana_accept' // Legacy, kept for compatibility\n | 'missing_fee_payer'\n | 'missing_decimals'\n | 'missing_amount'\n | 'amount_exceeds_max'\n | 'insufficient_balance'\n | 'wallet_missing_sign_transaction'\n | 'wallet_not_connected'\n | 'transaction_build_failed'\n | 'payment_rejected'\n // Server errors\n | 'invalid_payment_signature'\n | 'facilitator_verify_failed'\n | 'facilitator_settle_failed'\n | 'facilitator_request_failed'\n | 'no_matching_requirement'\n // Access pass errors\n | 'access_pass_expired'\n | 'access_pass_invalid'\n | 'access_pass_tier_not_found'\n | 'access_pass_exceeds_max_spend';\n\n/**\n * Custom error class for x402 operations\n */\nexport class X402Error extends Error {\n /** Error code for programmatic handling */\n code: X402ErrorCode;\n /** Additional error details */\n details?: unknown;\n\n constructor(code: X402ErrorCode, message: string, details?: unknown) {\n super(message);\n this.name = 'X402Error';\n this.code = code;\n this.details = details;\n // Maintain proper prototype chain\n Object.setPrototypeOf(this, X402Error.prototype);\n }\n}\n","/**\n * Utility Functions\n *\n * Chain-agnostic helpers for x402 payments.\n */\n\nimport { SOLANA_MAINNET_NETWORK, BASE_MAINNET_NETWORK } from './types';\n\n// ============================================================================\n// Amount Conversion\n// ============================================================================\n\n/**\n * Convert human-readable amount to atomic units\n *\n * @param amount - Human-readable amount (e.g., 0.05 for $0.05)\n * @param decimals - Token decimals (e.g., 6 for USDC)\n * @returns Amount in atomic units as string\n *\n * @example\n * ```typescript\n * toAtomicUnits(0.05, 6) // '50000'\n * toAtomicUnits(1.50, 6) // '1500000'\n * ```\n */\nexport function toAtomicUnits(amount: number, decimals: number): string {\n const multiplier = Math.pow(10, decimals);\n return Math.floor(amount * multiplier).toString();\n}\n\n/**\n * Convert atomic units to human-readable amount\n *\n * @param atomicUnits - Amount in smallest units\n * @param decimals - Token decimals\n * @returns Human-readable amount\n *\n * @example\n * ```typescript\n * fromAtomicUnits('50000', 6) // 0.05\n * fromAtomicUnits(1500000n, 6) // 1.5\n * ```\n */\nexport function fromAtomicUnits(\n atomicUnits: string | bigint | number,\n decimals: number\n): number {\n const divisor = Math.pow(10, decimals);\n return Number(atomicUnits) / divisor;\n}\n\n// ============================================================================\n// Network Helpers\n// ============================================================================\n\n/**\n * Network type\n */\nexport type ChainFamily = 'solana' | 'evm' | 'unknown';\n\n/**\n * Get the chain family from a CAIP-2 network identifier\n *\n * @param network - CAIP-2 network identifier\n * @returns Chain family\n *\n * @example\n * ```typescript\n * getChainFamily('solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp') // 'solana'\n * getChainFamily('eip155:8453') // 'evm'\n * ```\n */\nexport function getChainFamily(network: string): ChainFamily {\n if (network.startsWith('solana:') || network === 'solana') {\n return 'solana';\n }\n if (network.startsWith('eip155:') || ['base', 'ethereum', 'arbitrum'].includes(network)) {\n return 'evm';\n }\n return 'unknown';\n}\n\n/**\n * Get default RPC URL for a network\n *\n * @param network - CAIP-2 network identifier\n * @returns Default RPC URL\n */\nexport function getDefaultRpcUrl(network: string): string {\n const family = getChainFamily(network);\n\n if (family === 'solana') {\n if (network.includes('devnet') || network === 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1') {\n return 'https://api.devnet.solana.com';\n }\n if (network.includes('testnet') || network === 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z') {\n return 'https://api.testnet.solana.com';\n }\n // Mainnet uses Dexter's RPC proxy\n return 'https://api.dexter.cash/api/solana/rpc';\n }\n\n if (family === 'evm') {\n // Extract chain ID from CAIP-2\n if (network.startsWith('eip155:')) {\n const chainId = network.split(':')[1];\n switch (chainId) {\n case '8453': return 'https://api.dexter.cash/api/base/rpc'; // Dexter proxy\n case '84532': return 'https://sepolia.base.org';\n case '1': return 'https://eth.llamarpc.com';\n case '42161': return 'https://arb1.arbitrum.io/rpc';\n default: return 'https://api.dexter.cash/api/base/rpc';\n }\n }\n // Legacy names\n if (network === 'base') return 'https://api.dexter.cash/api/base/rpc';\n if (network === 'ethereum') return 'https://eth.llamarpc.com';\n if (network === 'arbitrum') return 'https://arb1.arbitrum.io/rpc';\n return 'https://api.dexter.cash/api/base/rpc';\n }\n\n // Unknown - return Dexter's Solana proxy\n return 'https://api.dexter.cash/api/solana/rpc';\n}\n\n/**\n * Get human-readable chain name\n *\n * @param network - CAIP-2 network identifier\n * @returns Human-readable name\n */\nexport function getChainName(network: string): string {\n const mapping: Record<string, string> = {\n [SOLANA_MAINNET_NETWORK]: 'Solana',\n 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1': 'Solana Devnet',\n 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z': 'Solana Testnet',\n 'solana': 'Solana',\n [BASE_MAINNET_NETWORK]: 'Base',\n 'eip155:84532': 'Base Sepolia',\n 'eip155:1': 'Ethereum',\n 'eip155:42161': 'Arbitrum One',\n 'base': 'Base',\n 'ethereum': 'Ethereum',\n 'arbitrum': 'Arbitrum',\n };\n return mapping[network] || network;\n}\n\n// ============================================================================\n// Transaction URL Helpers\n// ============================================================================\n\n/**\n * Get explorer URL for a transaction\n *\n * @param txSignature - Transaction signature/hash\n * @param network - CAIP-2 network identifier\n * @returns Explorer URL\n */\nexport function getExplorerUrl(txSignature: string, network: string): string {\n const family = getChainFamily(network);\n\n if (family === 'solana') {\n const isDevnet = network.includes('devnet') || network === 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1';\n if (isDevnet) {\n return `https://solscan.io/tx/${txSignature}?cluster=devnet`;\n }\n // Prefer Orb Markets for mainnet\n return `https://www.orbmarkets.io/tx/${txSignature}`;\n }\n\n if (family === 'evm') {\n // Extract chain ID\n let chainId = '8453'; // Default to Base\n if (network.startsWith('eip155:')) {\n chainId = network.split(':')[1];\n } else if (network === 'ethereum') {\n chainId = '1';\n } else if (network === 'arbitrum') {\n chainId = '42161';\n }\n\n switch (chainId) {\n case '8453': return `https://basescan.org/tx/${txSignature}`;\n case '84532': return `https://sepolia.basescan.org/tx/${txSignature}`;\n case '1': return `https://etherscan.io/tx/${txSignature}`;\n case '42161': return `https://arbiscan.io/tx/${txSignature}`;\n default: return `https://basescan.org/tx/${txSignature}`;\n }\n }\n\n return `https://solscan.io/tx/${txSignature}`;\n}\n\n// ============================================================================\n// Encoding Helpers\n// ============================================================================\n\n/**\n * Unicode-safe base64 encode a string.\n * Works in both Node.js and browsers, handling characters above U+00FF\n * that would cause btoa() to throw InvalidCharacterError.\n */\nfunction safeBase64Encode(str: string): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(str, 'utf-8').toString('base64');\n }\n // Browser fallback: encode UTF-8 bytes via TextEncoder\n const bytes = new TextEncoder().encode(str);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n\n/**\n * Unicode-safe base64 decode a string.\n * Works in both Node.js and browsers.\n */\nfunction safeBase64Decode(encoded: string): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(encoded, 'base64').toString('utf-8');\n }\n // Browser fallback: decode via atob then UTF-8\n const binary = atob(encoded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return new TextDecoder().decode(bytes);\n}\n\n/**\n * Encode an object as base64 JSON (Unicode-safe)\n */\nexport function encodeBase64Json(obj: unknown): string {\n return safeBase64Encode(JSON.stringify(obj));\n}\n\n/**\n * Decode base64 JSON to object (Unicode-safe)\n */\nexport function decodeBase64Json<T>(encoded: string): T {\n return JSON.parse(safeBase64Decode(encoded)) as T;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACYO,IAAM,yBAAyB;AAG/B,IAAM,uBAAuB;;;ACU7B,SAAS,cAAc,QAAgB,UAA0B;AACtE,QAAM,aAAa,KAAK,IAAI,IAAI,QAAQ;AACxC,SAAO,KAAK,MAAM,SAAS,UAAU,EAAE,SAAS;AAClD;AAeO,SAAS,gBACd,aACA,UACQ;AACR,QAAM,UAAU,KAAK,IAAI,IAAI,QAAQ;AACrC,SAAO,OAAO,WAAW,IAAI;AAC/B;AAuBO,SAAS,eAAe,SAA8B;AAC3D,MAAI,QAAQ,WAAW,SAAS,KAAK,YAAY,UAAU;AACzD,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,WAAW,SAAS,KAAK,CAAC,QAAQ,YAAY,UAAU,EAAE,SAAS,OAAO,GAAG;AACvF,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAmDO,SAAS,aAAa,SAAyB;AACpD,QAAM,UAAkC;AAAA,IACtC,CAAC,sBAAsB,GAAG;AAAA,IAC1B,2CAA2C;AAAA,IAC3C,2CAA2C;AAAA,IAC3C,UAAU;AAAA,IACV,CAAC,oBAAoB,GAAG;AAAA,IACxB,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,EACd;AACA,SAAO,QAAQ,OAAO,KAAK;AAC7B;AAaO,SAAS,eAAe,aAAqB,SAAyB;AAC3E,QAAM,SAAS,eAAe,OAAO;AAErC,MAAI,WAAW,UAAU;AACvB,UAAM,WAAW,QAAQ,SAAS,QAAQ,KAAK,YAAY;AAC3D,QAAI,UAAU;AACZ,aAAO,yBAAyB,WAAW;AAAA,IAC7C;AAEA,WAAO,gCAAgC,WAAW;AAAA,EACpD;AAEA,MAAI,WAAW,OAAO;AAEpB,QAAI,UAAU;AACd,QAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,gBAAU,QAAQ,MAAM,GAAG,EAAE,CAAC;AAAA,IAChC,WAAW,YAAY,YAAY;AACjC,gBAAU;AAAA,IACZ,WAAW,YAAY,YAAY;AACjC,gBAAU;AAAA,IACZ;AAEA,YAAQ,SAAS;AAAA,MACf,KAAK;AAAQ,eAAO,2BAA2B,WAAW;AAAA,MAC1D,KAAK;AAAS,eAAO,mCAAmC,WAAW;AAAA,MACnE,KAAK;AAAK,eAAO,2BAA2B,WAAW;AAAA,MACvD,KAAK;AAAS,eAAO,0BAA0B,WAAW;AAAA,MAC1D;AAAS,eAAO,2BAA2B,WAAW;AAAA,IACxD;AAAA,EACF;AAEA,SAAO,yBAAyB,WAAW;AAC7C;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/index.ts","../../src/types.ts","../../src/utils.ts"],"sourcesContent":["/**\n * @dexterai/x402 Utils\n *\n * Helper functions for x402 payments.\n *\n * @example\n * ```typescript\n * import { toAtomicUnits, fromAtomicUnits } from '@dexterai/x402/utils';\n *\n * const atomic = toAtomicUnits(0.05, 6); // '50000'\n * const human = fromAtomicUnits('50000', 6); // 0.05\n * ```\n */\n\nexport {\n toAtomicUnits,\n fromAtomicUnits,\n getChainFamily,\n getChainName,\n getExplorerUrl,\n type ChainFamily,\n} from '../utils';\n","/**\n * x402 v2 SDK — Shared Types\n *\n * Chain-agnostic types for x402 v2 payments.\n * Works with Solana, Base, and any future x402-compatible networks.\n */\n\n// ============================================================================\n// Network Constants\n// ============================================================================\n\n/** CAIP-2 network identifier for Solana mainnet */\nexport const SOLANA_MAINNET_NETWORK = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp';\n\n/** CAIP-2 network identifier for Base mainnet */\nexport const BASE_MAINNET_NETWORK = 'eip155:8453';\n\n/** Alias for Solana mainnet */\nexport const SOLANA_MAINNET = SOLANA_MAINNET_NETWORK;\n\n/** Alias for Base mainnet */\nexport const BASE_MAINNET = BASE_MAINNET_NETWORK;\n\n// ============================================================================\n// Asset Constants\n// ============================================================================\n\n/** USDC mint on Solana mainnet */\nexport const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';\n\n/** USDC address on Base mainnet */\nexport const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';\n\n// ============================================================================\n// Facilitator Constants\n// ============================================================================\n\n/** Dexter's public x402 v2 facilitator URL */\nexport const DEXTER_FACILITATOR_URL = 'https://x402.dexter.cash';\n\n// ============================================================================\n// PayTo Provider Types (for dynamic address resolution, e.g. Stripe)\n// ============================================================================\n\n/**\n * Context passed to a PayToProvider function.\n * Contains request-scoped information for dynamic address resolution.\n */\nexport interface PayToContext {\n /** The PAYMENT-SIGNATURE header value (present on retry/verify, undefined on initial 402) */\n paymentHeader?: string;\n /** Amount in atomic units (e.g., '10000' for 0.01 USDC) */\n amountAtomic?: string;\n /** The resource URL being accessed */\n resourceUrl?: string;\n}\n\n/**\n * Optional defaults a PayToProvider can advertise for auto-configuration.\n * Attached as `_x402Defaults` on the provider function.\n */\nexport interface PayToProviderDefaults {\n /** Default CAIP-2 network (e.g., 'eip155:8453' for Base) */\n network?: string;\n /** Default facilitator URL */\n facilitatorUrl?: string;\n}\n\n/**\n * A function that dynamically resolves a payment address.\n * Used for providers like Stripe that generate per-request deposit addresses.\n *\n * @example\n * ```typescript\n * import { stripePayTo } from '@dexterai/x402/server';\n *\n * const provider = stripePayTo(process.env.STRIPE_SECRET_KEY);\n * const address = await provider({ amountAtomic: '10000' });\n * ```\n */\nexport type PayToProvider = ((context: PayToContext) => Promise<string>) & {\n /** Auto-configuration defaults (set by provider factories like stripePayTo) */\n _x402Defaults?: PayToProviderDefaults;\n};\n\n// ============================================================================\n// Payment Types\n// ============================================================================\n\n/**\n * Asset configuration for payments\n */\nexport interface AssetConfig {\n /** Token address (mint on Solana, contract on EVM) */\n address: string;\n /** Token decimals */\n decimals: number;\n /** Optional: Human-readable symbol */\n symbol?: string;\n}\n\n/**\n * Resource info included in payment requirements\n */\nexport interface ResourceInfo {\n /** Resource URL */\n url: string;\n /** Human-readable description */\n description?: string;\n /** MIME type of the resource */\n mimeType?: string;\n}\n\n/**\n * Extra fields in payment requirements\n * Chain-specific fields may vary\n */\nexport interface AcceptsExtra {\n /** Facilitator address that pays tx fees (required for Solana) */\n feePayer?: string;\n /** Token decimals (optional - defaults to 6 for USDC) */\n decimals?: number;\n /** EIP-712: Token name (EVM only) */\n name?: string;\n /** EIP-712: Token version (EVM only) */\n version?: string;\n /** Additional chain-specific fields */\n [key: string]: unknown;\n}\n\n/**\n * A single payment option in the accepts array\n */\nexport interface PaymentAccept {\n /** x402 version (1 or 2, defaults to 2 if not specified) */\n x402Version?: 1 | 2;\n /** Payment scheme (always 'exact' for x402 v2) */\n scheme: 'exact';\n /** CAIP-2 network identifier (v1: 'solana', v2: 'solana:5eykt...') */\n network: string;\n /** Payment amount in atomic units (x402 v2 spec field) */\n amount: string;\n /** @deprecated v1 field — use `amount` instead. Kept for backwards compatibility with v1 data. */\n maxAmountRequired?: string;\n /** Token address */\n asset: string;\n /** Seller's address to receive payment */\n payTo: string;\n /** Maximum seconds until payment expires */\n maxTimeoutSeconds: number;\n /** Chain-specific extra data */\n extra?: AcceptsExtra;\n}\n\n/**\n * Full PaymentRequired structure (sent in PAYMENT-REQUIRED header)\n */\nexport interface PaymentRequired {\n /** x402 version (always 2) */\n x402Version: 2;\n /** Resource being accessed */\n resource: ResourceInfo;\n /** Available payment options */\n accepts: PaymentAccept[];\n /** Optional error message */\n error?: string;\n /** Protocol extensions */\n extensions?: Record<string, unknown>;\n}\n\n/**\n * PaymentSignature structure (sent in PAYMENT-SIGNATURE header)\n */\nexport interface PaymentSignature {\n /** x402 version (always 2) */\n x402Version: 2;\n /** Resource being accessed */\n resource: ResourceInfo;\n /** The payment option that was accepted */\n accepted: PaymentAccept;\n /** The signed payment */\n payload: {\n /** Signed transaction (base64 for Solana, JSON for EVM) */\n transaction: string;\n };\n /** Protocol extensions */\n extensions?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Facilitator Response Types\n// ============================================================================\n\n/**\n * Response from /verify endpoint\n */\nexport interface VerifyResponse {\n /** Whether the payment is valid */\n isValid: boolean;\n /** Reason for invalidity (if invalid) */\n invalidReason?: string;\n /** Payer address */\n payer?: string;\n}\n\n/**\n * Response from /settle endpoint\n */\nexport interface SettleResponse {\n /** Whether settlement succeeded */\n success: boolean;\n /** Transaction signature/hash */\n transaction?: string;\n /** Network the payment was made on */\n network: string;\n /** Error reason (if failed) */\n errorReason?: string;\n /** Error code (if failed) */\n errorCode?: string;\n /** Payer address */\n payer?: string;\n /** Protocol extensions returned by the facilitator (e.g., sponsored-access recommendations) */\n extensions?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Access Pass Types\n// ============================================================================\n\n/**\n * A single access pass tier offered by a seller\n */\nexport interface AccessPassTier {\n /** Tier ID (e.g., '1h', '24h') */\n id: string;\n /** Human-readable label (e.g., '1 hour') */\n label: string;\n /** Duration in seconds */\n seconds: number;\n /** Price in USD (e.g., '0.50') */\n price: string;\n /** Price in atomic units (e.g., '500000') */\n priceAtomic: string;\n}\n\n/**\n * Access pass info returned in X-ACCESS-PASS-TIERS header\n */\nexport interface AccessPassInfo {\n /** Available tiers (if tier-based pricing) */\n tiers?: AccessPassTier[];\n /** Rate per hour in USD (if custom duration pricing) */\n ratePerHour?: string;\n /** Pass issuer identifier */\n issuer?: string;\n}\n\n/**\n * JWT claims inside an access pass token\n */\nexport interface AccessPassClaims {\n /** Subject — always 'x402-access-pass' */\n sub: string;\n /** Tier ID or 'custom' */\n tier: string;\n /** Duration in seconds */\n duration: number;\n /** Issued at (unix seconds) */\n iat: number;\n /** Expires at (unix seconds) */\n exp: number;\n /** Payer wallet address */\n payer: string;\n /** Network used for payment */\n network: string;\n /** Issuer identifier */\n iss: string;\n}\n\n/**\n * Client-side access pass configuration\n */\nexport interface AccessPassClientConfig {\n /** Enable access pass mode (default: true when this config is present) */\n enabled?: boolean;\n /** Preferred tier ID (e.g., '1h') — pick this tier if available */\n preferTier?: string;\n /** Preferred custom duration in seconds (e.g., 3600) */\n preferDuration?: number;\n /** Maximum amount willing to spend in USD (e.g., '2.00') */\n maxSpend?: string;\n /** Auto-renew expired passes (default: true) */\n autoRenew?: boolean;\n}\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\n/**\n * SDK error codes\n */\nexport type X402ErrorCode =\n // Client errors\n | 'missing_payment_required_header'\n | 'invalid_payment_required'\n | 'unsupported_network'\n | 'no_matching_payment_option'\n | 'no_solana_accept' // Legacy, kept for compatibility\n | 'missing_fee_payer'\n | 'missing_decimals'\n | 'missing_amount'\n | 'amount_exceeds_max'\n | 'insufficient_balance'\n | 'wallet_missing_sign_transaction'\n | 'wallet_not_connected'\n | 'transaction_build_failed'\n | 'payment_rejected'\n // Server errors\n | 'invalid_payment_signature'\n | 'facilitator_verify_failed'\n | 'facilitator_settle_failed'\n | 'facilitator_request_failed'\n | 'no_matching_requirement'\n // Access pass errors\n | 'access_pass_expired'\n | 'access_pass_invalid'\n | 'access_pass_tier_not_found'\n | 'access_pass_exceeds_max_spend';\n\n/**\n * Custom error class for x402 operations\n */\nexport class X402Error extends Error {\n /** Error code for programmatic handling */\n code: X402ErrorCode;\n /** Additional error details */\n details?: unknown;\n\n constructor(code: X402ErrorCode, message: string, details?: unknown) {\n super(message);\n this.name = 'X402Error';\n this.code = code;\n this.details = details;\n // Maintain proper prototype chain\n Object.setPrototypeOf(this, X402Error.prototype);\n }\n}\n","/**\n * Utility Functions\n *\n * Chain-agnostic helpers for x402 payments.\n */\n\nimport { SOLANA_MAINNET_NETWORK, BASE_MAINNET_NETWORK } from './types';\n\n// ============================================================================\n// Amount Conversion\n// ============================================================================\n\n/**\n * Convert human-readable amount to atomic units\n *\n * @param amount - Human-readable amount (e.g., 0.05 for $0.05)\n * @param decimals - Token decimals (e.g., 6 for USDC)\n * @returns Amount in atomic units as string\n *\n * @example\n * ```typescript\n * toAtomicUnits(0.05, 6) // '50000'\n * toAtomicUnits(1.50, 6) // '1500000'\n * ```\n */\nexport function toAtomicUnits(amount: number, decimals: number): string {\n const multiplier = Math.pow(10, decimals);\n return Math.floor(amount * multiplier).toString();\n}\n\n/**\n * Convert atomic units to human-readable amount\n *\n * @param atomicUnits - Amount in smallest units\n * @param decimals - Token decimals\n * @returns Human-readable amount\n *\n * @example\n * ```typescript\n * fromAtomicUnits('50000', 6) // 0.05\n * fromAtomicUnits(1500000n, 6) // 1.5\n * ```\n */\nexport function fromAtomicUnits(\n atomicUnits: string | bigint | number,\n decimals: number\n): number {\n const divisor = Math.pow(10, decimals);\n return Number(atomicUnits) / divisor;\n}\n\n// ============================================================================\n// Network Helpers\n// ============================================================================\n\n/**\n * Network type\n */\nexport type ChainFamily = 'solana' | 'evm' | 'unknown';\n\n/**\n * Get the chain family from a CAIP-2 network identifier\n *\n * @param network - CAIP-2 network identifier\n * @returns Chain family\n *\n * @example\n * ```typescript\n * getChainFamily('solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp') // 'solana'\n * getChainFamily('eip155:8453') // 'evm'\n * ```\n */\nexport function getChainFamily(network: string): ChainFamily {\n if (network.startsWith('solana:') || network === 'solana') {\n return 'solana';\n }\n if (network.startsWith('eip155:') || ['base', 'ethereum', 'arbitrum'].includes(network)) {\n return 'evm';\n }\n return 'unknown';\n}\n\n/**\n * Get default RPC URL for a network\n *\n * @param network - CAIP-2 network identifier\n * @returns Default RPC URL\n */\nexport function getDefaultRpcUrl(network: string): string {\n const family = getChainFamily(network);\n\n if (family === 'solana') {\n if (network.includes('devnet') || network === 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1') {\n return 'https://api.devnet.solana.com';\n }\n if (network.includes('testnet') || network === 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z') {\n return 'https://api.testnet.solana.com';\n }\n // Mainnet uses Dexter's RPC proxy\n return 'https://api.dexter.cash/api/solana/rpc';\n }\n\n if (family === 'evm') {\n // Extract chain ID from CAIP-2\n if (network.startsWith('eip155:')) {\n const chainId = network.split(':')[1];\n switch (chainId) {\n case '8453': return 'https://api.dexter.cash/api/base/rpc'; // Dexter proxy\n case '84532': return 'https://sepolia.base.org';\n case '1': return 'https://eth.llamarpc.com';\n case '42161': return 'https://arb1.arbitrum.io/rpc';\n default: return 'https://api.dexter.cash/api/base/rpc';\n }\n }\n // Legacy names\n if (network === 'base') return 'https://api.dexter.cash/api/base/rpc';\n if (network === 'ethereum') return 'https://eth.llamarpc.com';\n if (network === 'arbitrum') return 'https://arb1.arbitrum.io/rpc';\n return 'https://api.dexter.cash/api/base/rpc';\n }\n\n // Unknown - return Dexter's Solana proxy\n return 'https://api.dexter.cash/api/solana/rpc';\n}\n\n/**\n * Get human-readable chain name\n *\n * @param network - CAIP-2 network identifier\n * @returns Human-readable name\n */\nexport function getChainName(network: string): string {\n const mapping: Record<string, string> = {\n [SOLANA_MAINNET_NETWORK]: 'Solana',\n 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1': 'Solana Devnet',\n 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z': 'Solana Testnet',\n 'solana': 'Solana',\n [BASE_MAINNET_NETWORK]: 'Base',\n 'eip155:84532': 'Base Sepolia',\n 'eip155:1': 'Ethereum',\n 'eip155:42161': 'Arbitrum One',\n 'base': 'Base',\n 'ethereum': 'Ethereum',\n 'arbitrum': 'Arbitrum',\n };\n return mapping[network] || network;\n}\n\n// ============================================================================\n// Transaction URL Helpers\n// ============================================================================\n\n/**\n * Get explorer URL for a transaction\n *\n * @param txSignature - Transaction signature/hash\n * @param network - CAIP-2 network identifier\n * @returns Explorer URL\n */\nexport function getExplorerUrl(txSignature: string, network: string): string {\n const family = getChainFamily(network);\n\n if (family === 'solana') {\n const isDevnet = network.includes('devnet') || network === 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1';\n if (isDevnet) {\n return `https://solscan.io/tx/${txSignature}?cluster=devnet`;\n }\n // Prefer Orb Markets for mainnet\n return `https://www.orbmarkets.io/tx/${txSignature}`;\n }\n\n if (family === 'evm') {\n // Extract chain ID\n let chainId = '8453'; // Default to Base\n if (network.startsWith('eip155:')) {\n chainId = network.split(':')[1];\n } else if (network === 'ethereum') {\n chainId = '1';\n } else if (network === 'arbitrum') {\n chainId = '42161';\n }\n\n switch (chainId) {\n case '8453': return `https://basescan.org/tx/${txSignature}`;\n case '84532': return `https://sepolia.basescan.org/tx/${txSignature}`;\n case '1': return `https://etherscan.io/tx/${txSignature}`;\n case '42161': return `https://arbiscan.io/tx/${txSignature}`;\n default: return `https://basescan.org/tx/${txSignature}`;\n }\n }\n\n return `https://solscan.io/tx/${txSignature}`;\n}\n\n// ============================================================================\n// Encoding Helpers\n// ============================================================================\n\n/**\n * Unicode-safe base64 encode a string.\n * Works in both Node.js and browsers, handling characters above U+00FF\n * that would cause btoa() to throw InvalidCharacterError.\n */\nfunction safeBase64Encode(str: string): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(str, 'utf-8').toString('base64');\n }\n // Browser fallback: encode UTF-8 bytes via TextEncoder\n const bytes = new TextEncoder().encode(str);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n\n/**\n * Unicode-safe base64 decode a string.\n * Works in both Node.js and browsers.\n */\nfunction safeBase64Decode(encoded: string): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(encoded, 'base64').toString('utf-8');\n }\n // Browser fallback: decode via atob then UTF-8\n const binary = atob(encoded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return new TextDecoder().decode(bytes);\n}\n\n/**\n * Encode an object as base64 JSON (Unicode-safe)\n */\nexport function encodeBase64Json(obj: unknown): string {\n return safeBase64Encode(JSON.stringify(obj));\n}\n\n/**\n * Decode base64 JSON to object (Unicode-safe)\n */\nexport function decodeBase64Json<T>(encoded: string): T {\n return JSON.parse(safeBase64Decode(encoded)) as T;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACYO,IAAM,yBAAyB;AAG/B,IAAM,uBAAuB;;;ACU7B,SAAS,cAAc,QAAgB,UAA0B;AACtE,QAAM,aAAa,KAAK,IAAI,IAAI,QAAQ;AACxC,SAAO,KAAK,MAAM,SAAS,UAAU,EAAE,SAAS;AAClD;AAeO,SAAS,gBACd,aACA,UACQ;AACR,QAAM,UAAU,KAAK,IAAI,IAAI,QAAQ;AACrC,SAAO,OAAO,WAAW,IAAI;AAC/B;AAuBO,SAAS,eAAe,SAA8B;AAC3D,MAAI,QAAQ,WAAW,SAAS,KAAK,YAAY,UAAU;AACzD,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,WAAW,SAAS,KAAK,CAAC,QAAQ,YAAY,UAAU,EAAE,SAAS,OAAO,GAAG;AACvF,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAmDO,SAAS,aAAa,SAAyB;AACpD,QAAM,UAAkC;AAAA,IACtC,CAAC,sBAAsB,GAAG;AAAA,IAC1B,2CAA2C;AAAA,IAC3C,2CAA2C;AAAA,IAC3C,UAAU;AAAA,IACV,CAAC,oBAAoB,GAAG;AAAA,IACxB,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,EACd;AACA,SAAO,QAAQ,OAAO,KAAK;AAC7B;AAaO,SAAS,eAAe,aAAqB,SAAyB;AAC3E,QAAM,SAAS,eAAe,OAAO;AAErC,MAAI,WAAW,UAAU;AACvB,UAAM,WAAW,QAAQ,SAAS,QAAQ,KAAK,YAAY;AAC3D,QAAI,UAAU;AACZ,aAAO,yBAAyB,WAAW;AAAA,IAC7C;AAEA,WAAO,gCAAgC,WAAW;AAAA,EACpD;AAEA,MAAI,WAAW,OAAO;AAEpB,QAAI,UAAU;AACd,QAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,gBAAU,QAAQ,MAAM,GAAG,EAAE,CAAC;AAAA,IAChC,WAAW,YAAY,YAAY;AACjC,gBAAU;AAAA,IACZ,WAAW,YAAY,YAAY;AACjC,gBAAU;AAAA,IACZ;AAEA,YAAQ,SAAS;AAAA,MACf,KAAK;AAAQ,eAAO,2BAA2B,WAAW;AAAA,MAC1D,KAAK;AAAS,eAAO,mCAAmC,WAAW;AAAA,MACnE,KAAK;AAAK,eAAO,2BAA2B,WAAW;AAAA,MACvD,KAAK;AAAS,eAAO,0BAA0B,WAAW;AAAA,MAC1D;AAAS,eAAO,2BAA2B,WAAW;AAAA,IACxD;AAAA,EACF;AAEA,SAAO,yBAAyB,WAAW;AAC7C;","names":[]}
|
package/dist/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/types.ts","../../src/utils.ts"],"sourcesContent":["/**\n * x402 v2 SDK — Shared Types\n *\n * Chain-agnostic types for x402 v2 payments.\n * Works with Solana, Base, and any future x402-compatible networks.\n */\n\n// ============================================================================\n// Network Constants\n// ============================================================================\n\n/** CAIP-2 network identifier for Solana mainnet */\nexport const SOLANA_MAINNET_NETWORK = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp';\n\n/** CAIP-2 network identifier for Base mainnet */\nexport const BASE_MAINNET_NETWORK = 'eip155:8453';\n\n/** Alias for Solana mainnet */\nexport const SOLANA_MAINNET = SOLANA_MAINNET_NETWORK;\n\n/** Alias for Base mainnet */\nexport const BASE_MAINNET = BASE_MAINNET_NETWORK;\n\n// ============================================================================\n// Asset Constants\n// ============================================================================\n\n/** USDC mint on Solana mainnet */\nexport const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';\n\n/** USDC address on Base mainnet */\nexport const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';\n\n// ============================================================================\n// Facilitator Constants\n// ============================================================================\n\n/** Dexter's public x402 v2 facilitator URL */\nexport const DEXTER_FACILITATOR_URL = 'https://x402.dexter.cash';\n\n// ============================================================================\n// PayTo Provider Types (for dynamic address resolution, e.g. Stripe)\n// ============================================================================\n\n/**\n * Context passed to a PayToProvider function.\n * Contains request-scoped information for dynamic address resolution.\n */\nexport interface PayToContext {\n /** The PAYMENT-SIGNATURE header value (present on retry/verify, undefined on initial 402) */\n paymentHeader?: string;\n /** Amount in atomic units (e.g., '10000' for 0.01 USDC) */\n amountAtomic?: string;\n /** The resource URL being accessed */\n resourceUrl?: string;\n}\n\n/**\n * Optional defaults a PayToProvider can advertise for auto-configuration.\n * Attached as `_x402Defaults` on the provider function.\n */\nexport interface PayToProviderDefaults {\n /** Default CAIP-2 network (e.g., 'eip155:8453' for Base) */\n network?: string;\n /** Default facilitator URL */\n facilitatorUrl?: string;\n}\n\n/**\n * A function that dynamically resolves a payment address.\n * Used for providers like Stripe that generate per-request deposit addresses.\n *\n * @example\n * ```typescript\n * import { stripePayTo } from '@dexterai/x402/server';\n *\n * const provider = stripePayTo(process.env.STRIPE_SECRET_KEY);\n * const address = await provider({ amountAtomic: '10000' });\n * ```\n */\nexport type PayToProvider = ((context: PayToContext) => Promise<string>) & {\n /** Auto-configuration defaults (set by provider factories like stripePayTo) */\n _x402Defaults?: PayToProviderDefaults;\n};\n\n// ============================================================================\n// Payment Types\n// ============================================================================\n\n/**\n * Asset configuration for payments\n */\nexport interface AssetConfig {\n /** Token address (mint on Solana, contract on EVM) */\n address: string;\n /** Token decimals */\n decimals: number;\n /** Optional: Human-readable symbol */\n symbol?: string;\n}\n\n/**\n * Resource info included in payment requirements\n */\nexport interface ResourceInfo {\n /** Resource URL */\n url: string;\n /** Human-readable description */\n description?: string;\n /** MIME type of the resource */\n mimeType?: string;\n}\n\n/**\n * Extra fields in payment requirements\n * Chain-specific fields may vary\n */\nexport interface AcceptsExtra {\n /** Facilitator address that pays tx fees (required for Solana) */\n feePayer?: string;\n /** Token decimals (optional - defaults to 6 for USDC) */\n decimals?: number;\n /** EIP-712: Token name (EVM only) */\n name?: string;\n /** EIP-712: Token version (EVM only) */\n version?: string;\n /** Additional chain-specific fields */\n [key: string]: unknown;\n}\n\n/**\n * A single payment option in the accepts array\n */\nexport interface PaymentAccept {\n /** x402 version (1 or 2, defaults to 2 if not specified) */\n x402Version?: 1 | 2;\n /** Payment scheme (always 'exact' for x402 v2) */\n scheme: 'exact';\n /** CAIP-2 network identifier (v1: 'solana', v2: 'solana:5eykt...') */\n network: string;\n /** Payment amount in atomic units (x402 spec field - REQUIRED) */\n maxAmountRequired: string;\n /** Alias for maxAmountRequired (for convenience) */\n amount?: string;\n /** Token address */\n asset: string;\n /** Seller's address to receive payment */\n payTo: string;\n /** Maximum seconds until payment expires */\n maxTimeoutSeconds: number;\n /** Chain-specific extra data */\n extra: AcceptsExtra;\n}\n\n/**\n * Full PaymentRequired structure (sent in PAYMENT-REQUIRED header)\n */\nexport interface PaymentRequired {\n /** x402 version (always 2) */\n x402Version: 2;\n /** Resource being accessed */\n resource: ResourceInfo;\n /** Available payment options */\n accepts: PaymentAccept[];\n /** Optional error message */\n error?: string;\n}\n\n/**\n * PaymentSignature structure (sent in PAYMENT-SIGNATURE header)\n */\nexport interface PaymentSignature {\n /** x402 version (always 2) */\n x402Version: 2;\n /** Resource being accessed */\n resource: ResourceInfo;\n /** The payment option that was accepted */\n accepted: PaymentAccept;\n /** The signed payment */\n payload: {\n /** Signed transaction (base64 for Solana, JSON for EVM) */\n transaction: string;\n };\n}\n\n// ============================================================================\n// Facilitator Response Types\n// ============================================================================\n\n/**\n * Response from /verify endpoint\n */\nexport interface VerifyResponse {\n /** Whether the payment is valid */\n isValid: boolean;\n /** Reason for invalidity (if invalid) */\n invalidReason?: string;\n /** Payer address */\n payer?: string;\n}\n\n/**\n * Response from /settle endpoint\n */\nexport interface SettleResponse {\n /** Whether settlement succeeded */\n success: boolean;\n /** Transaction signature/hash */\n transaction?: string;\n /** Network the payment was made on */\n network: string;\n /** Error reason (if failed) */\n errorReason?: string;\n /** Error code (if failed) */\n errorCode?: string;\n /** Payer address */\n payer?: string;\n /** Protocol extensions returned by the facilitator (e.g., sponsored-access recommendations) */\n extensions?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Access Pass Types\n// ============================================================================\n\n/**\n * A single access pass tier offered by a seller\n */\nexport interface AccessPassTier {\n /** Tier ID (e.g., '1h', '24h') */\n id: string;\n /** Human-readable label (e.g., '1 hour') */\n label: string;\n /** Duration in seconds */\n seconds: number;\n /** Price in USD (e.g., '0.50') */\n price: string;\n /** Price in atomic units (e.g., '500000') */\n priceAtomic: string;\n}\n\n/**\n * Access pass info returned in X-ACCESS-PASS-TIERS header\n */\nexport interface AccessPassInfo {\n /** Available tiers (if tier-based pricing) */\n tiers?: AccessPassTier[];\n /** Rate per hour in USD (if custom duration pricing) */\n ratePerHour?: string;\n /** Pass issuer identifier */\n issuer?: string;\n}\n\n/**\n * JWT claims inside an access pass token\n */\nexport interface AccessPassClaims {\n /** Subject — always 'x402-access-pass' */\n sub: string;\n /** Tier ID or 'custom' */\n tier: string;\n /** Duration in seconds */\n duration: number;\n /** Issued at (unix seconds) */\n iat: number;\n /** Expires at (unix seconds) */\n exp: number;\n /** Payer wallet address */\n payer: string;\n /** Network used for payment */\n network: string;\n /** Issuer identifier */\n iss: string;\n}\n\n/**\n * Client-side access pass configuration\n */\nexport interface AccessPassClientConfig {\n /** Enable access pass mode (default: true when this config is present) */\n enabled?: boolean;\n /** Preferred tier ID (e.g., '1h') — pick this tier if available */\n preferTier?: string;\n /** Preferred custom duration in seconds (e.g., 3600) */\n preferDuration?: number;\n /** Maximum amount willing to spend in USD (e.g., '2.00') */\n maxSpend?: string;\n /** Auto-renew expired passes (default: true) */\n autoRenew?: boolean;\n}\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\n/**\n * SDK error codes\n */\nexport type X402ErrorCode =\n // Client errors\n | 'missing_payment_required_header'\n | 'invalid_payment_required'\n | 'unsupported_network'\n | 'no_matching_payment_option'\n | 'no_solana_accept' // Legacy, kept for compatibility\n | 'missing_fee_payer'\n | 'missing_decimals'\n | 'missing_amount'\n | 'amount_exceeds_max'\n | 'insufficient_balance'\n | 'wallet_missing_sign_transaction'\n | 'wallet_not_connected'\n | 'transaction_build_failed'\n | 'payment_rejected'\n // Server errors\n | 'invalid_payment_signature'\n | 'facilitator_verify_failed'\n | 'facilitator_settle_failed'\n | 'facilitator_request_failed'\n | 'no_matching_requirement'\n // Access pass errors\n | 'access_pass_expired'\n | 'access_pass_invalid'\n | 'access_pass_tier_not_found'\n | 'access_pass_exceeds_max_spend';\n\n/**\n * Custom error class for x402 operations\n */\nexport class X402Error extends Error {\n /** Error code for programmatic handling */\n code: X402ErrorCode;\n /** Additional error details */\n details?: unknown;\n\n constructor(code: X402ErrorCode, message: string, details?: unknown) {\n super(message);\n this.name = 'X402Error';\n this.code = code;\n this.details = details;\n // Maintain proper prototype chain\n Object.setPrototypeOf(this, X402Error.prototype);\n }\n}\n","/**\n * Utility Functions\n *\n * Chain-agnostic helpers for x402 payments.\n */\n\nimport { SOLANA_MAINNET_NETWORK, BASE_MAINNET_NETWORK } from './types';\n\n// ============================================================================\n// Amount Conversion\n// ============================================================================\n\n/**\n * Convert human-readable amount to atomic units\n *\n * @param amount - Human-readable amount (e.g., 0.05 for $0.05)\n * @param decimals - Token decimals (e.g., 6 for USDC)\n * @returns Amount in atomic units as string\n *\n * @example\n * ```typescript\n * toAtomicUnits(0.05, 6) // '50000'\n * toAtomicUnits(1.50, 6) // '1500000'\n * ```\n */\nexport function toAtomicUnits(amount: number, decimals: number): string {\n const multiplier = Math.pow(10, decimals);\n return Math.floor(amount * multiplier).toString();\n}\n\n/**\n * Convert atomic units to human-readable amount\n *\n * @param atomicUnits - Amount in smallest units\n * @param decimals - Token decimals\n * @returns Human-readable amount\n *\n * @example\n * ```typescript\n * fromAtomicUnits('50000', 6) // 0.05\n * fromAtomicUnits(1500000n, 6) // 1.5\n * ```\n */\nexport function fromAtomicUnits(\n atomicUnits: string | bigint | number,\n decimals: number\n): number {\n const divisor = Math.pow(10, decimals);\n return Number(atomicUnits) / divisor;\n}\n\n// ============================================================================\n// Network Helpers\n// ============================================================================\n\n/**\n * Network type\n */\nexport type ChainFamily = 'solana' | 'evm' | 'unknown';\n\n/**\n * Get the chain family from a CAIP-2 network identifier\n *\n * @param network - CAIP-2 network identifier\n * @returns Chain family\n *\n * @example\n * ```typescript\n * getChainFamily('solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp') // 'solana'\n * getChainFamily('eip155:8453') // 'evm'\n * ```\n */\nexport function getChainFamily(network: string): ChainFamily {\n if (network.startsWith('solana:') || network === 'solana') {\n return 'solana';\n }\n if (network.startsWith('eip155:') || ['base', 'ethereum', 'arbitrum'].includes(network)) {\n return 'evm';\n }\n return 'unknown';\n}\n\n/**\n * Get default RPC URL for a network\n *\n * @param network - CAIP-2 network identifier\n * @returns Default RPC URL\n */\nexport function getDefaultRpcUrl(network: string): string {\n const family = getChainFamily(network);\n\n if (family === 'solana') {\n if (network.includes('devnet') || network === 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1') {\n return 'https://api.devnet.solana.com';\n }\n if (network.includes('testnet') || network === 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z') {\n return 'https://api.testnet.solana.com';\n }\n // Mainnet uses Dexter's RPC proxy\n return 'https://api.dexter.cash/api/solana/rpc';\n }\n\n if (family === 'evm') {\n // Extract chain ID from CAIP-2\n if (network.startsWith('eip155:')) {\n const chainId = network.split(':')[1];\n switch (chainId) {\n case '8453': return 'https://api.dexter.cash/api/base/rpc'; // Dexter proxy\n case '84532': return 'https://sepolia.base.org';\n case '1': return 'https://eth.llamarpc.com';\n case '42161': return 'https://arb1.arbitrum.io/rpc';\n default: return 'https://api.dexter.cash/api/base/rpc';\n }\n }\n // Legacy names\n if (network === 'base') return 'https://api.dexter.cash/api/base/rpc';\n if (network === 'ethereum') return 'https://eth.llamarpc.com';\n if (network === 'arbitrum') return 'https://arb1.arbitrum.io/rpc';\n return 'https://api.dexter.cash/api/base/rpc';\n }\n\n // Unknown - return Dexter's Solana proxy\n return 'https://api.dexter.cash/api/solana/rpc';\n}\n\n/**\n * Get human-readable chain name\n *\n * @param network - CAIP-2 network identifier\n * @returns Human-readable name\n */\nexport function getChainName(network: string): string {\n const mapping: Record<string, string> = {\n [SOLANA_MAINNET_NETWORK]: 'Solana',\n 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1': 'Solana Devnet',\n 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z': 'Solana Testnet',\n 'solana': 'Solana',\n [BASE_MAINNET_NETWORK]: 'Base',\n 'eip155:84532': 'Base Sepolia',\n 'eip155:1': 'Ethereum',\n 'eip155:42161': 'Arbitrum One',\n 'base': 'Base',\n 'ethereum': 'Ethereum',\n 'arbitrum': 'Arbitrum',\n };\n return mapping[network] || network;\n}\n\n// ============================================================================\n// Transaction URL Helpers\n// ============================================================================\n\n/**\n * Get explorer URL for a transaction\n *\n * @param txSignature - Transaction signature/hash\n * @param network - CAIP-2 network identifier\n * @returns Explorer URL\n */\nexport function getExplorerUrl(txSignature: string, network: string): string {\n const family = getChainFamily(network);\n\n if (family === 'solana') {\n const isDevnet = network.includes('devnet') || network === 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1';\n if (isDevnet) {\n return `https://solscan.io/tx/${txSignature}?cluster=devnet`;\n }\n // Prefer Orb Markets for mainnet\n return `https://www.orbmarkets.io/tx/${txSignature}`;\n }\n\n if (family === 'evm') {\n // Extract chain ID\n let chainId = '8453'; // Default to Base\n if (network.startsWith('eip155:')) {\n chainId = network.split(':')[1];\n } else if (network === 'ethereum') {\n chainId = '1';\n } else if (network === 'arbitrum') {\n chainId = '42161';\n }\n\n switch (chainId) {\n case '8453': return `https://basescan.org/tx/${txSignature}`;\n case '84532': return `https://sepolia.basescan.org/tx/${txSignature}`;\n case '1': return `https://etherscan.io/tx/${txSignature}`;\n case '42161': return `https://arbiscan.io/tx/${txSignature}`;\n default: return `https://basescan.org/tx/${txSignature}`;\n }\n }\n\n return `https://solscan.io/tx/${txSignature}`;\n}\n\n// ============================================================================\n// Encoding Helpers\n// ============================================================================\n\n/**\n * Unicode-safe base64 encode a string.\n * Works in both Node.js and browsers, handling characters above U+00FF\n * that would cause btoa() to throw InvalidCharacterError.\n */\nfunction safeBase64Encode(str: string): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(str, 'utf-8').toString('base64');\n }\n // Browser fallback: encode UTF-8 bytes via TextEncoder\n const bytes = new TextEncoder().encode(str);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n\n/**\n * Unicode-safe base64 decode a string.\n * Works in both Node.js and browsers.\n */\nfunction safeBase64Decode(encoded: string): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(encoded, 'base64').toString('utf-8');\n }\n // Browser fallback: decode via atob then UTF-8\n const binary = atob(encoded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return new TextDecoder().decode(bytes);\n}\n\n/**\n * Encode an object as base64 JSON (Unicode-safe)\n */\nexport function encodeBase64Json(obj: unknown): string {\n return safeBase64Encode(JSON.stringify(obj));\n}\n\n/**\n * Decode base64 JSON to object (Unicode-safe)\n */\nexport function decodeBase64Json<T>(encoded: string): T {\n return JSON.parse(safeBase64Decode(encoded)) as T;\n}\n"],"mappings":";AAYO,IAAM,yBAAyB;AAG/B,IAAM,uBAAuB;;;ACU7B,SAAS,cAAc,QAAgB,UAA0B;AACtE,QAAM,aAAa,KAAK,IAAI,IAAI,QAAQ;AACxC,SAAO,KAAK,MAAM,SAAS,UAAU,EAAE,SAAS;AAClD;AAeO,SAAS,gBACd,aACA,UACQ;AACR,QAAM,UAAU,KAAK,IAAI,IAAI,QAAQ;AACrC,SAAO,OAAO,WAAW,IAAI;AAC/B;AAuBO,SAAS,eAAe,SAA8B;AAC3D,MAAI,QAAQ,WAAW,SAAS,KAAK,YAAY,UAAU;AACzD,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,WAAW,SAAS,KAAK,CAAC,QAAQ,YAAY,UAAU,EAAE,SAAS,OAAO,GAAG;AACvF,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAmDO,SAAS,aAAa,SAAyB;AACpD,QAAM,UAAkC;AAAA,IACtC,CAAC,sBAAsB,GAAG;AAAA,IAC1B,2CAA2C;AAAA,IAC3C,2CAA2C;AAAA,IAC3C,UAAU;AAAA,IACV,CAAC,oBAAoB,GAAG;AAAA,IACxB,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,EACd;AACA,SAAO,QAAQ,OAAO,KAAK;AAC7B;AAaO,SAAS,eAAe,aAAqB,SAAyB;AAC3E,QAAM,SAAS,eAAe,OAAO;AAErC,MAAI,WAAW,UAAU;AACvB,UAAM,WAAW,QAAQ,SAAS,QAAQ,KAAK,YAAY;AAC3D,QAAI,UAAU;AACZ,aAAO,yBAAyB,WAAW;AAAA,IAC7C;AAEA,WAAO,gCAAgC,WAAW;AAAA,EACpD;AAEA,MAAI,WAAW,OAAO;AAEpB,QAAI,UAAU;AACd,QAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,gBAAU,QAAQ,MAAM,GAAG,EAAE,CAAC;AAAA,IAChC,WAAW,YAAY,YAAY;AACjC,gBAAU;AAAA,IACZ,WAAW,YAAY,YAAY;AACjC,gBAAU;AAAA,IACZ;AAEA,YAAQ,SAAS;AAAA,MACf,KAAK;AAAQ,eAAO,2BAA2B,WAAW;AAAA,MAC1D,KAAK;AAAS,eAAO,mCAAmC,WAAW;AAAA,MACnE,KAAK;AAAK,eAAO,2BAA2B,WAAW;AAAA,MACvD,KAAK;AAAS,eAAO,0BAA0B,WAAW;AAAA,MAC1D;AAAS,eAAO,2BAA2B,WAAW;AAAA,IACxD;AAAA,EACF;AAEA,SAAO,yBAAyB,WAAW;AAC7C;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/types.ts","../../src/utils.ts"],"sourcesContent":["/**\n * x402 v2 SDK — Shared Types\n *\n * Chain-agnostic types for x402 v2 payments.\n * Works with Solana, Base, and any future x402-compatible networks.\n */\n\n// ============================================================================\n// Network Constants\n// ============================================================================\n\n/** CAIP-2 network identifier for Solana mainnet */\nexport const SOLANA_MAINNET_NETWORK = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp';\n\n/** CAIP-2 network identifier for Base mainnet */\nexport const BASE_MAINNET_NETWORK = 'eip155:8453';\n\n/** Alias for Solana mainnet */\nexport const SOLANA_MAINNET = SOLANA_MAINNET_NETWORK;\n\n/** Alias for Base mainnet */\nexport const BASE_MAINNET = BASE_MAINNET_NETWORK;\n\n// ============================================================================\n// Asset Constants\n// ============================================================================\n\n/** USDC mint on Solana mainnet */\nexport const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';\n\n/** USDC address on Base mainnet */\nexport const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';\n\n// ============================================================================\n// Facilitator Constants\n// ============================================================================\n\n/** Dexter's public x402 v2 facilitator URL */\nexport const DEXTER_FACILITATOR_URL = 'https://x402.dexter.cash';\n\n// ============================================================================\n// PayTo Provider Types (for dynamic address resolution, e.g. Stripe)\n// ============================================================================\n\n/**\n * Context passed to a PayToProvider function.\n * Contains request-scoped information for dynamic address resolution.\n */\nexport interface PayToContext {\n /** The PAYMENT-SIGNATURE header value (present on retry/verify, undefined on initial 402) */\n paymentHeader?: string;\n /** Amount in atomic units (e.g., '10000' for 0.01 USDC) */\n amountAtomic?: string;\n /** The resource URL being accessed */\n resourceUrl?: string;\n}\n\n/**\n * Optional defaults a PayToProvider can advertise for auto-configuration.\n * Attached as `_x402Defaults` on the provider function.\n */\nexport interface PayToProviderDefaults {\n /** Default CAIP-2 network (e.g., 'eip155:8453' for Base) */\n network?: string;\n /** Default facilitator URL */\n facilitatorUrl?: string;\n}\n\n/**\n * A function that dynamically resolves a payment address.\n * Used for providers like Stripe that generate per-request deposit addresses.\n *\n * @example\n * ```typescript\n * import { stripePayTo } from '@dexterai/x402/server';\n *\n * const provider = stripePayTo(process.env.STRIPE_SECRET_KEY);\n * const address = await provider({ amountAtomic: '10000' });\n * ```\n */\nexport type PayToProvider = ((context: PayToContext) => Promise<string>) & {\n /** Auto-configuration defaults (set by provider factories like stripePayTo) */\n _x402Defaults?: PayToProviderDefaults;\n};\n\n// ============================================================================\n// Payment Types\n// ============================================================================\n\n/**\n * Asset configuration for payments\n */\nexport interface AssetConfig {\n /** Token address (mint on Solana, contract on EVM) */\n address: string;\n /** Token decimals */\n decimals: number;\n /** Optional: Human-readable symbol */\n symbol?: string;\n}\n\n/**\n * Resource info included in payment requirements\n */\nexport interface ResourceInfo {\n /** Resource URL */\n url: string;\n /** Human-readable description */\n description?: string;\n /** MIME type of the resource */\n mimeType?: string;\n}\n\n/**\n * Extra fields in payment requirements\n * Chain-specific fields may vary\n */\nexport interface AcceptsExtra {\n /** Facilitator address that pays tx fees (required for Solana) */\n feePayer?: string;\n /** Token decimals (optional - defaults to 6 for USDC) */\n decimals?: number;\n /** EIP-712: Token name (EVM only) */\n name?: string;\n /** EIP-712: Token version (EVM only) */\n version?: string;\n /** Additional chain-specific fields */\n [key: string]: unknown;\n}\n\n/**\n * A single payment option in the accepts array\n */\nexport interface PaymentAccept {\n /** x402 version (1 or 2, defaults to 2 if not specified) */\n x402Version?: 1 | 2;\n /** Payment scheme (always 'exact' for x402 v2) */\n scheme: 'exact';\n /** CAIP-2 network identifier (v1: 'solana', v2: 'solana:5eykt...') */\n network: string;\n /** Payment amount in atomic units (x402 v2 spec field) */\n amount: string;\n /** @deprecated v1 field — use `amount` instead. Kept for backwards compatibility with v1 data. */\n maxAmountRequired?: string;\n /** Token address */\n asset: string;\n /** Seller's address to receive payment */\n payTo: string;\n /** Maximum seconds until payment expires */\n maxTimeoutSeconds: number;\n /** Chain-specific extra data */\n extra?: AcceptsExtra;\n}\n\n/**\n * Full PaymentRequired structure (sent in PAYMENT-REQUIRED header)\n */\nexport interface PaymentRequired {\n /** x402 version (always 2) */\n x402Version: 2;\n /** Resource being accessed */\n resource: ResourceInfo;\n /** Available payment options */\n accepts: PaymentAccept[];\n /** Optional error message */\n error?: string;\n /** Protocol extensions */\n extensions?: Record<string, unknown>;\n}\n\n/**\n * PaymentSignature structure (sent in PAYMENT-SIGNATURE header)\n */\nexport interface PaymentSignature {\n /** x402 version (always 2) */\n x402Version: 2;\n /** Resource being accessed */\n resource: ResourceInfo;\n /** The payment option that was accepted */\n accepted: PaymentAccept;\n /** The signed payment */\n payload: {\n /** Signed transaction (base64 for Solana, JSON for EVM) */\n transaction: string;\n };\n /** Protocol extensions */\n extensions?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Facilitator Response Types\n// ============================================================================\n\n/**\n * Response from /verify endpoint\n */\nexport interface VerifyResponse {\n /** Whether the payment is valid */\n isValid: boolean;\n /** Reason for invalidity (if invalid) */\n invalidReason?: string;\n /** Payer address */\n payer?: string;\n}\n\n/**\n * Response from /settle endpoint\n */\nexport interface SettleResponse {\n /** Whether settlement succeeded */\n success: boolean;\n /** Transaction signature/hash */\n transaction?: string;\n /** Network the payment was made on */\n network: string;\n /** Error reason (if failed) */\n errorReason?: string;\n /** Error code (if failed) */\n errorCode?: string;\n /** Payer address */\n payer?: string;\n /** Protocol extensions returned by the facilitator (e.g., sponsored-access recommendations) */\n extensions?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Access Pass Types\n// ============================================================================\n\n/**\n * A single access pass tier offered by a seller\n */\nexport interface AccessPassTier {\n /** Tier ID (e.g., '1h', '24h') */\n id: string;\n /** Human-readable label (e.g., '1 hour') */\n label: string;\n /** Duration in seconds */\n seconds: number;\n /** Price in USD (e.g., '0.50') */\n price: string;\n /** Price in atomic units (e.g., '500000') */\n priceAtomic: string;\n}\n\n/**\n * Access pass info returned in X-ACCESS-PASS-TIERS header\n */\nexport interface AccessPassInfo {\n /** Available tiers (if tier-based pricing) */\n tiers?: AccessPassTier[];\n /** Rate per hour in USD (if custom duration pricing) */\n ratePerHour?: string;\n /** Pass issuer identifier */\n issuer?: string;\n}\n\n/**\n * JWT claims inside an access pass token\n */\nexport interface AccessPassClaims {\n /** Subject — always 'x402-access-pass' */\n sub: string;\n /** Tier ID or 'custom' */\n tier: string;\n /** Duration in seconds */\n duration: number;\n /** Issued at (unix seconds) */\n iat: number;\n /** Expires at (unix seconds) */\n exp: number;\n /** Payer wallet address */\n payer: string;\n /** Network used for payment */\n network: string;\n /** Issuer identifier */\n iss: string;\n}\n\n/**\n * Client-side access pass configuration\n */\nexport interface AccessPassClientConfig {\n /** Enable access pass mode (default: true when this config is present) */\n enabled?: boolean;\n /** Preferred tier ID (e.g., '1h') — pick this tier if available */\n preferTier?: string;\n /** Preferred custom duration in seconds (e.g., 3600) */\n preferDuration?: number;\n /** Maximum amount willing to spend in USD (e.g., '2.00') */\n maxSpend?: string;\n /** Auto-renew expired passes (default: true) */\n autoRenew?: boolean;\n}\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\n/**\n * SDK error codes\n */\nexport type X402ErrorCode =\n // Client errors\n | 'missing_payment_required_header'\n | 'invalid_payment_required'\n | 'unsupported_network'\n | 'no_matching_payment_option'\n | 'no_solana_accept' // Legacy, kept for compatibility\n | 'missing_fee_payer'\n | 'missing_decimals'\n | 'missing_amount'\n | 'amount_exceeds_max'\n | 'insufficient_balance'\n | 'wallet_missing_sign_transaction'\n | 'wallet_not_connected'\n | 'transaction_build_failed'\n | 'payment_rejected'\n // Server errors\n | 'invalid_payment_signature'\n | 'facilitator_verify_failed'\n | 'facilitator_settle_failed'\n | 'facilitator_request_failed'\n | 'no_matching_requirement'\n // Access pass errors\n | 'access_pass_expired'\n | 'access_pass_invalid'\n | 'access_pass_tier_not_found'\n | 'access_pass_exceeds_max_spend';\n\n/**\n * Custom error class for x402 operations\n */\nexport class X402Error extends Error {\n /** Error code for programmatic handling */\n code: X402ErrorCode;\n /** Additional error details */\n details?: unknown;\n\n constructor(code: X402ErrorCode, message: string, details?: unknown) {\n super(message);\n this.name = 'X402Error';\n this.code = code;\n this.details = details;\n // Maintain proper prototype chain\n Object.setPrototypeOf(this, X402Error.prototype);\n }\n}\n","/**\n * Utility Functions\n *\n * Chain-agnostic helpers for x402 payments.\n */\n\nimport { SOLANA_MAINNET_NETWORK, BASE_MAINNET_NETWORK } from './types';\n\n// ============================================================================\n// Amount Conversion\n// ============================================================================\n\n/**\n * Convert human-readable amount to atomic units\n *\n * @param amount - Human-readable amount (e.g., 0.05 for $0.05)\n * @param decimals - Token decimals (e.g., 6 for USDC)\n * @returns Amount in atomic units as string\n *\n * @example\n * ```typescript\n * toAtomicUnits(0.05, 6) // '50000'\n * toAtomicUnits(1.50, 6) // '1500000'\n * ```\n */\nexport function toAtomicUnits(amount: number, decimals: number): string {\n const multiplier = Math.pow(10, decimals);\n return Math.floor(amount * multiplier).toString();\n}\n\n/**\n * Convert atomic units to human-readable amount\n *\n * @param atomicUnits - Amount in smallest units\n * @param decimals - Token decimals\n * @returns Human-readable amount\n *\n * @example\n * ```typescript\n * fromAtomicUnits('50000', 6) // 0.05\n * fromAtomicUnits(1500000n, 6) // 1.5\n * ```\n */\nexport function fromAtomicUnits(\n atomicUnits: string | bigint | number,\n decimals: number\n): number {\n const divisor = Math.pow(10, decimals);\n return Number(atomicUnits) / divisor;\n}\n\n// ============================================================================\n// Network Helpers\n// ============================================================================\n\n/**\n * Network type\n */\nexport type ChainFamily = 'solana' | 'evm' | 'unknown';\n\n/**\n * Get the chain family from a CAIP-2 network identifier\n *\n * @param network - CAIP-2 network identifier\n * @returns Chain family\n *\n * @example\n * ```typescript\n * getChainFamily('solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp') // 'solana'\n * getChainFamily('eip155:8453') // 'evm'\n * ```\n */\nexport function getChainFamily(network: string): ChainFamily {\n if (network.startsWith('solana:') || network === 'solana') {\n return 'solana';\n }\n if (network.startsWith('eip155:') || ['base', 'ethereum', 'arbitrum'].includes(network)) {\n return 'evm';\n }\n return 'unknown';\n}\n\n/**\n * Get default RPC URL for a network\n *\n * @param network - CAIP-2 network identifier\n * @returns Default RPC URL\n */\nexport function getDefaultRpcUrl(network: string): string {\n const family = getChainFamily(network);\n\n if (family === 'solana') {\n if (network.includes('devnet') || network === 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1') {\n return 'https://api.devnet.solana.com';\n }\n if (network.includes('testnet') || network === 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z') {\n return 'https://api.testnet.solana.com';\n }\n // Mainnet uses Dexter's RPC proxy\n return 'https://api.dexter.cash/api/solana/rpc';\n }\n\n if (family === 'evm') {\n // Extract chain ID from CAIP-2\n if (network.startsWith('eip155:')) {\n const chainId = network.split(':')[1];\n switch (chainId) {\n case '8453': return 'https://api.dexter.cash/api/base/rpc'; // Dexter proxy\n case '84532': return 'https://sepolia.base.org';\n case '1': return 'https://eth.llamarpc.com';\n case '42161': return 'https://arb1.arbitrum.io/rpc';\n default: return 'https://api.dexter.cash/api/base/rpc';\n }\n }\n // Legacy names\n if (network === 'base') return 'https://api.dexter.cash/api/base/rpc';\n if (network === 'ethereum') return 'https://eth.llamarpc.com';\n if (network === 'arbitrum') return 'https://arb1.arbitrum.io/rpc';\n return 'https://api.dexter.cash/api/base/rpc';\n }\n\n // Unknown - return Dexter's Solana proxy\n return 'https://api.dexter.cash/api/solana/rpc';\n}\n\n/**\n * Get human-readable chain name\n *\n * @param network - CAIP-2 network identifier\n * @returns Human-readable name\n */\nexport function getChainName(network: string): string {\n const mapping: Record<string, string> = {\n [SOLANA_MAINNET_NETWORK]: 'Solana',\n 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1': 'Solana Devnet',\n 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z': 'Solana Testnet',\n 'solana': 'Solana',\n [BASE_MAINNET_NETWORK]: 'Base',\n 'eip155:84532': 'Base Sepolia',\n 'eip155:1': 'Ethereum',\n 'eip155:42161': 'Arbitrum One',\n 'base': 'Base',\n 'ethereum': 'Ethereum',\n 'arbitrum': 'Arbitrum',\n };\n return mapping[network] || network;\n}\n\n// ============================================================================\n// Transaction URL Helpers\n// ============================================================================\n\n/**\n * Get explorer URL for a transaction\n *\n * @param txSignature - Transaction signature/hash\n * @param network - CAIP-2 network identifier\n * @returns Explorer URL\n */\nexport function getExplorerUrl(txSignature: string, network: string): string {\n const family = getChainFamily(network);\n\n if (family === 'solana') {\n const isDevnet = network.includes('devnet') || network === 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1';\n if (isDevnet) {\n return `https://solscan.io/tx/${txSignature}?cluster=devnet`;\n }\n // Prefer Orb Markets for mainnet\n return `https://www.orbmarkets.io/tx/${txSignature}`;\n }\n\n if (family === 'evm') {\n // Extract chain ID\n let chainId = '8453'; // Default to Base\n if (network.startsWith('eip155:')) {\n chainId = network.split(':')[1];\n } else if (network === 'ethereum') {\n chainId = '1';\n } else if (network === 'arbitrum') {\n chainId = '42161';\n }\n\n switch (chainId) {\n case '8453': return `https://basescan.org/tx/${txSignature}`;\n case '84532': return `https://sepolia.basescan.org/tx/${txSignature}`;\n case '1': return `https://etherscan.io/tx/${txSignature}`;\n case '42161': return `https://arbiscan.io/tx/${txSignature}`;\n default: return `https://basescan.org/tx/${txSignature}`;\n }\n }\n\n return `https://solscan.io/tx/${txSignature}`;\n}\n\n// ============================================================================\n// Encoding Helpers\n// ============================================================================\n\n/**\n * Unicode-safe base64 encode a string.\n * Works in both Node.js and browsers, handling characters above U+00FF\n * that would cause btoa() to throw InvalidCharacterError.\n */\nfunction safeBase64Encode(str: string): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(str, 'utf-8').toString('base64');\n }\n // Browser fallback: encode UTF-8 bytes via TextEncoder\n const bytes = new TextEncoder().encode(str);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n\n/**\n * Unicode-safe base64 decode a string.\n * Works in both Node.js and browsers.\n */\nfunction safeBase64Decode(encoded: string): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(encoded, 'base64').toString('utf-8');\n }\n // Browser fallback: decode via atob then UTF-8\n const binary = atob(encoded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return new TextDecoder().decode(bytes);\n}\n\n/**\n * Encode an object as base64 JSON (Unicode-safe)\n */\nexport function encodeBase64Json(obj: unknown): string {\n return safeBase64Encode(JSON.stringify(obj));\n}\n\n/**\n * Decode base64 JSON to object (Unicode-safe)\n */\nexport function decodeBase64Json<T>(encoded: string): T {\n return JSON.parse(safeBase64Decode(encoded)) as T;\n}\n"],"mappings":";AAYO,IAAM,yBAAyB;AAG/B,IAAM,uBAAuB;;;ACU7B,SAAS,cAAc,QAAgB,UAA0B;AACtE,QAAM,aAAa,KAAK,IAAI,IAAI,QAAQ;AACxC,SAAO,KAAK,MAAM,SAAS,UAAU,EAAE,SAAS;AAClD;AAeO,SAAS,gBACd,aACA,UACQ;AACR,QAAM,UAAU,KAAK,IAAI,IAAI,QAAQ;AACrC,SAAO,OAAO,WAAW,IAAI;AAC/B;AAuBO,SAAS,eAAe,SAA8B;AAC3D,MAAI,QAAQ,WAAW,SAAS,KAAK,YAAY,UAAU;AACzD,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,WAAW,SAAS,KAAK,CAAC,QAAQ,YAAY,UAAU,EAAE,SAAS,OAAO,GAAG;AACvF,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAmDO,SAAS,aAAa,SAAyB;AACpD,QAAM,UAAkC;AAAA,IACtC,CAAC,sBAAsB,GAAG;AAAA,IAC1B,2CAA2C;AAAA,IAC3C,2CAA2C;AAAA,IAC3C,UAAU;AAAA,IACV,CAAC,oBAAoB,GAAG;AAAA,IACxB,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,EACd;AACA,SAAO,QAAQ,OAAO,KAAK;AAC7B;AAaO,SAAS,eAAe,aAAqB,SAAyB;AAC3E,QAAM,SAAS,eAAe,OAAO;AAErC,MAAI,WAAW,UAAU;AACvB,UAAM,WAAW,QAAQ,SAAS,QAAQ,KAAK,YAAY;AAC3D,QAAI,UAAU;AACZ,aAAO,yBAAyB,WAAW;AAAA,IAC7C;AAEA,WAAO,gCAAgC,WAAW;AAAA,EACpD;AAEA,MAAI,WAAW,OAAO;AAEpB,QAAI,UAAU;AACd,QAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,gBAAU,QAAQ,MAAM,GAAG,EAAE,CAAC;AAAA,IAChC,WAAW,YAAY,YAAY;AACjC,gBAAU;AAAA,IACZ,WAAW,YAAY,YAAY;AACjC,gBAAU;AAAA,IACZ;AAEA,YAAQ,SAAS;AAAA,MACf,KAAK;AAAQ,eAAO,2BAA2B,WAAW;AAAA,MAC1D,KAAK;AAAS,eAAO,mCAAmC,WAAW;AAAA,MACnE,KAAK;AAAK,eAAO,2BAA2B,WAAW;AAAA,MACvD,KAAK;AAAS,eAAO,0BAA0B,WAAW;AAAA,MAC1D;AAAS,eAAO,2BAA2B,WAAW;AAAA,IACxD;AAAA,EACF;AAEA,SAAO,yBAAyB,WAAW;AAC7C;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dexterai/x402",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"description": "Full-stack x402 SDK - add paid API monetization to any endpoint. Express middleware, React hooks, Access Pass, dynamic pricing. Solana, Base, Polygon, Arbitrum, Optimism, Avalanche, SKALE.",
|
|
5
5
|
"author": "Dexter",
|
|
6
6
|
"license": "MIT",
|
|
@@ -52,11 +52,11 @@
|
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"@solana/spl-token": "^0.4.9",
|
|
55
|
+
"@dexterai/x402-ads-types": "^0.1.0",
|
|
55
56
|
"@solana/web3.js": "^1.98.0",
|
|
56
57
|
"tiktoken": "^1.0.22"
|
|
57
58
|
},
|
|
58
59
|
"devDependencies": {
|
|
59
|
-
"@dexterai/x402-ads-types": "^0.1.0",
|
|
60
60
|
"@types/aws-lambda": "^8.10.161",
|
|
61
61
|
"@types/express": "^5.0.6",
|
|
62
62
|
"@types/node": "^22.10.0",
|
|
@@ -70,16 +70,12 @@
|
|
|
70
70
|
"vitest": "^2.1.8"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
|
-
"@dexterai/x402-ads-types": ">=0.1.0",
|
|
74
73
|
"@solana/wallet-adapter-base": "^0.9.0",
|
|
75
74
|
"react": "^18.0.0 || ^19.0.0",
|
|
76
75
|
"stripe": "^20.0.0",
|
|
77
76
|
"viem": "^2.0.0"
|
|
78
77
|
},
|
|
79
78
|
"peerDependenciesMeta": {
|
|
80
|
-
"@dexterai/x402-ads-types": {
|
|
81
|
-
"optional": true
|
|
82
|
-
},
|
|
83
79
|
"@solana/wallet-adapter-base": {
|
|
84
80
|
"optional": true
|
|
85
81
|
},
|