Break through Solana's ~1232-byte transaction size limit. MegaTxn is a Solana program that stores arbitrary instruction batches on-chain and executes them via CPI — letting you pack dozens of instructions into a single atomic execution.
Built with Pinocchio. Zero-copy, no_std, no Anchor.
MegaTxn has two flows depending on the size of your instruction batch:
When your serialized message fits in a single transaction (~1 KB):
txn_create → txn_execute
One transaction creates the on-chain MegaTransaction account with the message inlined. A second transaction executes all the inner instructions via CPI and closes the account, returning rent.
When your message is too large for a single transaction (up to ~10 KB):
txn_buffer_create → txn_buffer_extend (×N) → txn_create_from_buffer → txn_execute
The message is uploaded in chunks to a buffer account, then copied into a MegaTransaction once complete. The buffer is verified against a SHA-256 hash to ensure integrity. This flow is designed to work with Jito bundles — all buffer instructions use silent fails on resubmit, so a bundle can be safely retried without breaking.
- Ephemeral signers — up to 8 program-derived signer accounts per transaction, usable as fee payers or authorities in inner instructions
- Address Lookup Table support — inner instructions can reference accounts loaded from ALTs, keeping the message compact
- Jito bundle safety — buffer and execute instructions return
Ok(())on empty/already-processed accounts, so bundle resubmission never fails - Separated account ordering — remaining accounts are passed as
[ALTs, static, all writable, all readonly]for efficient on-chain index resolution
programs/megatxn/ Solana program (Rust, Pinocchio)
sdk/ TypeScript SDK (@megatxn/sdk)
- Rust with the Solana toolchain
- Node.js or Bun
- Solana CLI (for
cargo build-sbf)
cargo build-sbf --manifest-path programs/megatxn/Cargo.tomlTests use LiteSVM — an in-process Solana VM. No validator needed.
cd sdk
yarn install
yarn testcd app
yarn install
cp .env.example .env
# Edit .env with your RPC_URL and base58 private keyimport {
PROGRAM_ID,
serializeTransactionMessage,
getTransactionPda,
txnCreate,
txnExecute,
decodeMegaTransaction,
buildRemainingAccounts,
} from "@megatxn/sdk";
// 1. Serialize your instructions into the MegaTxn wire format
const messageBytes = serializeTransactionMessage(transactionMessage);
// 2. Create the MegaTransaction on-chain
const [transactionPda] = getTransactionPda(creator, transactionIndex);
const createIx = txnCreate({
transactionIndex,
ephemeralSigners: 0,
transactionMessage: messageBytes,
transaction: transactionPda,
creator,
rentPayer: creator,
});
// 3. Execute — the SDK resolves remaining accounts for you
const { instruction: executeIx, lookupTableAccounts } =
await buildExecuteInstruction({
connection,
creator,
transactionIndex,
});import {
getTransactionBufferPda,
txnBufferCreate,
txnBufferExtend,
txnCreateFromBuffer,
} from "@megatxn/sdk";
import { createHash } from "node:crypto";
const hash = createHash("sha256").update(messageBytes).digest();
const [bufferPda] = getTransactionBufferPda(creator, bufferIndex);
// 1. Create buffer with first chunk
const createBufIx = txnBufferCreate({
bufferIndex,
finalBufferHash: hash,
finalBufferSize: messageBytes.length,
buffer: messageBytes.subarray(0, 700),
transactionBuffer: bufferPda,
creator,
rentPayer: creator,
});
// 2. Extend with remaining chunks
for (let i = 700; i < messageBytes.length; i += 700) {
const extendIx = txnBufferExtend({
buffer: messageBytes.subarray(i, i + 700),
transactionBuffer: bufferPda,
creator,
});
// send in its own transaction
}
// 3. Finalize — creates MegaTransaction from buffer, closes buffer
const createFromBufIx = txnCreateFromBuffer({
transactionIndex,
ephemeralSigners: 0,
transaction: transactionPda,
creator,
rentPayer: creator,
transactionBuffer: bufferPda,
});The program ID is currently a placeholder. After deploying, update it in sdk/src/constants.ts:
export const PROGRAM_ID = new PublicKey("your_deployed_program_id_here");Unlicensed.