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:
| Conta | Recebe | Proprietário |
|---|---|---|
userADestinationAtaB | A perna em dinheiro (amount_b) | user_a_settlement_destination |
userBDestinationAtaA | A perna do ativo (amount_a) | user_b_settlement_destination |
userAAtaA | Qualquer excedente da perna do ativo | user_a |
userBAtaB | Qualquer excedente da perna em dinheiro | user_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 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,}),);
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
- Desfazer uma negociação: recuperação das pernas financiadas quando uma negociação não é liquidada
Is this page helpful?