Виведення коштів

Огляд

Виведення коштів з Private Channels — це двоетапний процес. Спочатку користувач спалює свій баланс токенів на стороні каналу, викликаючи WithdrawFunds у програмі Withdraw Program. Потім оператор викликає ReleaseFunds у програмі Escrow Program (devnet, у цьому покроковому керівництві) з дійсним доказом виключення Sparse Merkle Tree для вивільнення коштів. Доказ SMT гарантує, що кожен одноразовий код виведення може бути використаний лише один раз, запобігаючи подвійним витратам.

Крок 1: Ініціювання виведення в каналі

Викличте WithdrawFunds у програмі Withdraw Program, щоб спалити ваш баланс на стороні каналу. Використовуйте варіант Async, який автоматично визначає PDA tokenAccount і є рекомендованою формою для використання в продакшені:

import { getWithdrawFundsInstructionAsync } from "../private-channel-withdraw-program/clients/typescript/src/generated";
import {
createSolanaRpc,
address,
pipe,
createTransactionMessage,
setTransactionMessageFeePayerSigner,
setTransactionMessageLifetimeUsingBlockhash,
appendTransactionMessageInstruction,
signAndSendTransactionMessageWithSigners,
assertIsTransactionMessageWithSingleSendingSigner,
getBase58Decoder
} from "@solana/kit";
// Point to the gateway, not a public Solana RPC
const privateChannelRpc = createSolanaRpc("http://localhost:8899");
const withdrawIx = await getWithdrawFundsInstructionAsync({
user: userSigner,
mint: address(mintAddress),
amount: 1_000_000n, // 1 USDC (6 decimals)
destination: null // null = release to signer's devnet wallet
});
const { value: latestBlockhash } = await privateChannelRpc
.getLatestBlockhash({ commitment: "confirmed" })
.send();
// Sent to the Private Channels gateway (not Solana RPC directly), which
// expects the legacy transaction format - do not switch this to version 0.
const transactionMessage = pipe(
createTransactionMessage({ version: "legacy" }),
(m) => setTransactionMessageFeePayerSigner(userSigner, m),
(m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),
(m) => appendTransactionMessageInstruction(withdrawIx, m)
);
assertIsTransactionMessageWithSingleSendingSigner(transactionMessage);
const signatureBytes =
await signAndSendTransactionMessageWithSigners(transactionMessage);
const signature = getBase58Decoder().decode(signatureBytes);
console.log("Withdrawal initiated:", signature);
  • Ідентифікатор програми Withdraw Program: J231K9UEpS4y4KAPwGc4gsMNCjKFRMYcQBcjVW7vBhVi
  • Надішліть цю транзакцію до шлюзу (http://localhost:8899), а не до публічного RPC Solana
  • Якщо вказано destination, вивільнені кошти надходять до цього гаманця замість гаманця підписанта

Чого очікувати

Після виклику WithdrawFunds жодних додаткових дій не потрібно. Оператор автоматично обробляє розрахунки:

  1. indexer-private-channel опитує канал кожну 1 секунду на предмет подій спалення та записує запис про очікуване виведення, коли ваше буде виявлено
  2. operator-private-channel опитує базу даних кожну 1 секунду на предмет очікуваних записів і надсилає ReleaseFunds до програми Escrow Program на Solana devnet з необхідним доказом виключення SMT; він опитує devnet для підтвердження до 5 разів з інтервалом 400 мс перед повторною спробою

Зазвичай кошти з'являються у вашому гаманці devnet протягом кількох секунд за нормальних умов.

Як оператор здійснює розрахунки (Довідка)

Після спалення на стороні каналу підготовлений оператор повинен викликати ReleaseFunds у програмі Escrow Program з дійсним доказом виключення SMT; оператор не може вивільнити кошти без підтвердження того, що одноразовий код виведення не використовувався раніше. Цей крок виконується автоматично за допомогою operator-private-channel; вам не потрібно викликати його самостійно.

Оператор надає:

  • amount: має збігатися зі спаленою сумою
  • user: гаманець одержувача на devnet
  • new_withdrawal_root: оновлений корінь SMT після цього виведення
  • transaction_nonce: унікальний одноразовий код для цього листа
  • sibling_proofs: 512 байт (16 x 32-байтові хеші сусідніх вузлів для доказу дерева)

Перевірка

Після підтвердження виклику оператора перевірте ваш баланс токенів на devnet:

spl-token balance <MINT_ADDRESS> --owner <YOUR_WALLET>

Або через RPC:

import { createSolanaRpc, address } from "@solana/kit";
import { findAssociatedTokenPda } from "@solana-program/token";
const TOKEN_PROGRAM_ADDRESS = address(
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
);
// Query devnet directly; ReleaseFunds settles here, not through the gateway
const rpc = createSolanaRpc("https://api.devnet.solana.com");
const [yourDevnetAta] = await findAssociatedTokenPda({
mint: address(mintAddress),
owner: address(userSigner.address),
tokenProgram: TOKEN_PROGRAM_ADDRESS
});
const balance = await rpc.getTokenAccountBalance(yourDevnetAta).send();
console.log(balance.value.uiAmountString);

Ви завершили швидкий старт

Ви виконали повний цикл Private Channels на devnet: внесли SPL-токени до ескроу, здійснили позамережевий переказ через шлюз і вивели кошти назад до вашого гаманця devnet.

Is this page helpful?