> 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/de/entwicklerleitfaden/integrate-solana-program.md).

# Ein Solana-Programm integrieren

Integrieren Sie ein Solana-Programm mit Rome EVM — wer der Solana-Signer ist, wie Werte in Ihr Programm gelangen und wieder zurückkommen, die Einspurregel, atomar vs. iterativ sowie die Bereitstellung von Konten.

Dieser Leitfaden richtet sich an ein Solana-Programmierteam, das möchte, dass Rome-EVM-Nutzer ihr Programm verwenden — ein Privacy-, Lending- oder Vault-Programm, das Wert für EVM-Konten halten soll. Er baut auf [Solana aus EVM aufrufen](/de/entwicklerleitfaden/call-solana-from-evm.md) (den Mechaniken der Precompiles) und beantwortet die Fragen, die auftauchen, sobald auf der anderen Seite ein echtes Programm steht: *wer* signiert, *wie* Token rein- und rauskommen, und *welche* Grenzen das Design bestimmen.

Der durchgehend verwendete Ablauf: Ein EVM-Konto verschiebt USDC in Ihr Programm („shield“ oder „deposit“), Ihr Programm hält sie für diesen Nutzer und gibt sie später wieder frei („unshield“ oder „withdraw“). Ersetzen Sie die Verben durch Ihre eigenen.

## 1. Jede EVM-Adresse hat eine Solana-Identität

Rome leitet für jede EVM-Adresse — Nutzer und Verträge gleichermaßen — ein Solana-Konto ab, genannt ihre **externe Autorität**:

```
external_auth(addr) = find_program_address(["EXTERNAL_AUTHORITY", addr_h160], rome_evm_program_id)
```

Sie besitzt die zugehörigen Token-Konten der Adresse (dort, wo `wUSDC.balanceOf(addr)` ausliest) und sie ist der Schlüssel, der die auf dieser Adresse ausgeführten CPIs signiert. Ihr Programm sieht einen 32-Byte-Solana-Pubkey; es sieht nie ein H160.

**Wer signiert, wird dadurch entschieden, wer das Precompile aufruft:**

| Das Precompile wird aufgerufen von                                                        | Der Solana-Signer                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Eine **Vertrag**, mit einer normalen `CALL`                                               | Der **eigenen des Vertrags** externen Autorität (`external_auth(contract)`), plus alle gesalzenen Sub-PDAs des Vertrags, die er anfordert                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| Eine **Nutzer**, in einer Transaktion, die direkt an die Precompile-Adresse gesendet wird | Der **eigene des Nutzers** externe Autorität                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| Ein Vertrag über `DELEGATECALL`                                                           | Die bloße Autorität wird abgelehnt (`DelegatecallOwnerAuthority`). `invoke` ohne Signer, und `invoke_signed` mit gesalzenen Sub-PDAs laufen weiterhin — aber diese Sub-PDAs sind durch die Adresse des laufenden Vertrags im Namensraum getrennt (`["EXTERNAL_AUTHORITY", caller, running_contract, salt]`), daher `pda_with_salt` leitet sie nicht ab. Bei HelperProgram bestehen nur die Selektoren für Erzeugung und Finanzierung (`create_ata*`, `create_pda*`, `swap_gas_to_lamports`, `init_spl_mint`, `create_and_init_mint`); sie sind ausgenommen, weil ihr Ziel an den Aufrufer gebunden oder vom Operator finanziert wird, nicht weil sie nichts signieren |

Die Konsequenz ist die Ihnen bereits bekannte ERC-20-Regel: **ein Vertrag kann die Vermögenswerte eines Nutzers nicht von sich aus bewegen.** Er handelt als seine eigene Identität, und der Nutzer gewährt ihm zuerst Rechte. Für Token, die auf der Solana-Seite bewegt werden müssen, ist diese Erlaubnis eine SPL-Delegation auf dem Token-Konto des Nutzers; die ERC-20- `Genehmigung` ist nur eine Erlaubnis auf EVM-Seite, die die eigenen Transfers des Wrappers respektieren. So arbeiten die Token-Wrapper von Rome, und so wird auch Ihr Adapter arbeiten (Abschnitt 3).

