> For the complete documentation index, see [llms.txt](https://docs.rome.builders/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.rome.builders/developer-guides/call-solana-from-evm.md).

# 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-solidity`](https://github.com/rome-protocol/rome-solidity) repo (installed from GitHub, not npm — see [Deploy Solidity](/developer-guides/deploy-solidity.md#using-the-rome-solidity-sdk)) — precompile interfaces live in [`contracts/interface.sol`](https://github.com/rome-protocol/rome-solidity/blob/master/contracts/interface.sol)
* A deployed Rome contract (see [Deploy Solidity](/developer-guides/deploy-solidity.md))

Bind each interface to its precompile address:

```solidity
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`):

```solidity
// 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](/developer-guides/integrate-solana-program.md).

```solidity
contract Transfers {
    IHelperProgram constant Helper = IHelperProgram(0xFF00000000000000000000000000000000000009);

    // Transfer lamports from this contract's PDA to an EVM address's PDA
    function transferSol(address to, uint64 lamports) external {
        Helper.transfer_lamports(to, lamports);
    }

    // Transfer an SPL token from this contract's PDA-owned token account
    function transferSpl(address to, uint64 tokens, bytes32 mint) external {
        Helper.transfer_spl(to, tokens, mint);
    }

    // Create the caller's ATA for a mint
    function createAta(bytes32 mint) external {
        Helper.create_ata(msg.sender, mint);
    }
}
```

`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](/developer-guides/integrate-solana-program.md)).

## Reading account data

Read any Solana account's data via the CpiProgram read shortcuts:

```solidity
(
    uint64 lamports,
    bytes32 owner,
    bool isSigner,
    bool isWritable,
    bool executable,
    bytes memory data
) = CpiProgram.account_info(accountPubkey);
```

## PDA derivation

Find Program Derived Addresses from Solidity via the System precompile:

```solidity
ISystemProgram.Seed[] memory seeds = new ISystemProgram.Seed[](2);
seeds[0] = ISystemProgram.Seed("my-program-seed");
seeds[1] = ISystemProgram.Seed(abi.encodePacked(someValue));

(bytes32 pda, uint8 bump) = SystemProgram.find_program_address(targetProgramId, seeds);
```

## Base58 conversion

Convert between `bytes32` and base58 (Solana's address format):

```solidity
bytes memory base58Str = SystemProgram.bytes32_to_base58(pubkey);
bytes32 pubkey = SystemProgram.base58_to_bytes32(base58Bytes);
```

## Calling custom Solana programs

To call any Solana program, build the account list and instruction data yourself:

```solidity
contract CustomCPI {
    ICrossProgramInvocation constant CpiProgram = ICrossProgramInvocation(0xFF00000000000000000000000000000000000008);
    bytes32 constant MY_PROGRAM = 0x0000000000000000000000000000000000000000000000000000000000000000; // your Solana program ID

    function callMyProgram(bytes32 account1, bytes32 account2, bytes calldata ixData) external {
        ICrossProgramInvocation.AccountMeta[] memory accounts = new ICrossProgramInvocation.AccountMeta[](2);
        accounts[0] = ICrossProgramInvocation.AccountMeta(account1, false, true);
        accounts[1] = ICrossProgramInvocation.AccountMeta(account2, false, false);

        CpiProgram.invoke(MY_PROGRAM, accounts, ixData);
    }
}
```

## Key Constraints

1. **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.
2. **CPI depth limit: 4 levels.** Rome EVM → your target → the target's call → one more. Plan your call depth.
3. **Solana pubkeys are `bytes32`,** not 20-byte Ethereum addresses.
4. **Instruction data is raw bytes** in the format the target program expects (typically Borsh, little-endian).

## What's Next

* **See it in a real app** — [cardo](https://github.com/rome-protocol/cardo) sends CPI calls to the precompile straight from the user's wallet, with no adapter contract; [rome-dex](https://github.com/rome-protocol/rome-dex) (dual-lane AMM) does the same and also has a custody-less router contract; [aerarium](https://github.com/rome-protocol/aerarium) is a lending app that does not call Solana programs from Solidity.
* [Integrate a Solana program](/developer-guides/integrate-solana-program.md) — 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](/reference/precompiles.md) — every selector on every precompile
* [Call EVM from Solana](/developer-guides/call-evm-from-solana.md) — the reverse: drive EVM contracts from a Solana wallet
* [Token Interop](/core-concepts/token-interop.md) — how ERC-20 and SPL tokens work together
* [Constraints](/core-concepts/constraints.md) — CPI depth and other limits


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.rome.builders/developer-guides/call-solana-from-evm.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
