Aperçu
Le retrait depuis Private Channels est un processus en deux étapes. Premièrement, l'utilisateur brûle
son solde de tokens côté canal en appelant WithdrawFunds sur le Withdraw
Program. Deuxièmement, un opérateur appelle ReleaseFunds sur l'Escrow Program (devnet,
dans cette procédure) avec une preuve d'exclusion Sparse Merkle Tree valide pour libérer
les fonds. La preuve SMT garantit que chaque nonce de retrait ne peut être utilisé qu'une seule fois,
empêchant ainsi la double dépense.
Étape 1 : Initier le retrait sur le canal
Appelez WithdrawFunds sur le Withdraw Program pour brûler votre solde côté canal.
Utilisez la variante Async, qui dérive automatiquement le PDA tokenAccount et constitue la
forme recommandée pour une utilisation en production :
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 RPCconst 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);
- ID du Withdraw Program :
J231K9UEpS4y4KAPwGc4gsMNCjKFRMYcQBcjVW7vBhVi - Soumettez cette transaction à la passerelle (
http://localhost:8899), et non à un RPC Solana public - Si
destinationest fourni, les fonds libérés sont envoyés à ce portefeuille plutôt qu'à celui du signataire
À quoi s'attendre
Aucune action supplémentaire n'est requise après l'appel à WithdrawFunds. Les services
opérateurs gèrent le règlement automatiquement :
indexer-private-channelinterroge le canal toutes les secondes à la recherche d'événements de brûlage et enregistre une entrée de retrait en attente lorsque le vôtre est détectéoperator-private-channelinterroge la base de données toutes les secondes pour les entrées en attente et soumetReleaseFundsà l'Escrow Program sur Solana devnet avec la preuve d'exclusion SMT requise ; il interroge la confirmation devnet jusqu'à 5 fois à des intervalles de 400 ms avant de réessayer
Les fonds apparaissent généralement dans votre portefeuille devnet en quelques secondes dans des conditions normales.
Comment l'opérateur règle les fonds (Référence)
Après le brûlage côté canal, un opérateur provisionné doit appeler ReleaseFunds sur
l'Escrow Program avec une preuve d'exclusion SMT valide ; l'opérateur ne peut pas libérer
les fonds sans prouver que le nonce de retrait n'a pas encore été utilisé. Cette étape
est gérée automatiquement par operator-private-channel ; vous n'avez pas besoin de l'appeler
vous-même.
L'opérateur fournit :
amount: doit correspondre au montant brûléuser: portefeuille destinataire sur devnetnew_withdrawal_root: racine SMT mise à jour après ce retraittransaction_nonce: nonce unique pour cette feuillesibling_proofs: 512 octets (16 hachages de nœuds frères de 32 octets pour la preuve d'arbre)
Vérifier
Une fois l'appel de l'opérateur confirmé, vérifiez votre solde de tokens devnet :
spl-token balance <MINT_ADDRESS> --owner <YOUR_WALLET>
Ou via 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 gatewayconst 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);
Vous avez terminé le Démarrage rapide
Vous avez exécuté le cycle complet de Private Channels sur devnet : déposé des tokens SPL dans l'escrow, envoyé un transfert hors chaîne via la passerelle, et retiré des fonds vers votre portefeuille devnet.
Cycle de vie du canal
Comprenez en détail les participants et le flux complet du dépôt au retrait.
Authentification & Rôles
Ajoutez l'authentification JWT à votre intégration.
Référence Release Funds
Référence complète de l'instruction ReleaseFunds pour les outils opérateurs.
Sparse Merkle Tree
Comprenez comment les preuves de retrait empêchent la double dépense.
Is this page helpful?