SettleDvp jest podpisywana przez organ rozliczający wskazany w rekordzie wymiany. W
jednej transakcji przenosi amount_a nogi aktywa na adres docelowy strony nogi
gotówkowej oraz amount_b nogi gotówkowej na adres docelowy strony nogi aktywa,
zwraca ewentualną nadwyżkę z każdego escrow stronie danej nogi oraz zamyka rekord
i oba escrow. Jeśli któryś z tych transferów nie może zostać zrealizowany, instrukcja
kończy się niepowodzeniem i nic nie zostaje przeniesione.
Ta strona kontynuuje temat z tworzenia wymiany
i finansowania nóg; klient, sygnatariusze,
trade oraz token accounts stron pozostają bez zmian.
Ponowna weryfikacja przed podpisaniem
Organ rozliczający podpisuje transakcję przenoszącą obie nogi, dlatego przed podpisaniem wykonuje ten sam odczyt co każda ze stron: właściciel, rozmiar, strony, mints, kwoty, termin ważności i oba adresy docelowe (zob. finansowanie nóg). Sam program ponownie sprawdza przy rozliczeniu, czy każdy mint nadal należy do token program zapisanego podczas tworzenia, nie ma zablokowanego rozszerzenia i ma tę samą mint authority co w chwili utworzenia wymiany, więc mint utworzony ponownie z zablokowanym rozszerzeniem, innym token program lub inną mint authority nie może zostać rozliczony.
Tworzenie kont odbiorców
Instrukcja Settle zapisuje dane w czterech token accounts, których program nie tworzy:
| Konto | Otrzymuje | Właściciel |
|---|---|---|
userADestinationAtaB | Nogę gotówkową (amount_b) | user_a_settlement_destination |
userBDestinationAtaA | Nogę aktywa (amount_a) | user_b_settlement_destination |
userAAtaA | Ewentualną nadwyżkę z nogi aktywa | user_a |
userBAtaB | Ewentualną nadwyżkę z nogi gotówkowej | user_b |
Konta nadwyżek są używane tylko wtedy, gdy escrow zawiera więcej niż uzgodniona kwota, ale każdy, kto posiada dany mint, może wywołać nadwyżkę, wysyłając do escrow kilka jednostek bazowych. Jeśli konto zwrotu nie istnieje, całe Settle kończy się niepowodzeniem, dlatego utwórz wszystkie cztery idempotentnie w tej samej transakcji.
import {findAssociatedTokenPda,getCreateAssociatedTokenIdempotentInstruction,} from "@solana-program/token-2022";const destinationA = t.userASettlementDestination; // defaults to userAconst destinationB = t.userBSettlementDestination; // defaults to userBconst [userADestinationAtaB] = await findAssociatedTokenPda({mint: mintB,owner: destinationA,tokenProgram: tokenProgramB,});const [userBDestinationAtaA] = await findAssociatedTokenPda({mint: mintA,owner: destinationB,tokenProgram: tokenProgramA,});// userAAtaA and userBAtaB are from fund the legs.const createRecipientAccounts = [{ ata: userADestinationAtaB, owner: destinationA, mint: mintB, tokenProgram: tokenProgramB },{ ata: userBDestinationAtaA, owner: destinationB, mint: mintA, tokenProgram: tokenProgramA },{ ata: userAAtaA, owner: userA, mint: mintA, tokenProgram: tokenProgramA },{ ata: userBAtaB, owner: userB, mint: mintB, tokenProgram: tokenProgramB },].map(({ ata, owner, mint, tokenProgram }) =>getCreateAssociatedTokenIdempotentInstruction({payer: authoritySigner,ata,owner,mint,tokenProgram,}),);
Rozliczenie
legAExtrasCount informuje program, ile spośród kont dołączonych po stałej liście
należy do transfer hook nogi aktywa; pozostałe należą do transfer hook nogi
gotówkowej. W przypadku mints bez transfer hooks przekaż zero i nic nie dołączaj. Memo
program account jest wymagany przez instrukcję i jest używany tylko dla adresów docelowych,
które wymagają memo.
import { MEMO_PROGRAM_ADDRESS } from "@solana-program/memo";import { getSettleDvpInstruction } from "./dvp";const settleInstruction = getSettleDvpInstruction({settlementAuthority: authoritySigner,swapDvp,mintA,mintB,dvpAtaA: escrowA,dvpAtaB: escrowB,userADestinationAtaB,userBDestinationAtaA,userAAtaA,userBAtaB,tokenProgramA,tokenProgramB,memoProgram: MEMO_PROGRAM_ADDRESS,legAExtrasCount: 0,});const { context: settleContext } = await client.sendTransaction([...createRecipientAccounts,settleInstruction,]);const settleSignature = settleContext.signature;console.log("Submitted:", settleSignature);// sendTransaction returns at the client's commitment (confirmed by default).// Wait for finalized before recording the trade as settled.let finalized = false;for (let attempt = 0; attempt < 30 && !finalized; attempt++) {const {value: [status],} = await client.rpc.getSignatureStatuses([settleSignature]).send();if (status?.err) {throw new Error("Settle transaction failed");}finalized = status?.confirmationStatus === "finalized";if (!finalized) {await new Promise((resolve) => setTimeout(resolve, 2_000));}}if (!finalized) {// A closed record means it settled; an open one is safe to settle again.throw new Error("Settle not finalized after 60s; read the trade record");}console.log("Settled:", settleSignature);
Transfer hooks
Wygenerowany IDL wymienia tylko stałe konta, więc wygenerowane buildery
nie znają kont hooków. Rozwiąż dodatkowe account metas każdego mintu z hookiem
tak samo jak przy bezpośrednim transferze tego tokena, a następnie dołącz je do
instrukcji: najpierw konta nogi aktywa, potem nogi gotówkowej, przy czym
legAExtrasCount ustaw na liczbę kont w pierwszej grupie. W TypeScripcie rozwiń
rozwiązane metas w tablicy accounts instrukcji; w Ruście użyj
add_remaining_accounts w builderze. Limit wynosi 32 konta na nogę,
łącznie z programem hooka i kontem walidacyjnym; limit kont w transakcji
może zadziałać wcześniej, gdy obie nogi mają hooki. Program usuwa flagę sygnatariusza
z każdego przekazywanego konta, więc hook nie może uzyskać podpisu organu rozliczającego
tą drogą.
Zapisanie wyniku
Uznaj wymianę za rozliczoną, gdy transakcja Settle osiągnie status finalized. Rekord
i oba escrow nie istnieją po rozliczeniu, więc system, który później potrzebuje
warunków, zapisuje je z odczytu sprzed rozliczenia lub z transakcji
tworzącej. Rent zamkniętych kont trafił do organu rozliczającego.
Błędy rozliczenia
LegNotFunded oznacza, że escrow zawiera mniej niż uzgodniona kwota; DvpExpired
i SettlementTooEarly oznaczają, że okno czasowe zostało przegapione; MintAuthorityChanged
i BlockedMintExtension oznaczają, że mint zmienił się po utworzeniu;
RecipientAtaMismatch oznacza, że docelowy lub zwrotny token account nie istnieje albo
nie należy już do oczekiwanego właściciela i mintu, zwykle dlatego, że pominięto
opisany wyżej krok tworzenia kont. W każdym przypadku nic nie zostało przeniesione, a
strony mogą cofnąć wymianę, z zastrzeżeniem uprawnień mintu (zob.
cofnięcie wymiany).
Następne kroki
- Cofnięcie wymiany: odzyskiwanie sfinansowanych nóg, gdy wymiana nie zostaje rozliczona
Is this page helpful?