@unio-aggregator/unio-tx-sender 1.0.0 → 1.0.2

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/dist/index.js CHANGED
@@ -11,7 +11,6 @@ import {
11
11
  setTransactionMessageLifetimeUsingBlockhash,
12
12
  signTransactionMessageWithSigners
13
13
  } from "@solana/kit";
14
- import { fetchAllMaybeAddressLookupTable } from "@solana-program/address-lookup-table";
15
14
 
16
15
  // src/computeBudget.ts
17
16
  import { isWritableRole, prependTransactionMessageInstruction } from "@solana/kit";
@@ -19,37 +18,34 @@ import { getSetComputeUnitLimitInstruction, getSetComputeUnitPriceInstruction }
19
18
 
20
19
  // src/config.ts
21
20
  var DEFAULT_COMPUTE_UNIT_MARGIN_MULTIPLIER = 1.15;
22
- var DEFAULT_TRANSACTION_CONFIG = {
23
- priorityFee: {
24
- type: "none",
25
- priorityFeePercentile: "50"
26
- },
27
- jito: {
28
- type: "none",
29
- priorityFeePercentile: "50"
30
- },
31
- computeUnitMarginMultiplier: DEFAULT_COMPUTE_UNIT_MARGIN_MULTIPLIER,
32
- jitoBlockEngineUrl: "https://bundles.jito.wtf"
33
- };
21
+ var DEFAULT_JITO_BLOCK_ENGINE_URL = "https://bundles.jito.wtf";
34
22
 
35
23
  // src/computeBudget.ts
