# 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: 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:

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

The question should be specific, self-contained, and written in natural language.
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.
