Створення угоди

CreateDvp фіксує умови угоди та створює два ескроу-рахунки, які сторони мають поповнити. Токени він не переміщує. Надіслати його може будь-хто, тож запис може надійти від контрагента, оператора розрахункового органу або третьої сторони, а кожен інший учасник перевіряє збережені умови, перш ніж діяти на їх підставі (див. поповнення частин угоди).

У фрагментах коду клієнт, згенерований для програми, використовується напряму, а адреси є заповнювачами, щоб було видно, які акаунти та підписанти беруть участь. Вони націлені на devnet, де програма розгорнута за тією самою адресою, що й у mainnet-beta.

Встановлення

Станом на 2 жовтня 2026 року для клієнтів DvP не опубліковано жодного пакета. Обидва генеруються з IDL програми в репозиторії та використовуються з вихідного коду.

git clone https://github.com/solana-foundation/dvp.git
cd dvp && git checkout df9919ed02c25a93620e7f6820107c9050ce3b92
pnpm install && pnpm generate-clients && pnpm build
# The client is clients/typescript/src; copy or link it into your project as ./dvp
pnpm add @solana/kit @solana/kit-plugin-rpc @solana/kit-plugin-signer @solana-program/token-2022 @solana-program/memo

Ці сторінки написано для коміту df9919e, з @solana/kit 8 у TypeScript та крейтами solana-* 2.x у Rust.

Фрагменти коду на кожній сторінці кроків є наскрізними: кожна сторінка використовує клієнт, підписантів та адреси, визначені на попередніх сторінках. payerSigner, userASigner, userBSigner і authoritySigner — це TransactionSigner для платника rent, двох сторін і розрахункового органу, наприклад, створені через createKeyPairSignerFromBytes. Фрагменти на Rust формують інструкції, а підписання та надсилання залишають на розсуд викликача.

Виведення адрес

Запис угоди — це Program Derived Address, виведена з розрахункового органу, двох сторін, двох мінтів і nonce. Кожен ескроу — це associated token account запису для одного з мінтів. Обидва виведення не потребують доступу до мережі.

Використовуйте випадковий 64-бітний nonce для кожної угоди. Оскільки створення не потребує дозволів, передбачуваний nonce дає змогу третій стороні зайняти адресу, яку мала використати угода. Передавайте nonce і суми як bigint у TypeScript; згенерований клієнт відхиляє number.

Перед запуском замініть кожен заповнювач ..._HERE на справжню адресу. address() перевіряє base58 і викидає помилку на заповнювачі, а pubkey! не компілюється.

import { address, createClient } from "@solana/kit";
import { solanaRpc } from "@solana/kit-plugin-rpc";
import { signer } from "@solana/kit-plugin-signer";
import { TOKEN_2022_PROGRAM_ADDRESS } from "@solana-program/token-2022";
import { findSwapDvpEscrowAta, findSwapDvpPda } from "./dvp";
const client = createClient()
.use(signer(payerSigner))
.use(
solanaRpc({
rpcUrl: "https://api.devnet.solana.com",
rpcSubscriptionsUrl: "wss://api.devnet.solana.com",
}),
);
const settlementAuthority = address("SETTLEMENT_AUTHORITY_ADDRESS_HERE");
const userA = address("ASSET_LEG_PARTY_ADDRESS_HERE");
const userB = address("CASH_LEG_PARTY_ADDRESS_HERE");
const mintA = address("ASSET_MINT_ADDRESS_HERE");
const mintB = address("CASH_MINT_ADDRESS_HERE");
const tokenProgramA = TOKEN_2022_PROGRAM_ADDRESS;
const tokenProgramB = TOKEN_2022_PROGRAM_ADDRESS;
// A fresh random nonce per trade, as a bigint.
const nonce = new DataView(
crypto.getRandomValues(new Uint8Array(8)).buffer,
).getBigUint64(0);
const [swapDvp] = await findSwapDvpPda({
settlementAuthority,
userA,
userB,
mintA,
mintB,
nonce,
});
const [escrowA] = await findSwapDvpEscrowAta({
swapDvp,
mint: mintA,
tokenProgram: tokenProgramA,
});
const [escrowB] = await findSwapDvpEscrowAta({
swapDvp,
mint: mintB,
tokenProgram: tokenProgramB,
});

