@alleyboss/micropay-solana-x402-paywall 2.3.1 → 3.0.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 +85 -139
- package/dist/agent/index.cjs +1 -2
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +11 -2
- package/dist/agent/index.d.ts +11 -2
- package/dist/agent/index.js +1 -2
- package/dist/agent/index.js.map +1 -1
- package/dist/client/index.cjs +1 -1
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +10 -1
- package/dist/client/index.d.ts +10 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/express/index.cjs +79 -0
- package/dist/express/index.cjs.map +1 -0
- package/dist/express/index.d.cts +40 -0
- package/dist/express/index.d.ts +40 -0
- package/dist/express/index.js +76 -0
- package/dist/express/index.js.map +1 -0
- package/dist/index.cjs +257 -1357
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -12
- package/dist/index.d.ts +6 -12
- package/dist/index.js +239 -1319
- package/dist/index.js.map +1 -1
- package/dist/session/index.cjs.map +1 -1
- package/dist/session/index.d.cts +1 -1
- package/dist/session/index.d.ts +1 -1
- package/dist/session/index.js.map +1 -1
- package/dist/{session-D2IoWAWV.d.cts → types-BWYQMw03.d.cts} +1 -16
- package/dist/{session-D2IoWAWV.d.ts → types-BWYQMw03.d.ts} +1 -16
- package/package.json +28 -68
- package/dist/client-D-dteoJw.d.cts +0 -63
- package/dist/client-DfCIRrNG.d.ts +0 -63
- package/dist/memory-Daxkczti.d.cts +0 -29
- package/dist/memory-Daxkczti.d.ts +0 -29
- package/dist/middleware/index.cjs +0 -273
- package/dist/middleware/index.cjs.map +0 -1
- package/dist/middleware/index.d.cts +0 -91
- package/dist/middleware/index.d.ts +0 -91
- package/dist/middleware/index.js +0 -267
- package/dist/middleware/index.js.map +0 -1
- package/dist/nextjs-BDyOqGAq.d.cts +0 -81
- package/dist/nextjs-CbX8_9yK.d.ts +0 -81
- package/dist/payment-BGp7eMQl.d.cts +0 -103
- package/dist/payment-BGp7eMQl.d.ts +0 -103
- package/dist/priority-fees-C-OH4Trr.d.cts +0 -50
- package/dist/priority-fees-C-OH4Trr.d.ts +0 -50
- package/dist/solana/index.cjs +0 -589
- package/dist/solana/index.cjs.map +0 -1
- package/dist/solana/index.d.cts +0 -195
- package/dist/solana/index.d.ts +0 -195
- package/dist/solana/index.js +0 -567
- package/dist/solana/index.js.map +0 -1
- package/dist/store/index.cjs +0 -99
- package/dist/store/index.cjs.map +0 -1
- package/dist/store/index.d.cts +0 -38
- package/dist/store/index.d.ts +0 -38
- package/dist/store/index.js +0 -96
- package/dist/store/index.js.map +0 -1
- package/dist/utils/index.cjs +0 -68
- package/dist/utils/index.cjs.map +0 -1
- package/dist/utils/index.d.cts +0 -30
- package/dist/utils/index.d.ts +0 -30
- package/dist/utils/index.js +0 -65
- package/dist/utils/index.js.map +0 -1
- package/dist/x402/index.cjs +0 -387
- package/dist/x402/index.cjs.map +0 -1
- package/dist/x402/index.d.cts +0 -96
- package/dist/x402/index.d.ts +0 -96
- package/dist/x402/index.js +0 -375
- package/dist/x402/index.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,1153 +1,24 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var core = require('@x402/core');
|
|
4
|
+
var types = require('@x402/core/types');
|
|
5
|
+
var client = require('@x402/core/client');
|
|
6
|
+
var svm = require('@x402/svm');
|
|
3
7
|
var web3_js = require('@solana/web3.js');
|
|
4
8
|
var jose = require('jose');
|
|
5
9
|
var uuid = require('uuid');
|
|
6
10
|
|
|
7
|
-
// src/
|
|
8
|
-
var TOKEN_MINTS = {
|
|
9
|
-
/** USDC on mainnet */
|
|
10
|
-
USDC_MAINNET: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
|
|
11
|
-
/** USDC on devnet */
|
|
12
|
-
USDC_DEVNET: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
|
|
13
|
-
/** USDT on mainnet */
|
|
14
|
-
USDT_MAINNET: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
|
|
15
|
-
};
|
|
16
|
-
var cachedConnection = null;
|
|
17
|
-
var cachedNetwork = null;
|
|
18
|
-
var cachedFallbacks = [];
|
|
19
|
-
var cachedFallbackEnabled = false;
|
|
20
|
-
function buildRpcUrl(config2) {
|
|
21
|
-
const { network, rpcUrl, tatumApiKey } = config2;
|
|
22
|
-
if (rpcUrl) {
|
|
23
|
-
if (rpcUrl.includes("tatum.io") && tatumApiKey && !rpcUrl.includes(tatumApiKey)) {
|
|
24
|
-
return rpcUrl.endsWith("/") ? `${rpcUrl}${tatumApiKey}` : `${rpcUrl}/${tatumApiKey}`;
|
|
25
|
-
}
|
|
26
|
-
return rpcUrl;
|
|
27
|
-
}
|
|
28
|
-
if (tatumApiKey) {
|
|
29
|
-
const baseUrl = network === "mainnet-beta" ? "https://solana-mainnet.gateway.tatum.io" : "https://solana-devnet.gateway.tatum.io";
|
|
30
|
-
return `${baseUrl}/${tatumApiKey}`;
|
|
31
|
-
}
|
|
32
|
-
return web3_js.clusterApiUrl(network);
|
|
33
|
-
}
|
|
34
|
-
function createConnection(rpcUrl) {
|
|
35
|
-
return new web3_js.Connection(rpcUrl, {
|
|
36
|
-
commitment: "confirmed",
|
|
37
|
-
confirmTransactionInitialTimeout: 6e4
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
function getConnection(config2) {
|
|
41
|
-
const { network } = config2;
|
|
42
|
-
if (cachedConnection && cachedNetwork === network) {
|
|
43
|
-
return cachedConnection;
|
|
44
|
-
}
|
|
45
|
-
const rpcUrl = buildRpcUrl(config2);
|
|
46
|
-
cachedConnection = createConnection(rpcUrl);
|
|
47
|
-
cachedNetwork = network;
|
|
48
|
-
cachedFallbackEnabled = config2.enableFallback ?? false;
|
|
49
|
-
cachedFallbacks = [];
|
|
50
|
-
if (cachedFallbackEnabled && config2.fallbackRpcUrls?.length) {
|
|
51
|
-
cachedFallbacks = config2.fallbackRpcUrls.map(createConnection);
|
|
52
|
-
}
|
|
53
|
-
return cachedConnection;
|
|
54
|
-
}
|
|
55
|
-
function getConnectionWithFallback(config2) {
|
|
56
|
-
const connection = getConnection(config2);
|
|
57
|
-
return {
|
|
58
|
-
connection,
|
|
59
|
-
fallbacks: cachedFallbacks,
|
|
60
|
-
fallbackEnabled: cachedFallbackEnabled
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
async function withFallback(config2, operation) {
|
|
64
|
-
const { connection, fallbacks, fallbackEnabled } = getConnectionWithFallback(config2);
|
|
65
|
-
try {
|
|
66
|
-
return await operation(connection);
|
|
67
|
-
} catch (error) {
|
|
68
|
-
if (!fallbackEnabled || fallbacks.length === 0) {
|
|
69
|
-
throw error;
|
|
70
|
-
}
|
|
71
|
-
if (!isRetryableError(error)) {
|
|
72
|
-
throw error;
|
|
73
|
-
}
|
|
74
|
-
for (let i = 0; i < fallbacks.length; i++) {
|
|
75
|
-
try {
|
|
76
|
-
return await operation(fallbacks[i]);
|
|
77
|
-
} catch (fallbackError) {
|
|
78
|
-
if (i === fallbacks.length - 1) {
|
|
79
|
-
throw fallbackError;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
throw error;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
function isRetryableError(error) {
|
|
87
|
-
if (error instanceof Error) {
|
|
88
|
-
const message = error.message.toLowerCase();
|
|
89
|
-
return message.includes("429") || message.includes("503") || message.includes("502") || message.includes("timeout") || message.includes("econnrefused") || message.includes("enotfound") || message.includes("rate limit");
|
|
90
|
-
}
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
function resetConnection() {
|
|
94
|
-
cachedConnection = null;
|
|
95
|
-
cachedNetwork = null;
|
|
96
|
-
}
|
|
97
|
-
function isMainnet(network) {
|
|
98
|
-
return network === "mainnet-beta";
|
|
99
|
-
}
|
|
100
|
-
function toX402Network(network) {
|
|
101
|
-
return network === "mainnet-beta" ? "solana-mainnet" : "solana-devnet";
|
|
102
|
-
}
|
|
103
|
-
var SIGNATURE_REGEX = /^[1-9A-HJ-NP-Za-km-z]{87,88}$/;
|
|
104
|
-
var WALLET_REGEX = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
105
|
-
function isValidSignature(signature) {
|
|
106
|
-
if (!signature || typeof signature !== "string") return false;
|
|
107
|
-
return SIGNATURE_REGEX.test(signature);
|
|
108
|
-
}
|
|
109
|
-
function isValidWalletAddress(address) {
|
|
110
|
-
if (!address || typeof address !== "string") return false;
|
|
111
|
-
return WALLET_REGEX.test(address);
|
|
112
|
-
}
|
|
113
|
-
function parseSOLTransfer(transaction, expectedRecipient) {
|
|
114
|
-
const instructions = transaction.transaction.message.instructions;
|
|
115
|
-
for (const ix of instructions) {
|
|
116
|
-
if ("parsed" in ix && ix.program === "system") {
|
|
117
|
-
const parsed = ix.parsed;
|
|
118
|
-
if (parsed.type === "transfer" && parsed.info.destination === expectedRecipient) {
|
|
119
|
-
return {
|
|
120
|
-
from: parsed.info.source,
|
|
121
|
-
to: parsed.info.destination,
|
|
122
|
-
amount: BigInt(parsed.info.lamports)
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
if (transaction.meta?.innerInstructions) {
|
|
128
|
-
for (const inner of transaction.meta.innerInstructions) {
|
|
129
|
-
for (const ix of inner.instructions) {
|
|
130
|
-
if ("parsed" in ix && ix.program === "system") {
|
|
131
|
-
const parsed = ix.parsed;
|
|
132
|
-
if (parsed.type === "transfer" && parsed.info.destination === expectedRecipient) {
|
|
133
|
-
return {
|
|
134
|
-
from: parsed.info.source,
|
|
135
|
-
to: parsed.info.destination,
|
|
136
|
-
amount: BigInt(parsed.info.lamports)
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
async function verifyPayment(params) {
|
|
146
|
-
const {
|
|
147
|
-
signature,
|
|
148
|
-
expectedRecipient,
|
|
149
|
-
expectedAmount,
|
|
150
|
-
maxAgeSeconds = 300,
|
|
151
|
-
clientConfig,
|
|
152
|
-
signatureStore
|
|
153
|
-
} = params;
|
|
154
|
-
if (!isValidSignature(signature)) {
|
|
155
|
-
return { valid: false, confirmed: false, signature, error: "Invalid signature format" };
|
|
156
|
-
}
|
|
157
|
-
if (!isValidWalletAddress(expectedRecipient)) {
|
|
158
|
-
return { valid: false, confirmed: false, signature, error: "Invalid recipient address" };
|
|
159
|
-
}
|
|
160
|
-
if (expectedAmount <= 0n) {
|
|
161
|
-
return { valid: false, confirmed: false, signature, error: "Invalid expected amount" };
|
|
162
|
-
}
|
|
163
|
-
if (signatureStore) {
|
|
164
|
-
const isUsed = await signatureStore.hasBeenUsed(signature);
|
|
165
|
-
if (isUsed) {
|
|
166
|
-
return { valid: false, confirmed: true, signature, error: "Signature already used" };
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
const effectiveMaxAge = Math.min(Math.max(maxAgeSeconds, 60), 3600);
|
|
170
|
-
const connection = getConnection(clientConfig);
|
|
171
|
-
try {
|
|
172
|
-
const transaction = await connection.getParsedTransaction(signature, {
|
|
173
|
-
commitment: "confirmed",
|
|
174
|
-
maxSupportedTransactionVersion: 0
|
|
175
|
-
});
|
|
176
|
-
if (!transaction) {
|
|
177
|
-
return { valid: false, confirmed: false, signature, error: "Transaction not found" };
|
|
178
|
-
}
|
|
179
|
-
if (transaction.meta?.err) {
|
|
180
|
-
return {
|
|
181
|
-
valid: false,
|
|
182
|
-
confirmed: true,
|
|
183
|
-
signature,
|
|
184
|
-
error: "Transaction failed on-chain"
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
if (transaction.blockTime) {
|
|
188
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
189
|
-
if (now - transaction.blockTime > effectiveMaxAge) {
|
|
190
|
-
return { valid: false, confirmed: true, signature, error: "Transaction too old" };
|
|
191
|
-
}
|
|
192
|
-
if (transaction.blockTime > now + 60) {
|
|
193
|
-
return { valid: false, confirmed: true, signature, error: "Invalid transaction time" };
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
const transferDetails = parseSOLTransfer(transaction, expectedRecipient);
|
|
197
|
-
if (!transferDetails) {
|
|
198
|
-
return {
|
|
199
|
-
valid: false,
|
|
200
|
-
confirmed: true,
|
|
201
|
-
signature,
|
|
202
|
-
error: "No valid SOL transfer to recipient found"
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
if (transferDetails.amount < expectedAmount) {
|
|
206
|
-
return {
|
|
207
|
-
valid: false,
|
|
208
|
-
confirmed: true,
|
|
209
|
-
signature,
|
|
210
|
-
from: transferDetails.from,
|
|
211
|
-
to: transferDetails.to,
|
|
212
|
-
amount: transferDetails.amount,
|
|
213
|
-
error: "Insufficient payment amount"
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
return {
|
|
217
|
-
valid: true,
|
|
218
|
-
confirmed: true,
|
|
219
|
-
signature,
|
|
220
|
-
from: transferDetails.from,
|
|
221
|
-
to: transferDetails.to,
|
|
222
|
-
amount: transferDetails.amount,
|
|
223
|
-
blockTime: transaction.blockTime ?? void 0,
|
|
224
|
-
slot: transaction.slot
|
|
225
|
-
};
|
|
226
|
-
} catch (error) {
|
|
227
|
-
return {
|
|
228
|
-
valid: false,
|
|
229
|
-
confirmed: false,
|
|
230
|
-
signature,
|
|
231
|
-
error: "Verification failed"
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
async function waitForConfirmation(signature, clientConfig) {
|
|
236
|
-
if (!isValidSignature(signature)) {
|
|
237
|
-
return { confirmed: false, error: "Invalid signature format" };
|
|
238
|
-
}
|
|
239
|
-
const connection = getConnection(clientConfig);
|
|
240
|
-
try {
|
|
241
|
-
const confirmation = await connection.confirmTransaction(signature, "confirmed");
|
|
242
|
-
if (confirmation.value.err) {
|
|
243
|
-
return { confirmed: false, error: "Transaction failed" };
|
|
244
|
-
}
|
|
245
|
-
return { confirmed: true, slot: confirmation.context?.slot };
|
|
246
|
-
} catch {
|
|
247
|
-
return { confirmed: false, error: "Confirmation timeout" };
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
async function getWalletTransactions(walletAddress, clientConfig, limit = 20) {
|
|
251
|
-
if (!isValidWalletAddress(walletAddress)) {
|
|
252
|
-
return [];
|
|
253
|
-
}
|
|
254
|
-
const safeLimit = Math.min(Math.max(limit, 1), 100);
|
|
255
|
-
const connection = getConnection(clientConfig);
|
|
256
|
-
try {
|
|
257
|
-
const pubkey = new web3_js.PublicKey(walletAddress);
|
|
258
|
-
const signatures = await connection.getSignaturesForAddress(pubkey, { limit: safeLimit });
|
|
259
|
-
return signatures.map((sig) => ({
|
|
260
|
-
signature: sig.signature,
|
|
261
|
-
blockTime: sig.blockTime ?? void 0,
|
|
262
|
-
slot: sig.slot
|
|
263
|
-
}));
|
|
264
|
-
} catch {
|
|
265
|
-
return [];
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
function lamportsToSol(lamports) {
|
|
269
|
-
return Number(lamports) / web3_js.LAMPORTS_PER_SOL;
|
|
270
|
-
}
|
|
271
|
-
function solToLamports(sol) {
|
|
272
|
-
if (!Number.isFinite(sol) || sol < 0) {
|
|
273
|
-
throw new Error("Invalid SOL amount");
|
|
274
|
-
}
|
|
275
|
-
return BigInt(Math.floor(sol * web3_js.LAMPORTS_PER_SOL));
|
|
276
|
-
}
|
|
277
|
-
var SIGNATURE_REGEX2 = /^[1-9A-HJ-NP-Za-km-z]{87,88}$/;
|
|
278
|
-
var WALLET_REGEX2 = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
279
|
-
function resolveMintAddress(asset, network) {
|
|
280
|
-
if (asset === "native") return null;
|
|
281
|
-
if (asset === "usdc") {
|
|
282
|
-
return network === "mainnet-beta" ? TOKEN_MINTS.USDC_MAINNET : TOKEN_MINTS.USDC_DEVNET;
|
|
283
|
-
}
|
|
284
|
-
if (asset === "usdt") {
|
|
285
|
-
return TOKEN_MINTS.USDT_MAINNET;
|
|
286
|
-
}
|
|
287
|
-
if (typeof asset === "object" && "mint" in asset) {
|
|
288
|
-
return asset.mint;
|
|
289
|
-
}
|
|
290
|
-
return null;
|
|
291
|
-
}
|
|
292
|
-
function getTokenDecimals(asset) {
|
|
293
|
-
if (asset === "native") return 9;
|
|
294
|
-
if (asset === "usdc" || asset === "usdt") return 6;
|
|
295
|
-
if (typeof asset === "object" && "decimals" in asset) {
|
|
296
|
-
return asset.decimals ?? 6;
|
|
297
|
-
}
|
|
298
|
-
return 6;
|
|
299
|
-
}
|
|
300
|
-
function parseSPLTransfer(transaction, expectedRecipient, expectedMint) {
|
|
301
|
-
const instructions = transaction.transaction.message.instructions;
|
|
302
|
-
for (const ix of instructions) {
|
|
303
|
-
if ("parsed" in ix && (ix.program === "spl-token" || ix.program === "spl-token-2022")) {
|
|
304
|
-
const parsed = ix.parsed;
|
|
305
|
-
if (parsed.type === "transfer" || parsed.type === "transferChecked") {
|
|
306
|
-
const amount = parsed.info.amount || parsed.info.tokenAmount?.amount;
|
|
307
|
-
if (amount && parsed.info.destination) {
|
|
308
|
-
return {
|
|
309
|
-
from: parsed.info.authority || parsed.info.source || "",
|
|
310
|
-
to: parsed.info.destination,
|
|
311
|
-
amount: BigInt(amount),
|
|
312
|
-
mint: parsed.info.mint || expectedMint
|
|
313
|
-
};
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
if (transaction.meta?.innerInstructions) {
|
|
319
|
-
for (const inner of transaction.meta.innerInstructions) {
|
|
320
|
-
for (const ix of inner.instructions) {
|
|
321
|
-
if ("parsed" in ix && (ix.program === "spl-token" || ix.program === "spl-token-2022")) {
|
|
322
|
-
const parsed = ix.parsed;
|
|
323
|
-
if (parsed.type === "transfer" || parsed.type === "transferChecked") {
|
|
324
|
-
const amount = parsed.info.amount || parsed.info.tokenAmount?.amount;
|
|
325
|
-
if (amount) {
|
|
326
|
-
return {
|
|
327
|
-
from: parsed.info.authority || parsed.info.source || "",
|
|
328
|
-
to: parsed.info.destination || "",
|
|
329
|
-
amount: BigInt(amount),
|
|
330
|
-
mint: parsed.info.mint || expectedMint
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
if (transaction.meta?.postTokenBalances && transaction.meta?.preTokenBalances) {
|
|
339
|
-
const preBalances = transaction.meta.preTokenBalances;
|
|
340
|
-
const postBalances = transaction.meta.postTokenBalances;
|
|
341
|
-
for (const post of postBalances) {
|
|
342
|
-
if (post.mint === expectedMint && post.owner === expectedRecipient) {
|
|
343
|
-
const pre = preBalances.find(
|
|
344
|
-
(p) => p.accountIndex === post.accountIndex
|
|
345
|
-
);
|
|
346
|
-
const preAmount = BigInt(pre?.uiTokenAmount?.amount || "0");
|
|
347
|
-
const postAmount = BigInt(post.uiTokenAmount?.amount || "0");
|
|
348
|
-
const transferred = postAmount - preAmount;
|
|
349
|
-
if (transferred > 0n) {
|
|
350
|
-
return {
|
|
351
|
-
from: "",
|
|
352
|
-
// Can't determine from balance changes
|
|
353
|
-
to: expectedRecipient,
|
|
354
|
-
amount: transferred,
|
|
355
|
-
mint: expectedMint
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
return null;
|
|
362
|
-
}
|
|
363
|
-
async function verifySPLPayment(params) {
|
|
364
|
-
const {
|
|
365
|
-
signature,
|
|
366
|
-
expectedRecipient,
|
|
367
|
-
expectedAmount,
|
|
368
|
-
asset,
|
|
369
|
-
clientConfig,
|
|
370
|
-
maxAgeSeconds = 300,
|
|
371
|
-
signatureStore
|
|
372
|
-
} = params;
|
|
373
|
-
if (signatureStore) {
|
|
374
|
-
const isUsed = await signatureStore.hasBeenUsed(signature);
|
|
375
|
-
if (isUsed) {
|
|
376
|
-
return { valid: false, confirmed: true, signature, error: "Signature already used" };
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
if (!SIGNATURE_REGEX2.test(signature)) {
|
|
380
|
-
return { valid: false, confirmed: false, signature, error: "Invalid signature format" };
|
|
381
|
-
}
|
|
382
|
-
if (!WALLET_REGEX2.test(expectedRecipient)) {
|
|
383
|
-
return { valid: false, confirmed: false, signature, error: "Invalid recipient address" };
|
|
384
|
-
}
|
|
385
|
-
const mintAddress = resolveMintAddress(asset, clientConfig.network);
|
|
386
|
-
if (!mintAddress) {
|
|
387
|
-
return { valid: false, confirmed: false, signature, error: "Invalid asset configuration" };
|
|
388
|
-
}
|
|
389
|
-
if (expectedAmount <= 0n) {
|
|
390
|
-
return { valid: false, confirmed: false, signature, error: "Invalid expected amount" };
|
|
391
|
-
}
|
|
392
|
-
const effectiveMaxAge = Math.min(Math.max(maxAgeSeconds, 60), 3600);
|
|
393
|
-
const connection = getConnection(clientConfig);
|
|
394
|
-
try {
|
|
395
|
-
const transaction = await connection.getParsedTransaction(signature, {
|
|
396
|
-
commitment: "confirmed",
|
|
397
|
-
maxSupportedTransactionVersion: 0
|
|
398
|
-
});
|
|
399
|
-
if (!transaction) {
|
|
400
|
-
return { valid: false, confirmed: false, signature, error: "Transaction not found" };
|
|
401
|
-
}
|
|
402
|
-
if (transaction.meta?.err) {
|
|
403
|
-
return { valid: false, confirmed: true, signature, error: "Transaction failed on-chain" };
|
|
404
|
-
}
|
|
405
|
-
if (transaction.blockTime) {
|
|
406
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
407
|
-
if (now - transaction.blockTime > effectiveMaxAge) {
|
|
408
|
-
return { valid: false, confirmed: true, signature, error: "Transaction too old" };
|
|
409
|
-
}
|
|
410
|
-
if (transaction.blockTime > now + 60) {
|
|
411
|
-
return { valid: false, confirmed: true, signature, error: "Invalid transaction time" };
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
const transfer = parseSPLTransfer(transaction, expectedRecipient, mintAddress);
|
|
415
|
-
if (!transfer) {
|
|
416
|
-
return {
|
|
417
|
-
valid: false,
|
|
418
|
-
confirmed: true,
|
|
419
|
-
signature,
|
|
420
|
-
error: "No valid token transfer to recipient found"
|
|
421
|
-
};
|
|
422
|
-
}
|
|
423
|
-
if (transfer.to) {
|
|
424
|
-
try {
|
|
425
|
-
const destinationInfo = await connection.getParsedAccountInfo(new web3_js.PublicKey(transfer.to));
|
|
426
|
-
const owner = destinationInfo.value?.data?.parsed?.info?.owner;
|
|
427
|
-
if (owner && owner !== expectedRecipient) {
|
|
428
|
-
return {
|
|
429
|
-
valid: false,
|
|
430
|
-
confirmed: true,
|
|
431
|
-
signature,
|
|
432
|
-
error: "Recipient mismatch: Token account not owned by merchant"
|
|
433
|
-
};
|
|
434
|
-
}
|
|
435
|
-
} catch (e) {
|
|
436
|
-
return {
|
|
437
|
-
valid: false,
|
|
438
|
-
confirmed: true,
|
|
439
|
-
signature,
|
|
440
|
-
error: "Could not verify token account owner"
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
if (transfer.mint !== mintAddress) {
|
|
445
|
-
return {
|
|
446
|
-
valid: false,
|
|
447
|
-
confirmed: true,
|
|
448
|
-
signature,
|
|
449
|
-
error: "Token mint mismatch"
|
|
450
|
-
};
|
|
451
|
-
}
|
|
452
|
-
if (transfer.amount < expectedAmount) {
|
|
453
|
-
return {
|
|
454
|
-
valid: false,
|
|
455
|
-
confirmed: true,
|
|
456
|
-
signature,
|
|
457
|
-
from: transfer.from,
|
|
458
|
-
to: transfer.to,
|
|
459
|
-
mint: transfer.mint,
|
|
460
|
-
amount: transfer.amount,
|
|
461
|
-
error: "Insufficient payment amount"
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
return {
|
|
465
|
-
valid: true,
|
|
466
|
-
confirmed: true,
|
|
467
|
-
signature,
|
|
468
|
-
from: transfer.from,
|
|
469
|
-
to: transfer.to,
|
|
470
|
-
mint: transfer.mint,
|
|
471
|
-
amount: transfer.amount,
|
|
472
|
-
blockTime: transaction.blockTime ?? void 0,
|
|
473
|
-
slot: transaction.slot
|
|
474
|
-
};
|
|
475
|
-
} catch {
|
|
476
|
-
return { valid: false, confirmed: false, signature, error: "Verification failed" };
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
function isNativeAsset(asset) {
|
|
480
|
-
return asset === "native";
|
|
481
|
-
}
|
|
482
|
-
var DEFAULT_COMPUTE_UNITS = 2e5;
|
|
483
|
-
var DEFAULT_MICRO_LAMPORTS = 1e3;
|
|
484
|
-
function createPriorityFeeInstructions(config2 = {}) {
|
|
485
|
-
const { enabled = false, microLamports, computeUnits } = config2;
|
|
486
|
-
if (!enabled) {
|
|
487
|
-
return [];
|
|
488
|
-
}
|
|
489
|
-
const instructions = [];
|
|
490
|
-
const units = computeUnits ?? DEFAULT_COMPUTE_UNITS;
|
|
491
|
-
instructions.push(web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units }));
|
|
492
|
-
const price = microLamports ?? DEFAULT_MICRO_LAMPORTS;
|
|
493
|
-
instructions.push(web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: price }));
|
|
494
|
-
return instructions;
|
|
495
|
-
}
|
|
496
|
-
async function estimatePriorityFee(connection, accounts = []) {
|
|
497
|
-
try {
|
|
498
|
-
const fees = await connection.getRecentPrioritizationFees({
|
|
499
|
-
lockedWritableAccounts: accounts
|
|
500
|
-
});
|
|
501
|
-
if (fees.length === 0) {
|
|
502
|
-
return DEFAULT_MICRO_LAMPORTS;
|
|
503
|
-
}
|
|
504
|
-
const sortedFees = fees.map((f) => f.prioritizationFee).filter((f) => f > 0).sort((a, b) => a - b);
|
|
505
|
-
if (sortedFees.length === 0) {
|
|
506
|
-
return DEFAULT_MICRO_LAMPORTS;
|
|
507
|
-
}
|
|
508
|
-
const medianIndex = Math.floor(sortedFees.length / 2);
|
|
509
|
-
return sortedFees[medianIndex];
|
|
510
|
-
} catch {
|
|
511
|
-
return DEFAULT_MICRO_LAMPORTS;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
function calculatePriorityFeeCost(microLamportsPerCU, computeUnits) {
|
|
515
|
-
return Math.ceil(microLamportsPerCU * computeUnits / 1e6);
|
|
516
|
-
}
|
|
517
|
-
async function buildVersionedTransaction(config2) {
|
|
518
|
-
const {
|
|
519
|
-
connection,
|
|
520
|
-
payer,
|
|
521
|
-
instructions,
|
|
522
|
-
lookupTables = [],
|
|
523
|
-
priorityFee,
|
|
524
|
-
recentBlockhash
|
|
525
|
-
} = config2;
|
|
526
|
-
const priorityIxs = createPriorityFeeInstructions(priorityFee);
|
|
527
|
-
const allInstructions = [...priorityIxs, ...instructions];
|
|
528
|
-
let blockhash;
|
|
529
|
-
let lastValidBlockHeight;
|
|
530
|
-
if (recentBlockhash) {
|
|
531
|
-
blockhash = recentBlockhash;
|
|
532
|
-
const slot = await connection.getSlot();
|
|
533
|
-
lastValidBlockHeight = slot + 150;
|
|
534
|
-
} else {
|
|
535
|
-
const latestBlockhash = await connection.getLatestBlockhash("confirmed");
|
|
536
|
-
blockhash = latestBlockhash.blockhash;
|
|
537
|
-
lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;
|
|
538
|
-
}
|
|
539
|
-
const message = new web3_js.TransactionMessage({
|
|
540
|
-
payerKey: payer,
|
|
541
|
-
recentBlockhash: blockhash,
|
|
542
|
-
instructions: allInstructions
|
|
543
|
-
}).compileToV0Message(lookupTables);
|
|
544
|
-
const transaction = new web3_js.VersionedTransaction(message);
|
|
545
|
-
return {
|
|
546
|
-
transaction,
|
|
547
|
-
blockhash,
|
|
548
|
-
lastValidBlockHeight
|
|
549
|
-
};
|
|
550
|
-
}
|
|
551
|
-
async function fetchLookupTables(connection, addresses) {
|
|
552
|
-
const tables = [];
|
|
553
|
-
for (const address of addresses) {
|
|
554
|
-
const result = await connection.getAddressLookupTable(address);
|
|
555
|
-
if (result.value) {
|
|
556
|
-
tables.push(result.value);
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
return tables;
|
|
560
|
-
}
|
|
561
|
-
function isVersionedTransaction(tx) {
|
|
562
|
-
return tx instanceof web3_js.VersionedTransaction;
|
|
563
|
-
}
|
|
564
|
-
var MAX_ARTICLES_PER_SESSION = 100;
|
|
565
|
-
var MIN_SECRET_LENGTH = 32;
|
|
566
|
-
function getSecretKey(secret) {
|
|
567
|
-
if (!secret || typeof secret !== "string") {
|
|
568
|
-
throw new Error("Session secret is required");
|
|
569
|
-
}
|
|
570
|
-
if (secret.length < MIN_SECRET_LENGTH) {
|
|
571
|
-
throw new Error(`Session secret must be at least ${MIN_SECRET_LENGTH} characters`);
|
|
572
|
-
}
|
|
573
|
-
return new TextEncoder().encode(secret);
|
|
574
|
-
}
|
|
575
|
-
function validateWalletAddress(address) {
|
|
576
|
-
if (!address || typeof address !== "string") return false;
|
|
577
|
-
const base58Regex = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
578
|
-
return base58Regex.test(address);
|
|
579
|
-
}
|
|
580
|
-
function validateArticleId(articleId) {
|
|
581
|
-
if (!articleId || typeof articleId !== "string") return false;
|
|
582
|
-
if (articleId.length > 128) return false;
|
|
583
|
-
const safeIdRegex = /^[a-zA-Z0-9_-]+$/;
|
|
584
|
-
return safeIdRegex.test(articleId);
|
|
585
|
-
}
|
|
586
|
-
async function createSession(walletAddress, articleId, config2, siteWide = false) {
|
|
587
|
-
if (!validateWalletAddress(walletAddress)) {
|
|
588
|
-
throw new Error("Invalid wallet address format");
|
|
589
|
-
}
|
|
590
|
-
if (!validateArticleId(articleId)) {
|
|
591
|
-
throw new Error("Invalid article ID format");
|
|
592
|
-
}
|
|
593
|
-
if (!config2.durationHours || config2.durationHours <= 0 || config2.durationHours > 720) {
|
|
594
|
-
throw new Error("Session duration must be between 1 and 720 hours");
|
|
595
|
-
}
|
|
596
|
-
const sessionId = uuid.v4();
|
|
597
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
598
|
-
const expiresAt = now + config2.durationHours * 3600;
|
|
599
|
-
const session = {
|
|
600
|
-
id: sessionId,
|
|
601
|
-
walletAddress,
|
|
602
|
-
unlockedArticles: [articleId],
|
|
603
|
-
siteWideUnlock: Boolean(siteWide),
|
|
604
|
-
createdAt: now,
|
|
605
|
-
expiresAt
|
|
606
|
-
};
|
|
607
|
-
const payload = {
|
|
608
|
-
sub: walletAddress,
|
|
609
|
-
sid: sessionId,
|
|
610
|
-
articles: session.unlockedArticles,
|
|
611
|
-
siteWide: session.siteWideUnlock,
|
|
612
|
-
iat: now,
|
|
613
|
-
exp: expiresAt
|
|
614
|
-
};
|
|
615
|
-
const token = await new jose.SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(`${config2.durationHours}h`).sign(getSecretKey(config2.secret));
|
|
616
|
-
return { token, session };
|
|
617
|
-
}
|
|
618
|
-
async function validateSession(token, secret) {
|
|
619
|
-
if (!token || typeof token !== "string") {
|
|
620
|
-
return { valid: false, reason: "Invalid token format" };
|
|
621
|
-
}
|
|
622
|
-
try {
|
|
623
|
-
const { payload } = await jose.jwtVerify(token, getSecretKey(secret));
|
|
624
|
-
const sessionPayload = payload;
|
|
625
|
-
if (!sessionPayload.sub || !sessionPayload.sid || !sessionPayload.exp) {
|
|
626
|
-
return { valid: false, reason: "Malformed session payload" };
|
|
627
|
-
}
|
|
628
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
629
|
-
if (sessionPayload.exp < now) {
|
|
630
|
-
return { valid: false, reason: "Session expired" };
|
|
631
|
-
}
|
|
632
|
-
if (!validateWalletAddress(sessionPayload.sub)) {
|
|
633
|
-
return { valid: false, reason: "Invalid session data" };
|
|
634
|
-
}
|
|
635
|
-
const session = {
|
|
636
|
-
id: sessionPayload.sid,
|
|
637
|
-
walletAddress: sessionPayload.sub,
|
|
638
|
-
unlockedArticles: Array.isArray(sessionPayload.articles) ? sessionPayload.articles : [],
|
|
639
|
-
siteWideUnlock: Boolean(sessionPayload.siteWide),
|
|
640
|
-
createdAt: sessionPayload.iat ?? 0,
|
|
641
|
-
expiresAt: sessionPayload.exp
|
|
642
|
-
};
|
|
643
|
-
return { valid: true, session };
|
|
644
|
-
} catch (error) {
|
|
645
|
-
return { valid: false, reason: "Invalid session" };
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
async function addArticleToSession(token, articleId, secret) {
|
|
649
|
-
if (!validateArticleId(articleId)) {
|
|
650
|
-
return null;
|
|
651
|
-
}
|
|
652
|
-
const validation = await validateSession(token, secret);
|
|
653
|
-
if (!validation.valid || !validation.session) {
|
|
654
|
-
return null;
|
|
655
|
-
}
|
|
656
|
-
const session = validation.session;
|
|
657
|
-
if (session.unlockedArticles.includes(articleId)) {
|
|
658
|
-
return { token, session };
|
|
659
|
-
}
|
|
660
|
-
if (session.unlockedArticles.length >= MAX_ARTICLES_PER_SESSION) {
|
|
661
|
-
return null;
|
|
662
|
-
}
|
|
663
|
-
const updatedArticles = [...session.unlockedArticles, articleId];
|
|
664
|
-
const payload = {
|
|
665
|
-
sub: session.walletAddress,
|
|
666
|
-
sid: session.id,
|
|
667
|
-
articles: updatedArticles,
|
|
668
|
-
siteWide: session.siteWideUnlock,
|
|
669
|
-
iat: session.createdAt,
|
|
670
|
-
exp: session.expiresAt
|
|
671
|
-
};
|
|
672
|
-
const newToken = await new jose.SignJWT(payload).setProtectedHeader({ alg: "HS256" }).sign(getSecretKey(secret));
|
|
673
|
-
return {
|
|
674
|
-
token: newToken,
|
|
675
|
-
session: { ...session, unlockedArticles: updatedArticles }
|
|
676
|
-
};
|
|
677
|
-
}
|
|
678
|
-
async function isArticleUnlocked(token, articleId, secret) {
|
|
679
|
-
if (!validateArticleId(articleId)) {
|
|
680
|
-
return false;
|
|
681
|
-
}
|
|
682
|
-
const validation = await validateSession(token, secret);
|
|
683
|
-
if (!validation.valid || !validation.session) {
|
|
684
|
-
return false;
|
|
685
|
-
}
|
|
686
|
-
if (validation.session.siteWideUnlock) {
|
|
687
|
-
return true;
|
|
688
|
-
}
|
|
689
|
-
return validation.session.unlockedArticles.includes(articleId);
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
// src/x402/config.ts
|
|
693
|
-
var WALLET_REGEX3 = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
694
|
-
function sanitizeDisplayString(str, maxLength = 200) {
|
|
695
|
-
if (!str || typeof str !== "string") return "";
|
|
696
|
-
return str.slice(0, maxLength).replace(/[<>"'&]/g, "");
|
|
697
|
-
}
|
|
698
|
-
function isValidUrl(url) {
|
|
699
|
-
try {
|
|
700
|
-
const parsed = new URL(url);
|
|
701
|
-
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
702
|
-
} catch {
|
|
703
|
-
return false;
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
function buildPaymentRequirement(params) {
|
|
707
|
-
if (!WALLET_REGEX3.test(params.creatorWallet)) {
|
|
708
|
-
throw new Error("Invalid creator wallet address");
|
|
709
|
-
}
|
|
710
|
-
if (params.priceInLamports <= 0n) {
|
|
711
|
-
throw new Error("Price must be positive");
|
|
712
|
-
}
|
|
713
|
-
if (!isValidUrl(params.resourceUrl)) {
|
|
714
|
-
throw new Error("Invalid resource URL");
|
|
715
|
-
}
|
|
716
|
-
if (params.network !== "devnet" && params.network !== "mainnet-beta") {
|
|
717
|
-
throw new Error("Invalid network");
|
|
718
|
-
}
|
|
719
|
-
const timeout = params.maxTimeoutSeconds ?? 300;
|
|
720
|
-
if (timeout < 60 || timeout > 3600) {
|
|
721
|
-
throw new Error("Timeout must be between 60 and 3600 seconds");
|
|
722
|
-
}
|
|
723
|
-
const x402Network = toX402Network(params.network);
|
|
724
|
-
const safeTitle = sanitizeDisplayString(params.articleTitle, 200);
|
|
725
|
-
const safeArticleId = sanitizeDisplayString(params.articleId, 128);
|
|
726
|
-
return {
|
|
727
|
-
scheme: "exact",
|
|
728
|
-
network: x402Network,
|
|
729
|
-
maxAmountRequired: params.priceInLamports.toString(),
|
|
730
|
-
resource: params.resourceUrl,
|
|
731
|
-
description: `Unlock: ${safeTitle}`,
|
|
732
|
-
mimeType: "text/html",
|
|
733
|
-
payTo: params.creatorWallet,
|
|
734
|
-
maxTimeoutSeconds: timeout,
|
|
735
|
-
asset: "native",
|
|
736
|
-
extra: {
|
|
737
|
-
name: safeTitle,
|
|
738
|
-
articleId: safeArticleId
|
|
739
|
-
}
|
|
740
|
-
};
|
|
741
|
-
}
|
|
742
|
-
function encodePaymentRequired(requirement) {
|
|
743
|
-
return Buffer.from(JSON.stringify(requirement)).toString("base64");
|
|
744
|
-
}
|
|
745
|
-
function decodePaymentRequired(encoded) {
|
|
746
|
-
if (!encoded || typeof encoded !== "string") {
|
|
747
|
-
throw new Error("Invalid encoded requirement");
|
|
748
|
-
}
|
|
749
|
-
if (encoded.length > 1e4) {
|
|
750
|
-
throw new Error("Encoded requirement too large");
|
|
751
|
-
}
|
|
752
|
-
try {
|
|
753
|
-
const decoded = Buffer.from(encoded, "base64").toString("utf-8");
|
|
754
|
-
return JSON.parse(decoded);
|
|
755
|
-
} catch {
|
|
756
|
-
throw new Error("Failed to decode payment requirement");
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
var X402_HEADERS = {
|
|
760
|
-
PAYMENT_REQUIRED: "X-Payment-Required",
|
|
761
|
-
PAYMENT: "X-Payment",
|
|
762
|
-
PAYMENT_RESPONSE: "X-Payment-Response"
|
|
763
|
-
};
|
|
764
|
-
function create402ResponseBody(requirement) {
|
|
765
|
-
const assetStr = typeof requirement.asset === "string" ? requirement.asset : requirement.asset.mint;
|
|
766
|
-
return {
|
|
767
|
-
error: "Payment Required",
|
|
768
|
-
message: requirement.description,
|
|
769
|
-
price: {
|
|
770
|
-
amount: requirement.maxAmountRequired,
|
|
771
|
-
asset: assetStr,
|
|
772
|
-
network: requirement.network
|
|
773
|
-
}
|
|
774
|
-
};
|
|
775
|
-
}
|
|
776
|
-
function create402Headers(requirement) {
|
|
777
|
-
const encoded = encodePaymentRequired(requirement);
|
|
778
|
-
return {
|
|
779
|
-
"Content-Type": "application/json",
|
|
780
|
-
[X402_HEADERS.PAYMENT_REQUIRED]: encoded,
|
|
781
|
-
"Access-Control-Expose-Headers": X402_HEADERS.PAYMENT_REQUIRED
|
|
782
|
-
};
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
// src/x402/verification.ts
|
|
786
|
-
var SIGNATURE_REGEX3 = /^[1-9A-HJ-NP-Za-km-z]{87,88}$/;
|
|
787
|
-
async function verifyX402Payment(payload, requirement, clientConfig) {
|
|
788
|
-
if (!payload || typeof payload !== "object") {
|
|
789
|
-
return { valid: false, invalidReason: "Invalid payload" };
|
|
790
|
-
}
|
|
791
|
-
const signature = payload.payload?.signature;
|
|
792
|
-
if (!signature || typeof signature !== "string") {
|
|
793
|
-
return { valid: false, invalidReason: "Missing transaction signature" };
|
|
794
|
-
}
|
|
795
|
-
if (!SIGNATURE_REGEX3.test(signature)) {
|
|
796
|
-
return { valid: false, invalidReason: "Invalid signature format" };
|
|
797
|
-
}
|
|
798
|
-
if (payload.x402Version !== 1) {
|
|
799
|
-
return { valid: false, invalidReason: "Unsupported x402 version" };
|
|
800
|
-
}
|
|
801
|
-
if (payload.scheme !== "exact") {
|
|
802
|
-
return { valid: false, invalidReason: "Unsupported payment scheme" };
|
|
803
|
-
}
|
|
804
|
-
if (payload.network !== requirement.network) {
|
|
805
|
-
return {
|
|
806
|
-
valid: false,
|
|
807
|
-
invalidReason: `Network mismatch: expected ${requirement.network}`
|
|
808
|
-
};
|
|
809
|
-
}
|
|
810
|
-
const walletRegex = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
811
|
-
if (!walletRegex.test(requirement.payTo)) {
|
|
812
|
-
return { valid: false, invalidReason: "Invalid recipient configuration" };
|
|
813
|
-
}
|
|
814
|
-
let expectedAmount;
|
|
815
|
-
try {
|
|
816
|
-
expectedAmount = BigInt(requirement.maxAmountRequired);
|
|
817
|
-
if (expectedAmount <= 0n) {
|
|
818
|
-
return { valid: false, invalidReason: "Invalid payment amount" };
|
|
819
|
-
}
|
|
820
|
-
} catch {
|
|
821
|
-
return { valid: false, invalidReason: "Invalid payment amount format" };
|
|
822
|
-
}
|
|
823
|
-
const verification = await verifyPayment({
|
|
824
|
-
signature,
|
|
825
|
-
expectedRecipient: requirement.payTo,
|
|
826
|
-
expectedAmount,
|
|
827
|
-
maxAgeSeconds: requirement.maxTimeoutSeconds,
|
|
828
|
-
clientConfig
|
|
829
|
-
});
|
|
830
|
-
if (!verification.valid) {
|
|
831
|
-
return {
|
|
832
|
-
valid: false,
|
|
833
|
-
invalidReason: verification.error || "Transaction verification failed"
|
|
834
|
-
};
|
|
835
|
-
}
|
|
836
|
-
return {
|
|
837
|
-
valid: true,
|
|
838
|
-
settled: verification.confirmed,
|
|
839
|
-
from: verification.from,
|
|
840
|
-
transaction: {
|
|
841
|
-
signature: verification.signature,
|
|
842
|
-
blockTime: verification.blockTime,
|
|
843
|
-
slot: verification.slot
|
|
844
|
-
}
|
|
845
|
-
};
|
|
846
|
-
}
|
|
847
|
-
function parsePaymentHeader(header) {
|
|
848
|
-
if (!header || typeof header !== "string") {
|
|
849
|
-
return null;
|
|
850
|
-
}
|
|
851
|
-
if (header.length > 1e4) {
|
|
852
|
-
return null;
|
|
853
|
-
}
|
|
854
|
-
try {
|
|
855
|
-
const decoded = Buffer.from(header, "base64").toString("utf-8");
|
|
856
|
-
const parsed = JSON.parse(decoded);
|
|
857
|
-
if (!parsed || typeof parsed !== "object") {
|
|
858
|
-
return null;
|
|
859
|
-
}
|
|
860
|
-
return parsed;
|
|
861
|
-
} catch {
|
|
862
|
-
return null;
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
function encodePaymentRequirement(requirement) {
|
|
866
|
-
return Buffer.from(JSON.stringify(requirement)).toString("base64");
|
|
867
|
-
}
|
|
868
|
-
function encodePaymentResponse(response) {
|
|
869
|
-
return Buffer.from(JSON.stringify(response)).toString("base64");
|
|
870
|
-
}
|
|
871
|
-
function create402Response(requirement, body) {
|
|
872
|
-
const headers = new Headers({
|
|
873
|
-
"Content-Type": "application/json",
|
|
874
|
-
"X-Payment-Required": encodePaymentRequirement(requirement)
|
|
875
|
-
});
|
|
876
|
-
const responseBody = body || {
|
|
877
|
-
error: "Payment Required",
|
|
878
|
-
message: "This resource requires payment to access",
|
|
879
|
-
x402Version: 1,
|
|
880
|
-
accepts: [requirement]
|
|
881
|
-
};
|
|
882
|
-
return new Response(JSON.stringify(responseBody), {
|
|
883
|
-
status: 402,
|
|
884
|
-
headers
|
|
885
|
-
});
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
// src/store/memory.ts
|
|
889
|
-
function createMemoryStore(options = {}) {
|
|
890
|
-
const { cleanupInterval = 6e4 } = options;
|
|
891
|
-
const store = /* @__PURE__ */ new Map();
|
|
892
|
-
const cleanupTimer = setInterval(() => {
|
|
893
|
-
const now = Date.now();
|
|
894
|
-
for (const [key, record] of store.entries()) {
|
|
895
|
-
if (record.expiresAt < now) {
|
|
896
|
-
store.delete(key);
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
}, cleanupInterval);
|
|
900
|
-
return {
|
|
901
|
-
async hasBeenUsed(signature) {
|
|
902
|
-
const record = store.get(signature);
|
|
903
|
-
if (!record) return false;
|
|
904
|
-
if (record.expiresAt < Date.now()) {
|
|
905
|
-
store.delete(signature);
|
|
906
|
-
return false;
|
|
907
|
-
}
|
|
908
|
-
return true;
|
|
909
|
-
},
|
|
910
|
-
async markAsUsed(signature, resourceId, expiresAt) {
|
|
911
|
-
store.set(signature, {
|
|
912
|
-
resourceId,
|
|
913
|
-
usedAt: Date.now(),
|
|
914
|
-
expiresAt: expiresAt.getTime()
|
|
915
|
-
});
|
|
916
|
-
},
|
|
917
|
-
async getUsage(signature) {
|
|
918
|
-
const record = store.get(signature);
|
|
919
|
-
if (!record) return null;
|
|
920
|
-
if (record.expiresAt < Date.now()) {
|
|
921
|
-
store.delete(signature);
|
|
922
|
-
return null;
|
|
923
|
-
}
|
|
924
|
-
return {
|
|
925
|
-
signature,
|
|
926
|
-
resourceId: record.resourceId,
|
|
927
|
-
usedAt: new Date(record.usedAt),
|
|
928
|
-
expiresAt: new Date(record.expiresAt),
|
|
929
|
-
walletAddress: record.walletAddress
|
|
930
|
-
};
|
|
931
|
-
},
|
|
932
|
-
/** Stop cleanup timer (for graceful shutdown) */
|
|
933
|
-
close() {
|
|
934
|
-
clearInterval(cleanupTimer);
|
|
935
|
-
store.clear();
|
|
936
|
-
}
|
|
937
|
-
};
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
// src/store/redis.ts
|
|
941
|
-
function createRedisStore(options) {
|
|
942
|
-
const { client, keyPrefix = "micropay:sig:" } = options;
|
|
943
|
-
const buildKey = (signature) => `${keyPrefix}${signature}`;
|
|
944
|
-
return {
|
|
945
|
-
async hasBeenUsed(signature) {
|
|
946
|
-
const exists = await client.exists(buildKey(signature));
|
|
947
|
-
return exists > 0;
|
|
948
|
-
},
|
|
949
|
-
async markAsUsed(signature, resourceId, expiresAt) {
|
|
950
|
-
const key = buildKey(signature);
|
|
951
|
-
const ttl = Math.max(1, Math.floor((expiresAt.getTime() - Date.now()) / 1e3));
|
|
952
|
-
const record = {
|
|
953
|
-
signature,
|
|
954
|
-
resourceId,
|
|
955
|
-
usedAt: /* @__PURE__ */ new Date(),
|
|
956
|
-
expiresAt
|
|
957
|
-
};
|
|
958
|
-
if (client.setex) {
|
|
959
|
-
await client.setex(key, ttl, JSON.stringify(record));
|
|
960
|
-
} else {
|
|
961
|
-
await client.set(key, JSON.stringify(record), { EX: ttl });
|
|
962
|
-
}
|
|
963
|
-
},
|
|
964
|
-
async getUsage(signature) {
|
|
965
|
-
const data = await client.get(buildKey(signature));
|
|
966
|
-
if (!data) return null;
|
|
967
|
-
try {
|
|
968
|
-
const record = JSON.parse(data);
|
|
969
|
-
return {
|
|
970
|
-
...record,
|
|
971
|
-
usedAt: new Date(record.usedAt),
|
|
972
|
-
expiresAt: new Date(record.expiresAt)
|
|
973
|
-
};
|
|
974
|
-
} catch {
|
|
975
|
-
return null;
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
};
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
// src/middleware/nextjs.ts
|
|
982
|
-
function matchesProtectedPath(path, patterns) {
|
|
983
|
-
for (const pattern of patterns) {
|
|
984
|
-
const regexPattern = pattern.replace(/\*\*/g, "{{DOUBLE_STAR}}").replace(/\*/g, "[^/]*").replace(/{{DOUBLE_STAR}}/g, ".*");
|
|
985
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
986
|
-
if (regex.test(path)) {
|
|
987
|
-
return true;
|
|
988
|
-
}
|
|
989
|
-
}
|
|
990
|
-
return false;
|
|
991
|
-
}
|
|
992
|
-
async function checkPaywallAccess(path, sessionToken, config2) {
|
|
993
|
-
if (!matchesProtectedPath(path, config2.protectedPaths)) {
|
|
994
|
-
return { allowed: true };
|
|
995
|
-
}
|
|
996
|
-
if (!sessionToken) {
|
|
997
|
-
return {
|
|
998
|
-
allowed: false,
|
|
999
|
-
reason: "No session token",
|
|
1000
|
-
requiresPayment: true
|
|
1001
|
-
};
|
|
1002
|
-
}
|
|
1003
|
-
const validation = await validateSession(sessionToken, config2.sessionSecret);
|
|
1004
|
-
if (!validation.valid || !validation.session) {
|
|
1005
|
-
return {
|
|
1006
|
-
allowed: false,
|
|
1007
|
-
reason: validation.reason || "Invalid session",
|
|
1008
|
-
requiresPayment: true
|
|
1009
|
-
};
|
|
1010
|
-
}
|
|
1011
|
-
return {
|
|
1012
|
-
allowed: true,
|
|
1013
|
-
session: validation.session
|
|
1014
|
-
};
|
|
1015
|
-
}
|
|
1016
|
-
function createPaywallMiddleware(config2) {
|
|
1017
|
-
const { cookieName = "x402_session" } = config2;
|
|
1018
|
-
return async function middleware(request) {
|
|
1019
|
-
const url = new URL(request.url);
|
|
1020
|
-
const path = url.pathname;
|
|
1021
|
-
const cookieHeader = request.headers.get("cookie") || "";
|
|
1022
|
-
const cookies = Object.fromEntries(
|
|
1023
|
-
cookieHeader.split(";").map((c) => {
|
|
1024
|
-
const [key, ...vals] = c.trim().split("=");
|
|
1025
|
-
return [key, vals.join("=")];
|
|
1026
|
-
})
|
|
1027
|
-
);
|
|
1028
|
-
const sessionToken = cookies[cookieName];
|
|
1029
|
-
const result = await checkPaywallAccess(path, sessionToken, config2);
|
|
1030
|
-
if (!result.allowed && result.requiresPayment) {
|
|
1031
|
-
const headers = {
|
|
1032
|
-
"Content-Type": "application/json"
|
|
1033
|
-
};
|
|
1034
|
-
if (config2.paymentRequirement) {
|
|
1035
|
-
const requirement = typeof config2.paymentRequirement === "function" ? config2.paymentRequirement(path) : config2.paymentRequirement;
|
|
1036
|
-
headers["X-Payment-Required"] = encodePaymentRequirement(requirement);
|
|
1037
|
-
}
|
|
1038
|
-
const body = config2.custom402Response ? config2.custom402Response(path) : {
|
|
1039
|
-
error: "Payment Required",
|
|
1040
|
-
message: "This resource requires payment to access",
|
|
1041
|
-
x402Version: 1,
|
|
1042
|
-
path
|
|
1043
|
-
};
|
|
1044
|
-
return new Response(JSON.stringify(body), {
|
|
1045
|
-
status: 402,
|
|
1046
|
-
headers
|
|
1047
|
-
});
|
|
1048
|
-
}
|
|
1049
|
-
return null;
|
|
1050
|
-
};
|
|
1051
|
-
}
|
|
1052
|
-
function withPaywall(handler, options) {
|
|
1053
|
-
const { sessionSecret, cookieName = "x402_session", articleId } = options;
|
|
1054
|
-
return async function protectedHandler(request) {
|
|
1055
|
-
const cookieHeader = request.headers.get("cookie") || "";
|
|
1056
|
-
const cookies = Object.fromEntries(
|
|
1057
|
-
cookieHeader.split(";").map((c) => {
|
|
1058
|
-
const [key, ...vals] = c.trim().split("=");
|
|
1059
|
-
return [key, vals.join("=")];
|
|
1060
|
-
})
|
|
1061
|
-
);
|
|
1062
|
-
const sessionToken = cookies[cookieName];
|
|
1063
|
-
if (!sessionToken) {
|
|
1064
|
-
return new Response(
|
|
1065
|
-
JSON.stringify({ error: "Payment Required", message: "No session token" }),
|
|
1066
|
-
{ status: 402, headers: { "Content-Type": "application/json" } }
|
|
1067
|
-
);
|
|
1068
|
-
}
|
|
1069
|
-
const validation = await validateSession(sessionToken, sessionSecret);
|
|
1070
|
-
if (!validation.valid || !validation.session) {
|
|
1071
|
-
return new Response(
|
|
1072
|
-
JSON.stringify({ error: "Payment Required", message: validation.reason }),
|
|
1073
|
-
{ status: 402, headers: { "Content-Type": "application/json" } }
|
|
1074
|
-
);
|
|
1075
|
-
}
|
|
1076
|
-
if (articleId) {
|
|
1077
|
-
const { session } = validation;
|
|
1078
|
-
const hasAccess = session.siteWideUnlock || session.unlockedArticles.includes(articleId);
|
|
1079
|
-
if (!hasAccess) {
|
|
1080
|
-
return new Response(
|
|
1081
|
-
JSON.stringify({ error: "Payment Required", message: "Article not unlocked" }),
|
|
1082
|
-
{ status: 402, headers: { "Content-Type": "application/json" } }
|
|
1083
|
-
);
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
return handler(request, validation.session);
|
|
1087
|
-
};
|
|
1088
|
-
}
|
|
11
|
+
// src/index.ts
|
|
1089
12
|
|
|
1090
|
-
// src/
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
const jitterAmount = delay * 0.25;
|
|
1100
|
-
delay += Math.random() * jitterAmount * 2 - jitterAmount;
|
|
1101
|
-
}
|
|
1102
|
-
return Math.floor(delay);
|
|
1103
|
-
}
|
|
1104
|
-
async function withRetry(fn, options = {}) {
|
|
1105
|
-
const {
|
|
1106
|
-
maxAttempts = 3,
|
|
1107
|
-
baseDelay = 500,
|
|
1108
|
-
maxDelay = 1e4,
|
|
1109
|
-
jitter = true,
|
|
1110
|
-
retryOn = () => true
|
|
1111
|
-
} = options;
|
|
1112
|
-
let lastError;
|
|
1113
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
1114
|
-
try {
|
|
1115
|
-
return await fn();
|
|
1116
|
-
} catch (error) {
|
|
1117
|
-
lastError = error;
|
|
1118
|
-
if (!retryOn(error)) {
|
|
1119
|
-
throw error;
|
|
1120
|
-
}
|
|
1121
|
-
if (attempt < maxAttempts - 1) {
|
|
1122
|
-
const delay = calculateDelay(attempt, {
|
|
1123
|
-
baseDelay,
|
|
1124
|
-
maxDelay,
|
|
1125
|
-
jitter
|
|
1126
|
-
});
|
|
1127
|
-
await sleep(delay);
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
throw lastError;
|
|
1132
|
-
}
|
|
1133
|
-
function isRetryableRPCError(error) {
|
|
1134
|
-
if (error instanceof Error) {
|
|
1135
|
-
const message = error.message.toLowerCase();
|
|
1136
|
-
if (message.includes("429") || message.includes("rate limit")) {
|
|
1137
|
-
return true;
|
|
1138
|
-
}
|
|
1139
|
-
if (message.includes("timeout") || message.includes("econnreset")) {
|
|
1140
|
-
return true;
|
|
1141
|
-
}
|
|
1142
|
-
if (message.includes("503") || message.includes("502") || message.includes("500")) {
|
|
1143
|
-
return true;
|
|
1144
|
-
}
|
|
1145
|
-
if (message.includes("blockhash not found") || message.includes("slot skipped")) {
|
|
1146
|
-
return true;
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
return false;
|
|
1150
|
-
}
|
|
13
|
+
// src/client/types.ts
|
|
14
|
+
var TOKEN_MINTS = {
|
|
15
|
+
/** USDC on mainnet */
|
|
16
|
+
USDC_MAINNET: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
|
|
17
|
+
/** USDC on devnet */
|
|
18
|
+
USDC_DEVNET: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
|
|
19
|
+
/** USDT on mainnet */
|
|
20
|
+
USDT_MAINNET: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
|
|
21
|
+
};
|
|
1151
22
|
|
|
1152
23
|
// src/client/payment.ts
|
|
1153
24
|
function buildSolanaPayUrl(params) {
|
|
@@ -1198,172 +69,91 @@ function createPaymentFlow(config2) {
|
|
|
1198
69
|
/** Generate Solana Pay URL for QR codes */
|
|
1199
70
|
getSolanaPayUrl: (options = {}) => {
|
|
1200
71
|
return buildSolanaPayUrl({
|
|
1201
|
-
recipient: recipientWallet,
|
|
1202
|
-
amount: naturalAmount,
|
|
1203
|
-
splToken: mintAddress,
|
|
1204
|
-
label: options.label,
|
|
1205
|
-
reference: options.reference,
|
|
1206
|
-
message: memo
|
|
1207
|
-
});
|
|
1208
|
-
},
|
|
1209
|
-
/** Get the token mint address (undefined for native SOL) */
|
|
1210
|
-
getMintAddress: () => mintAddress,
|
|
1211
|
-
/** Check if this is a native SOL payment */
|
|
1212
|
-
isNativePayment: () => asset === "native",
|
|
1213
|
-
/** Get network information */
|
|
1214
|
-
getNetworkInfo: () => ({
|
|
1215
|
-
network,
|
|
1216
|
-
isMainnet: network === "mainnet-beta",
|
|
1217
|
-
explorerUrl: network === "mainnet-beta" ? "https://explorer.solana.com" : "https://explorer.solana.com?cluster=devnet"
|
|
1218
|
-
}),
|
|
1219
|
-
/** Build explorer URL for a transaction */
|
|
1220
|
-
getExplorerUrl: (signature) => {
|
|
1221
|
-
const baseUrl = "https://explorer.solana.com/tx";
|
|
1222
|
-
const cluster = network === "mainnet-beta" ? "" : "?cluster=devnet";
|
|
1223
|
-
return `${baseUrl}/${signature}${cluster}`;
|
|
1224
|
-
}
|
|
1225
|
-
};
|
|
1226
|
-
}
|
|
1227
|
-
function createPaymentReference() {
|
|
1228
|
-
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
1229
|
-
return crypto.randomUUID();
|
|
1230
|
-
}
|
|
1231
|
-
return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
// src/pricing/index.ts
|
|
1235
|
-
var cachedPrice = null;
|
|
1236
|
-
var config = {};
|
|
1237
|
-
var lastProviderIndex = -1;
|
|
1238
|
-
function configurePricing(newConfig) {
|
|
1239
|
-
config = { ...config, ...newConfig };
|
|
1240
|
-
cachedPrice = null;
|
|
1241
|
-
}
|
|
1242
|
-
var PROVIDERS = [
|
|
1243
|
-
{
|
|
1244
|
-
name: "coincap",
|
|
1245
|
-
url: "https://api.coincap.io/v2/assets/solana",
|
|
1246
|
-
parse: (data) => parseFloat(data.data?.priceUsd || "0")
|
|
1247
|
-
},
|
|
1248
|
-
{
|
|
1249
|
-
name: "binance",
|
|
1250
|
-
url: "https://api.binance.com/api/v3/ticker/price?symbol=SOLUSDT",
|
|
1251
|
-
parse: (data) => parseFloat(data.price || "0")
|
|
1252
|
-
},
|
|
1253
|
-
{
|
|
1254
|
-
name: "coingecko",
|
|
1255
|
-
url: "https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd",
|
|
1256
|
-
parse: (data) => data.solana?.usd || 0
|
|
1257
|
-
},
|
|
1258
|
-
{
|
|
1259
|
-
name: "kraken",
|
|
1260
|
-
url: "https://api.kraken.com/0/public/Ticker?pair=SOLUSD",
|
|
1261
|
-
parse: (data) => parseFloat(data.result?.SOLUSD?.c?.[0] || "0")
|
|
1262
|
-
}
|
|
1263
|
-
];
|
|
1264
|
-
async function fetchFromProvider(provider, timeout) {
|
|
1265
|
-
const controller = new AbortController();
|
|
1266
|
-
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
1267
|
-
try {
|
|
1268
|
-
const response = await fetch(provider.url, {
|
|
1269
|
-
headers: { "Accept": "application/json" },
|
|
1270
|
-
signal: controller.signal
|
|
1271
|
-
});
|
|
1272
|
-
if (!response.ok) {
|
|
1273
|
-
throw new Error(`HTTP ${response.status}`);
|
|
1274
|
-
}
|
|
1275
|
-
const data = await response.json();
|
|
1276
|
-
const price = provider.parse(data);
|
|
1277
|
-
if (!price || price <= 0) {
|
|
1278
|
-
throw new Error("Invalid price");
|
|
1279
|
-
}
|
|
1280
|
-
return price;
|
|
1281
|
-
} finally {
|
|
1282
|
-
clearTimeout(timeoutId);
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
async function getSolPrice() {
|
|
1286
|
-
const cacheTTL = config.cacheTTL ?? 6e4;
|
|
1287
|
-
const timeout = config.timeout ?? 5e3;
|
|
1288
|
-
if (cachedPrice && Date.now() - cachedPrice.fetchedAt.getTime() < cacheTTL) {
|
|
1289
|
-
return cachedPrice;
|
|
1290
|
-
}
|
|
1291
|
-
if (config.customProvider) {
|
|
1292
|
-
try {
|
|
1293
|
-
const price = await config.customProvider();
|
|
1294
|
-
if (price > 0) {
|
|
1295
|
-
cachedPrice = {
|
|
1296
|
-
solPrice: price,
|
|
1297
|
-
fetchedAt: /* @__PURE__ */ new Date(),
|
|
1298
|
-
source: "custom"
|
|
1299
|
-
};
|
|
1300
|
-
return cachedPrice;
|
|
1301
|
-
}
|
|
1302
|
-
} catch {
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
for (let i = 0; i < PROVIDERS.length; i++) {
|
|
1306
|
-
const idx = (lastProviderIndex + 1 + i) % PROVIDERS.length;
|
|
1307
|
-
const provider = PROVIDERS[idx];
|
|
1308
|
-
try {
|
|
1309
|
-
const price = await fetchFromProvider(provider, timeout);
|
|
1310
|
-
lastProviderIndex = idx;
|
|
1311
|
-
cachedPrice = {
|
|
1312
|
-
solPrice: price,
|
|
1313
|
-
fetchedAt: /* @__PURE__ */ new Date(),
|
|
1314
|
-
source: provider.name
|
|
1315
|
-
};
|
|
1316
|
-
return cachedPrice;
|
|
1317
|
-
} catch {
|
|
1318
|
-
continue;
|
|
72
|
+
recipient: recipientWallet,
|
|
73
|
+
amount: naturalAmount,
|
|
74
|
+
splToken: mintAddress,
|
|
75
|
+
label: options.label,
|
|
76
|
+
reference: options.reference,
|
|
77
|
+
message: memo
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
/** Get the token mint address (undefined for native SOL) */
|
|
81
|
+
getMintAddress: () => mintAddress,
|
|
82
|
+
/** Check if this is a native SOL payment */
|
|
83
|
+
isNativePayment: () => asset === "native",
|
|
84
|
+
/** Get network information */
|
|
85
|
+
getNetworkInfo: () => ({
|
|
86
|
+
network,
|
|
87
|
+
isMainnet: network === "mainnet-beta",
|
|
88
|
+
explorerUrl: network === "mainnet-beta" ? "https://explorer.solana.com" : "https://explorer.solana.com?cluster=devnet"
|
|
89
|
+
}),
|
|
90
|
+
/** Build explorer URL for a transaction */
|
|
91
|
+
getExplorerUrl: (signature) => {
|
|
92
|
+
const baseUrl = "https://explorer.solana.com/tx";
|
|
93
|
+
const cluster = network === "mainnet-beta" ? "" : "?cluster=devnet";
|
|
94
|
+
return `${baseUrl}/${signature}${cluster}`;
|
|
1319
95
|
}
|
|
1320
|
-
}
|
|
1321
|
-
if (cachedPrice) {
|
|
1322
|
-
return {
|
|
1323
|
-
...cachedPrice,
|
|
1324
|
-
source: `${cachedPrice.source} (stale)`
|
|
1325
|
-
};
|
|
1326
|
-
}
|
|
1327
|
-
throw new Error(
|
|
1328
|
-
"Failed to fetch SOL price from all providers. Configure a custom provider or ensure network connectivity."
|
|
1329
|
-
);
|
|
1330
|
-
}
|
|
1331
|
-
async function lamportsToUsd(lamports) {
|
|
1332
|
-
const { solPrice } = await getSolPrice();
|
|
1333
|
-
const sol = Number(lamports) / 1e9;
|
|
1334
|
-
return sol * solPrice;
|
|
96
|
+
};
|
|
1335
97
|
}
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
98
|
+
function createPaymentReference() {
|
|
99
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
100
|
+
return crypto.randomUUID();
|
|
101
|
+
}
|
|
102
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
1340
103
|
}
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
const
|
|
1345
|
-
|
|
104
|
+
var DEFAULT_COMPUTE_UNITS = 2e5;
|
|
105
|
+
var DEFAULT_MICRO_LAMPORTS = 1e3;
|
|
106
|
+
function createPriorityFeeInstructions(config2 = {}) {
|
|
107
|
+
const { enabled = false, microLamports, computeUnits } = config2;
|
|
108
|
+
if (!enabled) {
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
const instructions = [];
|
|
112
|
+
const units = computeUnits ?? DEFAULT_COMPUTE_UNITS;
|
|
113
|
+
instructions.push(web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units }));
|
|
114
|
+
const price = microLamports ?? DEFAULT_MICRO_LAMPORTS;
|
|
115
|
+
instructions.push(web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: price }));
|
|
116
|
+
return instructions;
|
|
1346
117
|
}
|
|
1347
|
-
function
|
|
1348
|
-
const
|
|
1349
|
-
|
|
118
|
+
async function buildVersionedTransaction(config2) {
|
|
119
|
+
const {
|
|
120
|
+
connection,
|
|
121
|
+
payer,
|
|
122
|
+
instructions,
|
|
123
|
+
priorityFee,
|
|
124
|
+
recentBlockhash
|
|
125
|
+
} = config2;
|
|
126
|
+
const priorityIxs = createPriorityFeeInstructions(priorityFee);
|
|
127
|
+
const allInstructions = [...priorityIxs, ...instructions];
|
|
128
|
+
let blockhash;
|
|
129
|
+
let lastValidBlockHeight;
|
|
130
|
+
if (recentBlockhash) {
|
|
131
|
+
blockhash = recentBlockhash;
|
|
132
|
+
const slot = await connection.getSlot();
|
|
133
|
+
lastValidBlockHeight = slot + 150;
|
|
134
|
+
} else {
|
|
135
|
+
const latestBlockhash = await connection.getLatestBlockhash("confirmed");
|
|
136
|
+
blockhash = latestBlockhash.blockhash;
|
|
137
|
+
lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;
|
|
138
|
+
}
|
|
139
|
+
const message = new web3_js.TransactionMessage({
|
|
140
|
+
payerKey: payer,
|
|
141
|
+
recentBlockhash: blockhash,
|
|
142
|
+
instructions: allInstructions
|
|
143
|
+
}).compileToV0Message([]);
|
|
144
|
+
const transaction = new web3_js.VersionedTransaction(message);
|
|
1350
145
|
return {
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
146
|
+
transaction,
|
|
147
|
+
blockhash,
|
|
148
|
+
lastValidBlockHeight
|
|
1354
149
|
};
|
|
1355
150
|
}
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
function getProviders() {
|
|
1361
|
-
return PROVIDERS.map((p) => ({ name: p.name, url: p.url }));
|
|
1362
|
-
}
|
|
1363
|
-
var WALLET_REGEX4 = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
1364
|
-
function isValidWalletAddress2(address) {
|
|
151
|
+
|
|
152
|
+
// src/agent/agentPayment.ts
|
|
153
|
+
var WALLET_REGEX = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
154
|
+
function isValidWalletAddress(address) {
|
|
1365
155
|
if (!address || typeof address !== "string") return false;
|
|
1366
|
-
return
|
|
156
|
+
return WALLET_REGEX.test(address);
|
|
1367
157
|
}
|
|
1368
158
|
async function executeAgentPayment(params) {
|
|
1369
159
|
const {
|
|
@@ -1374,7 +164,7 @@ async function executeAgentPayment(params) {
|
|
|
1374
164
|
priorityFee,
|
|
1375
165
|
confirmationTimeout = 6e4
|
|
1376
166
|
} = params;
|
|
1377
|
-
if (!
|
|
167
|
+
if (!isValidWalletAddress(recipientAddress)) {
|
|
1378
168
|
return {
|
|
1379
169
|
success: false,
|
|
1380
170
|
error: "Invalid recipient address format"
|
|
@@ -1481,23 +271,23 @@ function generateAgentKeypair() {
|
|
|
1481
271
|
};
|
|
1482
272
|
}
|
|
1483
273
|
var MAX_CREDITS = 1e3;
|
|
1484
|
-
var
|
|
1485
|
-
function
|
|
274
|
+
var MIN_SECRET_LENGTH = 32;
|
|
275
|
+
function getSecretKey(secret) {
|
|
1486
276
|
if (!secret || typeof secret !== "string") {
|
|
1487
277
|
throw new Error("Session secret is required");
|
|
1488
278
|
}
|
|
1489
|
-
if (secret.length <
|
|
1490
|
-
throw new Error(`Session secret must be at least ${
|
|
279
|
+
if (secret.length < MIN_SECRET_LENGTH) {
|
|
280
|
+
throw new Error(`Session secret must be at least ${MIN_SECRET_LENGTH} characters`);
|
|
1491
281
|
}
|
|
1492
282
|
return new TextEncoder().encode(secret);
|
|
1493
283
|
}
|
|
1494
|
-
function
|
|
284
|
+
function validateWalletAddress(address) {
|
|
1495
285
|
if (!address || typeof address !== "string") return false;
|
|
1496
286
|
const base58Regex = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
1497
287
|
return base58Regex.test(address);
|
|
1498
288
|
}
|
|
1499
289
|
async function createCreditSession(walletAddress, purchaseId, config2) {
|
|
1500
|
-
if (!
|
|
290
|
+
if (!validateWalletAddress(walletAddress)) {
|
|
1501
291
|
throw new Error("Invalid wallet address format");
|
|
1502
292
|
}
|
|
1503
293
|
if (config2.initialCredits <= 0 || config2.initialCredits > MAX_CREDITS) {
|
|
@@ -1532,7 +322,7 @@ async function createCreditSession(walletAddress, purchaseId, config2) {
|
|
|
1532
322
|
iat: now,
|
|
1533
323
|
exp: expiresAt
|
|
1534
324
|
};
|
|
1535
|
-
const token = await new jose.SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(`${config2.durationHours}h`).sign(
|
|
325
|
+
const token = await new jose.SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(`${config2.durationHours}h`).sign(getSecretKey(config2.secret));
|
|
1536
326
|
return { token, session };
|
|
1537
327
|
}
|
|
1538
328
|
async function validateCreditSession(token, secret) {
|
|
@@ -1540,7 +330,7 @@ async function validateCreditSession(token, secret) {
|
|
|
1540
330
|
return { valid: false, reason: "Invalid token format" };
|
|
1541
331
|
}
|
|
1542
332
|
try {
|
|
1543
|
-
const { payload } = await jose.jwtVerify(token,
|
|
333
|
+
const { payload } = await jose.jwtVerify(token, getSecretKey(secret));
|
|
1544
334
|
const creditPayload = payload;
|
|
1545
335
|
if (!creditPayload.sub || !creditPayload.sid || !creditPayload.exp) {
|
|
1546
336
|
return { valid: false, reason: "Malformed session payload" };
|
|
@@ -1552,7 +342,7 @@ async function validateCreditSession(token, secret) {
|
|
|
1552
342
|
if (creditPayload.bundleExpiry && creditPayload.bundleExpiry < now) {
|
|
1553
343
|
return { valid: false, reason: "Bundle expired" };
|
|
1554
344
|
}
|
|
1555
|
-
if (!
|
|
345
|
+
if (!validateWalletAddress(creditPayload.sub)) {
|
|
1556
346
|
return { valid: false, reason: "Invalid session data" };
|
|
1557
347
|
}
|
|
1558
348
|
const session = {
|
|
@@ -1603,7 +393,7 @@ async function useCredit(token, secret, creditsToUse = 1) {
|
|
|
1603
393
|
iat: session.createdAt,
|
|
1604
394
|
exp: session.expiresAt
|
|
1605
395
|
};
|
|
1606
|
-
const newToken = await new jose.SignJWT(payload).setProtectedHeader({ alg: "HS256" }).sign(
|
|
396
|
+
const newToken = await new jose.SignJWT(payload).setProtectedHeader({ alg: "HS256" }).sign(getSecretKey(secret));
|
|
1607
397
|
return {
|
|
1608
398
|
success: true,
|
|
1609
399
|
remainingCredits: newCredits,
|
|
@@ -1631,7 +421,7 @@ async function addCredits(token, secret, creditsToAdd) {
|
|
|
1631
421
|
iat: session.createdAt,
|
|
1632
422
|
exp: session.expiresAt
|
|
1633
423
|
};
|
|
1634
|
-
const newToken = await new jose.SignJWT(payload).setProtectedHeader({ alg: "HS256" }).sign(
|
|
424
|
+
const newToken = await new jose.SignJWT(payload).setProtectedHeader({ alg: "HS256" }).sign(getSecretKey(secret));
|
|
1635
425
|
return {
|
|
1636
426
|
success: true,
|
|
1637
427
|
newToken,
|
|
@@ -1650,70 +440,180 @@ async function getRemainingCredits(token, secret) {
|
|
|
1650
440
|
};
|
|
1651
441
|
}
|
|
1652
442
|
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
443
|
+
// src/pricing/index.ts
|
|
444
|
+
var cachedPrice = null;
|
|
445
|
+
var config = {};
|
|
446
|
+
var lastProviderIndex = -1;
|
|
447
|
+
function configurePricing(newConfig) {
|
|
448
|
+
config = { ...config, ...newConfig };
|
|
449
|
+
cachedPrice = null;
|
|
450
|
+
}
|
|
451
|
+
var PROVIDERS = [
|
|
452
|
+
{
|
|
453
|
+
name: "coincap",
|
|
454
|
+
url: "https://api.coincap.io/v2/assets/solana",
|
|
455
|
+
parse: (data) => parseFloat(data.data?.priceUsd || "0")
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
name: "binance",
|
|
459
|
+
url: "https://api.binance.com/api/v3/ticker/price?symbol=SOLUSDT",
|
|
460
|
+
parse: (data) => parseFloat(data.price || "0")
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
name: "coingecko",
|
|
464
|
+
url: "https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd",
|
|
465
|
+
parse: (data) => data.solana?.usd || 0
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
name: "kraken",
|
|
469
|
+
url: "https://api.kraken.com/0/public/Ticker?pair=SOLUSD",
|
|
470
|
+
parse: (data) => parseFloat(data.result?.SOLUSD?.c?.[0] || "0")
|
|
471
|
+
}
|
|
472
|
+
];
|
|
473
|
+
async function fetchFromProvider(provider, timeout) {
|
|
474
|
+
const controller = new AbortController();
|
|
475
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
476
|
+
try {
|
|
477
|
+
const response = await fetch(provider.url, {
|
|
478
|
+
headers: { "Accept": "application/json" },
|
|
479
|
+
signal: controller.signal
|
|
480
|
+
});
|
|
481
|
+
if (!response.ok) {
|
|
482
|
+
throw new Error(`HTTP ${response.status}`);
|
|
483
|
+
}
|
|
484
|
+
const data = await response.json();
|
|
485
|
+
const price = provider.parse(data);
|
|
486
|
+
if (!price || price <= 0) {
|
|
487
|
+
throw new Error("Invalid price");
|
|
488
|
+
}
|
|
489
|
+
return price;
|
|
490
|
+
} finally {
|
|
491
|
+
clearTimeout(timeoutId);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
async function getSolPrice() {
|
|
495
|
+
const cacheTTL = config.cacheTTL ?? 6e4;
|
|
496
|
+
const timeout = config.timeout ?? 5e3;
|
|
497
|
+
if (cachedPrice && Date.now() - cachedPrice.fetchedAt.getTime() < cacheTTL) {
|
|
498
|
+
return cachedPrice;
|
|
499
|
+
}
|
|
500
|
+
if (config.customProvider) {
|
|
501
|
+
try {
|
|
502
|
+
const price = await config.customProvider();
|
|
503
|
+
if (price > 0) {
|
|
504
|
+
cachedPrice = {
|
|
505
|
+
solPrice: price,
|
|
506
|
+
fetchedAt: /* @__PURE__ */ new Date(),
|
|
507
|
+
source: "custom"
|
|
508
|
+
};
|
|
509
|
+
return cachedPrice;
|
|
510
|
+
}
|
|
511
|
+
} catch {
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
for (let i = 0; i < PROVIDERS.length; i++) {
|
|
515
|
+
const idx = (lastProviderIndex + 1 + i) % PROVIDERS.length;
|
|
516
|
+
const provider = PROVIDERS[idx];
|
|
517
|
+
try {
|
|
518
|
+
const price = await fetchFromProvider(provider, timeout);
|
|
519
|
+
lastProviderIndex = idx;
|
|
520
|
+
cachedPrice = {
|
|
521
|
+
solPrice: price,
|
|
522
|
+
fetchedAt: /* @__PURE__ */ new Date(),
|
|
523
|
+
source: provider.name
|
|
524
|
+
};
|
|
525
|
+
return cachedPrice;
|
|
526
|
+
} catch {
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
if (cachedPrice) {
|
|
531
|
+
return {
|
|
532
|
+
...cachedPrice,
|
|
533
|
+
source: `${cachedPrice.source} (stale)`
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
throw new Error(
|
|
537
|
+
"Failed to fetch SOL price from all providers. Configure a custom provider or ensure network connectivity."
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
async function lamportsToUsd(lamports) {
|
|
541
|
+
const { solPrice } = await getSolPrice();
|
|
542
|
+
const sol = Number(lamports) / 1e9;
|
|
543
|
+
return sol * solPrice;
|
|
544
|
+
}
|
|
545
|
+
async function usdToLamports(usd) {
|
|
546
|
+
const { solPrice } = await getSolPrice();
|
|
547
|
+
const sol = usd / solPrice;
|
|
548
|
+
return BigInt(Math.floor(sol * 1e9));
|
|
549
|
+
}
|
|
550
|
+
async function formatPriceDisplay(lamports) {
|
|
551
|
+
const { solPrice } = await getSolPrice();
|
|
552
|
+
const sol = Number(lamports) / 1e9;
|
|
553
|
+
const usd = sol * solPrice;
|
|
554
|
+
return `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`;
|
|
555
|
+
}
|
|
556
|
+
function formatPriceSync(lamports, solPrice) {
|
|
557
|
+
const sol = Number(lamports) / 1e9;
|
|
558
|
+
const usd = sol * solPrice;
|
|
559
|
+
return {
|
|
560
|
+
sol,
|
|
561
|
+
usd,
|
|
562
|
+
formatted: `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
function clearPriceCache() {
|
|
566
|
+
cachedPrice = null;
|
|
567
|
+
lastProviderIndex = -1;
|
|
568
|
+
}
|
|
569
|
+
function getProviders() {
|
|
570
|
+
return PROVIDERS.map((p) => ({ name: p.name, url: p.url }));
|
|
571
|
+
}
|
|
572
|
+
|
|
1656
573
|
exports.addCredits = addCredits;
|
|
1657
|
-
exports.buildPaymentRequirement = buildPaymentRequirement;
|
|
1658
574
|
exports.buildSolanaPayUrl = buildSolanaPayUrl;
|
|
1659
|
-
exports.buildVersionedTransaction = buildVersionedTransaction;
|
|
1660
|
-
exports.calculatePriorityFeeCost = calculatePriorityFeeCost;
|
|
1661
|
-
exports.checkPaywallAccess = checkPaywallAccess;
|
|
1662
575
|
exports.clearPriceCache = clearPriceCache;
|
|
1663
576
|
exports.configurePricing = configurePricing;
|
|
1664
|
-
exports.create402Headers = create402Headers;
|
|
1665
|
-
exports.create402Response = create402Response;
|
|
1666
|
-
exports.create402ResponseBody = create402ResponseBody;
|
|
1667
577
|
exports.createCreditSession = createCreditSession;
|
|
1668
|
-
exports.createMemoryStore = createMemoryStore;
|
|
1669
578
|
exports.createPaymentFlow = createPaymentFlow;
|
|
1670
579
|
exports.createPaymentReference = createPaymentReference;
|
|
1671
|
-
exports.createPaywallMiddleware = createPaywallMiddleware;
|
|
1672
|
-
exports.createPriorityFeeInstructions = createPriorityFeeInstructions;
|
|
1673
|
-
exports.createRedisStore = createRedisStore;
|
|
1674
|
-
exports.createSession = createSession;
|
|
1675
|
-
exports.decodePaymentRequired = decodePaymentRequired;
|
|
1676
|
-
exports.encodePaymentRequired = encodePaymentRequired;
|
|
1677
|
-
exports.encodePaymentRequirement = encodePaymentRequirement;
|
|
1678
|
-
exports.encodePaymentResponse = encodePaymentResponse;
|
|
1679
|
-
exports.estimatePriorityFee = estimatePriorityFee;
|
|
1680
580
|
exports.executeAgentPayment = executeAgentPayment;
|
|
1681
|
-
exports.fetchLookupTables = fetchLookupTables;
|
|
1682
581
|
exports.formatPriceDisplay = formatPriceDisplay;
|
|
1683
582
|
exports.formatPriceSync = formatPriceSync;
|
|
1684
583
|
exports.generateAgentKeypair = generateAgentKeypair;
|
|
1685
584
|
exports.getAgentBalance = getAgentBalance;
|
|
1686
|
-
exports.getConnection = getConnection;
|
|
1687
|
-
exports.getConnectionWithFallback = getConnectionWithFallback;
|
|
1688
585
|
exports.getProviders = getProviders;
|
|
1689
586
|
exports.getRemainingCredits = getRemainingCredits;
|
|
1690
587
|
exports.getSolPrice = getSolPrice;
|
|
1691
|
-
exports.getTokenDecimals = getTokenDecimals;
|
|
1692
|
-
exports.getWalletTransactions = getWalletTransactions;
|
|
1693
588
|
exports.hasAgentSufficientBalance = hasAgentSufficientBalance;
|
|
1694
|
-
exports.isArticleUnlocked = isArticleUnlocked;
|
|
1695
|
-
exports.isMainnet = isMainnet;
|
|
1696
|
-
exports.isNativeAsset = isNativeAsset;
|
|
1697
|
-
exports.isRetryableRPCError = isRetryableRPCError;
|
|
1698
|
-
exports.isVersionedTransaction = isVersionedTransaction;
|
|
1699
589
|
exports.keypairFromBase58 = keypairFromBase58;
|
|
1700
|
-
exports.lamportsToSol = lamportsToSol;
|
|
1701
590
|
exports.lamportsToUsd = lamportsToUsd;
|
|
1702
|
-
exports.parsePaymentHeader = parsePaymentHeader;
|
|
1703
|
-
exports.resetConnection = resetConnection;
|
|
1704
|
-
exports.resolveMintAddress = resolveMintAddress;
|
|
1705
|
-
exports.solToLamports = solToLamports;
|
|
1706
|
-
exports.toX402Network = toX402Network;
|
|
1707
591
|
exports.usdToLamports = usdToLamports;
|
|
1708
592
|
exports.useCredit = useCredit;
|
|
1709
593
|
exports.validateCreditSession = validateCreditSession;
|
|
1710
|
-
|
|
1711
|
-
exports.
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
exports.
|
|
594
|
+
Object.keys(core).forEach(function (k) {
|
|
595
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
596
|
+
enumerable: true,
|
|
597
|
+
get: function () { return core[k]; }
|
|
598
|
+
});
|
|
599
|
+
});
|
|
600
|
+
Object.keys(types).forEach(function (k) {
|
|
601
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
602
|
+
enumerable: true,
|
|
603
|
+
get: function () { return types[k]; }
|
|
604
|
+
});
|
|
605
|
+
});
|
|
606
|
+
Object.keys(client).forEach(function (k) {
|
|
607
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
608
|
+
enumerable: true,
|
|
609
|
+
get: function () { return client[k]; }
|
|
610
|
+
});
|
|
611
|
+
});
|
|
612
|
+
Object.keys(svm).forEach(function (k) {
|
|
613
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
614
|
+
enumerable: true,
|
|
615
|
+
get: function () { return svm[k]; }
|
|
616
|
+
});
|
|
617
|
+
});
|
|
1718
618
|
//# sourceMappingURL=index.cjs.map
|
|
1719
619
|
//# sourceMappingURL=index.cjs.map
|