CreateDvp записує умови угоди та створює два ескроу-акаунти, які сторони поповнять. Токени він не переміщує. Надіслати його може будь-хто, тож запис може надійти від контрагента, оператора розрахункового органу або третьої особи, а кожен інший учасник перевіряє збережені умови, перш ніж діяти на їхній основі (див.
поповнення частин угоди).
Фрагменти коду напряму використовують згенерований клієнт із фіктивними адресами, щоб було видно задіяні акаунти та підписувачів. Вони націлені на devnet, де програма розгорнута за тією самою адресою, що й у mainnet-beta.
Встановлення
Станом на 2 жовтня 2026 року пакет для клієнтів DvP не опубліковано. Обидва клієнти генеруються з IDL програми в репозиторії та використовуються з вихідного коду.
git clone https://github.com/solana-foundation/dvp.gitcd dvp && git checkout df9919ed02c25a93620e7f6820107c9050ce3b92pnpm install && pnpm generate-clients && pnpm build# The client is clients/typescript/src; copy or link it into your project as ./dvppnpm 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, виведена з розрахункового органу, двох сторін, двох мінтів і нонса. Кожне ескроу — це associated token account запису для одного з мінтів. Обидва виведення не потребують доступу до мережі.
Використовуйте випадковий 64-бітний нонс для кожної угоди. Оскільки створення не потребує дозволу, передбачуваний нонс дає змогу третій особі зайняти адресу, яку мала використати угода. Передавайте нонс і суми як 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 приймає дві суми в базових одиницях, термін дії, нонс і чотири необов'язкові поля: непрозорий референс до 64 байт, адресу розрахунку для кожної сторони та найраніший час розрахунку (див. Примітки). Адреса розрахунку — це місце, куди надходять кошти цієї сторони під час розрахунку, наприклад депозитний гаманець кастодіана; за замовчуванням це власна адреса сторони, і після створення запису її не можна змінити. Повернення та вилучення завжди йдуть на власний token account сторони для мінта відповідної частини, а не на адресу розрахунку, хоч би хто поповнив ескроу.
Платник сплачує rent за запис, «надгробок» нонса та обидва ескроу. Під час закриття цей 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 assetamountB: 998_500_000n, // 998.50 units of a 6-decimal cash tokenexpiryTimestamp: nowSeconds + 60n * 60n * 24n, // one daynonce,refString: "ORDER-2026-10-02-0001",userASettlementDestination: null, // defaults to userAuserBSettlementDestination: null, // defaults to userBearliestSettlementTimestamp: 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 працюють і після його спливу, а депозит, що надійшов після спливу терміну, сторона відповідної частини може повернути. Термін дії може становити щонайбільше один рік від моменту створення.
- Час — це час мережі. Програма читає onchain-годинник, який може відрізнятися від звичайного часу, іноді на десятки секунд, і може стрибати вперед. Залишайте запас між терміном дії та запланованим часом розрахунку, уникайте поповнення чи розрахунку поблизу будь-якої з меж і не покладайтеся на термін дії для дотримання дедлайну з точністю до секунди. README програми радить закладати приблизно хвилину розбіжності.
- Найраніший розрахунок. Необов'язково. Якщо задано, Settle відхиляється до цього моменту, і він не має бути пізнішим за термін дії.
- Перевірки під час створення. Create завершується помилкою
PartyNotSignerCapable, якщо сторона не є акаунтом, що належить System Program (це виключає мультипідпис SPL Token), а такожSettlementDestinationIsSwapDvp,SelfDvp,SameMint,ZeroAmount,SettlementAuthorityIsPartyабоExpiryNotInFuture. Повний перелік наведено в таблиці помилок.
Наступні кроки
- Поповнення частин угоди: кожна сторона переказує свою частину в ескроу угоди
Is this page helpful?