Axelar-Solana: A Cross-Chain Protocol on a 1232-Byte Budget
/ 19 min read
Cross-chain messaging sounds like a routing problem until you try to land the proof on Solana.
A destination chain does not trust a relayer’s word that “validators approved this payload.” It needs cryptographic evidence: which validators signed, over what exact bytes, with enough combined weight to meet the hub chain’s threshold. On Ethereum-shaped chains, that evidence routinely ships in a single large transaction. On Solana, the wire format caps every transaction at 1,232 bytes of serialized data, and the runtime caps how much hashing and verification a program may perform in one execution.
The project I joined was a port of Axelar’s general message passing Gateway to Solana: same security story as the EVM Gateway, same validator quorum, same integration contract — but a chain whose constraints force a different transaction shape. The arithmetic is unforgiving. Twenty-seven ECDSA signatures at 65 bytes each are 1,755 bytes of signatures alone, before message headers, account metas, or application payload. The signatures do not fit. Nothing else gets a chance to try.
This post is Part 2 of a five-year blockchain retrospective. Part 1 covered the builder era on EVM chains. Here the constraint is physical: what happens when a hub-and-spoke interoperability protocol meets a packet-sized transaction model.
A cross-chain message, step by step
Before the byte math, it helps to name the objects moving through the system.
Imagine a contract on Ethereum that wants to call a program on Solana. The application does not talk to Solana directly. It calls its local Gateway, which records destination chain, destination address, and payload (or payload hash). Axelar validators observe that event on the source chain, agree on Axelar that the event is real, and sign a batch digest that commits to the messages they are approving. An off-chain relayer then submits transactions on Solana so the Solana Gateway can verify those signatures and mark the message approved. Only then may the Solana application run its execute handler with the full payload bytes.
The security invariant is the same on every chain: the destination only trusts validator signatures over a digest, not the relayer’s narration of what happened. The relayer is a courier. It can stall or waste fees; it cannot mint fake approvals without breaking cryptography.
Key concepts
Transaction
The atomic unit of state change on a blockchain: a signed instruction to update state. Validators gossip it, schedule it, execute it, and either commit the result or revert the whole thing. Every chain imposes limits; on Solana the serialized transaction size is a hard 1,232-byte ceiling, not a gas-priced soft limit.
Solana
A high-throughput layer-1 with explicit account lists per transaction, parallel scheduling where accounts do not alias, and programs compiled to SVM bytecode. Storage for program state lives in accounts, often at PDAs. Fees combine a base charge per signature with metered compute units for on-chain work.
EVM (Ethereum Virtual Machine)
The deterministic runtime behind Ethereum and most “EVM-compatible” chains: stack machine, 256-bit words, gas-metered execution, and a large tooling ecosystem (ABI encoding, JSON-RPC, explorers). Cross-chain gateways on EVM chains typically assume calldata and gas can scale with proof size.
What Axelar is responsible for
Axelar is a proof-of-stake hub chain whose job is authenticated messaging between external chains. An application on chain A calls a Gateway contract; Axelar validators observe that intent, reach agreement on Axelar itself, and produce a multisig attestation; a relayer delivers that attestation to chain B’s Gateway; the destination Gateway verifies signatures and lets the target contract execute the payload.
That division of labor matters when porting:
| Role | Responsibility |
|---|---|
| Source Gateway | Accept callContract, emit the cross-chain intent (including payload hash). |
| Axelar validators | Agree that the source event happened and sign a batch digest. |
| Destination Gateway | Verify the proof, mark messages approved, prevent replay on execution. |
| Relayer | Submit destination-chain transactions; cannot forge approvals without validator signatures. |
| Destination app | Run business logic once validateMessage succeeds. |
General message passing is Axelar’s name for arbitrary contract calls across chains — not only token bridges. Tokens are one payload shape; the protocol cares about authenticated bytes and destination addresses.
On EVM, the destination contract usually receives the payload in the same transaction that records approval. On Solana, approval and payload delivery are different phases: approvals are proved in many small transactions; the raw payload is uploaded afterward into a dedicated account (see After approval).

