Programm für Lieferung gegen Zahlung (Delivery vs Payment, DvP)

Das DvP-Programm ist das Standardprogramm für Lieferung gegen Zahlung (Delivery versus Payment) auf Solana. Es wird von der Solana Foundation gepflegt und von Cantina auditiert. Zwei Parteien einigen sich auf einen Trade, jede sendet ihr Leg per gewöhnlicher Token-Überweisung an ein Escrow-Konto, und eine Settlement-Authority wickelt beide Legs in einer Transaktion ab, sodass entweder beide ausgeführt werden oder keines. Die Settlement-Authority ist eine dritte Adresse, die im Trade benannt wird, etwa die Handelsplattform oder der Agent, der ihn vermittelt hat. Keine der Parteien schreibt oder deployt ein Programm, und ein Wallet oder Custodian, das bzw. der eine standardmäßige Token-Überweisung (TransferChecked) senden kann, kann ein Leg ohne DvP-spezifische Integration finanzieren. Bis zur Abwicklung kann jede Partei ihr eigenes Leg zurückholen oder den Trade rückabwickeln.

Den Datensatz prüfen, bevor man darauf reagiert

Das Erstellen eines Trade-Datensatzes ist permissionless, daher ist ein Datensatz kein Beweis für eine Einigung. Jede Partei liest die gespeicherten Konditionen, bevor sie finanziert, und die Settlement-Authority vor der Abwicklung (siehe Verifizierung).

So funktioniert es

Jeder Trade erhält einen programmeigenen Datensatz und zwei Escrow-Konten, eines pro Leg. Die Partei des Asset-Legs (user_a) und die Partei des Cash-Legs (user_b) finanzieren jeweils ein Leg, indem sie Token auf das Escrow-Konto dieses Legs überweisen. Die Settlement-Authority ist der einzige Signer, der abwickeln kann. Die Abwicklung überweist den vereinbarten Betrag jedes Legs an die Gegenseite, gibt einen etwaigen Überschuss an die Partei zurück, der das Leg gehört, und schließt den Datensatz sowie beide Escrows. Die Atomarität ergibt sich aus Solana- Transaktionen: Beide Überweisungen sind in einer Transaktion enthalten, sodass entweder beide wirksam werden oder keine.

Jede Rückerstattung, Rückforderung und Rückgabe von Überschüssen geht an das eigene associated token account der Partei: das Konto von user_a für mint_a, das von user_b für mint_b. Ein Custodian oder Treasury-System kann ein Leg von seinem eigenen Konto aus finanzieren, aber alles Zurückgegebene landet im Konto der Partei, nicht beim Custodian. Jeder Trade hinterlässt außerdem ein dauerhaftes Marker-Konto (siehe State).

Anleitungen

Die Schrittseiten führen einen Trade von der Erstellung bis zur Abwicklung, mit TypeScript und Rust für jede Anweisung.

Eine Devnet-Demo führt einen Trade von Anfang bis Ende aus: Sie erstellt den Trade, finanziert beide Escrows mit standardmäßigen Token-Überweisungen und wickelt beide Legs in einer Transaktion ab.

Zwischen Programm und Delegation wählen

Lieferung gegen Zahlung (DvP) per Delegation wickelt denselben Austausch ohne Programm ab: Jede Seite delegiert ihre Position an einen Settlement-Agent, der beide Legs in einer Transaktion bewegt.

  • Das Programm benötigt keine Delegation, sodass die Beschränkung auf einen Delegate pro Token account nicht greift und die Settlement-Authority Erlöse nicht umleiten kann. Jeder Trade hängt von einem upgradefähigen Programm und dessen Upgrade-Authority ab.
  • Das Delegationsmuster hat keine Programmabhängigkeit und funktioniert mit Mints, die Scaled UI Amount verwenden. Das Programm lehnt diese Mints ab, wodurch jeder Mint, der aus Mosaics tokenized-security-Vorlage erstellt wurde, ausgeschlossen ist (siehe Einschränkungen der Mint-Konfiguration).

Rollen

Das Programm ist symmetrisch. In seiner Schnittstelle sind die beiden Parteien user_a, der mint_a liefert, und user_b, der mint_b liefert. Die README und die generierten Clients nennen mint_a das Asset-Leg und mint_b das Cash-Leg, und diese Seiten folgen dieser Konvention, aber nichts im Programm verlangt, dass das Cash-Leg ein Cash-Instrument sein muss. Zwei Assets oder zwei Stablecoins werden auf dieselbe Weise abgewickelt.

