For the complete documentation index, see llms.txt. This page is also available as Markdown.

Eine App mit zwei Lanes bauen

Eine Schritt-für-Schritt-Anleitung für eine App mit zwei Lanes — ein Solidity-Contract, der sowohl von einem MetaMask-Nutzer als auch von einem Phantom-(Solana)-Nutzer verwendet wird, mit genau dem, was in jedem Schritt passiert.

Eine Dual-Lane-App ist ein Solidity-Vertrag, den sowohl ein MetaMask (EVM) Benutzer als auch ein Phantom (Solana) Benutzer direkt verwenden — derselbe Vertrag, derselbe Zustand, jeder mit der Wallet, die er bereits hat. Diese Seite führt genau durch genau was bei jedem Schritt auf jeder Seite geschieht.

Das Beispiel ist ein winziger Vault: du einzahlst USDC und später abhebst ihn. „Stake / unstake“, „supply / redeem“, „tip / claim“ haben dieselbe Form.

Was du schreibst — ein standardmäßiger ERC-20-Vault

Auf Rome erscheint das USDC eines Solana-Nutzers auf der EVM-Seite als ein gewöhnlicher ERC-20-Token — der SPL-Wrapper für dieses Mint (z. B. wUSDC). Dein Vertrag ist also ein normaler Token-Vault: Er zieht Tokens mit transferFrom und gibt sie mit transferzurück. Nichts Rome-Spezifisches:

interface IERC20 {
    function transfer(address to, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

contract Vault {
    IERC20 public immutable token;                 // der wUSDC-Wrapper
    mapping(address => uint256) public balanceOf;
    constructor(IERC20 _token) { token = _token; }

    function deposit(uint256 amount) external {
        require(token.transferFrom(msg.sender, address(this), amount));
        balanceOf[msg.sender] += amount;
    }
    function withdraw(uint256 amount) external {
        balanceOf[msg.sender] -= amount;
        require(token.transfer(msg.sender, amount));
    }
}

Warum ERC-20 und nicht payable / msg.value? Der ausgebbare Saldo eines Solana-Nutzers ist sein SPL-Token-Konto der Wallet, 1:1 als dieser ERC-20-Wrapper dargestellt — nicht der native EVM-Saldo. Ein Solana-Nutzer kann also immer einen transferFrom, aber ein deposit() payable würde nativen Wert benötigen, den er nicht als Guthaben hält. Wenn du um den Token herum baust, funktionieren beide Lanes gleich.

Deploye ihn mit Foundry oder Hardhat und richte den Konstruktor auf die Wrapper-Adresse für deinen Token aus (aus dem Registry). Auf Rome ist der Gas-Token USDC, also brauchst du ein kleines USDC-Gas-Guthaben für das Deployment.

Die beiden Lanes

Lane
Wallet
Wie die App ihn aufruft

EVM

MetaMask (ein EVM-Schlüssel)

submitRomeTx — der standardmäßige Rome-Write

Solana

Phantom (ein Solana-Schlüssel)

submitRomeTxSolanaLane — kein EVM-Schlüssel erforderlich

Die EVM-Lane ist gewöhnlich. Der Rest dieser Seite ist die Solana-Lane — die interessante Hälfte.

Die Kernidee: Das synthetische Konto ist ein Durchlaufkonto

Die EVM-Identität eines Solana-Nutzers ist seine synthetische Adressekeccak256(solana_pubkey)[12:]. Sie ist sein msg.sender im Vertrag, hält aber nichts als Guthaben. Das Geld des Nutzers liegt in seiner Solana-Wallet (als SPL USDC), und auf der EVM-Seite ist derselbe Saldo das, was wUSDC.balanceOf(synthetic) ausliest. Wert fließt durch das synthetische Konto:

  • In (deposit): Wallet-Token-Konto → synthetisches Token-Konto → Vertrag (über transferFrom).

  • Aus (withdraw): Vertrag → synthetisches Token-Konto → Wallet-Token-Konto.

Das synthetische Konto endet nach jedem Hin-und-her wieder bei null.

Einmalig: Aktivieren (das synthetische Konto bereitstellen)

Das On-Chain-Konto eines brandneuen synthetischen Kontos existiert nicht, bis du es erstellst. Wenn ein Solana-Nutzer zum ersten Mal handelt, wird sein synthetisches Konto bereitgestellt mit einem create_pda Aufruf — danach können wertbewegende Aufrufe (der ERC-20 transferFrom, der Sweep) von ihm signiert werden.

submitRomeTxSolanaLane macht das beim ersten Gebrauch automatisch (autoProvision ist standardmäßig aktiviert). Wenn du lieber einen expliziten „Aktivieren“-Bildschirm anzeigen möchtest (eine einmalige Kontoeinrichtung), mach es selbst:

Was jede Seite benötigt

Benötigt
Warum

Solana-Nutzer (Phantom)

SOL (ein bisschen)

zahlt die Solana-Transaktionsgebühr bei jeder Lane-Transaktion

USDC als SPL-Token in ihrer Wallet

den Wert, den sie einzahlen (auf der EVM-Seite als wUSDC)

EVM-Nutzer (MetaMask)

USDC als ihr Rome-Gas-Guthaben

Gas + Wert; sie füllen es auf, indem sie USDC bridgen nach Rome hinein (es gibt keinen Faucet)

Du (der Entwickler)

eine Wallet mit USDC Gas auf Rome

um den Vertrag zu deployen

Wert IN — Einzahlung (Schritt für Schritt)

Der Solana-Nutzer hat SOL + USDC in seiner Phantom-Wallet. Jede Lane-Transaktion wird von Phantom signiert — die Wallet signiert sie und sendet sie an den Solana-RPC (der Proxy wird nur zum Auffinden von Konten verwendet):

  1. Fund-LegbuildFundLeg(...)submitSolanaInstructions(...). Erstellt das USDC-Token-Konto des synthetischen Kontos (falls nötig) und führt ActivateAta, wobei amount USDC aus dem Token-Konto der Wallet in das des synthetischen Kontos. Jetzt wUSDC.balanceOf(synthetic) zeigt dieses Guthaben an.

  2. GenehmigensubmitRomeTxSolanaLane({ to: wUSDC, data: approve(vault, amount) }). Ermöglicht es dem Vault, die Tokens abzuziehen. (Das ist normalerweise der erste Lane-Aufruf, also wird das synthetische Konto hier automatisch bereitgestellt.)

  3. EinzahlensubmitRomeTxSolanaLane({ to: vault, data: deposit(amount) }). Der Vault führt transferFrom(synthetic, vault, amount) aus — die USDC wandern vom synthetischen Konto in den Vault und werden der Adresse des synthetischen Kontos gutgeschrieben.

Nettoeffekt: USDC ging Phantom-Wallet → (synthetisches Konto) → der Vault.

Wert OUT — Abhebung (Schritt für Schritt)

Nun hebt der Nutzer ab. Ebenfalls von Phantom signiert:

  1. AbhebensubmitRomeTxSolanaLane({ to: vault, data: withdraw(amount) }). vault.withdraw führt transfer(synthetic, amount) aus — USDC wandert vom Vault zurück in das synthetische Token-Konto.

  2. Sweep-LegbuildSweepLeg(...) gibt dir den HelperProgram.transfer_spl Aufruf + Konten; führe ihn aus (erstelle bei Bedarf das Token-Konto der Wallet, dann einen DoTxUnsigned an das Helper-Precompile), um die USDC vom synthetischen Konto zurück in die eigene Solana-Wallet des Nutzerszu verschieben. Das synthetische Konto endet bei null.

Nettoeffekt: USDC ging der Vault → (synthetisches Konto) → die Phantom-Wallet des Nutzers. Nichts bleibt liegen.

Die Stolpersteine — alle vom SDK gehandhabt

Das sind die Dinge, die eine manuell gebaute Solana-Lane-Transaktion falsch macht; submitRomeTxSolanaLane macht sie für dich:

  • Bereitstellung. Das Konto eines frischen synthetischen Kontos muss erstellt werden (create_pda) bevor irgendein wertbewegender Aufruf erfolgt, sonst kann es die Überweisung nicht signieren. Beim ersten Gebrauch automatisch; mit autoProvision: false + provisionSynthetic.

  • Den Wrapper ausgeben, nicht msg.value. Der Saldo eines Solana-Nutzers ist sein SPL-Token-Konto, dargestellt als der ERC-20-Wrapper — bewege ihn mit transfer / transferFrom, niemals nativen Wert.

  • ComputeBudget. Die EVM von Rome braucht ein erhöhtes CU-Limit (~1,35 Mio.) und einen großen Heap-Frame (~250 KB). Solanas Standardwerte von 200K CU / 32 KB verursachen Fehler.

  • Treasure-Wallet. Die Ausführung zahlt an ein chain-spezifisches Treasure-Konto eine kleine Gebühr; die Kontoerkennung lässt es aus, also hängt das SDK es an.

  • Wohin es gesendet wird. Die Wallet signiert die Solana-Transaktion und sendet sie an den Solana-RPC — nicht an den Proxy. Der Proxy wird nur für die Kontoerkennung verwendet. On-Chain leitet das Programm msg.sender aus dem Solana-Signer ab.

  • Gas ist USDC. Kein Faucet — bridge USDC hinein (siehe Finanzierung).

Dieselbe App von MetaMask aus

Ein EVM-Nutzer ruft denselben Vertrag mit submitRomeTx auf — standardmäßige EVM-Tools, Gas in USDC. Er muss trotzdem genehmigen und dann einzahlst (ERC-20 wie üblich), ohne Fund-/Sweep-Legs (seine Tokens liegen bereits an seiner EVM-Adresse). Beide Nutzer teilen denselben balanceOf Zustand.

Was als Nächstes kommt

Zuletzt aktualisiert

War das hilfreich?