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

# Туториал по чит-кодам в Foundry

> Разверните песочницу чит-кодов в Virtual Environment и используйте cast send для манипуляции балансами, nonce, хранилищем и логами событий из транзакций.

Этот туториал строит модульную песочницу чит-кодов в Foundry. Вы деплоите один контракт в Virtual Environment, а затем вызываете его функции через `cast send`, чтобы использовать каждый [Solidity precompile cheatcode](/virtual-environments/precompiles) изнутри транзакций.

## Что вы соберёте

Три файла:

* `src/IVnetPrecompile.sol`: интерфейс прекомпайла.
* `src/MyTestSetup.sol`: контракт-песочница. Каждая функция оборачивает один вызов прекомпайла, так что вы деплоите один раз, а затем вызываете отдельные функции, чтобы манипулировать одним элементом состояния за раз.
* `script/Deploy.s.sol`: деплой через `forge script`.

### Карта функций

| Категория | Функция                                 | Оборачивает                                          |
| --------- | --------------------------------------- | ---------------------------------------------------- |
| Balance   | `forceBalance(account, newBalance)`     | `setBalance`                                         |
|           | `topUp(account, amount)`                | `addBalance`                                         |
| Nonce     | `forceNonce(account, newNonce)`         | `setNonce`                                           |
| Storage   | `writeStorage(target, slot, value)`     | `setStorageAt`                                       |
|           | `readStorage(target, slot) → bytes32`   | `getStorageAt`                                       |
| Events    | `fireTransfer(token, from, to, amount)` | `emitEvent` (ERC-20 `Transfer`)                      |
|           | `fireEvent(target, topics[], data)`     | `emitEvent` (общий, 0–4 топика)                      |
| Demo      | `demoScenario(account)`                 | Комбинирует balance, nonce, storage, USDC `Transfer` |

## Предварительные условия

* Аккаунт Tenderly с проектом.
* Установленный Foundry (`curl -L https://foundry.paradigm.xyz | bash && foundryup`).

