# Вызов Solana из EVM

Предварительная компиляция CPI в Rome позволяет контрактам Solidity напрямую вызывать любой программный модуль Solana. В этом руководстве рассматривается механизм работы.

## Требования

* Установлен Rome Solidity SDK: `npm install @rome-protocol/solidity-sdk`
* Развернутый контракт Rome (см. [Развернуть Solidity](/ru/rukovodstva-dlya-razrabotchikov/deploy-solidity.md))

## Предварительная компиляция CPI

Предварительная компиляция CPI по адресу `0xFF00000000000000000000000000000000000008` предоставляет две функции:

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

// Вызов программы Solana
CpiProgram.invoke(programId, accounts, instructionData);

// Вызов с подписью PDA (ваш контракт подписывает как PDA)
CpiProgram.invoke_signed(programId, accounts, data, seeds);
```

## Базовый пример: перевод 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 {
        // Получить PDA отправителя
        bytes32 senderPda = RomeEVMAccount.pda(msg.sender);

        // Сформировать инструкцию перевода System Program
        ICrossProgramInvocation.AccountMeta[] memory accounts = new ICrossProgramInvocation.AccountMeta[](2);
        accounts[0] = ICrossProgramInvocation.AccountMeta(senderPda, true, true);   // отправитель (подписант, изменяемый)
        accounts[1] = ICrossProgramInvocation.AccountMeta(recipient, false, true);   // получатель (изменяемый)

        // Инструкция перевода System Program (вариант 2, little-endian u64 amount)
        bytes memory data = abi.encodePacked(uint32(2), lamports);

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

## Чтение данных аккаунта

Предварительная компиляция CPI также позволяет читать данные любого аккаунта Solana:

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

## Работа с токенами SPL

Используйте предварительную компиляцию SPL Token для распространенных операций с токенами:

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

contract TokenOperations {
    // Прочитать баланс токенного аккаунта
    function getBalance(bytes32 tokenAccount) external view returns (uint64) {
        ISplToken.Account memory acc = SplToken.account_state(tokenAccount);
        return acc.amount;
    }

    // Перевести токены SPL
    function transferTokens(bytes32 recipientAta, bytes32 mint, uint256 amount) external {
        SplToken.transfer(recipientAta, mint, amount);
    }

    // Создать ассоциированный токенный аккаунт
    function createAta(bytes32 wallet, bytes32 mint) external {
        AssociatedSplToken.create_associated_token_account(wallet, mint);
    }
}
```

## Вычисление PDA

Поиск адресов, производных от программы, из Solidity:

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

// Вывести 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

Преобразование между bytes32 и base58 (формат адресов Solana):

```solidity
// bytes32 → строка base58
bytes memory base58Str = SystemProgram.bytes32_to_base58(pubkey);

// строка base58 → bytes32
bytes32 pubkey = SystemProgram.base58_to_bytes32(base58Bytes);
```

## Вызов пользовательских программ Solana

Чтобы вызвать любую произвольную программу Solana:

```solidity
contract CustomCPI {
    bytes32 constant MY_PROGRAM = 0x...; // Идентификатор вашей программы Solana

    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);
    }
}
```

## Ключевые ограничения

1. **Все аккаунты должны быть объявлены заранее.** Транзакция Solana должна включать каждый аккаунт, к которому обратится CPI. Динамическое обнаружение аккаунтов внутри CPI невозможно.
2. **Ограничение глубины CPI: 4 уровня.** Rome EVM → Ваша цель → Вызов цели → Ещё один уровень. Тщательно планируйте глубину вызовов.
3. **Публичные ключи Solana — это bytes32.** Все адреса — это 32-байтовые публичные ключи Solana, а не 20-байтовые Ethereum-адреса.
4. **Данные инструкции — это необработанные байты.** Вам нужно кодировать данные инструкции в формате, который ожидает целевая программа (обычно Borsh-encoded, little-endian).

## Что дальше

* [Обертывание токенов](https://github.com/rome-protocol/docs/blob/main/developer-guides/token-wrapping.md) — развертывайте обертки ERC-20 для токенов SPL
* [Создание Transfer Hook](https://github.com/rome-protocol/docs/blob/main/developer-guides/build-transfer-hook.md) — создавайте transfer hooks на базе EVM
* [Оптимизация CU](https://github.com/rome-protocol/docs/blob/main/developer-guides/cu-optimization.md) — снижайте потребление compute units для вызовов CPI


---

# 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/ru/rukovodstva-dlya-razrabotchikov/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.
