Descripción General
Retirar fondos de Private Channels es un proceso de dos pasos. Primero, el usuario quema
su saldo de tokens del lado del canal llamando a WithdrawFunds en el Programa de
Retiro. Segundo, un operador llama a ReleaseFunds en el Programa de Custodia (devnet,
en este recorrido) con una prueba de exclusión de Sparse Merkle Tree válida para liberar
los fondos. La prueba SMT garantiza que cada nonce de retiro solo pueda usarse una vez,
previendo el doble gasto.
Paso 1: Iniciar el Retiro en el Canal
Llama a WithdrawFunds en el Programa de Retiro para quemar tu saldo del lado del canal.
Usa la variante Async, que deriva automáticamente el PDA de tokenAccount y es la
forma recomendada para uso en producción:
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 del Programa de Retiro:
J231K9UEpS4y4KAPwGc4gsMNCjKFRMYcQBcjVW7vBhVi - Envía esta transacción al gateway (
http://localhost:8899), no a un RPC público de Solana - Si se proporciona
destination, los fondos liberados se envían a esa billetera en lugar de la del firmante
Qué Esperar
No se requiere ninguna acción adicional después de llamar a WithdrawFunds. Los servicios
del operador gestionan la liquidación automáticamente:
indexer-private-channelconsulta el canal cada 1 segundo en busca de eventos de quema y registra un retiro pendiente cuando se detecta el tuyooperator-private-channelconsulta la base de datos cada 1 segundo en busca de registros pendientes y envíaReleaseFundsal Programa de Custodia en Solana devnet con la prueba de exclusión SMT requerida; consulta la confirmación en devnet hasta 5 veces en intervalos de 400 ms antes de reintentar
Los fondos suelen aparecer en tu billetera de devnet en pocos segundos en condiciones normales.
Cómo Liquida el Operador (Referencia)
Tras la quema del lado del canal, un operador aprovisionado debe llamar a ReleaseFunds en
el Programa de Custodia con una prueba de exclusión SMT válida; el operador no puede liberar
fondos sin demostrar que el nonce de retiro no ha sido utilizado anteriormente. Este paso
es gestionado automáticamente por operator-private-channel; no necesitas llamarlo
tú mismo.
El operador proporciona:
amount: debe coincidir con el monto quemadouser: billetera receptora en devnetnew_withdrawal_root: raíz SMT actualizada tras este retirotransaction_nonce: nonce único para esta hojasibling_proofs: 512 bytes (16 hashes de 32 bytes de nodos hermanos para la prueba del árbol)
Verificar
Una vez confirmada la llamada del operador, comprueba tu saldo de tokens en devnet:
spl-token balance <MINT_ADDRESS> --owner <YOUR_WALLET>
O mediante 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);
Has Completado el Inicio Rápido
Has ejecutado el ciclo completo de Private Channels en devnet: depositaste tokens SPL en el contrato de custodia, enviaste una transferencia fuera de cadena a través del gateway y retiraste fondos de vuelta a tu billetera de devnet.
Ciclo de Vida del Canal
Comprende en profundidad a los participantes y el flujo completo desde el depósito hasta el retiro.
Autenticación y Roles
Añade autenticación JWT a tu integración.
Referencia de Liberación de Fondos
Referencia completa de la instrucción ReleaseFunds para las herramientas del operador.
Sparse Merkle Tree
Comprende cómo las pruebas de retiro previenen el doble gasto.
Is this page helpful?