> ## Documentation Index
> Fetch the complete documentation index at: https://www.helius.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Transferencia

> Guía para transferir activos SOL, SPL o Token 2022 entre saldos privados, con un ejemplo de código completo.

1. Una transferencia privada mueve tokens en un Ring entre billeteras privadas.
2. Las transferencias privadas se envían en una sola transacción de Solana a la dirección de una billetera de Solana.

#### Transferencia: qué es privado

<Tabs>
  <Tab title="Permissionless Confidential Ring">
    | Field                 | Visibility | Why                                                                  |
    | --------------------- | ---------- | -------------------------------------------------------------------- |
    | Asset                 | Private    | The asset is encrypted onchain                                       |
    | Amount                | Private    | The transferred amount is encrypted onchain                          |
    | Source private wallet | Public     | In a confidential Ring the source private wallet is visible onchain. |
    | Recipient             | Public     | In a confidential Ring the recipient is visible onchain.             |
  </Tab>

  <Tab title="Custom Rings">
    Transfers from and within a ring reveal the program ID of the custom Ring.

    A balance in a Custom Ring can exit to an SPL token account, to the Default Ring, or to another Ring, as long as the source Ring's policy permits it.
    The default Ring is permissionless and does not have a policy or authority.

    One transaction can combine balances from the Default Ring and one Custom Ring. A transfer between two Custom Rings routes through the Default Ring.

    | Private transfer       | Custom confidential Rings | Custom anonymous Rings                                                                                      | Default confidential to or from Custom confidential | Confidential (Default or Custom) to Custom anonymous | Custom anonymous to confidential (Default or Custom)                                                        |
    | ---------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
    | Amount                 | Private                   | Private                                                                                                     | Private                                             | Private                                              | Private                                                                                                     |
    | Asset                  | Private                   | Private                                                                                                     | Private                                             | Private                                              | Private                                                                                                     |
    | Source private wallet  | Public                    | Private. A relayer submits the transaction, so the public ledger does not reveal the source private wallet. | Public                                              | Public                                               | Private. A relayer submits the transaction, so the public ledger does not reveal the source private wallet. |
    | Recipient              | Public                    | Private                                                                                                     | Public                                              | Private                                              | Public                                                                                                      |
    | Custom Ring program ID | Public                    | Public                                                                                                      | Public                                              | Public                                               | Public                                                                                                      |
  </Tab>
</Tabs>

<Info>
  El Ring sin permisos es confidencial y cifra el importe y el activo.
  Un Ring personalizado puede configurarse como confidencial o anónimo (remitente, destinatario, activo e importe cifrados).
</Info>

## Cómo funciona una transferencia

Una transferencia privada funciona de manera similar a una transferencia pública de Solana:

1. El saldo de SOL o SPL del usuario se cifra onchain.

2. El usuario descifra el estado privado, la billetera crea una transferencia y el propietario firma.

   * Obtén el estado cifrado con métodos RPC específicos. Solo el usuario puede descifrar los saldos de forma local.
   * La billetera establece el importe y el destinatario y, luego, solicita una prueba ZK. De forma predeterminada, el proveedor de RPC genera la prueba ZK y la devuelve.

3. El entorno de ejecución de Solana verifica las firmas e invoca el Programa de privacidad de Solana, que verifica la prueba ZK sin revelar el estado cifrado.

4. La aplicación consulta el estado mediante el hash de la transacción de Solana.

```mermaid theme={"system"}
%%{init: {
  'theme': 'base',
  'themeVariables': {
    'lineColor':           '#FF6B35',
    'primaryTextColor':    '#737373',
    'primaryBorderColor':  '#9CA3AF',
    'actorBkg':            '#FFFFFF',
    'actorBorder':         '#9CA3AF',
    'actorTextColor':      '#737373',
    'signalColor':         '#FF6B35',
    'signalTextColor':     '#737373',
    'labelBoxBkgColor':    '#FF6B351F',
    'labelBoxBorderColor': '#FF6B35',
    'noteBkgColor':        '#F5F5F5',
    'noteTextColor':       '#737373',
    'noteBorderColor':     '#9CA3AF'
  }
}}%%
sequenceDiagram
    participant Wallet
    participant RPC as RPC Provider
    participant Solana

    Wallet->>RPC: Fetch encrypted state
    RPC-->>Wallet: Encrypted state
    Note over Wallet: Decrypt

    Note over Wallet: Set amount and recipient
    Wallet->>RPC: Request ZK proof
    Note over RPC: Generate ZK proof
    RPC-->>Wallet: ZK proof
    Note over Wallet: Build transaction, sign

    Wallet->>RPC: Submit Transaction
    RPC->>Solana: Forward transaction
    Note over Solana: Verify signatures
    Note over Solana: CPI Solana Privacy Program
    Note over Solana: Verify ZK proof
    RPC-->>Wallet: Transaction signature
```

<Accordion title="Compare to Solana Transfer">
  1. El saldo de SOL o SPL del usuario es público onchain.
  2. La billetera lee el estado público, crea una transferencia y el propietario firma.
  3. El entorno de ejecución de Solana verifica las firmas e invoca el Programa del sistema o el Programa de tokens, que actualiza el saldo público.
  4. La aplicación consulta el estado mediante el hash de la transacción de Solana.

  ```mermaid theme={"system"}
  %%{init: {
    'theme': 'base',
    'themeVariables': {
      'lineColor':           '#FF6B35',
      'primaryTextColor':    '#737373',
      'primaryBorderColor':  '#9CA3AF',
      'actorBkg':            '#FFFFFF',
      'actorBorder':         '#9CA3AF',
      'actorTextColor':      '#737373',
      'signalColor':         '#FF6B35',
      'signalTextColor':     '#737373',
      'labelBoxBkgColor':    '#FF6B351F',
      'labelBoxBorderColor': '#FF6B35',
      'noteBkgColor':        '#F5F5F5',
      'noteTextColor':       '#737373',
      'noteBorderColor':     '#9CA3AF'
    }
  }}%%
  sequenceDiagram
      participant Wallet
      participant RPC
      participant Solana

      Wallet->>RPC: Get public balance
      RPC-->>Wallet: Public state
      Note over Wallet: Build transfer, owner signs
      Wallet->>RPC: sendTransaction
      RPC->>Solana: Forward transaction
      Note over Solana: Verify signatures
      Note over Solana: CPI System / Token Program
      Note over Solana: Update balance
      RPC-->>Wallet: Transaction signature
  ```
</Accordion>

