> ## Documentation Index
> Fetch the complete documentation index at: https://docs.raydium.io/llms.txt
> Use this file to discover all available pages before exploring further.

# PDAs und CPIs

> Program-derived addresses und cross-program invocation — die zwei Primitiven, die Raydium nutzt, um den Zustand deterministisch zu verwalten und mit anderen Programmen zu komponieren. Seeds, Bumps, invoke vs invoke_signed und die Muster, denen Raydiums Code folgt.

<Info>
  **Diese Seite wurde mit KI automatisch übersetzt. Maßgeblich ist stets die englische Version.**

  [Englische Version ansehen →](/solana-fundamentals/pdas-and-cpis)
</Info>

<Info>
  **PDAs** (program-derived addresses) und **CPIs** (cross-program invocation) sind die zwei Primitiven, die Raydium ermöglichen. PDAs lassen ein Programm deterministische Adressen „besitzen", ohne dass Privatschlüssel verwaltet werden — so funktionieren Pool-Authorities und Vaults. CPIs lassen ein Programm ein anderes aufrufen — so tauscht Raydium Token über das SPL Token Program aus und wie Integratoren Raydium in ihre eigenen Flows einbinden. Beide sind es wert, verstanden zu werden, bevor Sie Raydiums Source Code lesen.
</Info>

## PDAs: Adressen ohne Schlüssel

Eine **Program-Derived Address** ist ein Public Key, der:

* Nicht auf der ed25519-Kurve liegt (es existiert kein Privatschlüssel dafür).
* Deterministisch aus einer Program ID und einem Satz von Seeds abgeleitet wird.
* Nur vom ableitenden Programm signiert werden kann, über `invoke_signed`.

Jede Raydium Pool Authority, jedes Pool-State-Account, jeder Vault, jeder Farm-State — sie sind alle PDAs.

### Ableitung

Ein PDA wird berechnet, indem die Program ID mit den Seeds gehasht wird und dann ein „Bump"-Byte gefunden wird, das das Ergebnis von der Kurve drückt. Der erste Bump (typischerweise beginnend bei 255 und abnehmend), der eine off-curve Adresse produziert, gewinnt; dies ist der **kanonische Bump**.

```ts theme={null}
import { PublicKey } from "@solana/web3.js";

const [poolAuthority, bump] = PublicKey.findProgramAddressSync(
  [Buffer.from("authority"), poolId.toBuffer()],
  CPMM_PROGRAM_ID,
);
```

Die Seeds können alles sein — Strings, andere Public Keys, `u64`-Werte als Little-Endian Bytes. Raydiums Konvention ist ein lesbares Präfix gefolgt von eindeutigen Identifiern.

### Raydium PDA Muster

Häufige PDAs in Raydiums Programmen:

| PDA                      | Seeds                                     | Programm |
| ------------------------ | ----------------------------------------- | -------- |
| AMM Authority (AMM v4)   | `[b"amm authority"]` + bump               | AMM v4   |
| Pool State (CPMM)        | `[b"pool", amm_config, mint_a, mint_b]`   | CPMM     |
| Pool Vault (CPMM)        | `[b"pool_vault", pool, mint]`             | CPMM     |
| Authority (CPMM)         | `[b"vault_and_lp_mint_auth_seed"]`        | CPMM     |
| Pool State (CLMM)        | `[b"pool", amm_config, mint_0, mint_1]`   | CLMM     |
| Tick Array (CLMM)        | `[b"tick_array", pool, start_tick_index]` | CLMM     |
| Observation (CLMM)       | `[b"observation", pool]`                  | CLMM     |
| Personal Position (CLMM) | `[b"position", position_nft_mint]`        | CLMM     |
| Farm State (Farm v6)     | `[b"pool_farm_state", farm_id]`           | Farm v6  |
| User Ledger (Farm v6)    | `[b"user_ledger", farm, user]`            | Farm v6  |

