거래 되돌리기

정산 없이 자금을 반환하는 인스트럭션은 네 가지입니다. 거래가 열려 있는 동안 사용하는 Reclaim, Cancel, Reject와, 거래가 종료된 후 도착한 입금을 위한 Recover입니다. 이들 중 어느 것도 만료 여부를 확인하지 않습니다. 마지막의 참고 사항에서 설명하듯이, 동결된 에스크로나 변경된 민트 때문에 레그가 여전히 묶여 있을 수 있습니다.

인스트럭션서명자범위이후 거래 상태
ReclaimDvpuser_a 또는 user_b서명자 본인의 레그계속 열려 있음
CancelDvp정산 권한자양쪽 레그종료됨
RejectDvpuser_a 또는 user_b양쪽 레그종료됨
RecoverDvpuser_a 또는 user_b종료 후 서명자의 레그이미 종료됨

각 경우 에스크로에 들어 있는 모든 자산이 해당 레그의 당사자에게 반환됩니다. 에스크로 A는 user_a의 mint_a용 token account로, 에스크로 B는 user_b의 mint_b용 token account로 반환되며, 누가 입금했는지는 상관없습니다. 메모가 필요한 환불 계정을 위해 memo program account가 함께 제공됩니다.

이 페이지는 이전 단계에서 이어집니다. 거래 생성의 클라이언트와 서명자, 레그 입금의 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는 서명자에게 돌아갑니다. 두 인스트럭션은 동일한 계정을 사용하며 서명자만 다릅니다.

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은 이를 찾을 수 없고, nonce tombstone 덕분에 같은 주소에 새 거래가 생성되어 입금을 가로채는 일은 방지됩니다.

RecoverDvp를 사용하려면 레그의 당사자가 원래의 seed 입력값(정산 권한자, 양쪽 당사자, 두 민트, 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 입력값을 저장해 두어야 합니다.

참고 사항

  • 동결된 레그. 게이트가 설정된 민트에서는 동결된 에스크로가 이 인스트럭션들이 수행하는 토큰 전송을 동결 권한자가 해제할 때까지 막습니다. 개요를 참고하세요.
  • 생성 이후의 민트 변경. 되돌리기 경로는 프로그램 자체의 검사가 복구를 막지 않도록 확장 기능 및 민트 권한 검사를 건너뜁니다. token program 자체의 규칙은 그대로 적용됩니다. 민트는 공급량이 0일 때만 종료할 수 있으므로 에스크로에 잔액이 있는 동안에는 변경될 수 없지만, 늦은 입금이 도착하기 전에 민트가 종료된 뒤 NonTransferable이나 전송 수수료가 있는 형태로 다시 생성되면 해당 입금을 옮길 수 없거나 수수료가 부과될 수 있습니다. 이것이 지원되는 토큰에 나열된 민트 권한 의존성입니다.

다음 단계

Is this page helpful?

목차

페이지 편집