diff --git a/packages/common-sdk/package.json b/packages/common-sdk/package.json index 2e17115..dfbc4bd 100644 --- a/packages/common-sdk/package.json +++ b/packages/common-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@orca-so/common-sdk", - "version": "0.5.4", + "version": "0.5.5", "description": "Common Typescript components across Orca", "repository": "https://github.com/orca-so/orca-sdks", "author": "Orca Foundation", diff --git a/packages/common-sdk/src/web3/transactions/compute-budget.ts b/packages/common-sdk/src/web3/transactions/compute-budget.ts index dfd0c2c..c5df9a6 100644 --- a/packages/common-sdk/src/web3/transactions/compute-budget.ts +++ b/packages/common-sdk/src/web3/transactions/compute-budget.ts @@ -1,9 +1,40 @@ -import { Connection, PublicKey, RecentPrioritizationFees } from "@solana/web3.js"; +import { AddressLookupTableAccount, Connection, PublicKey, RecentPrioritizationFees, TransactionMessage, VersionedTransaction } from "@solana/web3.js"; import { Instruction } from "./types"; export const MICROLAMPORTS_PER_LAMPORT = 1_000_000; export const DEFAULT_PRIORITY_FEE_PERCENTILE = 0.9; export const DEFAULT_MAX_PRIORITY_FEE_LAMPORTS = 1000000; // 0.001 SOL +export const DEFAULT_MAX_COMPUTE_UNIT_LIMIT = 1_400_000; + +export async function estimateComputeBudgetLimit( + connection: Connection, + instructions: Instruction[], + lookupTableAccounts: AddressLookupTableAccount[] | undefined, + payer: PublicKey, + margin: number, +): Promise { + try { + const txMainInstructions = instructions.flatMap((instruction) => instruction.instructions); + const txCleanupInstruction = instructions.flatMap((instruction) => instruction.cleanupInstructions); + const txMessage = new TransactionMessage({ + recentBlockhash: PublicKey.default.toBase58(), + payerKey: payer, + instructions: [...txMainInstructions, ...txCleanupInstruction], + }).compileToV0Message(lookupTableAccounts); + + const tx = new VersionedTransaction(txMessage); + + const simulation = await connection.simulateTransaction(tx, { sigVerify: false, replaceRecentBlockhash: true }); + if (!simulation.value.unitsConsumed) { + return DEFAULT_MAX_COMPUTE_UNIT_LIMIT + } + const marginUnits = Math.max(100_000, margin * simulation.value.unitsConsumed); + const estimatedUnits = Math.ceil(simulation.value.unitsConsumed + marginUnits); + return Math.min(DEFAULT_MAX_COMPUTE_UNIT_LIMIT, estimatedUnits); + } catch { + return DEFAULT_MAX_COMPUTE_UNIT_LIMIT; + } +} export async function getPriorityFeeInLamports( connection: Connection, @@ -30,9 +61,9 @@ function getPriorityFeeSuggestion(recentPriorityFees: RecentPrioritizationFees[] } function getLockWritableAccounts(instructions: Instruction[]): PublicKey[] { - const accountKeys = instructions + return instructions .flatMap((instruction) => [...instruction.instructions, ...instruction.cleanupInstructions]) - .flatMap((instruction) => instruction.keys); - const writableAccounts = accountKeys.filter((key) => key.isWritable).map((key) => key.pubkey); - return Array.from(new Set(writableAccounts)); + .flatMap((instruction) => instruction.keys) + .filter((key) => key.isWritable) + .map((key) => key.pubkey); } diff --git a/packages/common-sdk/src/web3/transactions/transactions-builder.ts b/packages/common-sdk/src/web3/transactions/transactions-builder.ts index 9a82346..78dbf52 100644 --- a/packages/common-sdk/src/web3/transactions/transactions-builder.ts +++ b/packages/common-sdk/src/web3/transactions/transactions-builder.ts @@ -12,12 +12,10 @@ import { VersionedTransaction, } from "@solana/web3.js"; import { Wallet } from "../wallet"; -import { DEFAULT_MAX_PRIORITY_FEE_LAMPORTS, DEFAULT_PRIORITY_FEE_PERCENTILE, MICROLAMPORTS_PER_LAMPORT, getPriorityFeeInLamports } from "./compute-budget"; +import { DEFAULT_MAX_COMPUTE_UNIT_LIMIT, DEFAULT_MAX_PRIORITY_FEE_LAMPORTS, DEFAULT_PRIORITY_FEE_PERCENTILE, MICROLAMPORTS_PER_LAMPORT, estimateComputeBudgetLimit, getPriorityFeeInLamports } from "./compute-budget"; import { MEASUREMENT_BLOCKHASH } from "./constants"; import { Instruction, TransactionPayload } from "./types"; -const DEFAULT_MAX_COMPUTE_UNIT_LIMIT = 1_400_000; - /** Build options when building a transaction using TransactionBuilder @param latestBlockhash @@ -65,8 +63,8 @@ type ComputeBudgetOption = { } | { type: "auto"; maxPriorityFeeLamports?: number; - computeBudgetLimit?: number; - percentile?: number; + computeLimitMargin?: number; + computePricePercentile?: number; }; type SyncBuildOptions = BuildOptions & Required; @@ -102,7 +100,7 @@ export const defaultTransactionBuilderOptions: TransactionBuilderOptions = { export class TransactionBuilder { private instructions: Instruction[]; private signers: Signer[]; - private opts: TransactionBuilderOptions; + readonly opts: TransactionBuilderOptions; constructor( readonly connection: Connection, @@ -252,7 +250,7 @@ export class TransactionBuilder { // This should only be happening for calucling the tx size so it should be fine. prependInstructions = [ ComputeBudgetProgram.setComputeUnitLimit({ - units: computeBudgetOption.computeBudgetLimit ?? DEFAULT_MAX_COMPUTE_UNIT_LIMIT, + units: DEFAULT_MAX_COMPUTE_UNIT_LIMIT, }), ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 0, @@ -314,8 +312,10 @@ export class TransactionBuilder { } let finalComputeBudgetOption = computeBudgetOption ?? { type: "none" }; if (finalComputeBudgetOption.type === "auto") { - const computeBudgetLimit = finalComputeBudgetOption.computeBudgetLimit ?? DEFAULT_MAX_COMPUTE_UNIT_LIMIT; - const percentile = finalComputeBudgetOption.percentile ?? DEFAULT_PRIORITY_FEE_PERCENTILE; + const margin = finalComputeBudgetOption.computeLimitMargin ?? 0.1; + const lookupTableAccounts = finalOptions.maxSupportedTransactionVersion === "legacy" ? undefined : finalOptions.lookupTableAccounts; + const computeBudgetLimit = await estimateComputeBudgetLimit(this.connection, this.instructions, lookupTableAccounts, this.wallet.publicKey, margin); + const percentile = finalComputeBudgetOption.computePricePercentile ?? DEFAULT_PRIORITY_FEE_PERCENTILE; const priorityFee = await getPriorityFeeInLamports(this.connection, computeBudgetLimit, this.instructions, percentile); const maxPriorityFeeLamports = finalComputeBudgetOption.maxPriorityFeeLamports ?? DEFAULT_MAX_PRIORITY_FEE_LAMPORTS; const priorityFeeLamports = Math.min(priorityFee, maxPriorityFeeLamports);