Benutzer und Integratoren können diese berechnen, ohne etwas zu fetchen — anhand der öffentlichen Eingaben (Pool ID, Farm ID, Benutzer-Key) ist die PDA deterministisch.

### Kanonischer Bump

Obwohl es prinzipiell mehrere Bumps geben kann, die Off-Curve-Adressen erzeugen, verwenden Raydiums Programme immer den **kanonischen Bump** (gefunden durch Dekrementierung von 255). Dieser wird in den Account-Daten der PDA gespeichert, sodass nachfolgende Transaktionen ihn übergeben und die (teure) Ableitungsschleife überspringen können:

```rust theme={null}
#[account]
pub struct PoolState {
    pub bump: [u8; 1],
    // ... rest of pool state
}
```

Bei nachfolgenden Transaktionen wird der Bump aus dem Pool-State ausgelesen, anstatt neu berechnet zu werden.

## CPIs: Andere Programme aufrufen

**Cross-Program Invocation** lässt ein Programm ein anderes Programms Instruktionen inline innerhalb einer einzelnen Transaktion aufrufen. Raydium nutzt CPIs umfangreich:

* Swap-Instruktionen rufen SPL Token Program auf, um Token zu verschieben.
* CLMM ruft Metaplex auf, um die Position NFT zu prägen.
* Pool-Erstellung ruft System Program auf, um Accounts zuzuweisen.
* Farm v6 ruft SPL Token auf, um Rewards zu übertragen.

Integratoren nutzen CPIs auch, um *in* Raydium aufzurufen — so funktionieren Vault-Strategien, gehebelte LP-Protokolle und Auto-Compounder. Siehe [`integration-guides/cpi-integration`](/de/integration-guides/cpi-integration).

### invoke vs invoke\_signed

Die Solana Runtime bietet zwei CPI Primitiven:

* **`invoke`**: ruft ein anderes Programm auf; das aufgerufene Programm erbt die Unterzeichner der äußeren Transaktion.
* **`invoke_signed`**: ruft ein anderes Programm *im Namen einer PDA* auf; die Runtime verifiziert die PDA-Seeds und autorisiert die Signatur.

`invoke_signed` ist die Magie, die Programmen ermöglicht, die Autorität über Accounts zu halten, ohne Private Keys zu verwalten.

### Beispiel: Raydium überträgt aus einem Pool Vault

Ein Pool Vault ist ein Token Account, dessen Authority eine PDA des Pool Programs ist. Um Token während eines Swaps zu übertragen, muss das Pool Program als diese PDA unterzeichnen:

```rust theme={null}
// Seeds for the pool authority
let authority_seeds: &[&[u8]] = &[
    b"vault_and_lp_mint_auth_seed",
    &[authority_bump],
];
let signer_seeds: &[&[&[u8]]] = &[authority_seeds];

// Build CPI context
let cpi_accounts = Transfer {
    from:      input_vault.to_account_info(),
    to:        user_ata.to_account_info(),
    authority: pool_authority.to_account_info(),
};
let cpi_program = token_program.to_account_info();
let cpi_ctx     = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer_seeds);

// Execute
token::transfer(cpi_ctx, amount)?;
```

Die Runtime sieht, dass `invoke_signed` vom CPMM Programm aufgerufen wird, verifiziert, dass `vault_and_lp_mint_auth_seed + bump` zu `pool_authority`s Adresse gehasht wird, wenn es mit der CPMM Program ID gehasht wird, und erlaubt die Authority-Signatur auf dem Token-Transfer. Kein Privatschlüssel beteiligt.

### Beispiel: Integrator ruft Raydium CPMM auf

Ein Integrator-Programm (z. B. ein Escrow) kann Raydiums `swap_base_input` über CPI aufrufen:

```rust theme={null}
use raydium_cpmm::cpi::{self, accounts::Swap};

let cpi_accounts = Swap {
    payer:                order.to_account_info(),      // PDA, will sign
    authority:            pool_authority_info,
    amm_config:           amm_config_info,
    pool_state:           pool_info,
    input_token_account:  order_input_ata,
    output_token_account: order_output_ata,
    input_vault:          input_vault_info,
    output_vault:         output_vault_info,
    input_token_program:  input_token_program_info,
    output_token_program: output_token_program_info,
    input_token_mint:     input_mint_info,
    output_token_mint:    output_mint_info,
    observation_state:    observation_info,
};

let seeds = &[b"order", user.key.as_ref(), &[order.bump]];
let cpi_ctx = CpiContext::new_with_signer(
    cpmm_program.to_account_info(),
    cpi_accounts,
    &[seeds],
);

cpi::swap_base_input(cpi_ctx, amount_in, min_out)?;
```

Dies ist das kanonische Integrationsmuster — siehe [`integration-guides/cpi-integration`](/de/integration-guides/cpi-integration) für das vollständige Escrow-Beispiel.

## CPI Tiefenlimit

Solana begrenzt die CPI-Tiefe auf **4 Ebenen**. Eine Top-Level-Instruktion einer Transaktion zählt als Tiefe 0; jeder CPI-Aufruf erhöht die Tiefe.

Praktische Auswirkung: Raydiums eigener Swap nutzt bereits 1-2 Ebenen von CPIs (Raydium → SPL Token). Ein Integrator, der Raydium aufruft, nutzt 2. Wenn dieser Integrator von einem anderen Integrator aufgerufen wird, sind es 3. Die 4. Ebene ist das Limit.

Die meisten Kompositionsszenarien bleiben darunter problemlos, aber tiefe Verschachtelung (Aggregator → Router → Raydium → Hook) kann dieses Limit erreichen. Entwerfen Sie flach statt tief.

## Remaining Accounts

Wenn eine Raydium Instruktion eine variable Anzahl von Accounts benötigt (z. B. CLMM Swap, der eine unbekannte Anzahl von Tick Arrays kreuzt), werden die zusätzlichen Accounts als **remaining accounts** übergeben — angehängt an die Fest-Account-Liste, interpretiert nach Position.

CPMMs `SwapV2` nutzt remaining accounts für zusätzliche erforderliche Accounts von Transfer-Hook-Programmen. Clients fetchen die benötigten Accounts und hängen sie an:

```ts theme={null}
const swapIx = await raydium.cpmm.swap({
  /* ... */
  // SDK handles remaining accounts automatically
});
```

Auf CPI-Ebene müssen Integratoren remaining accounts durch ihre eigene Instruktion weitergeben:

```rust theme={null}
pub struct Swap<'info> {
    // ... fixed accounts
    // Plus Remaining accounts forwarded via ctx.remaining_accounts
}

// Forward remaining_accounts into the CPI
cpi::swap_base_input(
    cpi_ctx.with_remaining_accounts(ctx.remaining_accounts.to_vec()),
    amount_in,
    min_out,
)?;
```

## PDA Fallstricke

### Falsche Seeds → falsche Adresse

Ein Bug, bei dem Seeds in der falschen Reihenfolge, falscher Codierung sind oder ein zusätzliches Byte einschließen/ausschließen, erzeugt stillschweigend eine andere PDA. Die Transaktion schlägt mehrdeutig fehl (das Programm versucht, ein Account zu lesen, das nicht existiert). Testen Sie die Seed-Ableitung immer mit bekannten Referenzwerten.

### Bump nicht speichern

Wenn Sie den Bump bei jeder Transaktion neu ableiten, zahlen Sie Compute für die Ableitungsschleife. Speichern Sie den kanonischen Bump in den Daten der PDA und lesen Sie ihn von dort aus.

### Verwechslung zwischen kanonischem und nicht-kanonischem Bump

Nicht-kanonische Bumps (wenn jemand einen findet, der off-curve erzeugt) sind von `invoke_signed` erlaubt, aber von Raydiums Programmen durch `assert_eq!(bump, canonical_bump)` abgelehnt. Wenn jemand versucht, eine PDA mit einem nicht-kanonischen Bump zu beanspruchen, schlägt die Transaktion fehl.

