chore(blockchain-link): set max CU limit for sol priority fee simulation

This commit is contained in:
tomasklim
2025-09-07 20:52:00 +02:00
committed by Tomáš Klíma
parent 7e28120769
commit 071523a391
7 changed files with 73 additions and 16 deletions

View File

@@ -71,6 +71,7 @@
"worker-loader": "^3.0.8"
},
"dependencies": {
"@solana-program/compute-budget": "^0.8.0",
"@solana-program/stake": "^0.2.1",
"@solana-program/token": "^0.5.1",
"@solana-program/token-2022": "^0.4.2",

View File

@@ -73,12 +73,6 @@ const THROTTLE_OPTIONS: ThrottledTransportOptions = {
interval: 100,
};
const DEFAULT_PRIORITY_FEE = {
computeUnitPrice: '0',
computeUnitLimit: '0',
fee: '0',
};
export type SolanaAPI = Readonly<{
clusterUrl: ClusterUrl;
rpc: RpcMainnet<SolanaRpcApiMainnet>;
@@ -556,11 +550,7 @@ const estimateFee = async (request: Request<MessageTypes.EstimateFee>) => {
decompiledTransactionMessage,
message,
transaction.signatures,
).catch(err => {
console.warn('getPriorityFee failed:', err);
return DEFAULT_PRIORITY_FEE;
});
);
const baseFee = await getBaseFee(api.rpc, message);

View File

@@ -16,8 +16,13 @@ import {
isWritableRole,
pipe,
} from '@solana/kit';
import {
MAX_COMPUTE_UNIT_LIMIT,
SET_COMPUTE_UNIT_LIMIT_DISCRIMINATOR,
} from '@solana-program/compute-budget';
import { COMPUTE_BUDGET_PROGRAM_ID } from '@trezor/blockchain-link-utils/src/solana';
import { safeBigIntStringify } from '@trezor/utils';
import { BigNumber } from '@trezor/utils/src/bigNumber';
const DEFAULT_COMPUTE_UNIT_PRICE_MICROLAMPORTS = BigInt(300_000); // micro-lamports, value taken from other wallets
@@ -33,6 +38,28 @@ const stripComputeBudgetInstructions = (message: CompiledTransactionMessage) =>
),
});
// increase compute unit limit to maximum for priority fee simulation
// avoid simulation fail in case instructions are wrong (e.g. from backend)
const bumpUnitLimitComputeBudgetInstructions = (
message: CompiledTransactionMessage,
): CompiledTransactionMessage => ({
...message,
instructions: message.instructions.map(ix => {
if (
message.staticAccounts[ix.programAddressIndex] === COMPUTE_BUDGET_PROGRAM_ID &&
ix.data?.[0] === SET_COMPUTE_UNIT_LIMIT_DISCRIMINATOR
) {
const data = new Uint8Array(5);
data[0] = SET_COMPUTE_UNIT_LIMIT_DISCRIMINATOR; // SetComputeUnitLimit
new DataView(data.buffer).setUint32(1, MAX_COMPUTE_UNIT_LIMIT, true);
return { ...ix, data };
}
return ix;
}),
});
export const getBaseFee = async (
api: Rpc<GetFeeForMessageApi>,
message: CompiledTransactionMessage,
@@ -69,9 +96,10 @@ export const getPriorityFee = async (
// Reconstruct TX for simulation
const messageBytes = pipe(
compiledMessage,
bumpUnitLimitComputeBudgetInstructions(compiledMessage),
getCompiledTransactionMessageEncoder().encode,
) as TransactionMessageBytes;
const rawTx = pipe(
{
messageBytes,
@@ -82,12 +110,21 @@ export const getPriorityFee = async (
) as Base64EncodedWireTransaction;
const simulated = await api
.simulateTransaction(rawTx, { commitment: 'confirmed', encoding: 'base64' })
.simulateTransaction(rawTx, {
commitment: 'confirmed',
encoding: 'base64',
sigVerify: false,
replaceRecentBlockhash: true,
})
.send();
if (simulated.value.err != null || !simulated.value.unitsConsumed) {
console.error('Could not simulate transaction:', JSON.stringify(simulated.value.err));
throw new Error(`Could not simulate transaction: ${JSON.stringify(simulated.value.err)}`);
if (simulated.value.err != null || simulated.value.unitsConsumed == null) {
const stringifiedError = safeBigIntStringify(simulated.value.err);
console.error('Could not simulate transaction:', stringifiedError);
throw new Error(`Could not simulate transaction: ${stringifiedError}`);
}
// Add 20% margin to the computed limit
const computeUnitLimit = new BigNumber(simulated.value.unitsConsumed.toString())
.times(1.2)

View File

@@ -60,3 +60,4 @@ export * from './urlToOnion';
export * from './zip';
export * from './removeTrailingSlashes';
export * from './getIntegerInRangeFromString';
export * from './safeBigIntStringify';

View File

@@ -0,0 +1,2 @@
export const safeBigIntStringify = (v: unknown) =>
JSON.stringify(v, (_k, val) => (typeof val === 'bigint' ? val.toString() : val));

View File

@@ -0,0 +1,25 @@
import { safeBigIntStringify } from '../src';
describe('safeStringify', () => {
it('serializes regular objects normally', () => {
const obj = { a: 1, b: 'text', c: true, d: null };
expect(safeBigIntStringify(obj)).toBe(JSON.stringify(obj));
});
it('converts BigInt values to strings', () => {
const x = { val: 1234567890123456789n };
const json = safeBigIntStringify(x);
// Should not throw and should include quoted bigint
expect(json).toBe('{"val":"1234567890123456789"}');
});
it('serializes nested BigInt in arrays and objects', () => {
const x = { arr: [1n, { nested: 2n }, 3] };
expect(safeBigIntStringify(x)).toBe('{"arr":["1",{"nested":"2"},3]}');
});
it('does not modify non-BigInt values', () => {
const x = { num: 42, str: 'hello', bool: false };
expect(safeBigIntStringify(x)).toBe(JSON.stringify(x));
});
});

View File

@@ -11854,6 +11854,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@trezor/blockchain-link@workspace:packages/blockchain-link"
dependencies:
"@solana-program/compute-budget": "npm:^0.8.0"
"@solana-program/stake": "npm:^0.2.1"
"@solana-program/token": "npm:^0.5.1"
"@solana-program/token-2022": "npm:^0.4.2"