Regolare lo scambio

SettleDvp è firmata dalla settlement authority indicata nel record dello scambio. In un'unica transazione sposta amount_a della leg dell'asset verso la destinazione della parte della leg in denaro e amount_b della leg in denaro verso la destinazione della parte della leg dell'asset, restituisce l'eventuale eccedenza di ciascun escrow alla parte della rispettiva leg e chiude il record e entrambi gli escrow. Se anche uno solo di questi trasferimenti non può essere completato, l'istruzione fallisce e nulla viene spostato.

Questa pagina prosegue da creare uno scambio e finanziare le leg; il client, i firmatari, trade e i token account delle parti restano gli stessi.

Rivalidare prima di firmare

La settlement authority firma la transazione che sposta entrambe le leg, quindi effettua la stessa lettura di ciascuna parte prima di firmare: proprietario, dimensione, parti, mint, importi, scadenza ed entrambe le destinazioni (vedi finanziare le leg). Il programma stesso ricontrolla al momento del regolamento che ogni mint appartenga ancora al token program registrato alla creazione, non abbia estensioni bloccate e abbia la stessa mint authority di quando lo scambio è stato creato, quindi un mint ricreato con un'estensione bloccata, un token program diverso o una mint authority diversa non può essere regolato.

Creare gli account destinatari

L'istruzione Settle scrive su quattro token account che il programma non crea:

AccountRiceveProprietario
userADestinationAtaBLa leg in denaro (amount_b)user_a_settlement_destination
userBDestinationAtaALa leg dell'asset (amount_a)user_b_settlement_destination
userAAtaAEventuale eccedenza sulla leg dell'assetuser_a
userBAtaBEventuale eccedenza sulla leg in denarouser_b

Gli account per le eccedenze vengono usati solo quando un escrow contiene più dell'importo concordato, ma chiunque possieda il mint può creare un'eccedenza inviando qualche unità base all'escrow. Se l'account di rimborso non esiste, l'intera istruzione Settle fallisce, quindi crea tutti e quattro gli account in modo idempotente nella stessa transazione.

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

Regolamento

legAExtrasCount indica al programma quanti degli account aggiunti dopo l'elenco fisso appartengono al transfer hook della leg dell'asset; i restanti vanno a quello della leg in denaro. Per i mint senza transfer hook, passa zero e non aggiungere nulla. Il memo program account è richiesto dall'istruzione e viene usato solo per le destinazioni che richiedono un memo.

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 hook

L'IDL generato elenca solo gli account fissi, quindi i builder generati non conoscono gli account dei hook. Risolvi i meta degli account aggiuntivi di ciascun mint con hook nello stesso modo in cui lo faresti per un trasferimento diretto di quel token, quindi aggiungili all'istruzione: prima gli account della leg dell'asset, poi quelli della leg in denaro, con legAExtrasCount impostato sul numero del primo gruppo. In TypeScript, espandi i meta risolti nell'array accounts dell'istruzione; in Rust, usa add_remaining_accounts sul builder. Il limite è di 32 account per leg, compresi il programma hook e l'account di validazione; il limite di account della transazione può essere raggiunto per primo quando entrambe le leg hanno hook. Il programma rimuove il flag di firmatario di ogni account inoltrato, quindi un hook non può ottenere la firma della settlement authority attraverso questo percorso.

Registrare il risultato

Considera lo scambio regolato quando la transazione Settle raggiunge lo stato finalized. Il record e entrambi gli escrow non esistono più dopo il regolamento, quindi un sistema che abbia bisogno dei termini in seguito li memorizza dalla lettura precedente al regolamento o dalla transazione di creazione. La rent degli account chiusi è stata trasferita alla settlement authority.

Errori di regolamento

LegNotFunded significa che un escrow contiene meno dell'importo concordato; DvpExpired e SettlementTooEarly significano che la finestra temporale è stata mancata; MintAuthorityChanged e BlockedMintExtension significano che un mint è cambiato dopo la creazione; RecipientAtaMismatch significa che un token account di destinazione o di rimborso manca oppure non appartiene più al proprietario e al mint previsti, di solito perché il passo di creazione degli account indicato sopra è stato saltato. In ogni caso nulla è stato spostato e le parti possono annullare lo scambio, nei limiti delle authority del mint (vedi annullare uno scambio).

Passi successivi

Is this page helpful?

Indice dei contenuti

Modifica pagina
© 2026 Solana Foundation. Tutti i diritti riservati.