Фіксація умов

CreateDvp приймає дві суми в базових одиницях, термін дії, nonce та чотири необов’язкові поля: непрозоре посилання до 64 байтів, адресу призначення розрахунку для кожної сторони та найраніший час розрахунку (див. примітки). Адреса призначення — це місце, куди надходять кошти цієї сторони під час розрахунку, наприклад, депозитний гаманець кастодіана; за замовчуванням це власна адреса сторони, і після створення запису її змінити не можна. Повернення коштів і відкликання завжди надходять на власний token account сторони для мінта цієї частини угоди, а не на адресу призначення, хоч би хто поповнив ескроу.

Платник вносить rent за запис, надгробок nonce і обидва ескроу. Під час закриття цей rent отримує той, хто підписує інструкцію закриття, а не платник. Надгробок ніколи не закривається, тому кожна угода залишає невеликий постійний баланс. Плануючи SOL платника, визначайте суми rent за допомогою RPC-методу getMinimumBalanceForRentExemption, а не фіксуйте їх у коді.

import { getCreateDvpInstruction } from "./dvp";
const nowSeconds = BigInt(Math.floor(Date.now() / 1000));
const createInstruction = getCreateDvpInstruction({
payer: payerSigner,
swapDvp,
nonceTombstone: (
await getProgramDerivedAddress({
programAddress: DVP_SWAP_PROGRAM_PROGRAM_ADDRESS,
seeds: ["nonce", getAddressEncoder().encode(swapDvp)],
})
)[0],
settlementAuthority,
userA,
userB,
mintA,
mintB,
dvpAtaA: escrowA,
dvpAtaB: escrowB,
tokenProgramA,
tokenProgramB,
amountA: 1_000_000_000n, // 1,000 units of a 6-decimal asset
amountB: 998_500_000n, // 998.50 units of a 6-decimal cash token
expiryTimestamp: nowSeconds + 60n * 60n * 24n, // one day
nonce,
refString: "ORDER-2026-10-02-0001",
userASettlementDestination: null, // defaults to userA
userBSettlementDestination: null, // defaults to userB
earliestSettlementTimestamp: null,
});
const { context } = await client.sendTransaction([createInstruction]);
console.log("Trade record:", swapDvp, "signature:", context.signature);

Для TypeScript-фрагмента в області видимості мають бути getProgramDerivedAddress і getAddressEncoder з @solana/kit, а також DVP_SWAP_PROGRAM_PROGRAM_ADDRESS з клієнта. Білдер Rust залишає System Program та Associated Token Program зі значеннями за замовчуванням.

Примітки

  • Термін дії. Лише Settle перевіряє термін дії. Reclaim, Cancel і Reject працюють і після його завершення, а депозит, що надійшов після завершення терміну, може повернути сторона відповідної частини угоди. Термін дії може становити щонайбільше один рік від створення.
  • Час — це час мережі. Програма зчитує ончейн-годинник, який може відрізнятися від звичайного часу, подекуди на десятки секунд, і може стрибнути вперед. Залишайте запас між терміном дії та запланованим часом розрахунку, не поповнюйте й не розраховуйте поблизу жодної з меж і не покладайтеся на термін дії як на дедлайн з точністю до секунди. У README програми радять закладати близько хвилини розбіжності.
  • Найраніший розрахунок. Необов’язково. Якщо вказано, Settle відхиляється до цього часу, і він не має бути пізнішим за термін дії.
  • Перевірки під час створення. Створення завершується помилкою PartyNotSignerCapable, якщо сторона не є акаунтом, що належить системі (це виключає мультипідпис SPL Token), а також SettlementDestinationIsSwapDvp, SelfDvp, SameMint, ZeroAmount, SettlementAuthorityIsParty або ExpiryNotInFuture. Повний перелік наведено в таблиці помилок.

Наступні кроки

Is this page helpful?

Зміст

Редагувати сторінку