<Steps>
  ### Создайте Virtual Environment

  В Tenderly Dashboard перейдите в **Virtual Environments** и создайте Virtual Environment. Скопируйте URL **Admin RPC** — публичный RPC возвращает 401 при вызовах пополнения и верификации, поэтому используйте Admin RPC на протяжении всего туториала.

  ### Инициализируйте Foundry-проект

  ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  forge init tenderly-cheatcodes
  cd tenderly-cheatcodes
  ```

  ### Добавьте интерфейс прекомпайла

  Сохраните как `src/IVnetPrecompile.sol`:

  ```solidity title="src/IVnetPrecompile.sol" theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  // SPDX-License-Identifier: MIT
  pragma solidity ^0.8.0;

  interface IVnetPrecompile {
      function setStorageAt(address target, bytes32 slot, bytes32 value) external returns (bool);
      function getStorageAt(address target, bytes32 slot) external view returns (bytes32);
      function setBalance(address target, uint256 balance) external returns (bool);
      function addBalance(address target, uint256 amount) external returns (bool);
      function setNonce(address target, uint256 value) external returns (bool);

      function emitEvent(address target, bytes calldata data) external returns (bool);
      function emitEvent(address target, bytes32 topic1) external returns (bool);
      function emitEvent(address target, bytes32 topic1, bytes calldata data) external returns (bool);
      function emitEvent(address target, bytes32 topic1, bytes32 topic2) external returns (bool);
      function emitEvent(address target, bytes32 topic1, bytes32 topic2, bytes calldata data) external returns (bool);
      function emitEvent(address target, bytes32 topic1, bytes32 topic2, bytes32 topic3) external returns (bool);
      function emitEvent(address target, bytes32 topic1, bytes32 topic2, bytes32 topic3, bytes calldata data) external returns (bool);
      function emitEvent(address target, bytes32 topic1, bytes32 topic2, bytes32 topic3, bytes32 topic4) external returns (bool);
      function emitEvent(address target, bytes32 topic1, bytes32 topic2, bytes32 topic3, bytes32 topic4, bytes calldata data) external returns (bool);
  }
  ```

  ### Добавьте контракт-песочницу

  Сохраните как `src/MyTestSetup.sol`:

  ```solidity title="src/MyTestSetup.sol" theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  // SPDX-License-Identifier: MIT
  pragma solidity ^0.8.0;

  import {IVnetPrecompile} from "./IVnetPrecompile.sol";

  contract MyTestSetup {
      IVnetPrecompile constant VNET = IVnetPrecompile(0xc100000000000000000000000000000000000000);

      bytes32 constant TRANSFER_TOPIC = keccak256("Transfer(address,address,uint256)");

      function forceBalance(address account, uint256 amount) external {
          VNET.setBalance(account, amount);
      }

      function topUp(address account, uint256 amount) external {
          VNET.addBalance(account, amount);
      }

      function forceNonce(address account, uint256 n) external {
          VNET.setNonce(account, n);
      }

      function writeStorage(address target, bytes32 slot, bytes32 value) external {
          VNET.setStorageAt(target, slot, value);
      }

      function readStorage(address target, bytes32 slot) external view returns (bytes32) {
          return VNET.getStorageAt(target, slot);
      }

      function fireTransfer(address token, address from, address to, uint256 amount) external {
          VNET.emitEvent(
              token,
              TRANSFER_TOPIC,
              bytes32(uint256(uint160(from))),
              bytes32(uint256(uint160(to))),
              abi.encode(amount)
          );
      }

      function fireEvent(address target, bytes32[] calldata topics, bytes calldata data) external {
          if (topics.length == 0)      VNET.emitEvent(target, data);
          else if (topics.length == 1) VNET.emitEvent(target, topics[0], data);
          else if (topics.length == 2) VNET.emitEvent(target, topics[0], topics[1], data);
          else if (topics.length == 3) VNET.emitEvent(target, topics[0], topics[1], topics[2], data);
          else                         VNET.emitEvent(target, topics[0], topics[1], topics[2], topics[3], data);
      }

      function demoScenario(address account) external {
          VNET.setBalance(account, 10 ether);
          VNET.setNonce(account, 5);
          VNET.setStorageAt(account, bytes32(uint256(0)), bytes32(uint256(0x1234)));
          VNET.addBalance(account, 2 ether);
          VNET.emitEvent(
              0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48,
              TRANSFER_TOPIC,
              bytes32(uint256(0)),
              bytes32(uint256(uint160(account))),
              abi.encode(uint256(1000e6))
          );
      }
  }
  ```

  ### Добавьте deploy-скрипт

  Сохраните как `script/Deploy.s.sol`:

  ```solidity title="script/Deploy.s.sol" theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  // SPDX-License-Identifier: MIT
  pragma solidity ^0.8.0;

  import {Script} from "forge-std/Script.sol";
  import {MyTestSetup} from "../src/MyTestSetup.sol";

  contract Deploy is Script {
      function run() external {
          vm.startBroadcast();
          new MyTestSetup();
          vm.stopBroadcast();
      }
  }
  ```

  ### Настройте переменные окружения

  Сохраните как `.env`:

  ```bash title=".env" theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  VNET_RPC=https://virtual.mainnet.eu.rpc.tenderly.co/<your-virtual-env-uuid>
  PRIVATE_KEY=0x...
  DEPLOYER=0x...
  ```

  Подгрузите файл: `source .env`.

  ### Пополните аккаунт деплойера

  ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  cast rpc tenderly_setBalance "[\"$DEPLOYER\"]" "0x56BC75E2D63100000" --rpc-url "$VNET_RPC"
  ```

  ### Задеплойте и верифицируйте

  ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  forge script script/Deploy.s.sol:Deploy \
      --rpc-url "$VNET_RPC" \
      --private-key "$PRIVATE_KEY" \
      --broadcast --slow \
      --verify \
      --verifier custom \
      --verifier-url "$VNET_RPC/verify"
  ```

  Экспортируйте задеплоенный адрес из вывода скрипта:

  ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  export TEST_CONTRACT=0x...
  ```

  ### Попробуйте чит-коды

  Каждый вызов — отдельная транзакция. Можно перезапускать с другими аргументами в любой момент, без повторного деплоя.

  ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  # Set 999 ETH on an arbitrary account
  cast send "$TEST_CONTRACT" "forceBalance(address,uint256)" \
      0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 999000000000000000000 \
      --rpc-url "$VNET_RPC" --private-key "$PRIVATE_KEY"

  # Set vitalik.eth's nonce to 4242
  cast send "$TEST_CONTRACT" "forceNonce(address,uint256)" \
      0xd8da6bf26964af9d7eed9e03e53415d37aa96045 4242 \
      --rpc-url "$VNET_RPC" --private-key "$PRIVATE_KEY"

  # Write storage slot 0 of a contract
  cast send "$TEST_CONTRACT" "writeStorage(address,bytes32,bytes32)" \
      "$TARGET" \
      0x0000000000000000000000000000000000000000000000000000000000000000 \
      0x0000000000000000000000000000000000000000000000000000000000001234 \
      --rpc-url "$VNET_RPC" --private-key "$PRIVATE_KEY"

  # Spoof a 1 DAI Transfer from the real DAI contract
  cast send "$TEST_CONTRACT" "fireTransfer(address,address,address,uint256)" \
      0x6B175474E89094C44Da98b954EedeAC495271d0F \
      0xCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC \
      0xDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD \
      1000000000000000000 \
      --rpc-url "$VNET_RPC" --private-key "$PRIVATE_KEY"

  # Spoof a BAYC ERC-721 Transfer of token #1234 (all args indexed, data is empty)
  cast send "$TEST_CONTRACT" "fireEvent(address,bytes32[],bytes)" \
      0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D \
      '[0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef,0x000000000000000000000000CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC,0x000000000000000000000000DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD,0x00000000000000000000000000000000000000000000000000000000000004d2]' \
      0x \
      --rpc-url "$VNET_RPC" --private-key "$PRIVATE_KEY"
  ```

  ### Проверьте изменения состояния

  ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  cast balance 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA --rpc-url "$VNET_RPC"
  cast nonce   0xd8da6bf26964af9d7eed9e03e53415d37aa96045 --rpc-url "$VNET_RPC"
  cast storage "$TARGET" 0                                --rpc-url "$VNET_RPC"
  ```

  ### Подтвердите подделанный лог

  Получите чек и проверьте `logs[]` на запись, у которой `address` совпадает с контрактом, чью эмиссию вы подделали:

  ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  cast receipt <TX_HASH> --rpc-url "$VNET_RPC" --json | jq '.logs'
  ```

  Для примера с BAYC чек должен содержать:

  ```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  {
    "address": "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d",
    "topics": [
      "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
      "0x000000000000000000000000cccccccccccccccccccccccccccccccccccccccc",
      "0x000000000000000000000000dddddddddddddddddddddddddddddddddddddddd",
      "0x00000000000000000000000000000000000000000000000000000000000004d2"
    ],
    "data": "0x"
  }
  ```

  `address` совпадает с контрактом BAYC, `topics[0]` — это хеш сигнатуры события `Transfer`, а `topics[1–3]` несут индексируемые `from`, `to` и `tokenId`. Индексер, слушающий трансферы BAYC, обработает этот лог идентично реальной эмиссии.

  <Note>
    Прекомпайл также эмитирует диагностические логи с `address` = `0xc100000000000000000000000000000000000000`, записывающие каждую мутацию состояния. Фильтруйте `logs[]` по целевому адресу, чтобы отделить подделанный лог от собственных записей прекомпайла.
  </Note>
</Steps>

## См. также

* [Справочник по Solidity Cheatcodes](/virtual-environments/precompiles) — полное определение интерфейса и таблица функций.
* [Admin RPC](/virtual-environments/admin-rpc) — манипуляция состоянием извне транзакции через JSON-RPC.
