solami 0.1.7

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 ADDED
@@ -0,0 +1,201 @@
1
+ # solami
2
+
3
+ TypeScript SDK for [solami.fast](https://solami.fast) - Solana RPC, gRPC streaming, WebSocket subscriptions, and SWQOS transaction landing.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install solami
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ Create a `.env` file:
14
+
15
+ ```
16
+ SOLAMI_RPC_TOKEN=your_rpc_token_here
17
+ SOLANA_KEYPAIR=your_base58_keypair_here
18
+ SOLAMI_SWQOS_KEY=your_swqos_key_here
19
+ ```
20
+
21
+ Get your tokens from the [solami.fast dashboard](https://solami.fast).
22
+
23
+ ## Usage
24
+
25
+ ### Get Account Info
26
+
27
+ ```ts
28
+ import "dotenv/config";
29
+ import { PublicKey } from "@solana/web3.js";
30
+ import { solamiFromEnv } from "solami";
31
+
32
+ const client = solamiFromEnv().build();
33
+ const pubkey = new PublicKey("11111111111111111111111111111111");
34
+ const account = await client.connection.getAccountInfo(pubkey);
35
+
36
+ console.log(`Lamports: ${account?.lamports}`);
37
+ console.log(`Owner: ${account?.owner}`);
38
+ ```
39
+
40
+ ### Get Balance
41
+
42
+ ```ts
43
+ import "dotenv/config";
44
+ import { PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";
45
+ import { solamiFromEnv } from "solami";
46
+
47
+ const client = solamiFromEnv().build();
48
+ const pubkey = new PublicKey("11111111111111111111111111111111");
49
+ const balance = await client.connection.getBalance(pubkey);
50
+
51
+ console.log(`Balance: ${balance / LAMPORTS_PER_SOL} SOL`);
52
+ ```
53
+
54
+ ### Send Transaction (RPC)
55
+
56
+ ```ts
57
+ import "dotenv/config";
58
+ import { Keypair, PublicKey, Transaction, SystemProgram, sendAndConfirmTransaction } from "@solana/web3.js";
59
+ import bs58 from "bs58";
60
+ import { solamiFromEnv } from "solami";
61
+
62
+ const client = solamiFromEnv().build();
63
+ const payer = Keypair.fromSecretKey(bs58.decode(process.env.SOLANA_KEYPAIR!));
64
+ const recipient = new PublicKey("11111111111111111111111111111112");
65
+
66
+ const blockhash = await client.connection.getLatestBlockhash();
67
+ const tx = new Transaction({ ...blockhash, feePayer: payer.publicKey }).add(
68
+ SystemProgram.transfer({ fromPubkey: payer.publicKey, toPubkey: recipient, lamports: 1 }),
69
+ );
70
+
71
+ const sig = await sendAndConfirmTransaction(client.connection, tx, [payer]);
72
+ console.log(`Signature: ${sig}`);
73
+ ```
74
+
75
+ ### Send Transaction (SWQOS)
76
+
77
+ ```ts
78
+ import "dotenv/config";
79
+ import { Keypair, PublicKey, TransactionMessage, VersionedTransaction, SystemProgram } from "@solana/web3.js";
80
+ import bs58 from "bs58";
81
+ import { solamiFromEnv, buildTipIx } from "solami";
82
+
83
+ const client = solamiFromEnv().buildWithSwqos();
84
+ const payer = Keypair.fromSecretKey(bs58.decode(process.env.SOLANA_KEYPAIR!));
85
+ const recipient = new PublicKey("11111111111111111111111111111112");
86
+
87
+ const blockhash = await client.connection.getLatestBlockhash();
88
+ const message = new TransactionMessage({
89
+ payerKey: payer.publicKey,
90
+ recentBlockhash: blockhash.blockhash,
91
+ instructions: [
92
+ SystemProgram.transfer({ fromPubkey: payer.publicKey, toPubkey: recipient, lamports: 1 }),
93
+ buildTipIx(payer.publicKey, 0.0001),
94
+ ],
95
+ }).compileToV0Message();
96
+
97
+ const tx = new VersionedTransaction(message);
98
+ tx.sign([payer]);
99
+
100
+ const sig = await client.landTransaction(tx);
101
+ console.log(`Signature: ${sig}`);
102
+ ```
103
+
104
+ ### gRPC: Subscribe to Slots
105
+
106
+ ```ts
107
+ import "dotenv/config";
108
+ import { solamiFromEnv, SubscriptionBuilder } from "solami";
109
+
110
+ const client = solamiFromEnv().build();
111
+ const request = new SubscriptionBuilder()
112
+ .addSlots("slot_sub", { filterByCommitment: undefined, interslotUpdates: false })
113
+ .build();
114
+
115
+ const stream = await client.grpc.subscribe(request);
116
+
117
+ stream.on("data", (msg: any) => {
118
+ if (msg.slot) {
119
+ console.log(`Slot: ${msg.slot.slot} | Parent: ${msg.slot.parent} | Status: ${msg.slot.status}`);
120
+ }
121
+ });
122
+ ```
123
+
124
+ ### gRPC: Subscribe to Transactions
125
+
126
+ ```ts
127
+ import "dotenv/config";
128
+ import { solamiFromEnv, CommitmentLevel } from "solami";
129
+ import bs58 from "bs58";
130
+
131
+ const client = solamiFromEnv().build();
132
+ const stream = await client.grpc.subscribeTransactions(
133
+ "tx_sub",
134
+ ["11111111111111111111111111111111"],
135
+ CommitmentLevel.CONFIRMED,
136
+ );
137
+
138
+ stream.on("data", (msg: any) => {
139
+ if (msg.transaction) {
140
+ const sig = bs58.encode(Buffer.from(msg.transaction.transaction.signature));
141
+ console.log(`Signature: ${sig} | Slot: ${msg.transaction.slot}`);
142
+ }
143
+ });
144
+ ```
145
+
146
+ ### WebSocket: Subscribe to Slots
147
+
148
+ ```ts
149
+ import "dotenv/config";
150
+ import { solamiFromEnv } from "solami";
151
+
152
+ const client = solamiFromEnv().build();
153
+ client.ws.connection.onSlotChange((slotInfo) => {
154
+ console.log(`Slot: ${slotInfo.slot} | Parent: ${slotInfo.parent} | Root: ${slotInfo.root}`);
155
+ });
156
+ ```
157
+
158
+ ### WebSocket: Subscribe to Logs
159
+
160
+ ```ts
161
+ import "dotenv/config";
162
+ import { PublicKey } from "@solana/web3.js";
163
+ import { solamiFromEnv } from "solami";
164
+
165
+ const client = solamiFromEnv().build();
166
+ const programId = new PublicKey("11111111111111111111111111111111");
167
+
168
+ client.ws.connection.onLogs(programId, (log) => {
169
+ console.log(`Signature: ${log.signature}`);
170
+ for (const line of log.logs) {
171
+ console.log(` ${line}`);
172
+ }
173
+ }, "confirmed");
174
+ ```
175
+
176
+ ## Builder Configuration
177
+
178
+ ```ts
179
+ import { solami } from "solami";
180
+
181
+ const client = solami("your_rpc_token")
182
+ .grpcToken("optional_separate_grpc_token")
183
+ .rpcBase("https://custom-rpc.example.com")
184
+ .wsBase("wss://custom-ws.example.com")
185
+ .grpcUrl("https://custom-grpc.example.com")
186
+ .swqosKey("your_swqos_key")
187
+ .buildWithSwqos();
188
+ ```
189
+
190
+ ## Environment Variables
191
+
192
+ | Variable | Required | Description |
193
+ |---|---|---|
194
+ | `SOLAMI_RPC_TOKEN` | Yes | RPC/gRPC API token |
195
+ | `SOLAMI_GRPC_TOKEN` | No | Separate gRPC token (defaults to RPC token) |
196
+ | `SOLAMI_SWQOS_KEY` | No | SWQOS API key (required for `buildWithSwqos()`) |
197
+ | `SOLANA_KEYPAIR` | No | Base58 keypair (required for sending transactions) |
198
+
199
+ ## License
200
+
201
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,477 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ CommitmentLevel: () => import_yellowstone_grpc.CommitmentLevel,
34
+ DEFAULT_GRPC_URL: () => DEFAULT_GRPC_URL,
35
+ DEFAULT_RPC_BASE: () => DEFAULT_RPC_BASE,
36
+ DEFAULT_WS_BASE: () => DEFAULT_WS_BASE,
37
+ ENDPOINTS: () => ENDPOINTS,
38
+ ENV_GRPC_TOKEN: () => ENV_GRPC_TOKEN,
39
+ ENV_RPC_TOKEN: () => ENV_RPC_TOKEN,
40
+ ENV_SWQOS_KEY: () => ENV_SWQOS_KEY,
41
+ MIN_TIP_SOL: () => MIN_TIP_SOL,
42
+ SolamiBuilder: () => SolamiBuilder,
43
+ SolamiClient: () => SolamiClient,
44
+ SolamiError: () => SolamiError,
45
+ SolamiGrpc: () => SolamiGrpc,
46
+ SolamiRpc: () => SolamiRpc,
47
+ SolamiWs: () => SolamiWs,
48
+ SubscriptionBuilder: () => SubscriptionBuilder,
49
+ SwqosClient: () => SwqosClient,
50
+ TIP_ACCOUNTS: () => TIP_ACCOUNTS,
51
+ TIP_API_URL: () => TIP_API_URL,
52
+ buildTipIx: () => buildTipIx,
53
+ configFromEnv: () => configFromEnv,
54
+ createConfig: () => createConfig,
55
+ extractTip: () => extractTip,
56
+ fetchTipAccounts: () => fetchTipAccounts,
57
+ rpcUrl: () => rpcUrl,
58
+ solami: () => solami,
59
+ solamiFromEnv: () => solamiFromEnv,
60
+ wsUrl: () => wsUrl
61
+ });
62
+ module.exports = __toCommonJS(index_exports);
63
+
64
+ // src/error.ts
65
+ var SolamiError = class extends Error {
66
+ code;
67
+ constructor(code, message, options) {
68
+ super(message, options);
69
+ this.code = code;
70
+ this.name = "SolamiError";
71
+ }
72
+ };
73
+
74
+ // src/config.ts
75
+ var DEFAULT_RPC_BASE = "https://rpc.solami.fast/sol";
76
+ var DEFAULT_WS_BASE = "wss://rpc.solami.fast/ws/sol";
77
+ var DEFAULT_GRPC_URL = "https://grpc.solami.fast";
78
+ var ENV_RPC_TOKEN = "SOLAMI_RPC_TOKEN";
79
+ var ENV_GRPC_TOKEN = "SOLAMI_GRPC_TOKEN";
80
+ var ENV_SWQOS_KEY = "SOLAMI_SWQOS_KEY";
81
+ function createConfig(rpcToken) {
82
+ return {
83
+ rpcToken,
84
+ grpcToken: rpcToken,
85
+ rpcBase: DEFAULT_RPC_BASE,
86
+ wsBase: DEFAULT_WS_BASE,
87
+ grpcUrl: DEFAULT_GRPC_URL
88
+ };
89
+ }
90
+ function configFromEnv() {
91
+ const rpcToken = process.env[ENV_RPC_TOKEN];
92
+ if (!rpcToken) {
93
+ throw new SolamiError("CONFIG_MISSING", `missing environment variable: ${ENV_RPC_TOKEN}`);
94
+ }
95
+ const grpcToken = process.env[ENV_GRPC_TOKEN] ?? rpcToken;
96
+ const swqosKey = process.env[ENV_SWQOS_KEY] ?? void 0;
97
+ return {
98
+ rpcToken,
99
+ grpcToken,
100
+ rpcBase: DEFAULT_RPC_BASE,
101
+ wsBase: DEFAULT_WS_BASE,
102
+ grpcUrl: DEFAULT_GRPC_URL,
103
+ swqosKey
104
+ };
105
+ }
106
+ function rpcUrl(config) {
107
+ return `${config.rpcBase}?api_key=${config.rpcToken}`;
108
+ }
109
+ function wsUrl(config) {
110
+ return `${config.wsBase}?api_key=${config.rpcToken}`;
111
+ }
112
+
113
+ // src/rpc.ts
114
+ var import_web3 = require("@solana/web3.js");
115
+ var SolamiRpc = class {
116
+ connection;
117
+ config;
118
+ constructor(config) {
119
+ this.config = config;
120
+ this.connection = new import_web3.Connection(rpcUrl(config), "confirmed");
121
+ }
122
+ get url() {
123
+ return rpcUrl(this.config);
124
+ }
125
+ async getBlock(slot) {
126
+ const block = await this.connection.getBlock(slot, {
127
+ maxSupportedTransactionVersion: 0
128
+ });
129
+ if (!block) {
130
+ throw new Error(`block not found at slot ${slot}`);
131
+ }
132
+ return block;
133
+ }
134
+ };
135
+
136
+ // src/websocket.ts
137
+ var import_web32 = require("@solana/web3.js");
138
+ var SolamiWs = class {
139
+ connection;
140
+ constructor(config) {
141
+ this.connection = new import_web32.Connection(rpcUrl(config), {
142
+ wsEndpoint: wsUrl(config),
143
+ commitment: "confirmed"
144
+ });
145
+ }
146
+ };
147
+
148
+ // src/grpc/client.ts
149
+ var import_yellowstone_grpc2 = __toESM(require("@triton-one/yellowstone-grpc"), 1);
150
+
151
+ // src/grpc/subscription-builder.ts
152
+ var import_yellowstone_grpc = require("@triton-one/yellowstone-grpc");
153
+ var SubscriptionBuilder = class {
154
+ accounts = {};
155
+ slots = {};
156
+ transactions = {};
157
+ transactionsStatus = {};
158
+ blocks = {};
159
+ blocksMeta = {};
160
+ entry = {};
161
+ _commitment;
162
+ accountsDataSlice = [];
163
+ _fromSlot;
164
+ commitment(level) {
165
+ this._commitment = level;
166
+ return this;
167
+ }
168
+ fromSlot(slot) {
169
+ this._fromSlot = slot.toString();
170
+ return this;
171
+ }
172
+ addAccounts(label, filter) {
173
+ this.accounts[label] = filter;
174
+ return this;
175
+ }
176
+ addSlots(label, filter) {
177
+ this.slots[label] = filter;
178
+ return this;
179
+ }
180
+ addTransactions(label, filter) {
181
+ this.transactions[label] = filter;
182
+ return this;
183
+ }
184
+ addTransactionsStatus(label, filter) {
185
+ this.transactionsStatus[label] = filter;
186
+ return this;
187
+ }
188
+ addBlocks(label, filter) {
189
+ this.blocks[label] = filter;
190
+ return this;
191
+ }
192
+ addBlocksMeta(label) {
193
+ this.blocksMeta[label] = {};
194
+ return this;
195
+ }
196
+ addEntry(label) {
197
+ this.entry[label] = {};
198
+ return this;
199
+ }
200
+ addAccountsDataSlice(offset, length) {
201
+ this.accountsDataSlice.push({ offset, length });
202
+ return this;
203
+ }
204
+ build() {
205
+ return {
206
+ accounts: this.accounts,
207
+ slots: this.slots,
208
+ transactions: this.transactions,
209
+ transactionsStatus: this.transactionsStatus,
210
+ blocks: this.blocks,
211
+ blocksMeta: this.blocksMeta,
212
+ entry: this.entry,
213
+ commitment: this._commitment,
214
+ accountsDataSlice: this.accountsDataSlice,
215
+ ping: void 0,
216
+ fromSlot: this._fromSlot
217
+ };
218
+ }
219
+ };
220
+
221
+ // src/grpc/client.ts
222
+ var Client = import_yellowstone_grpc2.default.default ?? import_yellowstone_grpc2.default;
223
+ var SolamiGrpc = class {
224
+ client;
225
+ config;
226
+ constructor(config) {
227
+ this.config = config;
228
+ this.client = new Client(config.grpcUrl, config.grpcToken, void 0);
229
+ }
230
+ get url() {
231
+ return this.config.grpcUrl;
232
+ }
233
+ async subscribe(request) {
234
+ try {
235
+ const stream = await this.client.subscribe();
236
+ stream.write(request);
237
+ return stream;
238
+ } catch (e) {
239
+ throw new SolamiError("GRPC_ERROR", e.message ?? String(e));
240
+ }
241
+ }
242
+ async subscribeTransactions(label, accounts, commitment) {
243
+ const request = new SubscriptionBuilder().addTransactions(label, {
244
+ vote: false,
245
+ failed: false,
246
+ accountInclude: accounts,
247
+ accountExclude: [],
248
+ accountRequired: [],
249
+ signature: void 0
250
+ }).commitment(commitment).build();
251
+ return this.subscribe(request);
252
+ }
253
+ };
254
+
255
+ // src/swqos/client.ts
256
+ var import_bs58 = __toESM(require("bs58"), 1);
257
+ var SWQOS_SEND_URL = "https://swqos.solami.fast/api/v1/transactions";
258
+ var SwqosClient = class {
259
+ endpoint;
260
+ apiKey;
261
+ constructor(apiKey, endpoint) {
262
+ this.apiKey = apiKey;
263
+ this.endpoint = endpoint ?? SWQOS_SEND_URL;
264
+ }
265
+ async sendTransaction(tx) {
266
+ const sig = import_bs58.default.encode(tx.signatures[0]);
267
+ const serialized = import_bs58.default.encode(tx.serialize());
268
+ const res = await fetch(this.endpoint, {
269
+ method: "POST",
270
+ headers: {
271
+ "Content-Type": "application/json",
272
+ "Authorization": `Bearer ${this.apiKey}`
273
+ },
274
+ body: JSON.stringify({
275
+ transaction: serialized,
276
+ encoding: "base58"
277
+ })
278
+ });
279
+ if (!res.ok) {
280
+ const body = await res.text().catch(() => "");
281
+ throw new SolamiError("SWQOS_ERROR", `send failed (${res.status}): ${body}`);
282
+ }
283
+ return sig;
284
+ }
285
+ };
286
+
287
+ // src/swqos/constants.ts
288
+ var import_web33 = require("@solana/web3.js");
289
+ var ENDPOINTS = ["landing.solami.fast:11000"];
290
+ var TIP_ACCOUNTS = [
291
+ new import_web33.PublicKey("6993ZufwyEDNdB94kciDTGB17ANXguiNH22VmMQU1ami"),
292
+ new import_web33.PublicKey("E1BkG293HQocKfCkfPS7tEvs8Enh6FK8pUKi17EH1ami"),
293
+ new import_web33.PublicKey("2Ga87xvZwpP9WRsSdiPiWre21cQbDcFhGLmT5EMo1ami"),
294
+ new import_web33.PublicKey("CGzT5jzT68vGUWVagQAQqESfrCEHSKYe9DAjfgfC1ami")
295
+ ];
296
+ var TIP_API_URL = "https://api.solami.fast/onchain/tip-addresses";
297
+ var MIN_TIP_SOL = 1e-4;
298
+
299
+ // src/swqos/tip.ts
300
+ var import_web34 = require("@solana/web3.js");
301
+ function buildTipIx(payer, sol) {
302
+ const amount = sol < MIN_TIP_SOL ? MIN_TIP_SOL : sol;
303
+ const lamports = Math.floor(amount * import_web34.LAMPORTS_PER_SOL);
304
+ const tipAccount = TIP_ACCOUNTS[Math.floor(Math.random() * TIP_ACCOUNTS.length)];
305
+ return import_web34.SystemProgram.transfer({
306
+ fromPubkey: payer,
307
+ toPubkey: tipAccount,
308
+ lamports
309
+ });
310
+ }
311
+ async function fetchTipAccounts() {
312
+ const res = await fetch(TIP_API_URL);
313
+ if (!res.ok) {
314
+ throw new SolamiError("API_ERROR", `fetch tip accounts failed: ${res.status}`);
315
+ }
316
+ const addresses = await res.json();
317
+ return addresses.map((a) => new import_web34.PublicKey(a));
318
+ }
319
+ function extractTip(tx, tipPubkeys = TIP_ACCOUNTS) {
320
+ const keys = tx.message.staticAccountKeys;
321
+ const systemProgramId = import_web34.SystemProgram.programId;
322
+ for (const ix of tx.message.compiledInstructions) {
323
+ const programKey = keys[ix.programIdIndex];
324
+ if (!programKey || !programKey.equals(systemProgramId)) continue;
325
+ if (ix.data.length < 12) continue;
326
+ const ixType = ix.data[0] | ix.data[1] << 8 | ix.data[2] << 16 | ix.data[3] << 24;
327
+ if (ixType !== 2) continue;
328
+ const view = new DataView(ix.data.buffer, ix.data.byteOffset);
329
+ const lamports = Number(view.getBigUint64(4, true));
330
+ const toIndex = ix.accountKeyIndexes[1];
331
+ if (toIndex === void 0) continue;
332
+ const toPubkey = keys[toIndex];
333
+ if (!toPubkey) continue;
334
+ if (tipPubkeys.some((tip) => tip.equals(toPubkey))) {
335
+ return { lamports, tipAccount: toPubkey };
336
+ }
337
+ }
338
+ return null;
339
+ }
340
+
341
+ // src/client.ts
342
+ var SolamiClient = class {
343
+ config;
344
+ rpc;
345
+ ws;
346
+ grpc;
347
+ swqos;
348
+ constructor(config, swqos) {
349
+ this.config = config;
350
+ this.rpc = new SolamiRpc(config);
351
+ this.ws = new SolamiWs(config);
352
+ this.grpc = new SolamiGrpc(config);
353
+ this.swqos = swqos;
354
+ }
355
+ get connection() {
356
+ return this.rpc.connection;
357
+ }
358
+ async landTransaction(tx) {
359
+ if (!this.swqos) {
360
+ throw new SolamiError("SWQOS_ERROR", "client was built without swqos support");
361
+ }
362
+ if (!extractTip(tx, TIP_ACCOUNTS)) {
363
+ throw new SolamiError("TIP_MISSING", "transaction must include a tip instruction");
364
+ }
365
+ return this.swqos.sendTransaction(tx);
366
+ }
367
+ };
368
+
369
+ // src/builder.ts
370
+ var SolamiBuilder = class _SolamiBuilder {
371
+ _rpcToken;
372
+ _grpcToken;
373
+ _rpcBase;
374
+ _wsBase;
375
+ _grpcUrl;
376
+ _swqosKey;
377
+ constructor(rpcToken) {
378
+ this._rpcToken = rpcToken;
379
+ }
380
+ static fromEnv() {
381
+ const builder = new _SolamiBuilder(process.env[ENV_RPC_TOKEN] ?? void 0);
382
+ builder._grpcToken = process.env[ENV_GRPC_TOKEN] ?? void 0;
383
+ builder._swqosKey = process.env[ENV_SWQOS_KEY] ?? void 0;
384
+ return builder;
385
+ }
386
+ grpcToken(token) {
387
+ this._grpcToken = token;
388
+ return this;
389
+ }
390
+ rpcBase(url) {
391
+ this._rpcBase = url;
392
+ return this;
393
+ }
394
+ wsBase(url) {
395
+ this._wsBase = url;
396
+ return this;
397
+ }
398
+ grpcUrl(url) {
399
+ this._grpcUrl = url;
400
+ return this;
401
+ }
402
+ swqosKey(key) {
403
+ this._swqosKey = key;
404
+ return this;
405
+ }
406
+ intoConfig() {
407
+ const rpcToken = this._rpcToken;
408
+ if (!rpcToken) {
409
+ throw new SolamiError(
410
+ "CONFIG_MISSING",
411
+ `missing ${ENV_RPC_TOKEN} (set via env or new SolamiBuilder("token"))`
412
+ );
413
+ }
414
+ return {
415
+ rpcToken,
416
+ grpcToken: this._grpcToken ?? rpcToken,
417
+ rpcBase: this._rpcBase ?? DEFAULT_RPC_BASE,
418
+ wsBase: this._wsBase ?? DEFAULT_WS_BASE,
419
+ grpcUrl: this._grpcUrl ?? DEFAULT_GRPC_URL,
420
+ swqosKey: this._swqosKey
421
+ };
422
+ }
423
+ build() {
424
+ const config = this.intoConfig();
425
+ return new SolamiClient(config);
426
+ }
427
+ buildWithSwqos() {
428
+ const config = this.intoConfig();
429
+ if (!config.swqosKey) {
430
+ throw new SolamiError(
431
+ "CONFIG_MISSING",
432
+ `missing ${ENV_SWQOS_KEY} (set via env or .swqosKey("key"))`
433
+ );
434
+ }
435
+ const swqos = new SwqosClient(config.swqosKey);
436
+ return new SolamiClient(config, swqos);
437
+ }
438
+ };
439
+
440
+ // src/index.ts
441
+ function solami(token) {
442
+ return new SolamiBuilder(token);
443
+ }
444
+ function solamiFromEnv() {
445
+ return SolamiBuilder.fromEnv();
446
+ }
447
+ // Annotate the CommonJS export names for ESM import in node:
448
+ 0 && (module.exports = {
449
+ CommitmentLevel,
450
+ DEFAULT_GRPC_URL,
451
+ DEFAULT_RPC_BASE,
452
+ DEFAULT_WS_BASE,
453
+ ENDPOINTS,
454
+ ENV_GRPC_TOKEN,
455
+ ENV_RPC_TOKEN,
456
+ ENV_SWQOS_KEY,
457
+ MIN_TIP_SOL,
458
+ SolamiBuilder,
459
+ SolamiClient,
460
+ SolamiError,
461
+ SolamiGrpc,
462
+ SolamiRpc,
463
+ SolamiWs,
464
+ SubscriptionBuilder,
465
+ SwqosClient,
466
+ TIP_ACCOUNTS,
467
+ TIP_API_URL,
468
+ buildTipIx,
469
+ configFromEnv,
470
+ createConfig,
471
+ extractTip,
472
+ fetchTipAccounts,
473
+ rpcUrl,
474
+ solami,
475
+ solamiFromEnv,
476
+ wsUrl
477
+ });
@@ -0,0 +1,131 @@
1
+ import * as _solana_web3_js from '@solana/web3.js';
2
+ import { Connection, BlockResponse, VersionedTransaction, PublicKey, TransactionInstruction } from '@solana/web3.js';
3
+ import { CommitmentLevel, SubscribeRequestFilterAccounts, SubscribeRequestFilterSlots, SubscribeRequestFilterTransactions, SubscribeRequestFilterBlocks, SubscribeRequest, SubscribeUpdate } from '@triton-one/yellowstone-grpc';
4
+ export { CommitmentLevel, SubscribeRequest } from '@triton-one/yellowstone-grpc';
5
+ import { ClientDuplexStream } from '@grpc/grpc-js';
6
+
7
+ declare const DEFAULT_RPC_BASE = "https://rpc.solami.fast/sol";
8
+ declare const DEFAULT_WS_BASE = "wss://rpc.solami.fast/ws/sol";
9
+ declare const DEFAULT_GRPC_URL = "https://grpc.solami.fast";
10
+ declare const ENV_RPC_TOKEN = "SOLAMI_RPC_TOKEN";
11
+ declare const ENV_GRPC_TOKEN = "SOLAMI_GRPC_TOKEN";
12
+ declare const ENV_SWQOS_KEY = "SOLAMI_SWQOS_KEY";
13
+ interface SolamiConfig {
14
+ rpcToken: string;
15
+ grpcToken: string;
16
+ rpcBase: string;
17
+ wsBase: string;
18
+ grpcUrl: string;
19
+ swqosKey?: string;
20
+ }
21
+ declare function createConfig(rpcToken: string): SolamiConfig;
22
+ declare function configFromEnv(): SolamiConfig;
23
+ declare function rpcUrl(config: SolamiConfig): string;
24
+ declare function wsUrl(config: SolamiConfig): string;
25
+
26
+ declare class SolamiRpc {
27
+ readonly connection: Connection;
28
+ private readonly config;
29
+ constructor(config: SolamiConfig);
30
+ get url(): string;
31
+ getBlock(slot: number): Promise<BlockResponse>;
32
+ }
33
+
34
+ declare class SolamiWs {
35
+ readonly connection: Connection;
36
+ constructor(config: SolamiConfig);
37
+ }
38
+
39
+ declare class SubscriptionBuilder {
40
+ private accounts;
41
+ private slots;
42
+ private transactions;
43
+ private transactionsStatus;
44
+ private blocks;
45
+ private blocksMeta;
46
+ private entry;
47
+ private _commitment?;
48
+ private accountsDataSlice;
49
+ private _fromSlot?;
50
+ commitment(level: CommitmentLevel): this;
51
+ fromSlot(slot: bigint | number | string): this;
52
+ addAccounts(label: string, filter: SubscribeRequestFilterAccounts): this;
53
+ addSlots(label: string, filter: SubscribeRequestFilterSlots): this;
54
+ addTransactions(label: string, filter: SubscribeRequestFilterTransactions): this;
55
+ addTransactionsStatus(label: string, filter: SubscribeRequestFilterTransactions): this;
56
+ addBlocks(label: string, filter: SubscribeRequestFilterBlocks): this;
57
+ addBlocksMeta(label: string): this;
58
+ addEntry(label: string): this;
59
+ addAccountsDataSlice(offset: string, length: string): this;
60
+ build(): SubscribeRequest;
61
+ }
62
+
63
+ type GrpcStream = ClientDuplexStream<SubscribeRequest, SubscribeUpdate>;
64
+ declare class SolamiGrpc {
65
+ private client;
66
+ private readonly config;
67
+ constructor(config: SolamiConfig);
68
+ get url(): string;
69
+ subscribe(request: SubscribeRequest): Promise<GrpcStream>;
70
+ subscribeTransactions(label: string, accounts: string[], commitment: CommitmentLevel): Promise<GrpcStream>;
71
+ }
72
+
73
+ declare class SwqosClient {
74
+ private readonly endpoint;
75
+ private readonly apiKey;
76
+ constructor(apiKey: string, endpoint?: string);
77
+ sendTransaction(tx: VersionedTransaction): Promise<string>;
78
+ }
79
+
80
+ declare const ENDPOINTS: readonly ["landing.solami.fast:11000"];
81
+ declare const TIP_ACCOUNTS: PublicKey[];
82
+ declare const TIP_API_URL = "https://api.solami.fast/onchain/tip-addresses";
83
+ declare const MIN_TIP_SOL = 0.0001;
84
+
85
+ declare function buildTipIx(payer: PublicKey, sol: number): TransactionInstruction;
86
+ declare function fetchTipAccounts(): Promise<PublicKey[]>;
87
+ declare function extractTip(tx: VersionedTransaction, tipPubkeys?: PublicKey[]): {
88
+ lamports: number;
89
+ tipAccount: PublicKey;
90
+ } | null;
91
+
92
+ declare class SolamiClient {
93
+ readonly config: SolamiConfig;
94
+ readonly rpc: SolamiRpc;
95
+ readonly ws: SolamiWs;
96
+ readonly grpc: SolamiGrpc;
97
+ readonly swqos?: SwqosClient;
98
+ constructor(config: SolamiConfig, swqos?: SwqosClient);
99
+ get connection(): _solana_web3_js.Connection;
100
+ landTransaction(tx: VersionedTransaction): Promise<string>;
101
+ }
102
+
103
+ declare class SolamiBuilder {
104
+ private _rpcToken?;
105
+ private _grpcToken?;
106
+ private _rpcBase?;
107
+ private _wsBase?;
108
+ private _grpcUrl?;
109
+ private _swqosKey?;
110
+ constructor(rpcToken?: string);
111
+ static fromEnv(): SolamiBuilder;
112
+ grpcToken(token: string): this;
113
+ rpcBase(url: string): this;
114
+ wsBase(url: string): this;
115
+ grpcUrl(url: string): this;
116
+ swqosKey(key: string): this;
117
+ intoConfig(): SolamiConfig;
118
+ build(): SolamiClient;
119
+ buildWithSwqos(): SolamiClient;
120
+ }
121
+
122
+ type SolamiErrorCode = "CONFIG_MISSING" | "RPC_ERROR" | "WS_ERROR" | "GRPC_ERROR" | "SWQOS_ERROR" | "TIP_MISSING" | "API_ERROR";
123
+ declare class SolamiError extends Error {
124
+ readonly code: SolamiErrorCode;
125
+ constructor(code: SolamiErrorCode, message: string, options?: ErrorOptions);
126
+ }
127
+
128
+ declare function solami(token: string): SolamiBuilder;
129
+ declare function solamiFromEnv(): SolamiBuilder;
130
+
131
+ export { DEFAULT_GRPC_URL, DEFAULT_RPC_BASE, DEFAULT_WS_BASE, ENDPOINTS, ENV_GRPC_TOKEN, ENV_RPC_TOKEN, ENV_SWQOS_KEY, type GrpcStream, MIN_TIP_SOL, SolamiBuilder, SolamiClient, type SolamiConfig, SolamiError, type SolamiErrorCode, SolamiGrpc, SolamiRpc, SolamiWs, SubscriptionBuilder, SwqosClient, TIP_ACCOUNTS, TIP_API_URL, buildTipIx, configFromEnv, createConfig, extractTip, fetchTipAccounts, rpcUrl, solami, solamiFromEnv, wsUrl };
@@ -0,0 +1,131 @@
1
+ import * as _solana_web3_js from '@solana/web3.js';
2
+ import { Connection, BlockResponse, VersionedTransaction, PublicKey, TransactionInstruction } from '@solana/web3.js';
3
+ import { CommitmentLevel, SubscribeRequestFilterAccounts, SubscribeRequestFilterSlots, SubscribeRequestFilterTransactions, SubscribeRequestFilterBlocks, SubscribeRequest, SubscribeUpdate } from '@triton-one/yellowstone-grpc';
4
+ export { CommitmentLevel, SubscribeRequest } from '@triton-one/yellowstone-grpc';
5
+ import { ClientDuplexStream } from '@grpc/grpc-js';
6
+
7
+ declare const DEFAULT_RPC_BASE = "https://rpc.solami.fast/sol";
8
+ declare const DEFAULT_WS_BASE = "wss://rpc.solami.fast/ws/sol";
9
+ declare const DEFAULT_GRPC_URL = "https://grpc.solami.fast";
10
+ declare const ENV_RPC_TOKEN = "SOLAMI_RPC_TOKEN";
11
+ declare const ENV_GRPC_TOKEN = "SOLAMI_GRPC_TOKEN";
12
+ declare const ENV_SWQOS_KEY = "SOLAMI_SWQOS_KEY";
13
+ interface SolamiConfig {
14
+ rpcToken: string;
15
+ grpcToken: string;
16
+ rpcBase: string;
17
+ wsBase: string;
18
+ grpcUrl: string;
19
+ swqosKey?: string;
20
+ }
21
+ declare function createConfig(rpcToken: string): SolamiConfig;
22
+ declare function configFromEnv(): SolamiConfig;
23
+ declare function rpcUrl(config: SolamiConfig): string;
24
+ declare function wsUrl(config: SolamiConfig): string;
25
+
26
+ declare class SolamiRpc {
27
+ readonly connection: Connection;
28
+ private readonly config;
29
+ constructor(config: SolamiConfig);
30
+ get url(): string;
31
+ getBlock(slot: number): Promise<BlockResponse>;
32
+ }
33
+
34
+ declare class SolamiWs {
35
+ readonly connection: Connection;
36
+ constructor(config: SolamiConfig);
37
+ }
38
+
39
+ declare class SubscriptionBuilder {
40
+ private accounts;
41
+ private slots;
42
+ private transactions;
43
+ private transactionsStatus;
44
+ private blocks;
45
+ private blocksMeta;
46
+ private entry;
47
+ private _commitment?;
48
+ private accountsDataSlice;
49
+ private _fromSlot?;
50
+ commitment(level: CommitmentLevel): this;
51
+ fromSlot(slot: bigint | number | string): this;
52
+ addAccounts(label: string, filter: SubscribeRequestFilterAccounts): this;
53
+ addSlots(label: string, filter: SubscribeRequestFilterSlots): this;
54
+ addTransactions(label: string, filter: SubscribeRequestFilterTransactions): this;
55
+ addTransactionsStatus(label: string, filter: SubscribeRequestFilterTransactions): this;
56
+ addBlocks(label: string, filter: SubscribeRequestFilterBlocks): this;
57
+ addBlocksMeta(label: string): this;
58
+ addEntry(label: string): this;
59
+ addAccountsDataSlice(offset: string, length: string): this;
60
+ build(): SubscribeRequest;
61
+ }
62
+
63
+ type GrpcStream = ClientDuplexStream<SubscribeRequest, SubscribeUpdate>;
64
+ declare class SolamiGrpc {
65
+ private client;
66
+ private readonly config;
67
+ constructor(config: SolamiConfig);
68
+ get url(): string;
69
+ subscribe(request: SubscribeRequest): Promise<GrpcStream>;
70
+ subscribeTransactions(label: string, accounts: string[], commitment: CommitmentLevel): Promise<GrpcStream>;
71
+ }
72
+
73
+ declare class SwqosClient {
74
+ private readonly endpoint;
75
+ private readonly apiKey;
76
+ constructor(apiKey: string, endpoint?: string);
77
+ sendTransaction(tx: VersionedTransaction): Promise<string>;
78
+ }
79
+
80
+ declare const ENDPOINTS: readonly ["landing.solami.fast:11000"];
81
+ declare const TIP_ACCOUNTS: PublicKey[];
82
+ declare const TIP_API_URL = "https://api.solami.fast/onchain/tip-addresses";
83
+ declare const MIN_TIP_SOL = 0.0001;
84
+
85
+ declare function buildTipIx(payer: PublicKey, sol: number): TransactionInstruction;
86
+ declare function fetchTipAccounts(): Promise<PublicKey[]>;
87
+ declare function extractTip(tx: VersionedTransaction, tipPubkeys?: PublicKey[]): {
88
+ lamports: number;
89
+ tipAccount: PublicKey;
90
+ } | null;
91
+
92
+ declare class SolamiClient {
93
+ readonly config: SolamiConfig;
94
+ readonly rpc: SolamiRpc;
95
+ readonly ws: SolamiWs;
96
+ readonly grpc: SolamiGrpc;
97
+ readonly swqos?: SwqosClient;
98
+ constructor(config: SolamiConfig, swqos?: SwqosClient);
99
+ get connection(): _solana_web3_js.Connection;
100
+ landTransaction(tx: VersionedTransaction): Promise<string>;
101
+ }
102
+
103
+ declare class SolamiBuilder {
104
+ private _rpcToken?;
105
+ private _grpcToken?;
106
+ private _rpcBase?;
107
+ private _wsBase?;
108
+ private _grpcUrl?;
109
+ private _swqosKey?;
110
+ constructor(rpcToken?: string);
111
+ static fromEnv(): SolamiBuilder;
112
+ grpcToken(token: string): this;
113
+ rpcBase(url: string): this;
114
+ wsBase(url: string): this;
115
+ grpcUrl(url: string): this;
116
+ swqosKey(key: string): this;
117
+ intoConfig(): SolamiConfig;
118
+ build(): SolamiClient;
119
+ buildWithSwqos(): SolamiClient;
120
+ }
121
+
122
+ type SolamiErrorCode = "CONFIG_MISSING" | "RPC_ERROR" | "WS_ERROR" | "GRPC_ERROR" | "SWQOS_ERROR" | "TIP_MISSING" | "API_ERROR";
123
+ declare class SolamiError extends Error {
124
+ readonly code: SolamiErrorCode;
125
+ constructor(code: SolamiErrorCode, message: string, options?: ErrorOptions);
126
+ }
127
+
128
+ declare function solami(token: string): SolamiBuilder;
129
+ declare function solamiFromEnv(): SolamiBuilder;
130
+
131
+ export { DEFAULT_GRPC_URL, DEFAULT_RPC_BASE, DEFAULT_WS_BASE, ENDPOINTS, ENV_GRPC_TOKEN, ENV_RPC_TOKEN, ENV_SWQOS_KEY, type GrpcStream, MIN_TIP_SOL, SolamiBuilder, SolamiClient, type SolamiConfig, SolamiError, type SolamiErrorCode, SolamiGrpc, SolamiRpc, SolamiWs, SubscriptionBuilder, SwqosClient, TIP_ACCOUNTS, TIP_API_URL, buildTipIx, configFromEnv, createConfig, extractTip, fetchTipAccounts, rpcUrl, solami, solamiFromEnv, wsUrl };
package/dist/index.js ADDED
@@ -0,0 +1,417 @@
1
+ // src/error.ts
2
+ var SolamiError = class extends Error {
3
+ code;
4
+ constructor(code, message, options) {
5
+ super(message, options);
6
+ this.code = code;
7
+ this.name = "SolamiError";
8
+ }
9
+ };
10
+
11
+ // src/config.ts
12
+ var DEFAULT_RPC_BASE = "https://rpc.solami.fast/sol";
13
+ var DEFAULT_WS_BASE = "wss://rpc.solami.fast/ws/sol";
14
+ var DEFAULT_GRPC_URL = "https://grpc.solami.fast";
15
+ var ENV_RPC_TOKEN = "SOLAMI_RPC_TOKEN";
16
+ var ENV_GRPC_TOKEN = "SOLAMI_GRPC_TOKEN";
17
+ var ENV_SWQOS_KEY = "SOLAMI_SWQOS_KEY";
18
+ function createConfig(rpcToken) {
19
+ return {
20
+ rpcToken,
21
+ grpcToken: rpcToken,
22
+ rpcBase: DEFAULT_RPC_BASE,
23
+ wsBase: DEFAULT_WS_BASE,
24
+ grpcUrl: DEFAULT_GRPC_URL
25
+ };
26
+ }
27
+ function configFromEnv() {
28
+ const rpcToken = process.env[ENV_RPC_TOKEN];
29
+ if (!rpcToken) {
30
+ throw new SolamiError("CONFIG_MISSING", `missing environment variable: ${ENV_RPC_TOKEN}`);
31
+ }
32
+ const grpcToken = process.env[ENV_GRPC_TOKEN] ?? rpcToken;
33
+ const swqosKey = process.env[ENV_SWQOS_KEY] ?? void 0;
34
+ return {
35
+ rpcToken,
36
+ grpcToken,
37
+ rpcBase: DEFAULT_RPC_BASE,
38
+ wsBase: DEFAULT_WS_BASE,
39
+ grpcUrl: DEFAULT_GRPC_URL,
40
+ swqosKey
41
+ };
42
+ }
43
+ function rpcUrl(config) {
44
+ return `${config.rpcBase}?api_key=${config.rpcToken}`;
45
+ }
46
+ function wsUrl(config) {
47
+ return `${config.wsBase}?api_key=${config.rpcToken}`;
48
+ }
49
+
50
+ // src/rpc.ts
51
+ import { Connection } from "@solana/web3.js";
52
+ var SolamiRpc = class {
53
+ connection;
54
+ config;
55
+ constructor(config) {
56
+ this.config = config;
57
+ this.connection = new Connection(rpcUrl(config), "confirmed");
58
+ }
59
+ get url() {
60
+ return rpcUrl(this.config);
61
+ }
62
+ async getBlock(slot) {
63
+ const block = await this.connection.getBlock(slot, {
64
+ maxSupportedTransactionVersion: 0
65
+ });
66
+ if (!block) {
67
+ throw new Error(`block not found at slot ${slot}`);
68
+ }
69
+ return block;
70
+ }
71
+ };
72
+
73
+ // src/websocket.ts
74
+ import { Connection as Connection2 } from "@solana/web3.js";
75
+ var SolamiWs = class {
76
+ connection;
77
+ constructor(config) {
78
+ this.connection = new Connection2(rpcUrl(config), {
79
+ wsEndpoint: wsUrl(config),
80
+ commitment: "confirmed"
81
+ });
82
+ }
83
+ };
84
+
85
+ // src/grpc/client.ts
86
+ import YellowstoneModule from "@triton-one/yellowstone-grpc";
87
+
88
+ // src/grpc/subscription-builder.ts
89
+ import { CommitmentLevel } from "@triton-one/yellowstone-grpc";
90
+ var SubscriptionBuilder = class {
91
+ accounts = {};
92
+ slots = {};
93
+ transactions = {};
94
+ transactionsStatus = {};
95
+ blocks = {};
96
+ blocksMeta = {};
97
+ entry = {};
98
+ _commitment;
99
+ accountsDataSlice = [];
100
+ _fromSlot;
101
+ commitment(level) {
102
+ this._commitment = level;
103
+ return this;
104
+ }
105
+ fromSlot(slot) {
106
+ this._fromSlot = slot.toString();
107
+ return this;
108
+ }
109
+ addAccounts(label, filter) {
110
+ this.accounts[label] = filter;
111
+ return this;
112
+ }
113
+ addSlots(label, filter) {
114
+ this.slots[label] = filter;
115
+ return this;
116
+ }
117
+ addTransactions(label, filter) {
118
+ this.transactions[label] = filter;
119
+ return this;
120
+ }
121
+ addTransactionsStatus(label, filter) {
122
+ this.transactionsStatus[label] = filter;
123
+ return this;
124
+ }
125
+ addBlocks(label, filter) {
126
+ this.blocks[label] = filter;
127
+ return this;
128
+ }
129
+ addBlocksMeta(label) {
130
+ this.blocksMeta[label] = {};
131
+ return this;
132
+ }
133
+ addEntry(label) {
134
+ this.entry[label] = {};
135
+ return this;
136
+ }
137
+ addAccountsDataSlice(offset, length) {
138
+ this.accountsDataSlice.push({ offset, length });
139
+ return this;
140
+ }
141
+ build() {
142
+ return {
143
+ accounts: this.accounts,
144
+ slots: this.slots,
145
+ transactions: this.transactions,
146
+ transactionsStatus: this.transactionsStatus,
147
+ blocks: this.blocks,
148
+ blocksMeta: this.blocksMeta,
149
+ entry: this.entry,
150
+ commitment: this._commitment,
151
+ accountsDataSlice: this.accountsDataSlice,
152
+ ping: void 0,
153
+ fromSlot: this._fromSlot
154
+ };
155
+ }
156
+ };
157
+
158
+ // src/grpc/client.ts
159
+ var Client = YellowstoneModule.default ?? YellowstoneModule;
160
+ var SolamiGrpc = class {
161
+ client;
162
+ config;
163
+ constructor(config) {
164
+ this.config = config;
165
+ this.client = new Client(config.grpcUrl, config.grpcToken, void 0);
166
+ }
167
+ get url() {
168
+ return this.config.grpcUrl;
169
+ }
170
+ async subscribe(request) {
171
+ try {
172
+ const stream = await this.client.subscribe();
173
+ stream.write(request);
174
+ return stream;
175
+ } catch (e) {
176
+ throw new SolamiError("GRPC_ERROR", e.message ?? String(e));
177
+ }
178
+ }
179
+ async subscribeTransactions(label, accounts, commitment) {
180
+ const request = new SubscriptionBuilder().addTransactions(label, {
181
+ vote: false,
182
+ failed: false,
183
+ accountInclude: accounts,
184
+ accountExclude: [],
185
+ accountRequired: [],
186
+ signature: void 0
187
+ }).commitment(commitment).build();
188
+ return this.subscribe(request);
189
+ }
190
+ };
191
+
192
+ // src/swqos/client.ts
193
+ import bs58 from "bs58";
194
+ var SWQOS_SEND_URL = "https://swqos.solami.fast/api/v1/transactions";
195
+ var SwqosClient = class {
196
+ endpoint;
197
+ apiKey;
198
+ constructor(apiKey, endpoint) {
199
+ this.apiKey = apiKey;
200
+ this.endpoint = endpoint ?? SWQOS_SEND_URL;
201
+ }
202
+ async sendTransaction(tx) {
203
+ const sig = bs58.encode(tx.signatures[0]);
204
+ const serialized = bs58.encode(tx.serialize());
205
+ const res = await fetch(this.endpoint, {
206
+ method: "POST",
207
+ headers: {
208
+ "Content-Type": "application/json",
209
+ "Authorization": `Bearer ${this.apiKey}`
210
+ },
211
+ body: JSON.stringify({
212
+ transaction: serialized,
213
+ encoding: "base58"
214
+ })
215
+ });
216
+ if (!res.ok) {
217
+ const body = await res.text().catch(() => "");
218
+ throw new SolamiError("SWQOS_ERROR", `send failed (${res.status}): ${body}`);
219
+ }
220
+ return sig;
221
+ }
222
+ };
223
+
224
+ // src/swqos/constants.ts
225
+ import { PublicKey } from "@solana/web3.js";
226
+ var ENDPOINTS = ["landing.solami.fast:11000"];
227
+ var TIP_ACCOUNTS = [
228
+ new PublicKey("6993ZufwyEDNdB94kciDTGB17ANXguiNH22VmMQU1ami"),
229
+ new PublicKey("E1BkG293HQocKfCkfPS7tEvs8Enh6FK8pUKi17EH1ami"),
230
+ new PublicKey("2Ga87xvZwpP9WRsSdiPiWre21cQbDcFhGLmT5EMo1ami"),
231
+ new PublicKey("CGzT5jzT68vGUWVagQAQqESfrCEHSKYe9DAjfgfC1ami")
232
+ ];
233
+ var TIP_API_URL = "https://api.solami.fast/onchain/tip-addresses";
234
+ var MIN_TIP_SOL = 1e-4;
235
+
236
+ // src/swqos/tip.ts
237
+ import {
238
+ PublicKey as PublicKey2,
239
+ SystemProgram,
240
+ LAMPORTS_PER_SOL
241
+ } from "@solana/web3.js";
242
+ function buildTipIx(payer, sol) {
243
+ const amount = sol < MIN_TIP_SOL ? MIN_TIP_SOL : sol;
244
+ const lamports = Math.floor(amount * LAMPORTS_PER_SOL);
245
+ const tipAccount = TIP_ACCOUNTS[Math.floor(Math.random() * TIP_ACCOUNTS.length)];
246
+ return SystemProgram.transfer({
247
+ fromPubkey: payer,
248
+ toPubkey: tipAccount,
249
+ lamports
250
+ });
251
+ }
252
+ async function fetchTipAccounts() {
253
+ const res = await fetch(TIP_API_URL);
254
+ if (!res.ok) {
255
+ throw new SolamiError("API_ERROR", `fetch tip accounts failed: ${res.status}`);
256
+ }
257
+ const addresses = await res.json();
258
+ return addresses.map((a) => new PublicKey2(a));
259
+ }
260
+ function extractTip(tx, tipPubkeys = TIP_ACCOUNTS) {
261
+ const keys = tx.message.staticAccountKeys;
262
+ const systemProgramId = SystemProgram.programId;
263
+ for (const ix of tx.message.compiledInstructions) {
264
+ const programKey = keys[ix.programIdIndex];
265
+ if (!programKey || !programKey.equals(systemProgramId)) continue;
266
+ if (ix.data.length < 12) continue;
267
+ const ixType = ix.data[0] | ix.data[1] << 8 | ix.data[2] << 16 | ix.data[3] << 24;
268
+ if (ixType !== 2) continue;
269
+ const view = new DataView(ix.data.buffer, ix.data.byteOffset);
270
+ const lamports = Number(view.getBigUint64(4, true));
271
+ const toIndex = ix.accountKeyIndexes[1];
272
+ if (toIndex === void 0) continue;
273
+ const toPubkey = keys[toIndex];
274
+ if (!toPubkey) continue;
275
+ if (tipPubkeys.some((tip) => tip.equals(toPubkey))) {
276
+ return { lamports, tipAccount: toPubkey };
277
+ }
278
+ }
279
+ return null;
280
+ }
281
+
282
+ // src/client.ts
283
+ var SolamiClient = class {
284
+ config;
285
+ rpc;
286
+ ws;
287
+ grpc;
288
+ swqos;
289
+ constructor(config, swqos) {
290
+ this.config = config;
291
+ this.rpc = new SolamiRpc(config);
292
+ this.ws = new SolamiWs(config);
293
+ this.grpc = new SolamiGrpc(config);
294
+ this.swqos = swqos;
295
+ }
296
+ get connection() {
297
+ return this.rpc.connection;
298
+ }
299
+ async landTransaction(tx) {
300
+ if (!this.swqos) {
301
+ throw new SolamiError("SWQOS_ERROR", "client was built without swqos support");
302
+ }
303
+ if (!extractTip(tx, TIP_ACCOUNTS)) {
304
+ throw new SolamiError("TIP_MISSING", "transaction must include a tip instruction");
305
+ }
306
+ return this.swqos.sendTransaction(tx);
307
+ }
308
+ };
309
+
310
+ // src/builder.ts
311
+ var SolamiBuilder = class _SolamiBuilder {
312
+ _rpcToken;
313
+ _grpcToken;
314
+ _rpcBase;
315
+ _wsBase;
316
+ _grpcUrl;
317
+ _swqosKey;
318
+ constructor(rpcToken) {
319
+ this._rpcToken = rpcToken;
320
+ }
321
+ static fromEnv() {
322
+ const builder = new _SolamiBuilder(process.env[ENV_RPC_TOKEN] ?? void 0);
323
+ builder._grpcToken = process.env[ENV_GRPC_TOKEN] ?? void 0;
324
+ builder._swqosKey = process.env[ENV_SWQOS_KEY] ?? void 0;
325
+ return builder;
326
+ }
327
+ grpcToken(token) {
328
+ this._grpcToken = token;
329
+ return this;
330
+ }
331
+ rpcBase(url) {
332
+ this._rpcBase = url;
333
+ return this;
334
+ }
335
+ wsBase(url) {
336
+ this._wsBase = url;
337
+ return this;
338
+ }
339
+ grpcUrl(url) {
340
+ this._grpcUrl = url;
341
+ return this;
342
+ }
343
+ swqosKey(key) {
344
+ this._swqosKey = key;
345
+ return this;
346
+ }
347
+ intoConfig() {
348
+ const rpcToken = this._rpcToken;
349
+ if (!rpcToken) {
350
+ throw new SolamiError(
351
+ "CONFIG_MISSING",
352
+ `missing ${ENV_RPC_TOKEN} (set via env or new SolamiBuilder("token"))`
353
+ );
354
+ }
355
+ return {
356
+ rpcToken,
357
+ grpcToken: this._grpcToken ?? rpcToken,
358
+ rpcBase: this._rpcBase ?? DEFAULT_RPC_BASE,
359
+ wsBase: this._wsBase ?? DEFAULT_WS_BASE,
360
+ grpcUrl: this._grpcUrl ?? DEFAULT_GRPC_URL,
361
+ swqosKey: this._swqosKey
362
+ };
363
+ }
364
+ build() {
365
+ const config = this.intoConfig();
366
+ return new SolamiClient(config);
367
+ }
368
+ buildWithSwqos() {
369
+ const config = this.intoConfig();
370
+ if (!config.swqosKey) {
371
+ throw new SolamiError(
372
+ "CONFIG_MISSING",
373
+ `missing ${ENV_SWQOS_KEY} (set via env or .swqosKey("key"))`
374
+ );
375
+ }
376
+ const swqos = new SwqosClient(config.swqosKey);
377
+ return new SolamiClient(config, swqos);
378
+ }
379
+ };
380
+
381
+ // src/index.ts
382
+ function solami(token) {
383
+ return new SolamiBuilder(token);
384
+ }
385
+ function solamiFromEnv() {
386
+ return SolamiBuilder.fromEnv();
387
+ }
388
+ export {
389
+ CommitmentLevel,
390
+ DEFAULT_GRPC_URL,
391
+ DEFAULT_RPC_BASE,
392
+ DEFAULT_WS_BASE,
393
+ ENDPOINTS,
394
+ ENV_GRPC_TOKEN,
395
+ ENV_RPC_TOKEN,
396
+ ENV_SWQOS_KEY,
397
+ MIN_TIP_SOL,
398
+ SolamiBuilder,
399
+ SolamiClient,
400
+ SolamiError,
401
+ SolamiGrpc,
402
+ SolamiRpc,
403
+ SolamiWs,
404
+ SubscriptionBuilder,
405
+ SwqosClient,
406
+ TIP_ACCOUNTS,
407
+ TIP_API_URL,
408
+ buildTipIx,
409
+ configFromEnv,
410
+ createConfig,
411
+ extractTip,
412
+ fetchTipAccounts,
413
+ rpcUrl,
414
+ solami,
415
+ solamiFromEnv,
416
+ wsUrl
417
+ };
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "solami",
3
+ "version": "0.1.7",
4
+ "type": "module",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "require": {
15
+ "types": "./dist/index.d.cts",
16
+ "default": "./dist/index.cjs"
17
+ }
18
+ }
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "engines": {
24
+ "node": ">=18"
25
+ },
26
+ "scripts": {
27
+ "build": "tsup",
28
+ "test": "vitest run",
29
+ "typecheck": "tsc --noEmit"
30
+ },
31
+ "dependencies": {
32
+ "@solana/web3.js": "^1.98.0",
33
+ "@triton-one/yellowstone-grpc": "^1.4.0",
34
+ "bs58": "^6.0.0",
35
+ "dotenv": "^17.3.1"
36
+ },
37
+ "devDependencies": {
38
+ "tsup": "^8.5.0",
39
+ "typescript": "^5.5.0",
40
+ "vitest": "^3.0.0"
41
+ }
42
+ }