@alleyboss/micropay-solana-x402-paywall 1.0.1 → 2.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 +100 -167
- package/dist/client/index.cjs +99 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.cts +112 -0
- package/dist/client/index.d.ts +112 -0
- package/dist/client/index.js +95 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client-CSZHI8o8.d.ts +32 -0
- package/dist/client-vRr48m2x.d.cts +32 -0
- package/dist/index.cjs +624 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -3
- package/dist/index.d.ts +11 -3
- package/dist/index.js +604 -7
- package/dist/index.js.map +1 -1
- package/dist/memory-Daxkczti.d.cts +29 -0
- package/dist/memory-Daxkczti.d.ts +29 -0
- package/dist/middleware/index.cjs +261 -0
- package/dist/middleware/index.cjs.map +1 -0
- package/dist/middleware/index.d.cts +90 -0
- package/dist/middleware/index.d.ts +90 -0
- package/dist/middleware/index.js +255 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/nextjs-BK0pVb9Y.d.ts +78 -0
- package/dist/nextjs-Bm272Jkj.d.cts +78 -0
- package/dist/{client-kfCr7G-P.d.cts → payment-CTxdtqmc.d.cts} +23 -34
- package/dist/{client-kfCr7G-P.d.ts → payment-CTxdtqmc.d.ts} +23 -34
- package/dist/pricing/index.cjs +79 -0
- package/dist/pricing/index.cjs.map +1 -0
- package/dist/pricing/index.d.cts +67 -0
- package/dist/pricing/index.d.ts +67 -0
- package/dist/pricing/index.js +72 -0
- package/dist/pricing/index.js.map +1 -0
- package/dist/session/index.d.cts +29 -1
- package/dist/session/index.d.ts +29 -1
- package/dist/{index-uxMb72hH.d.cts → session-D2IoWAWV.d.cts} +1 -27
- package/dist/{index-uxMb72hH.d.ts → session-D2IoWAWV.d.ts} +1 -27
- package/dist/solana/index.cjs +193 -0
- package/dist/solana/index.cjs.map +1 -1
- package/dist/solana/index.d.cts +60 -3
- package/dist/solana/index.d.ts +60 -3
- package/dist/solana/index.js +190 -1
- package/dist/solana/index.js.map +1 -1
- package/dist/store/index.cjs +99 -0
- package/dist/store/index.cjs.map +1 -0
- package/dist/store/index.d.cts +38 -0
- package/dist/store/index.d.ts +38 -0
- package/dist/store/index.js +96 -0
- package/dist/store/index.js.map +1 -0
- package/dist/utils/index.cjs +68 -0
- package/dist/utils/index.cjs.map +1 -0
- package/dist/utils/index.d.cts +30 -0
- package/dist/utils/index.d.ts +30 -0
- package/dist/utils/index.js +65 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/x402/index.cjs +2 -1
- package/dist/x402/index.cjs.map +1 -1
- package/dist/x402/index.d.cts +2 -1
- package/dist/x402/index.d.ts +2 -1
- package/dist/x402/index.js +2 -1
- package/dist/x402/index.js.map +1 -1
- package/package.json +56 -3
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/pricing/index.ts
|
|
4
|
+
var cachedPrice = null;
|
|
5
|
+
var CACHE_TTL_MS = 6e4;
|
|
6
|
+
async function getSolPrice() {
|
|
7
|
+
if (cachedPrice && Date.now() - cachedPrice.fetchedAt.getTime() < CACHE_TTL_MS) {
|
|
8
|
+
return cachedPrice;
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
const response = await fetch(
|
|
12
|
+
"https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd",
|
|
13
|
+
{
|
|
14
|
+
headers: { "Accept": "application/json" },
|
|
15
|
+
signal: AbortSignal.timeout(5e3)
|
|
16
|
+
}
|
|
17
|
+
);
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
throw new Error(`Price fetch failed: ${response.status}`);
|
|
20
|
+
}
|
|
21
|
+
const data = await response.json();
|
|
22
|
+
if (!data.solana?.usd) {
|
|
23
|
+
throw new Error("Invalid price response");
|
|
24
|
+
}
|
|
25
|
+
cachedPrice = {
|
|
26
|
+
solPrice: data.solana.usd,
|
|
27
|
+
fetchedAt: /* @__PURE__ */ new Date(),
|
|
28
|
+
source: "coingecko"
|
|
29
|
+
};
|
|
30
|
+
return cachedPrice;
|
|
31
|
+
} catch (error) {
|
|
32
|
+
if (cachedPrice) {
|
|
33
|
+
return cachedPrice;
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
solPrice: 150,
|
|
37
|
+
// Fallback price
|
|
38
|
+
fetchedAt: /* @__PURE__ */ new Date(),
|
|
39
|
+
source: "fallback"
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async function lamportsToUsd(lamports) {
|
|
44
|
+
const { solPrice } = await getSolPrice();
|
|
45
|
+
const sol = Number(lamports) / 1e9;
|
|
46
|
+
return sol * solPrice;
|
|
47
|
+
}
|
|
48
|
+
async function usdToLamports(usd) {
|
|
49
|
+
const { solPrice } = await getSolPrice();
|
|
50
|
+
const sol = usd / solPrice;
|
|
51
|
+
return BigInt(Math.floor(sol * 1e9));
|
|
52
|
+
}
|
|
53
|
+
async function formatPriceDisplay(lamports) {
|
|
54
|
+
const { solPrice } = await getSolPrice();
|
|
55
|
+
const sol = Number(lamports) / 1e9;
|
|
56
|
+
const usd = sol * solPrice;
|
|
57
|
+
return `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`;
|
|
58
|
+
}
|
|
59
|
+
function formatPriceSync(lamports, solPrice) {
|
|
60
|
+
const sol = Number(lamports) / 1e9;
|
|
61
|
+
const usd = sol * solPrice;
|
|
62
|
+
return {
|
|
63
|
+
sol,
|
|
64
|
+
usd,
|
|
65
|
+
formatted: `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function clearPriceCache() {
|
|
69
|
+
cachedPrice = null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
exports.clearPriceCache = clearPriceCache;
|
|
73
|
+
exports.formatPriceDisplay = formatPriceDisplay;
|
|
74
|
+
exports.formatPriceSync = formatPriceSync;
|
|
75
|
+
exports.getSolPrice = getSolPrice;
|
|
76
|
+
exports.lamportsToUsd = lamportsToUsd;
|
|
77
|
+
exports.usdToLamports = usdToLamports;
|
|
78
|
+
//# sourceMappingURL=index.cjs.map
|
|
79
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/pricing/index.ts"],"names":[],"mappings":";;;AAkBA,IAAI,WAAA,GAAgC,IAAA;AACpC,IAAM,YAAA,GAAe,GAAA;AAYrB,eAAsB,WAAA,GAAkC;AAEpD,EAAA,IAAI,WAAA,IAAe,KAAK,GAAA,EAAI,GAAI,YAAY,SAAA,CAAU,OAAA,KAAY,YAAA,EAAc;AAC5E,IAAA,OAAO,WAAA;AAAA,EACX;AAEA,EAAA,IAAI;AAEA,IAAA,MAAM,WAAW,MAAM,KAAA;AAAA,MACnB,4EAAA;AAAA,MACA;AAAA,QACI,OAAA,EAAS,EAAE,QAAA,EAAU,kBAAA,EAAmB;AAAA,QACxC,MAAA,EAAQ,WAAA,CAAY,OAAA,CAAQ,GAAI;AAAA;AACpC,KACJ;AAEA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AACd,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,oBAAA,EAAuB,QAAA,CAAS,MAAM,CAAA,CAAE,CAAA;AAAA,IAC5D;AAEA,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AAEjC,IAAA,IAAI,CAAC,IAAA,CAAK,MAAA,EAAQ,GAAA,EAAK;AACnB,MAAA,MAAM,IAAI,MAAM,wBAAwB,CAAA;AAAA,IAC5C;AAEA,IAAA,WAAA,GAAc;AAAA,MACV,QAAA,EAAU,KAAK,MAAA,CAAO,GAAA;AAAA,MACtB,SAAA,sBAAe,IAAA,EAAK;AAAA,MACpB,MAAA,EAAQ;AAAA,KACZ;AAEA,IAAA,OAAO,WAAA;AAAA,EACX,SAAS,KAAA,EAAO;AAEZ,IAAA,IAAI,WAAA,EAAa;AACb,MAAA,OAAO,WAAA;AAAA,IACX;AAGA,IAAA,OAAO;AAAA,MACH,QAAA,EAAU,GAAA;AAAA;AAAA,MACV,SAAA,sBAAe,IAAA,EAAK;AAAA,MACpB,MAAA,EAAQ;AAAA,KACZ;AAAA,EACJ;AACJ;AAWA,eAAsB,cAAc,QAAA,EAAmC;AACnE,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,WAAA,EAAY;AACvC,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,QAAQ,CAAA,GAAI,GAAA;AAC/B,EAAA,OAAO,GAAA,GAAM,QAAA;AACjB;AAWA,eAAsB,cAAc,GAAA,EAA8B;AAC9D,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,WAAA,EAAY;AACvC,EAAA,MAAM,MAAM,GAAA,GAAM,QAAA;AAClB,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,GAAa,CAAC,CAAA;AACjD;AAWA,eAAsB,mBAAmB,QAAA,EAAmC;AACxE,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,WAAA,EAAY;AACvC,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,QAAQ,CAAA,GAAI,GAAA;AAC/B,EAAA,MAAM,MAAM,GAAA,GAAM,QAAA;AAElB,EAAA,OAAO,CAAA,EAAG,IAAI,OAAA,CAAQ,CAAC,CAAC,CAAA,QAAA,EAAW,GAAA,CAAI,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA,CAAA;AACrD;AAMO,SAAS,eAAA,CAAgB,UAAkB,QAAA,EAIhD;AACE,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,QAAQ,CAAA,GAAI,GAAA;AAC/B,EAAA,MAAM,MAAM,GAAA,GAAM,QAAA;AAElB,EAAA,OAAO;AAAA,IACH,GAAA;AAAA,IACA,GAAA;AAAA,IACA,SAAA,EAAW,CAAA,EAAG,GAAA,CAAI,OAAA,CAAQ,CAAC,CAAC,CAAA,QAAA,EAAW,GAAA,CAAI,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA;AAAA,GACzD;AACJ;AAKO,SAAS,eAAA,GAAwB;AACpC,EAAA,WAAA,GAAc,IAAA;AAClB","file":"index.cjs","sourcesContent":["// Price Conversion Helpers\n// Fetch SOL price and convert between USD/SOL\n\n/**\n * Price data from API\n */\nexport interface PriceData {\n /** SOL price in USD */\n solPrice: number;\n /** Timestamp of price fetch */\n fetchedAt: Date;\n /** Source of price data */\n source: string;\n}\n\n/**\n * Cached price data\n */\nlet cachedPrice: PriceData | null = null;\nconst CACHE_TTL_MS = 60000; // 1 minute cache\n\n/**\n * Fetch current SOL price from CoinGecko\n * Includes caching to avoid rate limits\n * \n * @example\n * ```typescript\n * const { solPrice } = await getSolPrice();\n * console.log(`SOL is $${solPrice}`);\n * ```\n */\nexport async function getSolPrice(): Promise<PriceData> {\n // Return cached price if still valid\n if (cachedPrice && Date.now() - cachedPrice.fetchedAt.getTime() < CACHE_TTL_MS) {\n return cachedPrice;\n }\n\n try {\n // CoinGecko free API (no key required, 10-30 calls/min)\n const response = await fetch(\n 'https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd',\n {\n headers: { 'Accept': 'application/json' },\n signal: AbortSignal.timeout(5000),\n }\n );\n\n if (!response.ok) {\n throw new Error(`Price fetch failed: ${response.status}`);\n }\n\n const data = await response.json() as { solana?: { usd?: number } };\n\n if (!data.solana?.usd) {\n throw new Error('Invalid price response');\n }\n\n cachedPrice = {\n solPrice: data.solana.usd,\n fetchedAt: new Date(),\n source: 'coingecko',\n };\n\n return cachedPrice;\n } catch (error) {\n // If fetch fails and we have stale cache, use it\n if (cachedPrice) {\n return cachedPrice;\n }\n\n // Fallback to a reasonable estimate (update periodically)\n return {\n solPrice: 150, // Fallback price\n fetchedAt: new Date(),\n source: 'fallback',\n };\n }\n}\n\n/**\n * Convert lamports to USD\n * \n * @example\n * ```typescript\n * const usd = await lamportsToUsd(10000000n); // 0.01 SOL\n * console.log(`$${usd.toFixed(2)}`);\n * ```\n */\nexport async function lamportsToUsd(lamports: bigint): Promise<number> {\n const { solPrice } = await getSolPrice();\n const sol = Number(lamports) / 1_000_000_000; // LAMPORTS_PER_SOL\n return sol * solPrice;\n}\n\n/**\n * Convert USD to lamports\n * \n * @example\n * ```typescript\n * const lamports = await usdToLamports(1.50); // $1.50\n * console.log(`${lamports} lamports`);\n * ```\n */\nexport async function usdToLamports(usd: number): Promise<bigint> {\n const { solPrice } = await getSolPrice();\n const sol = usd / solPrice;\n return BigInt(Math.floor(sol * 1_000_000_000));\n}\n\n/**\n * Format a price for display with both SOL and USD\n * \n * @example\n * ```typescript\n * const display = await formatPriceDisplay(10000000n);\n * // Returns: \"0.01 SOL (~$1.50)\"\n * ```\n */\nexport async function formatPriceDisplay(lamports: bigint): Promise<string> {\n const { solPrice } = await getSolPrice();\n const sol = Number(lamports) / 1_000_000_000;\n const usd = sol * solPrice;\n\n return `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`;\n}\n\n/**\n * Synchronous price formatting (requires pre-fetched price)\n * Use this when you've already fetched the price\n */\nexport function formatPriceSync(lamports: bigint, solPrice: number): {\n sol: number;\n usd: number;\n formatted: string;\n} {\n const sol = Number(lamports) / 1_000_000_000;\n const usd = sol * solPrice;\n\n return {\n sol,\n usd,\n formatted: `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`,\n };\n}\n\n/**\n * Clear the price cache (for testing or manual refresh)\n */\nexport function clearPriceCache(): void {\n cachedPrice = null;\n}\n"]}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Price data from API
|
|
3
|
+
*/
|
|
4
|
+
interface PriceData {
|
|
5
|
+
/** SOL price in USD */
|
|
6
|
+
solPrice: number;
|
|
7
|
+
/** Timestamp of price fetch */
|
|
8
|
+
fetchedAt: Date;
|
|
9
|
+
/** Source of price data */
|
|
10
|
+
source: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Fetch current SOL price from CoinGecko
|
|
14
|
+
* Includes caching to avoid rate limits
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const { solPrice } = await getSolPrice();
|
|
19
|
+
* console.log(`SOL is $${solPrice}`);
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
declare function getSolPrice(): Promise<PriceData>;
|
|
23
|
+
/**
|
|
24
|
+
* Convert lamports to USD
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const usd = await lamportsToUsd(10000000n); // 0.01 SOL
|
|
29
|
+
* console.log(`$${usd.toFixed(2)}`);
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
declare function lamportsToUsd(lamports: bigint): Promise<number>;
|
|
33
|
+
/**
|
|
34
|
+
* Convert USD to lamports
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* const lamports = await usdToLamports(1.50); // $1.50
|
|
39
|
+
* console.log(`${lamports} lamports`);
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
declare function usdToLamports(usd: number): Promise<bigint>;
|
|
43
|
+
/**
|
|
44
|
+
* Format a price for display with both SOL and USD
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* const display = await formatPriceDisplay(10000000n);
|
|
49
|
+
* // Returns: "0.01 SOL (~$1.50)"
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
declare function formatPriceDisplay(lamports: bigint): Promise<string>;
|
|
53
|
+
/**
|
|
54
|
+
* Synchronous price formatting (requires pre-fetched price)
|
|
55
|
+
* Use this when you've already fetched the price
|
|
56
|
+
*/
|
|
57
|
+
declare function formatPriceSync(lamports: bigint, solPrice: number): {
|
|
58
|
+
sol: number;
|
|
59
|
+
usd: number;
|
|
60
|
+
formatted: string;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Clear the price cache (for testing or manual refresh)
|
|
64
|
+
*/
|
|
65
|
+
declare function clearPriceCache(): void;
|
|
66
|
+
|
|
67
|
+
export { type PriceData, clearPriceCache, formatPriceDisplay, formatPriceSync, getSolPrice, lamportsToUsd, usdToLamports };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Price data from API
|
|
3
|
+
*/
|
|
4
|
+
interface PriceData {
|
|
5
|
+
/** SOL price in USD */
|
|
6
|
+
solPrice: number;
|
|
7
|
+
/** Timestamp of price fetch */
|
|
8
|
+
fetchedAt: Date;
|
|
9
|
+
/** Source of price data */
|
|
10
|
+
source: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Fetch current SOL price from CoinGecko
|
|
14
|
+
* Includes caching to avoid rate limits
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const { solPrice } = await getSolPrice();
|
|
19
|
+
* console.log(`SOL is $${solPrice}`);
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
declare function getSolPrice(): Promise<PriceData>;
|
|
23
|
+
/**
|
|
24
|
+
* Convert lamports to USD
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const usd = await lamportsToUsd(10000000n); // 0.01 SOL
|
|
29
|
+
* console.log(`$${usd.toFixed(2)}`);
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
declare function lamportsToUsd(lamports: bigint): Promise<number>;
|
|
33
|
+
/**
|
|
34
|
+
* Convert USD to lamports
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* const lamports = await usdToLamports(1.50); // $1.50
|
|
39
|
+
* console.log(`${lamports} lamports`);
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
declare function usdToLamports(usd: number): Promise<bigint>;
|
|
43
|
+
/**
|
|
44
|
+
* Format a price for display with both SOL and USD
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* const display = await formatPriceDisplay(10000000n);
|
|
49
|
+
* // Returns: "0.01 SOL (~$1.50)"
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
declare function formatPriceDisplay(lamports: bigint): Promise<string>;
|
|
53
|
+
/**
|
|
54
|
+
* Synchronous price formatting (requires pre-fetched price)
|
|
55
|
+
* Use this when you've already fetched the price
|
|
56
|
+
*/
|
|
57
|
+
declare function formatPriceSync(lamports: bigint, solPrice: number): {
|
|
58
|
+
sol: number;
|
|
59
|
+
usd: number;
|
|
60
|
+
formatted: string;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Clear the price cache (for testing or manual refresh)
|
|
64
|
+
*/
|
|
65
|
+
declare function clearPriceCache(): void;
|
|
66
|
+
|
|
67
|
+
export { type PriceData, clearPriceCache, formatPriceDisplay, formatPriceSync, getSolPrice, lamportsToUsd, usdToLamports };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// src/pricing/index.ts
|
|
2
|
+
var cachedPrice = null;
|
|
3
|
+
var CACHE_TTL_MS = 6e4;
|
|
4
|
+
async function getSolPrice() {
|
|
5
|
+
if (cachedPrice && Date.now() - cachedPrice.fetchedAt.getTime() < CACHE_TTL_MS) {
|
|
6
|
+
return cachedPrice;
|
|
7
|
+
}
|
|
8
|
+
try {
|
|
9
|
+
const response = await fetch(
|
|
10
|
+
"https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd",
|
|
11
|
+
{
|
|
12
|
+
headers: { "Accept": "application/json" },
|
|
13
|
+
signal: AbortSignal.timeout(5e3)
|
|
14
|
+
}
|
|
15
|
+
);
|
|
16
|
+
if (!response.ok) {
|
|
17
|
+
throw new Error(`Price fetch failed: ${response.status}`);
|
|
18
|
+
}
|
|
19
|
+
const data = await response.json();
|
|
20
|
+
if (!data.solana?.usd) {
|
|
21
|
+
throw new Error("Invalid price response");
|
|
22
|
+
}
|
|
23
|
+
cachedPrice = {
|
|
24
|
+
solPrice: data.solana.usd,
|
|
25
|
+
fetchedAt: /* @__PURE__ */ new Date(),
|
|
26
|
+
source: "coingecko"
|
|
27
|
+
};
|
|
28
|
+
return cachedPrice;
|
|
29
|
+
} catch (error) {
|
|
30
|
+
if (cachedPrice) {
|
|
31
|
+
return cachedPrice;
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
solPrice: 150,
|
|
35
|
+
// Fallback price
|
|
36
|
+
fetchedAt: /* @__PURE__ */ new Date(),
|
|
37
|
+
source: "fallback"
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async function lamportsToUsd(lamports) {
|
|
42
|
+
const { solPrice } = await getSolPrice();
|
|
43
|
+
const sol = Number(lamports) / 1e9;
|
|
44
|
+
return sol * solPrice;
|
|
45
|
+
}
|
|
46
|
+
async function usdToLamports(usd) {
|
|
47
|
+
const { solPrice } = await getSolPrice();
|
|
48
|
+
const sol = usd / solPrice;
|
|
49
|
+
return BigInt(Math.floor(sol * 1e9));
|
|
50
|
+
}
|
|
51
|
+
async function formatPriceDisplay(lamports) {
|
|
52
|
+
const { solPrice } = await getSolPrice();
|
|
53
|
+
const sol = Number(lamports) / 1e9;
|
|
54
|
+
const usd = sol * solPrice;
|
|
55
|
+
return `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`;
|
|
56
|
+
}
|
|
57
|
+
function formatPriceSync(lamports, solPrice) {
|
|
58
|
+
const sol = Number(lamports) / 1e9;
|
|
59
|
+
const usd = sol * solPrice;
|
|
60
|
+
return {
|
|
61
|
+
sol,
|
|
62
|
+
usd,
|
|
63
|
+
formatted: `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function clearPriceCache() {
|
|
67
|
+
cachedPrice = null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export { clearPriceCache, formatPriceDisplay, formatPriceSync, getSolPrice, lamportsToUsd, usdToLamports };
|
|
71
|
+
//# sourceMappingURL=index.js.map
|
|
72
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/pricing/index.ts"],"names":[],"mappings":";AAkBA,IAAI,WAAA,GAAgC,IAAA;AACpC,IAAM,YAAA,GAAe,GAAA;AAYrB,eAAsB,WAAA,GAAkC;AAEpD,EAAA,IAAI,WAAA,IAAe,KAAK,GAAA,EAAI,GAAI,YAAY,SAAA,CAAU,OAAA,KAAY,YAAA,EAAc;AAC5E,IAAA,OAAO,WAAA;AAAA,EACX;AAEA,EAAA,IAAI;AAEA,IAAA,MAAM,WAAW,MAAM,KAAA;AAAA,MACnB,4EAAA;AAAA,MACA;AAAA,QACI,OAAA,EAAS,EAAE,QAAA,EAAU,kBAAA,EAAmB;AAAA,QACxC,MAAA,EAAQ,WAAA,CAAY,OAAA,CAAQ,GAAI;AAAA;AACpC,KACJ;AAEA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AACd,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,oBAAA,EAAuB,QAAA,CAAS,MAAM,CAAA,CAAE,CAAA;AAAA,IAC5D;AAEA,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AAEjC,IAAA,IAAI,CAAC,IAAA,CAAK,MAAA,EAAQ,GAAA,EAAK;AACnB,MAAA,MAAM,IAAI,MAAM,wBAAwB,CAAA;AAAA,IAC5C;AAEA,IAAA,WAAA,GAAc;AAAA,MACV,QAAA,EAAU,KAAK,MAAA,CAAO,GAAA;AAAA,MACtB,SAAA,sBAAe,IAAA,EAAK;AAAA,MACpB,MAAA,EAAQ;AAAA,KACZ;AAEA,IAAA,OAAO,WAAA;AAAA,EACX,SAAS,KAAA,EAAO;AAEZ,IAAA,IAAI,WAAA,EAAa;AACb,MAAA,OAAO,WAAA;AAAA,IACX;AAGA,IAAA,OAAO;AAAA,MACH,QAAA,EAAU,GAAA;AAAA;AAAA,MACV,SAAA,sBAAe,IAAA,EAAK;AAAA,MACpB,MAAA,EAAQ;AAAA,KACZ;AAAA,EACJ;AACJ;AAWA,eAAsB,cAAc,QAAA,EAAmC;AACnE,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,WAAA,EAAY;AACvC,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,QAAQ,CAAA,GAAI,GAAA;AAC/B,EAAA,OAAO,GAAA,GAAM,QAAA;AACjB;AAWA,eAAsB,cAAc,GAAA,EAA8B;AAC9D,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,WAAA,EAAY;AACvC,EAAA,MAAM,MAAM,GAAA,GAAM,QAAA;AAClB,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,GAAa,CAAC,CAAA;AACjD;AAWA,eAAsB,mBAAmB,QAAA,EAAmC;AACxE,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,WAAA,EAAY;AACvC,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,QAAQ,CAAA,GAAI,GAAA;AAC/B,EAAA,MAAM,MAAM,GAAA,GAAM,QAAA;AAElB,EAAA,OAAO,CAAA,EAAG,IAAI,OAAA,CAAQ,CAAC,CAAC,CAAA,QAAA,EAAW,GAAA,CAAI,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA,CAAA;AACrD;AAMO,SAAS,eAAA,CAAgB,UAAkB,QAAA,EAIhD;AACE,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,QAAQ,CAAA,GAAI,GAAA;AAC/B,EAAA,MAAM,MAAM,GAAA,GAAM,QAAA;AAElB,EAAA,OAAO;AAAA,IACH,GAAA;AAAA,IACA,GAAA;AAAA,IACA,SAAA,EAAW,CAAA,EAAG,GAAA,CAAI,OAAA,CAAQ,CAAC,CAAC,CAAA,QAAA,EAAW,GAAA,CAAI,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA;AAAA,GACzD;AACJ;AAKO,SAAS,eAAA,GAAwB;AACpC,EAAA,WAAA,GAAc,IAAA;AAClB","file":"index.js","sourcesContent":["// Price Conversion Helpers\n// Fetch SOL price and convert between USD/SOL\n\n/**\n * Price data from API\n */\nexport interface PriceData {\n /** SOL price in USD */\n solPrice: number;\n /** Timestamp of price fetch */\n fetchedAt: Date;\n /** Source of price data */\n source: string;\n}\n\n/**\n * Cached price data\n */\nlet cachedPrice: PriceData | null = null;\nconst CACHE_TTL_MS = 60000; // 1 minute cache\n\n/**\n * Fetch current SOL price from CoinGecko\n * Includes caching to avoid rate limits\n * \n * @example\n * ```typescript\n * const { solPrice } = await getSolPrice();\n * console.log(`SOL is $${solPrice}`);\n * ```\n */\nexport async function getSolPrice(): Promise<PriceData> {\n // Return cached price if still valid\n if (cachedPrice && Date.now() - cachedPrice.fetchedAt.getTime() < CACHE_TTL_MS) {\n return cachedPrice;\n }\n\n try {\n // CoinGecko free API (no key required, 10-30 calls/min)\n const response = await fetch(\n 'https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd',\n {\n headers: { 'Accept': 'application/json' },\n signal: AbortSignal.timeout(5000),\n }\n );\n\n if (!response.ok) {\n throw new Error(`Price fetch failed: ${response.status}`);\n }\n\n const data = await response.json() as { solana?: { usd?: number } };\n\n if (!data.solana?.usd) {\n throw new Error('Invalid price response');\n }\n\n cachedPrice = {\n solPrice: data.solana.usd,\n fetchedAt: new Date(),\n source: 'coingecko',\n };\n\n return cachedPrice;\n } catch (error) {\n // If fetch fails and we have stale cache, use it\n if (cachedPrice) {\n return cachedPrice;\n }\n\n // Fallback to a reasonable estimate (update periodically)\n return {\n solPrice: 150, // Fallback price\n fetchedAt: new Date(),\n source: 'fallback',\n };\n }\n}\n\n/**\n * Convert lamports to USD\n * \n * @example\n * ```typescript\n * const usd = await lamportsToUsd(10000000n); // 0.01 SOL\n * console.log(`$${usd.toFixed(2)}`);\n * ```\n */\nexport async function lamportsToUsd(lamports: bigint): Promise<number> {\n const { solPrice } = await getSolPrice();\n const sol = Number(lamports) / 1_000_000_000; // LAMPORTS_PER_SOL\n return sol * solPrice;\n}\n\n/**\n * Convert USD to lamports\n * \n * @example\n * ```typescript\n * const lamports = await usdToLamports(1.50); // $1.50\n * console.log(`${lamports} lamports`);\n * ```\n */\nexport async function usdToLamports(usd: number): Promise<bigint> {\n const { solPrice } = await getSolPrice();\n const sol = usd / solPrice;\n return BigInt(Math.floor(sol * 1_000_000_000));\n}\n\n/**\n * Format a price for display with both SOL and USD\n * \n * @example\n * ```typescript\n * const display = await formatPriceDisplay(10000000n);\n * // Returns: \"0.01 SOL (~$1.50)\"\n * ```\n */\nexport async function formatPriceDisplay(lamports: bigint): Promise<string> {\n const { solPrice } = await getSolPrice();\n const sol = Number(lamports) / 1_000_000_000;\n const usd = sol * solPrice;\n\n return `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`;\n}\n\n/**\n * Synchronous price formatting (requires pre-fetched price)\n * Use this when you've already fetched the price\n */\nexport function formatPriceSync(lamports: bigint, solPrice: number): {\n sol: number;\n usd: number;\n formatted: string;\n} {\n const sol = Number(lamports) / 1_000_000_000;\n const usd = sol * solPrice;\n\n return {\n sol,\n usd,\n formatted: `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`,\n };\n}\n\n/**\n * Clear the price cache (for testing or manual refresh)\n */\nexport function clearPriceCache(): void {\n cachedPrice = null;\n}\n"]}
|
package/dist/session/index.d.cts
CHANGED
|
@@ -1 +1,29 @@
|
|
|
1
|
-
|
|
1
|
+
import { a as SessionConfig, S as SessionData, b as SessionValidation } from '../session-D2IoWAWV.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create a new session after successful payment
|
|
5
|
+
* SECURITY: Validates inputs, enforces limits
|
|
6
|
+
*/
|
|
7
|
+
declare function createSession(walletAddress: string, articleId: string, config: SessionConfig, siteWide?: boolean): Promise<{
|
|
8
|
+
token: string;
|
|
9
|
+
session: SessionData;
|
|
10
|
+
}>;
|
|
11
|
+
/**
|
|
12
|
+
* Validate an existing session token
|
|
13
|
+
* SECURITY: jose library handles timing-safe comparison internally
|
|
14
|
+
*/
|
|
15
|
+
declare function validateSession(token: string, secret: string): Promise<SessionValidation>;
|
|
16
|
+
/**
|
|
17
|
+
* Add an article to an existing session
|
|
18
|
+
* SECURITY: Enforces article limit to prevent token bloat
|
|
19
|
+
*/
|
|
20
|
+
declare function addArticleToSession(token: string, articleId: string, secret: string): Promise<{
|
|
21
|
+
token: string;
|
|
22
|
+
session: SessionData;
|
|
23
|
+
} | null>;
|
|
24
|
+
/**
|
|
25
|
+
* Check if an article is unlocked for a session
|
|
26
|
+
*/
|
|
27
|
+
declare function isArticleUnlocked(token: string, articleId: string, secret: string): Promise<boolean>;
|
|
28
|
+
|
|
29
|
+
export { addArticleToSession, createSession, isArticleUnlocked, validateSession };
|
package/dist/session/index.d.ts
CHANGED
|
@@ -1 +1,29 @@
|
|
|
1
|
-
|
|
1
|
+
import { a as SessionConfig, S as SessionData, b as SessionValidation } from '../session-D2IoWAWV.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create a new session after successful payment
|
|
5
|
+
* SECURITY: Validates inputs, enforces limits
|
|
6
|
+
*/
|
|
7
|
+
declare function createSession(walletAddress: string, articleId: string, config: SessionConfig, siteWide?: boolean): Promise<{
|
|
8
|
+
token: string;
|
|
9
|
+
session: SessionData;
|
|
10
|
+
}>;
|
|
11
|
+
/**
|
|
12
|
+
* Validate an existing session token
|
|
13
|
+
* SECURITY: jose library handles timing-safe comparison internally
|
|
14
|
+
*/
|
|
15
|
+
declare function validateSession(token: string, secret: string): Promise<SessionValidation>;
|
|
16
|
+
/**
|
|
17
|
+
* Add an article to an existing session
|
|
18
|
+
* SECURITY: Enforces article limit to prevent token bloat
|
|
19
|
+
*/
|
|
20
|
+
declare function addArticleToSession(token: string, articleId: string, secret: string): Promise<{
|
|
21
|
+
token: string;
|
|
22
|
+
session: SessionData;
|
|
23
|
+
} | null>;
|
|
24
|
+
/**
|
|
25
|
+
* Check if an article is unlocked for a session
|
|
26
|
+
*/
|
|
27
|
+
declare function isArticleUnlocked(token: string, articleId: string, secret: string): Promise<boolean>;
|
|
28
|
+
|
|
29
|
+
export { addArticleToSession, createSession, isArticleUnlocked, validateSession };
|
|
@@ -45,30 +45,4 @@ interface SessionJWTPayload {
|
|
|
45
45
|
exp: number;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
* Create a new session after successful payment
|
|
50
|
-
* SECURITY: Validates inputs, enforces limits
|
|
51
|
-
*/
|
|
52
|
-
declare function createSession(walletAddress: string, articleId: string, config: SessionConfig, siteWide?: boolean): Promise<{
|
|
53
|
-
token: string;
|
|
54
|
-
session: SessionData;
|
|
55
|
-
}>;
|
|
56
|
-
/**
|
|
57
|
-
* Validate an existing session token
|
|
58
|
-
* SECURITY: jose library handles timing-safe comparison internally
|
|
59
|
-
*/
|
|
60
|
-
declare function validateSession(token: string, secret: string): Promise<SessionValidation>;
|
|
61
|
-
/**
|
|
62
|
-
* Add an article to an existing session
|
|
63
|
-
* SECURITY: Enforces article limit to prevent token bloat
|
|
64
|
-
*/
|
|
65
|
-
declare function addArticleToSession(token: string, articleId: string, secret: string): Promise<{
|
|
66
|
-
token: string;
|
|
67
|
-
session: SessionData;
|
|
68
|
-
} | null>;
|
|
69
|
-
/**
|
|
70
|
-
* Check if an article is unlocked for a session
|
|
71
|
-
*/
|
|
72
|
-
declare function isArticleUnlocked(token: string, articleId: string, secret: string): Promise<boolean>;
|
|
73
|
-
|
|
74
|
-
export { type SessionData as S, type SessionConfig as a, type SessionValidation as b, type SessionJWTPayload as c, createSession as d, addArticleToSession as e, isArticleUnlocked as i, validateSession as v };
|
|
48
|
+
export type { SessionData as S, SessionConfig as a, SessionValidation as b, SessionJWTPayload as c };
|
|
@@ -45,30 +45,4 @@ interface SessionJWTPayload {
|
|
|
45
45
|
exp: number;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
* Create a new session after successful payment
|
|
50
|
-
* SECURITY: Validates inputs, enforces limits
|
|
51
|
-
*/
|
|
52
|
-
declare function createSession(walletAddress: string, articleId: string, config: SessionConfig, siteWide?: boolean): Promise<{
|
|
53
|
-
token: string;
|
|
54
|
-
session: SessionData;
|
|
55
|
-
}>;
|
|
56
|
-
/**
|
|
57
|
-
* Validate an existing session token
|
|
58
|
-
* SECURITY: jose library handles timing-safe comparison internally
|
|
59
|
-
*/
|
|
60
|
-
declare function validateSession(token: string, secret: string): Promise<SessionValidation>;
|
|
61
|
-
/**
|
|
62
|
-
* Add an article to an existing session
|
|
63
|
-
* SECURITY: Enforces article limit to prevent token bloat
|
|
64
|
-
*/
|
|
65
|
-
declare function addArticleToSession(token: string, articleId: string, secret: string): Promise<{
|
|
66
|
-
token: string;
|
|
67
|
-
session: SessionData;
|
|
68
|
-
} | null>;
|
|
69
|
-
/**
|
|
70
|
-
* Check if an article is unlocked for a session
|
|
71
|
-
*/
|
|
72
|
-
declare function isArticleUnlocked(token: string, articleId: string, secret: string): Promise<boolean>;
|
|
73
|
-
|
|
74
|
-
export { type SessionData as S, type SessionConfig as a, type SessionValidation as b, type SessionJWTPayload as c, createSession as d, addArticleToSession as e, isArticleUnlocked as i, validateSession as v };
|
|
48
|
+
export type { SessionData as S, SessionConfig as a, SessionValidation as b, SessionJWTPayload as c };
|
package/dist/solana/index.cjs
CHANGED
|
@@ -210,14 +210,207 @@ function solToLamports(sol) {
|
|
|
210
210
|
return BigInt(Math.floor(sol * web3_js.LAMPORTS_PER_SOL));
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
+
// src/types/payment.ts
|
|
214
|
+
var TOKEN_MINTS = {
|
|
215
|
+
/** USDC on mainnet */
|
|
216
|
+
USDC_MAINNET: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
|
|
217
|
+
/** USDC on devnet */
|
|
218
|
+
USDC_DEVNET: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
|
|
219
|
+
/** USDT on mainnet */
|
|
220
|
+
USDT_MAINNET: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// src/solana/spl.ts
|
|
224
|
+
var SIGNATURE_REGEX2 = /^[1-9A-HJ-NP-Za-km-z]{87,88}$/;
|
|
225
|
+
var WALLET_REGEX2 = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
226
|
+
function resolveMintAddress(asset, network) {
|
|
227
|
+
if (asset === "native") return null;
|
|
228
|
+
if (asset === "usdc") {
|
|
229
|
+
return network === "mainnet-beta" ? TOKEN_MINTS.USDC_MAINNET : TOKEN_MINTS.USDC_DEVNET;
|
|
230
|
+
}
|
|
231
|
+
if (asset === "usdt") {
|
|
232
|
+
return TOKEN_MINTS.USDT_MAINNET;
|
|
233
|
+
}
|
|
234
|
+
if (typeof asset === "object" && "mint" in asset) {
|
|
235
|
+
return asset.mint;
|
|
236
|
+
}
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
function getTokenDecimals(asset) {
|
|
240
|
+
if (asset === "native") return 9;
|
|
241
|
+
if (asset === "usdc" || asset === "usdt") return 6;
|
|
242
|
+
if (typeof asset === "object" && "decimals" in asset) {
|
|
243
|
+
return asset.decimals ?? 6;
|
|
244
|
+
}
|
|
245
|
+
return 6;
|
|
246
|
+
}
|
|
247
|
+
function parseSPLTransfer(transaction, expectedRecipient, expectedMint) {
|
|
248
|
+
const instructions = transaction.transaction.message.instructions;
|
|
249
|
+
for (const ix of instructions) {
|
|
250
|
+
if ("parsed" in ix && (ix.program === "spl-token" || ix.program === "spl-token-2022")) {
|
|
251
|
+
const parsed = ix.parsed;
|
|
252
|
+
if (parsed.type === "transfer" || parsed.type === "transferChecked") {
|
|
253
|
+
const amount = parsed.info.amount || parsed.info.tokenAmount?.amount;
|
|
254
|
+
if (amount && parsed.info.destination) {
|
|
255
|
+
return {
|
|
256
|
+
from: parsed.info.authority || parsed.info.source || "",
|
|
257
|
+
to: parsed.info.destination,
|
|
258
|
+
amount: BigInt(amount),
|
|
259
|
+
mint: parsed.info.mint || expectedMint
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (transaction.meta?.innerInstructions) {
|
|
266
|
+
for (const inner of transaction.meta.innerInstructions) {
|
|
267
|
+
for (const ix of inner.instructions) {
|
|
268
|
+
if ("parsed" in ix && (ix.program === "spl-token" || ix.program === "spl-token-2022")) {
|
|
269
|
+
const parsed = ix.parsed;
|
|
270
|
+
if (parsed.type === "transfer" || parsed.type === "transferChecked") {
|
|
271
|
+
const amount = parsed.info.amount || parsed.info.tokenAmount?.amount;
|
|
272
|
+
if (amount) {
|
|
273
|
+
return {
|
|
274
|
+
from: parsed.info.authority || parsed.info.source || "",
|
|
275
|
+
to: parsed.info.destination || "",
|
|
276
|
+
amount: BigInt(amount),
|
|
277
|
+
mint: parsed.info.mint || expectedMint
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (transaction.meta?.postTokenBalances && transaction.meta?.preTokenBalances) {
|
|
286
|
+
const preBalances = transaction.meta.preTokenBalances;
|
|
287
|
+
const postBalances = transaction.meta.postTokenBalances;
|
|
288
|
+
for (const post of postBalances) {
|
|
289
|
+
if (post.mint === expectedMint && post.owner === expectedRecipient) {
|
|
290
|
+
const pre = preBalances.find(
|
|
291
|
+
(p) => p.accountIndex === post.accountIndex
|
|
292
|
+
);
|
|
293
|
+
const preAmount = BigInt(pre?.uiTokenAmount?.amount || "0");
|
|
294
|
+
const postAmount = BigInt(post.uiTokenAmount?.amount || "0");
|
|
295
|
+
const transferred = postAmount - preAmount;
|
|
296
|
+
if (transferred > 0n) {
|
|
297
|
+
return {
|
|
298
|
+
from: "",
|
|
299
|
+
// Can't determine from balance changes
|
|
300
|
+
to: expectedRecipient,
|
|
301
|
+
amount: transferred,
|
|
302
|
+
mint: expectedMint
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
async function verifySPLPayment(params) {
|
|
311
|
+
const {
|
|
312
|
+
signature,
|
|
313
|
+
expectedRecipient,
|
|
314
|
+
expectedAmount,
|
|
315
|
+
asset,
|
|
316
|
+
clientConfig,
|
|
317
|
+
maxAgeSeconds = 300
|
|
318
|
+
} = params;
|
|
319
|
+
if (!SIGNATURE_REGEX2.test(signature)) {
|
|
320
|
+
return { valid: false, confirmed: false, signature, error: "Invalid signature format" };
|
|
321
|
+
}
|
|
322
|
+
if (!WALLET_REGEX2.test(expectedRecipient)) {
|
|
323
|
+
return { valid: false, confirmed: false, signature, error: "Invalid recipient address" };
|
|
324
|
+
}
|
|
325
|
+
const mintAddress = resolveMintAddress(asset, clientConfig.network);
|
|
326
|
+
if (!mintAddress) {
|
|
327
|
+
return { valid: false, confirmed: false, signature, error: "Invalid asset configuration" };
|
|
328
|
+
}
|
|
329
|
+
if (expectedAmount <= 0n) {
|
|
330
|
+
return { valid: false, confirmed: false, signature, error: "Invalid expected amount" };
|
|
331
|
+
}
|
|
332
|
+
const effectiveMaxAge = Math.min(Math.max(maxAgeSeconds, 60), 3600);
|
|
333
|
+
const connection = getConnection(clientConfig);
|
|
334
|
+
try {
|
|
335
|
+
const transaction = await connection.getParsedTransaction(signature, {
|
|
336
|
+
commitment: "confirmed",
|
|
337
|
+
maxSupportedTransactionVersion: 0
|
|
338
|
+
});
|
|
339
|
+
if (!transaction) {
|
|
340
|
+
return { valid: false, confirmed: false, signature, error: "Transaction not found" };
|
|
341
|
+
}
|
|
342
|
+
if (transaction.meta?.err) {
|
|
343
|
+
return { valid: false, confirmed: true, signature, error: "Transaction failed on-chain" };
|
|
344
|
+
}
|
|
345
|
+
if (transaction.blockTime) {
|
|
346
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
347
|
+
if (now - transaction.blockTime > effectiveMaxAge) {
|
|
348
|
+
return { valid: false, confirmed: true, signature, error: "Transaction too old" };
|
|
349
|
+
}
|
|
350
|
+
if (transaction.blockTime > now + 60) {
|
|
351
|
+
return { valid: false, confirmed: true, signature, error: "Invalid transaction time" };
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
const transfer = parseSPLTransfer(transaction, expectedRecipient, mintAddress);
|
|
355
|
+
if (!transfer) {
|
|
356
|
+
return {
|
|
357
|
+
valid: false,
|
|
358
|
+
confirmed: true,
|
|
359
|
+
signature,
|
|
360
|
+
error: "No valid token transfer to recipient found"
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
if (transfer.mint !== mintAddress) {
|
|
364
|
+
return {
|
|
365
|
+
valid: false,
|
|
366
|
+
confirmed: true,
|
|
367
|
+
signature,
|
|
368
|
+
error: "Token mint mismatch"
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
if (transfer.amount < expectedAmount) {
|
|
372
|
+
return {
|
|
373
|
+
valid: false,
|
|
374
|
+
confirmed: true,
|
|
375
|
+
signature,
|
|
376
|
+
from: transfer.from,
|
|
377
|
+
to: transfer.to,
|
|
378
|
+
mint: transfer.mint,
|
|
379
|
+
amount: transfer.amount,
|
|
380
|
+
error: "Insufficient payment amount"
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
return {
|
|
384
|
+
valid: true,
|
|
385
|
+
confirmed: true,
|
|
386
|
+
signature,
|
|
387
|
+
from: transfer.from,
|
|
388
|
+
to: transfer.to,
|
|
389
|
+
mint: transfer.mint,
|
|
390
|
+
amount: transfer.amount,
|
|
391
|
+
blockTime: transaction.blockTime ?? void 0,
|
|
392
|
+
slot: transaction.slot
|
|
393
|
+
};
|
|
394
|
+
} catch {
|
|
395
|
+
return { valid: false, confirmed: false, signature, error: "Verification failed" };
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
function isNativeAsset(asset) {
|
|
399
|
+
return asset === "native";
|
|
400
|
+
}
|
|
401
|
+
|
|
213
402
|
exports.getConnection = getConnection;
|
|
403
|
+
exports.getTokenDecimals = getTokenDecimals;
|
|
214
404
|
exports.getWalletTransactions = getWalletTransactions;
|
|
215
405
|
exports.isMainnet = isMainnet;
|
|
406
|
+
exports.isNativeAsset = isNativeAsset;
|
|
216
407
|
exports.lamportsToSol = lamportsToSol;
|
|
217
408
|
exports.resetConnection = resetConnection;
|
|
409
|
+
exports.resolveMintAddress = resolveMintAddress;
|
|
218
410
|
exports.solToLamports = solToLamports;
|
|
219
411
|
exports.toX402Network = toX402Network;
|
|
220
412
|
exports.verifyPayment = verifyPayment;
|
|
413
|
+
exports.verifySPLPayment = verifySPLPayment;
|
|
221
414
|
exports.waitForConfirmation = waitForConfirmation;
|
|
222
415
|
//# sourceMappingURL=index.cjs.map
|
|
223
416
|
//# sourceMappingURL=index.cjs.map
|