Call Solana from EVM
Call Solana programs from Solidity via Rome's CPI and helper precompiles.
Rome's precompiles let Solidity contracts call Solana programs directly. This guide covers the mechanics.
Prerequisites
The Rome Solidity interfaces from the public
rome-solidityrepo (installed from GitHub, not npm — see Deploy Solidity) — precompile interfaces live incontracts/interface.solA deployed Rome contract (see Deploy Solidity)
Bind each interface to its precompile address:
import {ICrossProgramInvocation, ISystemProgram, IHelperProgram}
from "@rome-protocol/rome-solidity/contracts/interface.sol";
ICrossProgramInvocation constant CpiProgram = ICrossProgramInvocation(0xFF00000000000000000000000000000000000008);
ISystemProgram constant SystemProgram = ISystemProgram(0xFF00000000000000000000000000000000000007);
IHelperProgram constant Helper = IHelperProgram(0xFF00000000000000000000000000000000000009);The CpiProgram precompile
CpiProgram (0xFF…08) dispatches CPI (invoke / invoke_signed) plus cross-state read shortcuts (account_info, account_data_at, account_u64_at, account_lamports, pdas_batch_derive):
// Call a Solana program
CpiProgram.invoke(programId, accounts, instructionData);
// Call with PDA signing: your contract's external-authority PDA signs, plus one
// salted sub-PDA per entry in `salts` (Rome derives ["EXTERNAL_AUTHORITY", contract, salt])
CpiProgram.invoke_signed(programId, accounts, data, salts);Transfer lamports
For simple lamport and SPL transfers, use the HelperProgram precompile — no hand-built CPI needed. A contract that calls it acts as itself: the transfers below move the contract's own lamports and tokens (its external-authority PDA signs), not the user's. For a contract to pull a user's SPL tokens into a token account it controls, and know whose they are, the user grants an SPL delegation. An ERC-20 transfer or transferFrom to a contract only credits the wrapper's escrow ledger and gives the contract no token account to spend from — see Integrate a Solana program.
transfer_spl has several overloads (including a delegate variant for transferFrom flows); see interface.sol. On the cached track, use ISplCached (0xff…05) / IAssociatedSplCached (0xff…06) instead — a contract uses one track consistently, and only the legacy track can invoke a custom program (see Integrate a Solana program).
Reading account data
Read any Solana account's data via the CpiProgram read shortcuts:
PDA derivation
Find Program Derived Addresses from Solidity via the System precompile:
Base58 conversion
Convert between bytes32 and base58 (Solana's address format):
Calling custom Solana programs
To call any Solana program, build the account list and instruction data yourself:
Key Constraints
All accounts must be declared upfront. The Solana transaction must include every account the CPI will touch — dynamic account discovery inside CPI is not possible.
CPI depth limit: 4 levels. Rome EVM → your target → the target's call → one more. Plan your call depth.
Solana pubkeys are
bytes32, not 20-byte Ethereum addresses.Instruction data is raw bytes in the format the target program expects (typically Borsh, little-endian).
What's Next
Integrate a Solana program — the user's Solana identity, signing as it, the one-track rule, and account provisioning for a real program on the other side
Precompile reference — every selector on every precompile
Call EVM from Solana — the reverse: drive EVM contracts from a Solana wallet
Token Interop — how ERC-20 and SPL tokens work together
Constraints — CPI depth and other limits
Last updated
Was this helpful?