@alleyboss/micropay-solana-x402-paywall 3.2.1 → 3.3.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 +30 -0
- package/dist/agent/index.cjs +2 -2
- package/dist/agent/index.d.cts +1 -1
- package/dist/agent/index.d.ts +1 -1
- package/dist/agent/index.js +2 -2
- package/dist/client/index.cjs +220 -0
- package/dist/client/index.d.cts +101 -1
- package/dist/client/index.d.ts +101 -1
- package/dist/client/index.js +218 -2
- package/dist/index.cjs +278 -154
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +276 -156
- package/dist/next/index.cjs +86 -37
- package/dist/next/index.js +86 -37
- package/dist/pricing/index.cjs +54 -38
- package/dist/pricing/index.d.cts +8 -9
- package/dist/pricing/index.d.ts +8 -9
- package/dist/pricing/index.js +54 -38
- package/package.json +1 -1
package/dist/next/index.cjs
CHANGED
|
@@ -13,49 +13,66 @@ var LocalSvmFacilitator = class {
|
|
|
13
13
|
caipFamily = "solana:*";
|
|
14
14
|
connection;
|
|
15
15
|
constructor(rpcUrl) {
|
|
16
|
+
console.log("[LocalSvmFacilitator] Initialized with RPC:", rpcUrl);
|
|
16
17
|
this.connection = new web3_js.Connection(rpcUrl, "confirmed");
|
|
17
18
|
}
|
|
18
19
|
/**
|
|
19
20
|
* Get supported payment kinds
|
|
20
21
|
* Mocking the response of the /supported endpoint
|
|
21
22
|
*/
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
// Network Constants - CAIP-2 format for x402 v2
|
|
24
|
+
NETWORKS = {
|
|
25
|
+
DEVNET: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
|
|
26
|
+
MAINNET: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
|
|
27
|
+
};
|
|
28
|
+
// Dummy fee payer address (System Program) - not used in local verification
|
|
29
|
+
// but required by x402 protocol for supported kinds
|
|
30
|
+
DUMMY_FEE_PAYER = "11111111111111111111111111111111";
|
|
31
|
+
/**
|
|
32
|
+
* Get supported payment kinds
|
|
33
|
+
* Returns x402 v2 compatible response with CAIP-2 network identifiers
|
|
34
|
+
*/
|
|
35
|
+
async getSupported(_extensionKeys = []) {
|
|
36
|
+
console.log("[LocalSvmFacilitator] getSupported called");
|
|
37
|
+
const supported = {
|
|
24
38
|
kinds: [
|
|
25
39
|
{
|
|
26
|
-
x402Version:
|
|
40
|
+
x402Version: 2,
|
|
27
41
|
scheme: "exact",
|
|
28
|
-
network:
|
|
29
|
-
|
|
30
|
-
extra: {}
|
|
42
|
+
network: this.NETWORKS.DEVNET,
|
|
43
|
+
extra: { feePayer: this.DUMMY_FEE_PAYER }
|
|
31
44
|
},
|
|
32
45
|
{
|
|
33
|
-
x402Version:
|
|
46
|
+
x402Version: 2,
|
|
34
47
|
scheme: "exact",
|
|
35
|
-
network:
|
|
36
|
-
|
|
37
|
-
extra: {}
|
|
48
|
+
network: this.NETWORKS.MAINNET,
|
|
49
|
+
extra: { feePayer: this.DUMMY_FEE_PAYER }
|
|
38
50
|
}
|
|
39
51
|
],
|
|
40
52
|
extensions: [],
|
|
41
53
|
signers: {
|
|
42
|
-
"solana
|
|
43
|
-
"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": []
|
|
54
|
+
"solana:*": [this.DUMMY_FEE_PAYER]
|
|
44
55
|
}
|
|
45
56
|
};
|
|
57
|
+
console.log("[LocalSvmFacilitator] Returning supported:", JSON.stringify(supported));
|
|
58
|
+
return supported;
|
|
46
59
|
}
|
|
47
60
|
/**
|
|
48
61
|
* Get mechanism-specific extra data
|
|
49
62
|
*/
|
|
50
|
-
getExtra(
|
|
63
|
+
getExtra(_network) {
|
|
51
64
|
return void 0;
|
|
52
65
|
}
|
|
53
66
|
/**
|
|
54
67
|
* Get default signers (not used for local verification usually, but required by interface)
|
|
55
68
|
*/
|
|
56
|
-
getSigners(
|
|
69
|
+
getSigners(_network) {
|
|
57
70
|
return [];
|
|
58
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Enable debug logging (disable in production)
|
|
74
|
+
*/
|
|
75
|
+
debug = process.env.NODE_ENV === "development";
|
|
59
76
|
/**
|
|
60
77
|
* Verify a payment on-chain
|
|
61
78
|
*/
|
|
@@ -68,57 +85,89 @@ var LocalSvmFacilitator = class {
|
|
|
68
85
|
const payTo = requirements.payTo;
|
|
69
86
|
const amountVal = requirements.amount || requirements.maxAmountRequired || "0";
|
|
70
87
|
const requiredAmount = BigInt(amountVal);
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const tx = await this.
|
|
75
|
-
maxSupportedTransactionVersion: 0,
|
|
76
|
-
commitment: "confirmed"
|
|
77
|
-
});
|
|
88
|
+
if (this.debug) {
|
|
89
|
+
console.log(`[LocalSvmFacilitator] Verifying tx: ${signature.slice(0, 8)}...`);
|
|
90
|
+
}
|
|
91
|
+
const tx = await this.fetchTransactionWithRetry(signature, 3);
|
|
78
92
|
if (!tx) {
|
|
79
|
-
console.error("[LocalSvmFacilitator] Transaction not found or not confirmed");
|
|
80
93
|
return { isValid: false, invalidReason: "Transaction not found or not confirmed" };
|
|
81
94
|
}
|
|
82
|
-
console.log("[LocalSvmFacilitator] Transaction found. Parsing instructions...");
|
|
83
95
|
const instructions = tx.transaction.message.instructions;
|
|
84
96
|
let paidAmount = 0n;
|
|
85
97
|
let payer = void 0;
|
|
86
98
|
for (const ix of instructions) {
|
|
87
99
|
if ("program" in ix && ix.program === "system") {
|
|
88
100
|
const parsed = ix.parsed;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
101
|
+
if (parsed?.type === "transfer" && parsed.info?.destination === payTo) {
|
|
102
|
+
paidAmount += BigInt(parsed.info.lamports);
|
|
103
|
+
if (!payer) payer = parsed.info.source;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if ("program" in ix && (ix.program === "spl-token" || ix.program === "spl-token-2022")) {
|
|
107
|
+
const parsed = ix.parsed;
|
|
108
|
+
if (parsed?.type === "transferChecked" || parsed?.type === "transfer") {
|
|
109
|
+
if (this.debug) {
|
|
110
|
+
console.log(`[LocalSvmFacilitator] Found SPL transfer`);
|
|
96
111
|
}
|
|
97
112
|
}
|
|
98
113
|
}
|
|
99
114
|
}
|
|
100
|
-
console.log(`[LocalSvmFacilitator] Total Paid Correctly: ${paidAmount}`);
|
|
101
115
|
if (paidAmount >= requiredAmount) {
|
|
102
|
-
|
|
116
|
+
if (this.debug) {
|
|
117
|
+
console.log(`[LocalSvmFacilitator] Verification SUCCESS for tx: ${signature.slice(0, 8)}...`);
|
|
118
|
+
}
|
|
103
119
|
return {
|
|
104
120
|
isValid: true,
|
|
105
121
|
payer: payer || tx.transaction.message.accountKeys[0].pubkey.toBase58()
|
|
106
122
|
};
|
|
107
123
|
}
|
|
108
|
-
console.error(`[LocalSvmFacilitator] Verification FAILED. Paid: ${paidAmount}, Required: ${requiredAmount}`);
|
|
109
124
|
return {
|
|
110
125
|
isValid: false,
|
|
111
|
-
invalidReason:
|
|
126
|
+
invalidReason: "Insufficient payment amount",
|
|
112
127
|
payer
|
|
113
128
|
};
|
|
114
129
|
} catch (error) {
|
|
115
|
-
|
|
130
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
131
|
+
if (this.debug) {
|
|
132
|
+
console.error("[LocalSvmFacilitator] Verify error:", errorMessage);
|
|
133
|
+
}
|
|
116
134
|
throw new types.VerifyError(500, {
|
|
117
135
|
isValid: false,
|
|
118
|
-
invalidReason:
|
|
136
|
+
invalidReason: errorMessage
|
|
119
137
|
});
|
|
120
138
|
}
|
|
121
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* Fetch transaction with exponential backoff retry
|
|
142
|
+
*/
|
|
143
|
+
async fetchTransactionWithRetry(signature, maxRetries = 3) {
|
|
144
|
+
let lastError;
|
|
145
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
146
|
+
try {
|
|
147
|
+
const tx = await this.connection.getParsedTransaction(signature, {
|
|
148
|
+
maxSupportedTransactionVersion: 0,
|
|
149
|
+
commitment: "confirmed"
|
|
150
|
+
});
|
|
151
|
+
if (tx) return tx;
|
|
152
|
+
if (attempt < maxRetries - 1) {
|
|
153
|
+
await this.sleep(Math.pow(2, attempt) * 1e3);
|
|
154
|
+
}
|
|
155
|
+
} catch (error) {
|
|
156
|
+
lastError = error instanceof Error ? error : new Error("RPC error");
|
|
157
|
+
if (attempt < maxRetries - 1) {
|
|
158
|
+
await this.sleep(Math.pow(2, attempt) * 1e3);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (lastError) throw lastError;
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Sleep helper
|
|
167
|
+
*/
|
|
168
|
+
sleep(ms) {
|
|
169
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
170
|
+
}
|
|
122
171
|
/**
|
|
123
172
|
* Settle a payment (not applicable for direct chain verification, usually)
|
|
124
173
|
* But we must implement it. For 'exact', settlement is just verification + finality.
|
|
@@ -160,7 +209,7 @@ function createX402Middleware(config) {
|
|
|
160
209
|
accepts: {
|
|
161
210
|
scheme: "exact",
|
|
162
211
|
payTo: config.walletAddress,
|
|
163
|
-
|
|
212
|
+
price: config.price?.toString() || "0",
|
|
164
213
|
network: config.network === "mainnet-beta" ? "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" : "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"
|
|
165
214
|
}
|
|
166
215
|
};
|
package/dist/next/index.js
CHANGED
|
@@ -12,49 +12,66 @@ var LocalSvmFacilitator = class {
|
|
|
12
12
|
caipFamily = "solana:*";
|
|
13
13
|
connection;
|
|
14
14
|
constructor(rpcUrl) {
|
|
15
|
+
console.log("[LocalSvmFacilitator] Initialized with RPC:", rpcUrl);
|
|
15
16
|
this.connection = new Connection(rpcUrl, "confirmed");
|
|
16
17
|
}
|
|
17
18
|
/**
|
|
18
19
|
* Get supported payment kinds
|
|
19
20
|
* Mocking the response of the /supported endpoint
|
|
20
21
|
*/
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
// Network Constants - CAIP-2 format for x402 v2
|
|
23
|
+
NETWORKS = {
|
|
24
|
+
DEVNET: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
|
|
25
|
+
MAINNET: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
|
|
26
|
+
};
|
|
27
|
+
// Dummy fee payer address (System Program) - not used in local verification
|
|
28
|
+
// but required by x402 protocol for supported kinds
|
|
29
|
+
DUMMY_FEE_PAYER = "11111111111111111111111111111111";
|
|
30
|
+
/**
|
|
31
|
+
* Get supported payment kinds
|
|
32
|
+
* Returns x402 v2 compatible response with CAIP-2 network identifiers
|
|
33
|
+
*/
|
|
34
|
+
async getSupported(_extensionKeys = []) {
|
|
35
|
+
console.log("[LocalSvmFacilitator] getSupported called");
|
|
36
|
+
const supported = {
|
|
23
37
|
kinds: [
|
|
24
38
|
{
|
|
25
|
-
x402Version:
|
|
39
|
+
x402Version: 2,
|
|
26
40
|
scheme: "exact",
|
|
27
|
-
network:
|
|
28
|
-
|
|
29
|
-
extra: {}
|
|
41
|
+
network: this.NETWORKS.DEVNET,
|
|
42
|
+
extra: { feePayer: this.DUMMY_FEE_PAYER }
|
|
30
43
|
},
|
|
31
44
|
{
|
|
32
|
-
x402Version:
|
|
45
|
+
x402Version: 2,
|
|
33
46
|
scheme: "exact",
|
|
34
|
-
network:
|
|
35
|
-
|
|
36
|
-
extra: {}
|
|
47
|
+
network: this.NETWORKS.MAINNET,
|
|
48
|
+
extra: { feePayer: this.DUMMY_FEE_PAYER }
|
|
37
49
|
}
|
|
38
50
|
],
|
|
39
51
|
extensions: [],
|
|
40
52
|
signers: {
|
|
41
|
-
"solana
|
|
42
|
-
"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": []
|
|
53
|
+
"solana:*": [this.DUMMY_FEE_PAYER]
|
|
43
54
|
}
|
|
44
55
|
};
|
|
56
|
+
console.log("[LocalSvmFacilitator] Returning supported:", JSON.stringify(supported));
|
|
57
|
+
return supported;
|
|
45
58
|
}
|
|
46
59
|
/**
|
|
47
60
|
* Get mechanism-specific extra data
|
|
48
61
|
*/
|
|
49
|
-
getExtra(
|
|
62
|
+
getExtra(_network) {
|
|
50
63
|
return void 0;
|
|
51
64
|
}
|
|
52
65
|
/**
|
|
53
66
|
* Get default signers (not used for local verification usually, but required by interface)
|
|
54
67
|
*/
|
|
55
|
-
getSigners(
|
|
68
|
+
getSigners(_network) {
|
|
56
69
|
return [];
|
|
57
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Enable debug logging (disable in production)
|
|
73
|
+
*/
|
|
74
|
+
debug = process.env.NODE_ENV === "development";
|
|
58
75
|
/**
|
|
59
76
|
* Verify a payment on-chain
|
|
60
77
|
*/
|
|
@@ -67,57 +84,89 @@ var LocalSvmFacilitator = class {
|
|
|
67
84
|
const payTo = requirements.payTo;
|
|
68
85
|
const amountVal = requirements.amount || requirements.maxAmountRequired || "0";
|
|
69
86
|
const requiredAmount = BigInt(amountVal);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const tx = await this.
|
|
74
|
-
maxSupportedTransactionVersion: 0,
|
|
75
|
-
commitment: "confirmed"
|
|
76
|
-
});
|
|
87
|
+
if (this.debug) {
|
|
88
|
+
console.log(`[LocalSvmFacilitator] Verifying tx: ${signature.slice(0, 8)}...`);
|
|
89
|
+
}
|
|
90
|
+
const tx = await this.fetchTransactionWithRetry(signature, 3);
|
|
77
91
|
if (!tx) {
|
|
78
|
-
console.error("[LocalSvmFacilitator] Transaction not found or not confirmed");
|
|
79
92
|
return { isValid: false, invalidReason: "Transaction not found or not confirmed" };
|
|
80
93
|
}
|
|
81
|
-
console.log("[LocalSvmFacilitator] Transaction found. Parsing instructions...");
|
|
82
94
|
const instructions = tx.transaction.message.instructions;
|
|
83
95
|
let paidAmount = 0n;
|
|
84
96
|
let payer = void 0;
|
|
85
97
|
for (const ix of instructions) {
|
|
86
98
|
if ("program" in ix && ix.program === "system") {
|
|
87
99
|
const parsed = ix.parsed;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
100
|
+
if (parsed?.type === "transfer" && parsed.info?.destination === payTo) {
|
|
101
|
+
paidAmount += BigInt(parsed.info.lamports);
|
|
102
|
+
if (!payer) payer = parsed.info.source;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if ("program" in ix && (ix.program === "spl-token" || ix.program === "spl-token-2022")) {
|
|
106
|
+
const parsed = ix.parsed;
|
|
107
|
+
if (parsed?.type === "transferChecked" || parsed?.type === "transfer") {
|
|
108
|
+
if (this.debug) {
|
|
109
|
+
console.log(`[LocalSvmFacilitator] Found SPL transfer`);
|
|
95
110
|
}
|
|
96
111
|
}
|
|
97
112
|
}
|
|
98
113
|
}
|
|
99
|
-
console.log(`[LocalSvmFacilitator] Total Paid Correctly: ${paidAmount}`);
|
|
100
114
|
if (paidAmount >= requiredAmount) {
|
|
101
|
-
|
|
115
|
+
if (this.debug) {
|
|
116
|
+
console.log(`[LocalSvmFacilitator] Verification SUCCESS for tx: ${signature.slice(0, 8)}...`);
|
|
117
|
+
}
|
|
102
118
|
return {
|
|
103
119
|
isValid: true,
|
|
104
120
|
payer: payer || tx.transaction.message.accountKeys[0].pubkey.toBase58()
|
|
105
121
|
};
|
|
106
122
|
}
|
|
107
|
-
console.error(`[LocalSvmFacilitator] Verification FAILED. Paid: ${paidAmount}, Required: ${requiredAmount}`);
|
|
108
123
|
return {
|
|
109
124
|
isValid: false,
|
|
110
|
-
invalidReason:
|
|
125
|
+
invalidReason: "Insufficient payment amount",
|
|
111
126
|
payer
|
|
112
127
|
};
|
|
113
128
|
} catch (error) {
|
|
114
|
-
|
|
129
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
130
|
+
if (this.debug) {
|
|
131
|
+
console.error("[LocalSvmFacilitator] Verify error:", errorMessage);
|
|
132
|
+
}
|
|
115
133
|
throw new VerifyError(500, {
|
|
116
134
|
isValid: false,
|
|
117
|
-
invalidReason:
|
|
135
|
+
invalidReason: errorMessage
|
|
118
136
|
});
|
|
119
137
|
}
|
|
120
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Fetch transaction with exponential backoff retry
|
|
141
|
+
*/
|
|
142
|
+
async fetchTransactionWithRetry(signature, maxRetries = 3) {
|
|
143
|
+
let lastError;
|
|
144
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
145
|
+
try {
|
|
146
|
+
const tx = await this.connection.getParsedTransaction(signature, {
|
|
147
|
+
maxSupportedTransactionVersion: 0,
|
|
148
|
+
commitment: "confirmed"
|
|
149
|
+
});
|
|
150
|
+
if (tx) return tx;
|
|
151
|
+
if (attempt < maxRetries - 1) {
|
|
152
|
+
await this.sleep(Math.pow(2, attempt) * 1e3);
|
|
153
|
+
}
|
|
154
|
+
} catch (error) {
|
|
155
|
+
lastError = error instanceof Error ? error : new Error("RPC error");
|
|
156
|
+
if (attempt < maxRetries - 1) {
|
|
157
|
+
await this.sleep(Math.pow(2, attempt) * 1e3);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (lastError) throw lastError;
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Sleep helper
|
|
166
|
+
*/
|
|
167
|
+
sleep(ms) {
|
|
168
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
169
|
+
}
|
|
121
170
|
/**
|
|
122
171
|
* Settle a payment (not applicable for direct chain verification, usually)
|
|
123
172
|
* But we must implement it. For 'exact', settlement is just verification + finality.
|
|
@@ -159,7 +208,7 @@ function createX402Middleware(config) {
|
|
|
159
208
|
accepts: {
|
|
160
209
|
scheme: "exact",
|
|
161
210
|
payTo: config.walletAddress,
|
|
162
|
-
|
|
211
|
+
price: config.price?.toString() || "0",
|
|
163
212
|
network: config.network === "mainnet-beta" ? "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" : "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"
|
|
164
213
|
}
|
|
165
214
|
};
|
package/dist/pricing/index.cjs
CHANGED
|
@@ -6,12 +6,10 @@ function lamportsToSol(lamports) {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
// src/pricing/index.ts
|
|
9
|
-
var
|
|
10
|
-
var
|
|
11
|
-
var lastProviderIndex = -1;
|
|
9
|
+
var priceCache = null;
|
|
10
|
+
var currentConfig = {};
|
|
12
11
|
function configurePricing(newConfig) {
|
|
13
|
-
|
|
14
|
-
cachedPrice = null;
|
|
12
|
+
currentConfig = { ...currentConfig, ...newConfig };
|
|
15
13
|
}
|
|
16
14
|
var PROVIDERS = [
|
|
17
15
|
{
|
|
@@ -51,56 +49,75 @@ async function fetchFromProvider(provider, timeout) {
|
|
|
51
49
|
if (!price || price <= 0) {
|
|
52
50
|
throw new Error("Invalid price");
|
|
53
51
|
}
|
|
54
|
-
return price;
|
|
52
|
+
return { price, source: provider.name };
|
|
55
53
|
} finally {
|
|
56
54
|
clearTimeout(timeoutId);
|
|
57
55
|
}
|
|
58
56
|
}
|
|
57
|
+
async function fetchPriceParallel(timeout) {
|
|
58
|
+
const promises = PROVIDERS.map(
|
|
59
|
+
(provider) => fetchFromProvider(provider, timeout).catch(() => null)
|
|
60
|
+
);
|
|
61
|
+
const results = await Promise.all(promises);
|
|
62
|
+
const validResult = results.find((r) => r !== null);
|
|
63
|
+
if (validResult) {
|
|
64
|
+
return validResult;
|
|
65
|
+
}
|
|
66
|
+
throw new Error("All providers failed");
|
|
67
|
+
}
|
|
68
|
+
async function fetchPriceSequential(timeout) {
|
|
69
|
+
for (const provider of PROVIDERS) {
|
|
70
|
+
try {
|
|
71
|
+
return await fetchFromProvider(provider, timeout);
|
|
72
|
+
} catch {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
throw new Error("All providers failed");
|
|
77
|
+
}
|
|
59
78
|
async function getSolPrice() {
|
|
60
|
-
const cacheTTL =
|
|
61
|
-
const timeout =
|
|
62
|
-
|
|
63
|
-
|
|
79
|
+
const cacheTTL = currentConfig.cacheTTL ?? 6e4;
|
|
80
|
+
const timeout = currentConfig.timeout ?? 3e3;
|
|
81
|
+
const useParallel = currentConfig.parallelFetch ?? true;
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
if (priceCache && now - priceCache.timestamp < cacheTTL) {
|
|
84
|
+
return priceCache.data;
|
|
64
85
|
}
|
|
65
|
-
if (
|
|
86
|
+
if (currentConfig.customProvider) {
|
|
66
87
|
try {
|
|
67
|
-
const price = await
|
|
88
|
+
const price = await currentConfig.customProvider();
|
|
68
89
|
if (price > 0) {
|
|
69
|
-
|
|
90
|
+
const data = {
|
|
70
91
|
solPrice: price,
|
|
71
92
|
fetchedAt: /* @__PURE__ */ new Date(),
|
|
72
93
|
source: "custom"
|
|
73
94
|
};
|
|
74
|
-
|
|
95
|
+
priceCache = { data, timestamp: now };
|
|
96
|
+
return data;
|
|
75
97
|
}
|
|
76
98
|
} catch {
|
|
77
99
|
}
|
|
78
100
|
}
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
101
|
+
try {
|
|
102
|
+
const result = useParallel ? await fetchPriceParallel(timeout) : await fetchPriceSequential(timeout);
|
|
103
|
+
const data = {
|
|
104
|
+
solPrice: result.price,
|
|
105
|
+
fetchedAt: /* @__PURE__ */ new Date(),
|
|
106
|
+
source: result.source
|
|
107
|
+
};
|
|
108
|
+
priceCache = { data, timestamp: now };
|
|
109
|
+
return data;
|
|
110
|
+
} catch {
|
|
111
|
+
if (priceCache) {
|
|
112
|
+
return {
|
|
113
|
+
...priceCache.data,
|
|
114
|
+
source: `${priceCache.data.source} (stale)`
|
|
89
115
|
};
|
|
90
|
-
return cachedPrice;
|
|
91
|
-
} catch {
|
|
92
|
-
continue;
|
|
93
116
|
}
|
|
117
|
+
throw new Error(
|
|
118
|
+
"Failed to fetch SOL price from all providers. Configure a custom provider or ensure network connectivity."
|
|
119
|
+
);
|
|
94
120
|
}
|
|
95
|
-
if (cachedPrice) {
|
|
96
|
-
return {
|
|
97
|
-
...cachedPrice,
|
|
98
|
-
source: `${cachedPrice.source} (stale)`
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
throw new Error(
|
|
102
|
-
"Failed to fetch SOL price from all providers. Configure a custom provider or ensure network connectivity."
|
|
103
|
-
);
|
|
104
121
|
}
|
|
105
122
|
async function lamportsToUsd(lamports) {
|
|
106
123
|
const { solPrice } = await getSolPrice();
|
|
@@ -128,8 +145,7 @@ function formatPriceSync(lamports, solPrice) {
|
|
|
128
145
|
};
|
|
129
146
|
}
|
|
130
147
|
function clearPriceCache() {
|
|
131
|
-
|
|
132
|
-
lastProviderIndex = -1;
|
|
148
|
+
priceCache = null;
|
|
133
149
|
}
|
|
134
150
|
function getProviders() {
|
|
135
151
|
return PROVIDERS.map((p) => ({ name: p.name, url: p.url }));
|
package/dist/pricing/index.d.cts
CHANGED
|
@@ -23,8 +23,10 @@ interface PriceConfig {
|
|
|
23
23
|
customProvider?: CustomPriceProvider;
|
|
24
24
|
/** Cache TTL in milliseconds (default: 60000) */
|
|
25
25
|
cacheTTL?: number;
|
|
26
|
-
/** Request timeout in milliseconds (default:
|
|
26
|
+
/** Request timeout in milliseconds (default: 3000) */
|
|
27
27
|
timeout?: number;
|
|
28
|
+
/** Use parallel fetching (default: true, faster but more network calls) */
|
|
29
|
+
parallelFetch?: boolean;
|
|
28
30
|
}
|
|
29
31
|
/**
|
|
30
32
|
* Configure price fetching
|
|
@@ -39,19 +41,16 @@ interface PriceConfig {
|
|
|
39
41
|
* },
|
|
40
42
|
* });
|
|
41
43
|
*
|
|
42
|
-
* // Or
|
|
43
|
-
* configurePricing({ cacheTTL: 30000 });
|
|
44
|
+
* // Or adjust settings
|
|
45
|
+
* configurePricing({ cacheTTL: 30000, parallelFetch: true });
|
|
44
46
|
* ```
|
|
45
47
|
*/
|
|
46
48
|
declare function configurePricing(newConfig: PriceConfig): void;
|
|
47
49
|
/**
|
|
48
|
-
* Get SOL price with multi-provider
|
|
50
|
+
* Get SOL price with multi-provider support
|
|
49
51
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* 2. Binance (backup #1)
|
|
53
|
-
* 3. CoinGecko (backup #2)
|
|
54
|
-
* 4. Kraken (backup #3)
|
|
52
|
+
* Default behavior: Parallel fetch (all providers race, fastest wins)
|
|
53
|
+
* This is faster and more reliable in serverless environments.
|
|
55
54
|
*
|
|
56
55
|
* @example
|
|
57
56
|
* ```typescript
|
package/dist/pricing/index.d.ts
CHANGED
|
@@ -23,8 +23,10 @@ interface PriceConfig {
|
|
|
23
23
|
customProvider?: CustomPriceProvider;
|
|
24
24
|
/** Cache TTL in milliseconds (default: 60000) */
|
|
25
25
|
cacheTTL?: number;
|
|
26
|
-
/** Request timeout in milliseconds (default:
|
|
26
|
+
/** Request timeout in milliseconds (default: 3000) */
|
|
27
27
|
timeout?: number;
|
|
28
|
+
/** Use parallel fetching (default: true, faster but more network calls) */
|
|
29
|
+
parallelFetch?: boolean;
|
|
28
30
|
}
|
|
29
31
|
/**
|
|
30
32
|
* Configure price fetching
|
|
@@ -39,19 +41,16 @@ interface PriceConfig {
|
|
|
39
41
|
* },
|
|
40
42
|
* });
|
|
41
43
|
*
|
|
42
|
-
* // Or
|
|
43
|
-
* configurePricing({ cacheTTL: 30000 });
|
|
44
|
+
* // Or adjust settings
|
|
45
|
+
* configurePricing({ cacheTTL: 30000, parallelFetch: true });
|
|
44
46
|
* ```
|
|
45
47
|
*/
|
|
46
48
|
declare function configurePricing(newConfig: PriceConfig): void;
|
|
47
49
|
/**
|
|
48
|
-
* Get SOL price with multi-provider
|
|
50
|
+
* Get SOL price with multi-provider support
|
|
49
51
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* 2. Binance (backup #1)
|
|
53
|
-
* 3. CoinGecko (backup #2)
|
|
54
|
-
* 4. Kraken (backup #3)
|
|
52
|
+
* Default behavior: Parallel fetch (all providers race, fastest wins)
|
|
53
|
+
* This is faster and more reliable in serverless environments.
|
|
55
54
|
*
|
|
56
55
|
* @example
|
|
57
56
|
* ```typescript
|