> 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

Rome's CPI precompile lets Solidity contracts call any Solana program directly. This guide covers the mechanics.

## Prerequisites

* Rome Solidity SDK installed: `npm install @rome-protocol/solidity-sdk`
* A deployed Rome contract (see [Deploy Solidity](/developer-guides/deploy-solidity.md))

## The CPI Precompile

The CPI precompile at `0xFF00000000000000000000000000000000000008` provides two functions:

```solidity
import {CpiProgram} from "@rome-protocol/solidity-sdk/contracts/core/Precompiles.sol";

// Call a Solana program
CpiProgram.invoke(programId, accounts, instructionData);

// Call with PDA signing (your contract signs as a PDA)
CpiProgram.invoke_signed(programId, accounts, data, seeds);
```

## Basic Example: Transfer SOL

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {SystemProgram, CpiProgram} from "@rome-protocol/solidity-sdk/contracts/core/Precompiles.sol";
import {RomeEVMAccount} from "@rome-protocol/solidity-sdk/contracts/core/RomeEVMAccount.sol";

contract SolTransfer {
    function transferSol(bytes32 recipient, uint64 lamports) external {
        // Get sender's PDA
        bytes32 senderPda = RomeEVMAccount.pda(msg.sender);

        // Build System Program transfer instruction
        ICrossProgramInvocation.AccountMeta[] memory accounts = new ICrossProgramInvocation.AccountMeta[](2);
        accounts[0] = ICrossProgramInvocation.AccountMeta(senderPda, true, true);   // sender (signer, writable)
        accounts[1] = ICrossProgramInvocation.AccountMeta(recipient, false, true);   // recipient (writable)

        // System Program transfer instruction (variant 2, little-endian u64 amount)
        bytes memory data = abi.encodePacked(uint32(2), lamports);

        CpiProgram.invoke(SystemProgram.program_id(), accounts, data);
    }
}
```

## Reading Account Data

The CPI precompile also lets you read any Solana account's data:

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

## Working with SPL Tokens

Use the SPL Token precompile for common token operations:

```solidity
import {SplToken, AssociatedSplToken} from "@rome-protocol/solidity-sdk/contracts/core/Precompiles.sol";

contract TokenOperations {
    // Read a token account's balance
    function getBalance(bytes32 tokenAccount) external view returns (uint64) {
        ISplToken.Account memory acc = SplToken.account_state(tokenAccount);
        return acc.amount;
    }

    // Transfer SPL tokens
    function transferTokens(bytes32 recipientAta, bytes32 mint, uint256 amount) external {
        SplToken.transfer(recipientAta, mint, amount);
    }

    // Create an Associated Token Account
    function createAta(bytes32 wallet, bytes32 mint) external {
        AssociatedSplToken.create_associated_token_account(wallet, mint);
    }
}
```

## PDA Derivation

Find Program Derived Addresses from Solidity:

```solidity
import {SystemProgram} from "@rome-protocol/solidity-sdk/contracts/core/Precompiles.sol";

// Derive a PDA
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
// bytes32 → base58 string
bytes memory base58Str = SystemProgram.bytes32_to_base58(pubkey);

// base58 string → bytes32
bytes32 pubkey = SystemProgram.base58_to_bytes32(base58Bytes);
```

## Calling Custom Solana Programs

To call any arbitrary Solana program:

```solidity
contract CustomCPI {
    bytes32 constant MY_PROGRAM = 0x...; // 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 → Target's call → One more level. Plan your call depth carefully.
3. **Solana pubkeys are bytes32.** All addresses are 32-byte Solana pubkeys, not 20-byte Ethereum addresses.
4. **Instruction data is raw bytes.** You need to encode instruction data in the format the target program expects (typically Borsh-encoded, little-endian).

## What's Next

* [Token Wrapping](https://github.com/rome-protocol/docs/blob/main/developer-guides/token-wrapping.md) — deploy ERC-20 wrappers for SPL tokens
* [Build Transfer Hook](https://github.com/rome-protocol/docs/blob/main/developer-guides/build-transfer-hook.md) — create EVM-powered transfer hooks
* [CU Optimization](https://github.com/rome-protocol/docs/blob/main/developer-guides/cu-optimization.md) — reduce compute unit consumption for CPI calls


---

# 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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
