EIP-7702 после Pectra: Практическое руководство для разработчиков приложений Ethereum
7 мая 2025 года обновление Ethereum Pectra (Prague + Electra) было запущено в основной сети. Среди наиболее заметных для разработчиков изменений — EIP-7702, который позволяет внешнему аккаунту (EOA) «подключать» логику смарт-контракта без переноса средств или изменения адресов. Если вы создаете кошельки, децентрализованные приложения или ретрансляторы, это открывает более простой путь к улучшенному UX смарт-аккаунтов.
Ниже представлено краткое руководство, ориентированное на реализацию: что было фактически выпущено, как работает 7702, когда его выбрать вместо чистого ERC-4337, а также готовый шаблон, который вы можете адаптировать уже сегодня.
Что было фактически выпущено
- EIP-7702 включен в окончательный объем Pectra. Мета-EIP для хардфорка Pectra официально включает 7702 в список изменений.
- Детали активации: Pectra был активирован в основной сети на эпохе 364032 7 мая 2025 года после успешных активаций во всех основных тестовых сетях.
- Примечание по инструментарию: Solidity v0.8.30 обновил свою целевую EVM по умолчанию до prague для совместимости с Pectra. Вам потребуется обновить свои компиляторы и CI-конвейеры, особенно если вы используете конкретные версии.
EIP-7702 — Как это работает (основы)
EIP-7702 вводит новый тип транзакций и механизм для EOA, позволяющий делегировать свою логику выполнения смарт-контракту.
- Новый тип транзакций (0x04): Транзакция типа 4 включает новое поле под названием
authorization_list
. Этот список содержит одну или несколько кортежей авторизации —(chain_id, address, nonce, y_parity, r, s)
— каждый из которых подписан приватным ключом EOA. При обработке этой транзакции протокол записывает индикатор делегирования в поле кода EOA:0xef0100 || address
. С этого момента любые вызовы EOA проксируются на указанныйaddress
( реализация), но выполняются в контексте хранения и баланса EOA. Это делегирование остается активным до тех пор, пока оно не будет явно изменено. - Область действия цепочки: Авторизация может быть специфичной для цепочки путем предоставления
chain_id
, или она может применяться ко всем цепочкам, еслиchain_id
установлен в0
. Это позволяет развертывать один и тот же контракт реализации в нескольких сетях, не требуя от пользователей подписывать новую авторизацию для каждой из них. - Отзыв: Чтобы вернуть EOA к его исходному, непрограммируемому поведению, вы просто отправляете еще одну транзакцию 7702, где
address
реализации устанавливается в нулевой адрес. Это очищает индикатор делегирования. - Самостоятельное спонсирование против ретрансляции: EOA может отправить транзакцию типа 4 самостоятельно, или сторонний ретранслятор может отправить ее от имени EOA. Последнее часто используется для создания пользовательского опыта без газа. Обработка nonce немного отличается в зависимости от метода, поэтому важно использовать библиотеки, которые правильно управляют этим различием.
Изменение модели безопасности: Поскольку оригинальный приватный ключ EOA все еще существует, он всегда может отменить любые правила смарт-контракта (например, социальное восстановление или лимиты расходов), отправив новую транзакцию 7702 для изменения делегирования. Это фундаментальное изменение. Контракты, которые полагаются на
tx.origin
для проверки того, что вызов исходит от EOA, должны быть повторно проверены, так как 7702 может нарушить эти предположения. Соответственно, проверьте свои рабочие процессы.
7702 или ERC-4337? (И когда их комбинировать)
И EIP-7702, и ERC-4337 обеспечивают абстракцию аккаунтов, но они служат разным целям.
- Выбирайте EIP-7702, когда…
- Вы хотите предоставить мгновенный UX смарт-аккаунта для существующих EOA, не заставляя пользователей переносить средства или менять адреса.
- Вам нужны последовательные адреса в разных цепочках, которые могут быть постепенно обновлены новыми функциями.
- Вы хотите поэтапно перейти к абстракции аккаунтов, начиная с простых функций и постепенно добавляя сложность.
- Выбирайте чистый ERC-4337, когда…
- Ваш продукт требует полной программируемости и сложных механизмов политик (например, мультиподпись, расширенное восстановление) с первого дня.
- Вы создаете для новых пользователей, у которых нет существующих EOA, что делает новые адреса смарт-аккаунтов и связанную с ними настройку приемлемыми.
- Комбинируйте их: Самый мощный паттерн — использовать оба. EOA может использовать транзакцию 7702 для назначения реализации кошелька ERC-4337 в качестве своей логики. Это заставляет EOA вести себя как аккаунт 4337, позволяя ему быть объединенным, спонсируемым пеймастерами и обрабатываемым существующей инфраструктурой 4337 — и все это без необходимости для пользователя в новом адресе. Это перспективный путь, явно поощряемый авторами EIP.
Минимальный шаблон 7702, который вы можете адаптировать
Вот практический пример контракта реализации и клиентского кода для его активации.
1. Маленький, поддающийся аудиту контракт реализации
Этот код контракта будет выполняться в контексте EOA после назначения. Держите его небольшим, поддающимся аудиту и рассмотрите возможность добавления механизма обновления.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @notice Executes calls from the EOA context when designated via EIP-7702.
contract DelegatedAccount {
// Unique storage slot to avoid collisions with other contracts.
bytes32 private constant INIT_SLOT =
0x3fb93b3d3dcd1d1f4b4a1a8db6f4c5d55a1b7f9ac01dfe8e53b1b0f35f0c1a01;
event Initialized(address indexed account);
event Executed(address indexed to, uint256 value, bytes data, bytes result);
modifier onlyEOA() {
// Optional: add checks to restrict who can call certain functions.
_;
}
function initialize() external payable onlyEOA {
// Set a simple one-time init flag in the EOA's storage.
bytes32 slot = INIT_SLOT;
assembly {
if iszero(iszero(sload(slot))) { revert(0, 0) } // Revert if already initialized
sstore(slot, 1)
}
emit Initialized(address(this));
}
function execute(address to, uint256 value, bytes calldata data)
external
payable
onlyEOA
returns (bytes memory result)
{
(bool ok, bytes memory ret) = to.call{value: value}(data);
require(ok, "CALL_FAILED");
emit Executed(to, value, data, ret);
return ret;
}
function executeBatch(address[] calldata to, uint256[] calldata value, bytes[] calldata data)
external
payable
onlyEOA
{
uint256 n = to.length;
require(n == value.length && n == data.length, "LENGTH_MISMATCH");
for (uint256 i = 0; i < n; i++) {
(bool ok, ) = to[i].call{value: value[i]}(data[i]);
require(ok, "CALL_FAILED");
}
}
}
2. Назначение контракта на EOA (транзакция типа 4) с помощью viem
Современные клиенты, такие как viem
, имеют встроенные вспомогательные функции для подписания авторизаций и отправки транзакций типа 4. В этом примере аккаунт relayer
оплачивает газ для обновления eoa
.
import { createWalletClient, http, encodeFunctionData } from "viem";
import { sepolia } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
import { abi, implementationAddress } from "./DelegatedAccountABI";
// 1. Define the relayer (sponsors gas) and the EOA to be upgraded
const relayer = privateKeyToAccount(process.env.RELAYER_PK as `0x${string}`);
const eoa = privateKeyToAccount(process.env.EOA_PK as `0x${string}`);
const client = createWalletClient({
account: relayer,
chain: sepolia,
transport: http(),
});
// 2. The EOA signs the authorization pointing to the implementation contract
const authorization = await client.signAuthorization({
account: eoa,
contractAddress: implementationAddress,
// If the EOA itself were sending this, you would add: executor: 'self'
});
// 3. The relayer sends a Type-4 transaction to set the EOA's code and call initialize()
const hash = await client.sendTransaction({
to: eoa.address, // The destination is the EOA itself
authorizationList: [authorization], // The new EIP-7702 field
data: encodeFunctionData({ abi, functionName: "initialize" }),
});
// 4. Now, the EOA can be controlled via its new logic without further authorizations
// For example, to execute a transaction:
// await client.sendTransaction({
// to: eoa.address,
// data: encodeFunctionData({ abi, functionName: 'execute', args: [...] })
// });
3. Отзыв делегирования (возврат к обычному EOA)
Чтобы отменить обновление, попросите EOA подписать авторизацию, которая назначает нулевой адрес в качестве реализации, и отправить еще одну транзакцию типа 4. После этого вызов eth_getCode(eoa.address)
должен вернуть пустые байты.