> ## 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. Приватный перевод перемещает токены внутри кольца между приватными кошельками.
2. Приватные переводы отправляются одной транзакцией Solana на адрес кошелька Solana.

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

<Tabs>
  <Tab title="Default Ring">
    | Поле | Видимость |
    | - | - |
    | Актив | Приватный. Зашифрован в блокчейне. |
    | Сумма | Приватная. Зашифрована в блокчейне. |
    | Отправитель | Публичный. Кольцо по умолчанию конфиденциально. |
    | Получатель | Публичный. Кольцо по умолчанию конфиденциально. |
    | Примечание (необязательно) | Приватное. Зашифровано в блокчейне. |
  </Tab>

  <Tab title="Custom Rings">
    | Перевод | Актив | Сумма | Отправитель | Получатель | Примечание (необязательно) |
    | - | - | - | - | - | - |
    | В пределах пользовательского конфиденциального кольца | Приватный | Приватная | Публичный | Публичный | Приватное |
    | В пределах пользовательского анонимного кольца | Приватный | Приватная | Приватный | Приватный | Приватное |
    | Из кольца по умолчанию в пользовательское конфиденциальное кольцо или обратно | Приватный | Приватная | Публичный | Публичный | Приватное |
    | Из конфиденциального кольца в пользовательское анонимное кольцо | Приватный | Приватная | Публичный | Приватный | Приватное |
    | Из пользовательского анонимного кольца в конфиденциальное кольцо | Приватный | Приватная | Приватный | Публичный | Приватное |

    Ретранслятор отправляет переводы из анонимных колец, поэтому публичный реестр не раскрывает исходный приватный кошелек. Идентификатор программы пользовательского кольца остается публичным.

    Переводы между кольцами должны соответствовать политике исходного кольца. Одна транзакция может объединять балансы из кольца по умолчанию и одного пользовательского кольца. Перевод между двумя пользовательскими кольцами проходит через кольцо по умолчанию.
  </Tab>
</Tabs>

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

## Как работает перевод

Приватный перевод работает аналогично публичному переводу Solana:

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

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

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

3. Среда выполнения Solana проверяет подписи и вызывает Solana Privacy Program, которая проверяет ZK-доказательство, не раскрывая зашифрованное состояние.

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 } from "@heliuslabs/zolana/interface";
            import {
              ConfidentialTransfer,
              ProofInputUtxo,
            } from "@heliuslabs/zolana/transaction";
            import { sendAndConfirmFactory } from "../src/lib.js";


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

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


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

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

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

            В этом примере входные UTXO выбираются вручную. Вспомогательные средства кошелька из SDK могут выбрать и зарезервировать их за вас. Рекомендации по выбору входов приведены в разделе [Выбор UTXO](/docs/ru/privacy/concepts/utxo#выбор-utxo).

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

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

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

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

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

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

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

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

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

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

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

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

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

            * `sendAndConfirm` подписывает и отправляет `transferInstruction` как транзакцию 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;
        use zolana_transaction::{instructions::transact::ConfidentialTransaction, SOL_MINT};

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

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

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

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

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

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

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

            В этом примере входные UTXO выбираются вручную. Вспомогательные средства кошелька из Rust SDK могут выбрать UTXO за вас. Ваше приложение должно отслеживать UTXO, используемые ожидающими переводами. Рекомендации по выбору входов приведены в разделе [Выбор UTXO](/docs/ru/privacy/concepts/utxo#выбор-utxo).

            * В примере расходуются средства из приватного токен-аккаунта Solana, созданного предыдущим внесением. Перевод может расходовать несколько UTXO.
            * `transfer_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 transfer = ConfidentialTransaction::new(vec![transfer_utxo], sender.pubkey())?;
            ```

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

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

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

            * `recipient.shielded_address()?` — защищённый адрес получателя. Выход перевода шифруется с помощью ключа просмотра получателя.
            * `transfer.transfer_sol` выбирает SOL. Комментарий `// SPL:` показывает адрес выпуска токена для активов SPL и Token 2022.
            * `TRANSFER_AMOUNT` указывается в базовых единицах актива. Для SOL используются лампорты. Для активов SPL и Token 2022 используются базовые единицы токена.
            * `transfer.encrypt(&sender)` шифрует выходы и создаёт входные данные для системы доказательства с нулевым разглашением.
          </Accordion>

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

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

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

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

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

            * `payer` подписывает и оплачивает транзакцию Solana. Комиссию может оплатить спонсор газа.
            * `input_trees` определяет дерево Меркла состояния, содержащее потраченные UTXO.
            * `output_tree` определяет дерево Меркла состояния, которое принимает обязательства для выхода получателя и сдачи отправителя.
            * `interface_transfer_accounts` пуст, поскольку приватный перевод перемещает актив только между приватными балансами и не взаимодействует с публичными балансами в аккаунтах Solana или токен-аккаунтах.
            * `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(
                &[transfer_ix],
                sender.pubkey(),
                &[&sender],
                client.compute_budget(),
            )?;
            let slot = landed_slot(&client, signature)?;
            ```

            * `create_and_send_transaction` подписывает и отправляет `transfer_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="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.