fema-pf-calc 1.0.6 → 1.1.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 +108 -70
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/safeSend/aiExplainer.d.ts +9 -0
- package/dist/safeSend/aiExplainer.d.ts.map +1 -0
- package/dist/safeSend/aiExplainer.js +99 -0
- package/dist/safeSend/aiExplainer.js.map +1 -0
- package/dist/safeSend/autoFix.d.ts +14 -0
- package/dist/safeSend/autoFix.d.ts.map +1 -0
- package/dist/safeSend/autoFix.js +49 -0
- package/dist/safeSend/autoFix.js.map +1 -0
- package/dist/safeSend/connection.d.ts +4 -0
- package/dist/safeSend/connection.d.ts.map +1 -0
- package/dist/safeSend/connection.js +27 -0
- package/dist/safeSend/connection.js.map +1 -0
- package/dist/safeSend/errorClassifier.d.ts +7 -0
- package/dist/safeSend/errorClassifier.d.ts.map +1 -0
- package/dist/safeSend/errorClassifier.js +40 -0
- package/dist/safeSend/errorClassifier.js.map +1 -0
- package/dist/safeSend/index.d.ts +3 -0
- package/dist/safeSend/index.d.ts.map +1 -0
- package/dist/safeSend/index.js +202 -0
- package/dist/safeSend/index.js.map +1 -0
- package/dist/safeSend/types.d.ts +39 -0
- package/dist/safeSend/types.d.ts.map +1 -0
- package/dist/safeSend/types.js +3 -0
- package/dist/safeSend/types.js.map +1 -0
- package/dist/safeSend/utils.d.ts +14 -0
- package/dist/safeSend/utils.d.ts.map +1 -0
- package/dist/safeSend/utils.js +51 -0
- package/dist/safeSend/utils.js.map +1 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -1,24 +1,20 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Fema
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Fema is a TypeScript SDK for Solana with two tools: `estimateFee` calculates the right priority fee based on what the network is actually paying right now, and `safeSend` handles the full transaction lifecycle including simulation, signing, submission, and confirmation.
|
|
4
4
|
|
|
5
5
|
Supports **mainnet-beta** and **devnet**.
|
|
6
6
|
|
|
7
|
-
---
|
|
8
|
-
|
|
9
7
|
## The Problem
|
|
10
8
|
|
|
11
9
|
Every Solana transaction can include a priority fee, a tip paid to validators to land the transaction faster. Set it too low and your transaction gets stuck or dropped. Set it too high and you overpay.
|
|
12
10
|
|
|
13
11
|
The fee you need also depends on what kind of transaction you are sending. A Jupiter swap competes with other Jupiter swaps. An NFT mint competes with other NFT mints. They are not in the same queue, so they should not use the same fee.
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
---
|
|
13
|
+
Fema solves this by scanning recent blocks, building a real fee distribution, and giving you a number you can use directly.
|
|
18
14
|
|
|
19
15
|
## How It Works
|
|
20
16
|
|
|
21
|
-
1.
|
|
17
|
+
1. Fema connects to Solana via a built-in Helius-backed RPC proxy — no API key or RPC setup required
|
|
22
18
|
2. For each transaction in the last 30 blocks it extracts the fee paid in microLamports per compute unit
|
|
23
19
|
3. It sorts these into a distribution and computes percentiles (p25, p50, p75)
|
|
24
20
|
4. If you specify a protocol or pass your transaction, it filters the data to only transactions that used the same programs as yours
|
|
@@ -26,17 +22,13 @@ FEMA solves this by scanning recent blocks, building a real fee distribution, an
|
|
|
26
22
|
|
|
27
23
|
Results are cached for 7 seconds to avoid hammering the RPC on every call.
|
|
28
24
|
|
|
29
|
-
---
|
|
30
|
-
|
|
31
25
|
## Installation
|
|
32
26
|
|
|
33
27
|
```bash
|
|
34
28
|
npm install fema-pf-calc
|
|
35
29
|
```
|
|
36
30
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
## Quick Start
|
|
31
|
+
## estimateFee Quick Start
|
|
40
32
|
|
|
41
33
|
```ts
|
|
42
34
|
import { init, estimateFee } from "fema-pf-calc";
|
|
@@ -56,18 +48,16 @@ const priorityIx = ComputeBudgetProgram.setComputeUnitPrice({
|
|
|
56
48
|
|
|
57
49
|
That is all a developer needs to do. No RPC key, no math, no guessing.
|
|
58
50
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
## Options
|
|
51
|
+
## estimateFee Options
|
|
62
52
|
|
|
63
53
|
```ts
|
|
64
54
|
const result = await estimateFee({
|
|
65
|
-
speed: "fast",
|
|
66
|
-
strategy: "balanced",
|
|
67
|
-
protocol: "jupiter",
|
|
55
|
+
speed: "fast", // "slow" | "medium" | "fast"
|
|
56
|
+
strategy: "balanced", // "cheap" | "balanced" | "aggressive"
|
|
57
|
+
protocol: "jupiter", // see supported protocols below
|
|
68
58
|
transaction: myTransaction, // pass your actual tx for automatic protocol detection
|
|
69
|
-
maxFee: 500_000,
|
|
70
|
-
includeStats: true,
|
|
59
|
+
maxFee: 500_000, // optional cap in microLamports
|
|
60
|
+
includeStats: true, // include full distribution in response
|
|
71
61
|
});
|
|
72
62
|
```
|
|
73
63
|
|
|
@@ -87,11 +77,9 @@ const result = await estimateFee({
|
|
|
87
77
|
| `balanced` | none | Hit the target exactly. Safe default for most use cases. |
|
|
88
78
|
| `aggressive` | +15 percentile points | Slightly overpay to guarantee landing. |
|
|
89
79
|
|
|
90
|
-
---
|
|
91
|
-
|
|
92
80
|
## Protocol-Aware Estimation
|
|
93
81
|
|
|
94
|
-
The most powerful feature of
|
|
82
|
+
The most powerful feature of Fema. When you specify a protocol, Fema filters the fee data to only transactions that used the same programs — giving you a fee based on your actual competition, not the whole network.
|
|
95
83
|
|
|
96
84
|
```ts
|
|
97
85
|
// Jupiter swap
|
|
@@ -110,7 +98,7 @@ const { fee: pumpFee } = await estimateFee({
|
|
|
110
98
|
});
|
|
111
99
|
```
|
|
112
100
|
|
|
113
|
-
Alternatively, pass your actual transaction and
|
|
101
|
+
Alternatively, pass your actual transaction and Fema will detect the protocol automatically:
|
|
114
102
|
|
|
115
103
|
```ts
|
|
116
104
|
const { fee } = await estimateFee({
|
|
@@ -132,17 +120,15 @@ const { fee } = await estimateFee({
|
|
|
132
120
|
| `system` | System Program (SOL transfers) |
|
|
133
121
|
| `token` | Token Program, Token 2022, Associated Token Program |
|
|
134
122
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
## Response
|
|
123
|
+
## estimateFee Response
|
|
138
124
|
|
|
139
125
|
```ts
|
|
140
126
|
{
|
|
141
|
-
fee: 253637,
|
|
142
|
-
congestion: "high",
|
|
143
|
-
percentileUsed: 75,
|
|
144
|
-
sampleSize: 1203,
|
|
145
|
-
programFiltered: true,
|
|
127
|
+
fee: 253637, // microLamports per CU — pass directly to setComputeUnitPrice
|
|
128
|
+
congestion: "high", // "low" | "medium" | "high"
|
|
129
|
+
percentileUsed: 75, // which percentile was used
|
|
130
|
+
sampleSize: 1203, // number of transactions analyzed
|
|
131
|
+
programFiltered: true, // true = fee is based on your protocol's txs specifically
|
|
146
132
|
|
|
147
133
|
// only included when includeStats: true
|
|
148
134
|
stats: {
|
|
@@ -163,36 +149,15 @@ const { fee } = await estimateFee({
|
|
|
163
149
|
|
|
164
150
|
The `fee` value is in **microLamports per compute unit** — the exact unit that `ComputeBudgetProgram.setComputeUnitPrice` expects. No conversion needed.
|
|
165
151
|
|
|
166
|
-
---
|
|
167
|
-
|
|
168
152
|
## Configuration
|
|
169
153
|
|
|
170
154
|
```ts
|
|
171
155
|
init({
|
|
172
|
-
cluster: "mainnet-beta",
|
|
173
|
-
|
|
174
|
-
cacheDurationMs: 7000, // how long to cache results in ms (default: 7000)
|
|
156
|
+
cluster: "mainnet-beta", // "mainnet-beta" | "devnet" (default: "mainnet-beta")
|
|
157
|
+
cacheDurationMs: 7000, // how long to cache results in ms (default: 7000)
|
|
175
158
|
});
|
|
176
159
|
```
|
|
177
160
|
|
|
178
|
-
You can also override the RPC via environment variables:
|
|
179
|
-
|
|
180
|
-
```env
|
|
181
|
-
# Optional: use your own RPC provider (overrides the built-in default)
|
|
182
|
-
SOLANA_RPC_URL=https://your-custom-rpc.com
|
|
183
|
-
|
|
184
|
-
# Or use your own Helius API key
|
|
185
|
-
HELIUS_API_KEY=your-api-key-here
|
|
186
|
-
|
|
187
|
-
# Network: mainnet-beta (default) or devnet
|
|
188
|
-
SOLANA_CLUSTER=mainnet-beta
|
|
189
|
-
|
|
190
|
-
# How long to cache fee data in milliseconds (default: 7000)
|
|
191
|
-
CACHE_DURATION_MS=7000
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
---
|
|
195
|
-
|
|
196
161
|
## Sample Code
|
|
197
162
|
|
|
198
163
|
```ts
|
|
@@ -226,25 +191,98 @@ app.listen(8000, () => {
|
|
|
226
191
|
export default app;
|
|
227
192
|
```
|
|
228
193
|
|
|
229
|
-
|
|
194
|
+
## safeSend
|
|
195
|
+
|
|
196
|
+
`safeSend` handles the full lifecycle of sending a Solana transaction. It simulates before sending, signs with your wallet, submits, and confirms on-chain. It catches errors before they cost SOL, retries automatically when safe to do so, and tells you exactly what went wrong when it isn't.
|
|
197
|
+
|
|
198
|
+
## safeSend Quick Start
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
import { safeSend } from "fema-pf-calc";
|
|
202
|
+
|
|
203
|
+
const result = await safeSend({
|
|
204
|
+
tx: myTransaction,
|
|
205
|
+
wallet: walletAdapter, // any wallet with signTransaction (Phantom, Backpack, etc.)
|
|
206
|
+
network: "mainnet-beta",
|
|
207
|
+
mode: "safe",
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
if (result.success) {
|
|
211
|
+
console.log("Confirmed:", result.signature);
|
|
212
|
+
} else {
|
|
213
|
+
console.log("Failed:", result.error?.type);
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## safeSend Options
|
|
230
218
|
|
|
231
|
-
|
|
219
|
+
```ts
|
|
220
|
+
const result = await safeSend({
|
|
221
|
+
tx: myTransaction, // Transaction | VersionedTransaction
|
|
222
|
+
wallet: walletAdapter, // must implement signTransaction
|
|
223
|
+
network: "mainnet-beta", // "devnet" | "mainnet-beta" (default: "devnet")
|
|
224
|
+
mode: "safe", // "safe" | "fast" (default: "safe")
|
|
225
|
+
ai: true, // enable AI diagnostics on failure (default: false)
|
|
226
|
+
});
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Mode
|
|
230
|
+
|
|
231
|
+
| Mode | What it does |
|
|
232
|
+
| ------ | -------------------------------------------------------------------------- |
|
|
233
|
+
| `safe` | Simulates first, retries up to 3× on recoverable errors, confirms on-chain |
|
|
234
|
+
| `fast` | Simulates first, sends once, returns immediately — no retries |
|
|
235
|
+
|
|
236
|
+
## safeSend Response
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
// Success
|
|
240
|
+
{ success: true, signature: "5Kj...", network: "mainnet-beta", retriesUsed: 0 }
|
|
232
241
|
|
|
242
|
+
// Failure
|
|
243
|
+
{
|
|
244
|
+
success: false,
|
|
245
|
+
network: "mainnet-beta",
|
|
246
|
+
retriesUsed: 2,
|
|
247
|
+
error: {
|
|
248
|
+
type: "INSUFFICIENT_FUNDS",
|
|
249
|
+
logs: ["..."],
|
|
250
|
+
|
|
251
|
+
// only included when ai: true
|
|
252
|
+
explanation: "The sender account does not have enough lamports...",
|
|
253
|
+
suggestedFix: "Fund the account with at least 0.05 SOL before retrying.",
|
|
254
|
+
}
|
|
255
|
+
}
|
|
233
256
|
```
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
257
|
+
|
|
258
|
+
### Error Types
|
|
259
|
+
|
|
260
|
+
| Type | Recoverable | What happened |
|
|
261
|
+
| ---------------------- | ----------- | ------------------------------------------------- |
|
|
262
|
+
| `INSUFFICIENT_FUNDS` | No | Sender cannot cover the transfer + fees |
|
|
263
|
+
| `BLOCKHASH_EXPIRED` | Yes | Blockhash went stale, retried with a fresh one |
|
|
264
|
+
| `COMPUTE_EXCEEDED` | Yes | Compute limit too low, auto-bumped and retried |
|
|
265
|
+
| `INVALID_INSTRUCTION` | No | Instruction references a wrong or unknown program |
|
|
266
|
+
| `NETWORK_FAILURE` | No | Could not reach the RPC |
|
|
267
|
+
| `SEND_FAILED` | No | Transaction was rejected at submission |
|
|
268
|
+
| `SIGNING_REJECTED` | No | Wallet refused to sign |
|
|
269
|
+
| `CONFIRMATION_TIMEOUT` | No | Transaction was sent but not confirmed in time |
|
|
270
|
+
| `MAX_RETRIES_EXCEEDED` | No | Hit the 3-retry limit without landing |
|
|
271
|
+
|
|
272
|
+
## AI Diagnostics
|
|
273
|
+
|
|
274
|
+
Set `ai: true` and add a `GROQ_API_KEY` to your environment to get a plain-English explanation and a concrete fix suggestion on every failure.
|
|
275
|
+
|
|
276
|
+
```ts
|
|
277
|
+
const result = await safeSend({ tx, wallet, network: "mainnet-beta", ai: true });
|
|
278
|
+
|
|
279
|
+
if (!result.success) {
|
|
280
|
+
console.log(result.error?.explanation); // "The compute budget was exceeded..."
|
|
281
|
+
console.log(result.error?.suggestedFix); // "Add ComputeBudgetProgram.setComputeUnitLimit..."
|
|
282
|
+
}
|
|
245
283
|
```
|
|
246
284
|
|
|
247
|
-
|
|
285
|
+
Get a free Groq API key at console.groq.com. Fema uses Llama 3.3-70b under the hood and falls back silently if the key is missing or the API is unavailable.
|
|
248
286
|
|
|
249
287
|
## License
|
|
250
288
|
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { FemaConfig, EstimateFeeOptions, EstimateFeeResult } from "./types";
|
|
|
2
2
|
export type { EstimateFeeOptions, EstimateFeeResult, FemaConfig, FeeSnapshot, FeeStats, FeeEntry, Speed, Strategy, CongestionLevel, SolanaCluster, } from "./types";
|
|
3
3
|
export type { Protocol } from "./protocols";
|
|
4
4
|
export { PROTOCOLS } from "./protocols";
|
|
5
|
+
export { safeSend } from "./safeSend";
|
|
6
|
+
export type { SafeSendOptions, SafeSendResult, SafeSendNetwork, SafeSendMode, SafeSendError, WalletAdapter, AIExplanation, } from "./safeSend/types";
|
|
5
7
|
export declare function init(config: Partial<FemaConfig>): void;
|
|
6
8
|
export declare function estimateFee(options?: EstimateFeeOptions): Promise<EstimateFeeResult>;
|
|
7
9
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,iBAAiB,EAA6B,MAAM,SAAS,CAAC;AAiBvG,YAAY,EACV,kBAAkB,EAClB,iBAAiB,EACjB,UAAU,EACV,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,eAAe,EACf,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,iBAAiB,EAA6B,MAAM,SAAS,CAAC;AAiBvG,YAAY,EACV,kBAAkB,EAClB,iBAAiB,EACjB,UAAU,EACV,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,eAAe,EACf,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAIxC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EACV,eAAe,EACf,cAAc,EACd,eAAe,EACf,YAAY,EACZ,aAAa,EACb,aAAa,EACb,aAAa,GACd,MAAM,kBAAkB,CAAC;AAkB1B,wBAAgB,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI,CAiBtD;AAID,wBAAsB,WAAW,CAC/B,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CAsE5B"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PROTOCOLS = void 0;
|
|
3
|
+
exports.safeSend = exports.PROTOCOLS = void 0;
|
|
4
4
|
exports.init = init;
|
|
5
5
|
exports.estimateFee = estimateFee;
|
|
6
6
|
const config_1 = require("./config");
|
|
@@ -11,6 +11,9 @@ const database_1 = require("./services/database");
|
|
|
11
11
|
const feeCalculator_1 = require("./engine/feeCalculator");
|
|
12
12
|
var protocols_2 = require("./protocols");
|
|
13
13
|
Object.defineProperty(exports, "PROTOCOLS", { enumerable: true, get: function () { return protocols_2.PROTOCOLS; } });
|
|
14
|
+
// ─── safeSend ─────────────────────────────────────────────────────────────────
|
|
15
|
+
var safeSend_1 = require("./safeSend");
|
|
16
|
+
Object.defineProperty(exports, "safeSend", { enumerable: true, get: function () { return safeSend_1.safeSend; } });
|
|
14
17
|
// ─── SDK initialisation ───────────────────────────────────────────────────────
|
|
15
18
|
let _started = false;
|
|
16
19
|
// Deduplicate concurrent fetches — if a block scan is already in flight,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ErrorType, AIExplanation } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Use Groq (Llama 3.3-70b) to analyze a Solana transaction failure.
|
|
4
|
+
* AI layer is post-process only — it never mutates transactions or controls retries.
|
|
5
|
+
*
|
|
6
|
+
* Falls back gracefully when GROQ_API_KEY is not set or the API call fails.
|
|
7
|
+
*/
|
|
8
|
+
export declare function explain(errorType: ErrorType, logs: string[], network: string, attempts: number): Promise<AIExplanation>;
|
|
9
|
+
//# sourceMappingURL=aiExplainer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aiExplainer.d.ts","sourceRoot":"","sources":["../../src/safeSend/aiExplainer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AA+BxD;;;;;GAKG;AACH,wBAAsB,OAAO,CAC3B,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,aAAa,CAAC,CAgCxB"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.explain = explain;
|
|
7
|
+
const groq_sdk_1 = __importDefault(require("groq-sdk"));
|
|
8
|
+
const MODEL = "llama-3.3-70b-versatile";
|
|
9
|
+
const SYSTEM_PROMPT = `You are a Solana blockchain transaction diagnostics expert.
|
|
10
|
+
|
|
11
|
+
You receive failed Solana transaction data — error type, simulation logs, network, and attempt count — and you return a precise diagnosis.
|
|
12
|
+
|
|
13
|
+
Respond ONLY with a valid JSON object in this exact shape:
|
|
14
|
+
{
|
|
15
|
+
"explanation": "<1–2 sentences explaining the root cause>",
|
|
16
|
+
"fixSuggestion": "<specific, actionable steps the developer should take now>",
|
|
17
|
+
"confidenceScore": <float 0.0–1.0>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
Rules:
|
|
21
|
+
- Read the simulation logs carefully. Programs emit custom error codes and messages — use them.
|
|
22
|
+
- Do not repeat the error type label verbatim. Explain WHY it happened.
|
|
23
|
+
- fixSuggestion must be concrete (e.g. "Add ComputeBudgetProgram.setComputeUnitLimit({ units: 400000 }) as the first instruction").
|
|
24
|
+
- confidenceScore reflects how certain you are given the available log detail.
|
|
25
|
+
- No markdown, no prose outside the JSON.`;
|
|
26
|
+
let _client = null;
|
|
27
|
+
function getClient() {
|
|
28
|
+
if (!_client) {
|
|
29
|
+
_client = new groq_sdk_1.default({ apiKey: process.env.GROQ_API_KEY });
|
|
30
|
+
}
|
|
31
|
+
return _client;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Use Groq (Llama 3.3-70b) to analyze a Solana transaction failure.
|
|
35
|
+
* AI layer is post-process only — it never mutates transactions or controls retries.
|
|
36
|
+
*
|
|
37
|
+
* Falls back gracefully when GROQ_API_KEY is not set or the API call fails.
|
|
38
|
+
*/
|
|
39
|
+
async function explain(errorType, logs, network, attempts) {
|
|
40
|
+
if (!process.env.GROQ_API_KEY?.trim()) {
|
|
41
|
+
return noKeyFallback(errorType);
|
|
42
|
+
}
|
|
43
|
+
const userMessage = buildUserMessage(errorType, logs, network, attempts);
|
|
44
|
+
try {
|
|
45
|
+
const client = getClient();
|
|
46
|
+
const response = await client.chat.completions.create({
|
|
47
|
+
model: MODEL,
|
|
48
|
+
temperature: 0.1,
|
|
49
|
+
max_tokens: 512,
|
|
50
|
+
response_format: { type: "json_object" },
|
|
51
|
+
messages: [
|
|
52
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
53
|
+
{ role: "user", content: userMessage },
|
|
54
|
+
],
|
|
55
|
+
});
|
|
56
|
+
const raw = response.choices[0]?.message?.content?.trim() ?? "{}";
|
|
57
|
+
const parsed = JSON.parse(raw);
|
|
58
|
+
return {
|
|
59
|
+
explanation: String(parsed.explanation ?? ""),
|
|
60
|
+
fixSuggestion: String(parsed.fixSuggestion ?? ""),
|
|
61
|
+
confidenceScore: clamp(Number(parsed.confidenceScore ?? 0.5)),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return apiFallback(errorType);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
69
|
+
function buildUserMessage(errorType, logs, network, attempts) {
|
|
70
|
+
const logsBlock = logs.length > 0 ? logs.join("\n") : "(no simulation logs returned)";
|
|
71
|
+
return `Analyze this failed Solana transaction and diagnose the root cause.
|
|
72
|
+
|
|
73
|
+
Error Type : ${errorType}
|
|
74
|
+
Network : ${network}
|
|
75
|
+
Attempts : ${attempts}
|
|
76
|
+
|
|
77
|
+
Simulation Logs:
|
|
78
|
+
\`\`\`
|
|
79
|
+
${logsBlock}
|
|
80
|
+
\`\`\``;
|
|
81
|
+
}
|
|
82
|
+
function noKeyFallback(errorType) {
|
|
83
|
+
return {
|
|
84
|
+
explanation: `Transaction failed with error: ${errorType}. Set the GROQ_API_KEY environment variable to enable AI-powered diagnostics.`,
|
|
85
|
+
fixSuggestion: "Add GROQ_API_KEY to your .env file. Get a free key at console.groq.com.",
|
|
86
|
+
confidenceScore: 0,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function apiFallback(errorType) {
|
|
90
|
+
return {
|
|
91
|
+
explanation: `Transaction failed with error: ${errorType}. AI analysis is temporarily unavailable.`,
|
|
92
|
+
fixSuggestion: "Inspect the simulation logs in the error object for program-specific error codes.",
|
|
93
|
+
confidenceScore: 0.1,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function clamp(n) {
|
|
97
|
+
return Math.min(1, Math.max(0, n));
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=aiExplainer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aiExplainer.js","sourceRoot":"","sources":["../../src/safeSend/aiExplainer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Transaction, VersionedTransaction } from "@solana/web3.js";
|
|
2
|
+
import type { ErrorType } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Apply an automatic fix for a recoverable simulation error.
|
|
5
|
+
*
|
|
6
|
+
* BLOCKHASH_EXPIRED — no-op here; the state machine's Step 2 always refreshes
|
|
7
|
+
* the blockhash at the top of every retry iteration, so no extra RPC call needed.
|
|
8
|
+
*
|
|
9
|
+
* COMPUTE_EXCEEDED — adds or increases the SetComputeUnitLimit instruction.
|
|
10
|
+
* Only possible for legacy Transaction; VersionedTransaction messages are
|
|
11
|
+
* compiled and cannot be modified post-construction.
|
|
12
|
+
*/
|
|
13
|
+
export declare function applyFix(tx: Transaction | VersionedTransaction, errorType: ErrorType): Transaction | VersionedTransaction;
|
|
14
|
+
//# sourceMappingURL=autoFix.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autoFix.d.ts","sourceRoot":"","sources":["../../src/safeSend/autoFix.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,oBAAoB,EAErB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AASzC;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CACtB,EAAE,EAAE,WAAW,GAAG,oBAAoB,EACtC,SAAS,EAAE,SAAS,GACnB,WAAW,GAAG,oBAAoB,CAMpC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyFix = applyFix;
|
|
4
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
5
|
+
const MAX_COMPUTE_UNITS = 1400000;
|
|
6
|
+
const DEFAULT_COMPUTE_UNITS = 400000;
|
|
7
|
+
const COMPUTE_UNIT_SCALE_FACTOR = 1.5;
|
|
8
|
+
// Discriminator byte for ComputeBudgetProgram.setComputeUnitLimit
|
|
9
|
+
const SET_UNIT_LIMIT_DISCRIMINATOR = 2;
|
|
10
|
+
/**
|
|
11
|
+
* Apply an automatic fix for a recoverable simulation error.
|
|
12
|
+
*
|
|
13
|
+
* BLOCKHASH_EXPIRED — no-op here; the state machine's Step 2 always refreshes
|
|
14
|
+
* the blockhash at the top of every retry iteration, so no extra RPC call needed.
|
|
15
|
+
*
|
|
16
|
+
* COMPUTE_EXCEEDED — adds or increases the SetComputeUnitLimit instruction.
|
|
17
|
+
* Only possible for legacy Transaction; VersionedTransaction messages are
|
|
18
|
+
* compiled and cannot be modified post-construction.
|
|
19
|
+
*/
|
|
20
|
+
function applyFix(tx, errorType) {
|
|
21
|
+
if (errorType === "COMPUTE_EXCEEDED") {
|
|
22
|
+
return increaseComputeBudget(tx);
|
|
23
|
+
}
|
|
24
|
+
// BLOCKHASH_EXPIRED: Step 2 handles this — return unchanged.
|
|
25
|
+
return tx;
|
|
26
|
+
}
|
|
27
|
+
function increaseComputeBudget(tx) {
|
|
28
|
+
if (!(tx instanceof web3_js_1.Transaction)) {
|
|
29
|
+
// VersionedTransaction messages are immutable after compilation.
|
|
30
|
+
// Callers should build the transaction with a sufficient compute budget upfront.
|
|
31
|
+
return tx;
|
|
32
|
+
}
|
|
33
|
+
const BUDGET_PROGRAM_ID = web3_js_1.ComputeBudgetProgram.programId.toBase58();
|
|
34
|
+
const existingIdx = tx.instructions.findIndex((ix) => ix.programId.toBase58() === BUDGET_PROGRAM_ID &&
|
|
35
|
+
ix.data.length >= 5 &&
|
|
36
|
+
ix.data[0] === SET_UNIT_LIMIT_DISCRIMINATOR);
|
|
37
|
+
if (existingIdx >= 0) {
|
|
38
|
+
const current = new DataView(tx.instructions[existingIdx].data.buffer, tx.instructions[existingIdx].data.byteOffset).getUint32(1, true);
|
|
39
|
+
const increased = Math.min(Math.round(current * COMPUTE_UNIT_SCALE_FACTOR), MAX_COMPUTE_UNITS);
|
|
40
|
+
tx.instructions[existingIdx] = web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({
|
|
41
|
+
units: increased,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
tx.instructions.unshift(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: DEFAULT_COMPUTE_UNITS }));
|
|
46
|
+
}
|
|
47
|
+
return tx;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=autoFix.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autoFix.js","sourceRoot":"","sources":["../../src/safeSend/autoFix.ts"],"names":[],"mappings":";;AAwBA,4BASC;AAjCD,6CAIyB;AAGzB,MAAM,iBAAiB,GAAG,OAAS,CAAC;AACpC,MAAM,qBAAqB,GAAG,MAAO,CAAC;AACtC,MAAM,yBAAyB,GAAG,GAAG,CAAC;AAEtC,kEAAkE;AAClE,MAAM,4BAA4B,GAAG,CAAC,CAAC;AAEvC;;;;;;;;;GASG;AACH,SAAgB,QAAQ,CACtB,EAAsC,EACtC,SAAoB;IAEpB,IAAI,SAAS,KAAK,kBAAkB,EAAE,CAAC;QACrC,OAAO,qBAAqB,CAAC,EAAE,CAAC,CAAC;IACnC,CAAC;IACD,6DAA6D;IAC7D,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,qBAAqB,CAC5B,EAAsC;IAEtC,IAAI,CAAC,CAAC,EAAE,YAAY,qBAAW,CAAC,EAAE,CAAC;QACjC,iEAAiE;QACjE,iFAAiF;QACjF,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,iBAAiB,GAAG,8BAAoB,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;IAEpE,MAAM,WAAW,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,CAC3C,CAAC,EAAE,EAAE,EAAE,CACL,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,KAAK,iBAAiB;QAC7C,EAAE,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC;QACnB,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,4BAA4B,CAC9C,CAAC;IAEF,IAAI,WAAW,IAAI,CAAC,EAAE,CAAC;QACrB,MAAM,OAAO,GAAG,IAAI,QAAQ,CAC1B,EAAE,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,MAAM,EACxC,EAAE,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,UAAU,CAC7C,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QAErB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CACxB,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,yBAAyB,CAAC,EAC/C,iBAAiB,CAClB,CAAC;QAEF,EAAE,CAAC,YAAY,CAAC,WAAW,CAAC,GAAG,8BAAoB,CAAC,mBAAmB,CAAC;YACtE,KAAK,EAAE,SAAS;SACjB,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,EAAE,CAAC,YAAY,CAAC,OAAO,CACrB,8BAAoB,CAAC,mBAAmB,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,CAC3E,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/safeSend/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAwB/C,wBAAgB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,UAAU,CAElE"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getConnection = getConnection;
|
|
4
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
5
|
+
const HELIUS_ENDPOINTS = {
|
|
6
|
+
"mainnet-beta": "https://mainnet.helius-rpc.com",
|
|
7
|
+
devnet: "https://devnet.helius-rpc.com",
|
|
8
|
+
};
|
|
9
|
+
// Zero-config fallback — FEMA proxy is Helius-backed; no API key required.
|
|
10
|
+
const FEMA_PROXY = "https://fema-rpc-proxy.fema-proxy.workers.dev";
|
|
11
|
+
/**
|
|
12
|
+
* Build the RPC URL for the given network.
|
|
13
|
+
* Uses HELIUS_API_KEY env var when available; falls back to the FEMA proxy.
|
|
14
|
+
* The URL is intentionally not exported — callers get a Connection, not a URL,
|
|
15
|
+
* to prevent the API key from leaking into application code.
|
|
16
|
+
*/
|
|
17
|
+
function buildRpcUrl(network) {
|
|
18
|
+
const apiKey = process.env.HELIUS_API_KEY?.trim();
|
|
19
|
+
if (apiKey) {
|
|
20
|
+
return `${HELIUS_ENDPOINTS[network]}/?api-key=${apiKey}`;
|
|
21
|
+
}
|
|
22
|
+
return FEMA_PROXY;
|
|
23
|
+
}
|
|
24
|
+
function getConnection(network) {
|
|
25
|
+
return new web3_js_1.Connection(buildRpcUrl(network), "confirmed");
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=connection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/safeSend/connection.ts"],"names":[],"mappings":";;AAyBA,sCAEC;AA3BD,6CAA6C;AAG7C,MAAM,gBAAgB,GAAoC;IACxD,cAAc,EAAE,gCAAgC;IAChD,MAAM,EAAE,+BAA+B;CACxC,CAAC;AAEF,2EAA2E;AAC3E,MAAM,UAAU,GAAG,+CAA+C,CAAC;AAEnE;;;;;GAKG;AACH,SAAS,WAAW,CAAC,OAAwB;IAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC;IAClD,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,GAAG,gBAAgB,CAAC,OAAO,CAAC,aAAa,MAAM,EAAE,CAAC;IAC3D,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAgB,aAAa,CAAC,OAAwB;IACpD,OAAO,IAAI,oBAAU,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,WAAW,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SimulationError } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Pure function — converts raw simulation logs into a structured SimulationError.
|
|
4
|
+
* Order matters: more specific patterns are checked before UNKNOWN.
|
|
5
|
+
*/
|
|
6
|
+
export declare function classifySimulationError(logs: string[]): SimulationError;
|
|
7
|
+
//# sourceMappingURL=errorClassifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errorClassifier.d.ts","sourceRoot":"","sources":["../../src/safeSend/errorClassifier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,eAAe,EAAE,MAAM,SAAS,CAAC;AAK1D;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,eAAe,CAGvE"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.classifySimulationError = classifySimulationError;
|
|
4
|
+
// Error types that can be automatically recovered from.
|
|
5
|
+
const RECOVERABLE = new Set(["BLOCKHASH_EXPIRED", "COMPUTE_EXCEEDED"]);
|
|
6
|
+
/**
|
|
7
|
+
* Pure function — converts raw simulation logs into a structured SimulationError.
|
|
8
|
+
* Order matters: more specific patterns are checked before UNKNOWN.
|
|
9
|
+
*/
|
|
10
|
+
function classifySimulationError(logs) {
|
|
11
|
+
const type = detectType(logs);
|
|
12
|
+
return { type, logs, recoverable: RECOVERABLE.has(type) };
|
|
13
|
+
}
|
|
14
|
+
function detectType(logs) {
|
|
15
|
+
const text = logs.join(" ").toLowerCase();
|
|
16
|
+
if (text.includes("insufficient funds") ||
|
|
17
|
+
text.includes("insufficient lamports") ||
|
|
18
|
+
text.includes("0x1") // common Solana insufficient-funds error code
|
|
19
|
+
) {
|
|
20
|
+
return "INSUFFICIENT_FUNDS";
|
|
21
|
+
}
|
|
22
|
+
if (text.includes("blockhash not found") ||
|
|
23
|
+
text.includes("blockhash expired") ||
|
|
24
|
+
text.includes("recent blockhash") ||
|
|
25
|
+
text.includes("blockhashnot")) {
|
|
26
|
+
return "BLOCKHASH_EXPIRED";
|
|
27
|
+
}
|
|
28
|
+
if (text.includes("exceeded") &&
|
|
29
|
+
(text.includes("compute") || text.includes("budget") || text.includes("units"))) {
|
|
30
|
+
return "COMPUTE_EXCEEDED";
|
|
31
|
+
}
|
|
32
|
+
if (text.includes("invalid instruction") ||
|
|
33
|
+
text.includes("invalid program id") ||
|
|
34
|
+
text.includes("incorrect program id") ||
|
|
35
|
+
text.includes("unknown instruction")) {
|
|
36
|
+
return "INVALID_INSTRUCTION";
|
|
37
|
+
}
|
|
38
|
+
return "UNKNOWN";
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=errorClassifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errorClassifier.js","sourceRoot":"","sources":["../../src/safeSend/errorClassifier.ts"],"names":[],"mappings":";;AASA,0DAGC;AAVD,wDAAwD;AACxD,MAAM,WAAW,GAAG,IAAI,GAAG,CAAY,CAAC,mBAAmB,EAAE,kBAAkB,CAAC,CAAC,CAAC;AAElF;;;GAGG;AACH,SAAgB,uBAAuB,CAAC,IAAc;IACpD,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC9B,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;AAC5D,CAAC;AAED,SAAS,UAAU,CAAC,IAAc;IAChC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IAE1C,IACE,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAAC;QACnC,IAAI,CAAC,QAAQ,CAAC,uBAAuB,CAAC;QACtC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,8CAA8C;MACnE,CAAC;QACD,OAAO,oBAAoB,CAAC;IAC9B,CAAC;IAED,IACE,IAAI,CAAC,QAAQ,CAAC,qBAAqB,CAAC;QACpC,IAAI,CAAC,QAAQ,CAAC,mBAAmB,CAAC;QAClC,IAAI,CAAC,QAAQ,CAAC,kBAAkB,CAAC;QACjC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,EAC7B,CAAC;QACD,OAAO,mBAAmB,CAAC;IAC7B,CAAC;IAED,IACE,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC;QACzB,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAC/E,CAAC;QACD,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IAED,IACE,IAAI,CAAC,QAAQ,CAAC,qBAAqB,CAAC;QACpC,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAAC;QACnC,IAAI,CAAC,QAAQ,CAAC,sBAAsB,CAAC;QACrC,IAAI,CAAC,QAAQ,CAAC,qBAAqB,CAAC,EACpC,CAAC;QACD,OAAO,qBAAqB,CAAC;IAC/B,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/safeSend/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EAMf,MAAM,SAAS,CAAC;AAejB,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAsJhF"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.safeSend = safeSend;
|
|
4
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
5
|
+
const connection_1 = require("./connection");
|
|
6
|
+
const errorClassifier_1 = require("./errorClassifier");
|
|
7
|
+
const autoFix_1 = require("./autoFix");
|
|
8
|
+
const aiExplainer_1 = require("./aiExplainer");
|
|
9
|
+
const utils_1 = require("./utils");
|
|
10
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
11
|
+
const MAX_RETRIES = 3;
|
|
12
|
+
const CONFIRMATION_TIMEOUT_MS = 60000;
|
|
13
|
+
const CONFIRMATION_POLL_MS = 2000;
|
|
14
|
+
// ─── Public API ───────────────────────────────────────────────────────────────
|
|
15
|
+
async function safeSend(options) {
|
|
16
|
+
// ── STEP 1: INPUT NORMALIZATION ─────────────────────────────────────────────
|
|
17
|
+
const validationError = (0, utils_1.validateInput)(options);
|
|
18
|
+
if (validationError) {
|
|
19
|
+
return buildFailure(options.network ?? "devnet", 0, {
|
|
20
|
+
type: "INVALID_INPUT",
|
|
21
|
+
explanation: validationError,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
const { tx, wallet, network = "devnet", mode = "safe", ai = false } = options;
|
|
25
|
+
const connection = (0, connection_1.getConnection)(network);
|
|
26
|
+
let workingTx = tx;
|
|
27
|
+
let retriesUsed = 0;
|
|
28
|
+
// ── STATE MACHINE ───────────────────────────────────────────────────────────
|
|
29
|
+
for (;;) {
|
|
30
|
+
if (retriesUsed > MAX_RETRIES) {
|
|
31
|
+
return buildFailure(network, retriesUsed, { type: "MAX_RETRIES_EXCEEDED" });
|
|
32
|
+
}
|
|
33
|
+
// ── STEP 2: GET LATEST BLOCKHASH ──────────────────────────────────────────
|
|
34
|
+
let lastValidBlockHeight = 0;
|
|
35
|
+
try {
|
|
36
|
+
const bh = await connection.getLatestBlockhash("confirmed");
|
|
37
|
+
lastValidBlockHeight = bh.lastValidBlockHeight;
|
|
38
|
+
attachBlockhash(workingTx, bh.blockhash, bh.lastValidBlockHeight);
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
const msg = (0, utils_1.sanitizeMessage)(err.message);
|
|
42
|
+
if (mode === "fast" || retriesUsed >= MAX_RETRIES) {
|
|
43
|
+
const aiFields = await fetchAIFields(ai, "NETWORK_FAILURE", [msg], network, retriesUsed + 1);
|
|
44
|
+
return buildFailure(network, retriesUsed, {
|
|
45
|
+
type: "NETWORK_FAILURE",
|
|
46
|
+
logs: [msg],
|
|
47
|
+
...aiFields,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
retriesUsed++;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
// ── STEP 3: SIMULATION (CRITICAL GATE) ───────────────────────────────────
|
|
54
|
+
let simError = null;
|
|
55
|
+
try {
|
|
56
|
+
const { err, logs } = await runSimulation(connection, workingTx);
|
|
57
|
+
if (err !== null) {
|
|
58
|
+
simError = (0, errorClassifier_1.classifySimulationError)((0, utils_1.truncateLogs)(logs ?? []));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
const msg = (0, utils_1.sanitizeMessage)(err.message);
|
|
63
|
+
simError = { type: "UNKNOWN", logs: [msg], recoverable: false };
|
|
64
|
+
}
|
|
65
|
+
// ── STEP 3A: FAILURE CLASSIFICATION ──────────────────────────────────────
|
|
66
|
+
if (simError !== null) {
|
|
67
|
+
if (!simError.recoverable || retriesUsed >= MAX_RETRIES) {
|
|
68
|
+
const aiFields = await fetchAIFields(ai, simError.type, simError.logs, network, retriesUsed + 1);
|
|
69
|
+
return buildFailure(network, retriesUsed, {
|
|
70
|
+
type: simError.type,
|
|
71
|
+
logs: simError.logs,
|
|
72
|
+
...aiFields,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
// ── STEP 4: AUTO-FIX ENGINE — re-enter loop at Step 3 ──────────────────
|
|
76
|
+
workingTx = (0, autoFix_1.applyFix)(workingTx, simError.type);
|
|
77
|
+
retriesUsed++;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
// ── STEP 5: WALLET SIGNING (USER CONTROLLED — never bypassed) ────────────
|
|
81
|
+
let signedTx;
|
|
82
|
+
try {
|
|
83
|
+
signedTx = await wallet.signTransaction(workingTx);
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
const msg = (0, utils_1.sanitizeMessage)(err.message);
|
|
87
|
+
const aiFields = await fetchAIFields(ai, "SIGNING_REJECTED", [msg], network, retriesUsed + 1);
|
|
88
|
+
return buildFailure(network, retriesUsed, {
|
|
89
|
+
type: "SIGNING_REJECTED",
|
|
90
|
+
logs: [msg],
|
|
91
|
+
...aiFields,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
// ── STEP 6: TRANSACTION SUBMISSION ───────────────────────────────────────
|
|
95
|
+
let signature;
|
|
96
|
+
try {
|
|
97
|
+
const raw = serializeTx(signedTx);
|
|
98
|
+
signature = await connection.sendRawTransaction(raw, {
|
|
99
|
+
skipPreflight: true, // safe — already simulated in Step 3
|
|
100
|
+
preflightCommitment: "confirmed",
|
|
101
|
+
maxRetries: 2,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
const msg = (0, utils_1.sanitizeMessage)(err.message);
|
|
106
|
+
if (mode === "safe" && retriesUsed < MAX_RETRIES) {
|
|
107
|
+
retriesUsed++;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const aiFields = await fetchAIFields(ai, "SEND_FAILED", [msg], network, retriesUsed + 1);
|
|
111
|
+
return buildFailure(network, retriesUsed, {
|
|
112
|
+
type: "SEND_FAILED",
|
|
113
|
+
logs: [msg],
|
|
114
|
+
...aiFields,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
// ── STEP 7: CONFIRMATION LOOP ─────────────────────────────────────────────
|
|
118
|
+
const confirmResult = await waitForConfirmation(connection, signature, lastValidBlockHeight);
|
|
119
|
+
if (confirmResult === "confirmed") {
|
|
120
|
+
return { success: true, signature, network, retriesUsed };
|
|
121
|
+
}
|
|
122
|
+
// ── STEP 8: RETRY ENGINE (SAFE ONLY) ─────────────────────────────────────
|
|
123
|
+
// Only retry when the blockhash provably expired — transaction is dead,
|
|
124
|
+
// no risk of duplicating a confirmed send.
|
|
125
|
+
if (confirmResult === "expired" && mode === "safe" && retriesUsed < MAX_RETRIES) {
|
|
126
|
+
retriesUsed++;
|
|
127
|
+
continue; // Step 2 attaches a fresh blockhash
|
|
128
|
+
}
|
|
129
|
+
// ── STEP 9: FINAL RESPONSE ────────────────────────────────────────────────
|
|
130
|
+
const finalType = confirmResult === "expired" ? "BLOCKHASH_EXPIRED" : "CONFIRMATION_TIMEOUT";
|
|
131
|
+
const aiFields = await fetchAIFields(ai, finalType, [], network, retriesUsed + 1);
|
|
132
|
+
return buildFailure(network, retriesUsed, {
|
|
133
|
+
type: finalType,
|
|
134
|
+
...(aiFields.explanation && {
|
|
135
|
+
explanation: `Transaction (${signature}) could not be confirmed. ${aiFields.explanation}`,
|
|
136
|
+
suggestedFix: aiFields.suggestedFix,
|
|
137
|
+
}),
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// ─── Internal helpers ─────────────────────────────────────────────────────────
|
|
142
|
+
/**
|
|
143
|
+
* Fetch AI explanation fields when ai=true.
|
|
144
|
+
* Returns an empty object when ai=false so callers can spread it safely.
|
|
145
|
+
*/
|
|
146
|
+
async function fetchAIFields(ai, type, logs, network, attempts) {
|
|
147
|
+
if (!ai)
|
|
148
|
+
return {};
|
|
149
|
+
const data = await (0, aiExplainer_1.explain)(type, logs, network, attempts);
|
|
150
|
+
return { explanation: data.explanation, suggestedFix: data.fixSuggestion };
|
|
151
|
+
}
|
|
152
|
+
function attachBlockhash(tx, blockhash, lastValidBlockHeight) {
|
|
153
|
+
if (tx instanceof web3_js_1.Transaction) {
|
|
154
|
+
tx.recentBlockhash = blockhash;
|
|
155
|
+
tx.lastValidBlockHeight = lastValidBlockHeight;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
tx.message.recentBlockhash = blockhash;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function serializeTx(tx) {
|
|
162
|
+
if (tx instanceof web3_js_1.Transaction)
|
|
163
|
+
return tx.serialize();
|
|
164
|
+
return tx.serialize();
|
|
165
|
+
}
|
|
166
|
+
async function runSimulation(connection, tx) {
|
|
167
|
+
// Cast required: web3.js has different overloads for Transaction vs VersionedTransaction.
|
|
168
|
+
// sigVerify: false allows simulation of an unsigned transaction.
|
|
169
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
170
|
+
const res = await connection.simulateTransaction(tx, tx instanceof web3_js_1.Transaction ? undefined : { sigVerify: false });
|
|
171
|
+
return { err: res.value.err ?? null, logs: res.value.logs ?? null };
|
|
172
|
+
}
|
|
173
|
+
async function waitForConfirmation(connection, signature, lastValidBlockHeight) {
|
|
174
|
+
const deadline = Date.now() + CONFIRMATION_TIMEOUT_MS;
|
|
175
|
+
while (Date.now() < deadline) {
|
|
176
|
+
try {
|
|
177
|
+
const blockHeight = await connection.getBlockHeight("confirmed");
|
|
178
|
+
if (blockHeight > lastValidBlockHeight)
|
|
179
|
+
return "expired";
|
|
180
|
+
const statuses = await connection.getSignatureStatuses([signature]);
|
|
181
|
+
const status = statuses?.value?.[0];
|
|
182
|
+
if (status?.err)
|
|
183
|
+
return "timeout"; // on-chain execution error — not retriable
|
|
184
|
+
if (status?.confirmationStatus === "confirmed" ||
|
|
185
|
+
status?.confirmationStatus === "finalized") {
|
|
186
|
+
return "confirmed";
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
// Transient RPC error — keep polling until deadline.
|
|
191
|
+
}
|
|
192
|
+
await sleep(CONFIRMATION_POLL_MS);
|
|
193
|
+
}
|
|
194
|
+
return "timeout";
|
|
195
|
+
}
|
|
196
|
+
function buildFailure(network, retriesUsed, error) {
|
|
197
|
+
return { success: false, network, retriesUsed, error };
|
|
198
|
+
}
|
|
199
|
+
function sleep(ms) {
|
|
200
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/safeSend/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Transaction, VersionedTransaction } from "@solana/web3.js";
|
|
2
|
+
export type SafeSendNetwork = "devnet" | "mainnet-beta";
|
|
3
|
+
export type SafeSendMode = "safe" | "fast";
|
|
4
|
+
export type ErrorType = "INSUFFICIENT_FUNDS" | "BLOCKHASH_EXPIRED" | "COMPUTE_EXCEEDED" | "INVALID_INSTRUCTION" | "NETWORK_FAILURE" | "SEND_FAILED" | "SIGNING_REJECTED" | "CONFIRMATION_TIMEOUT" | "INVALID_INPUT" | "MAX_RETRIES_EXCEEDED" | "UNKNOWN";
|
|
5
|
+
export interface WalletAdapter {
|
|
6
|
+
signTransaction<T extends Transaction | VersionedTransaction>(tx: T): Promise<T>;
|
|
7
|
+
}
|
|
8
|
+
export interface SafeSendOptions {
|
|
9
|
+
tx: Transaction | VersionedTransaction;
|
|
10
|
+
wallet: WalletAdapter;
|
|
11
|
+
network?: SafeSendNetwork;
|
|
12
|
+
mode?: SafeSendMode;
|
|
13
|
+
ai?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface SafeSendError {
|
|
16
|
+
type: ErrorType | string;
|
|
17
|
+
logs?: string[];
|
|
18
|
+
explanation?: string;
|
|
19
|
+
suggestedFix?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface SafeSendResult {
|
|
22
|
+
success: boolean;
|
|
23
|
+
signature?: string;
|
|
24
|
+
network: SafeSendNetwork;
|
|
25
|
+
retriesUsed: number;
|
|
26
|
+
error?: SafeSendError;
|
|
27
|
+
}
|
|
28
|
+
export interface SimulationError {
|
|
29
|
+
type: ErrorType;
|
|
30
|
+
logs: string[];
|
|
31
|
+
recoverable: boolean;
|
|
32
|
+
}
|
|
33
|
+
export interface AIExplanation {
|
|
34
|
+
explanation: string;
|
|
35
|
+
fixSuggestion: string;
|
|
36
|
+
confidenceScore: number;
|
|
37
|
+
}
|
|
38
|
+
export type ConfirmationStatus = "confirmed" | "expired" | "timeout";
|
|
39
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/safeSend/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAIpE,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,cAAc,CAAC;AACxD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,CAAC;AAI3C,MAAM,MAAM,SAAS,GACjB,oBAAoB,GACpB,mBAAmB,GACnB,kBAAkB,GAClB,qBAAqB,GACrB,iBAAiB,GACjB,aAAa,GACb,kBAAkB,GAClB,sBAAsB,GACtB,eAAe,GACf,sBAAsB,GACtB,SAAS,CAAC;AAId,MAAM,WAAW,aAAa;IAC5B,eAAe,CAAC,CAAC,SAAS,WAAW,GAAG,oBAAoB,EAAE,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAClF;AAID,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,WAAW,GAAG,oBAAoB,CAAC;IACvC,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,EAAE,CAAC,EAAE,OAAO,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB;AAID,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/safeSend/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SafeSendOptions } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Strip API keys from RPC URLs that may appear in error messages.
|
|
4
|
+
* Helius and similar providers embed the key as ?api-key=<value>.
|
|
5
|
+
*/
|
|
6
|
+
export declare function sanitizeMessage(message: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Truncate simulation logs to prevent oversized payloads reaching the caller.
|
|
9
|
+
* Keeps the first N lines which contain the most useful diagnostic info.
|
|
10
|
+
*/
|
|
11
|
+
export declare function truncateLogs(logs: string[], maxLines?: number): string[];
|
|
12
|
+
/** Validate safeSend inputs. Returns an error string or null if valid. */
|
|
13
|
+
export declare function validateInput(options: SafeSendOptions): string | null;
|
|
14
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/safeSend/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,QAAQ,SAAK,GAAG,MAAM,EAAE,CAMpE;AAED,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,GAAG,IAAI,CAiCrE"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sanitizeMessage = sanitizeMessage;
|
|
4
|
+
exports.truncateLogs = truncateLogs;
|
|
5
|
+
exports.validateInput = validateInput;
|
|
6
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
7
|
+
/**
|
|
8
|
+
* Strip API keys from RPC URLs that may appear in error messages.
|
|
9
|
+
* Helius and similar providers embed the key as ?api-key=<value>.
|
|
10
|
+
*/
|
|
11
|
+
function sanitizeMessage(message) {
|
|
12
|
+
return message.replace(/([?&]api-key=)[^&\s"']*/gi, "$1***");
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Truncate simulation logs to prevent oversized payloads reaching the caller.
|
|
16
|
+
* Keeps the first N lines which contain the most useful diagnostic info.
|
|
17
|
+
*/
|
|
18
|
+
function truncateLogs(logs, maxLines = 20) {
|
|
19
|
+
if (logs.length <= maxLines)
|
|
20
|
+
return logs;
|
|
21
|
+
return [
|
|
22
|
+
...logs.slice(0, maxLines),
|
|
23
|
+
`... (${logs.length - maxLines} more lines truncated)`,
|
|
24
|
+
];
|
|
25
|
+
}
|
|
26
|
+
/** Validate safeSend inputs. Returns an error string or null if valid. */
|
|
27
|
+
function validateInput(options) {
|
|
28
|
+
if (!options.tx)
|
|
29
|
+
return "options.tx is required";
|
|
30
|
+
if (!(options.tx instanceof web3_js_1.Transaction) &&
|
|
31
|
+
!(options.tx instanceof web3_js_1.VersionedTransaction)) {
|
|
32
|
+
return "options.tx must be a Transaction or VersionedTransaction instance";
|
|
33
|
+
}
|
|
34
|
+
if (!options.wallet)
|
|
35
|
+
return "options.wallet is required";
|
|
36
|
+
if (typeof options.wallet.signTransaction !== "function") {
|
|
37
|
+
return "options.wallet must implement signTransaction()";
|
|
38
|
+
}
|
|
39
|
+
if (options.network !== undefined &&
|
|
40
|
+
options.network !== "devnet" &&
|
|
41
|
+
options.network !== "mainnet-beta") {
|
|
42
|
+
return 'options.network must be "devnet" or "mainnet-beta"';
|
|
43
|
+
}
|
|
44
|
+
if (options.mode !== undefined &&
|
|
45
|
+
options.mode !== "safe" &&
|
|
46
|
+
options.mode !== "fast") {
|
|
47
|
+
return 'options.mode must be "safe" or "fast"';
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/safeSend/utils.ts"],"names":[],"mappings":";;AAOA,0CAEC;AAMD,oCAMC;AAGD,sCAiCC;AAzDD,6CAAoE;AAGpE;;;GAGG;AACH,SAAgB,eAAe,CAAC,OAAe;IAC7C,OAAO,OAAO,CAAC,OAAO,CAAC,2BAA2B,EAAE,OAAO,CAAC,CAAC;AAC/D,CAAC;AAED;;;GAGG;AACH,SAAgB,YAAY,CAAC,IAAc,EAAE,QAAQ,GAAG,EAAE;IACxD,IAAI,IAAI,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAC;IACzC,OAAO;QACL,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC;QAC1B,QAAQ,IAAI,CAAC,MAAM,GAAG,QAAQ,wBAAwB;KACvD,CAAC;AACJ,CAAC;AAED,0EAA0E;AAC1E,SAAgB,aAAa,CAAC,OAAwB;IACpD,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,OAAO,wBAAwB,CAAC;IAEjD,IACE,CAAC,CAAC,OAAO,CAAC,EAAE,YAAY,qBAAW,CAAC;QACpC,CAAC,CAAC,OAAO,CAAC,EAAE,YAAY,8BAAoB,CAAC,EAC7C,CAAC;QACD,OAAO,mEAAmE,CAAC;IAC7E,CAAC;IAED,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,OAAO,4BAA4B,CAAC;IAEzD,IAAI,OAAO,OAAO,CAAC,MAAM,CAAC,eAAe,KAAK,UAAU,EAAE,CAAC;QACzD,OAAO,iDAAiD,CAAC;IAC3D,CAAC;IAED,IACE,OAAO,CAAC,OAAO,KAAK,SAAS;QAC7B,OAAO,CAAC,OAAO,KAAK,QAAQ;QAC5B,OAAO,CAAC,OAAO,KAAK,cAAc,EAClC,CAAC;QACD,OAAO,oDAAoD,CAAC;IAC9D,CAAC;IAED,IACE,OAAO,CAAC,IAAI,KAAK,SAAS;QAC1B,OAAO,CAAC,IAAI,KAAK,MAAM;QACvB,OAAO,CAAC,IAAI,KAAK,MAAM,EACvB,CAAC;QACD,OAAO,uCAAuC,CAAC;IACjD,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fema-pf-calc",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Intelligent Solana priority fee estimation engine",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"build": "tsc",
|
|
23
23
|
"prepublishOnly": "npm run build",
|
|
24
24
|
"dev": "ts-node test-fees.ts",
|
|
25
|
+
"test:safesend": "ts-node test-safesend.ts",
|
|
25
26
|
"lint": "eslint src/**/*.ts"
|
|
26
27
|
},
|
|
27
28
|
"keywords": [
|
|
@@ -41,5 +42,8 @@
|
|
|
41
42
|
"express": "^4.21.2",
|
|
42
43
|
"ts-node": "^10.9.2",
|
|
43
44
|
"typescript": "^5.8.3"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"groq-sdk": "^1.1.2"
|
|
44
48
|
}
|
|
45
49
|
}
|