Расторжение сделки

Четыре инструкции возвращают средства без расчёта: Reclaim, Cancel и Reject — пока сделка открыта, и Recover — для депозита, поступившего после её закрытия. Ни одна из них не проверяет срок действия. Замороженный эскроу или изменённый mint всё равно могут заблокировать часть, как описано в примечаниях в конце.

ИнструкцияПодписантОбласть действияСделка после этого
ReclaimDvpuser_a или user_bСобственная часть подписантаОстаётся открытой
CancelDvpAuthority расчётовОбе частиЗакрыта
RejectDvpuser_a или user_bОбе частиЗакрыта
RecoverDvpuser_a или user_bЧасть подписанта, после закрытияУже закрыта

В каждом случае всё, что находится в эскроу, возвращается стороне соответствующей части: эскроу A — на token account user_a для mint_a, эскроу B — на token account user_b для mint_b, независимо от того, кто его пополнил. Program account memo-программы передаётся для аккаунтов возврата, которым требуется memo.

Эта страница продолжает предыдущие шаги: клиент и подписанты из создания сделки, userAAtaA и userBAtaB из финансирования частей, а также MEMO_PROGRAM_ID и authority из расчёта по сделке переносятся сюда.

Возврат части

Reclaim возвращает часть подписанта и оставляет сделку открытой, поэтому часть можно профинансировать снова. Используйте его, когда проблема с частью контрагента; Cancel и Reject закрывают всю сделку.

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]);

Cancel или Reject

Cancel подписывает authority расчётов, а Reject — любая из сторон. Обе инструкции возвращают всё, что лежит в каждом эскроу, стороне соответствующей части и закрывают запись и оба эскроу, при этом rent закрытых аккаунтов отправляется подписанту. Обе инструкции принимают одни и те же аккаунты; отличается только подписант.

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

Аккаунты возврата (userAAtaA, userBAtaB) должны существовать для любой части с ненулевым балансом; неинициализированный аккаунт возврата приводит к сбою перевода токенов и откату инструкции. Часть, которая никогда не пополнялась, не требует перевода и допускает отсутствие аккаунта.

Возврат позднего депозита

Settle, Cancel и Reject закрывают оба token account эскроу, поэтому обычный перевод в закрытый эскроу завершается ошибкой. Депозит попадает в эскроу после закрытия, только если что-то сначала заново его создаёт — например, транзакция пополнения, которая включает инструкцию create-idempotent associated token account перед переводом и находилась в процессе выполнения, когда контрагент отправил Reject. Тогда токены оказываются в эскроу, сделки которого уже не существует: записи нет, поэтому Reclaim не может её найти, а tombstone-запись nonce не позволяет создать новую сделку по тому же адресу и перехватить депозит.

Чтобы использовать RecoverDvp, сторона соответствующей части передаёт исходные входные данные seed (authority расчётов, обе стороны, оба mint, nonce). Программа заново выводит адрес записи, требует наличия её tombstone как доказательства того, что сделка существовала, переводит содержимое эскроу на собственный token account подписанта и закрывает эскроу, возвращая rent эскроу подписанту. Часть подписанта определяется тем, какая сторона подписывает: mint_a для user_a, mint_b для 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]);

Восстановление зависит от того, сохранила ли сторона параметры создания. Система, которая финансирует сделки, хранит входные данные seed для каждого ордера, пока сделка не закрыта и не подтверждено, что балансы эскроу пусты.

Примечания

  • Замороженные части. Для mint с контролем доступа замороженный эскроу блокирует перевод токенов, выполняемый этими инструкциями, пока freeze authority не разморозит его. См. обзор.
  • Изменения mint после создания. Пути расторжения пропускают проверки расширений и mint authority, чтобы собственные проверки программы не блокировали возврат. Правила самой Token Program при этом по-прежнему действуют. Mint можно закрыть только при нулевом supply, поэтому он не может измениться, пока в эскроу есть баланс, но mint, закрытый и воссозданный как NonTransferable или с комиссией за перевод до поступления позднего депозита, может сделать этот депозит непереводимым или облагаемым комиссией. Это зависимость от mint authority, указанная в разделе «Поддерживаемые токены».

Дальнейшие шаги

Is this page helpful?

Содержание

Редактировать страницу