Den Trade abwickeln

SettleDvp wird von der im Trade-Datensatz genannten Abwicklungsstelle signiert. In einer Transaktion überträgt es amount_a des Asset-Legs an das Ziel der Cash-Leg-Partei und amount_b des Cash-Legs an das Ziel der Asset-Leg-Partei, gibt jeden Überschuss in einem der beiden Escrows an die Partei des jeweiligen Legs zurück und schließt den Datensatz sowie beide Escrows. Kann eine dieser Überweisungen nicht abgeschlossen werden, schlägt die Anweisung fehl und es wird nichts bewegt.

Diese Seite knüpft an Einen Trade erstellen und Die Legs finanzieren an; Client, Signer, trade und die token account der Parteien werden übernommen.

Vor dem Signieren erneut validieren

Die Abwicklungsstelle signiert die Transaktion, die beide Legs überträgt, und führt daher vor dem Signieren denselben Lesevorgang durch wie jede Partei: Eigentümer, Größe, Parteien, Mints, Beträge, Ablauf und beide Ziele (siehe Die Legs finanzieren). Das Programm selbst prüft bei der Abwicklung erneut, dass jeder Mint weiterhin zu dem bei der Erstellung erfassten Token Program gehört, keine gesperrte Erweiterung enthält und dieselbe Mint Authority hat wie bei der Erstellung des Trades. Ein Mint, der mit einer gesperrten Erweiterung, einem anderen Token Program oder einer anderen Mint Authority neu erstellt wurde, kann daher nicht abgewickelt werden.

Die Empfängerkonten erstellen

Settle schreibt in vier token account, die das Programm nicht erstellt:

KontoEmpfängtEigentümer
userADestinationAtaBDas Cash-Leg (amount_b)user_a_settlement_destination
userBDestinationAtaADas Asset-Leg (amount_a)user_b_settlement_destination
userAAtaAJeden Überschuss im Asset-Leguser_a
userBAtaBJeden Überschuss im Cash-Leguser_b

Die Überschusskonten werden nur verwendet, wenn ein Escrow mehr als den vereinbarten Betrag enthält. Allerdings kann jeder, der den Mint besitzt, einen Überschuss erzeugen, indem er ein paar Basis­ einheiten in den Escrow sendet. Existiert das Rückerstattungskonto nicht, schlägt der gesamte Settle fehl. Erstellen Sie daher alle vier idempotent in derselben Transaktion.

import {
findAssociatedTokenPda,
getCreateAssociatedTokenIdempotentInstruction,
} from "@solana-program/token-2022";
const destinationA = t.userASettlementDestination; // defaults to userA
const destinationB = t.userBSettlementDestination; // defaults to userB
const [userADestinationAtaB] = await findAssociatedTokenPda({
mint: mintB,
owner: destinationA,
tokenProgram: tokenProgramB,
});
const [userBDestinationAtaA] = await findAssociatedTokenPda({
mint: mintA,
owner: destinationB,
tokenProgram: tokenProgramA,
});
// userAAtaA and userBAtaB are from fund the legs.
const createRecipientAccounts = [
{ ata: userADestinationAtaB, owner: destinationA, mint: mintB, tokenProgram: tokenProgramB },
{ ata: userBDestinationAtaA, owner: destinationB, mint: mintA, tokenProgram: tokenProgramA },
{ ata: userAAtaA, owner: userA, mint: mintA, tokenProgram: tokenProgramA },
{ ata: userBAtaB, owner: userB, mint: mintB, tokenProgram: tokenProgramB },
].map(({ ata, owner, mint, tokenProgram }) =>
getCreateAssociatedTokenIdempotentInstruction({
payer: authoritySigner,
ata,
owner,
mint,
tokenProgram,
}),
);

Settle

legAExtrasCount teilt dem Programm mit, wie viele der nach der festen Liste angehängten Konten zum Transfer Hook des Asset-Legs gehören; der Rest gehört zu dem des Cash-Legs. Übergeben Sie für Mints ohne Transfer Hooks null und hängen Sie nichts an. Das Memo- program account ist für die Anweisung erforderlich und wird nur für Ziele verwendet, die ein Memo verlangen.

