> 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/ru/rukovodstva-dlya-razrabotchikov/call-evm-from-solana.md).

# Вызов EVM из Solana

Позвольте кошельку Solana управлять EVM-контрактами в Rome — без Ethereum-ключа. Кошелёк подписывает, а транзакция отправляется в Solana.

Обратный вариант [Вызов Solana из EVM](/ru/rukovodstva-dlya-razrabotchikov/call-solana-from-evm.md): кошелёк Solana (например, Solflare) напрямую вызывает любой EVM-контракт в Rome, без **ключа Ethereum**. Именно так [Aerarium](/ru/prilozheniya-v-rome/apps.md) позволяет пользователям, нативным для Solana, использовать те же контракты, что и пользователи EVM.

Способ с одним вызовом — это `submitRomeTxV1`, при котором прокси формирует для вызова одну транзакцию Solana v1; на этой странице описана механика.

## Синтетические адреса

EVM-идентичность кошелька Solana — это его **синтетический адрес** — последние 20 байт `keccak256` от его 32-байтного открытого ключа Solana:

```
synthetic_evm_address = keccak256(solana_pubkey)[12:]
```

Это `msg.sender` при вызове контракта кошельком, а также стабильная идентичность, под которой хранятся его nonce и storage. Способ вычисления закреплён в протоколе, поэтому кошелёк всегда отображается на один и тот же адрес.

```javascript
import { syntheticAddress } from "@rome-protocol/sdk";
const from = syntheticAddress(solanaPubkey); // 0x… 20 байт
```

## Синтетический адрес — это промежуточный адрес: он не хранит токены в состоянии покоя

Расходуемый баланс пользователя, нативного для Solana, находится в его **кошелька Solana** (в виде SPL-токенов), а на стороне EVM отображается в соотношении 1:1 как **обёртка ERC20SPL** (например, `wUSDC`). Синтетический адрес ничего не хранит в состоянии покоя, поэтому стоимость проходит *через* него — на каждом шаге подпись ставит кошелёк Solana:

1. **Инициализация (один раз).** У совершенно нового синтетического адреса PDA внешней авторизации не существует, пока `create_pda` не будет выполнен этот шаг — и нижеприведённые переводы подписываются этим PDA. `submitRomeTxSolanaLane` инициализирует его автоматически при первом использовании (или вызовите `provisionSynthetic` для явного шага «Активация»; проверьте с помощью `isSyntheticProvisioned`). С `submitRomeTxV1`, проверьте `isSyntheticProvisioned` и отправьте `buildCreatePdaCall` через него один раз.
2. **Шаг пополнения (кошелёк → синтетический адрес).** Переместите токен из ATA кошелька в ATA синтетического адреса — шаг `ActivateAta` создаваемый `buildFundLeg`. Теперь `wrapper.balanceOf(synthetic)` считывает этот баланс, поэтому синтетический адрес расходует его как обычный ERC-20 (`transfer` / `transferFrom`) — **не** как нативный `msg.value`, которым он не владеет.
3. **Вызов(ы) (`DoTxUnsigned`).** Синтетический адрес выполняет EVM-транзакции — например, `approve` затем vault `deposit` , который извлекает через `transferFrom`.
4. **Шаг возврата (синтетический адрес → кошелёк).** После того как вывод / заём / получение награды зачисляет токены на синтетический адрес, `buildSweepLeg` возвращает их обратно в ATA собственного кошелька пользователя (`HelperProgram.transfer_spl`), так что на синтетическом адресе итоговый баланс становится нулевым.