RolleWerSigniertHinweise
Partei des Asset-Legsuser_a, liefert mint_aDie eigene Finanzierungsüberweisung; Reclaim, Reject, RecoverEin keypair-Wallet oder ein PDA-basiertes Smart Wallet wie ein Multisig-Vault. Ein SPL-Token-Multisig-Konto oder ein program account kann keine Partei sein: Create schlägt mit PartyNotSignerCapable fehl
Partei des Cash-Legsuser_b, liefert mint_bDie eigene Finanzierungsüberweisung; Reclaim, Reject, RecoverGleiche Anforderung
Settlement-AuthorityEine dritte Adresse, die bei der Erstellung benannt wirdSettle, CancelDer einzige Signer, der abwickeln kann. Sie kann Erlöse nicht umleiten, da diese bei der Erstellung festgelegt werden. Sie erhält den Rent der geschlossenen Konten, wenn sie abwickelt oder storniert
Rent-ZahlerWer CreateDvp signiertCreateFinanziert den Rent (die SOL-Einlage, die ein Konto offen hält) für den Datensatz, den Nonce-Tombstone und beide Escrows. Wird nicht als Rent-Empfänger erfasst: Der Rent geschlossener Konten geht an den, der den Trade schließt

Program ID

dvp34bdbcEm4f4FCUjGV4mDAkDshaQR4LkK8fdcsyZq
ClusterProgrammUpgrade-AuthorityZuletzt deployter slot
mainnet-betadvp34bdbcEm4f4FCUjGV4mDAkDshaQR4LkK8fdcsyZqDXtFpbPjcn2hxPnw79x1Pfoj35vXh5AsWBkS37YnXMVv452,058,374
devnetdvp34bdbcEm4f4FCUjGV4mDAkDshaQR4LkK8fdcsyZq5EX1zFeJWj4rGYZv432WnYw8CbaSUeTdxPx4r573UgYU479,376,863

Das Programm ist auf beiden Clustern upgradefähig. Die obigen Angaben entsprechen dem, was solana program show am 2. Oktober 2026 ausgegeben hat; führe denselben Befehl aus, um aktuelle Werte zu erhalten. Der Devnet-Build ist älter als der Commit, auf den die Schrittseiten ausgelegt sind (siehe Installation), mit demselben Anweisungs- und Fehlersatz.

Lebenszyklus

Ein Trade ist ab CreateDvp offen, bis einer von Settle, Cancel oder Reject ihn schließt. Die Finanzierung hat keine eigene Anweisung: Ein Leg gilt als finanziert, wenn sein Escrow- Konto mindestens den vereinbarten Betrag enthält. Reclaim erfolgt pro Leg und lässt den Trade offen, sodass ein Problem mit einem Leg die andere Partei nicht zwingt, den gesamten Trade zu stornieren.

State

Jeder Trade hat ein SwapDvp-Konto mit 458 Bytes, das dem Programm gehört. Seine Adresse wird aus der Settlement-Authority, den beiden Parteien, den beiden Mints und einem Nonce abgeleitet (die Seeds ["dvp", settlement_authority, user_a, user_b, mint_a, mint_b, nonce]). Die Beträge und Zeiten werden im Konto gespeichert und beeinflussen die Adresse nicht, weshalb sie gelesen statt abgeleitet werden müssen. Ein kleines Marker-Konto, der Nonce-Tombstone unter ["nonce", swap_dvp], wird mit dem Trade erstellt und nie geschlossen, sodass jede Kombination aus Parteien, Mints, Authority und Nonce nur für einen Trade verwendet werden kann.

FeldTypBedeutung
bumpu8PDA-Bump
user_a, user_bPubkeyDie beiden Parteien
mint_a, mint_bPubkeyDie Mints des Asset-Legs und des Cash-Legs
settlement_authorityPubkeyDer einzige Signer, der abwickeln oder stornieren kann
token_program_a, token_program_bPubkeyDas Token Program, dem der jeweilige Mint gehört; ein Trade kann SPL-Token- und Token-2022-Legs mischen
amount_a, amount_bu64Der vereinbarte Betrag jedes Legs in der kleinsten Einheit des Mints (Basiseinheiten)
expiry_timestampi64Nach dieser Netzwerkzeit (der Onchain-Uhr) wird Settle abgelehnt. Reclaim, Cancel und Reject funktionieren weiterhin
nonceu64Unterscheidet Trades, die die übrigen Seeds gemeinsam haben. Einmalig verwendbar
ref_string[u8; 64]Eine opake Client-Referenz, mit Nullen aufgefüllt; das Programm liest sie nie
user_a_settlement_destinationPubkeyErhält das Cash-Leg bei Settle; Standardwert ist user_a
user_b_settlement_destinationPubkeyErhält das Asset-Leg bei Settle; Standardwert ist user_b
mint_a_authority, mint_b_authorityPubkeyDie bei der Erstellung erfassten Mint-Authorities; Settle schlägt fehl, wenn sich eine davon geändert hat
earliest_settlement_timestampOption<i64>Falls gesetzt, wird Settle vor dieser Netzwerkzeit abgelehnt

