Régler l'opération

SettleDvp est signée par l'autorité de règlement indiquée dans l'enregistrement de l'opération. En une seule transaction, l'instruction transfère amount_a du volet des actifs vers la destination de la partie du volet espèces et amount_b du volet espèces vers la destination de la partie du volet des actifs, restitue tout excédent présent dans l'un ou l'autre escrow à la partie du volet concerné, puis ferme l'enregistrement et les deux escrows. Si l'un de ces transferts ne peut pas aboutir, l'instruction échoue et rien ne bouge.

Cette page fait suite à créer une opération et financer les volets ; 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 volets ; 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 volets). 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 possède la même autorité de mint qu'à la création de l'opération ; 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
userADestinationAtaBLe volet espèces (amount_b)user_a_settlement_destination
userBDestinationAtaALe volet des actifs (amount_a)user_b_settlement_destination
userAAtaATout excédent du volet des actifsuser_a
userBAtaBTout excédent du volet espècesuser_b

Les comptes d'excédent ne sont utilisés que lorsqu'un escrow 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 l'escrow. Si le token account de remboursement n'existe pas, l'ensemble de Settle é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 de comptes ajoutés après la liste fixe appartiennent au transfer hook du volet des actifs ; les autres reviennent à celui du volet espèces. Pour les mints sans transfer hook, 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 répertorie que les comptes fixes ; les builders générés ne connaissent donc pas les comptes de hook. Résolvez les account metas supplémentaires de chaque mint avec hook comme vous le feriez pour un transfert direct de ce token, puis ajoutez-les à l'instruction : d'abord les comptes du volet des actifs, puis ceux du volet espèces, avec legAExtrasCount défini sur le nombre de comptes du premier groupe. En TypeScript, étalez les metas résolus dans le tableau accounts de l'instruction ; en Rust, utilisez add_remaining_accounts sur le builder. La limite est de 32 comptes par volet, y compris le programme de hook et le compte de validation ; la limite de comptes par transaction peut être atteinte en premier lorsque les deux volets 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'opération comme réglée lorsque la transaction Settle atteint l'état finalized. L'enregistrement et les deux escrows 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 escrow contient moins que le 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é omise. Dans tous les cas, rien n'a bougé, et les parties peuvent annuler l'opération, sous réserve des autorités du mint (voir annuler une opération).

Étapes suivantes

Is this page helpful?

Table des matières

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