> ## 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.

# Вывод средств

> Руководство по выводу активов SOL, SPL или Token 2022 с приватного баланса на публичный аккаунт Solana с полным примером кода.

1. При выводе токены перемещаются с приватного баланса на публичный баланс Solana.
2. Вывод выполняется одной транзакцией Solana на адрес кошелька Solana.

#### Вывод средств: что остаётся приватным

| Поле | Видимость |
| - | - |
| Актив | Публично. Отображается в публичной операции вывода. |
| Сумма | Публично. Отображается в публичной операции вывода. |
| Отправитель | Публично в конфиденциальном кольце; скрыто в анонимном кольце. |
| Получатель | Публично. Адрес кошелька назначения отображается в публичной операции вывода. |
| Примечание (необязательно) | Скрыто, если прикреплено к зашифрованному выходу. |
| Итоговый публичный баланс | Публично. Хранится в публичном аккаунте получателя. |
| Оставшийся приватный баланс | Скрыто. Зашифровано в блокчейне. |

<Info>
  Общедоступное кольцо обеспечивает конфиденциальность благодаря шифрованию суммы и актива.
  Пользовательское кольцо можно настроить как конфиденциальное или анонимное (с шифрованием отправителя, получателя, актива и суммы).
</Info>

## Как работает вывод средств

Вывод средств выполняется аналогично публичному переводу в Solana:

1. Баланс SOL или SPL пользователя зашифрован в блокчейне.

2. Пользователь расшифровывает приватное состояние, кошелёк формирует операцию вывода, а владелец подписывает её.

   * Получите зашифрованное состояние с помощью специальных методов RPC. Только пользователь может локально расшифровать баланс.
   * Кошелёк задаёт сумму и получателя, а затем запрашивает доказательство с нулевым разглашением. По умолчанию провайдер RPC создаёт и возвращает это доказательство.

3. Среда выполнения Solana проверяет подписи и вызывает программу конфиденциальности Solana, которая проверяет доказательство с нулевым разглашением, не раскрывая зашифрованное состояние.

