取引を巻き戻す

決済せずに資金を返却する instructions は 4 つあります。取引がオープンの間に使う Reclaim、Cancel、Reject と、クローズ後に届いた入金のための Recover です。いずれも有効期限はチェックしません。末尾の注意事項で説明するとおり、escrow のフリーズや mint の変更によって、レッグの資金が引き出せなくなる場合があります。

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

いずれの場合も、escrow に保持されているものはすべて、そのレッグの当事者に払い戻されます。escrow A は user_a の mint_a 用 token account へ、escrow B は user_b の mint_b 用 token account へ、誰が入金したかにかかわらず返却されます。memo が必要な返金先アカウントのために、memo program account を指定します。

このページは前のステップの続きです。取引を作成するで用意した client と署名者、レッグに入金するで用意した 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 はどちらかの当事者が署名します。どちらも、各 escrow に保持されているものをそのレッグの当事者に払い戻し、レコードと両方の escrow をクローズします。クローズしたアカウントの 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 はいずれも両方の escrow token account をクローズするため、クローズ済みの escrow への通常の転送は失敗します。クローズ後に入金が着金するのは、先に何かが escrow を再作成した場合に限られます。たとえば、転送の前に冪等な associated token account 作成 instructions を含む入金トランザクションが、相手方が Reject を送信した時点で処理中だった場合です。この場合、トークンは取引がもう存在しない escrow に残ります。レコードは消えているため Reclaim では見つけられず、nonce の tombstone があるため、同じアドレスに新しい取引を作成して入金を奪われることもありません。

RecoverDvp を使うには、そのレッグの当事者が元の seed の入力(決済権限者、両当事者、両 mint、nonce)を指定します。プログラムはレコードのアドレスを再導出し、取引が存在した証拠としてその tombstone を要求します。その後、escrow の残高を署名者自身の token account に払い出してクローズし、escrow の 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]);

リカバリーは、当事者が作成時のパラメータを保管していることを前提とします。取引に入金するシステムは、取引がクローズされ、escrow の残高が空であることを確認するまで、各注文の seed の入力を保管しておきます。

注意事項

  • フリーズされたレッグ。 ゲート付き mint では、escrow がフリーズされていると、フリーズ権限者が解除するまで、これらの instructions が行うトークン転送がブロックされます。概要を参照してください。
  • 作成後の mint の変更。 巻き戻しのパスでは、プログラム自身のチェックがリカバリーを妨げないよう、拡張機能と mint authority のチェックをスキップします。ただし、token program 自体のルールは引き続き適用されます。mint は供給量がゼロの場合にのみクローズできるため、escrow に残高がある間は変更されません。しかし、遅れた入金が着金する前に mint がクローズされ、NonTransferable や転送手数料付きで再作成されると、その入金は移動不能になったり、手数料が発生したりする可能性があります。これは、サポートされているトークンに記載されている mint authority への依存関係です。

次のステップ

Is this page helpful?

目次

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