Die Feldliste stammt aus dem IDL des Programms beim Commit, der unter Einen Trade erstellen genannt wird.

Anweisungen

AnweisungSignerWirkung
CreateDvpBeliebiger ZahlerErstellt den SwapDvp-Datensatz, seinen Nonce-Tombstone und beide Escrow-Konten. Bewegt keine Token
ReclaimDvpuser_a oder user_bGibt das eigene Leg des Signers aus dem Escrow zurück. Der Trade bleibt offen und das Leg kann erneut finanziert werden
SettleDvpSettlement-AuthorityÜberweist amount_a an das Ziel von user_b und amount_b an das Ziel von user_a, gibt einen etwaigen Überschuss an die Partei des Legs zurück, schließt den Datensatz und beide Escrows
CancelDvpSettlement-AuthorityErstattet alles, was Escrow A hält, an user_a und Escrow B an user_b, schließt den Datensatz und beide Escrows
RejectDvpuser_a oder user_bGleiche Wirkung wie Cancel, von einer Partei signiert
RecoverDvpuser_a oder user_bNachdem der Trade geschlossen wurde: leert ein neu erstelltes Escrow in das eigene token account des Signers und schließt es. Deckt eine Einzahlung ab, die nach Settle, Cancel oder Reject eingegangen ist

Jede Rückerstattung geht an das eigene associated token account der Partei für den Mint dieses Legs, unabhängig davon, wer das Escrow finanziert hat.

Die Konten und Argumente jeder Anweisung finden sich auf der Seite des jeweiligen Schritts.

Fehler

CodeFehlerWann
0SignerNotPartyReclaim, Reject oder Recover von einer Adresse signiert, die nicht user_a oder user_b ist
1DvpExpiredSettle nach expiry_timestamp
2SettlementAuthorityMismatchSettle oder Cancel von einer anderen Adresse als der Settlement-Authority signiert
3SettlementTooEarlySettle vor earliest_settlement_timestamp
4LegNotFundedSettle, während ein Escrow weniger als seinen vereinbarten Betrag hält
5ExpiryNotInFutureCreate mit einem Ablaufzeitpunkt zur oder vor der aktuellen Netzwerkzeit
6EarliestAfterExpiryCreate mit einem frühestmöglichen Abwicklungszeitpunkt nach dem Ablauf
7SelfDvpCreate mit user_a gleich user_b
8SameMintCreate mit mint_a gleich mint_b
9ZeroAmountCreate mit einem Betrag von null bei einem der Legs
10BlockedMintExtensionCreate oder Settle mit einem Mint, der TransferFee, InterestBearing, ScaledUiAmount oder NonTransferable verwendet
11SettlementAuthorityIsPartyCreate mit einer Settlement-Authority, die einer Partei entspricht
12SettlementAuthorityExecutableCreate mit einer ausführbaren Settlement-Authority
13NonceAlreadyUsedCreate mit Wiederverwendung eines (seeds, nonce), für das bereits ein Tombstone existiert
14ExpiryTooFarInFutureCreate mit einem Ablaufzeitpunkt, der mehr als ein Jahr in der Zukunft liegt
15RefStringTooLongCreate mit einer Referenz, die länger als 64 Bytes ist
16DvpStillOpenRecover, solange der Trade noch offen ist; stattdessen Reclaim verwenden
17DvpNeverCreatedRecover mit Seeds ohne Tombstone
18EscrowPreloadedWithLamportsCreate, wenn ein nicht-nativer Escrow lamports oberhalb seines rent-exempt-Minimums hält
19SettlementDestinationIsSwapDvpCreate mit einem Settlement-Ziel, das dem Trade-Record entspricht
20SwapDvpPreloadedWithLamportsCreate, wenn die Record-Adresse lamports oberhalb ihrer rent-Reserve hält
21RecipientAtaMismatchEin token account für Empfänger oder Rückerstattung gehört nicht mehr zur erwarteten Wallet und zum erwarteten Mint
22PartyNotSignerCapableCreate mit einer Partei, die kein systemeigenes, nicht ausführbares Konto ist
23MintAuthorityChangedSettle, wenn sich die Mint Authority eines Legs von dem bei der Erstellung erfassten Wert unterscheidet