Key concepts
Axelar
Cosmos-family hub chain specialized for cross-chain routing. Validators run external-chain watchers, sign approved message batches, and maintain gateway contracts on connected networks. Topology is hub-and-spoke: chains do not verify each other directly; they verify Axelar’s threshold signatures at their local Gateway.
GMP (General Message Passing)
Cross-chain contract calls with validator-attested payloads. Source contract specifies destination chain, destination address, and bytes; destination contract receives the same bytes only if the Gateway accepts the multisig proof.
EVM Gateway
Axelar’s reference destination contract on EVM chains. It stores verifier sets, verifies secp256k1 signatures over a payload digest, records approved messages, and exposes validateMessage / execute to applications. The critical convenience for EVM integrators is batch verification in one transaction.
Quorum
Minimum signing weight required to approve a batch. Axelar’s Amplifier integration guide assumes a ~⅔ threshold: with 40 signers in the set, implementations must verify at least 27 signatures. That number is what collides with Solana’s packet limit.
I joined several months into the effort. The schedule was late, the integration surface was large, and the uncomfortable fact was already visible: the EVM Gateway’s “one transaction, whole proof” design was not a small porting detail. It was the wrong shape for Solana.
A note on the source
The code for this work is open source. The old Eiger-era links are stale now, because that project location became archival after the handoff: the Solana Gateway lives in Equilibrium’s axelar-amplifier-solana repository, while the Axelar-side Amplifier code I reference below is now easiest to find through Common Prefix’s axelar-amplifier fork. So if you click through and the org names differ from the company names in the story, that is why. Open source is great, but GitHub archaeology is still archaeology.
Solana basics for EVM readers
If you have only used Solidity chains, three Solana mechanics explain why the Gateway could not be a straight port.
Accounts, not implicit storage. Every instruction declares the full list of accounts it may read or write. Think of them as explicitly passed storage slots. If the relayer omits an account the program needs, the transaction fails before your logic runs.
Programs cannot sign; PDAs can. There is no msg.sender on Solana. A program id is never a transaction signer. Programs act through program derived addresses (PDAs) — deterministic addresses they own — and sign cross-program invocations with invoke_signed. The Gateway uses PDAs for config, per-message state, signature-verification sessions, and payload buffers.
Cross-program invocation (CPI). When the Gateway calls an application’s execute, or when an application calls validate_message on the Gateway, that is a CPI with its own account list. Integrators implementing axelar-executable must encode not only application bytes but also the account metas the relayer needs on Solana — unlike EVM, where calldata alone is enough.
Sending messages from Solana (outbound)
Outbound GMP is symmetric in trust but different in shape. A Solana program CPIs into the Gateway’s call_contract (or call_contract_offchain_data when the payload is too large to fit in instruction data). Validators’ ampd process watches Solana logs for the ContractCall event, confirms it against RPC, and drives the same Amplifier signing flow. Large outbound payloads may be passed to the relayer off-chain while only the hash lands on-chain — mirroring the inbound chunking problem in reverse. See the Gateway sending flow in the integration docs.
Two ceilings: bytes and compute
The EVM Gateway’s approveMessages path is intentionally monolithic. It receives the full Proof — weighted signer set plus every signature — hashes the message batch into a payload digest, recovers signers with secp256k1 ECDSA, accumulates weight against the threshold, and writes approvals. One call, one state transition, one clear atomic story.
EVM chains tolerate that shape because transaction size is primarily an economic limit. Larger calldata costs more gas, but the protocol does not reject a 10 KB transaction on size alone.
Solana is different. Two independent limits apply to every transaction:
| Limit | What it bounds | Typical failure mode |
|---|---|---|
| Serialized size (1,232 bytes) | Entire transaction: signatures array + message (header, account keys, blockhash, instructions) | Transaction rejected before execution |
| Compute units | Work performed inside program instructions (hashing, account updates, custom logic) | Transaction executes then aborts with compute budget exceeded |
After reserving space for the message header, account addresses, recent blockhash, and the transaction’s own Ed25519 signer signatures, only a few hundred bytes remain for instruction data in a typical Gateway transaction. That is the workable “payload window,” not the full 1,232.
EVM monolith vs Solana pipeline
The architectural fork is not “Rust instead of Solidity.” It is how many transactions carry the proof.
| Stage | EVM Gateway (reference) | Solana Gateway (shipped shape) |
|---|---|---|
| Proof delivery | Single approveMessages calldata blob | ExecuteData streamed as many txs |
| Signature checks | All in one contract execution | One verify_signature per validator signature |
| Message approval | All messages in same call | One approve_message per GMP message |
| Application payload | Passed as call arguments | Chunked into Message Payload PDA (~800 B per tx) |
| Upper bound (integration) | > 1 MB on Ethereum | 10 KB payload PDA limit on Solana |
Size budget vs integration minimums
Axelar’s Amplifier integration limits state what a serious Gateway must support. The table below is a napkin layout of authentication data only — not the application payload, not Borsh padding, not account metas:
| Piece | Count (minimum) | Per-item | Subtotal |
|---|---|---|---|
| Signatures | 27 | 65 bytes (secp256k1) | 1,755 bytes |
| Signer addresses | 40 | 20 bytes | 800 bytes |
| One message header | 1 | ~150 bytes | ~150 bytes |
| Subtotal | ~2,700 bytes |
That is roughly 2.2× the entire transaction budget before the cross-chain payload. Signatures alone exceed the cap by ~40%.
The same integration document recommends 16 KB minimum cross-chain message size (64 KB recommended). Payload size is a separate axis from proof size, but both must eventually land on-chain. A port that only solves signatures still fails if it cannot commit multi-kilobyte application data.
Compute budget vs batch verification
Even a design that stuffed proof bytes into a PDA would still need to verify them. Hashing the verifier set, hashing messages into the digest, and recovering dozens of ECDSA signatures inside one program invocation overshot Solana’s per-transaction compute budget in our measurements.
Key concepts
Compute-unit budget
Per-transaction CPU allowance inside the SVM. Hashing, account serialization, and custom verification logic consume units; exceeding the budget aborts the transaction. Independent from the 1,232-byte wire limit — a transaction can be small and still too expensive to run.
Signature verification
On-chain proof that a secp256k1 signature recovers to an authorized verifier address over the exact digest validators signed. Cheap as a concept; expensive when multiplied by quorum size inside one execution.
secp256k1
Elliptic curve used by Bitcoin and Ethereum for ECDSA. Axelar validator signatures are recoverable secp256k1, 65 bytes each (r, s, recovery id). The byte cost is fixed; the count is set by security policy (~27 minimum at launch assumptions).
The EVM reference was not “a little over budget.” It violated both ceilings, by different factors, for different reasons.
First approach: store the proof, verify in one shot
The codebase I inherited tried the direct translation: put the full ExecuteData bundle in a PDA so the 1,232-byte limit applied only to the pointer transaction, then run digest reconstruction and signature recovery in one Gateway invocation against the stored blob.
That removed the size problem and exposed the compute problem. Measured limits from that architecture looked like:
| Parameter | Measured (PDA path) | Integration expectation |
|---|---|---|
| Signers verified per tx | 5 | 27 minimum |
| Messages per batch | 3 | many (batched approvals) |
| Payload | 635 bytes | 16 KB minimum (64 KB recommended) |
| Account references | 20 | higher for real batches |
Wrong on every security-relevant axis — not a tuning issue, a shape issue.
Key concepts
PDA (Program Derived Address)
Address derived deterministically from a program id and seeds; the program signs for it without a private key. Used for persistent Gateway state, chunk buffers, and progress counters across the Merkle upload sequence.
ExecuteData
Axelar’s packaged proof: verifier set, signatures, payload digest material, and the data needed to reconstruct what validators signed. On EVM this arrives in one approveMessages call. On Solana it became a Merkleized stream.
The rkyv detour
In parallel, we invested in rkyv for zero-copy deserialization of dynamically sized structs — fewer allocations, less decode work, lower compute per byte touched. For a while it looked like the right lever: if the bottleneck were deserialization cost inside one fat transaction, zero-copy might recover enough headroom.
It did not change the binding constraints. The proof still could not fit in 1,232 bytes. Even with cheaper parsing, we could not verify a 27-signature quorum in one execution, and the native precompile path could not verify more than eight secp256k1 signatures per transaction anyway. When the client’s final quorum and payload requirements landed, the optimization axis was clearly wrong. We removed rkyv and redesigned around Merkle streaming.
War story: optimizing the wrong variable
The rkyv work was technically sound and materially faster for the shapes it handled. The failure mode was architectural misclassification: we treated Solana’s limits as a performance problem when they were a protocol packaging problem. Serialization efficiency does not shrink a 27-signature proof below a packet cap, and it does not multiply the precompile signature budget. The lesson I took into later chain work: identify which limit is structural before investing in micro-optimizations.
Key concepts
rkyv
Rust serialization framework for in-place access to archived structs. Strong fit when wire layout is stable and hot paths deserialize repeatedly. Wrong fit when the wire shape itself must change from one monolithic batch to many proved fragments.
Zero-copy deserialization
Reading typed fields directly from a byte buffer without building a separate owned struct. Saves allocation and copy cost; requires careful layout, alignment, and version discipline. Cannot overcome hard packet or quorum-per-transaction limits.
Merkle streaming: same security, different transaction count
The design we shipped keeps Axelar’s security story — validators sign one payload digest; the Gateway only approves what that digest commits to — but changes how the destination chain learns the proof.
What validators sign (ExecuteData)
Off-chain, the Multisig Prover collects signatures and returns ExecuteData: the verifier set that signed, every signature with Merkle proofs, and the payload digest validators attested to. The digest is a keccak commitment over the message batch, domain separator, and signing verifier-set metadata — the cross-chain equivalent of “what exactly did the quorum agree to?”
The encoding crate Merkelizes that bundle so each piece is provable without shipping the whole set in one transaction:
| Merkle tree | Commits to | Used when |
|---|---|---|
| Verifier set tree | Authorized signers and weights | Proving a signature belongs to the set that signed the digest |
| Message batch tree | GMP messages in the batch | Proving a message was part of what validators signed |
| Payload digest | The signed batch hash | Tying every on-chain step to validator signatures |
On EVM, the contract reconstructs those hashes from full calldata. On Solana, the relayer brings one leaf at a time plus a short proof.
Relayer pipeline on Solana
Merkle trees commit to a set with a single root. Any leaf is provable with O(log N) sibling hashes. The off-chain Relayer submits a sequence of small Solana transactions (documented in the project’s Gateway guide):
| Step | On-chain instruction (conceptual) | Transactions |
|---|---|---|
| Open verification session | initialize_payload_verification_session | 1 |
| Check each validator signature | verify_signature + Merkle proof | 1 per signature (≥ 27) |
| Record each approved message | approve_message + Merkle proof | 1 per message |
| Deliver application bytes | initialize / write / commit payload chunks | 1 + ⌈payload / ~800 B⌉ |
| Run application logic | Destination program + validate_message CPI | 1 (+ close payload account) |
Key PDAs involved (each is a small on-chain state machine, not a single storage slot):
| PDA | Role |
|---|---|
| Gateway config | Epoch, verifier-set hashes, rotation delay |
| Signature verification session | Accumulates verified weight until quorum |
| Incoming message | Per-message approved / executed status (command_id seed) |
| Message payload | Holds chunked raw bytes until execute (up to 10 KB) |
A progress PDA accumulates verified weight across signature transactions until quorum matches the EVM contract’s threshold check.
After approval: payloads and execution
Message approval only proves that validators authorized a payload hash. The application still needs the raw bytes.
Because instruction data stays in the hundreds of bytes, the relayer chunks the payload into a Message Payload PDA (~800 bytes per write), commits the hash, and only then calls the destination program. The program uses a signing PDA to CPI validate_message on the Gateway (marking the message executed, like validateMessage on EVM) and then runs application logic. Closing the payload PDA refunds most rent to the relayer.
This split — approve hash first, upload body second, execute last — is the main behavioral difference a casual reader should remember when comparing to “one big execute on Ethereum.”

