Liquidar a negociação

SettleDvp é assinada pela autoridade de liquidação indicada no registro da negociação. Em uma única transação, ela move amount_a da perna do ativo para o destino da parte da perna em dinheiro e amount_b da perna em dinheiro para o destino da parte da perna do ativo, devolve qualquer excedente de cada escrow à parte da respectiva perna e fecha o registro e ambos os escrows. Se alguma dessas transferências não puder ser concluída, a instrução falha e nada é movimentado.

Esta página continua a partir de criar uma negociação e financiar as pernas; o cliente, os signatários, trade e as token accounts das partes são reaproveitados.

Revalide antes de assinar

A autoridade de liquidação assina a transação que move as duas pernas, por isso realiza a mesma leitura que cada parte antes de assinar: proprietário, tamanho, partes, mints, valores, expiração e ambos os destinos (consulte financiar as pernas). O prórpio programa revalida, na liquidação, que cada mint ainda pertence ao token program registrado na criação, não possui nenhuma extensão bloqueada e tem a mesma autoridade de mint de quando a negociação foi criada; assim, um mint recriado com uma extensão bloqueada, um token program diferente ou uma autoridade de mint diferente não pode ser liquidado.

Crie as token accounts de destino

Settle grava em quatro token accounts que o programa não cria:

ContaRecebeProprietário
userADestinationAtaBA perna em dinheiro (amount_b)user_a_settlement_destination
userBDestinationAtaAA perna do ativo (amount_a)user_b_settlement_destination
userAAtaAQualquer excedente da perna do ativouser_a
userBAtaBQualquer excedente da perna em dinheirouser_b

As contas de excedente só são usadas quando um escrow contém mais do que o valor acordado, mas qualquer pessoa que detenha tokens do mint pode criar um excedente enviando algumas unidades base ao escrow. Se a conta de reembolso não existir, todo o Settle falha; por isso, crie as quatro de forma idempotente na mesma transação.

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 informa ao programa quantas das contas anexadas após a lista fixa pertencem ao transfer hook da transferência da perna do ativo; as demais pertencem ao da perna em dinheiro. Para mints sem transfer hooks, passe zero e não anexe nada. O program account do Memo é exigido pela instrução e é usado apenas para destinos que exigem um 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 hooks

O IDL gerado lista apenas as contas fixas, portanto os builders gerados não conhecem as contas de hook. Resolva as extra account metas de cada mint com hook da mesma forma que faria em uma transferência direta desse token e, depois, anexe-as à instrução: primeiro as contas da perna do ativo, depois as da perna em dinheiro, com legAExtrasCount definido como o número do primeiro grupo. Em TypeScript, espalhe (spread) as metas resolvidas no array accounts da instrução; em Rust, use add_remaining_accounts no builder. O limite é de 32 contas por perna, incluindo o programa do hook e a conta de validação; o limite de contas da transação pode ser atingido primeiro quando ambas as pernas têm hooks. O programa remove o sinalizador de signatário de toda conta encaminhada, de modo que um hook não consegue obter a assinatura da autoridade de liquidação por esse caminho.

Registre o resultado

Considere a negociação liquidada quando a transação Settle atingir finalized. O registro e ambos os escrows deixam de existir após a liquidação, portanto um sistema que precise dos termos depois deve armazená-los a partir da leitura anterior à liquidação ou da transação de criação. O rent das contas fechadas foi para a autoridade de liquidação.

Erros de Settle

LegNotFunded significa que um escrow contém menos do que o valor acordado; DvpExpired e SettlementTooEarly significam que a janela de tempo foi perdida; MintAuthorityChanged e BlockedMintExtension significam que um mint mudou após a criação; RecipientAtaMismatch significa que uma token account de destino ou de reembolso está ausente ou não pertence mais ao proprietário e ao mint esperados, geralmente porque a etapa de criação de contas acima foi ignorada. Em todos os casos nada foi movimentado, e as partes podem desfazer a negociação, sujeitas às autoridades do mint (consulte desfazer uma negociação).

Próximos passos

Is this page helpful?

Índice

Editar Página
© 2026 Fundação Solana. Todos os direitos reservados.