Events und Indexierung

Das Programm gibt keine Onchain-Events aus. Ein Indexer oder Abgleichssystem liest den Zustand des SwapDvp-Kontos und die Token-Guthaben der beiden Escrows und nutzt ref_string, um einen Trade der zugehörigen Offchain-Order zuzuordnen. Da der Record bei der Abwicklung geschlossen wird, speichert ein System, das die Konditionen im Nachhinein benötigt, diese beim Auslesen oder rekonstruiert sie aus der Transaktion, die den Trade erstellt hat.

Unterstützte Tokens

Ein Trade kann ein SPL-Token-Leg mit einem Token-2022-Leg kombinieren; jedes Leg hat sein eigenes Token Program. Wrapped-SOL-Legs werden unterstützt.

Token-2022-Extension auf dem Mint eines LegsVerhalten des Programms
TransferFee, InterestBearing, ScaledUiAmount, NonTransferableWird bei Create und Settle mit BlockedMintExtension abgelehnt
PermanentDelegate, Pausable, DefaultAccountState, eine Freeze Authority, MintCloseAuthorityAkzeptiert; jede Authority kann auf die im Escrow gehaltenen Tokens zugreifen
ConfidentialTransferAkzeptiert; die Beträge auf diesem Leg sind öffentlich
TransferHookUnterstützt, bis zu 32 zusätzliche Konten pro Leg
MemoTransfer auf einem Ziel- oder RückerstattungskontoAkzeptiert; das Programm gibt das erforderliche Memo vor dem Transfer aus

Eine Transfer-Fee würde dazu führen, dass die empfangende Seite weniger als den vereinbarten Betrag erhält. Interest Bearing und Scaled UI Amount verändern die Rohbeträge nicht, doch ihr Zinssatz bzw. Multiplikator kann sich zwischen dem Erstellen und dem Abwickeln eines Trades ändern, wodurch sich die Anzeige der vereinbarten Beträge ändert. NonTransferable würde beide Legs blockieren.

Die akzeptierten Authorities können die im Escrow gehaltenen Tokens während der gesamten Laufzeit des Trades bewegen, einfrieren oder anhalten (siehe unten). Der Escrow kann nicht für den Empfang vertraulicher Transfers konfiguriert werden, daher sind die im Escrow gehaltenen und die abgewickelten Beträge auf einem vertraulichen Leg öffentlich.

Bei einem Mint mit Hook ermittelt der Client die zusätzlichen Konten des Hooks und hängt sie an Settle, Cancel, Reject, Reclaim und Recover an. Das Limit von 32 pro Leg umfasst das Hook-Programm und sein Validierungskonto. Wenn die Hook Authority die Kontenliste des Hooks über dieses Limit hinaus erweitert, schlägt jeder Transfer auf diesem Leg fehl, einschließlich Reclaim, Cancel, Reject und Recover, bis die Authority dies rückgängig macht. Das Kontolimit der Transaktion kann bereits vor dem Limit pro Leg greifen, wenn beide Legs Hooks haben.

Verlangt ein Ziel- oder Rückerstattungskonto Memos, ruft das Programm vor dem Transfer das Memo-Programm auf. Deshalb benötigt jede Anweisung, die Tokens aus dem Escrow bewegt, ein memo_program-Konto.

Einschränkungen bei der Mint-Konfiguration

Scaled UI Amount. Das tokenized-security-Template in Mosaic, dem Issuance-Toolkit der Solana Foundation, fügt immer Scaled UI Amount hinzu. Das Programm akzeptiert daher keinen aus diesem Template erstellten Mint als Leg. Ein für dieses Programm vorgesehener Mint kann ohne die Extension erstellt werden (siehe Token Extensions); DvP mit Delegation wird über delegierte Authority abgewickelt und wendet diese Prüfung nicht an.

Standardmäßig eingefrorene Mints. Der Escrow jedes Trades ist ein associated token account, der einer Program Derived Address gehört, die für diesen Trade neu ist. Bei einem Mint, dessen Default Account State auf „frozen“ gesetzt ist, wird der Escrow eingefroren erstellt, und ein Funding-Transfer dorthin schlägt fehl, bis er wieder freigegeben wird. Unter Token ACL bedeutet das, dass der Emittent oder sein Transfer Agent den Escrow des Trades vor dem Funding freigibt: entweder indem die Adresse des Trade-Records (der Eigentümer des Escrows) zur Allowlist hinzugefügt wird, sodass jeder den Escrow freigeben kann, sofern die Token-ACL-Konfiguration des Mints Permissionless Thaw aktiviert, oder indem die Freeze Authority ihn direkt freigibt. Ein eingefrorener Escrow blockiert auf diesem Leg auch Settle, Reclaim, Cancel, Reject und Recover. Die Freeze Authority, und bei einem Mint mit Hook die Hook Authority, ist somit für die Laufzeit des Trades eine vertrauenswürdige Partei. Der Pfad mit eingefrorenem Escrow wird hier beschrieben und in den Devnet-Snippets nicht ausgeführt.

