SettleDvp подписывает расчётный авторитет, указанный в записи сделки. В одной
транзакции инструкция переводит amount_a со стороны актива на адрес назначения
участника стороны оплаты и amount_b со стороны оплаты на адрес назначения
участника стороны актива, возвращает любой излишек в любом из эскроу участнику
соответствующей стороны и закрывает запись и оба эскроу. Если хотя бы один из
этих переводов не может завершиться, инструкция завершается ошибкой, и ничего не
перемещается.
Эта страница продолжает разделы «Создание сделки»
и «Обеспечение сторон»; клиент, подписанты,
trade и token accounts участников переносятся оттуда.
Повторная проверка перед подписанием
Расчётный авторитет подписывает транзакцию, которая перемещает обе стороны, поэтому перед подписанием он выполняет то же чтение, что и каждый участник: владельца, размер, участников, минты, суммы, срок действия и оба адреса назначения (см. «Обеспечение сторон»). Сама программа при расчёте повторно проверяет, что каждый минт по-прежнему принадлежит token program, записанной при создании, не имеет заблокированных расширений и имеет тот же mint authority, что и при создании сделки, поэтому минт, пересозданный с заблокированным расширением, другой token program или другим mint authority, рассчитан быть не может.
Создайте аккаунты получателей
Settle записывает данные в четыре token accounts, которые программа не создаёт:
| Аккаунт | Получает | Владелец |
|---|---|---|
userADestinationAtaB | Сторона оплаты (amount_b) | user_a_settlement_destination |
userBDestinationAtaA | Сторона актива (amount_a) | user_b_settlement_destination |
userAAtaA | Любой излишек на стороне актива | user_a |
userBAtaB | Любой излишек на стороне оплаты | user_b |
Аккаунты для излишка используются, только если в эскроу лежит больше согласованной суммы, но излишек может создать любой владелец токенов этого минта, отправив в эскроу несколько минимальных единиц. Если аккаунта возврата не существует, весь Settle завершается ошибкой, поэтому создайте все четыре идемпотентно в той же транзакции.
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,}),);
Выполните Settle
legAExtrasCount сообщает программе, сколько из аккаунтов, добавленных после
фиксированного списка, относятся к transfer hook стороны актива; остальные
относятся к hook стороны оплаты. Для минтов без transfer hooks передайте ноль и
ничего не добавляйте. memo program account требуется инструкции и используется
только для адресатов, которым нужен 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
Сгенерированный IDL перечисляет только фиксированные аккаунты, поэтому
сгенерированные билдеры ничего не знают об аккаунтах hook. Определите
дополнительные метаданные аккаунтов для минта каждого hook так же, как вы сделали
бы для прямого перевода этого токена, а затем добавьте их в инструкцию: сначала
аккаунты стороны актива, затем стороны оплаты, указав в legAExtrasCount число
аккаунтов из первой группы. В TypeScript разверните полученные метаданные в массив
accounts инструкции; в Rust используйте add_remaining_accounts у билдера.
Лимит составляет 32 аккаунта на сторону, включая программу hook и аккаунт
валидации; если hooks есть у обеих сторон, первым может сработать лимит аккаунтов
в транзакции. Программа снимает флаг подписанта с каждого передаваемого аккаунта,
поэтому hook не может получить подпись расчётного авторитета этим путём.
Запишите результат
Считайте сделку рассчитанной, когда транзакция Settle достигает статуса
finalized. После расчёта запись и оба эскроу больше не существуют, поэтому
система, которой условия понадобятся позже, сохраняет их из чтения до расчёта или
из транзакции создания. rent закрытых аккаунтов перешёл к расчётному авторитету.
Ошибки Settle
LegNotFunded означает, что в эскроу меньше согласованной суммы; DvpExpired и
SettlementTooEarly означают, что временное окно упущено; MintAuthorityChanged
и BlockedMintExtension означают, что минт изменился после создания;
RecipientAtaMismatch означает, что token account назначения или возврата
отсутствует либо больше не принадлежит ожидаемому владельцу и минту, обычно
потому, что был пропущен описанный выше шаг создания аккаунтов. Во всех случаях
ничего не переместилось, и участники могут расторгнуть сделку с учётом полномочий
минтов (см. «Расторжение сделки»).
Дальнейшие шаги
- Расторжение сделки: возврат обеспеченных сторон, если сделка не рассчитана
Is this page helpful?