В следующем разделе описано, что делает более старый `submitRomeTxSolanaLane` сборка на стороне клиента. `submitRomeTxV1` позволяет прокси вместо этого собрать транзакцию v1, без обнаружения аккаунтов, ComputeBudget или treasure wallet в вашем коде; см. [внешние конечные точки V1](#the-external-v1-endpoints) ниже.

## Как формируется вызов

`submitRomeTxSolanaLane` выполняет всё это. Механика:

1. **Соберите неподписанную транзакцию EIP-1559** с `from` = синтетический адрес — без подписи secp256k1.
2. **Обнаружьте аккаунты** — вызовите [`rome_emulateCallAccounts`](/ru/spravochnik/json-rpc.md) с `from`, `в`, `данными`, **и `value`** (если вызов платный). Передача `value` важна: прокси эмулирует *реальный* вызов, поэтому любая запись в storage, зависящая от значения, — например, новый слот mapping — выделяется, и возвращается его аккаунт Solana. Если пропустить его, этого аккаунта не будет, и транзакция завершится ошибкой *"instruction modified data of a read-only account."*
3. **Соберите транзакцию Solana:** две **ComputeBudget** инструкции (EVM Rome требуется повышенный лимит CU ≈1,35 млн и большой heap frame ≈250 КБ — значения по умолчанию Solana дают сбой) + **`DoTxUnsigned`** инструкция (неподписанный RLP + найденные аккаунты) + **treasure wallet** (исполнение платит ему небольшую комиссию; при обнаружении он опускается, поэтому SDK добавляет его).
4. **Кошелёк подписывает и отправляет.** Названия **Кошелёк Solana подписывает транзакцию Solana (Ed25519) и отправляет её в Solana RPC — не в прокси.** Среда выполнения Solana проверяет подпись; на блокчейне программа выводит `msg.sender` из этого подписанта. Полномочие определяется подписью Solana, а не Ethereum. (Прокси используется только для эмуляции/обнаружения на шаге 2.)

PDA пользователя (`["EXTERNAL_AUTHORITY", synthetic_address]`) владеет их токен-аккаунтами и подписывает SPL CPI от их имени — так что пользователь Solana может полностью выполнять supply, borrow, swap и LP из своего кошелька Solana.

## Внешние конечные точки V1

`submitRomeTxV1` — это одновызовный способ: он запрашивает у прокси собранную транзакцию v1, проверяет её, просит кошелёк подписать её и отправляет в Solana RPC, выполняя ретрансляцию через прокси, когда вызов не помещается. Прокси предоставляет тот же канал в виде RPC-методов, поэтому любой клиент может использовать его без SDK (полный список в [Поддержка JSON-RPC](/ru/spravochnik/json-rpc.md)):

* `rome_composeUnsignedAtomicV1(rlp, pubkey, signature)` — одна транзакция Solana v1; кошелёк подписывает её, а приложение транслирует её в Solana RPC. Отклоняется, если вызов не помещается в одну атомарную транзакцию.
* `rome_composeUnsignedIterativeV1(rlp, pubkey, signature)` — каждый этап исполнения (атомарный, если помещается, иначе итеративные этапы); кошелёк подписывает каждый, `rome_submitSignedV1Plan` транслирует их. Ограничения для длинного плана приведены в [Ретранслируемые вызовы и длинные вызовы](#relayed-calls-and-long-calls).
* `rome_relayUnsignedV1(rlp, pubkey, signature)` — одна подпись кошелька, плательщик прокси выполняет этапы и возвращает хэш EVM-транзакции; синтетический адрес оплачивает газ Rome, поэтому один раз пополните его через `rome_composeExternalDepositV1`.

**Подписанная авторизация.** `rlp` — это необработанное неподписанное тело EIP-1559 из 9 полей. Кошелёк подписывает своим ключом Ed25519 либо сырые 32 байта `keccak256(0x02 ‖ rlp)` либо читаемое сообщение

```
Rome authorization\n0x<64 строчных hex-символов этого хэша>
```

Оба варианта принимаются; читаемый формат — это то, что кошелёк отображает в понятном виде в своём `signMessage` окне подтверждения. Программа пересчитывает хэш из тела, которое она исполняет, и требует, чтобы подписанное сообщение было в точности в одной из двух форм, поэтому подпись для одного тела никогда не сможет авторизовать другое, а nonce тела предотвращает повторное воспроизведение.

## Ретранслируемые вызовы и длинные вызовы

Некоторые вызовы не помещаются в одну транзакцию Solana. В этом разделе описано, что происходит в таком случае и какие ограничения применяются.

### Когда атомарный компоновщик отказывает

`rome_composeUnsignedAtomicV1` отказывает в вызове, который не помещается в одну атомарную транзакцию. `submitRomeTxV1` затем ретранслирует вызов через прокси с той же **той же** подписью авторизации, поэтому кошелёк не запрашивается повторно. В результате будет `route: "relay"` и без подписи Solana `подписи`. Передайте `relayOnRefusal: false` чтобы вместо этого получить отказ как ошибку, либо вызовите `relayV1` самостоятельно, чтобы намеренно выполнить ретрансляцию.

Три вещи, которые следует знать о ретранслированном вызове:

* **Газ оплачивает синтетический адрес.** На маршруте ретрансляции прокси отправляет транзакции Solana и оплачивает их, а сам вызов оплачивается в нативном газовом токене сети из баланса синтетического адреса. Баланс должен быть не меньше `gasLimit × maxFeePerGas`. `fundSyntheticGas(deps, amountWei)` пополняет его из собственного токен-аккаунта кошелька для газового токена сети (USDC в Hadrian и Rubicon); кошелёк подписывает одну обычную транзакцию v1 и ему нужно немного SOL для комиссии Solana. `amountWei` выражается в wei сети с 18 знаками после запятой и должен быть кратен 10^12, иначе прокси отклонит его. Без SDK `rome_composeExternalDepositV1` собирает ту же транзакцию.
* **Revert не вызывает исключение.** Ретранслированный вызов, который завершается revert на стороне EVM, завершается штатно, с `receipt.status === "0x0"`. Проверьте `receipt?.status === "0x1"` прежде чем считать вызов завершённым. `receipt` является `null` если receipt не появился вовремя.
* **`relayOnRefusal: false` это выбор, сделанный в SDK, а не ограничение.** Прокси уже хранит подпись авторизации для этого полезного payload и может выполнить его любым маршрутом, вплоть до `gasLimit × maxFeePerGas`.

```javascript
const res = await submitRomeTxV1(deps, { to, data, value });
if (res.route === "relay" && res.receipt?.status !== "0x1") {
  // ретранслированный вызов, который завершился revert, возвращается штатно; он не выбрасывает исключение
}
```

### Долгие вызовы

Вызов, которому требуется более одной транзакции, выполняется как итеративная сессия: программа исполняет столько EVM-шагов, сколько помещается в каждую транзакцию Solana, и сохраняет состояние между ними. Есть два способа запускать её из кошелька Solana:

* **Ретранслируйте его.** `rome_relayUnsignedV1` требует одной подписи кошелька, а плательщик прокси отправляет каждый этап. Газ оплачивает синтетический адрес, как указано выше.
* **Подписывайте этапы самостоятельно.** `rome_composeUnsignedIterativeV1` возвращает каждый этап с пустым полем подписи, кошелёк подписывает каждый из них, а `rome_submitSignedV1Plan` транслирует их по порядку. Каждый этап — это отдельная транзакция Solana, которую кошелёк должен подписать, поэтому кошелёк, подписывающий по одной транзакции за раз, запрашивает подтверждение на каждом этапе, помимо подписи авторизации. Кошелёк оплачивает комиссию Solana на каждом этапе.

Ограничения:

| Ограничение                                         | Значение        | Откуда оно берётся                                                                                                            |
| --------------------------------------------------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| Транзакций в одном `rome_submitSignedV1Plan` вызове | 64              | Прокси отклоняет более длинный план                                                                                           |
| Этапов, которые спланирует итеративный компоновщик  | 128             | Предел в итеративном компоновщике Rust Rome SDK, который также ограничивает план `rome_composeUnsignedIterativeV1` возвращает |
| Итераций в одной сессии                             | 160             | Программа Rome EVM; после его достижения сессия перестаёт возобновляться, снимает блокировки аккаунтов и завершается          |
| Размер транзакции Solana и аккаунты                 | 4 КБ, 64 адреса | Каждый этап — это транзакция Solana v1                                                                                        |

Большинство реальных вызовов укладываются в значительно меньшее число этапов, чем эти пределы. Лимит в 64 транзакции для плана, подписанного кошельком, — это первое ограничение, с которым вы столкнётесь. Вызов, которому требуется больше этапов, не может быть транслирован через `rome_submitSignedV1Plan`.

Долгий вызов не может выполнять CPI по legacy-треку. Вызовы с изменением состояния в legacy-прекомпиляциях (`0xff…08`, `0xff…09` и `0x42…16`) отклоняются в итеративной сессии с `CpiProhibitedInIterativeTx`. Чтение состояния Solana и cached-track-прекомпиляции (`0xff…04`, `0xff…05`, `0xff…06`, `0xff…0b`) работают. Вызов, который выполняет CPI по legacy-треку, должен помещаться в одну атомарную транзакцию, независимо от канала отправки. См. [Ограничения](/ru/klyuchevye-ponyatiya/constraints.md) и [Бюджет вычислений](/ru/klyuchevye-ponyatiya/compute-budget.md).

## Референсные реализации

* **Aerarium** — управляет Compound v3 (supply / borrow) из кошелька Solana через `DoTxUnsigned`. Это референс для более старого канала legacy-транзакций, описанного выше, а не для `submitRomeTxV1`.
* **Rome DEX** не является примером для этой страницы: его сторона Solana — это нативная программа Solana, которую кошелёк подписывает напрямую, а его сторона EVM использует тот же пул.

Aerarium не требует моста или второго кошелька — синтетический адрес делает один ключ Solana полноценным EVM-аккаунтом. Для схемы v1 следуйте [Создание приложения с двумя каналами](/ru/rukovodstva-dlya-razrabotchikov/dual-lane-app.md).

## Что дальше

* [Вызов Solana из EVM](/ru/rukovodstva-dlya-razrabotchikov/call-solana-from-evm.md) — в обратном направлении, через CPI
* [Совместимость токенов](/ru/klyuchevye-ponyatiya/token-interop.md) — как балансы разделяются между EVM и Solana


---

# 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/ru/rukovodstva-dlya-razrabotchikov/call-evm-from-solana.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.
