Retirar Fondos

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 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);
  • 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:

  1. indexer-private-channel consulta el canal cada 1 segundo en busca de eventos de quema y registra un retiro pendiente cuando se detecta el tuyo
  2. operator-private-channel consulta la base de datos cada 1 segundo en busca de registros pendientes y envía ReleaseFunds al 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 quemado
  • user: billetera receptora en devnet
  • new_withdrawal_root: raíz SMT actualizada tras este retiro
  • transaction_nonce: nonce único para esta hoja
  • sibling_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 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);

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.

Is this page helpful?

Tabla de Contenidos

Editar Página