Финансирование — это обычный TransferChecked на эскроу плеча, поэтому профинансировать
плечо может любой кошелёк или кастодиальная система, умеющая отправлять переводы токенов.
Клиент DvP нужен только для чтения записи сделки перед финансированием. Программа
проверяет балансы эскроу только при расчёте; при поступлении перевода ничего не
проверяется, поэтому каждая сторона читает запись сделки до перевода.
Эта страница продолжает раздел создание сделки; клиент и подписанты сохраняются.
Проверьте условия
Создание записи не требует разрешений, а адреса эскроу выводятся только из
записи и mint, но никогда не из условий. Поэтому перевод на эскроу может
поступить по условиям, с которыми вы не соглашались, в том числе с адресами
расчёта, которые перенаправляют поступления вашего контрагента на третий адрес.
Перед финансированием прочитайте аккаунт SwapDvp и сверьте его с согласованной
сделкой.
verifySwapDvp в TypeScript и verify::decode_swap_dvp_account в Rust
подтверждают, что аккаунт принадлежит программе DvP, имеет ровно 458 байт и
находится по адресу, который выводится из его собственных полей (fetchSwapDvpChecked
выполняет только первые две проверки). Обычные ридеры fetchSwapDvp и decodeSwapDvp
не выполняют ни одной из этих проверок. Проверки сторон, сумм, времени и адресов назначения
вызывающий код выполняет самостоятельно, сверяя их с согласованной сделкой.
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 agreedconst expectedExpiry = 1_798_761_600n; // as agreed, unix secondsif (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");}
Если запись создана вашим контрагентом или расчётным органом, сверьте каждое поле с условиями, согласованными вне блокчейна. Если вы создали её сами, это чтение подтверждает, что она поступила в том виде, в каком была отправлена.
Перевод на эскроу
Финансируйте плечо ровно на согласованную сумму. Программа ограничивает расчёт
значением amount_x и возвращает излишек стороне плеча, поэтому излишек не теряется,
но при расчёте добавляется второй перевод, и он может завершиться ошибкой с некоторыми
transfer hooks (см. примечания ниже).
Используйте TransferChecked, а не непроверяемый Transfer. Mint-ы Token-2022 с
расширениями, зависящими от mint account, такими как Pausable, отклоняют перевод,
в котором он не передан, а mint с transfer hook требует, чтобы аккаунты хука были
определены и добавлены так же, как и для любого другого перевода этого токена.
Сторона плеча с активом, подписывая как userASigner, финансирует эскроу 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]);
Сторона денежного плеча делает то же самое для эскроу B с userBAtaB, mintB,
userBSigner, t.amountB и { programAddress: tokenProgramB }.
Перевод финансирования, замеченный на уровне confirmed,
всё ещё может быть вытеснен. Программа читает баланс эскроу ончейн при
расчёте, поэтому Settle, отправленный при не поступившем переводе финансирования,
завершится ошибкой LegNotFunded, и ничего не сдвинется; цена — неудачный Settle
и повторная попытка. Считайте плечо профинансированным, когда его перевод finalized.
Примечания
- Плечи с wrapped SOL. Программа читает баланс нативного эскроу после его синхронизации, поэтому lamports, отправленные на эскроу wrapped SOL системным переводом, учитываются. Никогда не отправляйте SOL на эскроу для любого другого mint: программа не может его вернуть, и он достанется тому, кто закроет аккаунт.
- Закрытые mint. У mint с Default Account State frozen эскроу создаётся замороженным, и перевод завершается ошибкой, пока эмитент не разморозит его или не допустит. Обзор описывает взаимодействие с Token ACL.
- Transfer hooks. Финансируйте эскроу с хуками ровно на согласованную сумму. Если излишек вынуждает делать возврат при расчёте, возврат повторно использует аккаунты хука плеча с другим адресом назначения и суммой, что некоторые хуки отклоняют. Выход — Reclaim, повторное финансирование на точную сумму и повторный расчёт.
- Финансирование после истечения срока. Ничто не мешает переводу после
expiry_timestamp. Он поступает, расчёт становится невозможен, и сторона плеча забирает средства через Reclaim, пока сделка открыта, или через Recover после её закрытия. См. отмену сделки. - Кто финансирует первым. Программа не задаёт очерёдность двух плеч. Порядок — дело сторон; любая сторона может вернуть своё плечо в любой момент до расчёта, если только mint authority (заморозка, пауза или transfer hook) не блокирует эскроу.
Следующие шаги
- Расчёт сделки: расчётный орган обменивает оба плеча в одной транзакции
- Отмена сделки: возврат профинансированного плеча, если сделка не рассчитывается
Is this page helpful?