Zasil strony transakcji

Zasilenie to zwykły TransferChecked na escrow danej strony, więc każdy portfel lub system kustodialny, który potrafi wysłać transfer tokenów, może zasilić stronę transakcji. Klienta DvP wymaga tylko odczyt rekordu transakcji przed zasileniem. Program sprawdza salda escrow dopiero podczas rozliczenia; nic nie weryfikuje transferu w chwili jego zaksięgowania, dlatego każda ze stron odczytuje rekord transakcji przed wykonaniem transferu.

Ta strona jest kontynuacją części tworzenie transakcji; klient i podpisujący są ci sami.

Zweryfikuj warunki

Utworzenie rekordu nie wymaga uprawnień, a adresy escrow wynikają wyłącznie z rekordu i mint, nigdy z warunków. Transfer na escrow może więc zostać zaksięgowany przy warunkach, na które nigdy się nie zgodziłeś, w tym przy docelowych adresach rozliczenia, które przekierowują środki twojego kontrahenta na trzeci adres. Przed zasileniem odczytaj konto SwapDvp i porównaj je z uzgodnioną transakcją.

verifySwapDvp w TypeScript i verify::decode_swap_dvp_account w Rust potwierdzają, że konto należy do programu DvP, ma dokładnie 458 bajtów i znajduje się pod adresem wyprowadzonym z jego własnych pól (fetchSwapDvpChecked wykonuje tylko dwie pierwsze kontrole). Zwykłe funkcje odczytu fetchSwapDvp i decodeSwapDvp nie wykonują żadnej z nich. Kontrole stron, kwot, terminów i adresów docelowych wykonuje wywołujący, porównując je z uzgodnioną transakcją.

import { unwrapOption } from "@solana/kit";
import { verifySwapDvp } from "./dvp";
const trade = await verifySwapDvp(client.rpc, swapDvp);
const t = trade.data;
const expectedAmountA = 1_000_000_000n;
const expectedAmountB = 998_500_000n;
const expectedEarliestSettlement: bigint | null = null; // as agreed
const expectedExpiry = 1_798_761_600n; // as agreed, unix seconds
if (
t.userA !== userA ||
t.userB !== userB ||
t.mintA !== mintA ||
t.mintB !== mintB ||
t.settlementAuthority !== settlementAuthority ||
t.tokenProgramA !== tokenProgramA ||
t.tokenProgramB !== tokenProgramB ||
t.amountA !== expectedAmountA ||
t.amountB !== expectedAmountB ||
unwrapOption(t.earliestSettlementTimestamp) !== expectedEarliestSettlement ||
t.expiryTimestamp !== expectedExpiry ||
t.userASettlementDestination !== userA ||
t.userBSettlementDestination !== userB
) {
throw new Error("Stored terms do not match the agreed trade; do not fund");
}
if (t.expiryTimestamp <= BigInt(Math.floor(Date.now() / 1000)) + 60n) {
throw new Error("Trade expires too soon to fund safely");
}

Jeśli rekord utworzył twój kontrahent lub organ rozliczający, porównaj każde pole z warunkami uzgodnionymi poza księgą. Jeśli utworzyłeś go samodzielnie, ten odczyt potwierdza, że został zaksięgowany tak, jak go wysłano.

Transfer na escrow

Zasil stronę transakcji dokładnie uzgodnioną kwotą. Program ogranicza rozliczenie do amount_x i zwraca nadwyżkę stronie, do której należy dana strona transakcji, więc nadwyżka nie przepada, ale wymaga drugiego transferu podczas rozliczenia i może się nie powieść przy niektórych hookach transferu (zobacz uwagi poniżej).

Użyj TransferChecked, a nie niesprawdzanego Transfer. Mint Token-2022 z rozszerzeniami zależnymi od konta mint, takimi jak Pausable, odrzucają transfer, który go nie zawiera, a mint z hookiem transferu wymaga, aby konta hooka zostały rozwiązane i dołączone tak samo jak przy każdym innym transferze tego tokena.

Strona aktywa, podpisująca jako userASigner, zasila escrow A:

import {
findAssociatedTokenPda,
getTransferCheckedInstruction,
} from "@solana-program/token-2022";
const [userAAtaA] = await findAssociatedTokenPda({
mint: mintA,
owner: userA,
tokenProgram: tokenProgramA,
});
const [userBAtaB] = await findAssociatedTokenPda({
mint: mintB,
owner: userB,
tokenProgram: tokenProgramB,
});
const fundAssetLeg = getTransferCheckedInstruction({
source: userAAtaA,
mint: mintA,
destination: escrowA,
authority: userASigner,
amount: t.amountA,
decimals: 6,
}, { programAddress: tokenProgramA });
await client.sendTransaction([fundAssetLeg]);

Strona gotówkowa robi to samo dla escrow B, używając userBAtaB, mintB, userBSigner, t.amountB oraz { programAddress: tokenProgramB }.

Transfer zasilający widoczny ze statusem confirmed nadal może zostać unieważniony. Program odczytuje saldo escrow onchain podczas rozliczenia, więc Settle wysłany dla transferu zasilającego, który nie został zaksięgowany, kończy się niepowodzeniem z błędem LegNotFunded i nic się nie przemieszcza; kosztem jest nieudany Settle i ponowna próba. Uznaj stronę transakcji za zasiloną dopiero, gdy jej transfer ma status finalized.

Uwagi

  • Strony z wrapped SOL. Program odczytuje saldo natywnego escrow po jego synchronizacji, więc lamport przesłane na escrow wrapped SOL przelewem systemowym są wliczane. Nigdy nie wysyłaj SOL na escrow dla żadnego innego mint: program nie może ich zwrócić, a trafią one do osoby, która zamknie konto.
  • Mint z ograniczeniami. W przypadku mint z Default Account State ustawionym na frozen escrow jest tworzony jako zamrożony, a transfer kończy się niepowodzeniem, dopóki emitent go nie odblokuje lub nie dopuści. W przeglądzie opisano współdziałanie z Token ACL.
  • Hooki transferu. Zasilaj escrow z hookami dokładnie uzgodnioną kwotą. Jeśli nadwyżka wymusza zwrot podczas rozliczenia, zwrot ponownie wykorzystuje konta hooka danej strony z innym miejscem docelowym i kwotą, co niektóre hooki odrzucają. Rozwiązaniem jest Reclaim, ponowne zasilenie dokładną kwotą i ponowne rozliczenie.
  • Zasilenie po wygaśnięciu. Nic nie blokuje transferu po expiry_timestamp. Zostaje zaksięgowany, rozliczenie nie jest już możliwe, a strona, do której należy dana strona transakcji, odzyskuje środki przez Reclaim, gdy transakcja jest otwarta, lub Recover po jej zamknięciu. Zobacz wycofanie transakcji.
  • Kto zasila pierwszy. Program nie ustala kolejności obu stron. Kolejność to kwestia uzgodnienia między stronami; każda ze stron może odzyskać swoją część w dowolnym momencie przed rozliczeniem, chyba że autorytet mint (freeze, pause lub hook transferu) blokuje escrow.

Następne kroki

Is this page helpful?

Spis treści

Edytuj stronę