@alleyboss/micropay-solana-x402-paywall 2.3.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -116
- package/dist/agent/index.cjs +358 -0
- package/dist/agent/index.cjs.map +1 -0
- package/dist/agent/index.d.cts +221 -0
- package/dist/agent/index.d.ts +221 -0
- package/dist/agent/index.js +347 -0
- package/dist/agent/index.js.map +1 -0
- 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 +315 -1116
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -10
- package/dist/index.d.ts +6 -10
- package/dist/index.js +283 -1074
- 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 +29 -59
- 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/solana/index.cjs +0 -589
- package/dist/solana/index.cjs.map +0 -1
- package/dist/solana/index.d.cts +0 -240
- package/dist/solana/index.d.ts +0 -240
- 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
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/** x402 network identifiers for Solana */
|
|
2
|
-
type X402Network = 'solana-devnet' | 'solana-mainnet';
|
|
3
|
-
/** Solana network types */
|
|
4
|
-
type SolanaNetwork = 'devnet' | 'mainnet-beta';
|
|
5
|
-
/** SPL Token asset specification */
|
|
6
|
-
interface SPLTokenAsset {
|
|
7
|
-
/** Token mint address */
|
|
8
|
-
mint: string;
|
|
9
|
-
/** Token decimals (default: 6 for USDC/USDT) */
|
|
10
|
-
decimals?: number;
|
|
11
|
-
}
|
|
12
|
-
/** Asset types for payments */
|
|
13
|
-
type PaymentAsset = 'native' | 'usdc' | 'usdt' | SPLTokenAsset;
|
|
14
|
-
/** Known SPL token mint addresses */
|
|
15
|
-
declare const TOKEN_MINTS: {
|
|
16
|
-
/** USDC on mainnet */
|
|
17
|
-
readonly USDC_MAINNET: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
18
|
-
/** USDC on devnet */
|
|
19
|
-
readonly USDC_DEVNET: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU";
|
|
20
|
-
/** USDT on mainnet */
|
|
21
|
-
readonly USDT_MAINNET: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB";
|
|
22
|
-
};
|
|
23
|
-
/** Payment requirement for x402 protocol */
|
|
24
|
-
interface PaymentRequirement {
|
|
25
|
-
/** Payment scheme - currently only 'exact' is supported */
|
|
26
|
-
scheme: 'exact';
|
|
27
|
-
/** Network identifier for x402 */
|
|
28
|
-
network: X402Network;
|
|
29
|
-
/** Amount in smallest unit as string (lamports for SOL, base units for tokens) */
|
|
30
|
-
maxAmountRequired: string;
|
|
31
|
-
/** URL of the protected resource */
|
|
32
|
-
resource: string;
|
|
33
|
-
/** Human-readable description */
|
|
34
|
-
description: string;
|
|
35
|
-
/** MIME type of the resource */
|
|
36
|
-
mimeType?: string;
|
|
37
|
-
/** Recipient wallet address */
|
|
38
|
-
payTo: string;
|
|
39
|
-
/** Maximum time in seconds to complete payment */
|
|
40
|
-
maxTimeoutSeconds: number;
|
|
41
|
-
/** Asset type - 'native' for SOL, 'usdc', 'usdt', or custom mint */
|
|
42
|
-
asset: PaymentAsset;
|
|
43
|
-
/** Additional metadata */
|
|
44
|
-
extra?: {
|
|
45
|
-
name?: string;
|
|
46
|
-
articleId?: string;
|
|
47
|
-
[key: string]: unknown;
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
/** Payment payload sent by client after transaction */
|
|
51
|
-
interface PaymentPayload {
|
|
52
|
-
/** x402 protocol version */
|
|
53
|
-
x402Version: number;
|
|
54
|
-
/** Payment scheme */
|
|
55
|
-
scheme: 'exact';
|
|
56
|
-
/** Network identifier */
|
|
57
|
-
network: X402Network;
|
|
58
|
-
/** Transaction details */
|
|
59
|
-
payload: {
|
|
60
|
-
/** Transaction signature (base58) */
|
|
61
|
-
signature: string;
|
|
62
|
-
/** Base64 encoded transaction (optional) */
|
|
63
|
-
transaction?: string;
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
/** Request to verify a payment */
|
|
67
|
-
interface VerificationRequest {
|
|
68
|
-
paymentPayload: PaymentPayload;
|
|
69
|
-
paymentRequirements: PaymentRequirement;
|
|
70
|
-
}
|
|
71
|
-
/** Response from payment verification */
|
|
72
|
-
interface VerificationResponse {
|
|
73
|
-
/** Whether the payment is valid */
|
|
74
|
-
valid: boolean;
|
|
75
|
-
/** Reason for invalid payment */
|
|
76
|
-
invalidReason?: string;
|
|
77
|
-
/** Whether the transaction is settled on-chain */
|
|
78
|
-
settled?: boolean;
|
|
79
|
-
/** Sender wallet address (payer) */
|
|
80
|
-
from?: string;
|
|
81
|
-
/** Transaction details */
|
|
82
|
-
transaction?: {
|
|
83
|
-
signature: string;
|
|
84
|
-
blockTime?: number;
|
|
85
|
-
slot?: number;
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
/** Payment status for tracking */
|
|
89
|
-
interface PaymentStatus {
|
|
90
|
-
status: 'pending' | 'confirmed' | 'failed' | 'expired';
|
|
91
|
-
signature?: string;
|
|
92
|
-
confirmations?: number;
|
|
93
|
-
error?: string;
|
|
94
|
-
}
|
|
95
|
-
/** Configuration for article pricing */
|
|
96
|
-
interface ArticlePaymentConfig {
|
|
97
|
-
articleId: string;
|
|
98
|
-
priceInLamports: bigint;
|
|
99
|
-
title: string;
|
|
100
|
-
description?: string;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export { type ArticlePaymentConfig as A, type PaymentRequirement as P, type SolanaNetwork as S, TOKEN_MINTS as T, type VerificationRequest as V, type X402Network as X, type PaymentPayload as a, type VerificationResponse as b, type PaymentStatus as c, type PaymentAsset as d, type SPLTokenAsset as e };
|
package/dist/solana/index.cjs
DELETED
|
@@ -1,589 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var web3_js = require('@solana/web3.js');
|
|
4
|
-
|
|
5
|
-
// src/solana/client.ts
|
|
6
|
-
var cachedConnection = null;
|
|
7
|
-
var cachedNetwork = null;
|
|
8
|
-
var cachedFallbacks = [];
|
|
9
|
-
var cachedFallbackEnabled = false;
|
|
10
|
-
function buildRpcUrl(config) {
|
|
11
|
-
const { network, rpcUrl, tatumApiKey } = config;
|
|
12
|
-
if (rpcUrl) {
|
|
13
|
-
if (rpcUrl.includes("tatum.io") && tatumApiKey && !rpcUrl.includes(tatumApiKey)) {
|
|
14
|
-
return rpcUrl.endsWith("/") ? `${rpcUrl}${tatumApiKey}` : `${rpcUrl}/${tatumApiKey}`;
|
|
15
|
-
}
|
|
16
|
-
return rpcUrl;
|
|
17
|
-
}
|
|
18
|
-
if (tatumApiKey) {
|
|
19
|
-
const baseUrl = network === "mainnet-beta" ? "https://solana-mainnet.gateway.tatum.io" : "https://solana-devnet.gateway.tatum.io";
|
|
20
|
-
return `${baseUrl}/${tatumApiKey}`;
|
|
21
|
-
}
|
|
22
|
-
return web3_js.clusterApiUrl(network);
|
|
23
|
-
}
|
|
24
|
-
function createConnection(rpcUrl) {
|
|
25
|
-
return new web3_js.Connection(rpcUrl, {
|
|
26
|
-
commitment: "confirmed",
|
|
27
|
-
confirmTransactionInitialTimeout: 6e4
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
function getConnection(config) {
|
|
31
|
-
const { network } = config;
|
|
32
|
-
if (cachedConnection && cachedNetwork === network) {
|
|
33
|
-
return cachedConnection;
|
|
34
|
-
}
|
|
35
|
-
const rpcUrl = buildRpcUrl(config);
|
|
36
|
-
cachedConnection = createConnection(rpcUrl);
|
|
37
|
-
cachedNetwork = network;
|
|
38
|
-
cachedFallbackEnabled = config.enableFallback ?? false;
|
|
39
|
-
cachedFallbacks = [];
|
|
40
|
-
if (cachedFallbackEnabled && config.fallbackRpcUrls?.length) {
|
|
41
|
-
cachedFallbacks = config.fallbackRpcUrls.map(createConnection);
|
|
42
|
-
}
|
|
43
|
-
return cachedConnection;
|
|
44
|
-
}
|
|
45
|
-
function getConnectionWithFallback(config) {
|
|
46
|
-
const connection = getConnection(config);
|
|
47
|
-
return {
|
|
48
|
-
connection,
|
|
49
|
-
fallbacks: cachedFallbacks,
|
|
50
|
-
fallbackEnabled: cachedFallbackEnabled
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
async function withFallback(config, operation) {
|
|
54
|
-
const { connection, fallbacks, fallbackEnabled } = getConnectionWithFallback(config);
|
|
55
|
-
try {
|
|
56
|
-
return await operation(connection);
|
|
57
|
-
} catch (error) {
|
|
58
|
-
if (!fallbackEnabled || fallbacks.length === 0) {
|
|
59
|
-
throw error;
|
|
60
|
-
}
|
|
61
|
-
if (!isRetryableError(error)) {
|
|
62
|
-
throw error;
|
|
63
|
-
}
|
|
64
|
-
for (let i = 0; i < fallbacks.length; i++) {
|
|
65
|
-
try {
|
|
66
|
-
return await operation(fallbacks[i]);
|
|
67
|
-
} catch (fallbackError) {
|
|
68
|
-
if (i === fallbacks.length - 1) {
|
|
69
|
-
throw fallbackError;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
throw error;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
function isRetryableError(error) {
|
|
77
|
-
if (error instanceof Error) {
|
|
78
|
-
const message = error.message.toLowerCase();
|
|
79
|
-
return message.includes("429") || message.includes("503") || message.includes("502") || message.includes("timeout") || message.includes("econnrefused") || message.includes("enotfound") || message.includes("rate limit");
|
|
80
|
-
}
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
function resetConnection() {
|
|
84
|
-
cachedConnection = null;
|
|
85
|
-
cachedNetwork = null;
|
|
86
|
-
}
|
|
87
|
-
function isMainnet(network) {
|
|
88
|
-
return network === "mainnet-beta";
|
|
89
|
-
}
|
|
90
|
-
function toX402Network(network) {
|
|
91
|
-
return network === "mainnet-beta" ? "solana-mainnet" : "solana-devnet";
|
|
92
|
-
}
|
|
93
|
-
var SIGNATURE_REGEX = /^[1-9A-HJ-NP-Za-km-z]{87,88}$/;
|
|
94
|
-
var WALLET_REGEX = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
95
|
-
function isValidSignature(signature) {
|
|
96
|
-
if (!signature || typeof signature !== "string") return false;
|
|
97
|
-
return SIGNATURE_REGEX.test(signature);
|
|
98
|
-
}
|
|
99
|
-
function isValidWalletAddress(address) {
|
|
100
|
-
if (!address || typeof address !== "string") return false;
|
|
101
|
-
return WALLET_REGEX.test(address);
|
|
102
|
-
}
|
|
103
|
-
function parseSOLTransfer(transaction, expectedRecipient) {
|
|
104
|
-
const instructions = transaction.transaction.message.instructions;
|
|
105
|
-
for (const ix of instructions) {
|
|
106
|
-
if ("parsed" in ix && ix.program === "system") {
|
|
107
|
-
const parsed = ix.parsed;
|
|
108
|
-
if (parsed.type === "transfer" && parsed.info.destination === expectedRecipient) {
|
|
109
|
-
return {
|
|
110
|
-
from: parsed.info.source,
|
|
111
|
-
to: parsed.info.destination,
|
|
112
|
-
amount: BigInt(parsed.info.lamports)
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
if (transaction.meta?.innerInstructions) {
|
|
118
|
-
for (const inner of transaction.meta.innerInstructions) {
|
|
119
|
-
for (const ix of inner.instructions) {
|
|
120
|
-
if ("parsed" in ix && ix.program === "system") {
|
|
121
|
-
const parsed = ix.parsed;
|
|
122
|
-
if (parsed.type === "transfer" && parsed.info.destination === expectedRecipient) {
|
|
123
|
-
return {
|
|
124
|
-
from: parsed.info.source,
|
|
125
|
-
to: parsed.info.destination,
|
|
126
|
-
amount: BigInt(parsed.info.lamports)
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
return null;
|
|
134
|
-
}
|
|
135
|
-
async function verifyPayment(params) {
|
|
136
|
-
const {
|
|
137
|
-
signature,
|
|
138
|
-
expectedRecipient,
|
|
139
|
-
expectedAmount,
|
|
140
|
-
maxAgeSeconds = 300,
|
|
141
|
-
clientConfig,
|
|
142
|
-
signatureStore
|
|
143
|
-
} = params;
|
|
144
|
-
if (!isValidSignature(signature)) {
|
|
145
|
-
return { valid: false, confirmed: false, signature, error: "Invalid signature format" };
|
|
146
|
-
}
|
|
147
|
-
if (!isValidWalletAddress(expectedRecipient)) {
|
|
148
|
-
return { valid: false, confirmed: false, signature, error: "Invalid recipient address" };
|
|
149
|
-
}
|
|
150
|
-
if (expectedAmount <= 0n) {
|
|
151
|
-
return { valid: false, confirmed: false, signature, error: "Invalid expected amount" };
|
|
152
|
-
}
|
|
153
|
-
if (signatureStore) {
|
|
154
|
-
const isUsed = await signatureStore.hasBeenUsed(signature);
|
|
155
|
-
if (isUsed) {
|
|
156
|
-
return { valid: false, confirmed: true, signature, error: "Signature already used" };
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
const effectiveMaxAge = Math.min(Math.max(maxAgeSeconds, 60), 3600);
|
|
160
|
-
const connection = getConnection(clientConfig);
|
|
161
|
-
try {
|
|
162
|
-
const transaction = await connection.getParsedTransaction(signature, {
|
|
163
|
-
commitment: "confirmed",
|
|
164
|
-
maxSupportedTransactionVersion: 0
|
|
165
|
-
});
|
|
166
|
-
if (!transaction) {
|
|
167
|
-
return { valid: false, confirmed: false, signature, error: "Transaction not found" };
|
|
168
|
-
}
|
|
169
|
-
if (transaction.meta?.err) {
|
|
170
|
-
return {
|
|
171
|
-
valid: false,
|
|
172
|
-
confirmed: true,
|
|
173
|
-
signature,
|
|
174
|
-
error: "Transaction failed on-chain"
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
if (transaction.blockTime) {
|
|
178
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
179
|
-
if (now - transaction.blockTime > effectiveMaxAge) {
|
|
180
|
-
return { valid: false, confirmed: true, signature, error: "Transaction too old" };
|
|
181
|
-
}
|
|
182
|
-
if (transaction.blockTime > now + 60) {
|
|
183
|
-
return { valid: false, confirmed: true, signature, error: "Invalid transaction time" };
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
const transferDetails = parseSOLTransfer(transaction, expectedRecipient);
|
|
187
|
-
if (!transferDetails) {
|
|
188
|
-
return {
|
|
189
|
-
valid: false,
|
|
190
|
-
confirmed: true,
|
|
191
|
-
signature,
|
|
192
|
-
error: "No valid SOL transfer to recipient found"
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
if (transferDetails.amount < expectedAmount) {
|
|
196
|
-
return {
|
|
197
|
-
valid: false,
|
|
198
|
-
confirmed: true,
|
|
199
|
-
signature,
|
|
200
|
-
from: transferDetails.from,
|
|
201
|
-
to: transferDetails.to,
|
|
202
|
-
amount: transferDetails.amount,
|
|
203
|
-
error: "Insufficient payment amount"
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
return {
|
|
207
|
-
valid: true,
|
|
208
|
-
confirmed: true,
|
|
209
|
-
signature,
|
|
210
|
-
from: transferDetails.from,
|
|
211
|
-
to: transferDetails.to,
|
|
212
|
-
amount: transferDetails.amount,
|
|
213
|
-
blockTime: transaction.blockTime ?? void 0,
|
|
214
|
-
slot: transaction.slot
|
|
215
|
-
};
|
|
216
|
-
} catch (error) {
|
|
217
|
-
return {
|
|
218
|
-
valid: false,
|
|
219
|
-
confirmed: false,
|
|
220
|
-
signature,
|
|
221
|
-
error: "Verification failed"
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
async function waitForConfirmation(signature, clientConfig) {
|
|
226
|
-
if (!isValidSignature(signature)) {
|
|
227
|
-
return { confirmed: false, error: "Invalid signature format" };
|
|
228
|
-
}
|
|
229
|
-
const connection = getConnection(clientConfig);
|
|
230
|
-
try {
|
|
231
|
-
const confirmation = await connection.confirmTransaction(signature, "confirmed");
|
|
232
|
-
if (confirmation.value.err) {
|
|
233
|
-
return { confirmed: false, error: "Transaction failed" };
|
|
234
|
-
}
|
|
235
|
-
return { confirmed: true, slot: confirmation.context?.slot };
|
|
236
|
-
} catch {
|
|
237
|
-
return { confirmed: false, error: "Confirmation timeout" };
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
async function getWalletTransactions(walletAddress, clientConfig, limit = 20) {
|
|
241
|
-
if (!isValidWalletAddress(walletAddress)) {
|
|
242
|
-
return [];
|
|
243
|
-
}
|
|
244
|
-
const safeLimit = Math.min(Math.max(limit, 1), 100);
|
|
245
|
-
const connection = getConnection(clientConfig);
|
|
246
|
-
try {
|
|
247
|
-
const pubkey = new web3_js.PublicKey(walletAddress);
|
|
248
|
-
const signatures = await connection.getSignaturesForAddress(pubkey, { limit: safeLimit });
|
|
249
|
-
return signatures.map((sig) => ({
|
|
250
|
-
signature: sig.signature,
|
|
251
|
-
blockTime: sig.blockTime ?? void 0,
|
|
252
|
-
slot: sig.slot
|
|
253
|
-
}));
|
|
254
|
-
} catch {
|
|
255
|
-
return [];
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
function lamportsToSol(lamports) {
|
|
259
|
-
return Number(lamports) / web3_js.LAMPORTS_PER_SOL;
|
|
260
|
-
}
|
|
261
|
-
function solToLamports(sol) {
|
|
262
|
-
if (!Number.isFinite(sol) || sol < 0) {
|
|
263
|
-
throw new Error("Invalid SOL amount");
|
|
264
|
-
}
|
|
265
|
-
return BigInt(Math.floor(sol * web3_js.LAMPORTS_PER_SOL));
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// src/types/payment.ts
|
|
269
|
-
var TOKEN_MINTS = {
|
|
270
|
-
/** USDC on mainnet */
|
|
271
|
-
USDC_MAINNET: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
|
|
272
|
-
/** USDC on devnet */
|
|
273
|
-
USDC_DEVNET: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
|
|
274
|
-
/** USDT on mainnet */
|
|
275
|
-
USDT_MAINNET: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
// src/solana/spl.ts
|
|
279
|
-
var SIGNATURE_REGEX2 = /^[1-9A-HJ-NP-Za-km-z]{87,88}$/;
|
|
280
|
-
var WALLET_REGEX2 = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
281
|
-
function resolveMintAddress(asset, network) {
|
|
282
|
-
if (asset === "native") return null;
|
|
283
|
-
if (asset === "usdc") {
|
|
284
|
-
return network === "mainnet-beta" ? TOKEN_MINTS.USDC_MAINNET : TOKEN_MINTS.USDC_DEVNET;
|
|
285
|
-
}
|
|
286
|
-
if (asset === "usdt") {
|
|
287
|
-
return TOKEN_MINTS.USDT_MAINNET;
|
|
288
|
-
}
|
|
289
|
-
if (typeof asset === "object" && "mint" in asset) {
|
|
290
|
-
return asset.mint;
|
|
291
|
-
}
|
|
292
|
-
return null;
|
|
293
|
-
}
|
|
294
|
-
function getTokenDecimals(asset) {
|
|
295
|
-
if (asset === "native") return 9;
|
|
296
|
-
if (asset === "usdc" || asset === "usdt") return 6;
|
|
297
|
-
if (typeof asset === "object" && "decimals" in asset) {
|
|
298
|
-
return asset.decimals ?? 6;
|
|
299
|
-
}
|
|
300
|
-
return 6;
|
|
301
|
-
}
|
|
302
|
-
function parseSPLTransfer(transaction, expectedRecipient, expectedMint) {
|
|
303
|
-
const instructions = transaction.transaction.message.instructions;
|
|
304
|
-
for (const ix of instructions) {
|
|
305
|
-
if ("parsed" in ix && (ix.program === "spl-token" || ix.program === "spl-token-2022")) {
|
|
306
|
-
const parsed = ix.parsed;
|
|
307
|
-
if (parsed.type === "transfer" || parsed.type === "transferChecked") {
|
|
308
|
-
const amount = parsed.info.amount || parsed.info.tokenAmount?.amount;
|
|
309
|
-
if (amount && parsed.info.destination) {
|
|
310
|
-
return {
|
|
311
|
-
from: parsed.info.authority || parsed.info.source || "",
|
|
312
|
-
to: parsed.info.destination,
|
|
313
|
-
amount: BigInt(amount),
|
|
314
|
-
mint: parsed.info.mint || expectedMint
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
if (transaction.meta?.innerInstructions) {
|
|
321
|
-
for (const inner of transaction.meta.innerInstructions) {
|
|
322
|
-
for (const ix of inner.instructions) {
|
|
323
|
-
if ("parsed" in ix && (ix.program === "spl-token" || ix.program === "spl-token-2022")) {
|
|
324
|
-
const parsed = ix.parsed;
|
|
325
|
-
if (parsed.type === "transfer" || parsed.type === "transferChecked") {
|
|
326
|
-
const amount = parsed.info.amount || parsed.info.tokenAmount?.amount;
|
|
327
|
-
if (amount) {
|
|
328
|
-
return {
|
|
329
|
-
from: parsed.info.authority || parsed.info.source || "",
|
|
330
|
-
to: parsed.info.destination || "",
|
|
331
|
-
amount: BigInt(amount),
|
|
332
|
-
mint: parsed.info.mint || expectedMint
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
if (transaction.meta?.postTokenBalances && transaction.meta?.preTokenBalances) {
|
|
341
|
-
const preBalances = transaction.meta.preTokenBalances;
|
|
342
|
-
const postBalances = transaction.meta.postTokenBalances;
|
|
343
|
-
for (const post of postBalances) {
|
|
344
|
-
if (post.mint === expectedMint && post.owner === expectedRecipient) {
|
|
345
|
-
const pre = preBalances.find(
|
|
346
|
-
(p) => p.accountIndex === post.accountIndex
|
|
347
|
-
);
|
|
348
|
-
const preAmount = BigInt(pre?.uiTokenAmount?.amount || "0");
|
|
349
|
-
const postAmount = BigInt(post.uiTokenAmount?.amount || "0");
|
|
350
|
-
const transferred = postAmount - preAmount;
|
|
351
|
-
if (transferred > 0n) {
|
|
352
|
-
return {
|
|
353
|
-
from: "",
|
|
354
|
-
// Can't determine from balance changes
|
|
355
|
-
to: expectedRecipient,
|
|
356
|
-
amount: transferred,
|
|
357
|
-
mint: expectedMint
|
|
358
|
-
};
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
return null;
|
|
364
|
-
}
|
|
365
|
-
async function verifySPLPayment(params) {
|
|
366
|
-
const {
|
|
367
|
-
signature,
|
|
368
|
-
expectedRecipient,
|
|
369
|
-
expectedAmount,
|
|
370
|
-
asset,
|
|
371
|
-
clientConfig,
|
|
372
|
-
maxAgeSeconds = 300,
|
|
373
|
-
signatureStore
|
|
374
|
-
} = params;
|
|
375
|
-
if (signatureStore) {
|
|
376
|
-
const isUsed = await signatureStore.hasBeenUsed(signature);
|
|
377
|
-
if (isUsed) {
|
|
378
|
-
return { valid: false, confirmed: true, signature, error: "Signature already used" };
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
if (!SIGNATURE_REGEX2.test(signature)) {
|
|
382
|
-
return { valid: false, confirmed: false, signature, error: "Invalid signature format" };
|
|
383
|
-
}
|
|
384
|
-
if (!WALLET_REGEX2.test(expectedRecipient)) {
|
|
385
|
-
return { valid: false, confirmed: false, signature, error: "Invalid recipient address" };
|
|
386
|
-
}
|
|
387
|
-
const mintAddress = resolveMintAddress(asset, clientConfig.network);
|
|
388
|
-
if (!mintAddress) {
|
|
389
|
-
return { valid: false, confirmed: false, signature, error: "Invalid asset configuration" };
|
|
390
|
-
}
|
|
391
|
-
if (expectedAmount <= 0n) {
|
|
392
|
-
return { valid: false, confirmed: false, signature, error: "Invalid expected amount" };
|
|
393
|
-
}
|
|
394
|
-
const effectiveMaxAge = Math.min(Math.max(maxAgeSeconds, 60), 3600);
|
|
395
|
-
const connection = getConnection(clientConfig);
|
|
396
|
-
try {
|
|
397
|
-
const transaction = await connection.getParsedTransaction(signature, {
|
|
398
|
-
commitment: "confirmed",
|
|
399
|
-
maxSupportedTransactionVersion: 0
|
|
400
|
-
});
|
|
401
|
-
if (!transaction) {
|
|
402
|
-
return { valid: false, confirmed: false, signature, error: "Transaction not found" };
|
|
403
|
-
}
|
|
404
|
-
if (transaction.meta?.err) {
|
|
405
|
-
return { valid: false, confirmed: true, signature, error: "Transaction failed on-chain" };
|
|
406
|
-
}
|
|
407
|
-
if (transaction.blockTime) {
|
|
408
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
409
|
-
if (now - transaction.blockTime > effectiveMaxAge) {
|
|
410
|
-
return { valid: false, confirmed: true, signature, error: "Transaction too old" };
|
|
411
|
-
}
|
|
412
|
-
if (transaction.blockTime > now + 60) {
|
|
413
|
-
return { valid: false, confirmed: true, signature, error: "Invalid transaction time" };
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
const transfer = parseSPLTransfer(transaction, expectedRecipient, mintAddress);
|
|
417
|
-
if (!transfer) {
|
|
418
|
-
return {
|
|
419
|
-
valid: false,
|
|
420
|
-
confirmed: true,
|
|
421
|
-
signature,
|
|
422
|
-
error: "No valid token transfer to recipient found"
|
|
423
|
-
};
|
|
424
|
-
}
|
|
425
|
-
if (transfer.to) {
|
|
426
|
-
try {
|
|
427
|
-
const destinationInfo = await connection.getParsedAccountInfo(new web3_js.PublicKey(transfer.to));
|
|
428
|
-
const owner = destinationInfo.value?.data?.parsed?.info?.owner;
|
|
429
|
-
if (owner && owner !== expectedRecipient) {
|
|
430
|
-
return {
|
|
431
|
-
valid: false,
|
|
432
|
-
confirmed: true,
|
|
433
|
-
signature,
|
|
434
|
-
error: "Recipient mismatch: Token account not owned by merchant"
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
} catch (e) {
|
|
438
|
-
return {
|
|
439
|
-
valid: false,
|
|
440
|
-
confirmed: true,
|
|
441
|
-
signature,
|
|
442
|
-
error: "Could not verify token account owner"
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
if (transfer.mint !== mintAddress) {
|
|
447
|
-
return {
|
|
448
|
-
valid: false,
|
|
449
|
-
confirmed: true,
|
|
450
|
-
signature,
|
|
451
|
-
error: "Token mint mismatch"
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
if (transfer.amount < expectedAmount) {
|
|
455
|
-
return {
|
|
456
|
-
valid: false,
|
|
457
|
-
confirmed: true,
|
|
458
|
-
signature,
|
|
459
|
-
from: transfer.from,
|
|
460
|
-
to: transfer.to,
|
|
461
|
-
mint: transfer.mint,
|
|
462
|
-
amount: transfer.amount,
|
|
463
|
-
error: "Insufficient payment amount"
|
|
464
|
-
};
|
|
465
|
-
}
|
|
466
|
-
return {
|
|
467
|
-
valid: true,
|
|
468
|
-
confirmed: true,
|
|
469
|
-
signature,
|
|
470
|
-
from: transfer.from,
|
|
471
|
-
to: transfer.to,
|
|
472
|
-
mint: transfer.mint,
|
|
473
|
-
amount: transfer.amount,
|
|
474
|
-
blockTime: transaction.blockTime ?? void 0,
|
|
475
|
-
slot: transaction.slot
|
|
476
|
-
};
|
|
477
|
-
} catch {
|
|
478
|
-
return { valid: false, confirmed: false, signature, error: "Verification failed" };
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
function isNativeAsset(asset) {
|
|
482
|
-
return asset === "native";
|
|
483
|
-
}
|
|
484
|
-
var DEFAULT_COMPUTE_UNITS = 2e5;
|
|
485
|
-
var DEFAULT_MICRO_LAMPORTS = 1e3;
|
|
486
|
-
function createPriorityFeeInstructions(config = {}) {
|
|
487
|
-
const { enabled = false, microLamports, computeUnits } = config;
|
|
488
|
-
if (!enabled) {
|
|
489
|
-
return [];
|
|
490
|
-
}
|
|
491
|
-
const instructions = [];
|
|
492
|
-
const units = computeUnits ?? DEFAULT_COMPUTE_UNITS;
|
|
493
|
-
instructions.push(web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units }));
|
|
494
|
-
const price = microLamports ?? DEFAULT_MICRO_LAMPORTS;
|
|
495
|
-
instructions.push(web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: price }));
|
|
496
|
-
return instructions;
|
|
497
|
-
}
|
|
498
|
-
async function estimatePriorityFee(connection, accounts = []) {
|
|
499
|
-
try {
|
|
500
|
-
const fees = await connection.getRecentPrioritizationFees({
|
|
501
|
-
lockedWritableAccounts: accounts
|
|
502
|
-
});
|
|
503
|
-
if (fees.length === 0) {
|
|
504
|
-
return DEFAULT_MICRO_LAMPORTS;
|
|
505
|
-
}
|
|
506
|
-
const sortedFees = fees.map((f) => f.prioritizationFee).filter((f) => f > 0).sort((a, b) => a - b);
|
|
507
|
-
if (sortedFees.length === 0) {
|
|
508
|
-
return DEFAULT_MICRO_LAMPORTS;
|
|
509
|
-
}
|
|
510
|
-
const medianIndex = Math.floor(sortedFees.length / 2);
|
|
511
|
-
return sortedFees[medianIndex];
|
|
512
|
-
} catch {
|
|
513
|
-
return DEFAULT_MICRO_LAMPORTS;
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
function calculatePriorityFeeCost(microLamportsPerCU, computeUnits) {
|
|
517
|
-
return Math.ceil(microLamportsPerCU * computeUnits / 1e6);
|
|
518
|
-
}
|
|
519
|
-
async function buildVersionedTransaction(config) {
|
|
520
|
-
const {
|
|
521
|
-
connection,
|
|
522
|
-
payer,
|
|
523
|
-
instructions,
|
|
524
|
-
lookupTables = [],
|
|
525
|
-
priorityFee,
|
|
526
|
-
recentBlockhash
|
|
527
|
-
} = config;
|
|
528
|
-
const priorityIxs = createPriorityFeeInstructions(priorityFee);
|
|
529
|
-
const allInstructions = [...priorityIxs, ...instructions];
|
|
530
|
-
let blockhash;
|
|
531
|
-
let lastValidBlockHeight;
|
|
532
|
-
if (recentBlockhash) {
|
|
533
|
-
blockhash = recentBlockhash;
|
|
534
|
-
const slot = await connection.getSlot();
|
|
535
|
-
lastValidBlockHeight = slot + 150;
|
|
536
|
-
} else {
|
|
537
|
-
const latestBlockhash = await connection.getLatestBlockhash("confirmed");
|
|
538
|
-
blockhash = latestBlockhash.blockhash;
|
|
539
|
-
lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;
|
|
540
|
-
}
|
|
541
|
-
const message = new web3_js.TransactionMessage({
|
|
542
|
-
payerKey: payer,
|
|
543
|
-
recentBlockhash: blockhash,
|
|
544
|
-
instructions: allInstructions
|
|
545
|
-
}).compileToV0Message(lookupTables);
|
|
546
|
-
const transaction = new web3_js.VersionedTransaction(message);
|
|
547
|
-
return {
|
|
548
|
-
transaction,
|
|
549
|
-
blockhash,
|
|
550
|
-
lastValidBlockHeight
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
async function fetchLookupTables(connection, addresses) {
|
|
554
|
-
const tables = [];
|
|
555
|
-
for (const address of addresses) {
|
|
556
|
-
const result = await connection.getAddressLookupTable(address);
|
|
557
|
-
if (result.value) {
|
|
558
|
-
tables.push(result.value);
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
return tables;
|
|
562
|
-
}
|
|
563
|
-
function isVersionedTransaction(tx) {
|
|
564
|
-
return tx instanceof web3_js.VersionedTransaction;
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
exports.buildVersionedTransaction = buildVersionedTransaction;
|
|
568
|
-
exports.calculatePriorityFeeCost = calculatePriorityFeeCost;
|
|
569
|
-
exports.createPriorityFeeInstructions = createPriorityFeeInstructions;
|
|
570
|
-
exports.estimatePriorityFee = estimatePriorityFee;
|
|
571
|
-
exports.fetchLookupTables = fetchLookupTables;
|
|
572
|
-
exports.getConnection = getConnection;
|
|
573
|
-
exports.getConnectionWithFallback = getConnectionWithFallback;
|
|
574
|
-
exports.getTokenDecimals = getTokenDecimals;
|
|
575
|
-
exports.getWalletTransactions = getWalletTransactions;
|
|
576
|
-
exports.isMainnet = isMainnet;
|
|
577
|
-
exports.isNativeAsset = isNativeAsset;
|
|
578
|
-
exports.isVersionedTransaction = isVersionedTransaction;
|
|
579
|
-
exports.lamportsToSol = lamportsToSol;
|
|
580
|
-
exports.resetConnection = resetConnection;
|
|
581
|
-
exports.resolveMintAddress = resolveMintAddress;
|
|
582
|
-
exports.solToLamports = solToLamports;
|
|
583
|
-
exports.toX402Network = toX402Network;
|
|
584
|
-
exports.verifyPayment = verifyPayment;
|
|
585
|
-
exports.verifySPLPayment = verifySPLPayment;
|
|
586
|
-
exports.waitForConfirmation = waitForConfirmation;
|
|
587
|
-
exports.withFallback = withFallback;
|
|
588
|
-
//# sourceMappingURL=index.cjs.map
|
|
589
|
-
//# sourceMappingURL=index.cjs.map
|