Gesalzene Sub-PDAs: `invoke_signed` nimmt eine Liste von `bytes32` Salts, und Rome signiert mit `["EXTERNAL_AUTHORITY", caller_h160, salt]` für jedes (Bump angehängt; der Aufrufer ist der Vertrag bei einem Direktaufruf). `HelperProgram.pda_with_salt(addr, salt)` liefert die Adresse aus Solidity zurück. Verwenden Sie einen Salt pro Nutzer (zum Beispiel die Adresse des Nutzers), wenn Ihr Programm pro Position ein eigenes Eigentümerkonto möchte.

Finanzierung: Ein PDA der externen Autorität muss nicht existieren, um ein ATA zu besitzen (`create_ata` ist idempotent und der Solana-Gebührenzahler der Transaktion deckt die Miete). Er muss nur existieren und Lamports halten, wenn er Konten finanziert, die Ihre Instruktion erstellt. `HelperProgram.create_pda(addr)` erstellt ihn als reines System- `CreateAccount`, daher nur einmal aufrufen; es ist nicht idempotent. `swap_gas_to_lamports(n)` schreibt dem PDA der Adresse gut mit `n` Lamports vom Solana-Gebührenzahler der Transaktion, und der Aufrufer bezahlt sie in Rome-Gas: Jeder Effekt eines Legacy-Track-CPI auf das Guthaben des Gebührenzahlers wird zur Transaktionsgebühr addiert und dem Nutzer zum Gaspreis berechnet, genauso wie die Miete für Konten berechnet wird, die ein CPI erstellt. Der Bereitstellungsschritt der Solana-Lane wird beschrieben in [EVM aus Solana aufrufen](/de/entwicklerleitfaden/call-evm-from-solana.md); ein Vertrag stellt sein eigenes PDA in seinem Konstruktor oder beim ersten Aufruf bereit.

## 2. Drei Möglichkeiten, eine Position eines Nutzers zu halten

**A. Ein Adaptervertrag (empfohlen für einen wiederverwendbaren Adapter).** Ein Solidity-Vertrag sitzt vor Ihrem Programm. Der Nutzer gewährt der Autorität des Adapters eine SPL-Delegation auf seinem Token-Konto; der Adapter zieht die Token in ein von ihm besessenes Token-Konto, ruft Ihr Programm als er selbst auf und adressiert die Position des Nutzers in Ihrem Programm über ein nutzerspezifisches Sub-PDA oder über eine Kennung, die er in den Instruktionsdaten übergibt. Auf dem Rückweg veranlasst er Ihr Programm, an das eigene Token-Konto des Nutzers freizugeben. Der Adapter ist die einzige Solana-Identität, die Ihr Programm kennen muss. Die Erlaubnis ist eine SPL-Delegation, keine ERC-20- `Genehmigung`; Abschnitt 3 erklärt, warum und was es den Nutzer kostet.

