> 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/apps-on-rome/bloom/lanes.md).

# The two lanes

One token deployment, two wallet worlds. Both lanes execute the **same contracts** with the **same compliance gate** — they differ only in who signs and how the transaction reaches the chain.

## EVM lane

Standard Ethereum tooling, unchanged: the wallet signs an EVM transaction, the chain's JSON-RPC endpoint accepts it, the token contract enforces the allowlist. Nothing Bloom-specific to integrate — measured cost of a gated transfer is \~3.1M gas (Rome gas is USDC-denominated; a plain native transfer is \~1.48M for scale).

## Solana lane

A Solana wallet drives the same EVM contracts **natively — no EVM key exists anywhere**:

1. **Identity.** The user's EVM-side address is *synthetic*: derived deterministically as `keccak256(solana_pubkey)[12..32]`. The Rome EVM program derives it on-chain from the transaction's actual signer — it cannot be spoofed, and no secp256k1 private key for it exists, so **the Solana signature is the only thing that can ever drive this address**.
2. **Discovery.** The client asks the chain's RPC (`rome_emulateCallAccounts`) which Solana accounts the EVM call will touch; the emulation also decides which are writable (the call's `value` must be included — writability of the recipient's balance account depends on it).
3. **Submit.** The client builds a `DoTxUnsigned` instruction — an *unsigned* EIP-1559 payload authorized by the Solana signature — plus compute-budget instructions (250KB heap, 1.35M CU) and the fee-pool account discovery omits. The wallet signs one Solana transaction; done.

   **Size, measured on this deployment 2026-07-30:** a storefront buy resolves to **22 accounts** and serialises to **1,061 bytes** against the 1232-byte legacy limit — 171 bytes of headroom, about five more accounts. So it does NOT need a lookup table today. A state that touches more — an associated token account still to be created, a first-time holder — could still cross it, and past 1232 bytes the client falls back to a v0 transaction over an address lookup table it CREATES for the purpose.

   **That fallback is expensive for a wallet:** each ALT-extend chunk and the v0 resubmit is a separate `signTransaction`, so one Buy becomes three or four prompts. Hadrian already has a persistent dApp table holding these contracts' accounts — referencing it instead is the fix, which the lane client cannot do yet.
4. **Confirm.** The transaction confirms on Solana; the EVM view catches up a beat later (the tooling polls the nonce before sending the next call).

### ATOMIC ONLY — the lane's hard boundary

`DoTxUnsigned` is **one EVM transaction executed inside one Solana transaction** by Rome's atomic VM. The iterative VM — the one that stages a single EVM execution across several Solana transactions — is not reachable through this lane yet, so **a call that will not fit atomically cannot take this lane at all.** There is no fallback to reach for and none should be added; the fix for a call that does not fit is a smaller call.

Two things are commonly mistaken for it, and neither is:

* **The v0 + lookup-table fallback** is a bigger *envelope*, not a different execution: the same single instruction, carried in a transaction format with room for far more account keys. Measured today the storefront buy does not need it (1,061 bytes of 1232), and a call that did would still be atomic.
* **A multi-step journey is not a multi-leg execution.** Bloom's `Run` calls its steps *legs* — approve, then buy — and each is a **whole transaction, signed separately, atomic on its own**. Rome calls the chunks of ONE split execution legs too. This app has the first and must never ask for the second. The word collides; the mechanisms do not.

Every flow in the table below is one atomic transaction, and the heaviest uses just over half the per-transaction compute ceiling — so the boundary is not close for anything Bloom does today.

Measured on Rome devnet, one signature each — ranges observed across two chains:

| Solana-lane flow                                                | CU         |
| --------------------------------------------------------------- | ---------- |
| Gated RWA transfer                                              | \~339–381K |
| wUSDC approve                                                   | \~189–191K |
| Storefront buy (approve + buy = 2 sigs total)                   | \~730–796K |
| Cash-leg sweep to the wallet's own token account                | \~42–54K   |
| Plain value transfer                                            | \~47K      |
| `TestApprover.approve` — allowlist admission, **wallet-signed** | 220,794    |

For comparison, Solana's per-transaction compute ceiling is 1.4M CU — the heaviest Bloom flow uses just over half of it.

## Custody: why the RWA stays at the synthetic

The design question every permissioned asset must answer: *can the token leak out of the compliance perimeter?* On the Solana lane the answer is structural, not policy:

* **The RWA is EVM-state-resident.** Its balances are storage inside the Rome EVM program's accounts. There is **no SPL mint** of it, no token account representation, nothing Solana-native to move. (This is deliberate — a wrapped/mirrored version would be the escape hatch.)
* **The Solana-side plumbing can't touch it.** The lane's asset-moving legs (`create_ata`, SPL transfers, sweeps) operate on SPL token accounts; EVM storage balances have no such account. There is no instruction that moves an EVM balance to a Solana token account.
* **Only the owner's Solana signature drives the synthetic**, and every movement it can express is a call into the token contract — which runs the allowlist gate — so a transfer to a non-whitelisted address reverts identically whether signed by an EVM key or a Solana key.
* **The cash leg is the deliberate exception.** wUSDC (yield, sale proceeds) is SPL-backed and *should* round-trip: the exit leg (`transfer_spl`) moves it from the synthetic to the wallet's own associated token account, verified end-to-end. Asset stays in the perimeter; cash flows freely.

Two practical consequences, stated plainly: a Solana wallet UI won't display the RWA holding (there is no SPL token to list) — the Bloom app is the positions surface; and a lost Solana key is recovered through the issuer's process ([COMPLIANCE.md](/apps-on-rome/bloom/compliance.md#issuer-powers-transfer-agent-shaped)), not by moving the asset out-of-band — which is exactly the property a permissioned asset wants.


---

# 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/apps-on-rome/bloom/lanes.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.