Einschränkungen

  • Kein Matching, keine Preisfindung und kein Orderbuch. Die Parteien vereinbaren die Konditionen außerhalb des Ledgers, und eine von ihnen zeichnet sie auf.
  • Kein Netting und nur bilateral: Ein Record ist ein Trade zwischen zwei Parteien.
  • Keine Teilausführungen. Settle bewegt genau den vereinbarten Betrag jedes Legs und gibt einen etwaigen Überschuss an die Partei des Legs zurück.
  • Kein Off-Ledger-Leg. Beide Legs sind token accounts auf Solana; ein Cash-Leg über bestehende Rails wird separat abgewickelt und abgeglichen.
  • Keine Berechtigungsprüfung, keine Allowlist und kein KYC. Die eigenen Kontrollen des Mints erzwingen die Berechtigung der Inhaber.
  • Keine automatische Abwicklung. Die Settlement Authority muss signieren.
  • Keine Events und kein Protokoll-Fee über Transaktions-Fees und rent hinaus.

Das Programm beseitigt das Principal-Risiko zwischen den beiden Parteien: Kein Leg wird bewegt, wenn nicht beide bewegt werden. Es beseitigt nicht das Emittenten- Kredit- oder Rücknahmerisiko bei einem der Tokens, die Möglichkeit der Freeze-, Pause- oder Permanent-Delegate-Authority eines Mints, auf im Escrow gehaltene Tokens zuzugreifen (siehe unterstützte Tokens), oder die Abhängigkeit davon, dass die Settlement Authority zum Signieren verfügbar ist.

Überprüfung vor Funding und Abwicklung

CreateDvp ist permissionless: Nur der Payer signiert, die Parteien und die Settlement Authority nicht. Jeder kann daher einen Record erstellen, der beliebige Parteien und beliebige Konditionen benennt. Lies vor dem Funding und erneut vor der Abwicklung das SwapDvp-Konto aus und bestätige:

  1. Das Konto gehört dem DvP-Programm und ist genau 458 Bytes groß. Ein Konto, das nicht dem Programm gehört, kann mit Bytes gefüllt sein, die sich wie ein echter Record dekodieren lassen.
  2. user_a, user_b, mint_a, mint_b und settlement_authority entsprechen dem vereinbarten Trade.
  3. amount_a, amount_b, expiry_timestamp und earliest_settlement_timestamp entsprechen den vereinbarten Konditionen.
  4. Beide Settlement-Ziele sind die vereinbarten Adressen. Ein gefälschter Record kann Erlöse umleiten.

Legs finanzieren listet die geprüften Reader der generierten Clients auf und zeigt die Prüfung im Code.

Betrachte einen Trade als abgewickelt, sobald die Settle-Transaktion finalized erreicht. Das ist ein Commitment-Level des Netzwerks; ob es auch die Settlement Finality im rechtlichen Sinne markiert, hängt von den Vereinbarungen der Parteien und den geltenden Regelwerken ab.

Versionierung

Das SwapDvp-Konto enthält kein Versionsfeld. Sein Layout ist auf 458 Bytes festgelegt (SwapDvp::LEN), und die geprüften Reader lehnen ein Konto mit anderer Länge ab. Die IDL trägt ihre eigene Version, 0.1.0 Stand 2. Oktober 2026.

Das Programm ist auf beiden Clustern upgradefähig (siehe Program ID). Ein Trade-Record existiert nur, bis er abgewickelt, storniert oder abgelehnt wird. Prüfe daher im Repository, wie ein Upgrade offene Trades behandelt, und generiere Clients aus der IDL des bereitgestellten Builds neu.

Repository und Audit

Quellcode, IDL, generierte Clients und Tests befinden sich in solana-foundation/dvp unter der MIT-Lizenz. Das Programm ist ein no_std-Pinocchio-Programm; die Clients werden mit Codama aus der IDL generiert. Meldungen zu Schwachstellen laufen über den Security-Advisory-Link des Repositorys, gemäß dessen SECURITY.md.

Das Programm wurde von Cantina auditiert.

Verwandte Themen

Is this page helpful?

© 2026 Solana Foundation. Alle Rechte vorbehalten.