> ## 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 на приватный баланс с полным примером кода.

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

#### Депозит: что остается приватным

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

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

## Как работает депозит

Депозит — это публичный перевод в Solana, в результате которого средства поступают на приватный баланс.

1. Баланс SOL или SPL пользователя публично доступен в блокчейне.
2. Кошелек задает сумму и получателя, формирует депозит, а владелец подписывает его. Для депозита не требуется доказательство с нулевым разглашением.
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: Get public balance
    RPC-->>Wallet: Public state
    Note over Wallet: Set amount and recipient
    Note over Wallet: Build deposit, sign
    Wallet->>RPC: Submit Transaction
    RPC->>Solana: Forward transaction
    Note over Solana: Verify signatures
    Note over Solana: CPI Solana Privacy Program
    RPC-->>Wallet: Transaction signature
```

<Accordion title="Compare to Solana Transfer">
  1. Баланс SOL или SPL пользователя публично доступен в блокчейне.
  2. Кошелек считывает публичное состояние, формирует перевод, а владелец подписывает его.
  3. Среда выполнения 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
      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>

        Для каждого выпуска SPL требуется интерфейс токена, в котором будут храниться внесенные токены. Если интерфейс не существует, создайте его в той же транзакции, что и депозит. Создатель оплачивает аренду аккаунта.

        <Tabs>
          <Tab title="Deposit Tokens with an Existing Interface">
            <Tabs>
              <Tab title="SOL">
                ```typescript theme={"system"}
                import {
                  depositInstruction,
                  DepositAsset,
                } from "@heliuslabs/zolana/interface";
                import { sendAndConfirmFactory } from "../src/lib.js";


                  const sendAndConfirm = sendAndConfirmFactory(
                    client,
                    senderSigner,
                  );
                  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,
                      },
                    ],
                  });

                  const depositTx = await sendAndConfirm([
                    depositIx,
                  ]);
                ```
              </Tab>

              <Tab title="SPL">
                ```typescript theme={"system"}
                import {
                  depositInstruction,
                  DepositAsset,
                } from "@heliuslabs/zolana/interface";
                import { sendAndConfirmFactory } from "../src/lib.js";


                  const sendAndConfirm = sendAndConfirmFactory(
                    client,
                    senderSigner,
                  );
                  const senderViewTag =
                    senderAddress.confidentialViewTag();
                  const depositIx = await depositInstruction({
                    tree: client.tree,
                    depositor: senderSigner,
                    deposits: [
                      {
                        asset: DepositAsset.spl({
                          mint: spl.mint,
                          sourceTokenAccount: spl.sourceTokenAccount,
                          tokenProgram: spl.tokenProgram,
                        }),
                        viewTag: senderViewTag,
                        recipientOwnerHash:
                          senderAddress.ownerHash(),
                        amount: DEPOSIT_AMOUNT,
                      },
                    ],
                  });

                  const depositTx = await sendAndConfirm([
                    depositIx,
                  ]);
                ```
              </Tab>
            </Tabs>

            <AccordionGroup>
              <Accordion title="1. Move public tokens into the sender's private balance">
                ```typescript theme={"system"}
                import {
                  depositInstruction,
                  DepositAsset,
                } from "@heliuslabs/zolana/interface";

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

                * `senderAddress` — это <Tooltip tip="The public key bundle (signing, nullifier, and viewing keys) published in a user's registry record. Not an onchain address.">защищенный адрес</Tooltip> получателя. В этом примере средства вносятся на баланс отправителя. Получателем может быть отправитель или сторонний кошелек.
                * `senderViewTag` — это `senderAddress.confidentialViewTag()`, публичный ключ отправителя в Solana для конфиденциальных колец. Индексатор использует его, чтобы возвращать соответствующие зашифрованные выходы.
                * `client.tree` — это дерево Меркла состояния, которое получает новое обязательство выхода.
                * `depositor` / `senderSigner` финансирует и подписывает депозит из публичного баланса в приватный.
                * `DepositAsset.sol()` выбирает SOL. `DepositAsset.spl` принимает адрес выпуска, исходный аккаунт токенов и программу токенов для активов SPL и Token 2022.
                * `recipientOwnerHash` — это `senderAddress.ownerHash()`. Этот хеш публичных ключей подписи и нуллификатора получателя записывается в поле владельца нового приватного аккаунта токенов.
                * `DEPOSIT_AMOUNT` использует лампорты для SOL или базовые единицы токена для активов SPL и Token 2022.
              </Accordion>

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

                const depositTx = await sendAndConfirm([
                  depositIx,
                ]);
                ```

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

          <Tab title="Deposit Tokens without an Interface">
            ```mermaid theme={"system"}
            %%{init: {
              'theme': 'base',
              'themeVariables': {
                'primaryColor': '#FFFFFF',
                'primaryTextColor': '#737373',
                'primaryBorderColor': '#9CA3AF',
                'lineColor': '#FF6B35',
                'edgeLabelBackground': '#FFFFFF'
              }
            }}%%
            flowchart LR
                GetAccount["Get account"] --> Exists{"Interface PDA exists?"}
                Exists -->|Yes| Deposit["Deposit to private balance"]
                Exists -->|No| Create["Create interface PDA<br/>and token account"]
                Create --> Deposit
            ```

            ```typescript theme={"system"}
            import { getSplAssetVaultAddress } from "@heliuslabs/zolana/addresses";
            import { getCreateSplInterfaceInstructionAsync } from "@heliuslabs/zolana/instructions";
            import { depositInstruction, DepositAsset } from "@heliuslabs/zolana/interface";
            import { TOKEN_PROGRAM_ADDRESS } from "@solana-program/token";
            import type { Instruction } from "@solana/kit";

            // 1. Check whether the mint already has an interface PDA, the escrow that holds deposited tokens.
            // A second create fails the transaction, so skip step 2 when the account exists.
            const interfaceAddress =
              await getSplAssetVaultAddress(mint);
            const interfaceAccount =
              await client.getAccount(interfaceAddress);
            const instructions: Instruction[] = [];

            // 2. Create the mint registry PDA and interface token account.
            // The authority signer pays their rent.
            if (!interfaceAccount) {
              const createInterfaceIx =
                await getCreateSplInterfaceInstructionAsync(
                  {
                    authority: senderSigner,
                    mint,
                    tokenProgram: TOKEN_PROGRAM_ADDRESS,
                  },
                );
              instructions.push(createInterfaceIx);
            }

            // 3. Move public tokens into the sender's private balance.
            // A deposit from a public balance reveals sender, recipient, asset and amount.
            const depositIx = await depositInstruction({
              tree: client.tree,
              depositor: senderSigner,
              deposits: [
                {
                  asset: DepositAsset.spl({
                    mint,
                    sourceTokenAccount: sourceToken,
                    tokenProgram: TOKEN_PROGRAM_ADDRESS,
                  }),
                  viewTag:
                    senderAddress.confidentialViewTag(),
                  recipientOwnerHash:
                    senderAddress.ownerHash(),
                  amount: DEPOSIT_AMOUNT,
                },
              ],
            });

            // 4. Send the instructions in one transaction.
            instructions.push(depositIx);
            const depositTx =
              await sendAndConfirm(instructions);
            ```

            <Info>
              Полный пример: [**TypeScript**](https://github.com/helius-labs/zolana-examples/blob/main/typescript-client/examples/deposit_with_interface_setup.ts).
            </Info>
          </Tab>
        </Tabs>
      </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>
        ### Внесение средств на приватный баланс

        Для каждого выпуска SPL требуется интерфейс токена, в котором будут храниться внесенные токены. Если интерфейс не существует, создайте его в той же транзакции, что и депозит. Создатель оплачивает аренду аккаунта.

        <Tabs>
          <Tab title="Deposit Tokens with an Existing Interface">
            ```rust theme={"system"}
            use zolana_program::instruction::{AssetDeposit, Deposit, DepositAsset};


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

            <AccordionGroup>
              <Accordion title="1. Move public SOL into the sender's private balance">
                ```rust theme={"system"}
                use zolana_program::instruction::{AssetDeposit, Deposit, DepositAsset};


                    // 1. Move public SOL into the sender's private balance.
                        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()?;
                ```

                * `sender_shielded_address` — это <Tooltip tip="The public key bundle (signing, nullifier, and viewing keys) published in a user's registry record. Not an onchain address.">защищенный адрес</Tooltip> получателя. В этом примере средства вносятся на баланс отправителя. Получателем может быть отправитель или сторонний кошелек.
                * `view_tag` — это `sender_shielded_address.confidential_view_tag()`, публичный ключ отправителя в Solana для конфиденциальных колец. Индексатор использует его, чтобы возвращать соответствующие зашифрованные выходы.
                * `tree` — это дерево Меркла состояния, которое получает новое обязательство выхода.
                * `depositor` / `sender` финансирует и подписывает депозит из публичного баланса в приватный.
                * `DepositAsset::Sol` выбирает SOL. В комментариях к `// SPL:` показан `DepositAsset::Spl` с адресом выпуска, исходным аккаунтом токенов и программой токенов для активов SPL и Token 2022.
                * `owner` — это `sender_shielded_address.owner_hash()`. Этот хеш публичных ключей подписи и нуллификатора получателя записывается в поле владельца нового приватного аккаунта токенов.
                * `DEPOSIT_AMOUNT` использует лампорты для SOL или базовые единицы токена для активов SPL и Token 2022. `memo` — это `None`.
              </Accordion>

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

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

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

          <Tab title="Deposit Tokens without an Interface">
            ```mermaid theme={"system"}
            %%{init: {
              'theme': 'base',
              'themeVariables': {
                'primaryColor': '#FFFFFF',
                'primaryTextColor': '#737373',
                'primaryBorderColor': '#9CA3AF',
                'lineColor': '#FF6B35',
                'edgeLabelBackground': '#FFFFFF'
              }
            }}%%
            flowchart LR
                GetAccount["Get account"] --> Exists{"Interface PDA exists?"}
                Exists -->|Yes| Deposit["Deposit to private balance"]
                Exists -->|No| Create["Create interface PDA<br/>and token account"]
                Create --> Deposit
            ```

            ```rust theme={"system"}
            use zolana_interface::{
                instruction::{AssetDeposit, CreateSplInterface, Deposit, DepositAsset, DepositSplAccounts},
                pda,
            };

            // 1. Check whether the mint already has an interface PDA, the escrow that holds deposited tokens.
            // A second create fails the transaction, so skip step 2 when the account exists.
            let interface_address = pda::spl_interface(&mint);
            let interface_account = client.get_account(interface_address)?;
            let mut instructions = Vec::new();

            // 2. Create the mint registry PDA and interface token account.
            // The authority signer pays their rent.
            if interface_account.is_none() {
                let create_interface_ix = CreateSplInterface {
                    authority: sender_pubkey,
                    mint,
                    token_program,
                }
                .instruction();
                instructions.push(create_interface_ix);
            }

            // 3. Move public tokens into the sender's private balance.
            // A deposit from a public balance reveals sender, recipient, asset and amount.
            let deposit_ix = Deposit {
                tree,
                depositor: sender_pubkey,
                deposits: vec![AssetDeposit {
                    asset: DepositAsset::Spl(DepositSplAccounts {
                        mint,
                        user_token: source_token,
                        token_program,
                    }),
                    view_tag: sender_shielded_address.confidential_view_tag()?,
                    owner: sender_shielded_address.owner_hash()?,
                    amount: DEPOSIT_AMOUNT,
                    utxo_data: None,
                    memo: None,
                }],
            }
            .instruction()?;

            // 4. Send the instructions in one transaction.
            instructions.push(deposit_ix);
            let signature = client.create_and_send_transaction(
                &instructions,
                sender_pubkey,
                &[&sender_solana_keypair],
                client.compute_budget(),
            )?;
            let slot = landed_slot(&client, signature)?;
            ```

            <Info>
              Полный пример: [**Rust**](https://github.com/helius-labs/zolana-examples/blob/main/rust-client/examples/deposit_with_interface_setup.rs).
            </Info>
          </Tab>
        </Tabs>
      </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="Create a Private Wallet" icon="wallet" href="/docs/ru/privacy/integration/wallet#создание-приватного-кошелька" horizontal />

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

  <Card title="Withdraw" icon="arrow-up-from-bracket" href="/docs/ru/privacy/guides/withdraw" 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.