**B. Der Nutzer als Solana-Signer.** Das Frontend sendet eine Transaktion vom EVM-Konto des Nutzers **direkt an das CpiProgram-Precompile** (`to = 0xff…08`, calldata = das ABI-kodierte `invoke` oder `invoke_signed`), sodass die eigene externe Autorität des Nutzers signiert und Ihr Programm die Position damit adressiert. Kein Adaptervertrag und keine Delegation, aber jede Aktion ist eine undurchsichtige Calldata-Transaktion in der Wallet, und alles, was der Ablauf außer dem CPI benötigt (das ATA, eine vorherige Überweisung), ist eine separate Transaktion. Das eignet sich für einmalige Erlaubnisse (eine Nutzer- `approve_spl` an einen Wrapper) und für einfache Programme, nicht für mehrstufige Abläufe. [Cardo](https://github.com/rome-protocol/cardo) verwendet diese Form: Sein Frontend sendet das `invoke` direkt aus der Wallet des Nutzers an das Precompile.

**C. Ein Verwahrer-loser Router.** Ein Vertrag, der nie die Token des Nutzers hält. Der Nutzer gewährt der Autorität des Routers eine SPL-Delegation auf seinem eigenen Token-Konto; der Router führt CPIs in Ihr Programm mit dieser Autorität als Transferautorität aus, sodass Token vom Token-Konto des Nutzers auf das Konto Ihres Programms (oder zurück zum Konto des Nutzers) bewegt werden, und der Router hält zwischen den Aufrufen nichts. Der Router leitet jedes nutzerseitige Konto ab aus `msg.sender`, sodass die Erlaubnis eines Nutzers nicht auf das Konto eines anderen Nutzers gerichtet werden kann, und er ruft ein festes Programm mit festen Instruktionsformen auf, statt als allgemeiner `invoke` Durchleiter zu fungieren. Der `RomeDexRouter` in [rome-dex](https://github.com/rome-protocol/rome-dex) ist so geschrieben; die EVM-Lane von rome-dex bietet auch das rohe direkte `invoke` in Form B an. Es benötigt dieselbe Erlaubnis wie A, mit denselben Kosten für einen einzigen Delegate (Abschnitt 3), und keinen Pull-Schritt.

Der Rest dieses Leitfadens folgt A; C verwendet dieselbe Erlaubnis und dieselbe Signaturregel.

## 3. Token rein, Token raus

USDC auf Rome ist ein einziger SPL-Mint, von zwei Seiten gesehen: der `wUSDC` ERC-20-Wrapper auf der EVM-Seite und das ATA der externen Autorität auf der Solana-Seite. Für die eigene Adresse eines Nutzers ist beides derselbe Kontostand: `wUSDC.balanceOf(user)` liest das ATA des Nutzers aus. Dazwischen gibt es keinen Bridge- oder Wrapping-Schritt.

**Das Wrapper-Guthaben eines Vertrags ist kein Token-Konto, das der Vertrag kontrolliert.** Die Factory stellt `SPL_ERC20_cached` für einen gewöhnlichen Mint bereit, und Hadrians `wUSDC` ist eines davon. Dieser Wrapper kann nicht für einen Vertrag signieren, also wenn `wUSDC.transferFrom(user, adapter, amount)` oder `transfer` Token an einen Vertrag sendet, bewegen sich die SPL-Token in das **eigene Escrow-Token-Konto des Wrappers** (`escrow_ata()`), und der Wrapper speichert den Betrag pro Vertrag in seinem Storage. `wUSDC.balanceOf(adapter)` gibt diesen Eintrag zurück, ein Escrow-Ledger, nicht den Kontostand eines Token-Kontos, das von `external_auth(adapter)`. Ihr Adapter kann diese Token Ihrem Programm nicht in einem CPI übergeben; sie können den Wrapper nur wieder über den Wrapper verlassen. Also hat ein Adapter, der sich selbst mit `transferFrom` finanziert, auf der Solana-Seite nichts, was er ausgeben kann.

Der funktionierende Weg ist eine SPL-Delegation:

* **Erlaubnis.** Der Nutzer sendet `approve_spl(adapter, amount, mint)` als Transaktion **direkt an das HelperProgram** (`to = 0xff…09`), wobei `mint` der des Wrappers `mint_id()`. Die eigene Autorität des Nutzers signiert, und der Delegate, der auf dem Token-Konto des Nutzers gesetzt wird, ist die externe Autorität des Adapters.
* **In.** Die `deposit(amount)` zieht mit der Delegate-Überladung `HelperProgram.transfer_spl(srcAta, dstAta, amount, mint)`, mit dem ATA des Nutzers als Quelle und dem eigenen ATA des Adapters (`HelperProgram.ata(address(this), mint)`, einmal erstellt mit `create_ata`) als Ziel. Die Autorität des Adapters signiert als Delegate. Die Token befinden sich nun in einem Token-Konto, das von `external_auth(adapter)`besitzt wird, also dem Konto, das Ihr CPI benennt; der Signer und die Quelle sind die eigenen des Adapters, daher ist keine weitere Berechtigung erforderlich. Sie erscheinen nicht in `wUSDC.balanceOf(adapter)`.
* **Aus.** Die Withdraw-Instruktion Ihres Programms überträgt an das **Konto des Nutzers** ATA, `HelperProgram.ata(user, mint)`. Der Empfang benötigt keine Signatur des Nutzers, und der Kontostand ist sofort sichtbar als `wUSDC.balanceOf(user)`. Falls das ATA noch nicht existiert, erstellen Sie es zuerst mit `HelperProgram.create_ata(user, mint)` (idempotent).

**Ein Token-Konto hat genau einen Delegate.** Die obige Erlaubnis ersetzt die eigene Erlaubnis des Wrappers auf diesem Konto. Bis der Nutzer dem Wrapper erneut erlaubt, wird jede `wUSDC` `transfer` oder `transferFrom` Überweisung von ihrer Adresse `0x4`zurückgewiesen mit SPL-Fehler `wUSDC.isEnabled(user)`; wenn es `false`ist, bitten Sie den Nutzer, zu senden `approve_spl(wrapper, 2^64-1, mint)` erneut an das HelperProgram, dieselbe maximale Erlaubnis, um die der Wrapper herum gebaut ist (siehe [Einmalig: dem Wrapper die Erlaubnis geben](/de/entwicklerleitfaden/dual-lane-app.md#one-time-grant-the-wrapper)). Eine begrenzte Erlaubnis würde die normalen `wUSDC` Übertragungen des Nutzers wieder unterbrechen, sobald sie knapp wird, und `isEnabled` bleibt `true` solange noch etwas davon übrig ist, wüsste die App nicht, dass sie erneut fragen muss.

Um zu prüfen, welchen Wrapper ein Mint hat, lesen Sie die Factorys `wrapper_kind_by_mint(mint)` (die Factory-Adresse steht in der `contracts.json` in der [Registry](https://github.com/rome-protocol/rome-registry)). `1` ist der gecachte Wrapper, den dieser Abschnitt beschreibt. `2` ist der Direct-CPI-Wrapper, den die Factory für einen Token-2022-Mint mit einem aktivierten Transfer-Hook bereitstellt; dieser Leitfaden behandelt ihn nicht. Die Registry `tokens.json` listet die Wrapper-Adresse und den Mint, aber nicht, um welche Art es sich handelt.

## 4. Ein Track pro Vertrag und pro Transaktion

Rome hat zwei Familien von Precompiles, die den Solana-Zustand verändern, und **eine Transaktion verwendet genau eines davon** — die Laufzeit lehnt eine Transaktion ab, die mutierende Aufrufe von beiden mischt, sogar über Vertragsgrenzen hinweg.

| Track      | Precompiles                                                                                         | Wirkungen landen                                                                                      | Funktioniert in iterativer Ausführung                                                                |
| ---------- | --------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| **Legacy** | `CpiProgram` `0xff…08` (`invoke`, `invoke_signed`), `HelperProgram` `0xff…09`, `Abheben` `0x42…16`  | Sofort, als echter CPI innerhalb des EVM-Schritts                                                     | **Nein** — ein Legacy-CPI in einer iterativen Transaktion scheitert mit `CpiProhibitedInIterativeTx` |
| **Cached** | `SplCached` `0xff…05`, `ASplCached` `0xff…06`, `SystemCached` `0xff…04`, `WithdrawCached` `0xff…0b` | In einem Overlay gestaged und am Ende der Transaktion als CPIs committed; ein EVM-Revert verwirft sie | Ja                                                                                                   |

Was das für eine Programmintegration bedeutet:

* **Das Aufrufen der Instruktionen Ihres eigenen Programms ist nur auf dem Legacy-Track möglich.** Der gecachte Track hat keinen generischen `invoke`; er deckt SPL-Übertragungen, ATA-Erstellung, Systemprogramm-Operationen und Gasabhebung ab.
* Daher muss der Schritt, der den Adapter finanziert, ebenfalls auf dem Legacy-Track sein, wenn er in derselben Transaktion wie der CPI läuft. Der gecachte Wrapper- `transferFrom` ist ein Aufruf auf dem gecachten Track, daher kann er sich keine Transaktion mit Ihrem CPI teilen (und wie Abschnitt 3 zeigt, würde er nur ein Escrow-Ledger gutschreiben). Ziehen Sie stattdessen mit `HelperProgram.transfer_spl(userAta, adapterAta, amount, mint)` stattdessen, nachdem der Nutzer den Adapter zum SPL-Delegate gemacht hat (`approve_spl`vom Nutzer direkt an das HelperProgram gesendet). Der Pull und der CPI sind beide Legacy-Track, daher laufen sie in einer Transaktion.
* **Halten Sie den Wrapper aus dieser Transaktion heraus.** Rufen Sie die mutierenden Funktionen des Wrappers nicht auf (`wUSDC.transfer`, `transferFrom`) vom Adapter aus in derselben Transaktion wie ein Legacy-CPI auf. Prüfen Sie `isEnabled(user)` und Salden vom Frontend mit `eth_call`.
* **Und die CPI-Transaktion muss atomar sein.** Das ist die Grenze, unter der das Design steht.

Lesezugriffe fallen nicht unter die Regel: `account_info`, `account_data_at`, `account_u64_at`, `account_lamports`, `pdas_batch_derive` und `HelperProgram.user_balance` / `allowance_of` sind Lesezugriffe auf vorab geladene Konten (kein CPI), auf beiden Tracks und in beiden Ausführungsmodi erlaubt. Legen Sie Ihre Vorprüfungen darauf. Eine Reihenfolgeregel: Sobald in einer Transaktion eine gecachte Mutation ausgelöst wurde, sind Legacy-Track-Lesungen für den Rest der Transaktion blockiert — erst lesen, dann mutieren.

## 5. Atomarität ist das Budget

Eine atomare Rome-Transaktion ist eine Solana-Transaktion: etwa **1,4 Mio. Compute Units**, eine **64-Adressen-** Konto-Liste und eine **4 KB** Übertragungsgröße (Solana-v1-Transaktionen). Das Rome-Programm und Ihr CPI teilen sich dieses Budget. Ein Wrapper-Transfer verbraucht auf Hadrian etwa 170K CU, und Ihre Instruktion addiert ihre eigenen; messen Sie mit dem Solana-Receipt `computeUnitsConsumed`, nicht EVM `gasUsed`.

Wenn die Emulation entscheidet, dass eine Transaktion nicht passt, führt Rome sie aus **iterativ** über mehrere Solana-Transaktionen hinweg aus, und ein darin enthaltener Legacy-CPI schlägt dann fehl. Beschränken Sie sowohl deposit als auch withdraw auf eine Tokenbewegung plus einen CPI in Ihr Programm, und halten Sie das umgebende Solidity klein. Teilen Sie alles Größere in zwei Nutzer-Transaktionen auf.

CPI-Tiefe: Solana erlaubt 4 Ebenen und Rome nutzt eine, also läuft Ihre Instruktion auf Ebene 2 mit zwei Ebenen darunter (Ihr Programm → SPL Token lässt eine übrig).

## 6. Kontenbereitstellung

Ihr Programm erhält seine Konten zur Laufzeit nicht aus Solidity. Der Proxy **emuliert** jede Transaktion, bevor sie gesendet wird, protokolliert jedes von den CPIs berührte Solana-Konto und platziert sie in der Solana-Transaktion. Ihr Adapter benennt die Konten in der `invoke_signed` Kontenliste; die Emulation beweist, dass sie aufgelöst werden. Daraus folgen drei Dinge:

* Jedes Konto, das Ihre Instruktion berührt, muss aus Eingaben ableitbar sein, die der Adapter hat (sein eigenes PDA und Sub-PDAs, Ihre Programm-ID, Mints, die Adresse des Nutzers). `SystemProgram.find_program_address` und `CpiProgram.pdas_batch_derive` führen die Ableitungen in Solidity durch.
* Ein Konto, das noch nicht existiert (eine neue Position, ein neues ATA), muss trotzdem in der Kontenliste genannt werden; seine Adresse ist das, was in der Solana-Transaktion ankommt.
* Alles, was die Transaktion berührt — einschließlich der eigenen Konten von Rome für den Nutzer, den Adapter und den Wrapper — muss unter 64 Adressen bleiben. Ein Design, das in einem Aufruf zehn Märkte berührt, passt nicht.

Für die Solana-Wallet-Lane `rome_emulateCallAccounts` stellt einem Client dieselbe Ermittlung bereit; das SDK verwendet sie.

Zwei Konten, die ein CPI niemals benennen darf: der Solana-Gebührenzahler, der die Transaktion einreicht (außer innerhalb einer einfachen System-Transfer-Instruktion), und das Rome-Programm selbst. Ein CPI zurück in das Rome-Programm wird abgelehnt.

## 7. Ein Deposit und ein Withdraw, in Solidity

Eine minimale Skizze eines Adapters auf dem Legacy-Track. Der Nutzer hat bereits `approve_spl(adapter, amount, mint)` an das HelperProgram gesendet (Abschnitt 3). `PRIVACY_PROGRAM` ist Ihre Programm-ID; `encodeDeposit` / `encodeWithdraw` erstellen Ihre Instruktionsdaten (Borsh, little-endian); die Account-Metas folgen dem Layout Ihrer Instruktion.

```solidity
import {ICrossProgramInvocation, IHelperProgram}
    from "@rome-protocol/rome-solidity/contracts/interface.sol";

contract PrivacyAdapter {
    ICrossProgramInvocation constant Cpi    = ICrossProgramInvocation(0xFF00000000000000000000000000000000000008);
    IHelperProgram          constant Helper = IHelperProgram(0xFF00000000000000000000000000000000000009);
    bytes32 constant SPL_TOKEN = 0x06ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a9; // TokenkegQ...

    bytes32 immutable PRIVACY_PROGRAM;
    bytes32 immutable USDC_MINT;

    constructor(bytes32 program, bytes32 mint) {
        PRIVACY_PROGRAM = program; USDC_MINT = mint;
        Helper.create_pda(address(this));                 // die eigene Solana-Identität des Adapters
        Helper.create_ata(address(this), USDC_MINT);      // und ein USDC-Konto, das er besitzt
    }

    function _salt(address user) internal pure returns (bytes32) { return bytes32(uint256(uint160(user))); }

    function deposit(uint64 amount) external {
        bytes32 self       = Helper.pda(address(this));                   // Signer: die Autorität des Adapters
        bytes32 adapterAta = Helper.ata(address(this), USDC_MINT);
        bytes32 position   = Helper.pda_with_salt(address(this), _salt(msg.sender)); // nutzerspezifisches Sub-PDA, signiert ebenfalls

        // Ziehen vom Token-Konto des Nutzers. Die Autorität des Adapters signiert als der SPL
        // Delegate, den der Nutzer gewährt hat; dies ist ein Aufruf auf dem Legacy-Track, wie der CPI unten.
        Helper.transfer_spl(Helper.ata(msg.sender, USDC_MINT), adapterAta, amount, USDC_MINT);

        ICrossProgramInvocation.AccountMeta[] memory accs = new ICrossProgramInvocation.AccountMeta[](5);
        accs[0] = ICrossProgramInvocation.AccountMeta(self,       true,  true);
        accs[1] = ICrossProgramInvocation.AccountMeta(position,   true,  true);
        accs[2] = ICrossProgramInvocation.AccountMeta(adapterAta, false, true);                          // Quelle: im Besitz der Autorität des Adapters
        accs[3] = ICrossProgramInvocation.AccountMeta(_vaultFor(position), false, true);                 // Tresor Ihres Programms
        accs[4] = ICrossProgramInvocation.AccountMeta(SPL_TOKEN, false, false);

        bytes32[] memory salts = new bytes32[](1);
        salts[0] = _salt(msg.sender);
        Cpi.invoke_signed(PRIVACY_PROGRAM, accs, encodeDeposit(amount), salts);
    }

    function withdraw(uint64 amount) external {
        bytes32 position = Helper.pda_with_salt(address(this), _salt(msg.sender));
        bytes32 userAta  = Helper.ata(msg.sender, USDC_MINT);            // Ziel: das eigene Konto des Nutzers
        // ... gleiche Form: Ihr Programm bewegt `amount` vom Tresor zu userAta, signiert von `position`
        Cpi.invoke_signed(PRIVACY_PROGRAM, accs, encodeWithdraw(amount), salts);
    }
}
```

Der Adapter ruft niemals das `wUSDC` -Vertrag auf. Der Pull ist ein HelperProgram-Aufruf, der die Delegation des Nutzers ausgibt, und die Token liegen in einem Token-Konto, das `external_auth(adapter)` besitzt, weshalb der CPI sie ausgeben kann.

Hinweise zum Signer. Rome signiert genau für zwei Arten von Konten: die bloße Autorität des aufrufenden Vertrags (`Helper.pda(address(this))`), die ohne Auflistung in `Salts`gesalzenes Sub-PDA pro Eintrag in `Salts`. Jeder von Ihnen aufgelistete Salt muss in der Kontenliste als Signer erscheinen, sonst schlägt der Aufruf fehl mit `Das aus dem bereitgestellten Seed berechnete Signer-Konto wurde nicht gefunden`. Jedes andere als Signer markierte Konto, einschließlich der eigenen Autorität des Nutzers, erhält von Rome keinen Seed, und die Solana-Laufzeit lehnt den CPI ab. Die Rechte des Nutzers an seinen Token kommen von der Delegation, niemals davon, dass der Adapter als Nutzer signiert.

Delegation ohne Pull: Wenn Ihr Programm direkt vom Token-Konto des Nutzers ausgeben soll, sendet der Nutzer `HelperProgram.approve_spl_raw_delegate(userAta, yourProgramPda, amount, mint, decimals)` direkt an das HelperProgram und benennt das PDA Ihres Programms als SPL-Delegate. Die Rome-Bridge nutzt dies für Wormhole. Die Ein-Delegate-Regel aus Abschnitt 3 gilt auch hier: Die Erlaubnis verdrängt die des Wrappers.

## 8. Testen

* Bereitstellen gegen **Hadrian** (Testnetz, Chain 200010) zuerst; Adressen und RPC stehen in [Contract Addresses](/de/referenz/contract-addresses.md) und die [Registry](https://github.com/rome-protocol/rome-registry).
* `eth_call` und `eth_estimateGas` führt dieselbe Emulation aus, die der Proxy zum Senden verwendet, sodass ein CPI, der dort scheitert, auf der Chain aus demselben Grund scheitert, und die Revert-Daten den zugrunde liegenden Solana-Fehler tragen (`Nicht-EVM-Aufruf-Fehler: …` im Solana-Programmlog).
* Lesen Sie die Solana-Seite einer bestätigten EVM-Transaktion mit `rome_solanaTxForEvmTx(hash)`; im Solana-Receipt stehen die CU und die Logs Ihres Programms.
* Ein CPI, der in `eth_call` funktioniert, aber beim Senden abgelehnt wird, hat meist eine von der Emulation gemeldete Grenze gerissen: die 64-Adressen-Grenze, das atomare CU-Budget (die Transaktion wurde iterativ) oder die Track-Regel.

## 9. Referenz

* [Precompile-Referenz](/de/referenz/precompiles.md) — jeder Selektor auf jedem Precompile, mit seiner Dispatch-Klasse und der Delegatecall-Regel.
* [Beschränkungen](/de/kernkonzepte/constraints.md) — die obigen Grenzen in einer Tabelle.
* [Token-Interop](/de/kernkonzepte/token-interop.md) — Wrapper, kanonische Mints, Deposit und Withdraw.
* Referenz-Apps in den öffentlichen Repos: [cardo](https://github.com/rome-protocol/cardo) (der Nutzer ruft das CPI-Precompile direkt auf, Form B), [rome-dex](https://github.com/rome-protocol/rome-dex) (direkt `invoke` plus ein verwahrerloser Router, Form C), und [aerarium](https://github.com/rome-protocol/aerarium) (eine Compound-III-Lending-App über einen gemeinsamen Markt; sie ruft keine Solana-Programme aus Solidity auf (kein CPI), also ist sie kein Beispiel für einen Adapter).


---

# 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/de/entwicklerleitfaden/integrate-solana-program.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.
