I have some code where by adding all the instructions I easily blast past the maximum transaction size of 1232 bytes. Before I go off writing my own -- is there code out there anywhere I'm overlooking to split out transactions already?
2 Answers
The ability to split a tx into multiple is very dependant of the application and can also introduce security issues by breaking atomicity.
The resulting txs might need to run in series, or could be run in any order, and that is application specific.
Good to run sequentially in series: set up ATAs, perform action
Not good to run in series: system program create account then initializing that account in another tx. Anyone can grab the created account in between if the program assigned allows it.
The only attempt I have seen at achieving it automatically is in @saber-hq/solana-contrib in the TransactionEnvelope
class
-
Bah, you're right. What a pain, but for all the right reasons. Thanks for the answer. Commented Sep 20, 2022 at 22:54
-
This is client side code, IIUC, there should be program code too? Commented Feb 1, 2023 at 5:19
I had to do something similar in TypeScript.
This function accepts array of arrays of instructions. This is important as instructions in internal arrays are considered to be atomic. While arrays themselves can be split into multiple transactions:
export const PACKET_DATA_SIZE = 1280 - 40 - 8;
export const SIGNATURE_LENGTH = 64;
export const splitInstructions = (
ixss: TransactionInstruction[][],
recentBlockhash: string,
feePayer: PublicKey,
): Transaction[] => {
const txs = [];
const dummy = new Keypair();
let i = 0;
let j = ixss.length;
while (i !== ixss.length) {
const dummyTx = new Transaction();
ixss.slice(i, j).forEach((ixs) => ixs.forEach((ix) => dummyTx.add(ix)));
dummyTx.recentBlockhash = recentBlockhash;
dummyTx.sign(dummy);
const transactionLength =
dummyTx.compileMessage().serialize().length +
SIGNATURE_LENGTH +
1; /* SIGNATURE_LENGTH_SIZE */
if (transactionLength <= PACKET_DATA_SIZE) {
const tx = new Transaction();
ixss.slice(i, j).forEach((ixs) => ixs.forEach((ix) => tx.add(ix)));
tx.recentBlockhash = recentBlockhash;
tx.feePayer = feePayer;
txs.push(tx);
i = j;
j = ixss.length;
} else {
j -= 1;
if (j < i) {
throw new Error("A single instruction doesn't fit into a transaction");
}
}
}
return txs;
};