取引を巻き戻す

決済を行わずに資金を返還する instructions は 4 つあります。取引がオープンの間に使う Reclaim、Cancel、Reject と、クローズ後に届いた入金のための Recover です。いずれも有効期限は確認しません。末尾の注記で説明するとおり、凍結されたエスクローや変更された mint によって、レグが保持されたままになる場合があります。

instruction署名者対象範囲その後の取引
ReclaimDvpuser_a または user_b署名者自身のレグオープンのまま
CancelDvp決済権限者両方のレグクローズ
RejectDvpuser_a または user_b両方のレグクローズ
RecoverDvpuser_a または user_b署名者のレグ(クローズ後)すでにクローズ済み

いずれの場合も、エスクローに残っているものはすべて、そのレグの当事者に返還されます。escrow A は user_a の mint_a 用 token account へ、escrow B は user_b の mint_b 用 token account へ、誰が入金したかにかかわらず返還されます。メモが必要な返金先アカウントのために、memo program account を指定します。

このページは前のステップの続きです。取引を作成するで用意した client と signers、レグに入金するの 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 は決済権限者が、Reject はいずれかの当事者が署名します。どちらも各エスクローに残っているものをそのレグの当事者に返還し、レコードと両方のエスクローをクローズします。クローズされたアカウントの rent は署名者に渡ります。2 つの instructions に必要なアカウントは同じで、異なるのは署名者だけです。

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)が存在している必要があります。未初期化の返金先アカウントがあるとトークン転送が失敗し、instruction は元に戻されます。入金されていないレグには転送が不要なため、アカウントがなくても問題ありません。

遅れて届いた入金を回収する

Settle、Cancel、Reject はいずれも両方のエスクロー token account をクローズするため、クローズ済みのエスクローへの通常の転送は失敗します。クローズ後に入金が着地するのは、先に何かがエスクローを再作成した場合に限られます。たとえば、転送の前に create-idempotent の associated token account instruction を含む入金トランザクションが、相手方が Reject を送信した時点で処理中だった場合です。この場合、トークンは取引がもはや存在しないエスクローに残ります。レコードは削除されているため Reclaim では見つけられず、nonce の tombstone があるため、同じアドレスに新しい取引が作成されて入金を奪われることもありません。

RecoverDvp を使用するには、レグの当事者が元の seed の入力値(決済権限者、両当事者、両方の mint、nonce)を指定します。プログラムはレコードのアドレスを再導出し、取引が存在した証拠としてその tombstone を要求し、エスクローの残高を署名者自身の token account に移し、エスクローをクローズして、エスクローの rent を署名者に返します。署名者のレグは、署名する当事者によって決まります。user_a なら mint_a、user_b なら mint_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 が解除するまで、これらの instructions が実行するトークン転送がブロックされます。概要を参照してください。
  • 作成後の mint の変更。 巻き戻しの経路では、プログラム独自のチェックがリカバリーを妨げないよう、拡張機能と mint authority のチェックを省略します。ただし token program 自体のルールは引き続き適用されます。mint は供給量が 0 のときにしかクローズできないため、エスクローに残高がある間は変更できません。しかし、遅れて届く入金より前に mint がクローズされ、NonTransferable または転送手数料付きで再作成された場合、その入金は移動できなくなるか、手数料が発生する可能性があります。これが対応トークンに記載されている mint authority への依存です。

次のステップ

Is this page helpful?

目次

ページを編集
© 2026 Solana Foundation. 無断転載を禁じます。