Desfazer uma operação

Quatro instruções devolvem fundos sem liquidar: Reclaim, Cancel e Reject enquanto a operaçã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 uma mint alterada ainda pode reter uma perna, conforme descrito nas notas no final.

InstruçãoSignatárioÂmbitoOperação depois
ReclaimDvpuser_a ou user_bA perna do próprio signatárioAinda aberta
CancelDvpAutoridade de liquidaçãoAmbas as pernasEncerrada
RejectDvpuser_a ou user_bAmbas as pernasEncerrada
RecoverDvpuser_a ou user_bA perna do signatário, após o encerramentoJá encerrada

Em cada caso, o escrow é esvaziado, e tudo o que ele contém é enviado à 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 dos passos anteriores: o cliente e os signatários de criar uma operação, userAAtaA e userBAtaB de financiar as pernas, e MEMO_PROGRAM_ID e authority de liquidar a operação são reaproveitados.

Reclamar uma perna

Reclaim devolve a perna do signatário e deixa a operação aberta, para que a perna possa ser financiada novamente. Use-o quando o problema estiver na perna da contraparte; Cancel e Reject encerram toda a operação.

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 é assinado pela autoridade de liquidação e Reject por qualquer uma das partes. Ambos reembolsam à parte da perna tudo o que cada escrow contém e encerram o registo e ambos os escrows, com o rent das contas encerradas indo para o signatário. As duas instruções recebem as mesmas contas; apenas o signatário difere.

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 encerram ambas as token accounts de escrow, pelo que uma simples transferência para um escrow encerrado falha. Um depósito só chega depois do encerramento se algo recriar primeiro o escrow, por exemplo uma transação de financiamento que inclua uma instrução create-idempotent de associated token account antes da transferência e que estava em curso quando a contraparte enviou Reject. Os tokens ficam então num escrow cuja operação já não existe: o registo desapareceu, portanto Reclaim não o consegue encontrar, e o tombstone do nonce impede que uma nova operaçã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, ambas as mints, nonce). O programa volta a derivar o endereço do registo, exige o respetivo tombstone como prova de que a operação existiu, esvazia o escrow para a token account do próprio signatário e encerra-o, devolvendo o rent do escrow ao signatário. A perna do signatário é escolhida pela 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 operações armazena as entradas de seed com cada ordem até que a operação tenha sido encerrada e os saldos dos escrows tenham sido confirmados como vazios.

Notas

  • Pernas congeladas. Numa mint com restrições, um escrow congelado bloqueia a transferência de tokens que estas instruções realizam até que a freeze authority o descongele. Consulte a visão geral.
  • Alterações da 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 aplicar-se. Uma mint só pode ser encerrada com oferta zero, por isso não pode mudar enquanto houver saldo no escrow, mas uma mint encerrada e recriada como NonTransferable ou com uma 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 suportados.

Próximos passos

Is this page helpful?

Índice

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