<Info>
  Este es el flujo de transacción de alto nivel del Ring confidencial sin permisos.
  Compáralo con los Rings personalizados en [conceptos](/docs/es/privacy/concepts#flujo-general-de-una-transacción).
</Info>

## Comienza

<Tabs>
  <Tab title="TypeScript Client">
    <Steps>
      <Step>
        ### Requisitos previos

        <Info>
          The TypeScript examples require Node.js 24 or later, pnpm 11.18.0, and the Solana CLI.
        </Info>

        ```bash theme={"system"}
        pnpm add 'git+ssh://git@github.com/helius-labs/zolana.git#v0.3.0-alpha&path:/sdk-libs/ts' @solana/kit@^8.3.0
        ```

        Source: [sdk-libs/ts](https://github.com/helius-labs/zolana/tree/v0.3.0-alpha/sdk-libs/ts)

        <Accordion title="Connect to Endpoints">
          <Tabs>
            <Tab title="Devnet">
              ```bash theme={"system"}
              pnpm install
              cp .env.example .env
              ```

              Add a [Helius API key](https://dashboard.helius.dev/):

              ```bash .env theme={"system"}
              API_KEY=YOUR_API_KEY
              ZOLANA_PAYER_KEYPAIR=~/.config/solana/id.json
              ```

              ```ts theme={"system"}
              import { createZolanaClient } from "@heliuslabs/zolana";

              const client = await createZolanaClient({
                solanaRpcUrl: "https://devnet.helius-rpc.com/?api-key=YOUR_API_KEY",
                indexerUrl: "https://d2xah7tnhdhcom.cloudfront.net",
                proverUrl: "https://d21ni15goiip6l.cloudfront.net",
              });
              ```

              The examples use the Solana CLI wallet as the payer by default. The payer must hold devnet SOL. See [How to Get Devnet SOL](/docs/rpc/devnet-sol).
            </Tab>

            <Tab title="Localnet">
              On localnet the SDK starts the local test validator (`:8899`), Photon indexer (`:8784`), and prover (`:3001`), and the
              client connects to them automatically without needing endpoint configuration.

              ```bash theme={"system"}
              cargo install --git https://github.com/helius-labs/zolana --tag v0.3.0-alpha zolana-cli
              zolana dev start
              ```

              ```ts theme={"system"}
              import { createZolanaClient } from "@heliuslabs/zolana";

              const client = await createZolanaClient({});
              ```
            </Tab>
          </Tabs>
        </Accordion>
      </Step>

      <Step>
        ### Transferir a un saldo privado

        <Accordion title="Solana Kit send helper">
          ```typescript theme={"system"}
          import {
            appendTransactionMessageInstructions,
            assertIsTransactionWithBlockhashLifetime,
            createTransactionMessage,
            getSignatureFromTransaction,
            pipe,
            sendTransactionWithoutConfirmingFactory,
            setTransactionMessageConfig,
            setTransactionMessageFeePayerSigner,
            setTransactionMessageLifetimeUsingBlockhash,
            signTransactionMessageWithSigners,
            type Instruction,
            type Signature,
            type TransactionSigner,
          } from "@solana/kit";
          import { createZolanaClient } from "@heliuslabs/zolana";

          type Client = Awaited<ReturnType<typeof createZolanaClient>>;

          export interface ConfirmedTransaction {
            readonly signature: Signature;
            readonly slot: bigint;
          }

          export function sendAndConfirmFactory(
            client: Client,
            feePayer: TransactionSigner,
          ): (instructions: readonly Instruction[]) => Promise<ConfirmedTransaction> {
            const sendTransaction = sendTransactionWithoutConfirmingFactory({
              rpc: client.solanaRpc,
            });

            return async function sendAndConfirm(
              instructions: readonly Instruction[],
            ): Promise<ConfirmedTransaction> {
              const { value: lifetime } = await client.solanaRpc
                .getLatestBlockhash()
                .send();
              const signed = await signTransactionMessageWithSigners(
                pipe(
                  createTransactionMessage({ version: 1 }),
                  (message) => setTransactionMessageFeePayerSigner(feePayer, message),
                  (message) =>
                    setTransactionMessageLifetimeUsingBlockhash(lifetime, message),
                  (message) =>
                    setTransactionMessageConfig(
                      {
                        computeUnitLimit: 450_000,
                        loadedAccountsDataSizeLimit: 64 * 1024 * 1024,
                      },
                      message,
                    ),
                  (message) =>
                    appendTransactionMessageInstructions(instructions, message),
                ),
              );
              assertIsTransactionWithBlockhashLifetime(signed);
              await sendTransaction(signed, { commitment: "confirmed" });
              const signature = getSignatureFromTransaction(signed);
              const slot = await client.confirmTransaction(signature);
              return { signature, slot };
            };
          }
          ```

          * El SDK devuelve instrucciones. La aplicación las firma y las envía.
          * `sendAndConfirmFactory` crea una transacción de Kit, la envía y devuelve la firma junto con el slot en el que se confirmó.
        </Accordion>

        <Tabs>
          <Tab title="SOL">
            ```typescript theme={"system"}
            import { LocalKeys } from "@heliuslabs/zolana/client";
            import { SOL_MINT } from "@heliuslabs/zolana";
            import { transactInstruction } from "@heliuslabs/zolana/interface";
            import {
              ConfidentialTransfer,
              ProofInputUtxo,
            } from "@heliuslabs/zolana/transaction";
            import { sendAndConfirmFactory } from "../src/lib.js";


              const sendAndConfirm = sendAndConfirmFactory(
                client,
                senderSigner,
              );
              const transferUtxo = depositBalance.utxos[0]!;
              const transferInput =
                ProofInputUtxo.fromKeypair(
                  transferUtxo,
                  sender,
                );
              const transfer = new ConfidentialTransfer(
                senderAddress,
                [transferInput],
                senderSigner.address,
              );
              transfer.send(
                recipient.shieldedAddress(),
                SOL_MINT,
                TRANSFER_AMOUNT,
              );
              const transferProofInputs = transfer.sign(
                sender,
                assets,
              );
              const senderKeys = LocalKeys.fromKeypair(sender, client.proofService);
              const transferData = await client.proveTransact(
                transferProofInputs,
                senderKeys,
              );
              const transferInstruction = await transactInstruction(
                {
                  payer: senderSigner,
                  inputTree: client.tree,
                  outputTree: client.tree,
                  data: transferData,
                },
              );
              const transferTx = await sendAndConfirm([
                transferInstruction,
              ]);
            ```
          </Tab>

          <Tab title="SPL">
            ```typescript theme={"system"}
            import { LocalKeys } from "@heliuslabs/zolana/client";
            import { transactInstruction } from "@heliuslabs/zolana/interface";
            import {
              ConfidentialTransfer,
              ProofInputUtxo,
            } from "@heliuslabs/zolana/transaction";
            import { sendAndConfirmFactory } from "../src/lib.js";


              const sendAndConfirm = sendAndConfirmFactory(
                client,
                senderSigner,
              );
              const transferUtxo = depositBalance.utxos[0]!;
              const transferInput =
                ProofInputUtxo.fromKeypair(
                  transferUtxo,
                  sender,
                );
              const transfer = new ConfidentialTransfer(
                senderAddress,
                [transferInput],
                senderSigner.address,
              );
              transfer.send(
                recipient.shieldedAddress(),
                spl.mint,
                TRANSFER_AMOUNT,
              );
              const transferProofInputs = transfer.sign(
                sender,
                assets,
              );
              const senderKeys = LocalKeys.fromKeypair(sender, client.proofService);
              const transferData = await client.proveTransact(
                transferProofInputs,
                senderKeys,
              );
              const transferInstruction = await transactInstruction(
                {
                  payer: senderSigner,
                  inputTree: client.tree,
                  outputTree: client.tree,
                  data: transferData,
                },
              );
              const transferTx = await sendAndConfirm([
                transferInstruction,
              ]);
            ```
          </Tab>
        </Tabs>

        <AccordionGroup>
          <Accordion title="1. Select private token accounts to spend">
            ```typescript theme={"system"}
            import { SOL_MINT } from "@heliuslabs/zolana";

            const transferUtxo = depositBalance.utxos[0]!;
            ```

            * El ejemplo gasta la cuenta privada de tokens de Solana creada por el depósito anterior. Una transferencia puede gastar varios UTXO.
            * `transferUtxo` selecciona una cuenta privada de tokens de Solana de ese saldo.
          </Accordion>

          <Accordion title="2. Prepare proof inputs">
            ```typescript theme={"system"}
            import { ProofInputUtxo } from "@heliuslabs/zolana/transaction";

            const transferInput =
              ProofInputUtxo.fromKeypair(
                transferUtxo,
                sender,
              );
            ```

            * `ProofInputUtxo.fromKeypair` prepara el UTXO seleccionado como entrada de la prueba con el par de claves de la billetera privada del remitente.
            * El par de claves deriva el anulador que marca el UTXO de entrada como gastado, mientras el activo y el importe permanecen cifrados.
          </Accordion>

          <Accordion title="3. Build and sign the confidential transfer">
            ```typescript theme={"system"}
            import { SOL_MINT } from "@heliuslabs/zolana";
            import { ConfidentialTransfer } from "@heliuslabs/zolana/transaction";

            const transfer = new ConfidentialTransfer(
              senderAddress,
              [transferInput],
              senderSigner.address,
            );
            transfer.send(
              recipient.shieldedAddress(),
              SOL_MINT,
              TRANSFER_AMOUNT,
            );
            const transferProofInputs = transfer.sign(
              sender,
              assets,
            );
            ```

            * `senderAddress` es la <Tooltip tip="The public key bundle (signing, nullifier, and viewing keys) published in a user's registry record. Not an onchain address.">dirección protegida</Tooltip> del remitente. La transferencia gasta fondos de esta billetera.
            * `[transferInput]` enumera los UTXO seleccionados del remitente. Una transferencia puede gastar varios UTXO.
            * `senderSigner.address` es la dirección de Solana del pagador de la comisión. Un patrocinador de gas puede pagar la comisión.
            * `recipient.shieldedAddress()` es la dirección protegida del destinatario. La salida transferida se cifra con la clave de visualización del destinatario.
            * `SOL_MINT` selecciona SOL. Para una transferencia de SPL o Token 2022, pasa la emisión del token.
            * `TRANSFER_AMOUNT` se expresa en las unidades base del activo. SOL usa lamports. Los activos SPL y Token 2022 usan las unidades base del token.
            * `transfer.sign` autoriza la transición de estado, cifra el activo y el importe, y produce las entradas para el generador de pruebas de conocimiento cero.
            * `assets` es el registro de activos que se usa para resolver los activos privados compatibles.
          </Accordion>

          <Accordion title="4. Fetch the zero-knowledge proof">
            ```typescript theme={"system"}
            import { LocalKeys } from "@heliuslabs/zolana/client";

            const senderKeys = LocalKeys.fromKeypair(sender, client.proofService);
            const transferData = await client.proveTransact(
              transferProofInputs,
              senderKeys,
            );
            ```

            * `senderKeys` usa `LocalKeys.fromKeypair(sender, client.proofService)` para autorizar la generación de la prueba con las claves del remitente.
            * `client.proveTransact` genera la prueba de conocimiento cero a partir de la transferencia firmada y devuelve los datos serializados de la instrucción.
            * La prueba demuestra que el remitente posee las entradas y puede gastarlas sin revelar el activo ni el importe cifrados.
          </Accordion>

          <Accordion title="5. Build the transfer instruction">
            ```typescript theme={"system"}
            import { transactInstruction } from "@heliuslabs/zolana/interface";

            const transferInstruction = await transactInstruction(
              {
                payer: senderSigner,
                inputTree: client.tree,
                outputTree: client.tree,
                data: transferData,
              },
            );
            ```

            * `payer` firma y paga la transacción de Solana. Un patrocinador de gas puede pagar la comisión.
            * `inputTree` e `outputTree` son `client.tree`, el árbol de Merkle de estado que contiene los UTXO gastados y recibe la salida del destinatario y el cambio del remitente.
            * `await transactInstruction` deriva localmente las direcciones de las cuentas de anuladores y devuelve la instrucción. Las cuentas de anuladores marcan los UTXO de entrada como gastados para impedir que un saldo privado se gaste dos veces.
            * `data` contiene la prueba de conocimiento cero y las salidas cifradas producidas en el paso anterior.
            * Una transferencia privada mueve el activo solo entre saldos privados. No pasa cuentas públicas de Solana ni cuentas de tokens.
          </Accordion>

          <Accordion title="6. Send like any Solana transaction">
            ```typescript theme={"system"}
            import { sendAndConfirmFactory } from "../src/lib.js";

            const transferTx = await sendAndConfirm([
              transferInstruction,
            ]);
            ```

            * `sendAndConfirm` firma y envía `transferInstruction` como una transacción de Solana.
            * La confirmación devuelve el slot en el que se confirmó, que se usa para controlar la consulta del indexador.
          </Accordion>
        </AccordionGroup>
      </Step>
    </Steps>

    ### Ejemplo de código completo

    Clona y ejecuta el ejemplo:

    ```bash theme={"system"}
    git clone https://github.com/helius-labs/zolana-examples.git
    cd zolana-examples
    git checkout v0.3.0-alpha
    cd typescript-client
    pnpm install
    pnpm example examples/deposit_transfer_withdraw.ts
    ```

    <Info>
      Los ejemplos usan un Ring confidencial en local/devnet [aquí](https://github.com/helius-labs/zolana-examples/blob/7775438e0a290a4c28dccf919719bd640c3ec5e3/typescript-client/examples/deposit_transfer_withdraw.ts).
    </Info>

    ```typescript deposit_transfer_withdraw.ts expandable theme={"system"}
    import {
      SOL_MINT,
      ShieldedKeypair,
      createZolanaClient,
    } from "@heliuslabs/zolana";
    import {
      LocalKeys,
      atSlot,
    } from "@heliuslabs/zolana/client";
    import {
      depositInstruction,
      transactInstruction,
      DepositAsset,
      TransactWithdrawal,
    } from "@heliuslabs/zolana/interface";
    import {
      AssetRegistry,
      ConfidentialTransfer,
      ProofInputUtxo,
      decryptToBalances,
      WithdrawalTarget,
    } from "@heliuslabs/zolana/transaction";

    import {
      cliKeypair,
      sendAndConfirmFactory,
      setup,
    } from "../src/lib.js";

    const DEPOSIT_AMOUNT = 10_000_000n;
    const TRANSFER_AMOUNT = 3_000_000n;
    const WITHDRAW_AMOUNT = 3_000_000n;

    async function main(): Promise<void> {
      const { clientConfig } = await setup();

      // Connect to Helius devnet RPC plus the Photon indexer and prover.
      const client =
        await createZolanaClient(clientConfig);

      // Initialize the sender's private wallet and local authority
      // to decrypt transactions and sync balances.
      // The Solana signer and private wallet are derived from the same Ed25519 seed.
      const sender = ShieldedKeypair.fromKeypair(
        await cliKeypair(),
      );
      const recipient = ShieldedKeypair.generate();
      const senderSigner = sender.toSolanaSigner();
      const senderAddress = sender.shieldedAddress();
      const senderKeys = LocalKeys.fromKeypair(
        sender,
        client.proofService,
      );

      // The SDK hands back instructions; the app owns signing and sending.
      const sendAndConfirm = sendAndConfirmFactory(
        client,
        senderSigner,
      );

      // Mints that are registered with Solana Rings for privacy.
      const assets = new AssetRegistry();

      // Deposit SOL into the sender's private balance.
      // A deposit from a public balance reveals
      // sender, recipient, asset and amount.
      // Alternatively, you can onramp fiat directly to a private balance.

      // 1. Move public SOL into the sender's private balance.
      // The view tag is the sender's Solana public key in confidential rings.
      // Used by the indexer to fetch the sender's UTXOs.
      const senderViewTag =
        senderAddress.confidentialViewTag();
      const depositIx = await depositInstruction({
        tree: client.tree,
        depositor: senderSigner,
        deposits: [
          {
            asset: DepositAsset.sol(),
            viewTag: senderViewTag,
            recipientOwnerHash:
              senderAddress.ownerHash(),
            amount: DEPOSIT_AMOUNT,
          },
        ],
      });

      // 2. Send and confirm like any Solana transaction; confirmation yields the landed slot.
      const depositTx = await sendAndConfirm([
        depositIx,
      ]);

      // 3. Fetch transaction outputs from the indexer, gated on the deposit's slot.
      // The indexer returns encrypted outputs by transaction signature.
      const depositResponse =
        await client.getShieldedTransactionsBySignature(
          depositTx.signature,
          atSlot(depositTx.slot),
        );

      // 4. The sender decrypts the transaction outputs locally to read the private balance.
      const balancesAfterDeposit =
        await decryptToBalances({
          keypair: sender,
          registry: assets,
          transactions:
            depositResponse.transactions.map(
              ({ transaction }) => transaction,
            ),
        });
      const depositBalance =
        balancesAfterDeposit.balance(SOL_MINT);
      if (depositBalance.amount !== DEPOSIT_AMOUNT) {
        throw new Error(
          `expected deposit amount ${DEPOSIT_AMOUNT}, got ${depositBalance.amount}`,
        );
      }
      if (depositBalance.utxos.length !== 1) {
        throw new Error(
          `expected 1 deposit utxo, got ${depositBalance.utxos.length}`,
        );
      }

      // Confidential SOL transfer to the recipient's private balance.
      // A confidential transfer reveals only sender and recipient,
      // not the asset or amount.

      // 1. Select private token accounts (UTXOs) that make up the private balance for the transfer.
      const transferUtxo = depositBalance.utxos[0]!;

      // 2. Prepare the selected UTXOs as inputs for the zero-knowledge proof.
      const transferInput =
        ProofInputUtxo.fromKeypair(
          transferUtxo,
          sender,
        );

      // 3. Build and sign the confidential transfer.
      // Signing encrypts the asset and amount and produces the proof inputs for the ZK prover.
      const transfer = new ConfidentialTransfer(
        senderAddress,
        [transferInput],
        senderSigner.address,
      );
      transfer.send(
        recipient.shieldedAddress(),
        SOL_MINT,
        TRANSFER_AMOUNT,
      );
      const transferProofInputs = transfer.sign(
        sender,
        assets,
      );

      // 4. Fetch the ZK proof to prove the sender can spend the balance without revealing asset and amount.
      const transferData = await client.proveTransact(
        transferProofInputs,
        senderKeys,
      );

      // 5. Build the instruction with the state Merkle tree and Solana accounts required for the transfer.
      // Private transfers move balances only between private token accounts, not public token accounts.
      const transferInstruction =
        await transactInstruction({
          payer: senderSigner,
          inputTree: client.tree,
          outputTree: client.tree,
          data: transferData,
        });

      // 6. Send and confirm like any Solana transaction; confirmation yields the landed slot.
      const transferTx = await sendAndConfirm([
        transferInstruction,
      ]);

      // 7. Fetch the sender's UTXOs from this transaction, gated on the transfer's slot,
      // and read the remaining private balance.
      const transferResponse =
        await client.getShieldedTransactionsBySignature(
          transferTx.signature,
          atSlot(transferTx.slot),
        );
      const balancesAfterTransfer =
        await decryptToBalances({
          keypair: sender,
          registry: assets,
          transactions:
            transferResponse.transactions.map(
              ({ transaction }) => transaction,
            ),
        });
      const transferBalance =
        balancesAfterTransfer.balance(SOL_MINT);
      if (
        transferBalance.amount !==
        DEPOSIT_AMOUNT - TRANSFER_AMOUNT
      ) {
        throw new Error(
          `expected remaining amount from this run ${DEPOSIT_AMOUNT - TRANSFER_AMOUNT}, got ${transferBalance.amount}`,
        );
      }
      if (transferBalance.utxos.length !== 1) {
        throw new Error(
          `expected 1 transfer utxo, got ${transferBalance.utxos.length}`,
        );
      }

      // Withdraw SOL from the sender's private balance to their public balance.
      // A withdrawal reveals the sender, recipient, asset, and amount.

      // 1. Select private token accounts (UTXOs) that make up the private balance for the withdrawal.
      const withdrawalUtxo =
        transferBalance.utxos[0]!;

      // 2. Prepare the selected UTXOs as inputs for the zero-knowledge proof.
      const withdrawalInput =
        ProofInputUtxo.fromKeypair(
          withdrawalUtxo,
          sender,
        );

      // 3. Build and sign the private-to-public withdrawal.
      // Signing encrypts the asset and amount of the remaining private balance
      // and produces the proof inputs for the ZK prover.
      const withdrawal = new ConfidentialTransfer(
        senderAddress,
        [withdrawalInput],
        senderSigner.address,
      );
      withdrawal.withdraw(
        SOL_MINT,
        WITHDRAW_AMOUNT,
        WithdrawalTarget.sol({
          recipient: senderSigner.address,
        }),
      );
      const withdrawalProofInputs = withdrawal.sign(
        sender,
        assets,
      );

      // 4. Fetch the ZK proof to prove the sender can spend the balance.
      const withdrawalData =
        await client.proveTransact(
          withdrawalProofInputs,
          senderKeys,
        );

      // 5. Build the instruction with the state Merkle tree and Solana accounts required for the withdrawal.
      const withdrawalInstruction =
        await transactInstruction({
          payer: senderSigner,
          inputTree: client.tree,
          outputTree: client.tree,
          withdrawal: TransactWithdrawal.sol({
            recipient: senderSigner.address,
          }),
          data: withdrawalData,
        });

      // 6. Send and confirm like any Solana transaction; confirmation yields the landed slot.
      const withdrawalTx = await sendAndConfirm([
        withdrawalInstruction,
      ]);

      // 7. Fetch the sender's UTXOs from this transaction, gated on the withdrawal's slot,
      // and read the remaining private balance.
      const withdrawalResponse =
        await client.getShieldedTransactionsBySignature(
          withdrawalTx.signature,
          atSlot(withdrawalTx.slot),
        );
      const balancesAfterWithdrawal =
        await decryptToBalances({
          keypair: sender,
          registry: assets,
          transactions:
            withdrawalResponse.transactions.map(
              ({ transaction }) => transaction,
            ),
        });
      const withdrawalBalance =
        balancesAfterWithdrawal.balance(SOL_MINT);
      if (
        withdrawalBalance.amount !==
        DEPOSIT_AMOUNT -
          TRANSFER_AMOUNT -
          WITHDRAW_AMOUNT
      ) {
        throw new Error(
          `expected remaining amount from this run ${DEPOSIT_AMOUNT - TRANSFER_AMOUNT - WITHDRAW_AMOUNT}, got ${withdrawalBalance.amount}`,
        );
      }
      if (withdrawalBalance.utxos.length !== 1) {
        throw new Error(
          `expected 1 withdrawal utxo, got ${withdrawalBalance.utxos.length}`,
        );
      }

      // 8. Read remaining private balance and the public balance.
      const solanaBalance = await client.getBalance(
        senderSigner.address,
      );
      console.log(
        `withdraw private_balance=${withdrawalBalance.amount} ` +
          `solana_balance=${solanaBalance} tx=${withdrawalTx.signature}`,
      );
    }

    await main();
    ```
  </Tab>

  <Tab title="Rust Client">
    <Steps>
      <Step>
        ### Requisitos previos

        <Info>
          The Rust examples require Rust 1.98.1 and the Solana CLI v4.0.2. See the [Solana installation guide](https://solana.com/docs/intro/installation).
        </Info>

        ```toml Cargo.toml theme={"system"}
        [dependencies]
        zolana-client = { git = "https://github.com/helius-labs/zolana", tag = "v0.3.0-alpha", features = ["indexer-api", "solana-rpc"] }
        zolana-interface = { git = "https://github.com/helius-labs/zolana", tag = "v0.3.0-alpha" }
        zolana-program = { git = "https://github.com/helius-labs/zolana", tag = "v0.3.0-alpha" }
        zolana-keypair = { git = "https://github.com/helius-labs/zolana", tag = "v0.3.0-alpha" }
        zolana-transaction = { git = "https://github.com/helius-labs/zolana", tag = "v0.3.0-alpha" }
        zolana-wallet = { git = "https://github.com/helius-labs/zolana", tag = "v0.3.0-alpha" }
        ```

        Source: [sdk-libs/client](https://github.com/helius-labs/zolana/tree/v0.3.0-alpha/sdk-libs/client)

        <Accordion title="Connect to Endpoints">
          <Tabs>
            <Tab title="Devnet">
              Add a [Helius API key](https://dashboard.helius.dev/):

              ```bash .env theme={"system"}
              API_KEY=YOUR_API_KEY
              ZOLANA_PAYER_KEYPAIR=~/.config/solana/id.json
              ```

              ```rust theme={"system"}
              use zolana_client::{SolanaRpc, ZolanaClient};
              use zolana_interface::pda;

              let tree = pda::tree(0);
              let client = ZolanaClient::from_urls(
                  SolanaRpc::new("https://devnet.helius-rpc.com/?api-key=YOUR_API_KEY"),
                  "https://d2xah7tnhdhcom.cloudfront.net",
                  "https://d21ni15goiip6l.cloudfront.net",
              )?;
              ```

              The examples use the Solana CLI wallet as the payer by default. The payer must hold devnet SOL. See [How to Get Devnet SOL](/docs/rpc/devnet-sol).
            </Tab>

            <Tab title="Localnet">
              ```bash theme={"system"}
              cargo install --git https://github.com/helius-labs/zolana --tag v0.3.0-alpha zolana-cli
              zolana dev start
              ```

              ```rust theme={"system"}
              use zolana_client::{SolanaRpc, ZolanaClient};
              use zolana_interface::pda;

              let tree = pda::tree(0);
              let client = ZolanaClient::from_urls(
                  SolanaRpc::new("http://127.0.0.1:8899"),
                  "http://127.0.0.1:8784",
                  "http://127.0.0.1:3001",
              )?;
              ```
            </Tab>
          </Tabs>
        </Accordion>
      </Step>

      <Step>
        ### Transferir a un saldo privado

        ```rust theme={"system"}
        use zolana_program::instruction::Transact;
        use zolana_transaction::{instructions::transact::ConfidentialTransaction, SOL_MINT};

        let transfer_utxo = sender_balances_after_deposit
            .get_balance(SOL_MINT)
            // SPL: .get_balance(spl.mint)
            .and_then(|balance| balance.utxos.first())
            .expect("failed to fetch deposited utxo")
            .clone();

        let mut transfer = ConfidentialTransaction::new(vec![transfer_utxo], sender.pubkey())?;

        transfer.transfer_sol(&recipient.shielded_address()?, TRANSFER_AMOUNT)?;
        // SPL: transfer.transfer(&recipient.shielded_address()?, spl.mint, TRANSFER_AMOUNT)?;
        let proof_inputs = transfer.encrypt(&sender)?;

        let transfer_data = client.prove_transact(proof_inputs, None, &sender)?;

        let transfer_ix = Transact {
            payer: sender.pubkey(),
            input_trees: vec![tree],
            output_tree: tree,
            owner_signers: Vec::new(),
            interface_transfer_accounts: Vec::new(),
            data: transfer_data,
        }
        .instruction();
        ```

        <AccordionGroup>
          <Accordion title="1. Select private token accounts to spend">
            ```rust theme={"system"}
            use zolana_transaction::SOL_MINT;

            let transfer_utxo = sender_balances_after_deposit
                .get_balance(SOL_MINT)
                // SPL: .get_balance(spl.mint)
                .and_then(|balance| balance.utxos.first())
                .expect("failed to fetch deposited utxo")
                .clone();
            ```

            * El ejemplo gasta la cuenta privada de tokens de Solana creada por el depósito anterior. Una transferencia puede gastar varios UTXO.
            * `transfer_utxo` es el primer UTXO disponible para gastar de ese activo. El comentario `// SPL:` muestra `get_balance(spl.mint)`.
          </Accordion>

          <Accordion title="2. Prepare proof inputs">
            ```rust theme={"system"}
            use zolana_transaction::instructions::transact::ConfidentialTransaction;

            let mut transfer = ConfidentialTransaction::new(vec![transfer_utxo], sender.pubkey())?;
            ```

            * `SppProofInputUtxo::new` prepara el UTXO seleccionado como entrada de la prueba con el par de claves de la billetera privada del remitente.
            * El par de claves deriva el anulador que marca el UTXO de entrada como gastado, mientras el activo y el importe permanecen cifrados.
          </Accordion>

          <Accordion title="3. Build and sign the confidential transfer">
            ```rust theme={"system"}
            use solana_signer::Signer;

            transfer.transfer_sol(&recipient.shielded_address()?, TRANSFER_AMOUNT)?;
            // SPL: transfer.transfer(&recipient.shielded_address()?, spl.mint, TRANSFER_AMOUNT)?;
            let proof_inputs = transfer.encrypt(&sender)?;
            ```

            * `sender_shielded_address` es la <Tooltip tip="The public key bundle (signing, nullifier, and viewing keys) published in a user's registry record. Not an onchain address.">dirección protegida</Tooltip> del remitente. La transferencia gasta fondos de esta billetera.
            * `vec![transfer_input_utxo]` enumera los UTXO seleccionados del remitente. Una transferencia puede gastar varios UTXO.
            * `sender.pubkey()` es el pagador de la comisión de la transacción. Un patrocinador de gas puede pagar la comisión.
            * `recipient.shielded_address()?` es la dirección protegida del destinatario. La salida transferida se cifra con la clave de visualización del destinatario.
            * `SOL_MINT` selecciona SOL. El comentario `// SPL:` muestra la emisión del token para los activos SPL y Token 2022.
            * `TRANSFER_AMOUNT` se expresa en las unidades base del activo. SOL usa lamports. Los activos SPL y Token 2022 usan las unidades base del token.
            * `transfer.sign` autoriza la transición de estado, cifra el activo y el importe, y produce las entradas para el generador de pruebas de conocimiento cero.
            * `assets` es el registro de activos que se usa para resolver los activos privados compatibles.
          </Accordion>

          <Accordion title="4. Fetch the zero-knowledge proof">
            ```rust theme={"system"}
            use zolana_client::Rpc;

            let transfer_data = client.prove_transact(proof_inputs, None, &sender)?;
            ```

            * `client.prove_transact` genera la prueba de conocimiento cero a partir de la transferencia firmada y devuelve los datos serializados de la instrucción.
            * `tree` identifica el árbol de Merkle de estado cuya raíz se usa para demostrar la pertenencia de los UTXO de entrada.
            * La prueba demuestra que el remitente posee las entradas y puede gastarlas sin revelar el activo ni el importe cifrados.
          </Accordion>

          <Accordion title="5. Build the transfer instruction">
            ```rust theme={"system"}
            use zolana_program::instruction::Transact;

            let transfer_ix = Transact {
                payer: sender.pubkey(),
                input_trees: vec![tree],
                output_tree: tree,
                owner_signers: Vec::new(),
                interface_transfer_accounts: Vec::new(),
                data: transfer_data,
            }
            .instruction();
            ```

            * `payer` firma y paga la transacción de Solana. Un patrocinador de gas puede pagar la comisión.
            * `input_trees` identifica el árbol de Merkle de estado que contiene los UTXO gastados.
            * `output_tree` identifica el árbol de Merkle de estado que recibe los compromisos de la salida del destinatario y del cambio del remitente.
            * `interface_transfer_accounts` está vacío porque una transferencia privada mueve el activo solo entre saldos privados y no interactúa con saldos públicos en cuentas de Solana ni cuentas de tokens.
            * `owner_signers` está vacío para esta transferencia confidencial.
            * `data` contiene la prueba de conocimiento cero y las salidas cifradas producidas en el paso anterior.
          </Accordion>

          <Accordion title="6. Send like any Solana transaction">
            ```rust theme={"system"}
            use zolana_client::Rpc;

            let signature = client.create_and_send_transaction(
                &[transfer_ix],
                sender.pubkey(),
                &[&sender],
                client.compute_budget(),
            )?;
            let slot = landed_slot(&client, signature)?;
            ```

            * `create_and_send_transaction` firma y envía `transfer_ix` como una transacción de Solana.
            * `landed_slot` lee el slot de confirmación que se usa para controlar la consulta del indexador.
            * `sender` paga la comisión y autoriza la transferencia.
          </Accordion>
        </AccordionGroup>
      </Step>
    </Steps>

    ### Ejemplo de código completo

    Clona y ejecuta el ejemplo:

    ```bash theme={"system"}
    git clone https://github.com/helius-labs/zolana-examples.git
    cd zolana-examples
    git checkout v0.3.0-alpha
    cd rust-client
    cargo run -p rust-client-example --example deposit_transfer_withdraw
    ```

    <Info>
      Los ejemplos usan un Ring confidencial en local/devnet [aquí](https://github.com/helius-labs/zolana-examples/blob/7775438e0a290a4c28dccf919719bd640c3ec5e3/rust-client/examples/deposit_transfer_withdraw.rs).
    </Info>

    ```rust deposit_transfer_withdraw.rs expandable theme={"system"}
    use anyhow::{anyhow, Result};
    use rust_client_example::{cli_keypair, landed_slot, setup, SetupContext};
    use solana_keypair::Keypair;
    use solana_signer::Signer;
    use zolana_client::{IndexerRpcConfig, Rpc, SolanaRpc, ZolanaClient};
    use zolana_keypair::ShieldedKeypair;
    use zolana_program::instruction::{
        AssetDeposit, Deposit, DepositAsset, Transact, TransactInterfaceTransferAccounts,
        TransactSolTransferAccounts,
    };
    use zolana_transaction::{
        decrypt_spendable, instructions::transact::ConfidentialTransaction, AssetRegistry, SOL_MINT,
    };

    const DEPOSIT_AMOUNT: u64 = 10_000_000;
    const TRANSFER_AMOUNT: u64 = 3_000_000;
    const WITHDRAW_AMOUNT: u64 = 3_000_000;

    fn main() -> Result<()> {
        let SetupContext {
            rpc_url,
            indexer_url,
            prover_url,
            tree,
        } = setup()?;

        // Connect to the RPC, indexer, and prover.
        let client = ZolanaClient::from_urls(SolanaRpc::new(rpc_url), &indexer_url, prover_url)?;

        // Mints that are registered with Solana Rings for privacy.
        let assets = AssetRegistry::default();
        // SPL: assets.insert(spl.asset_id, spl.mint)?;

        // Initialize the sender's private wallet and local authority
        // to decrypt transactions and sync balances.
        // The Solana signer and private wallet are derived from the same Ed25519 seed.
        let sender = ShieldedKeypair::from_keypair(&cli_keypair()?)?;
        let recipient = ShieldedKeypair::from_keypair(&Keypair::new())?;
        let sender_shielded_address = sender.shielded_address()?;

        // Deposit SOL into the sender's private balance.
        // A deposit from a public balance reveals
        // sender, recipient, asset and amount.
        // Alternatively, you can onramp fiat directly to a private balance.

        // 1. Move public SOL into the sender's private balance.
        let sender_balances_after_deposit = {
            let deposit_ix = Deposit {
                tree,
                depositor: sender.pubkey(),
                deposits: vec![AssetDeposit {
                    asset: DepositAsset::Sol,
                    // SPL: asset: DepositAsset::Spl(zolana_program::instruction::DepositSplAccounts {
                    // SPL:     mint: spl.mint,
                    // SPL:     user_token: spl.user_token_account,
                    // SPL:     token_program: spl.token_program,
                    // SPL: }),
                    view_tag: sender_shielded_address.confidential_view_tag()?,
                    owner: sender_shielded_address.owner_hash()?,
                    amount: DEPOSIT_AMOUNT,
                    memo: None,
                }],
            }
            .instruction()?;

            // 2. Send and confirm like any Solana transaction; the landed slot gates
            // the indexer fetch below.
            let signature = client.create_and_send_transaction(
                &[deposit_ix],
                sender.pubkey(),
                &[&sender],
                client.compute_budget(),
            )?;
            let slot = landed_slot(&client, signature)?;

            // 3. Fetch transaction outputs from the indexer, gated on the deposit's slot.
            // The indexer returns encrypted outputs by transaction signature.
            let response = client.get_shielded_transactions_by_signature(
                signature,
                Some(IndexerRpcConfig::at_slot(slot)),
            )?;
            let transactions = response
                .transactions
                .into_iter()
                .map(|indexed| indexed.transaction)
                .collect::<Vec<_>>();

            // 4. The sender decrypts the transaction outputs locally to update the private balance.
            let balances = decrypt_spendable(&sender, &transactions, &assets)
                .map_err(|e| anyhow!("decrypt sender transactions: {e:?}"))?
                .balances;

            let sender_balance = balances
                .get_balance(SOL_MINT)
                // SPL: .get_balance(spl.mint)
                .expect("failed to fetch sender's utxo");
            assert_eq!(sender_balance.amount, DEPOSIT_AMOUNT);
            assert_eq!(sender_balance.utxos.len(), 1);

            balances
        };

        // Confidential SOL transfer to the recipient's private balance.
        // A confidential transfer reveals only sender and recipient,
        // not the asset or amount.
        let sender_balances_after_transfer = {
            // 1. Select UTXOs that make up the private balance for the transfer.
            let transfer_utxo = sender_balances_after_deposit
                .get_balance(SOL_MINT)
                // SPL: .get_balance(spl.mint)
                .and_then(|balance| balance.utxos.first())
                .expect("failed to fetch deposited utxo")
                .clone();

            // 2. Prepare the selected UTXOs as inputs for the zero-knowledge proof.
            let mut transfer = ConfidentialTransaction::new(vec![transfer_utxo], sender.pubkey())?;

            // 3. Build and encrypt the confidential transfer.
            // Encryption hides the asset and amount and produces the proof inputs for the ZK prover.
            transfer.transfer_sol(&recipient.shielded_address()?, TRANSFER_AMOUNT)?;
            // SPL: transfer.transfer(&recipient.shielded_address()?, spl.mint, TRANSFER_AMOUNT)?;
            let proof_inputs = transfer.encrypt(&sender)?;

            // 4. Fetch the zk proof to prove the sender can spend the balance without revealing asset and amount.
            let transfer_data = client.prove_transact(proof_inputs, None, &sender)?;

            // 5. Construct the instruction.
            let transfer_ix = Transact {
                payer: sender.pubkey(),
                input_trees: vec![tree],
                output_tree: tree,
                owner_signers: Vec::new(),
                interface_transfer_accounts: Vec::new(),
                data: transfer_data,
            }
            .instruction();

            // 6. Send and confirm like any Solana transaction; confirmation yields the landed slot.
            let signature = client.create_and_send_transaction(
                &[transfer_ix],
                sender.pubkey(),
                &[&sender],
                client.compute_budget(),
            )?;
            let slot = landed_slot(&client, signature)?;

            // 7. Fetch the sender's UTXOs from this transaction, gated on the transfer's slot,
            // and read the remaining private balance.
            let response = client.get_shielded_transactions_by_signature(
                signature,
                Some(IndexerRpcConfig::at_slot(slot)),
            )?;
            let transactions = response
                .transactions
                .into_iter()
                .map(|indexed| indexed.transaction)
                .collect::<Vec<_>>();
            let sender_balances = decrypt_spendable(&sender, &transactions, &assets)
                .map_err(|e| anyhow!("decrypt sender transactions: {e:?}"))?
                .balances;
            let sender_balance = sender_balances
                .get_balance(SOL_MINT)
                // SPL: .get_balance(spl.mint)
                .expect("failed to fetch sender's utxo");
            assert_eq!(sender_balance.amount, DEPOSIT_AMOUNT - TRANSFER_AMOUNT);
            assert_eq!(sender_balance.utxos.len(), 1);

            sender_balances
        };

        // Withdraw SOL back to the sender's public balance.
        // A withdrawal from a confidential balance reveals
        // sender, recipient, asset and amount.
        {
            // 1. Select UTXOs that make up the private balance for the withdrawal.
            let withdrawal_utxo = sender_balances_after_transfer
                .get_balance(SOL_MINT)
                // SPL: .get_balance(spl.mint)
                .and_then(|balance| balance.utxos.first())
                .expect("failed to fetch sender's utxo")
                .clone();

            // 2. Prepare the selected UTXOs as inputs for the zero-knowledge proof.
            let mut withdrawal = ConfidentialTransaction::new(vec![withdrawal_utxo], sender.pubkey())?;

            // 3. Build and encrypt the confidential withdrawal.
            // Encryption hides the private change and produces the ZK prover inputs.
            withdrawal.withdraw_sol(WITHDRAW_AMOUNT, sender.pubkey())?;
            // SPL: withdrawal.withdraw(spl.mint, WITHDRAW_AMOUNT, spl.user_token_account)?;
            let proof_inputs = withdrawal.encrypt(&sender)?;

            // 4. Fetch the ZK proof to prove the sender can spend the balance.
            let withdrawal_data = client.prove_transact(proof_inputs, None, &sender)?;

            // 5. Combine the proof and withdrawal accounts in a single instruction.
            let withdraw_ix = Transact {
                payer: sender.pubkey(),
                input_trees: vec![tree],
                output_tree: tree,
                owner_signers: Vec::new(),
                interface_transfer_accounts: vec![TransactInterfaceTransferAccounts::Sol(
                    TransactSolTransferAccounts {
                        recipient: sender.pubkey(),
                    },
                )],
                // SPL: interface_transfer_accounts: vec![
                // SPL:     TransactInterfaceTransferAccounts::SplWithdrawal(
                // SPL:         zolana_program::instruction::TransactSplWithdrawalAccounts {
                // SPL:             mint: spl.mint,
                // SPL:             spl_interface: spl.vault,
                // SPL:             user_token_account: spl.user_token_account,
                // SPL:             token_program: spl.token_program,
                // SPL:         },
                // SPL:     ),
                // SPL: ],
                data: withdrawal_data,
            }
            .instruction();

            // 6. Send and confirm like any Solana transaction.
            let signature = client.create_and_send_transaction(
                &[withdraw_ix],
                sender.pubkey(),
                &[&sender],
                client.compute_budget(),
            )?;
            let slot = landed_slot(&client, signature)?;

            // 7. Fetch the sender's UTXOs from this transaction, gated on the withdrawal's slot,
            // and read the remaining private balance.
            let response = client.get_shielded_transactions_by_signature(
                signature,
                Some(IndexerRpcConfig::at_slot(slot)),
            )?;
            let transactions = response
                .transactions
                .into_iter()
                .map(|indexed| indexed.transaction)
                .collect::<Vec<_>>();
            let sender_balances = decrypt_spendable(&sender, &transactions, &assets)
                .map_err(|e| anyhow!("decrypt sender transactions: {e:?}"))?
                .balances;
            let sender_balance = sender_balances
                .get_balance(SOL_MINT)
                // SPL: .get_balance(spl.mint)
                .expect("failed to fetch sender's utxo");
            assert_eq!(
                sender_balance.amount,
                DEPOSIT_AMOUNT - TRANSFER_AMOUNT - WITHDRAW_AMOUNT
            );
            assert_eq!(sender_balance.utxos.len(), 1);

            // 8. Read remaining private balance and the public SOL balance.
            let solana_balance = client.get_balance(sender.pubkey())?;
            println!("withdraw solana_balance={solana_balance} tx={signature}");
            // SPL: println!(
            // SPL:     "withdraw user_token={} tx={signature}",
            // SPL:     spl.user_token_account,
            // SPL: );
        }
        Ok(())
    }
    ```
  </Tab>
</Tabs>

## Guías relacionadas

<CardGroup cols={2}>
  <Card title="Deposit" icon="arrow-down-to-bracket" href="/docs/es/privacy/guides/deposit" horizontal />

  <Card title="Withdraw" icon="arrow-up-from-bracket" href="/docs/es/privacy/guides/withdraw" horizontal />

  <Card title="Read a Private Balance" icon="wallet" href="/docs/es/privacy/guides/read-balance" horizontal />

  <Card title="Read Private History" icon="clock-rotate-left" href="/docs/es/privacy/guides/read-history" horizontal />
</CardGroup>

## Didn't find what you were looking for?

<Callout type="info">
  Reach out! [Telegram](https://t.me/tilo_light) | [E-Mail](mailto:sales@helius.xyz) | [Contact](https://www.helius.dev/contact)
</Callout>
