Desfazer uma negociação

Quatro instruções devolvem fundos sem liquidar: Reclaim, Cancel e Reject enquanto a negociação está aberta, e Recover para um depósito que chega depois de ela ter sido encerrada. Nenhuma delas verifica a expiração. Um escrow congelado ou um mint alterado ainda pode reter uma perna, como descrevem as notas no final.

InstruçãoAssinanteEscopoNegociação depois
ReclaimDvpuser_a ou user_bA perna do próprio assinanteContinua aberta
CancelDvpAutoridade de liquidaçãoAmbas as pernasEncerrada
RejectDvpuser_a ou user_bAmbas as pernasEncerrada
RecoverDvpuser_a ou user_bA perna do assinante, após o encerramentoJá encerrada

Em cada caso, o escrow é esvaziado de tudo o que contém em favor da parte da perna: o escrow A para a token account de user_a para mint_a, o escrow B para a de user_b para mint_b, independentemente de quem o financiou. A program account do memo é fornecida para contas de reembolso que exigem um memo.

Esta página continua a partir das etapas anteriores: o cliente e os assinantes de criar uma negociação, userAAtaA e userBAtaB de financiar as pernas, e MEMO_PROGRAM_ID e authority de liquidar a negociação são reaproveitados.

Recuperar uma perna

Reclaim devolve a perna do assinante e deixa a negociação aberta, de modo que a perna possa ser financiada novamente. Use-o quando o problema estiver na perna da contraparte; Cancel e Reject encerram a negociação inteira.

import { MEMO_PROGRAM_ADDRESS } from "@solana-program/memo";
import { getReclaimDvpInstruction } from "./dvp";
// The asset-leg party takes its asset back.
const reclaimInstruction = getReclaimDvpInstruction({
signer: userASigner,
swapDvp,
mint: mintA,
dvpSourceAta: escrowA,
signerDestAta: userAAtaA,
tokenProgram: tokenProgramA,
memoProgram: MEMO_PROGRAM_ADDRESS,
});
await client.sendTransaction([reclaimInstruction]);

Cancelar ou rejeitar

Cancel é assinada pela autoridade de liquidação e Reject por qualquer uma das partes. Ambas reembolsam o que cada escrow contém à parte da perna e fecham o registro e ambos os escrows, com a rent das contas fechadas indo para o assinante. As duas instruções recebem as mesmas contas; só o assinante muda.

import { getCancelDvpInstruction, getRejectDvpInstruction } from "./dvp";
const unwindAccounts = {
swapDvp,
mintA,
mintB,
dvpAtaA: escrowA,
dvpAtaB: escrowB,
userAAtaA,
userBAtaB,
tokenProgramA,
tokenProgramB,
memoProgram: MEMO_PROGRAM_ADDRESS,
legAExtrasCount: 0,
};
// Signed by the settlement authority:
const cancelInstruction = getCancelDvpInstruction({
settlementAuthority: authoritySigner,
...unwindAccounts,
});
// Or signed by either party:
const rejectInstruction = getRejectDvpInstruction({
signer: userBSigner,
...unwindAccounts,
});

As contas de reembolso (userAAtaA, userBAtaB) devem existir para qualquer perna que tenha saldo; uma conta de reembolso não inicializada faz a transferência de tokens falhar e reverte a instrução. Uma perna que nunca foi financiada não precisa de transferência e tolera uma conta ausente.

Recuperar um depósito tardio

Settle, Cancel e Reject fecham ambas as token accounts de escrow, portanto uma transferência simples para um escrow fechado falha. Um depósito só chega após o encerramento se algo recriar o escrow antes, por exemplo uma transação de financiamento que inclua uma instrução de associated token account create-idempotent antes da transferência e que estava em andamento quando a contraparte enviou Reject. Os tokens ficam então em um escrow cuja negociação já não existe: o registro desapareceu, então Reclaim não consegue encontrá-lo, e o tombstone do nonce impede que uma nova negociação seja criada no mesmo endereço e capture o depósito.

Para usar RecoverDvp, a parte da perna fornece as entradas de seed originais (autoridade de liquidação, ambas as partes, ambos os mints, nonce). O programa rederiva o endereço do registro, exige o seu tombstone como prova de que a negociação existiu, esvazia o escrow para a token account do próprio assinante e o fecha, devolvendo a rent do escrow ao assinante. A perna do assinante é escolhida conforme a parte que assina: mint_a para user_a, mint_b para user_b.

import { getAddressEncoder, getProgramDerivedAddress } from "@solana/kit";
import { DVP_SWAP_PROGRAM_PROGRAM_ADDRESS, getRecoverDvpInstruction } from "./dvp";
const [nonceTombstone] = await getProgramDerivedAddress({
programAddress: DVP_SWAP_PROGRAM_PROGRAM_ADDRESS,
seeds: ["nonce", getAddressEncoder().encode(swapDvp)],
});
const recoverInstruction = getRecoverDvpInstruction({
signer: userASigner,
swapDvp,
nonceTombstone,
mint: mintA,
dvpEscrowAta: escrowA,
signerDestAta: userAAtaA,
tokenProgram: tokenProgramA,
memoProgram: MEMO_PROGRAM_ADDRESS,
settlementAuthority,
userA,
userB,
mintA,
mintB,
nonce,
});
await client.sendTransaction([recoverInstruction]);

A recuperação depende de a parte guardar os parâmetros de criação. Um sistema que financia negociações armazena as entradas de seed junto com cada ordem até que a negociação tenha sido encerrada e os saldos dos escrows tenham sido confirmados como vazios.

Notas

  • Pernas congeladas. Em um mint com restrições, um escrow congelado bloqueia a transferência de tokens que essas instruções executam até que a freeze authority o descongele. Veja a visão geral.
  • Alterações no mint após a criação. Os caminhos de desfazimento ignoram as verificações de extensão e de mint authority para que as verificações do próprio programa não bloqueiem uma recuperação. As regras do próprio Token Program continuam a valer. Um mint só pode ser fechado com supply zero, portanto não pode mudar enquanto houver saldo no escrow, mas um mint fechado e recriado como NonTransferable ou com taxa de transferência antes de um depósito tardio chegar pode tornar esse depósito imovível ou sujeito a taxa. Essa é a dependência da mint authority listada em tokens compatíveis.

Próximos passos

Is this page helpful?

Índice

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