資金の引き出し

概要

Private Channelsからの引き出しは2ステップのプロセスです。まず、ユーザーはWithdrawプログラムのWithdrawFundsを呼び出して、チャネル側のトークン残高をバーンします。次に、オペレーターがEscrowプログラム(このウォークスルーではdevnet)のReleaseFundsを有効なSparse Merkle Tree除外証明とともに呼び出して資金を解放します。SMT証明により、各引き出しノンスは一度しか使用できず、二重支払いを防ぎます。

ステップ1:チャネルで引き出しを開始する

WithdrawプログラムのWithdrawFundsを呼び出して、チャネル側の残高をバーンします。tokenAccount PDAを自動導出し、本番環境での使用に推奨されるAsyncバリアントを使用してください:

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 ID: J231K9UEpS4y4KAPwGc4gsMNCjKFRMYcQBcjVW7vBhVi
  • このトランザクションは公開SolanaRPCではなく、ゲートウェイ(http://localhost:8899)に送信してください
  • destinationが指定されている場合、解放された資金は署名者のウォレットではなく、そのウォレットに送られます

動作の説明

WithdrawFundsを呼び出した後、追加のアクションは不要です。オペレーターサービスが自動的に決済を処理します:

  1. indexer-private-channelは1秒ごとにチャネルをポーリングしてバーンイベントを検出し、検出されると保留中の引き出しレコードをデータベースに書き込みます
  2. operator-private-channelは1秒ごとにデータベースをポーリングして保留中のレコードを確認し、必要なSMT除外証明とともにSolana devnet上のEscrowプログラムにReleaseFundsを送信します。400msの間隔で最大5回devnetの確認をポーリングしてからリトライします

通常の状況では、資金は数秒以内にdevnetウォレットに反映されます。

オペレーターの決済方法(リファレンス)

チャネル側のバーン後、プロビジョニングされたオペレーターは有効なSMT除外証明とともにEscrowプログラムのReleaseFundsを呼び出す必要があります。オペレーターは引き出しノンスが以前に使用されていないことを証明しなければ資金を解放できません。このステップはoperator-private-channelによって自動的に処理されるため、ご自身で呼び出す必要はありません。

オペレーターが提供する情報:

  • amount:バーンされた金額と一致する必要があります
  • user:devnet上の受取ウォレット
  • new_withdrawal_root:この引き出し後に更新されたSMTルート
  • transaction_nonce:このリーフ固有のノンス
  • sibling_proofs:512バイト(ツリー証明用の16×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);

クイックスタートの完了

devnet上でPrivate Channelsの全サイクルを実行しました:エスクローにSPLトークンを預け入れ、ゲートウェイを通じてオフチェーン転送を送信し、devnetウォレットに資金を引き出しました。

Is this page helpful?

目次

ページを編集
© 2026 Solana Foundation. 無断転載を禁じます。