@alleyboss/micropay-solana-x402-paywall 2.3.0 → 2.3.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 CHANGED
@@ -54,7 +54,7 @@ if (result.valid) {
54
54
 
55
55
  ## 🔧 Modules
56
56
 
57
- 9 tree-shakeable entry points for minimal bundle size:
57
+ 10 tree-shakeable entry points for minimal bundle size:
58
58
 
59
59
  ```typescript
60
60
  // Core verification
@@ -80,8 +80,42 @@ import { getSolPrice, formatPriceDisplay, configurePricing } from '@alleyboss/mi
80
80
 
81
81
  // Retry utilities
82
82
  import { withRetry } from '@alleyboss/micropay-solana-x402-paywall/utils';
83
+
84
+ // 🆕 AI Agent Payments (v2.3.0)
85
+ import { executeAgentPayment, getAgentBalance } from '@alleyboss/micropay-solana-x402-paywall/agent';
83
86
  ```
84
87
 
88
+ ## 🤖 AI Agent Payments (NEW in v2.3.0)
89
+
90
+ Enable autonomous AI agents to pay for premium API access without user intervention:
91
+
92
+ ```typescript
93
+ import { executeAgentPayment } from '@alleyboss/micropay-solana-x402-paywall/agent';
94
+ import { Keypair, Connection } from '@solana/web3.js';
95
+
96
+ // Server-side: Load agent keypair from environment
97
+ const agentKeypair = Keypair.fromSecretKey(
98
+ Uint8Array.from(process.env.AGENT_SECRET.split(',').map(Number))
99
+ );
100
+
101
+ // Execute autonomous payment
102
+ const result = await executeAgentPayment({
103
+ connection: new Connection('https://api.devnet.solana.com'),
104
+ agentKeypair,
105
+ recipientAddress: 'CREATOR_WALLET',
106
+ amountLamports: BigInt(2_000_000), // 0.002 SOL
107
+ priorityFee: { enabled: true, microLamports: 5000 },
108
+ });
109
+
110
+ if (result.success) {
111
+ console.log('Payment confirmed:', result.signature);
112
+ // Deliver premium content to AI agent
113
+ }
114
+ ```
115
+
116
+ **Live Demo:** [solana-x402-paywall.vercel.app/agent-chat](https://solana-x402-paywall.vercel.app/agent-chat)
117
+
118
+
85
119
  ## 🔥 New in v2.2.0
86
120
 
87
121
  ### x402 Protocol Compliance
@@ -0,0 +1,359 @@
1
+ 'use strict';
2
+
3
+ var web3_js = require('@solana/web3.js');
4
+ var jose = require('jose');
5
+ var uuid = require('uuid');
6
+
7
+ // src/agent/agentPayment.ts
8
+ var DEFAULT_COMPUTE_UNITS = 2e5;
9
+ var DEFAULT_MICRO_LAMPORTS = 1e3;
10
+ function createPriorityFeeInstructions(config = {}) {
11
+ const { enabled = false, microLamports, computeUnits } = config;
12
+ if (!enabled) {
13
+ return [];
14
+ }
15
+ const instructions = [];
16
+ const units = computeUnits ?? DEFAULT_COMPUTE_UNITS;
17
+ instructions.push(web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units }));
18
+ const price = microLamports ?? DEFAULT_MICRO_LAMPORTS;
19
+ instructions.push(web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: price }));
20
+ return instructions;
21
+ }
22
+ async function buildVersionedTransaction(config) {
23
+ const {
24
+ connection,
25
+ payer,
26
+ instructions,
27
+ lookupTables = [],
28
+ priorityFee,
29
+ recentBlockhash
30
+ } = config;
31
+ const priorityIxs = createPriorityFeeInstructions(priorityFee);
32
+ const allInstructions = [...priorityIxs, ...instructions];
33
+ let blockhash;
34
+ let lastValidBlockHeight;
35
+ if (recentBlockhash) {
36
+ blockhash = recentBlockhash;
37
+ const slot = await connection.getSlot();
38
+ lastValidBlockHeight = slot + 150;
39
+ } else {
40
+ const latestBlockhash = await connection.getLatestBlockhash("confirmed");
41
+ blockhash = latestBlockhash.blockhash;
42
+ lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;
43
+ }
44
+ const message = new web3_js.TransactionMessage({
45
+ payerKey: payer,
46
+ recentBlockhash: blockhash,
47
+ instructions: allInstructions
48
+ }).compileToV0Message(lookupTables);
49
+ const transaction = new web3_js.VersionedTransaction(message);
50
+ return {
51
+ transaction,
52
+ blockhash,
53
+ lastValidBlockHeight
54
+ };
55
+ }
56
+
57
+ // src/agent/agentPayment.ts
58
+ var WALLET_REGEX = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
59
+ function isValidWalletAddress(address) {
60
+ if (!address || typeof address !== "string") return false;
61
+ return WALLET_REGEX.test(address);
62
+ }
63
+ async function executeAgentPayment(params) {
64
+ const {
65
+ connection,
66
+ agentKeypair,
67
+ recipientAddress,
68
+ amountLamports,
69
+ priorityFee,
70
+ confirmationTimeout = 6e4
71
+ } = params;
72
+ if (!isValidWalletAddress(recipientAddress)) {
73
+ return {
74
+ success: false,
75
+ error: "Invalid recipient address format"
76
+ };
77
+ }
78
+ if (amountLamports <= 0n) {
79
+ return {
80
+ success: false,
81
+ error: "Amount must be greater than 0"
82
+ };
83
+ }
84
+ try {
85
+ const recipientPubkey = new web3_js.PublicKey(recipientAddress);
86
+ const transferInstruction = web3_js.SystemProgram.transfer({
87
+ fromPubkey: agentKeypair.publicKey,
88
+ toPubkey: recipientPubkey,
89
+ lamports: amountLamports
90
+ });
91
+ const { transaction, lastValidBlockHeight } = await buildVersionedTransaction({
92
+ connection,
93
+ payer: agentKeypair.publicKey,
94
+ instructions: [transferInstruction],
95
+ priorityFee
96
+ });
97
+ transaction.sign([agentKeypair]);
98
+ const signature = await connection.sendTransaction(transaction, {
99
+ maxRetries: 3,
100
+ skipPreflight: false
101
+ });
102
+ const confirmationPromise = connection.confirmTransaction(
103
+ {
104
+ signature,
105
+ lastValidBlockHeight,
106
+ blockhash: transaction.message.recentBlockhash
107
+ },
108
+ "confirmed"
109
+ );
110
+ const timeoutPromise = new Promise((_, reject) => {
111
+ setTimeout(() => reject(new Error("Confirmation timeout")), confirmationTimeout);
112
+ });
113
+ const confirmation = await Promise.race([confirmationPromise, timeoutPromise]);
114
+ if (confirmation.value.err) {
115
+ return {
116
+ success: false,
117
+ signature,
118
+ error: "Transaction failed on-chain"
119
+ };
120
+ }
121
+ const txDetails = await connection.getTransaction(signature, {
122
+ commitment: "confirmed",
123
+ maxSupportedTransactionVersion: 0
124
+ });
125
+ return {
126
+ success: true,
127
+ signature,
128
+ confirmedAt: txDetails?.blockTime ?? Math.floor(Date.now() / 1e3),
129
+ slot: txDetails?.slot ?? confirmation.context.slot,
130
+ amountLamports,
131
+ amountSol: Number(amountLamports) / web3_js.LAMPORTS_PER_SOL
132
+ };
133
+ } catch (error) {
134
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
135
+ return {
136
+ success: false,
137
+ error: errorMessage
138
+ };
139
+ }
140
+ }
141
+ async function getAgentBalance(connection, agentKeypair) {
142
+ const balance = await connection.getBalance(agentKeypair.publicKey);
143
+ return {
144
+ balance: BigInt(balance),
145
+ balanceSol: balance / web3_js.LAMPORTS_PER_SOL
146
+ };
147
+ }
148
+ async function hasAgentSufficientBalance(connection, agentKeypair, requiredLamports) {
149
+ const { balance } = await getAgentBalance(connection, agentKeypair);
150
+ const totalRequired = requiredLamports + 10000n;
151
+ return {
152
+ sufficient: balance >= totalRequired,
153
+ balance,
154
+ required: totalRequired
155
+ };
156
+ }
157
+ function keypairFromBase58(base58Secret) {
158
+ const bytes = Buffer.from(base58Secret, "base64");
159
+ if (bytes.length !== 64) {
160
+ const parts = base58Secret.split(",").map((n) => parseInt(n.trim(), 10));
161
+ if (parts.length === 64) {
162
+ return web3_js.Keypair.fromSecretKey(Uint8Array.from(parts));
163
+ }
164
+ throw new Error("Invalid secret key format. Expected base58 string or comma-separated bytes.");
165
+ }
166
+ return web3_js.Keypair.fromSecretKey(bytes);
167
+ }
168
+ function generateAgentKeypair() {
169
+ const keypair = web3_js.Keypair.generate();
170
+ const secretBytes = Array.from(keypair.secretKey);
171
+ return {
172
+ keypair,
173
+ secretBase58: secretBytes.join(","),
174
+ // Comma-separated for easy storage
175
+ publicKey: keypair.publicKey.toBase58()
176
+ };
177
+ }
178
+ var MAX_CREDITS = 1e3;
179
+ var MIN_SECRET_LENGTH = 32;
180
+ function getSecretKey(secret) {
181
+ if (!secret || typeof secret !== "string") {
182
+ throw new Error("Session secret is required");
183
+ }
184
+ if (secret.length < MIN_SECRET_LENGTH) {
185
+ throw new Error(`Session secret must be at least ${MIN_SECRET_LENGTH} characters`);
186
+ }
187
+ return new TextEncoder().encode(secret);
188
+ }
189
+ function validateWalletAddress(address) {
190
+ if (!address || typeof address !== "string") return false;
191
+ const base58Regex = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
192
+ return base58Regex.test(address);
193
+ }
194
+ async function createCreditSession(walletAddress, purchaseId, config) {
195
+ if (!validateWalletAddress(walletAddress)) {
196
+ throw new Error("Invalid wallet address format");
197
+ }
198
+ if (config.initialCredits <= 0 || config.initialCredits > MAX_CREDITS) {
199
+ throw new Error(`Credits must be between 1 and ${MAX_CREDITS}`);
200
+ }
201
+ if (!config.durationHours || config.durationHours <= 0 || config.durationHours > 8760) {
202
+ throw new Error("Session duration must be between 1 and 8760 hours (1 year)");
203
+ }
204
+ const sessionId = uuid.v4();
205
+ const now = Math.floor(Date.now() / 1e3);
206
+ const expiresAt = now + config.durationHours * 3600;
207
+ const bundleExpiry = config.bundleExpiryHours ? now + config.bundleExpiryHours * 3600 : expiresAt;
208
+ const session = {
209
+ id: sessionId,
210
+ walletAddress,
211
+ unlockedArticles: [purchaseId],
212
+ siteWideUnlock: false,
213
+ createdAt: now,
214
+ expiresAt,
215
+ credits: config.initialCredits,
216
+ bundleExpiry,
217
+ bundleType: config.bundleType
218
+ };
219
+ const payload = {
220
+ sub: walletAddress,
221
+ sid: sessionId,
222
+ articles: session.unlockedArticles,
223
+ siteWide: false,
224
+ credits: config.initialCredits,
225
+ bundleExpiry,
226
+ bundleType: config.bundleType,
227
+ iat: now,
228
+ exp: expiresAt
229
+ };
230
+ const token = await new jose.SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(`${config.durationHours}h`).sign(getSecretKey(config.secret));
231
+ return { token, session };
232
+ }
233
+ async function validateCreditSession(token, secret) {
234
+ if (!token || typeof token !== "string") {
235
+ return { valid: false, reason: "Invalid token format" };
236
+ }
237
+ try {
238
+ const { payload } = await jose.jwtVerify(token, getSecretKey(secret));
239
+ const creditPayload = payload;
240
+ if (!creditPayload.sub || !creditPayload.sid || !creditPayload.exp) {
241
+ return { valid: false, reason: "Malformed session payload" };
242
+ }
243
+ const now = Math.floor(Date.now() / 1e3);
244
+ if (creditPayload.exp < now) {
245
+ return { valid: false, reason: "Session expired" };
246
+ }
247
+ if (creditPayload.bundleExpiry && creditPayload.bundleExpiry < now) {
248
+ return { valid: false, reason: "Bundle expired" };
249
+ }
250
+ if (!validateWalletAddress(creditPayload.sub)) {
251
+ return { valid: false, reason: "Invalid session data" };
252
+ }
253
+ const session = {
254
+ id: creditPayload.sid,
255
+ walletAddress: creditPayload.sub,
256
+ unlockedArticles: Array.isArray(creditPayload.articles) ? creditPayload.articles : [],
257
+ siteWideUnlock: Boolean(creditPayload.siteWide),
258
+ createdAt: creditPayload.iat ?? 0,
259
+ expiresAt: creditPayload.exp,
260
+ credits: creditPayload.credits ?? 0,
261
+ bundleExpiry: creditPayload.bundleExpiry,
262
+ bundleType: creditPayload.bundleType
263
+ };
264
+ return { valid: true, session };
265
+ } catch {
266
+ return { valid: false, reason: "Invalid session" };
267
+ }
268
+ }
269
+ async function useCredit(token, secret, creditsToUse = 1) {
270
+ if (creditsToUse <= 0) {
271
+ return { success: false, remainingCredits: 0, error: "Invalid credit amount" };
272
+ }
273
+ const validation = await validateCreditSession(token, secret);
274
+ if (!validation.valid || !validation.session) {
275
+ return {
276
+ success: false,
277
+ remainingCredits: 0,
278
+ error: validation.reason || "Invalid session"
279
+ };
280
+ }
281
+ const session = validation.session;
282
+ if (session.credits < creditsToUse) {
283
+ return {
284
+ success: false,
285
+ remainingCredits: session.credits,
286
+ error: "Insufficient credits"
287
+ };
288
+ }
289
+ const newCredits = session.credits - creditsToUse;
290
+ const payload = {
291
+ sub: session.walletAddress,
292
+ sid: session.id,
293
+ articles: session.unlockedArticles,
294
+ siteWide: session.siteWideUnlock,
295
+ credits: newCredits,
296
+ bundleExpiry: session.bundleExpiry,
297
+ bundleType: session.bundleType,
298
+ iat: session.createdAt,
299
+ exp: session.expiresAt
300
+ };
301
+ const newToken = await new jose.SignJWT(payload).setProtectedHeader({ alg: "HS256" }).sign(getSecretKey(secret));
302
+ return {
303
+ success: true,
304
+ remainingCredits: newCredits,
305
+ newToken
306
+ };
307
+ }
308
+ async function addCredits(token, secret, creditsToAdd) {
309
+ if (creditsToAdd <= 0 || creditsToAdd > MAX_CREDITS) {
310
+ return { success: false, error: "Invalid credit amount" };
311
+ }
312
+ const validation = await validateCreditSession(token, secret);
313
+ if (!validation.valid || !validation.session) {
314
+ return { success: false, error: validation.reason || "Invalid session" };
315
+ }
316
+ const session = validation.session;
317
+ const newCredits = Math.min(session.credits + creditsToAdd, MAX_CREDITS);
318
+ const payload = {
319
+ sub: session.walletAddress,
320
+ sid: session.id,
321
+ articles: session.unlockedArticles,
322
+ siteWide: session.siteWideUnlock,
323
+ credits: newCredits,
324
+ bundleExpiry: session.bundleExpiry,
325
+ bundleType: session.bundleType,
326
+ iat: session.createdAt,
327
+ exp: session.expiresAt
328
+ };
329
+ const newToken = await new jose.SignJWT(payload).setProtectedHeader({ alg: "HS256" }).sign(getSecretKey(secret));
330
+ return {
331
+ success: true,
332
+ newToken,
333
+ totalCredits: newCredits
334
+ };
335
+ }
336
+ async function getRemainingCredits(token, secret) {
337
+ const validation = await validateCreditSession(token, secret);
338
+ if (!validation.valid || !validation.session) {
339
+ return { credits: 0, valid: false };
340
+ }
341
+ return {
342
+ credits: validation.session.credits,
343
+ valid: true,
344
+ bundleExpiry: validation.session.bundleExpiry
345
+ };
346
+ }
347
+
348
+ exports.addCredits = addCredits;
349
+ exports.createCreditSession = createCreditSession;
350
+ exports.executeAgentPayment = executeAgentPayment;
351
+ exports.generateAgentKeypair = generateAgentKeypair;
352
+ exports.getAgentBalance = getAgentBalance;
353
+ exports.getRemainingCredits = getRemainingCredits;
354
+ exports.hasAgentSufficientBalance = hasAgentSufficientBalance;
355
+ exports.keypairFromBase58 = keypairFromBase58;
356
+ exports.useCredit = useCredit;
357
+ exports.validateCreditSession = validateCreditSession;
358
+ //# sourceMappingURL=index.cjs.map
359
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/solana/priority-fees.ts","../../src/solana/versioned.ts","../../src/agent/agentPayment.ts","../../src/agent/credits.ts"],"names":["ComputeBudgetProgram","TransactionMessage","VersionedTransaction","PublicKey","SystemProgram","LAMPORTS_PER_SOL","Keypair","uuidv4","SignJWT","jwtVerify"],"mappings":"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","file":"index.cjs","sourcesContent":["// Priority Fee Utilities for Solana Transactions\n// Adds compute budget instructions for landing transactions faster\n\nimport {\n ComputeBudgetProgram,\n type Connection,\n type PublicKey,\n type TransactionInstruction,\n} from '@solana/web3.js';\n\n/** Configuration for priority fees */\nexport interface PriorityFeeConfig {\n /** Enable priority fees (default: false) */\n enabled?: boolean;\n /** Price per compute unit in micro-lamports (default: auto-estimate) */\n microLamports?: number;\n /** Maximum compute units for transaction (default: 200_000) */\n computeUnits?: number;\n}\n\n/** Default priority fee settings */\nconst DEFAULT_COMPUTE_UNITS = 200_000;\nconst DEFAULT_MICRO_LAMPORTS = 1_000; // 0.001 lamports per CU\n\n/**\n * Create compute budget instructions for priority fees\n * \n * @example\n * ```typescript\n * const priorityIxs = createPriorityFeeInstructions({\n * enabled: true,\n * microLamports: 5000,\n * computeUnits: 150_000,\n * });\n * \n * // Add to transaction\n * transaction.add(...priorityIxs, ...yourInstructions);\n * ```\n */\nexport function createPriorityFeeInstructions(\n config: PriorityFeeConfig = {}\n): TransactionInstruction[] {\n const { enabled = false, microLamports, computeUnits } = config;\n\n if (!enabled) {\n return [];\n }\n\n const instructions: TransactionInstruction[] = [];\n\n // Set compute unit limit\n const units = computeUnits ?? DEFAULT_COMPUTE_UNITS;\n instructions.push(ComputeBudgetProgram.setComputeUnitLimit({ units }));\n\n // Set compute unit price\n const price = microLamports ?? DEFAULT_MICRO_LAMPORTS;\n instructions.push(ComputeBudgetProgram.setComputeUnitPrice({ microLamports: price }));\n\n return instructions;\n}\n\n/**\n * Estimate priority fee based on recent network activity\n * Returns micro-lamports per compute unit\n * \n * @example\n * ```typescript\n * const fee = await estimatePriorityFee(connection, [recipientPubkey]);\n * console.log(`Recommended fee: ${fee} micro-lamports/CU`);\n * ```\n */\nexport async function estimatePriorityFee(\n connection: Connection,\n accounts: PublicKey[] = []\n): Promise<number> {\n try {\n const fees = await connection.getRecentPrioritizationFees({\n lockedWritableAccounts: accounts,\n });\n\n if (fees.length === 0) {\n return DEFAULT_MICRO_LAMPORTS;\n }\n\n // Get median of recent fees (more stable than mean)\n const sortedFees = fees\n .map((f) => f.prioritizationFee)\n .filter((f) => f > 0)\n .sort((a, b) => a - b);\n\n if (sortedFees.length === 0) {\n return DEFAULT_MICRO_LAMPORTS;\n }\n\n const medianIndex = Math.floor(sortedFees.length / 2);\n return sortedFees[medianIndex];\n } catch {\n // Fallback on RPC errors\n return DEFAULT_MICRO_LAMPORTS;\n }\n}\n\n/**\n * Calculate total priority fee cost in lamports\n * \n * @example\n * ```typescript\n * const cost = calculatePriorityFeeCost(5000, 200_000);\n * console.log(`Priority fee: ${cost} lamports`);\n * ```\n */\nexport function calculatePriorityFeeCost(\n microLamportsPerCU: number,\n computeUnits: number\n): number {\n return Math.ceil((microLamportsPerCU * computeUnits) / 1_000_000);\n}\n","// Versioned Transaction Support\n// Utilities for building and working with versioned transactions\n\nimport {\n TransactionMessage,\n VersionedTransaction,\n type AddressLookupTableAccount,\n type Connection,\n type PublicKey,\n type TransactionInstruction,\n} from '@solana/web3.js';\nimport { createPriorityFeeInstructions, type PriorityFeeConfig } from './priority-fees';\n\n/** Configuration for building versioned transactions */\nexport interface VersionedTransactionConfig {\n /** Solana connection */\n connection: Connection;\n /** Fee payer public key */\n payer: PublicKey;\n /** Transaction instructions */\n instructions: TransactionInstruction[];\n /** Address lookup tables for compression (optional) */\n lookupTables?: AddressLookupTableAccount[];\n /** Priority fee configuration (optional, default: disabled) */\n priorityFee?: PriorityFeeConfig;\n /** Recent blockhash (optional, will fetch if not provided) */\n recentBlockhash?: string;\n}\n\n/** Result of building a versioned transaction */\nexport interface VersionedTransactionResult {\n /** The built versioned transaction */\n transaction: VersionedTransaction;\n /** The blockhash used */\n blockhash: string;\n /** Last valid block height for the transaction */\n lastValidBlockHeight: number;\n}\n\n/**\n * Build a versioned transaction (v0) with optional priority fees and lookup tables\n * \n * @example\n * ```typescript\n * const { transaction, blockhash, lastValidBlockHeight } = await buildVersionedTransaction({\n * connection,\n * payer: wallet.publicKey,\n * instructions: [transferIx],\n * priorityFee: { enabled: true, microLamports: 5000 },\n * });\n * \n * // Sign and send\n * transaction.sign([wallet]);\n * const sig = await connection.sendTransaction(transaction);\n * ```\n */\nexport async function buildVersionedTransaction(\n config: VersionedTransactionConfig\n): Promise<VersionedTransactionResult> {\n const {\n connection,\n payer,\n instructions,\n lookupTables = [],\n priorityFee,\n recentBlockhash,\n } = config;\n\n // Prepend priority fee instructions if configured\n const priorityIxs = createPriorityFeeInstructions(priorityFee);\n const allInstructions = [...priorityIxs, ...instructions];\n\n // Get recent blockhash if not provided\n let blockhash: string;\n let lastValidBlockHeight: number;\n\n if (recentBlockhash) {\n blockhash = recentBlockhash;\n // Estimate last valid block height (typically ~150 blocks)\n const slot = await connection.getSlot();\n lastValidBlockHeight = slot + 150;\n } else {\n const latestBlockhash = await connection.getLatestBlockhash('confirmed');\n blockhash = latestBlockhash.blockhash;\n lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;\n }\n\n // Build message\n const message = new TransactionMessage({\n payerKey: payer,\n recentBlockhash: blockhash,\n instructions: allInstructions,\n }).compileToV0Message(lookupTables);\n\n // Create versioned transaction\n const transaction = new VersionedTransaction(message);\n\n return {\n transaction,\n blockhash,\n lastValidBlockHeight,\n };\n}\n\n/**\n * Fetch address lookup table accounts\n * \n * @example\n * ```typescript\n * const tables = await fetchLookupTables(connection, [tableAddress1, tableAddress2]);\n * const { transaction } = await buildVersionedTransaction({\n * connection,\n * payer,\n * instructions,\n * lookupTables: tables,\n * });\n * ```\n */\nexport async function fetchLookupTables(\n connection: Connection,\n addresses: PublicKey[]\n): Promise<AddressLookupTableAccount[]> {\n const tables: AddressLookupTableAccount[] = [];\n\n for (const address of addresses) {\n const result = await connection.getAddressLookupTable(address);\n if (result.value) {\n tables.push(result.value);\n }\n }\n\n return tables;\n}\n\n/**\n * Check if a transaction version is supported\n */\nexport function isVersionedTransaction(\n tx: unknown\n): tx is VersionedTransaction {\n return tx instanceof VersionedTransaction;\n}\n","// Agent Payment Utilities\n// Server-side autonomous payment execution for AI agents\n// SECURITY: Keypair must only be loaded server-side\n\nimport {\n Connection,\n Keypair,\n PublicKey,\n SystemProgram,\n LAMPORTS_PER_SOL,\n} from '@solana/web3.js';\nimport { buildVersionedTransaction, type PriorityFeeConfig } from '../solana';\n\n/** Parameters for executing an agent payment */\nexport interface ExecuteAgentPaymentParams {\n /** Solana connection */\n connection: Connection;\n /** Agent's keypair (server-side only!) */\n agentKeypair: Keypair;\n /** Recipient wallet address (base58) */\n recipientAddress: string;\n /** Amount to send in lamports */\n amountLamports: bigint;\n /** Optional memo for the transaction */\n memo?: string;\n /** Optional priority fee configuration */\n priorityFee?: PriorityFeeConfig;\n /** Timeout for confirmation in ms (default: 60000) */\n confirmationTimeout?: number;\n}\n\n/** Result of an agent payment execution */\nexport interface AgentPaymentResult {\n /** Whether the payment was successful */\n success: boolean;\n /** Transaction signature (if successful) */\n signature?: string;\n /** Error message (if failed) */\n error?: string;\n /** Block time when confirmed (Unix timestamp) */\n confirmedAt?: number;\n /** Slot number */\n slot?: number;\n /** Amount sent in lamports */\n amountLamports?: bigint;\n /** Amount sent in SOL */\n amountSol?: number;\n}\n\n// Wallet address validation regex\nconst WALLET_REGEX = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;\n\n/**\n * Validate wallet address format\n */\nfunction isValidWalletAddress(address: string): boolean {\n if (!address || typeof address !== 'string') return false;\n return WALLET_REGEX.test(address);\n}\n\n/**\n * Execute an autonomous SOL payment from the agent's wallet\n * \n * This is the core utility for AI agents to pay for x402-protected resources.\n * The agent keypair must be loaded server-side only (never exposed to client).\n * \n * @example\n * ```typescript\n * import { executeAgentPayment } from '@alleyboss/micropay-solana-x402-paywall/agent';\n * import { Keypair, Connection } from '@solana/web3.js';\n * import bs58 from 'bs58';\n * \n * // Load agent keypair from environment (server-side only!)\n * const agentKeypair = Keypair.fromSecretKey(\n * bs58.decode(process.env.AGENT_KEYPAIR_SECRET!)\n * );\n * \n * const connection = new Connection('https://api.devnet.solana.com');\n * \n * const result = await executeAgentPayment({\n * connection,\n * agentKeypair,\n * recipientAddress: 'RecipientWallet...',\n * amountLamports: 20_000_000n, // 0.02 SOL\n * priorityFee: { enabled: true, microLamports: 5000 },\n * });\n * \n * if (result.success) {\n * console.log('Payment sent:', result.signature);\n * }\n * ```\n */\nexport async function executeAgentPayment(\n params: ExecuteAgentPaymentParams\n): Promise<AgentPaymentResult> {\n const {\n connection,\n agentKeypair,\n recipientAddress,\n amountLamports,\n priorityFee,\n confirmationTimeout = 60000,\n } = params;\n\n // Validate recipient address\n if (!isValidWalletAddress(recipientAddress)) {\n return {\n success: false,\n error: 'Invalid recipient address format',\n };\n }\n\n // Validate amount\n if (amountLamports <= 0n) {\n return {\n success: false,\n error: 'Amount must be greater than 0',\n };\n }\n\n try {\n const recipientPubkey = new PublicKey(recipientAddress);\n\n // Create transfer instruction\n const transferInstruction = SystemProgram.transfer({\n fromPubkey: agentKeypair.publicKey,\n toPubkey: recipientPubkey,\n lamports: amountLamports,\n });\n\n // Build versioned transaction with optional priority fee\n const { transaction, lastValidBlockHeight } = await buildVersionedTransaction({\n connection,\n payer: agentKeypair.publicKey,\n instructions: [transferInstruction],\n priorityFee,\n });\n\n // Sign transaction\n transaction.sign([agentKeypair]);\n\n // Send transaction\n const signature = await connection.sendTransaction(transaction, {\n maxRetries: 3,\n skipPreflight: false,\n });\n\n // Wait for confirmation with timeout\n const confirmationPromise = connection.confirmTransaction(\n {\n signature,\n lastValidBlockHeight,\n blockhash: transaction.message.recentBlockhash,\n },\n 'confirmed'\n );\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => reject(new Error('Confirmation timeout')), confirmationTimeout);\n });\n\n const confirmation = await Promise.race([confirmationPromise, timeoutPromise]);\n\n if (confirmation.value.err) {\n return {\n success: false,\n signature,\n error: 'Transaction failed on-chain',\n };\n }\n\n // Get transaction details for confirmed time\n const txDetails = await connection.getTransaction(signature, {\n commitment: 'confirmed',\n maxSupportedTransactionVersion: 0,\n });\n\n return {\n success: true,\n signature,\n confirmedAt: txDetails?.blockTime ?? Math.floor(Date.now() / 1000),\n slot: txDetails?.slot ?? confirmation.context.slot,\n amountLamports,\n amountSol: Number(amountLamports) / LAMPORTS_PER_SOL,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n return {\n success: false,\n error: errorMessage,\n };\n }\n}\n\n/**\n * Get the agent wallet's SOL balance\n */\nexport async function getAgentBalance(\n connection: Connection,\n agentKeypair: Keypair\n): Promise<{ balance: bigint; balanceSol: number }> {\n const balance = await connection.getBalance(agentKeypair.publicKey);\n return {\n balance: BigInt(balance),\n balanceSol: balance / LAMPORTS_PER_SOL,\n };\n}\n\n/**\n * Check if agent has sufficient balance for a payment\n */\nexport async function hasAgentSufficientBalance(\n connection: Connection,\n agentKeypair: Keypair,\n requiredLamports: bigint\n): Promise<{ sufficient: boolean; balance: bigint; required: bigint }> {\n const { balance } = await getAgentBalance(connection, agentKeypair);\n // Add buffer for transaction fees (~5000 lamports)\n const totalRequired = requiredLamports + 10000n;\n return {\n sufficient: balance >= totalRequired,\n balance,\n required: totalRequired,\n };\n}\n\n/**\n * Create a Keypair from a base58-encoded secret key string\n * \n * @example\n * ```typescript\n * const keypair = keypairFromBase58(process.env.AGENT_KEYPAIR_SECRET!);\n * ```\n */\nexport function keypairFromBase58(base58Secret: string): Keypair {\n // Dynamic import to avoid bundling bs58 in client code\n // For server-side use, install bs58 as a dependency\n const bytes = Buffer.from(base58Secret, 'base64');\n\n // Try base58 decode if base64 fails\n if (bytes.length !== 64) {\n // Fallback: assume it's a comma-separated array of numbers\n const parts = base58Secret.split(',').map(n => parseInt(n.trim(), 10));\n if (parts.length === 64) {\n return Keypair.fromSecretKey(Uint8Array.from(parts));\n }\n throw new Error('Invalid secret key format. Expected base58 string or comma-separated bytes.');\n }\n\n return Keypair.fromSecretKey(bytes);\n}\n\n/**\n * Create a new random Keypair for agent use\n * Returns both the keypair and its base58-encoded secret for storage\n */\nexport function generateAgentKeypair(): { keypair: Keypair; secretBase58: string; publicKey: string } {\n const keypair = Keypair.generate();\n const secretBytes = Array.from(keypair.secretKey);\n return {\n keypair,\n secretBase58: secretBytes.join(','), // Comma-separated for easy storage\n publicKey: keypair.publicKey.toBase58(),\n };\n}\n","// Bundle Credits System\n// Optional credit-based session management for agent payments\n\nimport { SignJWT, jwtVerify } from 'jose';\nimport { v4 as uuidv4 } from 'uuid';\nimport type { SessionConfig, SessionData } from '../types';\n\n/** Credit claims stored in session JWT */\nexport interface CreditSessionClaims {\n /** Number of remaining credits */\n credits: number;\n /** Unix timestamp when bundle expires (optional) */\n bundleExpiry?: number;\n /** Bundle type identifier (e.g., 'starter', 'pro') */\n bundleType?: string;\n}\n\n/** Extended session data with credit information */\nexport type CreditSessionData = SessionData & CreditSessionClaims;\n\n/** Configuration for credit sessions */\nexport interface CreditSessionConfig extends SessionConfig {\n /** Initial number of credits */\n initialCredits: number;\n /** Bundle expiry in hours (optional, defaults to session duration) */\n bundleExpiryHours?: number;\n /** Bundle type identifier */\n bundleType?: string;\n}\n\n/** JWT payload for credit sessions */\ninterface CreditJWTPayload {\n sub: string;\n sid: string;\n articles: string[];\n siteWide: boolean;\n credits: number;\n bundleExpiry?: number;\n bundleType?: string;\n iat: number;\n exp: number;\n}\n\n// Constants\nconst MAX_CREDITS = 1000;\nconst MIN_SECRET_LENGTH = 32;\n\n/**\n * Get the secret key for JWT signing\n */\nfunction getSecretKey(secret: string): Uint8Array {\n if (!secret || typeof secret !== 'string') {\n throw new Error('Session secret is required');\n }\n if (secret.length < MIN_SECRET_LENGTH) {\n throw new Error(`Session secret must be at least ${MIN_SECRET_LENGTH} characters`);\n }\n return new TextEncoder().encode(secret);\n}\n\n/**\n * Validate wallet address format\n */\nfunction validateWalletAddress(address: string): boolean {\n if (!address || typeof address !== 'string') return false;\n const base58Regex = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;\n return base58Regex.test(address);\n}\n\n/**\n * Create a new credit session after bundle purchase\n * \n * @example\n * ```typescript\n * import { createCreditSession } from '@alleyboss/micropay-solana-x402-paywall/agent';\n * \n * const { token, session } = await createCreditSession(\n * walletAddress,\n * 'bundle-purchase',\n * {\n * secret: process.env.SESSION_SECRET!,\n * durationHours: 24 * 30, // 30 days\n * initialCredits: 10,\n * bundleType: 'starter',\n * }\n * );\n * \n * // Set as cookie\n * response.cookies.set('x402_credits', token, { httpOnly: true });\n * ```\n */\nexport async function createCreditSession(\n walletAddress: string,\n purchaseId: string,\n config: CreditSessionConfig\n): Promise<{ token: string; session: CreditSessionData }> {\n // Input validation\n if (!validateWalletAddress(walletAddress)) {\n throw new Error('Invalid wallet address format');\n }\n if (config.initialCredits <= 0 || config.initialCredits > MAX_CREDITS) {\n throw new Error(`Credits must be between 1 and ${MAX_CREDITS}`);\n }\n if (!config.durationHours || config.durationHours <= 0 || config.durationHours > 8760) {\n throw new Error('Session duration must be between 1 and 8760 hours (1 year)');\n }\n\n const sessionId = uuidv4();\n const now = Math.floor(Date.now() / 1000);\n const expiresAt = now + (config.durationHours * 3600);\n const bundleExpiry = config.bundleExpiryHours\n ? now + (config.bundleExpiryHours * 3600)\n : expiresAt;\n\n const session: CreditSessionData = {\n id: sessionId,\n walletAddress,\n unlockedArticles: [purchaseId],\n siteWideUnlock: false,\n createdAt: now,\n expiresAt,\n credits: config.initialCredits,\n bundleExpiry,\n bundleType: config.bundleType,\n };\n\n const payload: CreditJWTPayload = {\n sub: walletAddress,\n sid: sessionId,\n articles: session.unlockedArticles,\n siteWide: false,\n credits: config.initialCredits,\n bundleExpiry,\n bundleType: config.bundleType,\n iat: now,\n exp: expiresAt,\n };\n\n const token = await new SignJWT(payload as unknown as Record<string, unknown>)\n .setProtectedHeader({ alg: 'HS256' })\n .setIssuedAt()\n .setExpirationTime(`${config.durationHours}h`)\n .sign(getSecretKey(config.secret));\n\n return { token, session };\n}\n\n/** Result of validating a credit session */\nexport interface CreditValidation {\n valid: boolean;\n session?: CreditSessionData;\n reason?: string;\n}\n\n/**\n * Validate a credit session token\n */\nexport async function validateCreditSession(\n token: string,\n secret: string\n): Promise<CreditValidation> {\n if (!token || typeof token !== 'string') {\n return { valid: false, reason: 'Invalid token format' };\n }\n\n try {\n const { payload } = await jwtVerify(token, getSecretKey(secret));\n const creditPayload = payload as unknown as CreditJWTPayload;\n\n // Validate required fields\n if (!creditPayload.sub || !creditPayload.sid || !creditPayload.exp) {\n return { valid: false, reason: 'Malformed session payload' };\n }\n\n // Check expiration\n const now = Math.floor(Date.now() / 1000);\n if (creditPayload.exp < now) {\n return { valid: false, reason: 'Session expired' };\n }\n\n // Check bundle expiry if set\n if (creditPayload.bundleExpiry && creditPayload.bundleExpiry < now) {\n return { valid: false, reason: 'Bundle expired' };\n }\n\n // Validate wallet format\n if (!validateWalletAddress(creditPayload.sub)) {\n return { valid: false, reason: 'Invalid session data' };\n }\n\n const session: CreditSessionData = {\n id: creditPayload.sid,\n walletAddress: creditPayload.sub,\n unlockedArticles: Array.isArray(creditPayload.articles) ? creditPayload.articles : [],\n siteWideUnlock: Boolean(creditPayload.siteWide),\n createdAt: creditPayload.iat ?? 0,\n expiresAt: creditPayload.exp,\n credits: creditPayload.credits ?? 0,\n bundleExpiry: creditPayload.bundleExpiry,\n bundleType: creditPayload.bundleType,\n };\n\n return { valid: true, session };\n } catch {\n return { valid: false, reason: 'Invalid session' };\n }\n}\n\n/** Result of using a credit */\nexport interface UseCreditResult {\n /** Whether the credit was successfully used */\n success: boolean;\n /** Remaining credits after use */\n remainingCredits: number;\n /** New token with decremented credits (if successful) */\n newToken?: string;\n /** Error message (if failed) */\n error?: string;\n}\n\n/**\n * Use one credit from the session\n * Returns a new token with decremented credit count\n * \n * @example\n * ```typescript\n * import { useCredit } from '@alleyboss/micropay-solana-x402-paywall/agent';\n * \n * const result = await useCredit(token, process.env.SESSION_SECRET!);\n * \n * if (result.success) {\n * console.log(`Credit used. ${result.remainingCredits} remaining`);\n * // Update cookie with new token\n * response.cookies.set('x402_credits', result.newToken!);\n * } else {\n * console.log('No credits:', result.error);\n * }\n * ```\n */\nexport async function useCredit(\n token: string,\n secret: string,\n creditsToUse: number = 1\n): Promise<UseCreditResult> {\n if (creditsToUse <= 0) {\n return { success: false, remainingCredits: 0, error: 'Invalid credit amount' };\n }\n\n const validation = await validateCreditSession(token, secret);\n\n if (!validation.valid || !validation.session) {\n return {\n success: false,\n remainingCredits: 0,\n error: validation.reason || 'Invalid session'\n };\n }\n\n const session = validation.session;\n\n // Check if enough credits\n if (session.credits < creditsToUse) {\n return {\n success: false,\n remainingCredits: session.credits,\n error: 'Insufficient credits'\n };\n }\n\n // Decrement credits and create new token\n const newCredits = session.credits - creditsToUse;\n\n const payload: CreditJWTPayload = {\n sub: session.walletAddress,\n sid: session.id,\n articles: session.unlockedArticles,\n siteWide: session.siteWideUnlock,\n credits: newCredits,\n bundleExpiry: session.bundleExpiry,\n bundleType: session.bundleType,\n iat: session.createdAt,\n exp: session.expiresAt,\n };\n\n const newToken = await new SignJWT(payload as unknown as Record<string, unknown>)\n .setProtectedHeader({ alg: 'HS256' })\n .sign(getSecretKey(secret));\n\n return {\n success: true,\n remainingCredits: newCredits,\n newToken,\n };\n}\n\n/**\n * Add credits to an existing session\n * Useful for top-ups or rewards\n */\nexport async function addCredits(\n token: string,\n secret: string,\n creditsToAdd: number\n): Promise<{ success: boolean; newToken?: string; totalCredits?: number; error?: string }> {\n if (creditsToAdd <= 0 || creditsToAdd > MAX_CREDITS) {\n return { success: false, error: 'Invalid credit amount' };\n }\n\n const validation = await validateCreditSession(token, secret);\n\n if (!validation.valid || !validation.session) {\n return { success: false, error: validation.reason || 'Invalid session' };\n }\n\n const session = validation.session;\n const newCredits = Math.min(session.credits + creditsToAdd, MAX_CREDITS);\n\n const payload: CreditJWTPayload = {\n sub: session.walletAddress,\n sid: session.id,\n articles: session.unlockedArticles,\n siteWide: session.siteWideUnlock,\n credits: newCredits,\n bundleExpiry: session.bundleExpiry,\n bundleType: session.bundleType,\n iat: session.createdAt,\n exp: session.expiresAt,\n };\n\n const newToken = await new SignJWT(payload as unknown as Record<string, unknown>)\n .setProtectedHeader({ alg: 'HS256' })\n .sign(getSecretKey(secret));\n\n return {\n success: true,\n newToken,\n totalCredits: newCredits,\n };\n}\n\n/**\n * Get remaining credits from a session token (quick check without full validation)\n */\nexport async function getRemainingCredits(\n token: string,\n secret: string\n): Promise<{ credits: number; valid: boolean; bundleExpiry?: number }> {\n const validation = await validateCreditSession(token, secret);\n\n if (!validation.valid || !validation.session) {\n return { credits: 0, valid: false };\n }\n\n return {\n credits: validation.session.credits,\n valid: true,\n bundleExpiry: validation.session.bundleExpiry,\n };\n}\n"]}