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 :
| Compte | Reçoit | Propriétaire |
|---|---|---|
userADestinationAtaB | La jambe cash (amount_b) | user_a_settlement_destination |
userBDestinationAtaA | La jambe d'actif (amount_a) | user_b_settlement_destination |
userAAtaA | Tout excédent sur la jambe d'actif | user_a |
userBAtaB | Tout excédent sur la jambe cash | user_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 userAconst destinationB = t.userBSettlementDestination; // defaults to userBconst [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
- Annuler un échange : récupérer les jambes financées lorsqu'un échange n'est pas réglé
Is this page helpful?