### PDA als Unterzeichner übergeben, wenn Sie nicht das besitzende Programm sind

Nur das Programm, dessen ID in der PDA-Ableitung enthalten ist, kann `invoke_signed` mit seinen Seeds aufrufen. Wenn Sie es versuchen, lehnt die Runtime es ab.

## CPI Fallstricke

### Vergessen, `remaining_accounts` weiterzugeben

Wenn Ihre äußere Instruktion Transfer-Hook-Accounts in `remaining_accounts` übergibt, aber der CPI in Raydium sie nicht weitergibt, schlägt Raydium fehl, da es die Hook-Accounts nicht finden kann. Schließen Sie immer `with_remaining_accounts` in CPIs ein, die sie benötigen.

### Writable Flags stimmen nicht überein

Ein Account, den die äußere Instruktion als schreibbar kennzeichnet, muss auch in der CPI-Aufrufe schreibbar sein, wenn das aufgerufene Programm beabsichtigt, ihn zu schreiben. Nichtübereinstimmung → Runtime-Ablehnung.

### Keine Berücksichtigung der Rent

CPI zu einem Programm, das ein Account erstellt (z. B. ATA-Erstellung), erfordert, dass der Zahler genügend SOL für Rent hat. Fehlgeschlagene Rent-Prüfungen erscheinen als undurchsichtige Fehler.

## Praktisches Beispiel: Raydium CPMM PDAs berechnen

```ts theme={null}
import { PublicKey } from "@solana/web3.js";

const CPMM_PROGRAM_ID = new PublicKey("CPMMoo8L3F4NbTegBCKVNunggL7H1Zpdmwpwh8KMoZ0F");

function computeCpmmPdas(ammConfig, mintA, mintB) {
  const [poolState, poolBump] = PublicKey.findProgramAddressSync(
    [
      Buffer.from("pool"),
      ammConfig.toBuffer(),
      mintA.toBuffer(),
      mintB.toBuffer(),
    ],
    CPMM_PROGRAM_ID,
  );

  const [authority] = PublicKey.findProgramAddressSync(
    [Buffer.from("vault_and_lp_mint_auth_seed")],
    CPMM_PROGRAM_ID,
  );

  const [vaultA] = PublicKey.findProgramAddressSync(
    [Buffer.from("pool_vault"), poolState.toBuffer(), mintA.toBuffer()],
    CPMM_PROGRAM_ID,
  );

  const [vaultB] = PublicKey.findProgramAddressSync(
    [Buffer.from("pool_vault"), poolState.toBuffer(), mintB.toBuffer()],
    CPMM_PROGRAM_ID,
  );

  const [observation] = PublicKey.findProgramAddressSync(
    [Buffer.from("observation"), poolState.toBuffer()],
    CPMM_PROGRAM_ID,
  );

  return { poolState, authority, vaultA, vaultB, observation, poolBump };
}
```

Dies ist exakt das, was Raydium SDK unter der Haube tut, wenn Sie `getPoolInfoFromRpc({ poolId })` aufrufen — es leitet die zugehörigen PDAs ab, ohne einen Round-Trip zu benötigen.

## Verweise

* [`solana-fundamentals/account-model`](/de/solana-fundamentals/account-model) — wie PDAs in das Account-Modell passen.
* [`solana-fundamentals/programs-and-anchor`](/de/solana-fundamentals/programs-and-anchor) — Anchors Hilfsfunktionen zum Deklarieren von PDAs.
* [`integration-guides/cpi-integration`](/de/integration-guides/cpi-integration) — Erstellen von Integrationen, die CPIs in Raydium aufrufen.
* [`sdk-api/rust-cpi`](/de/sdk-api/rust-cpi) — Raydiums Rust CPI Typen.

Quellen:

* [Solana PDA Dokumentation](https://docs.solana.com/developing/programming-model/calling-between-programs).
* [Anchor PDA & CPI Dokumentation](https://www.anchor-lang.com/docs/pdas-and-cpis).