import { MEMO_PROGRAM_ADDRESS } from "@solana-program/memo";
import { getSettleDvpInstruction } from "./dvp";
const settleInstruction = getSettleDvpInstruction({
settlementAuthority: authoritySigner,
swapDvp,
mintA,
mintB,
dvpAtaA: escrowA,
dvpAtaB: escrowB,
userADestinationAtaB,
userBDestinationAtaA,
userAAtaA,
userBAtaB,
tokenProgramA,
tokenProgramB,
memoProgram: MEMO_PROGRAM_ADDRESS,
legAExtrasCount: 0,
});
const { context: settleContext } = await client.sendTransaction([
...createRecipientAccounts,
settleInstruction,
]);
const settleSignature = settleContext.signature;
console.log("Submitted:", settleSignature);
// sendTransaction returns at the client's commitment (confirmed by default).
// Wait for finalized before recording the trade as settled.
let finalized = false;
for (let attempt = 0; attempt < 30 && !finalized; attempt++) {
const {
value: [status],
} = await client.rpc.getSignatureStatuses([settleSignature]).send();
if (status?.err) {
throw new Error("Settle transaction failed");
}
finalized = status?.confirmationStatus === "finalized";
if (!finalized) {
await new Promise((resolve) => setTimeout(resolve, 2_000));
}
}
if (!finalized) {
// A closed record means it settled; an open one is safe to settle again.
throw new Error("Settle not finalized after 60s; read the trade record");
}
console.log("Settled:", settleSignature);

Transfer Hooks

Die generierte IDL listet nur die festen Konten auf, daher kennen die generierten Builder keine Hook-Konten. Lösen Sie die zusätzlichen Account-Metas jedes Hook-Mints auf die gleiche Weise auf wie bei einem direkten Transfer dieses Tokens und hängen Sie sie dann an die Anweisung an: zuerst die Konten des Asset-Legs, dann die des Cash-Legs, wobei legAExtrasCount auf die Anzahl in der ersten Gruppe gesetzt wird. In TypeScript fügen Sie die aufgelösten Metas per Spread in das accounts-Array der Anweisung ein; in Rust verwenden Sie add_remaining_accounts am Builder. Das Limit liegt bei 32 Konten pro Leg, einschließlich des Hook-Programms und des Validierungskontos; das Konten-Limit der Transaktion kann zuerst greifen, wenn beide Legs Hooks haben. Das Programm entfernt das Signer-Flag von jedem weitergeleiteten Konto, sodass ein Hook auf diesem Weg nicht die Signatur der Abwicklungsstelle erlangen kann.

Das Ergebnis erfassen

Betrachten Sie den Trade als abgewickelt, wenn die Settle-Transaktion finalized erreicht. Der Datensatz und beide Escrows existieren nach der Abwicklung nicht mehr. Ein System, das die Bedingungen später benötigt, speichert sie daher aus seinem Lesevorgang vor der Abwicklung oder aus der erstellenden Transaktion. Die rent der geschlossenen Konten ist an die Abwicklungsstelle gegangen.

Settle-Fehler

LegNotFunded bedeutet, dass ein Escrow weniger als den vereinbarten Betrag enthält; DvpExpired und SettlementTooEarly bedeuten, dass das Zeitfenster verpasst wurde; MintAuthorityChanged und BlockedMintExtension bedeuten, dass sich ein Mint nach der Erstellung geändert hat; RecipientAtaMismatch bedeutet, dass ein Ziel- oder Rückerstattungs-token account fehlt oder nicht mehr dem erwarteten Eigentümer und Mint gehört, meist weil der oben beschriebene Schritt zum Erstellen der Konten übersprungen wurde. In jedem Fall wurde nichts bewegt, und die Parteien können rückabwickeln, vorbehaltlich der Authorities des Mints (siehe Einen Trade rückabwickeln).

Nächste Schritte

Is this page helpful?

Inhaltsverzeichnis

Seite bearbeiten
© 2026 Solana Foundation. Alle Rechte vorbehalten.