Régler l'échange

SettleDvp est signée par l'autorité de règlement indiquée dans l'enregistrement de l'échange. En une seule transaction, elle transfère amount_a de la jambe d'actif vers la destination de la partie de la jambe cash et amount_b de la jambe cash vers la destination de la partie de la jambe d'actif, restitue tout excédent présent dans l'un ou l'autre séquestre à la partie de la jambe concernée, puis ferme l'enregistrement et les deux séquestres. Si l'un de ces transferts ne peut pas aboutir, l'instruction échoue et rien ne bouge.

Cette page fait suite à créer un échange et financer les jambes ; le client, les signataires, trade et les token accounts des parties sont repris tels quels.

Revalider avant de signer

L'autorité de règlement signe la transaction qui déplace les deux jambes ; elle effectue donc la même lecture que chaque partie avant de signer : propriétaire, taille, parties, mints, montants, expiration et les deux destinations (voir financer les jambes). Le programme revérifie lui-même au moment du règlement que chaque mint appartient toujours au token program enregistré à la création, ne comporte aucune extension bloquée et a la même autorité de mint que lors de la création de l'échange ; un mint recréé avec une extension bloquée, un autre token program ou une autre autorité de mint ne peut donc pas être réglé.

Créer les comptes destinataires

Settle écrit dans quatre token accounts que le programme ne crée pas :

CompteReçoitPropriétaire
userADestinationAtaBLa jambe cash (amount_b)user_a_settlement_destination
userBDestinationAtaALa jambe d'actif (amount_a)user_b_settlement_destination
userAAtaATout excédent sur la jambe d'actifuser_a
userBAtaBTout excédent sur la jambe cashuser_b

Les token accounts d'excédent ne servent que lorsqu'un séquestre contient plus que le montant convenu, mais toute personne détenant le mint peut créer un excédent en envoyant quelques unités de base dans le séquestre. Si le token account de remboursement n'existe pas, l'instruction Settle entière échoue ; créez donc les quatre de façon idempotente dans la même transaction.

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,
}),
);

Régler

legAExtrasCount indique au programme combien des comptes ajoutés après la liste fixe appartiennent au transfer hook de la jambe d'actif ; les autres relèvent de celui de la jambe cash. Pour les mints sans transfer hooks, passez zéro et n'ajoutez rien. Le program account du programme Memo est requis par l'instruction et n'est utilisé que pour les destinations qui exigent un mémo.

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

L'IDL généré ne liste que les comptes fixes ; les builders générés ne connaissent donc pas les comptes de hook. Résolvez les extra account metas de chaque mint à hook de la même manière que pour un transfert direct de ce token, puis ajoutez-les à l'instruction : d'abord les comptes de la jambe d'actif, puis ceux de la jambe cash, avec legAExtrasCount défini sur le nombre du premier groupe. En TypeScript, étalez (spread) les metas résolues dans le tableau accounts de l'instruction ; en Rust, utilisez add_remaining_accounts sur le builder. La limite est de 32 comptes par jambe, programme de hook et compte de validation compris ; la limite de comptes par transaction peut s'appliquer en premier lorsque les deux jambes comportent des hooks. Le programme retire l'indicateur de signataire de chaque compte transmis, de sorte qu'un hook ne peut pas obtenir la signature de l'autorité de règlement par ce biais.

Enregistrer le résultat

Considérez l'échange comme réglé lorsque la transaction Settle atteint finalized. L'enregistrement et les deux séquestres n'existent plus après le règlement ; un système qui a besoin des conditions par la suite doit donc les conserver à partir de sa lecture avant règlement ou de la transaction de création. Le rent des comptes fermés a été versé à l'autorité de règlement.

Erreurs de règlement

LegNotFunded signifie qu'un séquestre contient moins que son montant convenu ; DvpExpired et SettlementTooEarly signifient que la fenêtre de temps a été manquée ; MintAuthorityChanged et BlockedMintExtension signifient qu'un mint a changé après la création ; RecipientAtaMismatch signifie qu'un token account de destination ou de remboursement est absent ou n'appartient plus au propriétaire et au mint attendus, généralement parce que l'étape de création de compte ci-dessus a été ignorée. Dans tous les cas, rien n'a bougé, et les parties peuvent annuler l'échange, sous réserve des autorités du mint (voir annuler un échange).

Étapes suivantes

Is this page helpful?

Table des matières

Modifier la page
© 2026 Fondation Solana. Tous droits réservés.