4. Приложение отслеживает статус по хешу транзакции 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. Баланс SOL или SPL пользователя общедоступен в блокчейне.
  2. Кошелёк считывает публичное состояние, формирует перевод, а владелец подписывает его.
  3. Среда выполнения Solana проверяет подписи и вызывает System Program или Token Program, которая обновляет публичный баланс.
  4. Приложение отслеживает статус по хешу транзакции 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>
  Это высокоуровневый процесс транзакции для общедоступного конфиденциального кольца.
  Сравните его с пользовательскими кольцами в разделе [Высокоуровневый процесс транзакции](/docs/ru/privacy/concepts/overview#общая-схема-транзакции).
</Info>

## Начало работы

<Tabs>
  <Tab title="TypeScript Client">
    <Steps>
      <Step>
        ### Предварительные требования

        <Info>
          Для примеров на TypeScript требуются Node.js 24 или более поздней версии, pnpm 11.18.0 и Solana CLI.
        </Info>

        ```bash theme={"system"}
        pnpm add @heliuslabs/zolana@^0.4.0-alpha @solana/kit@^8.3.0
        ```

        Источник: [sdk-libs/ts](https://github.com/helius-labs/zolana/tree/ts-sdk-v0.4.0-alpha/sdk-libs/ts)

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

              Добавьте [ключ API Helius](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",
              });
              ```

              По умолчанию в примерах в качестве плательщика используется кошелёк Solana CLI. На балансе плательщика должны быть SOL в devnet. См. [Как получить SOL в devnet](/docs/ru/rpc/devnet-sol).
            </Tab>

            <Tab title="Localnet">
              В localnet SDK запускает локальный тестовый валидатор (`:8899`), индексатор Photon (`:8784`) и средство доказательства (`:3001`), после чего
              клиент автоматически подключается к ним без необходимости настраивать конечные точки.

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

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

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

      <Step>
        ### Вывод на публичный баланс

        <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 };
            };
          }
          ```

          * SDK возвращает инструкции. Приложение подписывает и отправляет их.
          * `sendAndConfirmFactory` формирует транзакцию Kit, отправляет её и возвращает подпись вместе со слотом, в который она была включена.
        </Accordion>

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

            const withdrawalUtxo =
              transferBalance.utxos[0]!;
            const withdrawalInput =
              ProofInputUtxo.fromKeypair(
                withdrawalUtxo,
                sender,
              );
            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,
            );
            const senderKeys = LocalKeys.fromKeypair(sender, client.proofService);
            const withdrawalData =
              await client.proveTransact(
                withdrawalProofInputs,
                senderKeys,
              );
            const withdrawalInstruction =
              await transactInstruction({
                payer: senderSigner,
                inputTree: client.tree,
                outputTree: client.tree,
                withdrawal: TransactWithdrawal.sol({
                  recipient: senderSigner.address,
                }),
                data: withdrawalData,
              });
            const withdrawalTx = await sendAndConfirm([
              withdrawalInstruction,
            ]);
            ```
          </Tab>

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

            const withdrawalUtxo =
              transferBalance.utxos[0]!;
            const withdrawalInput =
              ProofInputUtxo.fromKeypair(
                withdrawalUtxo,
                sender,
              );
            const withdrawal = new ConfidentialTransfer(
              senderAddress,
              [withdrawalInput],
              senderSigner.address,
            );
            withdrawal.withdraw(
              spl.mint,
              WITHDRAW_AMOUNT,
              WithdrawalTarget.spl({
                recipientTokenAccount: spl.userTokenAccount,
                splTokenInterface: spl.splTokenInterface,
                splInterfaceBump: spl.splInterfaceBump,
              }),
            );
            const withdrawalProofInputs = withdrawal.sign(
              sender,
              assets,
            );
            const senderKeys = LocalKeys.fromKeypair(sender, client.proofService);
            const withdrawalData =
              await client.proveTransact(
                withdrawalProofInputs,
                senderKeys,
              );
            const withdrawalInstruction =
              await transactInstruction({
                payer: senderSigner,
                inputTree: client.tree,
                outputTree: client.tree,
                withdrawal: TransactWithdrawal.spl({
                  mint: spl.mint,
                  splTokenInterface: spl.splTokenInterface,
                  recipientTokenAccount: spl.userTokenAccount,
                  tokenProgram: spl.tokenProgram,
                }),
                data: withdrawalData,
              });
            const withdrawalTx = await sendAndConfirm([
              withdrawalInstruction,
            ]);
            ```
          </Tab>
        </Tabs>

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

            const withdrawalUtxo =
              transferBalance.utxos[0]!;
            ```

            * В примере расходуется остаток на приватном токен-аккаунте Solana после предыдущего перевода. Для вывода можно использовать несколько UTXO.
            * `withdrawalUtxo` выбирает один приватный токен-аккаунт Solana из этого баланса.
          </Accordion>

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

            const withdrawalInput =
              ProofInputUtxo.fromKeypair(
                withdrawalUtxo,
                sender,
              );
            ```

            * `ProofInputUtxo.fromKeypair` подготавливает выбранный UTXO в качестве входных данных доказательства, используя приватную пару ключей кошелька отправителя.
            * Из пары ключей выводится нуллификатор, который помечает входной UTXO как потраченный, при этом актив и сумма на входе остаются зашифрованными.
          </Accordion>

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

            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,
            );
            ```

            * `senderAddress` — это <Tooltip tip="The public key bundle (signing, nullifier, and viewing keys) published in a user's registry record. Not an onchain address.">защищённый адрес</Tooltip> отправителя. Средства для вывода списываются с этого кошелька.
            * `[withdrawalInput]` содержит список выбранных UTXO отправителя. Для вывода можно использовать несколько UTXO.
            * `senderSigner.address` — адрес Solana плательщика комиссии. Комиссию может оплатить спонсор газа.
            * `WithdrawalTarget.sol` — публичный получатель в Solana. Получателем может быть владелец или третье лицо.
            * `SOL_MINT` выбирает SOL. При выводе SPL или Token 2022 передаётся адрес выпуска токена.
            * `WITHDRAW_AMOUNT` указывается в базовых единицах актива. Для SOL используются лампорты. Для активов SPL и Token 2022 используются базовые единицы токена.
            * `withdrawal.sign` авторизует переход состояния, шифрует оставшуюся приватную сдачу и создаёт входные данные для системы доказательства с нулевым разглашением.
            * `assets` — реестр активов, используемый для определения поддерживаемых приватных активов.
          </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 withdrawalData =
              await client.proveTransact(
                withdrawalProofInputs,
                senderKeys,
              );
            ```

            * `senderKeys` использует `LocalKeys.fromKeypair(sender, client.proofService)`, чтобы авторизовать создание доказательства с помощью ключей отправителя.
            * `client.proveTransact` создаёт доказательство с нулевым разглашением на основе подписанной операции вывода и возвращает сериализованные данные инструкции.
            * Доказательство подтверждает, что отправитель владеет входными средствами и может их потратить. Выводимый актив и сумма общедоступны. Суммы на входе и сдача остаются зашифрованными.
          </Accordion>

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

            const withdrawalInstruction =
              await transactInstruction({
                payer: senderSigner,
                inputTree: client.tree,
                outputTree: client.tree,
                withdrawal: TransactWithdrawal.sol({
                  recipient: senderSigner.address,
                }),
                data: withdrawalData,
              });
            ```

            * `payer` подписывает и оплачивает транзакцию Solana. Комиссию может оплатить спонсор газа.
            * `inputTree` и `outputTree` — это `client.tree`, дерево Меркла состояния, которое содержит потраченные UTXO и получает приватную сдачу отправителя.
            * `withdrawal` — это `TransactWithdrawal.sol`, публичный аккаунт получателя в Solana.
            * `await transactInstruction` локально выводит адреса аккаунтов нуллификаторов и возвращает инструкцию. Аккаунты нуллификаторов помечают входные UTXO как потраченные, чтобы приватный баланс нельзя было потратить дважды.
            * `data` содержит доказательство с нулевым разглашением и зашифрованную сдачу, созданные на предыдущем шаге.
            * При выводе актив перемещается с приватного баланса на публичный аккаунт Solana. В операцию передаётся публичный аккаунт получателя.
          </Accordion>

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

            const withdrawalTx = await sendAndConfirm([
              withdrawalInstruction,
            ]);
            ```

            * `sendAndConfirm` подписывает и отправляет `withdrawalInstruction` как транзакцию Solana.
            * После подтверждения возвращается слот, в который попала транзакция. Он используется для ограничения выборки индексатора.
          </Accordion>
        </AccordionGroup>
      </Step>
    </Steps>

    ### Полный пример кода

    Клонируйте и запустите пример:

    ```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>
      В примерах используется конфиденциальное кольцо в локальной сети/devnet, доступное [здесь](https://github.com/helius-labs/zolana-examples/blob/v0.3.0-alpha/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,
    } 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 client = await createZolanaClient({
        solanaRpcUrl: `https://devnet.helius-rpc.com/?api-key=${process.env.API_KEY}`,
      });
      // localnet: const client = await createZolanaClient({});

      // 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 outputs.
      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 this transaction's outputs, gated on its confirmed slot.
      const depositResponse =
        await client.getShieldedTransactionsBySignature(
          depositTx.signature,
          atSlot(depositTx.slot),
        );

      // 4. The sender decrypts the transaction outputs locally to read the funds deposited in this run.
      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 this transaction's outputs, gated on its confirmed slot.
      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 this transaction's outputs, gated on its confirmed slot.
      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>
        ### Предварительные требования

        <Info>
          Для примеров на Rust требуются Rust 1.98.1 и Solana CLI v4.0.2. См. [руководство по установке Solana](https://solana.com/docs/intro/installation).
        </Info>

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

        Источник: [sdk-libs/client](https://github.com/helius-labs/zolana/tree/v0.4.0-alpha/sdk-libs/client)

        <Accordion title="Connect to Endpoints">
          <Tabs>
            <Tab title="Devnet">
              Добавьте [API-ключ Helius](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 url = "https://devnet.helius-rpc.com/?api-key=YOUR_API_KEY";
              let client = ZolanaClient::from_urls(SolanaRpc::new(url), url, url)?;
              ```

              По умолчанию в примерах в качестве плательщика используется кошелёк Solana CLI. На балансе плательщика должны быть SOL в сети devnet. См. [Как получить SOL в сети devnet](/docs/ru/rpc/devnet-sol).
            </Tab>

            <Tab title="Localnet">
              ```bash theme={"system"}
              cargo install --git https://github.com/helius-labs/zolana --tag v0.4.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>
        ### Вывод на публичный баланс

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

        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();

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

        withdrawal.withdraw_sol(WITHDRAW_AMOUNT, sender.pubkey())?;
        // SPL: withdrawal.withdraw(spl.mint, WITHDRAW_AMOUNT, spl.user_token_account)?;
        let proof_inputs = withdrawal.encrypt(&sender)?;

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

        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();
        ```

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

            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();
            ```

            * В примере расходуется остаток на приватном токен-аккаунте Solana после предыдущего перевода. Для вывода можно использовать несколько UTXO.
            * `withdrawal_utxo` — первый доступный для расходования UTXO этого актива. Комментарий `// SPL:` показывает `get_balance(spl.mint)`.
          </Accordion>

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

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

            * `ConfidentialTransaction::new` принимает выбранные UTXO напрямую.
            * `vec![withdrawal_utxo]` содержит список входных данных. `sender.pubkey()` — плательщик комиссии за транзакцию.
          </Accordion>

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

            withdrawal.withdraw_sol(WITHDRAW_AMOUNT, sender.pubkey())?;
            // SPL: withdrawal.withdraw(spl.mint, WITHDRAW_AMOUNT, spl.user_token_account)?;
            let proof_inputs = withdrawal.encrypt(&sender)?;
            ```

            * `sender.pubkey()` — публичный получатель SOL. Получателем может быть владелец или третье лицо. Для SPL и Token 2022 передайте токен-аккаунт получателя в `withdraw`.
            * `withdrawal.withdraw_sol` выбирает SOL. Комментарий `// SPL:` показывает адрес выпуска токена для активов SPL и Token 2022.
            * `WITHDRAW_AMOUNT` указывается в базовых единицах актива. Для SOL используются лампорты. Для активов SPL и Token 2022 используются базовые единицы токена.
            * `withdrawal.encrypt(&sender)` шифрует выходные данные и создаёт входные данные для системы доказательства с нулевым разглашением.
          </Accordion>

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

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

            * `client.prove_transact` создаёт доказательство с нулевым разглашением на основе зашифрованной операции вывода и возвращает сериализованные данные инструкции.
            * `sender` предоставляет ключи для авторизации доказательства. Входные UTXO определяют свои деревья состояния.
            * Доказательство подтверждает, что отправитель владеет входными средствами и может их потратить. Выводимый актив и сумма общедоступны. Суммы на входе и сдача остаются зашифрованными.
          </Accordion>

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

            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();
            ```

            * `payer` подписывает и оплачивает транзакцию Solana. Комиссию может оплатить спонсор газа.
            * `input_trees` определяет дерево Меркла состояния, содержащее потраченные UTXO.
            * `output_tree` определяет дерево Меркла состояния, которое получает обязательство для приватной сдачи отправителя.
            * `interface_transfer_accounts` предоставляет `TransactInterfaceTransferAccounts::Sol`, публичного получателя в Solana. Комментарий `// SPL:` показывает `SplWithdrawal`.
            * `owner_signers` пуст для этого конфиденциального вывода.
            * `data` содержит доказательство с нулевым разглашением и зашифрованную сдачу, созданные на предыдущем шаге.
          </Accordion>

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

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

            * `create_and_send_transaction` подписывает и отправляет `withdraw_ix` как транзакцию Solana.
            * `landed_slot` считывает слот подтверждения, используемый для ограничения выборки индексатора.
            * `sender` оплачивает комиссию и авторизует вывод.
          </Accordion>
        </AccordionGroup>
      </Step>
    </Steps>

    ### Полный пример кода

    Клонируйте и запустите пример:

    ```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>
      В примерах используется конфиденциальное кольцо в локальной сети/devnet, доступное [здесь](https://github.com/helius-labs/zolana-examples/blob/v0.3.0-alpha/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, connect, landed_slot};
    use solana_keypair::Keypair;
    use solana_signer::Signer;
    use zolana_client::{IndexerRpcConfig, Rpc};
    use zolana_interface::pda;
    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 client = connect("https://devnet.helius-rpc.com/?api-key=YOUR_API_KEY")?;
        let tree = pda::tree(0);

        // 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 this transaction's outputs, gated on its confirmed slot.
            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 read the funds deposited in this run.
            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 this transaction's outputs, gated on its confirmed slot.
            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 this transaction's outputs, gated on its confirmed slot.
            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 the funds remaining from this run 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>

## Связанные руководства

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

  <Card title="Transfer" icon="arrow-right-arrow-left" href="/docs/ru/privacy/guides/transfer" horizontal />

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

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

## Не нашли то, что искали?

<Callout type="info">
  Свяжитесь с нами! [Telegram](https://t.me/tilo_light) | [Электронная почта](mailto:sales@helius.xyz) | [Контакты](https://www.helius.dev/contact)
</Callout>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.