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

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.cjs CHANGED
@@ -29,7 +29,6 @@ module.exports = __toCommonJS(index_exports);
29
29
 
30
30
  // src/buildTransaction.ts
31
31
  var import_kit3 = require("@solana/kit");
32
- var import_address_lookup_table = require("@solana-program/address-lookup-table");
33
32
 
34
33
  // src/computeBudget.ts
35
34
  var import_kit = require("@solana/kit");
@@ -47,7 +46,8 @@ var DEFAULT_TRANSACTION_CONFIG = {
47
46
  priorityFeePercentile: "50"
48
47
  },
49
48
  computeUnitMarginMultiplier: DEFAULT_COMPUTE_UNIT_MARGIN_MULTIPLIER,
50
- jitoBlockEngineUrl: "https://bundles.jito.wtf"
49
+ jitoBlockEngineUrl: "https://bundles.jito.wtf",
50
+ skipSimulation: false
51
51
  };
52
52
 
53
53
  // src/computeBudget.ts
@@ -164,19 +164,18 @@ function getJitoTipAddress() {
164
164
  }
165
165
 
166
166
  // src/buildTransaction.ts
167
- async function buildTransaction(rpc, config, instructions, feePayer, lookupTableAddresses) {
167
+ async function buildTransaction(rpc, config, instructions, feePayer, lookupTableAccounts) {
168
168
  return buildTransactionMessage(
169
169
  rpc,
170
170
  config,
171
171
  instructions,
172
172
  !("address" in feePayer) ? (0, import_kit3.createNoopSigner)(feePayer) : feePayer,
173
- lookupTableAddresses
173
+ lookupTableAccounts
174
174
  );
175
175
  }
176
- async function buildTransactionMessage(rpc, config, instructions, signer, lookupTableAddresses) {
176
+ async function buildTransactionMessage(rpc, config, instructions, signer, lookupTableAccounts) {
177
177
  let message = await prepareTransactionMessage(instructions, rpc, signer);
178
- if (lookupTableAddresses?.length) {
179
- const lookupTableAccounts = await (0, import_address_lookup_table.fetchAllMaybeAddressLookupTable)(rpc, lookupTableAddresses);
178
+ if (lookupTableAccounts?.length) {
180
179
  const tables = lookupTableAccounts.reduce(
181
180
  (prev, account) => {
182
181
  if (account.exists) {
@@ -208,10 +207,12 @@ async function addPriorityInstructions(rpc, message, signer, config) {
208
207
  message = await processJitoTipForTxMessage(message, signer, config);
209
208
  }
210
209
  let computeUnits = 14e5;
211
- try {
212
- computeUnits = await getComputeUnitsForTxMessage(rpc, message);
213
- } catch {
214
- console.warn("Transaction simulation failed, using 1,400,000 compute units");
210
+ if (!config.skipSimulation) {
211
+ try {
212
+ computeUnits = await getComputeUnitsForTxMessage(rpc, message);
213
+ } catch {
214
+ console.warn("Transaction simulation failed, using 1,400,000 compute units");
215
+ }
215
216
  }
216
217
  return processComputeBudgetForTxMessage(rpc, message, computeUnits, config);
217
218
  }
@@ -221,24 +222,26 @@ async function getComputeUnitsForTxMessage(rpc, txMessage) {
221
222
 
222
223
  // src/sendTransaction.ts
223
224
  var import_kit4 = require("@solana/kit");
224
- async function sendTransaction(rpc, instructions, payer, config, lookupTableAddresses, commitment = "confirmed") {
225
+ async function sendTransaction(rpc, instructions, payer, config, lookupTableAccounts, commitment = "confirmed") {
225
226
  const tx = await buildTransaction(
226
227
  rpc,
227
228
  config ?? DEFAULT_TRANSACTION_CONFIG,
228
229
  instructions,
229
230
  payer,
230
- lookupTableAddresses
231
+ lookupTableAccounts
231
232
  );
232
- return sendSignedTransaction(rpc, tx, commitment);
233
+ return sendSignedTransaction(rpc, tx, config ?? DEFAULT_TRANSACTION_CONFIG, commitment);
233
234
  }
234
- async function sendSignedTransaction(rpc, transaction, commitment = "confirmed") {
235
+ async function sendSignedTransaction(rpc, transaction, config, commitment = "confirmed") {
235
236
  const txSignature = getTxSignature(transaction);
236
237
  const encodedTransaction = (0, import_kit4.getBase64EncodedWireTransaction)(transaction);
237
- const simResult = await rpc.simulateTransaction(encodedTransaction, {
238
- encoding: "base64"
239
- }).send();
240
- if (simResult.value.err) {
241
- throw new Error(`Transaction simulation failed`, { cause: simResult.value });
238
+ if (!config.skipSimulation) {
239
+ const simResult = await rpc.simulateTransaction(encodedTransaction, {
240
+ encoding: "base64"
241
+ }).send();
242
+ if (simResult.value.err) {
243
+ throw new Error(`Transaction simulation failed`, { cause: simResult.value });
244
+ }
242
245
  }
243
246
  const expiryTime = Date.now() + 6e4;
244
247
  while (Date.now() < expiryTime) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/buildTransaction.ts","../src/computeBudget.ts","../src/config.ts","../src/jito.ts","../src/sendTransaction.ts"],"sourcesContent":["export * from \"./buildTransaction\";\nexport * from \"./config\";\nexport * from \"./sendTransaction\";\n","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":["import_kit","import_kit","import_kit"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/buildTransaction.ts","../src/computeBudget.ts","../src/config.ts","../src/jito.ts","../src/sendTransaction.ts"],"sourcesContent":["export * from \"./buildTransaction\";\nexport * from \"./config\";\nexport * from \"./sendTransaction\";\n","import {\n Address,\n appendTransactionMessageInstructions,\n assertAccountDecoded,\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 config: TransactionConfig,\n instructions: Instruction[],\n feePayer: TransactionSigner | Address,\n lookupTableAccounts?: MaybeAccount<AddressLookupTable>[],\n): Promise<Readonly<SendableTransaction & Transaction & TransactionWithLifetime>> {\n return buildTransactionMessage(\n rpc,\n config,\n instructions,\n !(\"address\" in feePayer) ? createNoopSigner(feePayer) : feePayer,\n lookupTableAccounts,\n );\n}\n\nasync function buildTransactionMessage(\n rpc: Rpc<SolanaRpcApi>,\n config: TransactionConfig,\n instructions: Instruction[],\n signer: TransactionSigner,\n lookupTableAccounts?: MaybeAccount<AddressLookupTable>[],\n) {\n let message = await prepareTransactionMessage(instructions, rpc, signer);\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 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 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 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 skipSimulation: false,\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 skipSimulation: boolean;\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 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 { 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[]} [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(\n rpc,\n config ?? DEFAULT_TRANSACTION_CONFIG,\n instructions,\n payer,\n lookupTableAccounts,\n );\n return sendSignedTransaction(rpc, tx, config ?? DEFAULT_TRANSACTION_CONFIG, 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 {TransactionConfig} config - The transaction config.\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 config: TransactionConfig,\n commitment: Commitment = \"confirmed\",\n): Promise<Signature> {\n const txSignature = getTxSignature(transaction);\n const encodedTransaction = getBase64EncodedWireTransaction(transaction);\n\n if (!config.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\nfunction getTxSignature(transaction: Transaction) {\n const [signature] = Object.values(transaction.signatures);\n return getBase58Decoder().decode(signature!) as Signature;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,cAsBO;;;ACdP,iBAAqE;AACrE,4BAAqF;;;ACN9E,IAAM,yCAAyC;AAK/C,IAAM,6BAAgD;AAAA,EAC3D,aAAa;AAAA,IACX,MAAM;AAAA,IACN,uBAAuB;AAAA,EACzB;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,uBAAuB;AAAA,EACzB;AAAA,EACA,6BAA6B;AAAA,EAC7B,oBAAoB;AAAA,EACpB,gBAAgB;AAClB;;;ADNA,eAAsB,iCACpB,KACA,SACA,cACA,QACA;AACA,QAAM,cAAc,OAAO;AAC3B,QAAM,8BAA8B,OAAO;AAC3C,MAAI,2BAA2B;AAE/B,MAAI,YAAY,SAAS,SAAS;AAChC,+BAA4B,YAAY,iBAAiB,WAAc,OAAO,YAAY;AAAA,EAC5F,WAAW,YAAY,SAAS,WAAW;AACzC,UAAM,uBAAuB,MAAM;AAAA,MACjC;AAAA,MACA,QAAQ;AAAA,MACR,YAAY,yBAAyB;AAAA,IACvC;AAEA,QAAI,CAAC,YAAY,gBAAgB;AAC/B,iCAA2B;AAAA,IAC7B,OAAO;AACL,YAAM,sBAAuB,YAAY,iBAAiB,WAAc,OAAO,YAAY;AAE3F,iCACE,sBAAsB,uBAAuB,uBAAuB;AAAA,IACxE;AAAA,EACF;AAEA,MAAI,2BAA2B,GAAG;AAChC,kBAAU;AAAA,UACR,yDAAkC;AAAA,QAChC,eAAe;AAAA,MACjB,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF;AAEA,gBAAU;AAAA,QACR,yDAAkC;AAAA,MAChC,OAAO,KAAK,KAAK,gBAAgB,+BAA+B,uCAAuC;AAAA,IACzG,CAAC;AAAA,IACD;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,oBAAoB,KAA6B;AACxD,QAAM,WAAW,oBAAI,IAAa;AAClC,MAAI,QAAQ,QAAM;AAChB,QAAI,GAAG,UAAU;AACf,SAAG,SAAS,QAAQ,SAAO;AACzB,gBAAI,2BAAe,IAAI,IAAI,EAAG,UAAS,IAAI,IAAI,OAAO;AAAA,MACxD,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AACD,SAAO,MAAM,KAAK,QAAQ;AAC5B;AAEA,eAAe,6BACb,KACA,cACA,YACA;AACA,QAAM,mBAAmB,oBAAoB,YAAY;AACzD,QAAM,SAAS,MAAM,IAAI,4BAA4B,gBAAgB,EAAE,KAAK;AAC5E,QAAM,UAAU,OAAO,OAAO,QAAM,GAAG,oBAAoB,CAAC,EAAE,IAAI,QAAM,GAAG,iBAAiB;AAC5F,QAAM,SAAS,QAAQ,KAAK,CAAC,GAAG,MAAM,OAAO,IAAI,CAAC,CAAC;AACnD,SAAO,OAAO,KAAK,MAAM,OAAO,UAAU,SAAS,UAAU,IAAI,IAAI,CAAC,KAAK,OAAO,CAAC;AACrF;;;AEnFA,IAAAC,cAAwE;AACxE,oBAA0C;AAI1C,eAAsB,2BACpB,SACA,QACA,QACA;AACA,MAAI,kBAAkB,OAAO,CAAC;AAC9B,QAAM,OAAO,OAAO;AAEpB,MAAI,KAAK,SAAS,SAAS;AACzB,sBAAkB,KAAK;AAAA,EACzB,WAAW,KAAK,SAAS,WAAW;AAClC,sBAAkB,MAAM,cAAc,MAAM;AAAA,EAC9C;AACA,MAAI,kBAAkB,GAAG;AACvB,eAAO;AAAA,UACL,yCAA0B;AAAA,QACxB,QAAQ;AAAA,QACR,aAAa,kBAAkB;AAAA,QAC/B,QAAQ;AAAA,MACV,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF,OAAO;AACL,WAAO;AAAA,EACT;AACF;AAGA,eAAsB,cAAc,QAA2B;AAC7D,QAAM,WAAW,MAAM,MAAM,GAAG,OAAO,kBAAkB,2BAA2B;AACpF,MAAI,CAAC,SAAS,IAAI;AAChB,WAAO,OAAO,CAAC;AAAA,EACjB;AACA,QAAM,OAAO,MAAM,SAAS,KAAK,EAAE,KAAK,SAAO,IAAI,CAAC,CAAC;AAErD,QAAM,kBAAwD;AAAA,IAC5D,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,SAAS;AAAA,EACX;AAEA,QAAM,MAAM,gBAAgB,OAAO,YAAY,yBAAyB,IAAI;AAC5E,MAAI,CAAC,OAAO,CAAC,KAAK,GAAG,GAAG;AACtB,WAAO,OAAO,CAAC;AAAA,EACjB;AACA,aAAO,sBAAS,OAAO,KAAK,MAAM,OAAO,KAAK,GAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ;AAC3E;AAMA,IAAM,mBAAmB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,oBAA6B;AAGpC,aAAO,qBAAQ,iBAAiB,KAAK,MAAM,KAAK,OAAO,IAAI,iBAAiB,MAAM,CAAC,CAAC;AACtF;;;AH3BA,eAAsB,iBACpB,KACA,QACA,cACA,UACA,qBACgF;AAChF,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,EAAE,aAAa,gBAAY,8BAAiB,QAAQ,IAAI;AAAA,IACxD;AAAA,EACF;AACF;AAEA,eAAe,wBACb,KACA,QACA,cACA,QACA,qBACA;AACA,MAAI,UAAU,MAAM,0BAA0B,cAAc,KAAK,MAAM;AAEvE,MAAI,qBAAqB,QAAQ;AAC/B,UAAM,SAAS,oBAAoB;AAAA,MACjC,CAAC,MAAM,YAAY;AACjB,YAAI,QAAQ,QAAQ;AAClB,gDAAqB,OAAO;AAC5B,eAAK,QAAQ,OAAO,IAAI,QAAQ,KAAK;AAAA,QACvC;AACA,eAAO;AAAA,MACT;AAAA,MACA,CAAC;AAAA,IACH;AACA,kBAAU,gEAAmD,SAAS,MAAM;AAAA,EAC9E;AAEA,QAAM,+BAA+B,MAAM,wBAAwB,KAAK,SAAS,QAAQ,MAAM;AAE/F,aAAO,+CAAkC,4BAA4B;AACvE;AAEA,eAAe,0BACb,cACA,KACA,QACA;AACA,QAAM,EAAE,OAAO,UAAU,IAAI,MAAM,IAChC,mBAAmB;AAAA,IAClB,YAAY;AAAA,EACd,CAAC,EACA,KAAK;AAER,aAAO;AAAA,QACL,sCAAyB,EAAE,SAAS,EAAE,CAAC;AAAA,IACvC,YAAM,yDAA4C,WAAW,EAAE;AAAA,IAC/D,YAAM,iDAAoC,QAAQ,EAAE;AAAA,IACpD,YAAM,kDAAqC,cAAc,EAAE;AAAA,EAC7D;AACF;AAEA,eAAe,wBACb,KACA,SACA,QACA,QAC8D;AAC9D,MAAI,OAAO,KAAK,SAAS,QAAQ;AAC/B,cAAU,MAAM,2BAA2B,SAAS,QAAQ,MAAM;AAAA,EACpE;AAEA,MAAI,eAAe;AAEnB,MAAI,CAAC,OAAO,gBAAgB;AAC1B,QAAI;AACF,qBAAe,MAAM,4BAA4B,KAAK,OAAO;AAAA,IAC/D,QAAQ;AACN,cAAQ,KAAK,8DAA8D;AAAA,IAC7E;AAAA,EACF;AAEA,SAAO,iCAAiC,KAAK,SAAS,cAAc,MAAM;AAC5E;AAEA,eAAe,4BACb,KACA,WACA;AACA,SAAO,UAAM,6CAAgC,EAAE,IAAI,CAAC,EAAE,SAAS;AACjE;;;AI5IA,IAAAC,cAaO;AA+BP,eAAsB,gBACpB,KACA,cACA,OACA,QACA,qBACA,aAAyB,aACL;AACpB,QAAM,KAAK,MAAM;AAAA,IACf;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,SAAO,sBAAsB,KAAK,IAAI,UAAU,4BAA4B,UAAU;AACxF;AAuBA,eAAsB,sBACpB,KACA,aACA,QACA,aAAyB,aACL;AACpB,QAAM,cAAc,eAAe,WAAW;AAC9C,QAAM,yBAAqB,6CAAgC,WAAW;AAEtE,MAAI,CAAC,OAAO,gBAAgB;AAE1B,UAAM,YAAY,MAAM,IACrB,oBAAoB,oBAAoB;AAAA,MACvC,UAAU;AAAA,IACZ,CAAC,EACA,KAAK;AAER,QAAI,UAAU,MAAM,KAAK;AACvB,YAAM,IAAI,MAAM,iCAAiC,EAAE,OAAO,UAAU,MAAM,CAAC;AAAA,IAC7E;AAAA,EACF;AAEA,QAAM,aAAa,KAAK,IAAI,IAAI;AAEhC,SAAO,KAAK,IAAI,IAAI,YAAY;AAC9B,UAAM,IACH,gBAAgB,oBAAoB;AAAA,MACnC,YAAY,OAAO,CAAC;AAAA,MACpB,eAAe;AAAA,MACf,UAAU;AAAA,IACZ,CAAC,EACA,KAAK;AAER,UAAM,EAAE,MAAM,IAAI,MAAM,IAAI,qBAAqB,CAAC,WAAW,CAAC,EAAE,KAAK;AACrE,UAAM,SAAS,MAAM,CAAC;AACtB,QAAI,QAAQ,uBAAuB,YAAY;AAC7C,UAAI,OAAO,KAAK;AACd,cAAM,IAAI,MAAM,sBAAsB,EAAE,OAAO,OAAO,IAAI,CAAC;AAAA,MAC7D;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,IAAI,MAAM,qBAAqB;AACvC;AAEA,SAAS,eAAe,aAA0B;AAChD,QAAM,CAAC,SAAS,IAAI,OAAO,OAAO,YAAY,UAAU;AACxD,aAAO,8BAAiB,EAAE,OAAO,SAAU;AAC7C;","names":["import_kit","import_kit","import_kit"]}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,5 @@
1
- import { Rpc, SolanaRpcApi, Instruction, TransactionSigner, Address, SendableTransaction, Transaction, TransactionWithLifetime, Commitment, Signature, KeyPairSigner } from '@solana/kit';
1
+ import { Rpc, SolanaRpcApi, Instruction, TransactionSigner, Address, MaybeAccount, SendableTransaction, Transaction, TransactionWithLifetime, Commitment, Signature, KeyPairSigner } from '@solana/kit';
2
+ import { AddressLookupTable } from '@solana-program/address-lookup-table';
2
3
 
3
4
  /**
4
5
  * Default compute unit margin multiplier used to ensure sufficient compute budget.
@@ -31,6 +32,7 @@ type TransactionConfig = {
31
32
  priorityFee: PriorityFeeSetting;
32
33
  computeUnitMarginMultiplier: number;
33
34
  jitoBlockEngineUrl: string;
35
+ skipSimulation: boolean;
34
36
  };
35
37
  /**
36
38
  * Defines a percentile value for priority fee selection.
@@ -48,7 +50,7 @@ type ChainId = "solana" | "eclipse" | "solana-devnet" | "eclipse-testnet" | "unk
48
50
  * @param {TransactionConfig} config - The transaction config.
49
51
  * @param {Instruction[]} instructions - Array of instructions to include in the transaction
50
52
  * @param {TransactionSigner} feePayer - The signer that will pay for the transaction
51
- * @param {(Address | string)[]} [lookupTableAddresses] - Optional array of address lookup table addresses to compress the transaction
53
+ * @param {(Address | string)[]} [lookupTableAccounts] - Optional array of address lookup table accounts to compress the transaction
52
54
  *
53
55
  * @returns {Promise<Readonly<SendableTransaction & Transaction & TransactionWithLifetime>>} A signed and encoded transaction
54
56
  *
@@ -61,7 +63,7 @@ type ChainId = "solana" | "eclipse" | "solana-devnet" | "eclipse-testnet" | "unk
61
63
  * feePayer,
62
64
  * );
63
65
  */
64
- declare function buildTransaction(rpc: Rpc<SolanaRpcApi>, config: TransactionConfig, instructions: Instruction[], feePayer: TransactionSigner | Address, lookupTableAddresses?: Address[]): Promise<Readonly<SendableTransaction & Transaction & TransactionWithLifetime>>;
66
+ declare function buildTransaction(rpc: Rpc<SolanaRpcApi>, config: TransactionConfig, instructions: Instruction[], feePayer: TransactionSigner | Address, lookupTableAccounts?: MaybeAccount<AddressLookupTable>[]): Promise<Readonly<SendableTransaction & Transaction & TransactionWithLifetime>>;
65
67
 
66
68
  /**
67
69
  * Builds and sends a transaction with the given instructions, signers, and commitment level.
@@ -70,7 +72,7 @@ declare function buildTransaction(rpc: Rpc<SolanaRpcApi>, config: TransactionCon
70
72
  * @param {Instruction[]} instructions - Array of instructions to include in the transaction.
71
73
  * @param {KeyPairSigner} payer - The fee payer for the transaction.
72
74
  * @param {TransactionConfig} config - The transaction config.
73
- * @param {Address[]} [lookupTableAddresses] - Optional array of address lookup table addresses to use.
75
+ * @param {Address[]} [lookupTableAccounts] - Optional array of address lookup table accounts to use.
74
76
  * @param {Commitment} [commitment="confirmed"] - The commitment level for transaction confirmation.
75
77
  *
76
78
  * @returns {Promise<Signature>} A promise that resolves to the transaction signature.
@@ -88,12 +90,13 @@ declare function buildTransaction(rpc: Rpc<SolanaRpcApi>, config: TransactionCon
88
90
  * );
89
91
  * ```
90
92
  */
91
- declare function sendTransaction(rpc: Rpc<SolanaRpcApi>, instructions: Instruction[], payer: KeyPairSigner, config?: TransactionConfig, lookupTableAddresses?: Address[], commitment?: Commitment): Promise<Signature>;
93
+ declare function sendTransaction(rpc: Rpc<SolanaRpcApi>, instructions: Instruction[], payer: KeyPairSigner, config?: TransactionConfig, lookupTableAccounts?: MaybeAccount<AddressLookupTable>[], commitment?: Commitment): Promise<Signature>;
92
94
  /**
93
95
  * Sends a signed transaction message to the Solana network with a specified commitment level.
94
96
  *
95
97
  * @param {Rpc<SolanaRpcApi>} rpc - Rpc client.
96
98
  * @param {SendableTransaction & Transaction & TransactionWithLifetime} transaction - The fully signed transaction to send.
99
+ * @param {TransactionConfig} config - The transaction config.
97
100
  * @param {Commitment} [commitment="confirmed"] - The commitment level for transaction confirmation.
98
101
  *
99
102
  * @returns {Promise<Signature>} A promise that resolves to the transaction signature.
@@ -109,6 +112,6 @@ declare function sendTransaction(rpc: Rpc<SolanaRpcApi>, instructions: Instructi
109
112
  * );
110
113
  * ```
111
114
  */
112
- declare function sendSignedTransaction(rpc: Rpc<SolanaRpcApi>, transaction: SendableTransaction & Transaction & TransactionWithLifetime, commitment?: Commitment): Promise<Signature>;
115
+ declare function sendSignedTransaction(rpc: Rpc<SolanaRpcApi>, transaction: SendableTransaction & Transaction & TransactionWithLifetime, config: TransactionConfig, commitment?: Commitment): Promise<Signature>;
113
116
 
114
117
  export { type ChainId, DEFAULT_COMPUTE_UNIT_MARGIN_MULTIPLIER, DEFAULT_TRANSACTION_CONFIG, type JitoFeeSetting, type Percentile, type PriorityFeeSetting, type TransactionConfig, buildTransaction, sendSignedTransaction, sendTransaction };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { Rpc, SolanaRpcApi, Instruction, TransactionSigner, Address, SendableTransaction, Transaction, TransactionWithLifetime, Commitment, Signature, KeyPairSigner } from '@solana/kit';
1
+ import { Rpc, SolanaRpcApi, Instruction, TransactionSigner, Address, MaybeAccount, SendableTransaction, Transaction, TransactionWithLifetime, Commitment, Signature, KeyPairSigner } from '@solana/kit';
2
+ import { AddressLookupTable } from '@solana-program/address-lookup-table';
2
3
 
3
4
  /**
4
5
  * Default compute unit margin multiplier used to ensure sufficient compute budget.
@@ -31,6 +32,7 @@ type TransactionConfig = {
31
32
  priorityFee: PriorityFeeSetting;
32
33
  computeUnitMarginMultiplier: number;
33
34
  jitoBlockEngineUrl: string;
35
+ skipSimulation: boolean;
34
36
  };
35
37
  /**
36
38
  * Defines a percentile value for priority fee selection.
@@ -48,7 +50,7 @@ type ChainId = "solana" | "eclipse" | "solana-devnet" | "eclipse-testnet" | "unk
48
50
  * @param {TransactionConfig} config - The transaction config.
49
51
  * @param {Instruction[]} instructions - Array of instructions to include in the transaction
50
52
  * @param {TransactionSigner} feePayer - The signer that will pay for the transaction
51
- * @param {(Address | string)[]} [lookupTableAddresses] - Optional array of address lookup table addresses to compress the transaction
53
+ * @param {(Address | string)[]} [lookupTableAccounts] - Optional array of address lookup table accounts to compress the transaction
52
54
  *
53
55
  * @returns {Promise<Readonly<SendableTransaction & Transaction & TransactionWithLifetime>>} A signed and encoded transaction
54
56
  *
@@ -61,7 +63,7 @@ type ChainId = "solana" | "eclipse" | "solana-devnet" | "eclipse-testnet" | "unk
61
63
  * feePayer,
62
64
  * );
63
65
  */
64
- declare function buildTransaction(rpc: Rpc<SolanaRpcApi>, config: TransactionConfig, instructions: Instruction[], feePayer: TransactionSigner | Address, lookupTableAddresses?: Address[]): Promise<Readonly<SendableTransaction & Transaction & TransactionWithLifetime>>;
66
+ declare function buildTransaction(rpc: Rpc<SolanaRpcApi>, config: TransactionConfig, instructions: Instruction[], feePayer: TransactionSigner | Address, lookupTableAccounts?: MaybeAccount<AddressLookupTable>[]): Promise<Readonly<SendableTransaction & Transaction & TransactionWithLifetime>>;
65
67
 
66
68
  /**
67
69
  * Builds and sends a transaction with the given instructions, signers, and commitment level.
@@ -70,7 +72,7 @@ declare function buildTransaction(rpc: Rpc<SolanaRpcApi>, config: TransactionCon
70
72
  * @param {Instruction[]} instructions - Array of instructions to include in the transaction.
71
73
  * @param {KeyPairSigner} payer - The fee payer for the transaction.
72
74
  * @param {TransactionConfig} config - The transaction config.
73
- * @param {Address[]} [lookupTableAddresses] - Optional array of address lookup table addresses to use.
75
+ * @param {Address[]} [lookupTableAccounts] - Optional array of address lookup table accounts to use.
74
76
  * @param {Commitment} [commitment="confirmed"] - The commitment level for transaction confirmation.
75
77
  *
76
78
  * @returns {Promise<Signature>} A promise that resolves to the transaction signature.
@@ -88,12 +90,13 @@ declare function buildTransaction(rpc: Rpc<SolanaRpcApi>, config: TransactionCon
88
90
  * );
89
91
  * ```
90
92
  */
91
- declare function sendTransaction(rpc: Rpc<SolanaRpcApi>, instructions: Instruction[], payer: KeyPairSigner, config?: TransactionConfig, lookupTableAddresses?: Address[], commitment?: Commitment): Promise<Signature>;
93
+ declare function sendTransaction(rpc: Rpc<SolanaRpcApi>, instructions: Instruction[], payer: KeyPairSigner, config?: TransactionConfig, lookupTableAccounts?: MaybeAccount<AddressLookupTable>[], commitment?: Commitment): Promise<Signature>;
92
94
  /**
93
95
  * Sends a signed transaction message to the Solana network with a specified commitment level.
94
96
  *
95
97
  * @param {Rpc<SolanaRpcApi>} rpc - Rpc client.
96
98
  * @param {SendableTransaction & Transaction & TransactionWithLifetime} transaction - The fully signed transaction to send.
99
+ * @param {TransactionConfig} config - The transaction config.
97
100
  * @param {Commitment} [commitment="confirmed"] - The commitment level for transaction confirmation.
98
101
  *
99
102
  * @returns {Promise<Signature>} A promise that resolves to the transaction signature.
@@ -109,6 +112,6 @@ declare function sendTransaction(rpc: Rpc<SolanaRpcApi>, instructions: Instructi
109
112
  * );
110
113
  * ```
111
114
  */
112
- declare function sendSignedTransaction(rpc: Rpc<SolanaRpcApi>, transaction: SendableTransaction & Transaction & TransactionWithLifetime, commitment?: Commitment): Promise<Signature>;
115
+ declare function sendSignedTransaction(rpc: Rpc<SolanaRpcApi>, transaction: SendableTransaction & Transaction & TransactionWithLifetime, config: TransactionConfig, commitment?: Commitment): Promise<Signature>;
113
116
 
114
117
  export { type ChainId, DEFAULT_COMPUTE_UNIT_MARGIN_MULTIPLIER, DEFAULT_TRANSACTION_CONFIG, type JitoFeeSetting, type Percentile, type PriorityFeeSetting, type TransactionConfig, buildTransaction, sendSignedTransaction, sendTransaction };
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";
@@ -29,7 +28,8 @@ var DEFAULT_TRANSACTION_CONFIG = {
29
28
  priorityFeePercentile: "50"
30
29
  },
31
30
  computeUnitMarginMultiplier: DEFAULT_COMPUTE_UNIT_MARGIN_MULTIPLIER,
32
- jitoBlockEngineUrl: "https://bundles.jito.wtf"
31
+ jitoBlockEngineUrl: "https://bundles.jito.wtf",
32
+ skipSimulation: false
33
33
  };
34
34
 
35
35
  // src/computeBudget.ts
@@ -146,19 +146,18 @@ function getJitoTipAddress() {
146
146
  }
147
147
 
148
148
  // src/buildTransaction.ts
149
- async function buildTransaction(rpc, config, instructions, feePayer, lookupTableAddresses) {
149
+ async function buildTransaction(rpc, config, instructions, feePayer, lookupTableAccounts) {
150
150
  return buildTransactionMessage(
151
151
  rpc,
152
152
  config,
153
153
  instructions,
154
154
  !("address" in feePayer) ? createNoopSigner(feePayer) : feePayer,
155
- lookupTableAddresses
155
+ lookupTableAccounts
156
156
  );
157
157
  }
158
- async function buildTransactionMessage(rpc, config, instructions, signer, lookupTableAddresses) {
158
+ async function buildTransactionMessage(rpc, config, instructions, signer, lookupTableAccounts) {
159
159
  let message = await prepareTransactionMessage(instructions, rpc, signer);
160
- if (lookupTableAddresses?.length) {
161
- const lookupTableAccounts = await fetchAllMaybeAddressLookupTable(rpc, lookupTableAddresses);
160
+ if (lookupTableAccounts?.length) {
162
161
  const tables = lookupTableAccounts.reduce(
163
162
  (prev, account) => {
164
163
  if (account.exists) {
@@ -190,10 +189,12 @@ async function addPriorityInstructions(rpc, message, signer, config) {
190
189
  message = await processJitoTipForTxMessage(message, signer, config);
191
190
  }
192
191
  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");
192
+ if (!config.skipSimulation) {
193
+ try {
194
+ computeUnits = await getComputeUnitsForTxMessage(rpc, message);
195
+ } catch {
196
+ console.warn("Transaction simulation failed, using 1,400,000 compute units");
197
+ }
197
198
  }
198
199
  return processComputeBudgetForTxMessage(rpc, message, computeUnits, config);
199
200
  }
@@ -206,24 +207,26 @@ import {
206
207
  getBase58Decoder,
207
208
  getBase64EncodedWireTransaction
208
209
  } from "@solana/kit";
209
- async function sendTransaction(rpc, instructions, payer, config, lookupTableAddresses, commitment = "confirmed") {
210
+ async function sendTransaction(rpc, instructions, payer, config, lookupTableAccounts, commitment = "confirmed") {
210
211
  const tx = await buildTransaction(
211
212
  rpc,
212
213
  config ?? DEFAULT_TRANSACTION_CONFIG,
213
214
  instructions,
214
215
  payer,
215
- lookupTableAddresses
216
+ lookupTableAccounts
216
217
  );
217
- return sendSignedTransaction(rpc, tx, commitment);
218
+ return sendSignedTransaction(rpc, tx, config ?? DEFAULT_TRANSACTION_CONFIG, commitment);
218
219
  }
219
- async function sendSignedTransaction(rpc, transaction, commitment = "confirmed") {
220
+ async function sendSignedTransaction(rpc, transaction, config, commitment = "confirmed") {
220
221
  const txSignature = getTxSignature(transaction);
221
222
  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 });
223
+ if (!config.skipSimulation) {
224
+ const simResult = await rpc.simulateTransaction(encodedTransaction, {
225
+ encoding: "base64"
226
+ }).send();
227
+ if (simResult.value.err) {
228
+ throw new Error(`Transaction simulation failed`, { cause: simResult.value });
229
+ }
227
230
  }
228
231
  const expiryTime = Date.now() + 6e4;
229
232
  while (Date.now() < expiryTime) {
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 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 config: TransactionConfig,\n instructions: Instruction[],\n feePayer: TransactionSigner | Address,\n lookupTableAccounts?: MaybeAccount<AddressLookupTable>[],\n): Promise<Readonly<SendableTransaction & Transaction & TransactionWithLifetime>> {\n return buildTransactionMessage(\n rpc,\n config,\n instructions,\n !(\"address\" in feePayer) ? createNoopSigner(feePayer) : feePayer,\n lookupTableAccounts,\n );\n}\n\nasync function buildTransactionMessage(\n rpc: Rpc<SolanaRpcApi>,\n config: TransactionConfig,\n instructions: Instruction[],\n signer: TransactionSigner,\n lookupTableAccounts?: MaybeAccount<AddressLookupTable>[],\n) {\n let message = await prepareTransactionMessage(instructions, rpc, signer);\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 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 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 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 skipSimulation: false,\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 skipSimulation: boolean;\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 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 { 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[]} [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(\n rpc,\n config ?? DEFAULT_TRANSACTION_CONFIG,\n instructions,\n payer,\n lookupTableAccounts,\n );\n return sendSignedTransaction(rpc, tx, config ?? DEFAULT_TRANSACTION_CONFIG, 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 {TransactionConfig} config - The transaction config.\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 config: TransactionConfig,\n commitment: Commitment = \"confirmed\",\n): Promise<Signature> {\n const txSignature = getTxSignature(transaction);\n const encodedTransaction = getBase64EncodedWireTransaction(transaction);\n\n if (!config.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\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"]}
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.1",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
7
7
  "module": "./dist/index.js",
@@ -24,13 +24,6 @@
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",
@@ -52,5 +45,12 @@
52
45
  "eslint": "^10.6.0",
53
46
  "eslint-plugin-prettier": "^5.5.6",
54
47
  "eslint-plugin-simple-import-sort": "^13.0.0"
48
+ },
49
+ "scripts": {
50
+ "build": "tsup src/index.ts --format cjs,esm --dts --sourcemap",
51
+ "lint": "pnpm eslint && pnpm type-check",
52
+ "eslint": "eslint \"**/*.ts*\" --fix",
53
+ "type-check": "tsc -p .",
54
+ "clean": "rimraf dist"
55
55
  }
56
56
  }