36
24
  async function processComputeBudgetForTxMessage(rpc, message, computeUnits, config) {
37
- const priorityFee = config.priorityFee;
38
25
  const computeUnitMarginMultiplier = config.computeUnitMarginMultiplier;
39
26
  let priorityFeeMicroLamports = 0n;
40
- if (priorityFee.type === "exact") {
41
- priorityFeeMicroLamports = priorityFee.amountLamports * 1000000n / BigInt(computeUnits);
42
- } else if (priorityFee.type === "dynamic") {
43
- const estimatedPriorityFee = await calculateDynamicPriorityFees(
44
- rpc,
45
- message.instructions,
46
- priorityFee.priorityFeePercentile ?? "50"
47
- );
48
- if (!priorityFee.maxCapLamports) {
49
- priorityFeeMicroLamports = estimatedPriorityFee;
50
- } else {
51
- const maxCapMicroLamports = priorityFee.maxCapLamports * 1000000n / BigInt(computeUnits);
52
- priorityFeeMicroLamports = maxCapMicroLamports > estimatedPriorityFee ? estimatedPriorityFee : maxCapMicroLamports;
27
+ const units = BigInt(
28
+ Math.min(
29
+ 14e5,
30
+ Math.ceil(computeUnits * (computeUnitMarginMultiplier ?? DEFAULT_COMPUTE_UNIT_MARGIN_MULTIPLIER))
31
+ )
32
+ );
33
+ const priorityFee = config.priorityFee;
34
+ if (priorityFee) {
35
+ if (priorityFee.type === "exact") {
36
+ priorityFeeMicroLamports = priorityFee.amountLamports * 1000000n / units;
37
+ } else if (priorityFee.type === "dynamic") {
38
+ const estimatedPriorityFee = await calculateDynamicPriorityFees(
39
+ rpc,
40
+ message.instructions,
41
+ priorityFee.priorityFeePercentile ?? "50"
42
+ );
43
+ if (!priorityFee.maxCapLamports) {
44
+ priorityFeeMicroLamports = estimatedPriorityFee;
45
+ } else {
46
+ const maxCapMicroLamports = priorityFee.maxCapLamports * 1000000n / units;
47
+ priorityFeeMicroLamports = maxCapMicroLamports > estimatedPriorityFee ? estimatedPriorityFee : maxCapMicroLamports;
48
+ }
53
49
  }
54
50
  }
55
51
  if (priorityFeeMicroLamports > 0) {
@@ -62,7 +58,7 @@ async function processComputeBudgetForTxMessage(rpc, message, computeUnits, conf
62
58
  }
63
59
  message = prependTransactionMessageInstruction(
64
60
  getSetComputeUnitLimitInstruction({
65
- units: Math.ceil(computeUnits * (computeUnitMarginMultiplier ?? DEFAULT_COMPUTE_UNIT_MARGIN_MULTIPLIER))
61
+ units: computeUnits
66
62
  }),
67
63
  message
68
64
  );
@@ -88,18 +84,38 @@ async function calculateDynamicPriorityFees(rpc, instructions, percentile) {
88
84
  }
89
85
 
90
86
  // src/jito.ts
91
- import { address, lamports, prependTransactionMessageInstruction as prependTransactionMessageInstruction2 } from "@solana/kit";
87
+ import {
88
+ appendTransactionMessageInstruction,
89
+ getBase64EncodedWireTransaction
90
+ } from "@solana/kit";
91
+ import { address, lamports } from "@solana/kit";
92
92
  import { getTransferSolInstruction } from "@solana-program/system";
93
+ var JITO_BUNDLE_EXPIRY_TIME = 3e4;
94
+ var JitoApiUrl = /* @__PURE__ */ ((JitoApiUrl2) => {
95
+ JitoApiUrl2["Amsterdam"] = "https://amsterdam.mainnet.block-engine.jito.wtf";
96
+ JitoApiUrl2["Frankfurt"] = "https://frankfurt.mainnet.block-engine.jito.wtf";
97
+ JitoApiUrl2["London"] = "https://london.mainnet.block-engine.jito.wtf";
98
+ JitoApiUrl2["NY"] = "https://ny.mainnet.block-engine.jito.wtf";
99
+ JitoApiUrl2["Singapore"] = "https://singapore.mainnet.block-engine.jito.wtf";
100
+ JitoApiUrl2["SLC"] = "https://slc.mainnet.block-engine.jito.wtf";
101
+ JitoApiUrl2["Tokyo"] = "https://tokyo.mainnet.block-engine.jito.wtf";
102
+ JitoApiUrl2["Default"] = "https://mainnet.block-engine.jito.wtf";
103
+ return JitoApiUrl2;
104
+ })(JitoApiUrl || {});
93
105
  async function processJitoTipForTxMessage(message, signer, config) {
94
- let jitoTipLamports = BigInt(0);
95
106
  const jito = config.jito;
107
+ if (!jito) throw new Error("jito config is undefined");
108
+ let jitoTipLamports = BigInt(0);
96
109
  if (jito.type === "exact") {
97
110
  jitoTipLamports = jito.amountLamports;
98
111
  } else if (jito.type === "dynamic") {
99
112
  jitoTipLamports = await recentJitoTip(config);
113
+ if (jito.maxCapLamports && jitoTipLamports > jito.maxCapLamports) {
114
+ jitoTipLamports = jito.maxCapLamports;
115
+ }
100
116
  }
101
117
  if (jitoTipLamports > 0) {
102
- return prependTransactionMessageInstruction2(
118
+ return appendTransactionMessageInstruction(
103
119
  getTransferSolInstruction({
104
120
  source: signer,
105
121
  destination: getJitoTipAddress(),
@@ -112,7 +128,8 @@ async function processJitoTipForTxMessage(message, signer, config) {
112
128
  }
113
129
  }
114
130
  async function recentJitoTip(config) {
115
- const response = await fetch(`${config.jitoBlockEngineUrl}/api/v1/bundles/tip_floor`);
131
+ const baseUrl = config.jitoBlockEngineUrl ?? DEFAULT_JITO_BLOCK_ENGINE_URL;
132
+ const response = await fetch(`${baseUrl}/api/v1/bundles/tip_floor`);
116
133
  if (!response.ok) {
117
134
  return BigInt(0);
118
135
  }
@@ -125,7 +142,7 @@ async function recentJitoTip(config) {
125
142
  "99": "landed_tips_99th_percentile",
126
143
  "50ema": "ema_landed_tips_50th_percentile"
127
144
  };
128
- const key = percentileToKey[config.priorityFee.priorityFeePercentile ?? "50"];
145
+ const key = percentileToKey[config.jito?.priorityFeePercentile ?? "50"];
129
146
  if (!key || !data[key]) {
130
147
  return BigInt(0);
131
148
  }
@@ -144,21 +161,128 @@ var jitoTipAddresses = [
144
161
  function getJitoTipAddress() {
145
162
  return address(jitoTipAddresses[Math.floor(Math.random() * jitoTipAddresses.length)]);
146
163
  }
164
+ async function sendJitoBundle(jitoApiUrl, transactions, jitoUUID) {
165
+ const encodedTransactions = transactions.map((tx) => getBase64EncodedWireTransaction(tx));
166
+ const url = new URL("/api/v1/bundles", jitoApiUrl);
167
+ if (jitoUUID && jitoUUID.length > 0) {
168
+ url.searchParams.set("uuid", jitoUUID);
169
+ }
170
+ const request = {
171
+ id: 1,
172
+ jsonrpc: "2.0",
173
+ method: "sendBundle",
174
+ params: [
175
+ encodedTransactions,
176
+ {
177
+ encoding: "base64"
178
+ }
179
+ ]
180
+ };
181
+ const response = await fetch(url, {
182
+ method: "POST",
183
+ headers: {
184
+ "Content-Type": "application/json"
185
+ },
186
+ body: JSON.stringify(request)
187
+ });
188
+ if (!response.ok) {
189
+ throw new Error(`Jito HTTP error: ${response.status}`);
190
+ }
191
+ const data = await response.json();
192
+ if (data.error) {
193
+ throw new Error(`Jito bundle failed: ${data.error.code} ${data.error.message}`);
194
+ }
195
+ if (!data.result) {
196
+ throw new Error("sendBundle did not return a bundle id");
197
+ }
198
+ const bundleId = data.result;
199
+ const expiryTime = Date.now() + JITO_BUNDLE_EXPIRY_TIME;
200
+ while (Date.now() < expiryTime) {
201
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
202
+ const request2 = {
203
+ jsonrpc: "2.0",
204
+ id: 1,
205
+ method: "getInflightBundleStatuses",
206
+ params: [[bundleId]]
207
+ };
208
+ const response2 = await fetch(url, {
209
+ method: "POST",
210
+ headers: {
211
+ "Content-Type": "application/json"
212
+ },
213
+ body: JSON.stringify(request2)
214
+ });
215
+ if (!response2.ok) {
216
+ throw new Error(`Jito HTTP error: ${response2.status}`);
217
+ }
218
+ const data2 = await response2.json();
219
+ const result = data2.result?.value?.[0];
220
+ if (!result) {
221
+ throw new Error(`getInflightBundleStatuses method returned undefined result`, { cause: { bundleId } });
222
+ }
223
+ switch (result.status) {
224
+ case "Landed":
225
+ return bundleId;
226
+ case "Pending":
227
+ case "Invalid":
228
+ continue;
229
+ case "Failed":
230
+ throw new Error(`Bundle failed`, { cause: { bundleId } });
231
+ default:
232
+ throw new Error(`Bundle status is unknown`, { cause: { bundleId } });
233
+ }
234
+ }
235
+ throw new Error(`Bundle expired`, { cause: { bundleId } });
236
+ }
237
+ async function simulateBundle(rpc, transactions) {
238
+ const encodedTransactions = transactions.map((tx) => getBase64EncodedWireTransaction(tx));
239
+ return rpc.simulateBundle({
240
+ encodedTransactions
241
+ }).send();
242
+ }
243
+ async function sendBundle(rpc, transactions) {
244
+ const encodedTransactions = transactions.map((tx) => getBase64EncodedWireTransaction(tx));
245
+ const bundleId = await rpc.sendBundle(encodedTransactions, {
246
+ encoding: "base64"
247
+ }).send();
248
+ if (!bundleId) throw new Error(`sendBundle method failed`);
249
+ const expiryTime = Date.now() + JITO_BUNDLE_EXPIRY_TIME;
250
+ while (Date.now() < expiryTime) {
251
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
252
+ const statuses = await rpc.getBundleStatuses([bundleId]).send();
253
+ const bundleStatus = statuses?.value?.[0];
254
+ if (!bundleStatus) continue;
255
+ if (bundleStatus.err && "Err" in bundleStatus.err) {
256
+ throw new Error(`Bundle failed`, { cause: { bundleId, error: bundleStatus.err.Err } });
257
+ } else {
258
+ const confirmation_status = bundleStatus.confirmation_status;
259
+ if (confirmation_status == "confirmed") {
260
+ return bundleId;
261
+ }
262
+ }
263
+ }
264
+ throw new Error(`Bundle expired`, { cause: { bundleId } });
265
+ }
147
266
 
148
267
  // src/buildTransaction.ts
149
- async function buildTransaction(rpc, config, instructions, feePayer, lookupTableAddresses) {
268
+ async function buildTransaction(rpc, instructions, feePayer, config, lookupTableAccounts) {
150
269
  return buildTransactionMessage(
151
270
  rpc,
152
- config,
153
271
  instructions,
154
272
  !("address" in feePayer) ? createNoopSigner(feePayer) : feePayer,
155
- lookupTableAddresses
273
+ config,
274
+ lookupTableAccounts
156
275
  );
157
276
  }
158
- async function buildTransactionMessage(rpc, config, instructions, signer, lookupTableAddresses) {
159
- let message = await prepareTransactionMessage(instructions, rpc, signer);
160
- if (lookupTableAddresses?.length) {
161
- const lookupTableAccounts = await fetchAllMaybeAddressLookupTable(rpc, lookupTableAddresses);
277
+ async function buildTransactionMessage(rpc, instructions, signer, config, lookupTableAccounts) {
278
+ let blockhash = config.blockhash;
279
+ if (!blockhash) {
280
+ blockhash = (await rpc.getLatestBlockhash({
281
+ commitment: "confirmed"
282
+ }).send()).value;
283
+ }
284
+ let message = await prepareTransactionMessage(instructions, signer, blockhash);
285
+ if (lookupTableAccounts?.length) {
162
286
  const tables = lookupTableAccounts.reduce(
163
287
  (prev, account) => {
164
288
  if (account.exists) {
@@ -174,10 +298,7 @@ async function buildTransactionMessage(rpc, config, instructions, signer, lookup
174
298
  const messageWithExtraInstructions = await addPriorityInstructions(rpc, message, signer, config);
175
299
  return signTransactionMessageWithSigners(messageWithExtraInstructions);
176
300
  }
177
- async function prepareTransactionMessage(instructions, rpc, signer) {
178
- const { value: blockhash } = await rpc.getLatestBlockhash({
179
- commitment: "confirmed"
180
- }).send();
301
+ async function prepareTransactionMessage(instructions, signer, blockhash) {
181
302
  return pipe(
182
303
  createTransactionMessage({ version: 0 }),
183
304
  (tx) => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx),
@@ -186,14 +307,16 @@ async function prepareTransactionMessage(instructions, rpc, signer) {
186
307
  );
187
308
  }
188
309
  async function addPriorityInstructions(rpc, message, signer, config) {
189
- if (config.jito.type !== "none") {
310
+ if (config.jito && config.jito.type !== "none") {
190
311
  message = await processJitoTipForTxMessage(message, signer, config);
191
312
  }
192
313
  let computeUnits = 14e5;
193
- try {
194
- computeUnits = await getComputeUnitsForTxMessage(rpc, message);
195
- } catch {
196
- console.warn("Transaction simulation failed, using 1,400,000 compute units");
314
+ if (!config.skipSimulation) {
315
+ try {
316
+ computeUnits = await getComputeUnitsForTxMessage(rpc, message);
317
+ } catch {
318
+ console.warn("Transaction simulation failed, using 1,400,000 compute units");
319
+ }
197
320
  }
198
321
  return processComputeBudgetForTxMessage(rpc, message, computeUnits, config);
199
322
  }
@@ -204,26 +327,22 @@ async function getComputeUnitsForTxMessage(rpc, txMessage) {
204
327
  // src/sendTransaction.ts
205
328
  import {
206
329
  getBase58Decoder,
207
- getBase64EncodedWireTransaction
330
+ getBase64EncodedWireTransaction as getBase64EncodedWireTransaction2
208
331
  } from "@solana/kit";
209
- async function sendTransaction(rpc, instructions, payer, config, lookupTableAddresses, commitment = "confirmed") {
210
- const tx = await buildTransaction(
211
- rpc,
212
- config ?? DEFAULT_TRANSACTION_CONFIG,
213
- instructions,
214
- payer,
215
- lookupTableAddresses
216
- );
217
- return sendSignedTransaction(rpc, tx, commitment);
332
+ async function sendTransaction(rpc, instructions, payer, config, lookupTableAccounts, commitment = "confirmed") {
333
+ const tx = await buildTransaction(rpc, instructions, payer, config ?? {}, lookupTableAccounts);
334
+ return sendSignedTransaction(rpc, tx, config?.skipSimulation ?? false, commitment);
218
335
  }
219
- async function sendSignedTransaction(rpc, transaction, commitment = "confirmed") {
336
+ async function sendSignedTransaction(rpc, transaction, skipSimulation = false, commitment = "confirmed") {
220
337
  const txSignature = getTxSignature(transaction);
221
- const encodedTransaction = getBase64EncodedWireTransaction(transaction);
222
- const simResult = await rpc.simulateTransaction(encodedTransaction, {
223
- encoding: "base64"
224
- }).send();
225
- if (simResult.value.err) {
226
- throw new Error(`Transaction simulation failed`, { cause: simResult.value });
338
+ const encodedTransaction = getBase64EncodedWireTransaction2(transaction);
339
+ if (!skipSimulation) {
340
+ const simResult = await rpc.simulateTransaction(encodedTransaction, {
341
+ encoding: "base64"
342
+ }).send();
343
+ if (simResult.value.err) {
344
+ throw new Error(`Transaction simulation failed`, { cause: simResult.value });
345
+ }
227
346
  }
228
347
  const expiryTime = Date.now() + 6e4;
229
348
  while (Date.now() < expiryTime) {
@@ -249,9 +368,16 @@ function getTxSignature(transaction) {
249
368
  }
250
369
  export {
251
370
  DEFAULT_COMPUTE_UNIT_MARGIN_MULTIPLIER,
252
- DEFAULT_TRANSACTION_CONFIG,
371
+ DEFAULT_JITO_BLOCK_ENGINE_URL,
372
+ JitoApiUrl,
253
373
  buildTransaction,
374
+ getTxSignature,
375
+ processJitoTipForTxMessage,
376
+ recentJitoTip,
377
+ sendBundle,
378
+ sendJitoBundle,
254
379
  sendSignedTransaction,
255
- sendTransaction
380
+ sendTransaction,
381
+ simulateBundle
256
382
  };
257
383
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/buildTransaction.ts","../src/computeBudget.ts","../src/config.ts","../src/jito.ts","../src/sendTransaction.ts"],"sourcesContent":["import {\n Address,\n appendTransactionMessageInstructions,\n assertAccountDecoded,\n compressTransactionMessageUsingAddressLookupTables,\n createNoopSigner,\n createTransactionMessage,\n estimateComputeUnitLimitFactory,\n Instruction,\n pipe,\n Rpc,\n SendableTransaction,\n setTransactionMessageFeePayerSigner,\n setTransactionMessageLifetimeUsingBlockhash,\n signTransactionMessageWithSigners,\n SolanaRpcApi,\n Transaction,\n TransactionMessage,\n TransactionMessageWithFeePayer,\n TransactionSigner,\n TransactionWithLifetime,\n} from \"@solana/kit\";\nimport { fetchAllMaybeAddressLookupTable } from \"@solana-program/address-lookup-table\";\n\nimport { processComputeBudgetForTxMessage } from \"./computeBudget.ts\";\nimport { TransactionConfig } from \"./config\";\nimport { processJitoTipForTxMessage } from \"./jito.ts\";\n\n/**\n * Builds and signs a transaction from the given instructions and configuration.\n *\n * @param {Rpc<SolanaRpcApi>} rpc - Solana Rpc client.\n * @param {TransactionConfig} config - The transaction config.\n * @param {Instruction[]} instructions - Array of instructions to include in the transaction\n * @param {TransactionSigner} feePayer - The signer that will pay for the transaction\n * @param {(Address | string)[]} [lookupTableAddresses] - Optional array of address lookup table addresses to compress the transaction\n *\n * @returns {Promise<Readonly<SendableTransaction & Transaction & TransactionWithLifetime>>} A signed and encoded transaction\n *\n * @example\n * const instructions = [createATAix, createTransferSolInstruction];\n * const feePayer = wallet.publicKey;\n * const message = await buildTransaction(\n * rpc,\n * instructions,\n * feePayer,\n * );\n */\nexport async function buildTransaction(\n rpc: Rpc<SolanaRpcApi>,\n config: TransactionConfig,\n instructions: Instruction[],\n feePayer: TransactionSigner | Address,\n lookupTableAddresses?: Address[],\n): Promise<Readonly<SendableTransaction & Transaction & TransactionWithLifetime>> {\n return buildTransactionMessage(\n rpc,\n config,\n instructions,\n !(\"address\" in feePayer) ? createNoopSigner(feePayer) : feePayer,\n lookupTableAddresses,\n );\n}\n\nasync function buildTransactionMessage(\n rpc: Rpc<SolanaRpcApi>,\n config: TransactionConfig,\n instructions: Instruction[],\n signer: TransactionSigner,\n lookupTableAddresses?: Address[],\n) {\n let message = await prepareTransactionMessage(instructions, rpc, signer);\n\n if (lookupTableAddresses?.length) {\n const lookupTableAccounts = await fetchAllMaybeAddressLookupTable(rpc, lookupTableAddresses);\n const tables = lookupTableAccounts.reduce(\n (prev, account) => {\n if (account.exists) {\n assertAccountDecoded(account);\n prev[account.address] = account.data.addresses;\n }\n return prev;\n },\n {} as { [address: Address]: Address[] },\n );\n message = compressTransactionMessageUsingAddressLookupTables(message, tables);\n }\n\n const messageWithExtraInstructions = await addPriorityInstructions(rpc, message, signer, config);\n\n return signTransactionMessageWithSigners(messageWithExtraInstructions);\n}\n\nasync function prepareTransactionMessage(\n instructions: Instruction[],\n rpc: Rpc<SolanaRpcApi>,\n signer: TransactionSigner,\n) {\n const { value: blockhash } = await rpc\n .getLatestBlockhash({\n commitment: \"confirmed\",\n })\n .send();\n\n return pipe(\n createTransactionMessage({ version: 0 }),\n tx => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx),\n tx => setTransactionMessageFeePayerSigner(signer, tx),\n tx => appendTransactionMessageInstructions(instructions, tx),\n );\n}\n\nasync function addPriorityInstructions(\n rpc: Rpc<SolanaRpcApi>,\n message: TransactionMessage & TransactionMessageWithFeePayer,\n signer: TransactionSigner,\n config: TransactionConfig,\n): Promise<TransactionMessage & TransactionMessageWithFeePayer> {\n if (config.jito.type !== \"none\") {\n message = await processJitoTipForTxMessage(message, signer, config);\n }\n\n let computeUnits = 1_400_000;\n\n try {\n computeUnits = await getComputeUnitsForTxMessage(rpc, message);\n } catch {\n console.warn(\"Transaction simulation failed, using 1,400,000 compute units\");\n }\n\n return processComputeBudgetForTxMessage(rpc, message, computeUnits, config);\n}\n\nasync function getComputeUnitsForTxMessage(\n rpc: Rpc<SolanaRpcApi>,\n txMessage: TransactionMessage & TransactionMessageWithFeePayer,\n) {\n return await estimateComputeUnitLimitFactory({ rpc })(txMessage);\n}\n","import {\n Address,\n Instruction,\n Rpc,\n SolanaRpcApi,\n TransactionMessage,\n TransactionMessageWithFeePayer,\n} from \"@solana/kit\";\nimport { isWritableRole, prependTransactionMessageInstruction } from \"@solana/kit\";\nimport { getSetComputeUnitLimitInstruction, getSetComputeUnitPriceInstruction } from \"@solana-program/compute-budget\";\n\nimport type { Percentile, TransactionConfig } from \"./config\";\nimport { DEFAULT_COMPUTE_UNIT_MARGIN_MULTIPLIER } from \"./config\";\n\nexport async function processComputeBudgetForTxMessage(\n rpc: Rpc<SolanaRpcApi>,\n message: TransactionMessage & TransactionMessageWithFeePayer,\n computeUnits: number,\n config: TransactionConfig,\n) {\n const priorityFee = config.priorityFee;\n const computeUnitMarginMultiplier = config.computeUnitMarginMultiplier;\n let priorityFeeMicroLamports = 0n;\n\n if (priorityFee.type === \"exact\") {\n priorityFeeMicroLamports = (priorityFee.amountLamports * 1_000_000n) / BigInt(computeUnits);\n } else if (priorityFee.type === \"dynamic\") {\n const estimatedPriorityFee = await calculateDynamicPriorityFees(\n rpc,\n message.instructions,\n priorityFee.priorityFeePercentile ?? \"50\",\n );\n\n if (!priorityFee.maxCapLamports) {\n priorityFeeMicroLamports = estimatedPriorityFee;\n } else {\n const maxCapMicroLamports = (priorityFee.maxCapLamports * 1_000_000n) / BigInt(computeUnits);\n\n priorityFeeMicroLamports =\n maxCapMicroLamports > estimatedPriorityFee ? estimatedPriorityFee : maxCapMicroLamports;\n }\n }\n\n if (priorityFeeMicroLamports > 0) {\n message = prependTransactionMessageInstruction(\n getSetComputeUnitPriceInstruction({\n microLamports: priorityFeeMicroLamports,\n }),\n message,\n );\n }\n\n message = prependTransactionMessageInstruction(\n getSetComputeUnitLimitInstruction({\n units: Math.ceil(computeUnits * (computeUnitMarginMultiplier ?? DEFAULT_COMPUTE_UNIT_MARGIN_MULTIPLIER)),\n }),\n message,\n );\n\n return message;\n}\n\nfunction getWritableAccounts(ixs: readonly Instruction[]) {\n const writable = new Set<Address>();\n ixs.forEach(ix => {\n if (ix.accounts) {\n ix.accounts.forEach(acc => {\n if (isWritableRole(acc.role)) writable.add(acc.address);\n });\n }\n });\n return Array.from(writable);\n}\n\nasync function calculateDynamicPriorityFees(\n rpc: Rpc<SolanaRpcApi>,\n instructions: readonly Instruction[],\n percentile: Percentile,\n) {\n const writableAccounts = getWritableAccounts(instructions);\n const recent = await rpc.getRecentPrioritizationFees(writableAccounts).send();\n const nonZero = recent.filter(pf => pf.prioritizationFee > 0).map(pf => pf.prioritizationFee);\n const sorted = nonZero.sort((a, b) => Number(a - b));\n return sorted[Math.floor(sorted.length * (parseInt(percentile) / 100))] || BigInt(0);\n}\n","/**\n * Default compute unit margin multiplier used to ensure sufficient compute budget.\n */\nexport const DEFAULT_COMPUTE_UNIT_MARGIN_MULTIPLIER = 1.15;\n\n/**\n * Default prioritization settings, including priority fees and Jito tips.\n */\nexport const DEFAULT_TRANSACTION_CONFIG: TransactionConfig = {\n priorityFee: {\n type: \"none\",\n priorityFeePercentile: \"50\",\n },\n jito: {\n type: \"none\",\n priorityFeePercentile: \"50\",\n },\n computeUnitMarginMultiplier: DEFAULT_COMPUTE_UNIT_MARGIN_MULTIPLIER,\n jitoBlockEngineUrl: \"https://bundles.jito.wtf\",\n};\n\n/*\nasync function getChainIdFromGenesisHash(rpc: Rpc<SolanaRpcApi>): Promise<ChainId> {\n // not all rpc endpoints support getGenesisHash\n try {\n const genesisHash = await rpc.getGenesisHash().send();\n const genesisHashToChainId: Record<string, ChainId> = {\n \"5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d\": \"solana\",\n EAQLJCV2mh23BsK2P9oYpV5CHVLDNHTxYss3URrNmg3s: \"eclipse\",\n EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG: \"solana-devnet\",\n CX4huckiV9QNAkKNVKi5Tj8nxzBive5kQimd94viMKsU: \"eclipse-testnet\",\n };\n return genesisHashToChainId[genesisHash] || \"unknown\";\n } catch (error) {\n console.warn(\"Error getting chain ID from genesis hash\", error);\n return \"unknown\";\n }\n}\n */\n\ntype FeeSetting =\n | {\n type: \"dynamic\";\n maxCapLamports?: bigint;\n }\n | {\n type: \"exact\";\n amountLamports: bigint;\n }\n | {\n type: \"none\";\n };\n\n/**\n * Configuration for transaction fees, including Jito and priority fee settings.\n */\nexport type JitoFeeSetting = FeeSetting & {\n priorityFeePercentile?: Percentile | \"50ema\";\n};\n\nexport type PriorityFeeSetting = FeeSetting & {\n priorityFeePercentile?: Percentile;\n};\n\nexport type TransactionConfig = {\n jito: JitoFeeSetting;\n priorityFee: PriorityFeeSetting;\n computeUnitMarginMultiplier: number;\n jitoBlockEngineUrl: string;\n};\n\n/**\n * Defines a percentile value for priority fee selection.\n */\nexport type Percentile = \"25\" | \"50\" | \"75\" | \"95\" | \"99\";\n\n/**\n * Represents a supported blockchain network chain ID.\n */\nexport type ChainId = \"solana\" | \"eclipse\" | \"solana-devnet\" | \"eclipse-testnet\" | \"unknown\";\n","import { Address, TransactionMessage, TransactionMessageWithFeePayer, TransactionSigner } from \"@solana/kit\";\nimport { address, lamports, prependTransactionMessageInstruction } from \"@solana/kit\";\nimport { getTransferSolInstruction } from \"@solana-program/system\";\n\nimport { type Percentile, TransactionConfig } from \"./config\";\n\nexport async function processJitoTipForTxMessage(\n message: TransactionMessage & TransactionMessageWithFeePayer,\n signer: TransactionSigner,\n config: TransactionConfig,\n) {\n let jitoTipLamports = BigInt(0);\n const jito = config.jito;\n\n if (jito.type === \"exact\") {\n jitoTipLamports = jito.amountLamports;\n } else if (jito.type === \"dynamic\") {\n jitoTipLamports = await recentJitoTip(config);\n }\n if (jitoTipLamports > 0) {\n return prependTransactionMessageInstruction(\n getTransferSolInstruction({\n source: signer,\n destination: getJitoTipAddress(),\n amount: jitoTipLamports,\n }),\n message,\n );\n } else {\n return message;\n }\n}\n\n// returns recent jito tip in lamports\nexport async function recentJitoTip(config: TransactionConfig) {\n const response = await fetch(`${config.jitoBlockEngineUrl}/api/v1/bundles/tip_floor`);\n if (!response.ok) {\n return BigInt(0);\n }\n const data = await response.json().then(res => res[0]);\n\n const percentileToKey: Record<Percentile | \"50ema\", string> = {\n \"25\": \"landed_tips_25th_percentile\",\n \"50\": \"landed_tips_50th_percentile\",\n \"75\": \"landed_tips_75th_percentile\",\n \"95\": \"landed_tips_95th_percentile\",\n \"99\": \"landed_tips_99th_percentile\",\n \"50ema\": \"ema_landed_tips_50th_percentile\",\n };\n\n const key = percentileToKey[config.priorityFee.priorityFeePercentile ?? \"50\"];\n if (!key || !data[key]) {\n return BigInt(0);\n }\n return lamports(BigInt(Math.floor(Number(data[key]) * 10 ** 9))).valueOf();\n}\n\n// should we add an argument that dictates if we should use cached value in case fetch fails ?\n\n// below is taken from legacy sdk (no need to add the whole library)\n// https://jito-foundation.gitbook.io/mev/mev-payment-and-distribution/on-chain-addresses\nconst jitoTipAddresses = [\n \"96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5\",\n \"HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe\",\n \"Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY\",\n \"ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49\",\n \"DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh\",\n \"ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt\",\n \"DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL\",\n \"3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT\",\n];\n\nfunction getJitoTipAddress(): Address {\n // just pick a random one from the list. There are multiple addresses so that no single one\n // can cause local congestion.\n return address(jitoTipAddresses[Math.floor(Math.random() * jitoTipAddresses.length)]);\n}\n","import {\n Address,\n Commitment,\n getBase58Decoder,\n getBase64EncodedWireTransaction,\n Instruction,\n KeyPairSigner,\n Rpc,\n SendableTransaction,\n Signature,\n SolanaRpcApi,\n Transaction,\n TransactionWithLifetime,\n} from \"@solana/kit\";\n\nimport { buildTransaction } from \"./buildTransaction\";\nimport { DEFAULT_TRANSACTION_CONFIG, TransactionConfig } from \"./config\";\n\n/**\n * Builds and sends a transaction with the given instructions, signers, and commitment level.\n *\n * @param {Rpc<SolanaRpcApi>} rpc - Solana Rpc client.\n * @param {Instruction[]} instructions - Array of instructions to include in the transaction.\n * @param {KeyPairSigner} payer - The fee payer for the transaction.\n * @param {TransactionConfig} config - The transaction config.\n * @param {Address[]} [lookupTableAddresses] - Optional array of address lookup table addresses to use.\n * @param {Commitment} [commitment=\"confirmed\"] - The commitment level for transaction confirmation.\n *\n * @returns {Promise<Signature>} A promise that resolves to the transaction signature.\n *\n * @throws {Error} If transaction building or sending fails.\n *\n * @example\n * ```ts\n * const signature = await sendTransaction(\n * instructions,\n * keypairSigner,\n * DEFAULT_TRANSACTION_CONFIG,\n * lookupTables,\n * \"finalized\"\n * );\n * ```\n */\nexport async function sendTransaction(\n rpc: Rpc<SolanaRpcApi>,\n instructions: Instruction[],\n payer: KeyPairSigner,\n config?: TransactionConfig,\n lookupTableAddresses?: Address[],\n commitment: Commitment = \"confirmed\",\n): Promise<Signature> {\n const tx = await buildTransaction(\n rpc,\n config ?? DEFAULT_TRANSACTION_CONFIG,\n instructions,\n payer,\n lookupTableAddresses,\n );\n return sendSignedTransaction(rpc, tx, commitment);\n}\n\n/**\n * Sends a signed transaction message to the Solana network with a specified commitment level.\n *\n * @param {Rpc<SolanaRpcApi>} rpc - Rpc client.\n * @param {SendableTransaction & Transaction & TransactionWithLifetime} transaction - The fully signed transaction to send.\n * @param {Commitment} [commitment=\"confirmed\"] - The commitment level for transaction confirmation.\n *\n * @returns {Promise<Signature>} A promise that resolves to the transaction signature.\n *\n * @throws {Error} If transaction sending fails, the RPC connection fails, or the transaction expires.\n *\n * @example\n * ```ts\n * const signature = await sendSignedTransaction(\n * rpc,\n * signedTransaction,\n * \"finalized\"\n * );\n * ```\n */\nexport async function sendSignedTransaction(\n rpc: Rpc<SolanaRpcApi>,\n transaction: SendableTransaction & Transaction & TransactionWithLifetime,\n commitment: Commitment = \"confirmed\",\n): Promise<Signature> {\n const txSignature = getTxSignature(transaction);\n const encodedTransaction = getBase64EncodedWireTransaction(transaction);\n\n // Simulate transaction first\n const simResult = await rpc\n .simulateTransaction(encodedTransaction, {\n encoding: \"base64\",\n })\n .send();\n\n if (simResult.value.err) {\n throw new Error(`Transaction simulation failed`, { cause: simResult.value });\n }\n\n const expiryTime = Date.now() + 60_000;\n\n while (Date.now() < expiryTime) {\n await rpc\n .sendTransaction(encodedTransaction, {\n maxRetries: BigInt(0),\n skipPreflight: true,\n encoding: \"base64\",\n })\n .send();\n\n const { value } = await rpc.getSignatureStatuses([txSignature]).send();\n const status = value[0];\n if (status?.confirmationStatus === commitment) {\n if (status.err) {\n throw new Error(`Transaction failed`, { cause: status.err });\n }\n return txSignature;\n }\n }\n\n throw new Error(\"Transaction expired\");\n}\n\nfunction getTxSignature(transaction: Transaction) {\n const [signature] = Object.values(transaction.signatures);\n return getBase58Decoder().decode(signature!) as Signature;\n}\n"],"mappings":"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","names":["prependTransactionMessageInstruction"]}
1
+ {"version":3,"sources":["../src/buildTransaction.ts","../src/computeBudget.ts","../src/config.ts","../src/jito.ts","../src/sendTransaction.ts"],"sourcesContent":["import {\n Address,\n appendTransactionMessageInstructions,\n assertAccountDecoded,\n BlockhashLifetimeConstraint,\n compressTransactionMessageUsingAddressLookupTables,\n createNoopSigner,\n createTransactionMessage,\n estimateComputeUnitLimitFactory,\n Instruction,\n MaybeAccount,\n pipe,\n Rpc,\n SendableTransaction,\n setTransactionMessageFeePayerSigner,\n setTransactionMessageLifetimeUsingBlockhash,\n signTransactionMessageWithSigners,\n SolanaRpcApi,\n Transaction,\n TransactionMessage,\n TransactionMessageWithFeePayer,\n TransactionSigner,\n TransactionWithLifetime,\n} from \"@solana/kit\";\nimport { AddressLookupTable } from \"@solana-program/address-lookup-table\";\n\nimport { processComputeBudgetForTxMessage } from \"./computeBudget.ts\";\nimport { TransactionConfig } from \"./config\";\nimport { processJitoTipForTxMessage } from \"./jito.ts\";\n\n/**\n * Builds and signs a transaction from the given instructions and configuration.\n *\n * @param {Rpc<SolanaRpcApi>} rpc - Solana Rpc client.\n * @param {TransactionConfig} config - The transaction config.\n * @param {Instruction[]} instructions - Array of instructions to include in the transaction\n * @param {TransactionSigner} feePayer - The signer that will pay for the transaction\n * @param {(Address | string)[]} [lookupTableAccounts] - Optional array of address lookup table accounts to compress the transaction\n *\n * @returns {Promise<Readonly<SendableTransaction & Transaction & TransactionWithLifetime>>} A signed and encoded transaction\n *\n * @example\n * const instructions = [createATAix, createTransferSolInstruction];\n * const feePayer = wallet.publicKey;\n * const message = await buildTransaction(\n * rpc,\n * instructions,\n * feePayer,\n * );\n */\nexport async function buildTransaction(\n rpc: Rpc<SolanaRpcApi>,\n instructions: Instruction[],\n feePayer: TransactionSigner | Address,\n config: TransactionConfig,\n lookupTableAccounts?: MaybeAccount<AddressLookupTable>[],\n): Promise<Readonly<SendableTransaction & Transaction & TransactionWithLifetime>> {\n return buildTransactionMessage(\n rpc,\n instructions,\n !(\"address\" in feePayer) ? createNoopSigner(feePayer) : feePayer,\n config,\n lookupTableAccounts,\n );\n}\n\nasync function buildTransactionMessage(\n rpc: Rpc<SolanaRpcApi>,\n instructions: Instruction[],\n signer: TransactionSigner,\n config: TransactionConfig,\n lookupTableAccounts?: MaybeAccount<AddressLookupTable>[],\n) {\n let blockhash = config.blockhash;\n if (!blockhash) {\n blockhash = (\n await rpc\n .getLatestBlockhash({\n commitment: \"confirmed\",\n })\n .send()\n ).value;\n }\n\n let message = await prepareTransactionMessage(instructions, signer, blockhash);\n\n if (lookupTableAccounts?.length) {\n const tables = lookupTableAccounts.reduce(\n (prev, account) => {\n if (account.exists) {\n assertAccountDecoded(account);\n prev[account.address] = account.data.addresses;\n }\n return prev;\n },\n {} as { [address: Address]: Address[] },\n );\n message = compressTransactionMessageUsingAddressLookupTables(message, tables);\n }\n\n const messageWithExtraInstructions = await addPriorityInstructions(rpc, message, signer, config);\n\n return signTransactionMessageWithSigners(messageWithExtraInstructions);\n}\n\nasync function prepareTransactionMessage(\n instructions: Instruction[],\n signer: TransactionSigner,\n blockhash: BlockhashLifetimeConstraint,\n) {\n return pipe(\n createTransactionMessage({ version: 0 }),\n tx => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx),\n tx => setTransactionMessageFeePayerSigner(signer, tx),\n tx => appendTransactionMessageInstructions(instructions, tx),\n );\n}\n\nasync function addPriorityInstructions(\n rpc: Rpc<SolanaRpcApi>,\n message: TransactionMessage & TransactionMessageWithFeePayer,\n signer: TransactionSigner,\n config: TransactionConfig,\n): Promise<TransactionMessage & TransactionMessageWithFeePayer> {\n if (config.jito && config.jito.type !== \"none\") {\n message = await processJitoTipForTxMessage(message, signer, config);\n }\n\n let computeUnits = 1_400_000;\n\n if (!config.skipSimulation) {\n try {\n computeUnits = await getComputeUnitsForTxMessage(rpc, message);\n } catch {\n console.warn(\"Transaction simulation failed, using 1,400,000 compute units\");\n }\n }\n\n return processComputeBudgetForTxMessage(rpc, message, computeUnits, config);\n}\n\nasync function getComputeUnitsForTxMessage(\n rpc: Rpc<SolanaRpcApi>,\n txMessage: TransactionMessage & TransactionMessageWithFeePayer,\n) {\n return await estimateComputeUnitLimitFactory({ rpc })(txMessage);\n}\n","import {\n Address,\n Instruction,\n Rpc,\n SolanaRpcApi,\n TransactionMessage,\n TransactionMessageWithFeePayer,\n} from \"@solana/kit\";\nimport { isWritableRole, prependTransactionMessageInstruction } from \"@solana/kit\";\nimport { getSetComputeUnitLimitInstruction, getSetComputeUnitPriceInstruction } from \"@solana-program/compute-budget\";\n\nimport type { Percentile, TransactionConfig } from \"./config\";\nimport { DEFAULT_COMPUTE_UNIT_MARGIN_MULTIPLIER } from \"./config\";\n\nexport async function processComputeBudgetForTxMessage(\n rpc: Rpc<SolanaRpcApi>,\n message: TransactionMessage & TransactionMessageWithFeePayer,\n computeUnits: number,\n config: TransactionConfig,\n) {\n const computeUnitMarginMultiplier = config.computeUnitMarginMultiplier;\n let priorityFeeMicroLamports = 0n;\n\n const units = BigInt(\n Math.min(\n 1_400_000,\n Math.ceil(computeUnits * (computeUnitMarginMultiplier ?? DEFAULT_COMPUTE_UNIT_MARGIN_MULTIPLIER)),\n ),\n );\n\n const priorityFee = config.priorityFee;\n if (priorityFee) {\n if (priorityFee.type === \"exact\") {\n priorityFeeMicroLamports = (priorityFee.amountLamports * 1_000_000n) / units;\n } else if (priorityFee.type === \"dynamic\") {\n const estimatedPriorityFee = await calculateDynamicPriorityFees(\n rpc,\n message.instructions,\n priorityFee.priorityFeePercentile ?? \"50\",\n );\n\n if (!priorityFee.maxCapLamports) {\n priorityFeeMicroLamports = estimatedPriorityFee;\n } else {\n const maxCapMicroLamports = (priorityFee.maxCapLamports * 1_000_000n) / units;\n priorityFeeMicroLamports =\n maxCapMicroLamports > estimatedPriorityFee ? estimatedPriorityFee : maxCapMicroLamports;\n }\n }\n }\n\n if (priorityFeeMicroLamports > 0) {\n message = prependTransactionMessageInstruction(\n getSetComputeUnitPriceInstruction({\n microLamports: priorityFeeMicroLamports,\n }),\n message,\n );\n }\n\n message = prependTransactionMessageInstruction(\n getSetComputeUnitLimitInstruction({\n units: computeUnits,\n }),\n message,\n );\n\n return message;\n}\n\nfunction getWritableAccounts(ixs: readonly Instruction[]) {\n const writable = new Set<Address>();\n ixs.forEach(ix => {\n if (ix.accounts) {\n ix.accounts.forEach(acc => {\n if (isWritableRole(acc.role)) writable.add(acc.address);\n });\n }\n });\n return Array.from(writable);\n}\n\nasync function calculateDynamicPriorityFees(\n rpc: Rpc<SolanaRpcApi>,\n instructions: readonly Instruction[],\n percentile: Percentile,\n) {\n const writableAccounts = getWritableAccounts(instructions);\n const recent = await rpc.getRecentPrioritizationFees(writableAccounts).send();\n const nonZero = recent.filter(pf => pf.prioritizationFee > 0).map(pf => pf.prioritizationFee);\n const sorted = nonZero.sort((a, b) => Number(a - b));\n return sorted[Math.floor(sorted.length * (parseInt(percentile) / 100))] || BigInt(0);\n}\n","import { BlockhashLifetimeConstraint } from \"@solana/kit\";\n\n/**\n * Default compute unit margin multiplier used to ensure sufficient compute budget.\n */\nexport const DEFAULT_COMPUTE_UNIT_MARGIN_MULTIPLIER = 1.15;\nexport const DEFAULT_JITO_BLOCK_ENGINE_URL = \"https://bundles.jito.wtf\";\n\ntype FeeSetting =\n | {\n type: \"dynamic\";\n maxCapLamports?: bigint;\n }\n | {\n type: \"exact\";\n amountLamports: bigint;\n }\n | {\n type: \"none\";\n };\n\n/**\n * Configuration for transaction fees, including Jito and priority fee settings.\n */\nexport type JitoFeeSetting = FeeSetting & {\n priorityFeePercentile?: Percentile | \"50ema\";\n};\n\nexport type PriorityFeeSetting = FeeSetting & {\n priorityFeePercentile?: Percentile;\n};\n\n/**\n * Defines a percentile value for priority fee selection.\n */\nexport type Percentile = \"25\" | \"50\" | \"75\" | \"95\" | \"99\";\n\nexport type TransactionConfig = {\n jito?: JitoFeeSetting;\n priorityFee?: PriorityFeeSetting;\n computeUnitMarginMultiplier?: number;\n jitoBlockEngineUrl?: string;\n skipSimulation?: boolean;\n blockhash?: BlockhashLifetimeConstraint;\n};\n","import {\n Address,\n appendTransactionMessageInstruction,\n getBase64EncodedWireTransaction,\n Rpc,\n SendableTransaction,\n SolanaRpcApi,\n Transaction,\n TransactionMessage,\n TransactionMessageWithFeePayer,\n TransactionSigner,\n TransactionWithLifetime,\n} from \"@solana/kit\";\nimport { address, lamports } from \"@solana/kit\";\nimport { getTransferSolInstruction } from \"@solana-program/system\";\n\nimport { DEFAULT_JITO_BLOCK_ENGINE_URL, type Percentile, TransactionConfig } from \"./config\";\n\nconst JITO_BUNDLE_EXPIRY_TIME = 30_000;\n\nexport type AccountConfig = {\n accountIndex: number;\n addresses: string[];\n};\n\nexport type JitoAccountInfo = {\n executable: boolean;\n owner: string;\n lamports: number;\n data: string;\n rentEpoch: number;\n};\n\nexport type JitoRpcApi = SolanaRpcApi & {\n simulateBundle: (\n params: {\n encodedTransactions: string[];\n },\n config?: {\n preExecutionAccountsConfigs?: AccountConfig;\n postExecutionAccountsConfigs?: AccountConfig;\n skipSigVerify?: boolean;\n simulationBank?: {\n commitment?: {\n commitment: \"processed\" | \"confirmed\" | \"finalized\";\n };\n };\n transactionEncoding?: \"base64\";\n replaceRecentBlockhash?: boolean;\n },\n ) => SimulateBundleResponse;\n\n sendBundle: (\n params: string[],\n config?: {\n encoding?: \"base64\";\n },\n ) => string;\n\n getBundleStatuses: (params: string[]) => GetBundleStatusesResponse;\n};\n\nexport type SimulateBundleResponse = {\n context: {\n slot: number;\n apiVersion?: string;\n };\n value: {\n summary: \"succeeded\" | { failed: { error: unknown; tx_signature: string } };\n transactionResults: Array<{\n err: unknown;\n logs: string[] | null;\n unitsConsumed?: number;\n returnData: {\n programId: string;\n data: string;\n } | null;\n preExecutionAccounts: JitoAccountInfo[] | null;\n postExecutionAccounts: JitoAccountInfo[] | null;\n }>;\n };\n};\n\nexport type GetBundleStatusesResponse = {\n context: {\n slot: number;\n };\n value: Array<{\n bundleId: string;\n transactions: string[];\n slot: number;\n confirmation_status: \"processed\" | \"confirmed\" | \"finalized\";\n err:\n | {\n Ok: null;\n }\n | {\n Err: object;\n };\n }>;\n};\n\nexport enum JitoApiUrl {\n Amsterdam = \"https://amsterdam.mainnet.block-engine.jito.wtf\",\n Frankfurt = \"https://frankfurt.mainnet.block-engine.jito.wtf\",\n London = \"https://london.mainnet.block-engine.jito.wtf\",\n NY = \"https://ny.mainnet.block-engine.jito.wtf\",\n Singapore = \"https://singapore.mainnet.block-engine.jito.wtf\",\n SLC = \"https://slc.mainnet.block-engine.jito.wtf\",\n Tokyo = \"https://tokyo.mainnet.block-engine.jito.wtf\",\n Default = \"https://mainnet.block-engine.jito.wtf\",\n}\n\nexport async function processJitoTipForTxMessage(\n message: TransactionMessage & TransactionMessageWithFeePayer,\n signer: TransactionSigner,\n config: TransactionConfig,\n) {\n const jito = config.jito;\n if (!jito) throw new Error(\"jito config is undefined\");\n\n let jitoTipLamports = BigInt(0);\n\n if (jito.type === \"exact\") {\n jitoTipLamports = jito.amountLamports;\n } else if (jito.type === \"dynamic\") {\n jitoTipLamports = await recentJitoTip(config);\n if (jito.maxCapLamports && jitoTipLamports > jito.maxCapLamports) {\n jitoTipLamports = jito.maxCapLamports;\n }\n }\n\n if (jitoTipLamports > 0) {\n return appendTransactionMessageInstruction(\n getTransferSolInstruction({\n source: signer,\n destination: getJitoTipAddress(),\n amount: jitoTipLamports,\n }),\n message,\n );\n } else {\n return message;\n }\n}\n\n// returns recent jito tip in lamports\nexport async function recentJitoTip(config: TransactionConfig) {\n const baseUrl = config.jitoBlockEngineUrl ?? DEFAULT_JITO_BLOCK_ENGINE_URL;\n const response = await fetch(`${baseUrl}/api/v1/bundles/tip_floor`);\n if (!response.ok) {\n return BigInt(0);\n }\n const data = await response.json().then(res => res[0]);\n\n const percentileToKey: Record<Percentile | \"50ema\", string> = {\n \"25\": \"landed_tips_25th_percentile\",\n \"50\": \"landed_tips_50th_percentile\",\n \"75\": \"landed_tips_75th_percentile\",\n \"95\": \"landed_tips_95th_percentile\",\n \"99\": \"landed_tips_99th_percentile\",\n \"50ema\": \"ema_landed_tips_50th_percentile\",\n };\n\n const key = percentileToKey[config.jito?.priorityFeePercentile ?? \"50\"];\n if (!key || !data[key]) {\n return BigInt(0);\n }\n return lamports(BigInt(Math.floor(Number(data[key]) * 10 ** 9))).valueOf();\n}\n\n// should we add an argument that dictates if we should use cached value in case fetch fails ?\n\n// below is taken from legacy sdk (no need to add the whole library)\n// https://jito-foundation.gitbook.io/mev/mev-payment-and-distribution/on-chain-addresses\nconst jitoTipAddresses = [\n \"96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5\",\n \"HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe\",\n \"Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY\",\n \"ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49\",\n \"DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh\",\n \"ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt\",\n \"DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL\",\n \"3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT\",\n];\n\nfunction getJitoTipAddress(): Address {\n // just pick a random one from the list. There are multiple addresses so that no single one can cause local congestion.\n return address(jitoTipAddresses[Math.floor(Math.random() * jitoTipAddresses.length)]);\n}\n\ninterface JsonRpcResult {\n jsonrpc: string;\n id: number;\n result?: string; // bundle id\n error?: {\n code: number;\n message: string;\n data?: unknown;\n };\n}\n\nexport async function sendJitoBundle(\n jitoApiUrl: JitoApiUrl,\n transactions: (SendableTransaction & Transaction & TransactionWithLifetime)[],\n jitoUUID?: string,\n): Promise<string> {\n const encodedTransactions = transactions.map(tx => getBase64EncodedWireTransaction(tx)) as string[];\n\n const url = new URL(\"/api/v1/bundles\", jitoApiUrl);\n if (jitoUUID && jitoUUID.length > 0) {\n url.searchParams.set(\"uuid\", jitoUUID);\n }\n\n const request = {\n id: 1,\n jsonrpc: \"2.0\",\n method: \"sendBundle\",\n params: [\n encodedTransactions,\n {\n encoding: \"base64\",\n },\n ],\n };\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n throw new Error(`Jito HTTP error: ${response.status}`);\n }\n\n const data = (await response.json()) as JsonRpcResult;\n\n if (data.error) {\n throw new Error(`Jito bundle failed: ${data.error.code} ${data.error.message}`);\n }\n\n if (!data.result) {\n throw new Error(\"sendBundle did not return a bundle id\");\n }\n\n const bundleId = data.result;\n\n const expiryTime = Date.now() + JITO_BUNDLE_EXPIRY_TIME;\n\n while (Date.now() < expiryTime) {\n await new Promise(resolve => setTimeout(resolve, 1000));\n\n const request = {\n jsonrpc: \"2.0\",\n id: 1,\n method: \"getInflightBundleStatuses\",\n params: [[bundleId]],\n };\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n throw new Error(`Jito HTTP error: ${response.status}`);\n }\n\n const data = await response.json();\n const result = data.result?.value?.[0];\n if (!result) {\n throw new Error(`getInflightBundleStatuses method returned undefined result`, { cause: { bundleId } });\n }\n\n switch (result.status) {\n case \"Landed\":\n return bundleId;\n case \"Pending\":\n case \"Invalid\":\n continue;\n case \"Failed\":\n throw new Error(`Bundle failed`, { cause: { bundleId } });\n default:\n throw new Error(`Bundle status is unknown`, { cause: { bundleId } });\n }\n }\n\n throw new Error(`Bundle expired`, { cause: { bundleId } });\n}\n\nexport async function simulateBundle(\n rpc: Rpc<JitoRpcApi>,\n transactions: (SendableTransaction & Transaction & TransactionWithLifetime)[],\n): Promise<SimulateBundleResponse> {\n const encodedTransactions = transactions.map(tx => getBase64EncodedWireTransaction(tx));\n\n return rpc\n .simulateBundle({\n encodedTransactions,\n })\n .send();\n}\n\nexport async function sendBundle(\n rpc: Rpc<JitoRpcApi>,\n transactions: (SendableTransaction & Transaction & TransactionWithLifetime)[],\n): Promise<string> {\n const encodedTransactions = transactions.map(tx => getBase64EncodedWireTransaction(tx));\n\n const bundleId = await rpc\n .sendBundle(encodedTransactions, {\n encoding: \"base64\",\n })\n .send();\n\n if (!bundleId) throw new Error(`sendBundle method failed`);\n\n const expiryTime = Date.now() + JITO_BUNDLE_EXPIRY_TIME;\n\n while (Date.now() < expiryTime) {\n await new Promise(resolve => setTimeout(resolve, 1000));\n\n const statuses = await rpc.getBundleStatuses([bundleId]).send();\n\n const bundleStatus = statuses?.value?.[0];\n if (!bundleStatus) continue;\n\n if (bundleStatus.err && \"Err\" in bundleStatus.err) {\n throw new Error(`Bundle failed`, { cause: { bundleId, error: bundleStatus.err.Err } });\n } else {\n const confirmation_status = bundleStatus.confirmation_status;\n if (confirmation_status == \"confirmed\") {\n return bundleId;\n }\n }\n }\n\n throw new Error(`Bundle expired`, { cause: { bundleId } });\n}\n","import {\n Commitment,\n getBase58Decoder,\n getBase64EncodedWireTransaction,\n Instruction,\n KeyPairSigner,\n MaybeAccount,\n Rpc,\n SendableTransaction,\n Signature,\n SolanaRpcApi,\n Transaction,\n TransactionWithLifetime,\n} from \"@solana/kit\";\nimport { AddressLookupTable } from \"@solana-program/address-lookup-table\";\n\nimport { buildTransaction } from \"./buildTransaction\";\nimport { TransactionConfig } from \"./config\";\n\n/**\n * Builds and sends a transaction with the given instructions, signers, and commitment level.\n *\n * @param {Rpc<SolanaRpcApi>} rpc - Solana Rpc client.\n * @param {Instruction[]} instructions - Array of instructions to include in the transaction.\n * @param {KeyPairSigner} payer - The fee payer for the transaction.\n * @param {TransactionConfig} config - The transaction config.\n * @param {Address[]} [lookupTableAccounts] - Optional array of address lookup table accounts to use.\n * @param {Commitment} [commitment=\"confirmed\"] - The commitment level for transaction confirmation.\n *\n * @returns {Promise<Signature>} A promise that resolves to the transaction signature.\n *\n * @throws {Error} If transaction building or sending fails.\n *\n * @example\n * ```ts\n * const signature = await sendTransaction(\n * instructions,\n * keypairSigner,\n * DEFAULT_TRANSACTION_CONFIG,\n * lookupTables,\n * \"finalized\"\n * );\n * ```\n */\nexport async function sendTransaction(\n rpc: Rpc<SolanaRpcApi>,\n instructions: Instruction[],\n payer: KeyPairSigner,\n config?: TransactionConfig,\n lookupTableAccounts?: MaybeAccount<AddressLookupTable>[],\n commitment: Commitment = \"confirmed\",\n): Promise<Signature> {\n const tx = await buildTransaction(rpc, instructions, payer, config ?? {}, lookupTableAccounts);\n return sendSignedTransaction(rpc, tx, config?.skipSimulation ?? false, commitment);\n}\n\n/**\n * Sends a signed transaction message to the Solana network with a specified commitment level.\n *\n * @param {Rpc<SolanaRpcApi>} rpc - Rpc client.\n * @param {SendableTransaction & Transaction & TransactionWithLifetime} transaction - The fully signed transaction to send.\n * @param {boolean} skipSimulation - True if skip simulation\n * @param {Commitment} [commitment=\"confirmed\"] - The commitment level for transaction confirmation.\n *\n * @returns {Promise<Signature>} A promise that resolves to the transaction signature.\n *\n * @throws {Error} If transaction sending fails, the RPC connection fails, or the transaction expires.\n *\n * @example\n * ```ts\n * const signature = await sendSignedTransaction(\n * rpc,\n * signedTransaction,\n * \"finalized\"\n * );\n * ```\n */\nexport async function sendSignedTransaction(\n rpc: Rpc<SolanaRpcApi>,\n transaction: SendableTransaction & Transaction & TransactionWithLifetime,\n skipSimulation: boolean = false,\n commitment: Commitment = \"confirmed\",\n): Promise<Signature> {\n const txSignature = getTxSignature(transaction);\n const encodedTransaction = getBase64EncodedWireTransaction(transaction);\n\n if (!skipSimulation) {\n // Simulate transaction first\n const simResult = await rpc\n .simulateTransaction(encodedTransaction, {\n encoding: \"base64\",\n })\n .send();\n\n if (simResult.value.err) {\n throw new Error(`Transaction simulation failed`, { cause: simResult.value });\n }\n }\n\n const expiryTime = Date.now() + 60_000;\n\n while (Date.now() < expiryTime) {\n await rpc\n .sendTransaction(encodedTransaction, {\n maxRetries: BigInt(0),\n skipPreflight: true,\n encoding: \"base64\",\n })\n .send();\n\n const { value } = await rpc.getSignatureStatuses([txSignature]).send();\n const status = value[0];\n if (status?.confirmationStatus === commitment) {\n if (status.err) {\n throw new Error(`Transaction failed`, { cause: status.err });\n }\n return txSignature;\n }\n }\n\n throw new Error(\"Transaction expired\");\n}\n\nexport function getTxSignature(transaction: Transaction) {\n const [signature] = Object.values(transaction.signatures);\n return getBase58Decoder().decode(signature!) as Signature;\n}\n"],"mappings":"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","names":["JitoApiUrl","request","response","data","getBase64EncodedWireTransaction","getBase64EncodedWireTransaction"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@unio-aggregator/unio-tx-sender",
3
3
  "description": "A TypeScript package for building and sending Solana transactions with support for priority fees and Jito tips.",
4
- "version": "1.0.0",
4
+ "version": "1.0.2",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
7
7
  "module": "./dist/index.js",
@@ -24,20 +24,14 @@
24
24
  "dist",
25
25
  "README.md"
26
26
  ],
27
- "scripts": {
28
- "build": "tsup src/index.ts --format cjs,esm --dts --sourcemap",
29
- "lint": "pnpm eslint && pnpm type-check",
30
- "eslint": "eslint \"**/*.ts*\" --fix",
31
- "type-check": "tsc -p .",
32
- "clean": "rimraf dist"
33
- },
34
27
  "dependencies": {
35
28
  "@solana-program/address-lookup-table": "^0.11.0",
36
29
  "@solana-program/compute-budget": "^0.15.0",
37
30
  "@solana-program/system": "^0.11.0",
38
31
  "@solana/kit": "^6.9.0",
39
32
  "@solana/rpc": "^6.9.0",
40
- "@solana/rpc-api": "^6.9.0"
33
+ "@solana/rpc-api": "^6.9.0",
34
+ "bs58": "^6.0.0"
41
35
  },
42
36
  "devDependencies": {
43
37
  "@unio-aggregator/prettier-config": "^1.0.1",
@@ -52,5 +46,12 @@
52
46
  "eslint": "^10.6.0",
53
47
  "eslint-plugin-prettier": "^5.5.6",
54
48
  "eslint-plugin-simple-import-sort": "^13.0.0"
49
+ },
50
+ "scripts": {
51
+ "build": "tsup src/index.ts --format cjs,esm --dts --sourcemap",
52
+ "lint": "pnpm eslint && pnpm type-check",
53
+ "eslint": "eslint \"**/*.ts*\" --fix",
54
+ "type-check": "tsc -p .",
55
+ "clean": "rimraf dist"
55
56
  }
56
57
  }