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

# Solidity Cheatcodes

> 每个 Virtual Environment 都在 0xc100000000000000000000000000000000000000 上提供的 IVnetPrecompile 接口参考，用于 Solidity 级作弊码。

每个 Virtual Environment 都在 `0xc100000000000000000000000000000000000000` 上暴露一个预编译合约。从交易内部调用此预编译上的函数可让您的合约直接操纵链上状态、存储槽、余额、nonce 和事件日志，而无需假冒账户或部署辅助代理。

这些作弊码是对 [Admin RPC 方法](/virtual-environments/admin-rpc)的补充。Admin RPC 方法从交易外部操纵状态（通过 JSON-RPC）。预编译从交易内部操纵状态（通过 Solidity 调用）。当您的设置需要保持原子性、可从 tx 哈希重放，或者要在多个槽上循环执行时，请使用预编译。

## 接口

将此文件作为 `IVnetPrecompile.sol` 添加到您的项目中：

```solidity title="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);
}
```

在您的合约中通过固定地址引用它：

```solidity theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
IVnetPrecompile constant VNET = IVnetPrecompile(0xc100000000000000000000000000000000000000);
```

## 函数

### Storage

| 函数             | 参数                                                | 返回值       | 描述                               |
| -------------- | ------------------------------------------------- | --------- | -------------------------------- |
| `setStorageAt` | `address target`, `bytes32 slot`, `bytes32 value` | `bool`    | 将 `value` 写入 `target` 上的 `slot`。 |
| `getStorageAt` | `address target`, `bytes32 slot`                  | `bytes32` | 读取 `target` 上 `slot` 的当前值。       |

```solidity theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
VNET.setStorageAt(tokenContract, balanceSlot, bytes32(uint256(1000e18)));
bytes32 val = VNET.getStorageAt(tokenContract, balanceSlot);
```

### Balance

| 函数           | 参数                                  | 返回值    | 描述                                |
| ------------ | ----------------------------------- | ------ | --------------------------------- |
| `setBalance` | `address target`, `uint256 balance` | `bool` | 将 `target` 的 ETH 余额设置为精确值。        |
| `addBalance` | `address target`, `uint256 amount`  | `bool` | 向 `target` 的当前 ETH 余额添加 `amount`。 |

```solidity theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
VNET.setBalance(account, 10 ether);
VNET.addBalance(account, 2 ether);
```

### Nonce

| 函数         | 参数                                | 返回值    | 描述                 |
| ---------- | --------------------------------- | ------ | ------------------ |
| `setNonce` | `address target`, `uint256 value` | `bool` | 设置 `target` 的交易计数。 |

```solidity theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
VNET.setNonce(account, 42);
```

### Events

`emitEvent` 触发一条 `address` 字段为 `target`（而不是调用合约）的日志。下游消费者（索引器、subgraph、webhook）会将该日志视为来自 `target` 的真实发出。

九个重载覆盖了从零到四个索引 topic，以及可选的未索引 data 载荷：

| 重载                                        | Topics | Data |
| ----------------------------------------- | ------ | ---- |
| `emitEvent(target, data)`                 | 0      | ✓    |
| `emitEvent(target, t1)`                   | 1      | ,    |
| `emitEvent(target, t1, data)`             | 1      | ✓    |
| `emitEvent(target, t1, t2)`               | 2      | ,    |
| `emitEvent(target, t1, t2, data)`         | 2      | ✓    |
| `emitEvent(target, t1, t2, t3)`           | 3      | ,    |
| `emitEvent(target, t1, t2, t3, data)`     | 3      | ✓    |
| `emitEvent(target, t1, t2, t3, t4)`       | 4      | ,    |
| `emitEvent(target, t1, t2, t3, t4, data)` | 4      | ✓    |

`topic1` 通常是 `keccak256("EventName(type,type,...)")`。示例，伪造一次 USDC 的 `Transfer`：

```solidity theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
VNET.emitEvent(
    USDC,
    keccak256("Transfer(address,address,uint256)"),
    bytes32(uint256(uint160(from))),
    bytes32(uint256(uint160(to))),
    abi.encode(amount)
);
```

<Note>
  预编译还会发出它自己的诊断日志（余额设置、nonce 设置、存储写入），其 `address` = `0xc100000000000000000000000000000000000000`。检查伪造日志的收据时，请按目标地址过滤 `logs[]` 以忽略预编译自身的条目。
</Note>

## 已知限制

* **基于 Arbitrum 的 Virtual Environments。** 由于 Arbitrum 内部机制，通过 `eth_call` 进行的作弊码调用无法工作。交易、gas 估算和模拟按预期工作。

## 另请参阅

* [Admin RPC](/virtual-environments/admin-rpc)，用于从交易外部操纵状态的等价 JSON-RPC 方法。
* [Foundry 作弊码教程](/virtual-environments/develop/solidity-cheatcodes)，部署可复用作弊码演练环境的端到端教程。