Same invariants:
- Validators sign exactly one digest for the batch.
- The Gateway never approves a message not committed by that digest.
- Replay protection still keys off source chain + message id.
- A malicious relayer cannot forge signatures; it can only waste fees submitting invalid fragments.
Different operational profile: more transactions, more relayer orchestration, more on-chain state for upload progress — but spec-compliant on Solana’s actual limits.
Key concepts
Merkle trees
Hash tree where leaves are data items and internal nodes hash their children. The root commits to the entire set; a proof is the sibling path from leaf to root. For N leaves, proofs are ~log₂(N) hashes.

Payload digest
keccak256 commitment over the batch validators signed: messages, signer set metadata, domain separator, and ordering. Individual messages and signatures are proved against this digest later without re-signing.
Merkle-proved
Carrying a leaf plus sibling hashes so the program recomputes the root and compares it to on-chain state. Enables streaming: the chain never needs the full proof in one packet.
Chunked uploads
Splitting payloads larger than one transaction’s data window into sequential writes to a buffer account, then checking the buffer hash against the digest before approval completes. This is how multi-kilobyte application data survives a 1,232-byte wire limit.
Multisig Prover
Axelar-side service that collects validator signatures on a batch and packages ExecuteData for the destination Gateway. It does not decide message validity; it assembles the evidence chain B must verify.
Off-chain Relayer
Service that submits the Solana transaction sequence, pays fees, handles retries, and orders chunk uploads. Correctness remains on-chain; liveness depends on someone running the relayer well.
Borsh
Binary Object Representation Serializer for Hashing — Solana’s common deterministic encoding. We used it for instruction payloads and account data layouts across Gateway, client, and relayer.
Keccak
Hash family used in EVM ecosystems (Keccak-256). Axelar’s digest rules are keccak-shaped; the Gateway uses Solana’s keccak syscall so digests match other chains without implementing keccak in BPF.
Syscall
Runtime-native operations (hashing, logging, cryptography) callable from programs at lower cost than emulating the same work in bytecode. Keccak via syscall was load-bearing for digest checks inside tight compute budgets.
By the time I handed off, the implementation supported large signer sets (practically up to 256 per quorum configuration), ~10 KB payloads via chunking, and batched message approvals — a different order of magnitude from the five-signer PDA prototype. The client’s downstream requirement was TypeScript bindings for integrators; we had skipped Anchor early for optimization headroom and paid for that later when auto-generated clients became mandatory. Colleagues refactored the public API onto Anchor after my tenure, completed audits, and shipped to Axelar’s public testnet in April 2026.
Key concepts
Anchor
Solana framework with account macros, IDL generation, and TypeScript clients. Default choice for most programs. We initially avoided it for control; the binding cost was integrator tooling, not consensus safety.
TypeScript bindings
Generated clients mirroring program instructions and account layouts. Wallets and application teams depend on them; hand-rolling byte layouts does not scale.
Audit
Third-party security review before production deployment. Necessary signal, not a substitute for protocol-level reasoning about split-transaction state machines.
Testnet
Non-production network for integration testing — same software shapes, no real economic stake. Where relayer sequencing bugs surface before mainnet fees.
Mainnet
Production deployment with permanent state and real value. Split-transaction Gateways fail more expensively here: partial progress, stuck chunks, and relayer race bugs become user-visible incidents.
The theme: limits are part of the protocol
Axelar’s EVM Gateway is elegant because Ethereum’s transaction model amortizes proof size into gas. Solana’s model trades that flexibility for predictable packets and metered execution. Porting between them is not a line-by-line translation of Solidity into Rust; it is a negotiation between what the hub signs and what the destination chain can ingest per packet and per invocation.
The hard part was not implementing Merkle proofs or PDAs. The hard part was accepting that the reference architecture encoded an EVM assumption — monolithic verification — and that Solana’s limits are not bugs to optimize around. They are constraints that change the protocol’s on-chain shape while preserving its off-chain trust model.
The next project in this series had a different kind of limit: not bytes per transaction, but consensus and execution boundaries on a chain that wanted storage reads inside the EVM without pretending they were free. That is Part 3: Irys.