@alleyboss/micropay-solana-x402-paywall 1.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/LICENSE +21 -0
- package/README.md +232 -0
- package/dist/client-kfCr7G-P.d.cts +112 -0
- package/dist/client-kfCr7G-P.d.ts +112 -0
- package/dist/index-DptevtnU.d.cts +71 -0
- package/dist/index-DptevtnU.d.ts +71 -0
- package/dist/index.cjs +396 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +373 -0
- package/dist/index.js.map +1 -0
- package/dist/session/index.cjs +100 -0
- package/dist/session/index.cjs.map +1 -0
- package/dist/session/index.d.cts +1 -0
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.js +95 -0
- package/dist/session/index.js.map +1 -0
- package/dist/solana/index.cjs +196 -0
- package/dist/solana/index.cjs.map +1 -0
- package/dist/solana/index.d.cts +68 -0
- package/dist/solana/index.d.ts +68 -0
- package/dist/solana/index.js +186 -0
- package/dist/solana/index.js.map +1 -0
- package/dist/x402/index.cjs +249 -0
- package/dist/x402/index.cjs.map +1 -0
- package/dist/x402/index.d.cts +71 -0
- package/dist/x402/index.d.ts +71 -0
- package/dist/x402/index.js +239 -0
- package/dist/x402/index.js.map +1 -0
- package/package.json +93 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/session/core.ts"],"names":["uuidv4"],"mappings":"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","file":"index.js","sourcesContent":["// Session management with JWT (framework-agnostic core)\nimport { SignJWT, jwtVerify } from 'jose';\nimport { v4 as uuidv4 } from 'uuid';\nimport type { SessionData, SessionConfig, SessionValidation, SessionJWTPayload } from '../types';\n\n/**\n * Get the secret key for JWT signing\n */\nfunction getSecretKey(secret: string): Uint8Array {\n if (secret.length < 32) {\n throw new Error('Session secret must be at least 32 characters');\n }\n return new TextEncoder().encode(secret);\n}\n\n/**\n * Create a new session after successful payment\n */\nexport async function createSession(\n walletAddress: string,\n articleId: string,\n config: SessionConfig,\n siteWide: boolean = false\n): Promise<{ token: string; session: SessionData }> {\n const sessionId = uuidv4();\n const now = Math.floor(Date.now() / 1000);\n const expiresAt = now + (config.durationHours * 3600);\n\n const session: SessionData = {\n id: sessionId,\n walletAddress,\n unlockedArticles: [articleId],\n siteWideUnlock: siteWide,\n createdAt: now,\n expiresAt,\n };\n\n const payload: SessionJWTPayload = {\n sub: walletAddress,\n sid: sessionId,\n articles: session.unlockedArticles,\n siteWide,\n iat: now,\n exp: expiresAt,\n };\n\n const token = await new SignJWT(payload as unknown as Record<string, unknown>)\n .setProtectedHeader({ alg: 'HS256' })\n .setIssuedAt()\n .setExpirationTime(`${config.durationHours}h`)\n .sign(getSecretKey(config.secret));\n\n return { token, session };\n}\n\n/**\n * Validate an existing session token\n */\nexport async function validateSession(\n token: string,\n secret: string\n): Promise<SessionValidation> {\n try {\n const { payload } = await jwtVerify(token, getSecretKey(secret));\n const sessionPayload = payload as unknown as SessionJWTPayload;\n\n const now = Math.floor(Date.now() / 1000);\n if (sessionPayload.exp < now) {\n return { valid: false, reason: 'Session expired' };\n }\n\n const session: SessionData = {\n id: sessionPayload.sid,\n walletAddress: sessionPayload.sub,\n unlockedArticles: sessionPayload.articles,\n siteWideUnlock: sessionPayload.siteWide,\n createdAt: sessionPayload.iat,\n expiresAt: sessionPayload.exp,\n };\n\n return { valid: true, session };\n } catch (error) {\n return {\n valid: false,\n reason: error instanceof Error ? error.message : 'Invalid session',\n };\n }\n}\n\n/**\n * Add an article to an existing session\n */\nexport async function addArticleToSession(\n token: string,\n articleId: string,\n secret: string\n): Promise<{ token: string; session: SessionData } | null> {\n const validation = await validateSession(token, secret);\n if (!validation.valid || !validation.session) {\n return null;\n }\n\n const session = validation.session;\n\n // Already unlocked\n if (session.unlockedArticles.includes(articleId)) {\n return { token, session };\n }\n\n const updatedArticles = [...session.unlockedArticles, articleId];\n\n const payload: SessionJWTPayload = {\n sub: session.walletAddress,\n sid: session.id,\n articles: updatedArticles,\n siteWide: session.siteWideUnlock,\n iat: session.createdAt,\n exp: session.expiresAt,\n };\n\n const newToken = await new SignJWT(payload as unknown as Record<string, unknown>)\n .setProtectedHeader({ alg: 'HS256' })\n .sign(getSecretKey(secret));\n\n return {\n token: newToken,\n session: { ...session, unlockedArticles: updatedArticles },\n };\n}\n\n/**\n * Check if an article is unlocked for a session\n */\nexport async function isArticleUnlocked(\n token: string,\n articleId: string,\n secret: string\n): Promise<boolean> {\n const validation = await validateSession(token, secret);\n if (!validation.valid || !validation.session) {\n return false;\n }\n\n if (validation.session.siteWideUnlock) {\n return true;\n }\n\n return validation.session.unlockedArticles.includes(articleId);\n}\n"]}
|
|
@@ -0,0 +1,196 @@
|
|
|
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
|
+
function buildRpcUrl(config) {
|
|
9
|
+
const { network, rpcUrl, tatumApiKey } = config;
|
|
10
|
+
if (rpcUrl) {
|
|
11
|
+
if (rpcUrl.includes("tatum.io") && tatumApiKey && !rpcUrl.includes(tatumApiKey)) {
|
|
12
|
+
return rpcUrl.endsWith("/") ? `${rpcUrl}${tatumApiKey}` : `${rpcUrl}/${tatumApiKey}`;
|
|
13
|
+
}
|
|
14
|
+
return rpcUrl;
|
|
15
|
+
}
|
|
16
|
+
if (tatumApiKey) {
|
|
17
|
+
const baseUrl = network === "mainnet-beta" ? "https://solana-mainnet.gateway.tatum.io" : "https://solana-devnet.gateway.tatum.io";
|
|
18
|
+
return `${baseUrl}/${tatumApiKey}`;
|
|
19
|
+
}
|
|
20
|
+
return web3_js.clusterApiUrl(network);
|
|
21
|
+
}
|
|
22
|
+
function getConnection(config) {
|
|
23
|
+
const { network } = config;
|
|
24
|
+
if (cachedConnection && cachedNetwork === network) {
|
|
25
|
+
return cachedConnection;
|
|
26
|
+
}
|
|
27
|
+
const rpcUrl = buildRpcUrl(config);
|
|
28
|
+
cachedConnection = new web3_js.Connection(rpcUrl, {
|
|
29
|
+
commitment: "confirmed",
|
|
30
|
+
confirmTransactionInitialTimeout: 6e4
|
|
31
|
+
});
|
|
32
|
+
cachedNetwork = network;
|
|
33
|
+
return cachedConnection;
|
|
34
|
+
}
|
|
35
|
+
function resetConnection() {
|
|
36
|
+
cachedConnection = null;
|
|
37
|
+
cachedNetwork = null;
|
|
38
|
+
}
|
|
39
|
+
function isMainnet(network) {
|
|
40
|
+
return network === "mainnet-beta";
|
|
41
|
+
}
|
|
42
|
+
function toX402Network(network) {
|
|
43
|
+
return network === "mainnet-beta" ? "solana-mainnet" : "solana-devnet";
|
|
44
|
+
}
|
|
45
|
+
function parseSOLTransfer(transaction, expectedRecipient) {
|
|
46
|
+
const instructions = transaction.transaction.message.instructions;
|
|
47
|
+
for (const ix of instructions) {
|
|
48
|
+
if ("parsed" in ix && ix.program === "system") {
|
|
49
|
+
const parsed = ix.parsed;
|
|
50
|
+
if (parsed.type === "transfer" && parsed.info.destination === expectedRecipient) {
|
|
51
|
+
return {
|
|
52
|
+
from: parsed.info.source,
|
|
53
|
+
to: parsed.info.destination,
|
|
54
|
+
amount: BigInt(parsed.info.lamports)
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (transaction.meta?.innerInstructions) {
|
|
60
|
+
for (const inner of transaction.meta.innerInstructions) {
|
|
61
|
+
for (const ix of inner.instructions) {
|
|
62
|
+
if ("parsed" in ix && ix.program === "system") {
|
|
63
|
+
const parsed = ix.parsed;
|
|
64
|
+
if (parsed.type === "transfer" && parsed.info.destination === expectedRecipient) {
|
|
65
|
+
return {
|
|
66
|
+
from: parsed.info.source,
|
|
67
|
+
to: parsed.info.destination,
|
|
68
|
+
amount: BigInt(parsed.info.lamports)
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
async function verifyPayment(params) {
|
|
78
|
+
const {
|
|
79
|
+
signature,
|
|
80
|
+
expectedRecipient,
|
|
81
|
+
expectedAmount,
|
|
82
|
+
maxAgeSeconds = 300,
|
|
83
|
+
clientConfig
|
|
84
|
+
} = params;
|
|
85
|
+
const connection = getConnection(clientConfig);
|
|
86
|
+
try {
|
|
87
|
+
const transaction = await connection.getParsedTransaction(signature, {
|
|
88
|
+
commitment: "confirmed",
|
|
89
|
+
maxSupportedTransactionVersion: 0
|
|
90
|
+
});
|
|
91
|
+
if (!transaction) {
|
|
92
|
+
return { valid: false, confirmed: false, signature, error: "Transaction not found" };
|
|
93
|
+
}
|
|
94
|
+
if (transaction.meta?.err) {
|
|
95
|
+
return {
|
|
96
|
+
valid: false,
|
|
97
|
+
confirmed: true,
|
|
98
|
+
signature,
|
|
99
|
+
error: `Transaction failed: ${JSON.stringify(transaction.meta.err)}`
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (transaction.blockTime) {
|
|
103
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
104
|
+
if (now - transaction.blockTime > maxAgeSeconds) {
|
|
105
|
+
return { valid: false, confirmed: true, signature, error: "Transaction too old" };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const transferDetails = parseSOLTransfer(transaction, expectedRecipient);
|
|
109
|
+
if (!transferDetails) {
|
|
110
|
+
return {
|
|
111
|
+
valid: false,
|
|
112
|
+
confirmed: true,
|
|
113
|
+
signature,
|
|
114
|
+
error: "No valid SOL transfer to recipient found"
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
if (transferDetails.amount < expectedAmount) {
|
|
118
|
+
return {
|
|
119
|
+
valid: false,
|
|
120
|
+
confirmed: true,
|
|
121
|
+
signature,
|
|
122
|
+
from: transferDetails.from,
|
|
123
|
+
to: transferDetails.to,
|
|
124
|
+
amount: transferDetails.amount,
|
|
125
|
+
error: `Insufficient amount: expected ${expectedAmount}, got ${transferDetails.amount}`
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
valid: true,
|
|
130
|
+
confirmed: true,
|
|
131
|
+
signature,
|
|
132
|
+
from: transferDetails.from,
|
|
133
|
+
to: transferDetails.to,
|
|
134
|
+
amount: transferDetails.amount,
|
|
135
|
+
blockTime: transaction.blockTime ?? void 0,
|
|
136
|
+
slot: transaction.slot
|
|
137
|
+
};
|
|
138
|
+
} catch (error) {
|
|
139
|
+
return {
|
|
140
|
+
valid: false,
|
|
141
|
+
confirmed: false,
|
|
142
|
+
signature,
|
|
143
|
+
error: error instanceof Error ? error.message : "Unknown verification error"
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async function waitForConfirmation(signature, clientConfig) {
|
|
148
|
+
const connection = getConnection(clientConfig);
|
|
149
|
+
try {
|
|
150
|
+
const confirmation = await connection.confirmTransaction(signature, "confirmed");
|
|
151
|
+
if (confirmation.value.err) {
|
|
152
|
+
return {
|
|
153
|
+
confirmed: false,
|
|
154
|
+
error: `Transaction failed: ${JSON.stringify(confirmation.value.err)}`
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return { confirmed: true, slot: confirmation.context?.slot };
|
|
158
|
+
} catch (error) {
|
|
159
|
+
return {
|
|
160
|
+
confirmed: false,
|
|
161
|
+
error: error instanceof Error ? error.message : "Confirmation timeout"
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
async function getWalletTransactions(walletAddress, clientConfig, limit = 20) {
|
|
166
|
+
const connection = getConnection(clientConfig);
|
|
167
|
+
const pubkey = new web3_js.PublicKey(walletAddress);
|
|
168
|
+
try {
|
|
169
|
+
const signatures = await connection.getSignaturesForAddress(pubkey, { limit });
|
|
170
|
+
return signatures.map((sig) => ({
|
|
171
|
+
signature: sig.signature,
|
|
172
|
+
blockTime: sig.blockTime ?? void 0,
|
|
173
|
+
slot: sig.slot
|
|
174
|
+
}));
|
|
175
|
+
} catch {
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function lamportsToSol(lamports) {
|
|
180
|
+
return Number(lamports) / web3_js.LAMPORTS_PER_SOL;
|
|
181
|
+
}
|
|
182
|
+
function solToLamports(sol) {
|
|
183
|
+
return BigInt(Math.floor(sol * web3_js.LAMPORTS_PER_SOL));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
exports.getConnection = getConnection;
|
|
187
|
+
exports.getWalletTransactions = getWalletTransactions;
|
|
188
|
+
exports.isMainnet = isMainnet;
|
|
189
|
+
exports.lamportsToSol = lamportsToSol;
|
|
190
|
+
exports.resetConnection = resetConnection;
|
|
191
|
+
exports.solToLamports = solToLamports;
|
|
192
|
+
exports.toX402Network = toX402Network;
|
|
193
|
+
exports.verifyPayment = verifyPayment;
|
|
194
|
+
exports.waitForConfirmation = waitForConfirmation;
|
|
195
|
+
//# sourceMappingURL=index.cjs.map
|
|
196
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/solana/client.ts","../../src/solana/verification.ts"],"names":["clusterApiUrl","Connection","PublicKey","LAMPORTS_PER_SOL"],"mappings":"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","file":"index.cjs","sourcesContent":["// Solana RPC client with multi-provider support\nimport { Connection, clusterApiUrl, type Cluster } from '@solana/web3.js';\nimport type { SolanaNetwork } from '../types';\n\n/** Configuration for Solana client */\nexport interface SolanaClientConfig {\n /** Network to connect to */\n network: SolanaNetwork;\n /** Custom RPC URL (optional) */\n rpcUrl?: string;\n /** Tatum.io API key for RPC (optional) */\n tatumApiKey?: string;\n}\n\n// Singleton state\nlet cachedConnection: Connection | null = null;\nlet cachedNetwork: SolanaNetwork | null = null;\n\n/**\n * Build RPC URL based on configuration priority:\n * 1. Custom RPC URL\n * 2. Tatum.io with API key\n * 3. Public RPC (rate limited)\n */\nfunction buildRpcUrl(config: SolanaClientConfig): string {\n const { network, rpcUrl, tatumApiKey } = config;\n\n if (rpcUrl) {\n // If Tatum URL without key, append key if available\n if (rpcUrl.includes('tatum.io') && tatumApiKey && !rpcUrl.includes(tatumApiKey)) {\n return rpcUrl.endsWith('/') ? `${rpcUrl}${tatumApiKey}` : `${rpcUrl}/${tatumApiKey}`;\n }\n return rpcUrl;\n }\n\n if (tatumApiKey) {\n const baseUrl = network === 'mainnet-beta'\n ? 'https://solana-mainnet.gateway.tatum.io'\n : 'https://solana-devnet.gateway.tatum.io';\n return `${baseUrl}/${tatumApiKey}`;\n }\n\n // Fallback to public RPC\n return clusterApiUrl(network as Cluster);\n}\n\n/**\n * Get or create a Solana connection\n * Uses singleton pattern with network-aware caching\n */\nexport function getConnection(config: SolanaClientConfig): Connection {\n const { network } = config;\n\n // Return cached if same network\n if (cachedConnection && cachedNetwork === network) {\n return cachedConnection;\n }\n\n const rpcUrl = buildRpcUrl(config);\n\n cachedConnection = new Connection(rpcUrl, {\n commitment: 'confirmed',\n confirmTransactionInitialTimeout: 60000,\n });\n cachedNetwork = network;\n\n return cachedConnection;\n}\n\n/**\n * Reset the cached connection\n * Useful for testing or network switching\n */\nexport function resetConnection(): void {\n cachedConnection = null;\n cachedNetwork = null;\n}\n\n/**\n * Check if network is mainnet\n */\nexport function isMainnet(network: SolanaNetwork): boolean {\n return network === 'mainnet-beta';\n}\n\n/**\n * Convert Solana network to x402 network identifier\n */\nexport function toX402Network(network: SolanaNetwork): 'solana-devnet' | 'solana-mainnet' {\n return network === 'mainnet-beta' ? 'solana-mainnet' : 'solana-devnet';\n}\n","// Transaction verification for SOL payments\nimport { PublicKey, LAMPORTS_PER_SOL, type ParsedTransactionWithMeta } from '@solana/web3.js';\nimport { getConnection, type SolanaClientConfig } from './client';\n\n/** Result of transaction verification */\nexport interface TransactionVerificationResult {\n /** Whether the transaction is valid for the payment */\n valid: boolean;\n /** Whether the transaction is confirmed on-chain */\n confirmed: boolean;\n /** Transaction signature */\n signature: string;\n /** Sender wallet address */\n from?: string;\n /** Recipient wallet address */\n to?: string;\n /** Amount transferred in lamports */\n amount?: bigint;\n /** Block time (Unix timestamp) */\n blockTime?: number;\n /** Slot number */\n slot?: number;\n /** Error message if verification failed */\n error?: string;\n}\n\n/** Parameters for verifying a payment */\nexport interface VerifyPaymentParams {\n /** Transaction signature to verify */\n signature: string;\n /** Expected recipient wallet address */\n expectedRecipient: string;\n /** Expected amount in lamports */\n expectedAmount: bigint;\n /** Maximum age of transaction in seconds (default: 300) */\n maxAgeSeconds?: number;\n /** Solana client configuration */\n clientConfig: SolanaClientConfig;\n}\n\n/**\n * Parse SOL transfer details from a transaction\n */\nfunction parseSOLTransfer(\n transaction: ParsedTransactionWithMeta,\n expectedRecipient: string\n): { from: string; to: string; amount: bigint } | null {\n const instructions = transaction.transaction.message.instructions;\n\n // Check main instructions\n for (const ix of instructions) {\n if ('parsed' in ix && ix.program === 'system') {\n const parsed = ix.parsed as {\n type: string;\n info: { source: string; destination: string; lamports: number }\n };\n\n if (parsed.type === 'transfer' && parsed.info.destination === expectedRecipient) {\n return {\n from: parsed.info.source,\n to: parsed.info.destination,\n amount: BigInt(parsed.info.lamports),\n };\n }\n }\n }\n\n // Check inner instructions\n if (transaction.meta?.innerInstructions) {\n for (const inner of transaction.meta.innerInstructions) {\n for (const ix of inner.instructions) {\n if ('parsed' in ix && ix.program === 'system') {\n const parsed = ix.parsed as {\n type: string;\n info: { source: string; destination: string; lamports: number }\n };\n\n if (parsed.type === 'transfer' && parsed.info.destination === expectedRecipient) {\n return {\n from: parsed.info.source,\n to: parsed.info.destination,\n amount: BigInt(parsed.info.lamports),\n };\n }\n }\n }\n }\n }\n\n return null;\n}\n\n/**\n * Verify a SOL transfer transaction\n */\nexport async function verifyPayment(\n params: VerifyPaymentParams\n): Promise<TransactionVerificationResult> {\n const {\n signature,\n expectedRecipient,\n expectedAmount,\n maxAgeSeconds = 300,\n clientConfig\n } = params;\n\n const connection = getConnection(clientConfig);\n\n try {\n const transaction = await connection.getParsedTransaction(signature, {\n commitment: 'confirmed',\n maxSupportedTransactionVersion: 0,\n });\n\n if (!transaction) {\n return { valid: false, confirmed: false, signature, error: 'Transaction not found' };\n }\n\n // Check for transaction errors\n if (transaction.meta?.err) {\n return {\n valid: false,\n confirmed: true,\n signature,\n error: `Transaction failed: ${JSON.stringify(transaction.meta.err)}`,\n };\n }\n\n // Validate transaction age\n if (transaction.blockTime) {\n const now = Math.floor(Date.now() / 1000);\n if (now - transaction.blockTime > maxAgeSeconds) {\n return { valid: false, confirmed: true, signature, error: 'Transaction too old' };\n }\n }\n\n // Parse transfer details\n const transferDetails = parseSOLTransfer(transaction, expectedRecipient);\n\n if (!transferDetails) {\n return {\n valid: false,\n confirmed: true,\n signature,\n error: 'No valid SOL transfer to recipient found',\n };\n }\n\n // Validate amount\n if (transferDetails.amount < expectedAmount) {\n return {\n valid: false,\n confirmed: true,\n signature,\n from: transferDetails.from,\n to: transferDetails.to,\n amount: transferDetails.amount,\n error: `Insufficient amount: expected ${expectedAmount}, got ${transferDetails.amount}`,\n };\n }\n\n return {\n valid: true,\n confirmed: true,\n signature,\n from: transferDetails.from,\n to: transferDetails.to,\n amount: transferDetails.amount,\n blockTime: transaction.blockTime ?? undefined,\n slot: transaction.slot,\n };\n } catch (error) {\n return {\n valid: false,\n confirmed: false,\n signature,\n error: error instanceof Error ? error.message : 'Unknown verification error',\n };\n }\n}\n\n/**\n * Wait for transaction confirmation\n */\nexport async function waitForConfirmation(\n signature: string,\n clientConfig: SolanaClientConfig\n): Promise<{ confirmed: boolean; slot?: number; error?: string }> {\n const connection = getConnection(clientConfig);\n\n try {\n const confirmation = await connection.confirmTransaction(signature, 'confirmed');\n\n if (confirmation.value.err) {\n return {\n confirmed: false,\n error: `Transaction failed: ${JSON.stringify(confirmation.value.err)}`,\n };\n }\n\n return { confirmed: true, slot: confirmation.context?.slot };\n } catch (error) {\n return {\n confirmed: false,\n error: error instanceof Error ? error.message : 'Confirmation timeout',\n };\n }\n}\n\n/**\n * Get recent transactions for a wallet\n */\nexport async function getWalletTransactions(\n walletAddress: string,\n clientConfig: SolanaClientConfig,\n limit: number = 20\n): Promise<Array<{ signature: string; blockTime?: number; slot: number }>> {\n const connection = getConnection(clientConfig);\n const pubkey = new PublicKey(walletAddress);\n\n try {\n const signatures = await connection.getSignaturesForAddress(pubkey, { limit });\n return signatures.map((sig) => ({\n signature: sig.signature,\n blockTime: sig.blockTime ?? undefined,\n slot: sig.slot,\n }));\n } catch {\n return [];\n }\n}\n\n/**\n * Convert lamports to SOL\n */\nexport function lamportsToSol(lamports: bigint | number): number {\n return Number(lamports) / LAMPORTS_PER_SOL;\n}\n\n/**\n * Convert SOL to lamports\n */\nexport function solToLamports(sol: number): bigint {\n return BigInt(Math.floor(sol * LAMPORTS_PER_SOL));\n}\n"]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { d as SolanaClientConfig } from '../client-kfCr7G-P.cjs';
|
|
2
|
+
export { g as getConnection, i as isMainnet, r as resetConnection, t as toX402Network } from '../client-kfCr7G-P.cjs';
|
|
3
|
+
import '@solana/web3.js';
|
|
4
|
+
|
|
5
|
+
/** Result of transaction verification */
|
|
6
|
+
interface TransactionVerificationResult {
|
|
7
|
+
/** Whether the transaction is valid for the payment */
|
|
8
|
+
valid: boolean;
|
|
9
|
+
/** Whether the transaction is confirmed on-chain */
|
|
10
|
+
confirmed: boolean;
|
|
11
|
+
/** Transaction signature */
|
|
12
|
+
signature: string;
|
|
13
|
+
/** Sender wallet address */
|
|
14
|
+
from?: string;
|
|
15
|
+
/** Recipient wallet address */
|
|
16
|
+
to?: string;
|
|
17
|
+
/** Amount transferred in lamports */
|
|
18
|
+
amount?: bigint;
|
|
19
|
+
/** Block time (Unix timestamp) */
|
|
20
|
+
blockTime?: number;
|
|
21
|
+
/** Slot number */
|
|
22
|
+
slot?: number;
|
|
23
|
+
/** Error message if verification failed */
|
|
24
|
+
error?: string;
|
|
25
|
+
}
|
|
26
|
+
/** Parameters for verifying a payment */
|
|
27
|
+
interface VerifyPaymentParams {
|
|
28
|
+
/** Transaction signature to verify */
|
|
29
|
+
signature: string;
|
|
30
|
+
/** Expected recipient wallet address */
|
|
31
|
+
expectedRecipient: string;
|
|
32
|
+
/** Expected amount in lamports */
|
|
33
|
+
expectedAmount: bigint;
|
|
34
|
+
/** Maximum age of transaction in seconds (default: 300) */
|
|
35
|
+
maxAgeSeconds?: number;
|
|
36
|
+
/** Solana client configuration */
|
|
37
|
+
clientConfig: SolanaClientConfig;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Verify a SOL transfer transaction
|
|
41
|
+
*/
|
|
42
|
+
declare function verifyPayment(params: VerifyPaymentParams): Promise<TransactionVerificationResult>;
|
|
43
|
+
/**
|
|
44
|
+
* Wait for transaction confirmation
|
|
45
|
+
*/
|
|
46
|
+
declare function waitForConfirmation(signature: string, clientConfig: SolanaClientConfig): Promise<{
|
|
47
|
+
confirmed: boolean;
|
|
48
|
+
slot?: number;
|
|
49
|
+
error?: string;
|
|
50
|
+
}>;
|
|
51
|
+
/**
|
|
52
|
+
* Get recent transactions for a wallet
|
|
53
|
+
*/
|
|
54
|
+
declare function getWalletTransactions(walletAddress: string, clientConfig: SolanaClientConfig, limit?: number): Promise<Array<{
|
|
55
|
+
signature: string;
|
|
56
|
+
blockTime?: number;
|
|
57
|
+
slot: number;
|
|
58
|
+
}>>;
|
|
59
|
+
/**
|
|
60
|
+
* Convert lamports to SOL
|
|
61
|
+
*/
|
|
62
|
+
declare function lamportsToSol(lamports: bigint | number): number;
|
|
63
|
+
/**
|
|
64
|
+
* Convert SOL to lamports
|
|
65
|
+
*/
|
|
66
|
+
declare function solToLamports(sol: number): bigint;
|
|
67
|
+
|
|
68
|
+
export { SolanaClientConfig, type TransactionVerificationResult, type VerifyPaymentParams, getWalletTransactions, lamportsToSol, solToLamports, verifyPayment, waitForConfirmation };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { d as SolanaClientConfig } from '../client-kfCr7G-P.js';
|
|
2
|
+
export { g as getConnection, i as isMainnet, r as resetConnection, t as toX402Network } from '../client-kfCr7G-P.js';
|
|
3
|
+
import '@solana/web3.js';
|
|
4
|
+
|
|
5
|
+
/** Result of transaction verification */
|
|
6
|
+
interface TransactionVerificationResult {
|
|
7
|
+
/** Whether the transaction is valid for the payment */
|
|
8
|
+
valid: boolean;
|
|
9
|
+
/** Whether the transaction is confirmed on-chain */
|
|
10
|
+
confirmed: boolean;
|
|
11
|
+
/** Transaction signature */
|
|
12
|
+
signature: string;
|
|
13
|
+
/** Sender wallet address */
|
|
14
|
+
from?: string;
|
|
15
|
+
/** Recipient wallet address */
|
|
16
|
+
to?: string;
|
|
17
|
+
/** Amount transferred in lamports */
|
|
18
|
+
amount?: bigint;
|
|
19
|
+
/** Block time (Unix timestamp) */
|
|
20
|
+
blockTime?: number;
|
|
21
|
+
/** Slot number */
|
|
22
|
+
slot?: number;
|
|
23
|
+
/** Error message if verification failed */
|
|
24
|
+
error?: string;
|
|
25
|
+
}
|
|
26
|
+
/** Parameters for verifying a payment */
|
|
27
|
+
interface VerifyPaymentParams {
|
|
28
|
+
/** Transaction signature to verify */
|
|
29
|
+
signature: string;
|
|
30
|
+
/** Expected recipient wallet address */
|
|
31
|
+
expectedRecipient: string;
|
|
32
|
+
/** Expected amount in lamports */
|
|
33
|
+
expectedAmount: bigint;
|
|
34
|
+
/** Maximum age of transaction in seconds (default: 300) */
|
|
35
|
+
maxAgeSeconds?: number;
|
|
36
|
+
/** Solana client configuration */
|
|
37
|
+
clientConfig: SolanaClientConfig;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Verify a SOL transfer transaction
|
|
41
|
+
*/
|
|
42
|
+
declare function verifyPayment(params: VerifyPaymentParams): Promise<TransactionVerificationResult>;
|
|
43
|
+
/**
|
|
44
|
+
* Wait for transaction confirmation
|
|
45
|
+
*/
|
|
46
|
+
declare function waitForConfirmation(signature: string, clientConfig: SolanaClientConfig): Promise<{
|
|
47
|
+
confirmed: boolean;
|
|
48
|
+
slot?: number;
|
|
49
|
+
error?: string;
|
|
50
|
+
}>;
|
|
51
|
+
/**
|
|
52
|
+
* Get recent transactions for a wallet
|
|
53
|
+
*/
|
|
54
|
+
declare function getWalletTransactions(walletAddress: string, clientConfig: SolanaClientConfig, limit?: number): Promise<Array<{
|
|
55
|
+
signature: string;
|
|
56
|
+
blockTime?: number;
|
|
57
|
+
slot: number;
|
|
58
|
+
}>>;
|
|
59
|
+
/**
|
|
60
|
+
* Convert lamports to SOL
|
|
61
|
+
*/
|
|
62
|
+
declare function lamportsToSol(lamports: bigint | number): number;
|
|
63
|
+
/**
|
|
64
|
+
* Convert SOL to lamports
|
|
65
|
+
*/
|
|
66
|
+
declare function solToLamports(sol: number): bigint;
|
|
67
|
+
|
|
68
|
+
export { SolanaClientConfig, type TransactionVerificationResult, type VerifyPaymentParams, getWalletTransactions, lamportsToSol, solToLamports, verifyPayment, waitForConfirmation };
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { Connection, PublicKey, LAMPORTS_PER_SOL, clusterApiUrl } from '@solana/web3.js';
|
|
2
|
+
|
|
3
|
+
// src/solana/client.ts
|
|
4
|
+
var cachedConnection = null;
|
|
5
|
+
var cachedNetwork = null;
|
|
6
|
+
function buildRpcUrl(config) {
|
|
7
|
+
const { network, rpcUrl, tatumApiKey } = config;
|
|
8
|
+
if (rpcUrl) {
|
|
9
|
+
if (rpcUrl.includes("tatum.io") && tatumApiKey && !rpcUrl.includes(tatumApiKey)) {
|
|
10
|
+
return rpcUrl.endsWith("/") ? `${rpcUrl}${tatumApiKey}` : `${rpcUrl}/${tatumApiKey}`;
|
|
11
|
+
}
|
|
12
|
+
return rpcUrl;
|
|
13
|
+
}
|
|
14
|
+
if (tatumApiKey) {
|
|
15
|
+
const baseUrl = network === "mainnet-beta" ? "https://solana-mainnet.gateway.tatum.io" : "https://solana-devnet.gateway.tatum.io";
|
|
16
|
+
return `${baseUrl}/${tatumApiKey}`;
|
|
17
|
+
}
|
|
18
|
+
return clusterApiUrl(network);
|
|
19
|
+
}
|
|
20
|
+
function getConnection(config) {
|
|
21
|
+
const { network } = config;
|
|
22
|
+
if (cachedConnection && cachedNetwork === network) {
|
|
23
|
+
return cachedConnection;
|
|
24
|
+
}
|
|
25
|
+
const rpcUrl = buildRpcUrl(config);
|
|
26
|
+
cachedConnection = new Connection(rpcUrl, {
|
|
27
|
+
commitment: "confirmed",
|
|
28
|
+
confirmTransactionInitialTimeout: 6e4
|
|
29
|
+
});
|
|
30
|
+
cachedNetwork = network;
|
|
31
|
+
return cachedConnection;
|
|
32
|
+
}
|
|
33
|
+
function resetConnection() {
|
|
34
|
+
cachedConnection = null;
|
|
35
|
+
cachedNetwork = null;
|
|
36
|
+
}
|
|
37
|
+
function isMainnet(network) {
|
|
38
|
+
return network === "mainnet-beta";
|
|
39
|
+
}
|
|
40
|
+
function toX402Network(network) {
|
|
41
|
+
return network === "mainnet-beta" ? "solana-mainnet" : "solana-devnet";
|
|
42
|
+
}
|
|
43
|
+
function parseSOLTransfer(transaction, expectedRecipient) {
|
|
44
|
+
const instructions = transaction.transaction.message.instructions;
|
|
45
|
+
for (const ix of instructions) {
|
|
46
|
+
if ("parsed" in ix && ix.program === "system") {
|
|
47
|
+
const parsed = ix.parsed;
|
|
48
|
+
if (parsed.type === "transfer" && parsed.info.destination === expectedRecipient) {
|
|
49
|
+
return {
|
|
50
|
+
from: parsed.info.source,
|
|
51
|
+
to: parsed.info.destination,
|
|
52
|
+
amount: BigInt(parsed.info.lamports)
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (transaction.meta?.innerInstructions) {
|
|
58
|
+
for (const inner of transaction.meta.innerInstructions) {
|
|
59
|
+
for (const ix of inner.instructions) {
|
|
60
|
+
if ("parsed" in ix && ix.program === "system") {
|
|
61
|
+
const parsed = ix.parsed;
|
|
62
|
+
if (parsed.type === "transfer" && parsed.info.destination === expectedRecipient) {
|
|
63
|
+
return {
|
|
64
|
+
from: parsed.info.source,
|
|
65
|
+
to: parsed.info.destination,
|
|
66
|
+
amount: BigInt(parsed.info.lamports)
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
async function verifyPayment(params) {
|
|
76
|
+
const {
|
|
77
|
+
signature,
|
|
78
|
+
expectedRecipient,
|
|
79
|
+
expectedAmount,
|
|
80
|
+
maxAgeSeconds = 300,
|
|
81
|
+
clientConfig
|
|
82
|
+
} = params;
|
|
83
|
+
const connection = getConnection(clientConfig);
|
|
84
|
+
try {
|
|
85
|
+
const transaction = await connection.getParsedTransaction(signature, {
|
|
86
|
+
commitment: "confirmed",
|
|
87
|
+
maxSupportedTransactionVersion: 0
|
|
88
|
+
});
|
|
89
|
+
if (!transaction) {
|
|
90
|
+
return { valid: false, confirmed: false, signature, error: "Transaction not found" };
|
|
91
|
+
}
|
|
92
|
+
if (transaction.meta?.err) {
|
|
93
|
+
return {
|
|
94
|
+
valid: false,
|
|
95
|
+
confirmed: true,
|
|
96
|
+
signature,
|
|
97
|
+
error: `Transaction failed: ${JSON.stringify(transaction.meta.err)}`
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (transaction.blockTime) {
|
|
101
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
102
|
+
if (now - transaction.blockTime > maxAgeSeconds) {
|
|
103
|
+
return { valid: false, confirmed: true, signature, error: "Transaction too old" };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const transferDetails = parseSOLTransfer(transaction, expectedRecipient);
|
|
107
|
+
if (!transferDetails) {
|
|
108
|
+
return {
|
|
109
|
+
valid: false,
|
|
110
|
+
confirmed: true,
|
|
111
|
+
signature,
|
|
112
|
+
error: "No valid SOL transfer to recipient found"
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
if (transferDetails.amount < expectedAmount) {
|
|
116
|
+
return {
|
|
117
|
+
valid: false,
|
|
118
|
+
confirmed: true,
|
|
119
|
+
signature,
|
|
120
|
+
from: transferDetails.from,
|
|
121
|
+
to: transferDetails.to,
|
|
122
|
+
amount: transferDetails.amount,
|
|
123
|
+
error: `Insufficient amount: expected ${expectedAmount}, got ${transferDetails.amount}`
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
valid: true,
|
|
128
|
+
confirmed: true,
|
|
129
|
+
signature,
|
|
130
|
+
from: transferDetails.from,
|
|
131
|
+
to: transferDetails.to,
|
|
132
|
+
amount: transferDetails.amount,
|
|
133
|
+
blockTime: transaction.blockTime ?? void 0,
|
|
134
|
+
slot: transaction.slot
|
|
135
|
+
};
|
|
136
|
+
} catch (error) {
|
|
137
|
+
return {
|
|
138
|
+
valid: false,
|
|
139
|
+
confirmed: false,
|
|
140
|
+
signature,
|
|
141
|
+
error: error instanceof Error ? error.message : "Unknown verification error"
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async function waitForConfirmation(signature, clientConfig) {
|
|
146
|
+
const connection = getConnection(clientConfig);
|
|
147
|
+
try {
|
|
148
|
+
const confirmation = await connection.confirmTransaction(signature, "confirmed");
|
|
149
|
+
if (confirmation.value.err) {
|
|
150
|
+
return {
|
|
151
|
+
confirmed: false,
|
|
152
|
+
error: `Transaction failed: ${JSON.stringify(confirmation.value.err)}`
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
return { confirmed: true, slot: confirmation.context?.slot };
|
|
156
|
+
} catch (error) {
|
|
157
|
+
return {
|
|
158
|
+
confirmed: false,
|
|
159
|
+
error: error instanceof Error ? error.message : "Confirmation timeout"
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
async function getWalletTransactions(walletAddress, clientConfig, limit = 20) {
|
|
164
|
+
const connection = getConnection(clientConfig);
|
|
165
|
+
const pubkey = new PublicKey(walletAddress);
|
|
166
|
+
try {
|
|
167
|
+
const signatures = await connection.getSignaturesForAddress(pubkey, { limit });
|
|
168
|
+
return signatures.map((sig) => ({
|
|
169
|
+
signature: sig.signature,
|
|
170
|
+
blockTime: sig.blockTime ?? void 0,
|
|
171
|
+
slot: sig.slot
|
|
172
|
+
}));
|
|
173
|
+
} catch {
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function lamportsToSol(lamports) {
|
|
178
|
+
return Number(lamports) / LAMPORTS_PER_SOL;
|
|
179
|
+
}
|
|
180
|
+
function solToLamports(sol) {
|
|
181
|
+
return BigInt(Math.floor(sol * LAMPORTS_PER_SOL));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export { getConnection, getWalletTransactions, isMainnet, lamportsToSol, resetConnection, solToLamports, toX402Network, verifyPayment, waitForConfirmation };
|
|
185
|
+
//# sourceMappingURL=index.js.map
|
|
186
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/solana/client.ts","../../src/solana/verification.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// Solana RPC client with multi-provider support\nimport { Connection, clusterApiUrl, type Cluster } from '@solana/web3.js';\nimport type { SolanaNetwork } from '../types';\n\n/** Configuration for Solana client */\nexport interface SolanaClientConfig {\n /** Network to connect to */\n network: SolanaNetwork;\n /** Custom RPC URL (optional) */\n rpcUrl?: string;\n /** Tatum.io API key for RPC (optional) */\n tatumApiKey?: string;\n}\n\n// Singleton state\nlet cachedConnection: Connection | null = null;\nlet cachedNetwork: SolanaNetwork | null = null;\n\n/**\n * Build RPC URL based on configuration priority:\n * 1. Custom RPC URL\n * 2. Tatum.io with API key\n * 3. Public RPC (rate limited)\n */\nfunction buildRpcUrl(config: SolanaClientConfig): string {\n const { network, rpcUrl, tatumApiKey } = config;\n\n if (rpcUrl) {\n // If Tatum URL without key, append key if available\n if (rpcUrl.includes('tatum.io') && tatumApiKey && !rpcUrl.includes(tatumApiKey)) {\n return rpcUrl.endsWith('/') ? `${rpcUrl}${tatumApiKey}` : `${rpcUrl}/${tatumApiKey}`;\n }\n return rpcUrl;\n }\n\n if (tatumApiKey) {\n const baseUrl = network === 'mainnet-beta'\n ? 'https://solana-mainnet.gateway.tatum.io'\n : 'https://solana-devnet.gateway.tatum.io';\n return `${baseUrl}/${tatumApiKey}`;\n }\n\n // Fallback to public RPC\n return clusterApiUrl(network as Cluster);\n}\n\n/**\n * Get or create a Solana connection\n * Uses singleton pattern with network-aware caching\n */\nexport function getConnection(config: SolanaClientConfig): Connection {\n const { network } = config;\n\n // Return cached if same network\n if (cachedConnection && cachedNetwork === network) {\n return cachedConnection;\n }\n\n const rpcUrl = buildRpcUrl(config);\n\n cachedConnection = new Connection(rpcUrl, {\n commitment: 'confirmed',\n confirmTransactionInitialTimeout: 60000,\n });\n cachedNetwork = network;\n\n return cachedConnection;\n}\n\n/**\n * Reset the cached connection\n * Useful for testing or network switching\n */\nexport function resetConnection(): void {\n cachedConnection = null;\n cachedNetwork = null;\n}\n\n/**\n * Check if network is mainnet\n */\nexport function isMainnet(network: SolanaNetwork): boolean {\n return network === 'mainnet-beta';\n}\n\n/**\n * Convert Solana network to x402 network identifier\n */\nexport function toX402Network(network: SolanaNetwork): 'solana-devnet' | 'solana-mainnet' {\n return network === 'mainnet-beta' ? 'solana-mainnet' : 'solana-devnet';\n}\n","// Transaction verification for SOL payments\nimport { PublicKey, LAMPORTS_PER_SOL, type ParsedTransactionWithMeta } from '@solana/web3.js';\nimport { getConnection, type SolanaClientConfig } from './client';\n\n/** Result of transaction verification */\nexport interface TransactionVerificationResult {\n /** Whether the transaction is valid for the payment */\n valid: boolean;\n /** Whether the transaction is confirmed on-chain */\n confirmed: boolean;\n /** Transaction signature */\n signature: string;\n /** Sender wallet address */\n from?: string;\n /** Recipient wallet address */\n to?: string;\n /** Amount transferred in lamports */\n amount?: bigint;\n /** Block time (Unix timestamp) */\n blockTime?: number;\n /** Slot number */\n slot?: number;\n /** Error message if verification failed */\n error?: string;\n}\n\n/** Parameters for verifying a payment */\nexport interface VerifyPaymentParams {\n /** Transaction signature to verify */\n signature: string;\n /** Expected recipient wallet address */\n expectedRecipient: string;\n /** Expected amount in lamports */\n expectedAmount: bigint;\n /** Maximum age of transaction in seconds (default: 300) */\n maxAgeSeconds?: number;\n /** Solana client configuration */\n clientConfig: SolanaClientConfig;\n}\n\n/**\n * Parse SOL transfer details from a transaction\n */\nfunction parseSOLTransfer(\n transaction: ParsedTransactionWithMeta,\n expectedRecipient: string\n): { from: string; to: string; amount: bigint } | null {\n const instructions = transaction.transaction.message.instructions;\n\n // Check main instructions\n for (const ix of instructions) {\n if ('parsed' in ix && ix.program === 'system') {\n const parsed = ix.parsed as {\n type: string;\n info: { source: string; destination: string; lamports: number }\n };\n\n if (parsed.type === 'transfer' && parsed.info.destination === expectedRecipient) {\n return {\n from: parsed.info.source,\n to: parsed.info.destination,\n amount: BigInt(parsed.info.lamports),\n };\n }\n }\n }\n\n // Check inner instructions\n if (transaction.meta?.innerInstructions) {\n for (const inner of transaction.meta.innerInstructions) {\n for (const ix of inner.instructions) {\n if ('parsed' in ix && ix.program === 'system') {\n const parsed = ix.parsed as {\n type: string;\n info: { source: string; destination: string; lamports: number }\n };\n\n if (parsed.type === 'transfer' && parsed.info.destination === expectedRecipient) {\n return {\n from: parsed.info.source,\n to: parsed.info.destination,\n amount: BigInt(parsed.info.lamports),\n };\n }\n }\n }\n }\n }\n\n return null;\n}\n\n/**\n * Verify a SOL transfer transaction\n */\nexport async function verifyPayment(\n params: VerifyPaymentParams\n): Promise<TransactionVerificationResult> {\n const {\n signature,\n expectedRecipient,\n expectedAmount,\n maxAgeSeconds = 300,\n clientConfig\n } = params;\n\n const connection = getConnection(clientConfig);\n\n try {\n const transaction = await connection.getParsedTransaction(signature, {\n commitment: 'confirmed',\n maxSupportedTransactionVersion: 0,\n });\n\n if (!transaction) {\n return { valid: false, confirmed: false, signature, error: 'Transaction not found' };\n }\n\n // Check for transaction errors\n if (transaction.meta?.err) {\n return {\n valid: false,\n confirmed: true,\n signature,\n error: `Transaction failed: ${JSON.stringify(transaction.meta.err)}`,\n };\n }\n\n // Validate transaction age\n if (transaction.blockTime) {\n const now = Math.floor(Date.now() / 1000);\n if (now - transaction.blockTime > maxAgeSeconds) {\n return { valid: false, confirmed: true, signature, error: 'Transaction too old' };\n }\n }\n\n // Parse transfer details\n const transferDetails = parseSOLTransfer(transaction, expectedRecipient);\n\n if (!transferDetails) {\n return {\n valid: false,\n confirmed: true,\n signature,\n error: 'No valid SOL transfer to recipient found',\n };\n }\n\n // Validate amount\n if (transferDetails.amount < expectedAmount) {\n return {\n valid: false,\n confirmed: true,\n signature,\n from: transferDetails.from,\n to: transferDetails.to,\n amount: transferDetails.amount,\n error: `Insufficient amount: expected ${expectedAmount}, got ${transferDetails.amount}`,\n };\n }\n\n return {\n valid: true,\n confirmed: true,\n signature,\n from: transferDetails.from,\n to: transferDetails.to,\n amount: transferDetails.amount,\n blockTime: transaction.blockTime ?? undefined,\n slot: transaction.slot,\n };\n } catch (error) {\n return {\n valid: false,\n confirmed: false,\n signature,\n error: error instanceof Error ? error.message : 'Unknown verification error',\n };\n }\n}\n\n/**\n * Wait for transaction confirmation\n */\nexport async function waitForConfirmation(\n signature: string,\n clientConfig: SolanaClientConfig\n): Promise<{ confirmed: boolean; slot?: number; error?: string }> {\n const connection = getConnection(clientConfig);\n\n try {\n const confirmation = await connection.confirmTransaction(signature, 'confirmed');\n\n if (confirmation.value.err) {\n return {\n confirmed: false,\n error: `Transaction failed: ${JSON.stringify(confirmation.value.err)}`,\n };\n }\n\n return { confirmed: true, slot: confirmation.context?.slot };\n } catch (error) {\n return {\n confirmed: false,\n error: error instanceof Error ? error.message : 'Confirmation timeout',\n };\n }\n}\n\n/**\n * Get recent transactions for a wallet\n */\nexport async function getWalletTransactions(\n walletAddress: string,\n clientConfig: SolanaClientConfig,\n limit: number = 20\n): Promise<Array<{ signature: string; blockTime?: number; slot: number }>> {\n const connection = getConnection(clientConfig);\n const pubkey = new PublicKey(walletAddress);\n\n try {\n const signatures = await connection.getSignaturesForAddress(pubkey, { limit });\n return signatures.map((sig) => ({\n signature: sig.signature,\n blockTime: sig.blockTime ?? undefined,\n slot: sig.slot,\n }));\n } catch {\n return [];\n }\n}\n\n/**\n * Convert lamports to SOL\n */\nexport function lamportsToSol(lamports: bigint | number): number {\n return Number(lamports) / LAMPORTS_PER_SOL;\n}\n\n/**\n * Convert SOL to lamports\n */\nexport function solToLamports(sol: number): bigint {\n return BigInt(Math.floor(sol * LAMPORTS_PER_SOL));\n}\n"]}
|