Каждая Virtual Environment предоставляет прекомпайл-контракт по адресу 0xc100000000000000000000000000000000000000. Вызов функций этого прекомпайла изнутри транзакции позволяет вашим контрактам напрямую манипулировать состоянием on-chain — слотами хранилища, балансами, nonce и логами событий — без имперсонации аккаунтов и деплоя вспомогательных прокси.
Эти чит-коды дополняют методы Admin RPC. Методы Admin RPC манипулируют состоянием извне транзакции (через JSON-RPC). Прекомпайл манипулирует состоянием изнутри транзакции (через Solidity-вызов). Используйте прекомпайл, когда ваш сетап должен оставаться атомарным, быть воспроизводимым по хешу транзакции или выполняться в цикле по многим слотам.
Интерфейс
Добавьте этот файл в ваш проект как IVnetPrecompile.sol:
// 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);
}
Ссылайтесь на него в вашем контракте по фиксированному адресу:
IVnetPrecompile constant VNET = IVnetPrecompile(0xc100000000000000000000000000000000000000);
Функции
Хранилище
| Функция | Параметры | Возвращает | Описание |
|---|
setStorageAt | address target, bytes32 slot, bytes32 value | bool | Записывает value в slot на target. |
getStorageAt | address target, bytes32 slot | bytes32 | Читает текущее значение slot на target. |
VNET.setStorageAt(tokenContract, balanceSlot, bytes32(uint256(1000e18)));
bytes32 val = VNET.getStorageAt(tokenContract, balanceSlot);
Баланс
| Функция | Параметры | Возвращает | Описание |
|---|
setBalance | address target, uint256 balance | bool | Устанавливает ETH-баланс target в точное значение. |
addBalance | address target, uint256 amount | bool | Добавляет amount к текущему ETH-балансу target. |
VNET.setBalance(account, 10 ether);
VNET.addBalance(account, 2 ether);
Nonce
| Функция | Параметры | Возвращает | Описание |
|---|
setNonce | address target, uint256 value | bool | Задаёт счётчик транзакций для target. |
VNET.setNonce(account, 42);
События
emitEvent эмитирует лог, у которого поле address — это target, а не вызывающий контракт. Нижестоящие потребители (индексеры, subgraphs, вебхуки) считают лог реальной эмиссией от target.
Девять перегрузок покрывают от нуля до четырёх индексируемых топиков с необязательным неиндексируемым data-payload:
| Перегрузка | Топики | Данные |
|---|
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,...)"). Пример, подделываем Transfer для USDC:
VNET.emitEvent(
USDC,
keccak256("Transfer(address,address,uint256)"),
bytes32(uint256(uint160(from))),
bytes32(uint256(uint160(to))),
abi.encode(amount)
);
Прекомпайл также эмитирует собственные диагностические логи (установка баланса, установка nonce, запись в хранилище) с address = 0xc100000000000000000000000000000000000000. При проверке чека транзакции на предмет подделанного лога фильтруйте logs[] по целевому адресу, чтобы игнорировать собственные записи прекомпайла.
Известные ограничения
- Virtual Environments на основе Arbitrum. Вызовы чит-кодов через
eth_call не работают из-за внутренних особенностей Arbitrum. Транзакции, оценка газа и симуляции работают как ожидается.
См. также