В мире криптовалют одна случайная ошибка при нажатии может привести к «цифровой катастрофе». Одним из самых распространённых кошмаров является отправка активов на неправильный блокчейн. Например, вы хотели отправить ETH на адрес в тестовой сети Sepolia Ethereum, но по ошибке отправили его на адрес основной сети Ethereum. В таком случае, можно ли вернуть ошибочно переведённые средства с основной сети Ethereum? Возможно ли восстановить активы, зависит от типа получающего адреса. В этой статье мы проанализируем разные ситуации.
1. Сценарий 1: адрес получателя — EOA
EOA (Externally Owned Account) — это обычный кошелёк, управляемый приватным ключом или мнемонической фразой.
Предпосылки для возврата активов:
Вы перевели активы на адрес EOA.
У вас есть приватный ключ или мнемоническая фраза этого целевого EOA. (Обычно это ваш собственный другой кошелёк или кошелёк друга, который согласен помочь).
Целевая цепочка — совместимая с EVM.
Способы возврата активов:
Владелец приватного ключа адреса EOA может напрямую вывести средства на целевой цепочке.
2. Сценарий 2: адрес получателя — контракт
Это один из самых безнадёжных сценариев. Поскольку адрес смарт-контракта не генерируется приватным ключом, никто не владеет приватным ключом этого контракта, и управлять им так же, как EOA, невозможно. Более того, если контракт изначально не содержит функции для обработки «ошибочного зачисления», то ошибочно переведённые средства могут быть навсегда заблокированы внутри контракта, и никто не сможет их вывести.
Однако в некоторых случаях всё же есть шанс. Далее мы построим сценарий, в котором ETH заблокированы на основной сети Ethereum, и расскажем, как можно спасти средства.
2.1. Описание сценария
Общий сценарий таков: пользователь хотел вызвать контракт в тестовой сети Sepolia и перевести ETH для чеканки токенов, но при отправке транзакции ошибочно подключился к основной сети, в результате ETH оказались заблокированы в контракте основной сети. Конкретный сценарий следующий:
1.В тестовой сети Ethereum Sepolia проект (EOA) развернул контракт, предположим, что этот контракт позволяет пользователю внести ETH для чеканки соответствующих AToken, примерно так реализована функция mintTokens. Адрес развертывания — A. Важно отметить, что в контракте A нет функции для прямого вывода ETH.
2.В тестовой сети Ethereum Sepolia проект (EOA) развернул фабричный контракт, который позволяет, передавая адрес реализуемого контракта и salt, развернуть прокси-контракт (например, с помощью функции deployProxyByImplementation), указывающий на реализуемый контракт A. Адрес развертывания — B. В данном случае вызывается deployProxyByImplementation с адресом A в качестве _implementation, что приводит к созданию прокси-контракта по адресу C.
3.Пользователь хочет в тестовой сети Sepolia через перевод ETH чеканить AToken, и инициирует вызов по адресу прокси-контракта C. Обычно, прокси вызывает функцию mintTokens в контракте A. Но пользователь ошибочно подключается к основной сети Ethereum и переводит ETH на адрес C в основной сети. В результате ETH попадают на адрес C в основной сети, где нет развернутого контракта, и приватный ключ этого адреса неизвестен — средства заблокированы на основном сетевом адресе C.
2.2. Ключевые моменты
Перед рассмотрением вариантов спасения, нужно понять базовые знания.
2.2.1. create & create2
create и create2 — это два способа развертывания контрактов в Solidity.
create — адрес контракта определяется исходя из адреса создателя и nonce транзакции, не зависит от содержимого контракта.
create2 — адрес контракта вычисляется по формуле, которая зависит от:
Минимальный прокси-контракт, также известный как клон (Clones), — это контракт, который с минимальными затратами (Gas) развертывает прокси, указывающий на целевой реализуемый контракт. В контракте Clones можно использовать create или create2 для развертывания прокси. Например, вызов cloneDeterministic использует create2.
В функции cloneDeterministic создаваемый прокси-контракт очень короткий, его байт-код примерно такой: 0x363d3d373d3d3d363d73<адрес_реализуемого_контракта>5af43d82803e903d91602b57fd5bf3. Адрес реализуемого контракта жестко закодирован в байт-коде, и все вызовы delegatecall идут на этот контракт.
Из функции cloneDeterministic видно, что адрес создаваемого прокси зависит от адреса создателя, salt и адреса реализуемого контракта, а не от байт-кода реализуемого контракта.
Теперь расскажем, как можно вернуть ETH, заблокированные на адресе C в основной сети. Основная идея — развернуть контракт на адресе C в основной сети, чтобы он взял на себя управление и вывел средства. Конкретные шаги:
1. В основной сети развернуть такой же фабричный контракт, как в тестовой сети, на адресе B. Для этого нужно, чтобы адрес фабричного контракта совпадал. Для этого в основном сети повторно развернуть тот же контракт с теми же параметрами, что и в тестовой, учитывая nonce. Для этого можно посмотреть транзакцию развертывания в тестовой сети, определить nonce создателя, и в основной сети развернуть контракт с таким же nonce, чтобы получить тот же адрес B.
2. В основной сети развернуть контракт реализуемого контракта A на том же адресе. Поскольку адрес прокси зависит только от адреса создателя, salt и байт-кода, достаточно развернуть контракт A на нужном адресе. Для этого можно использовать тот же подход — развернуть контракт с тем же nonce, что и в тестовой, или использовать create2 с известным salt и байт-кодом.
3. В основной сети развернуть прокси-контракт на адресе C. Для этого нужно взять транзакцию развертывания прокси в тестовой сети, определить salt, и вызвать deployProxyByImplementation с теми же параметрами, чтобы получить адрес C.
4. Вызвать функцию withdraw у прокси-контракта C, чтобы вывести заблокированные ETH. После этого средства вернутся владельцу.
)# 2.4. Итог
Из этого следует, что для успешного спасения средств необходимо, чтобы:
Контракт развернут на целевом адресе (или его можно было развернуть с теми же параметрами).
Владелец или разработчик знает параметры развертывания (salt, nonce).
В контракте есть функция для вывода средств или есть возможность развернуть управляемый контракт.
Поэтому при работе с контрактами очень важно тщательно проверять адреса и параметры, а также использовать инструменты для анализа безопасности. В случае блокировки средств — не паниковать, а обратиться к специалистам по безопасности и аудитам.
Этот материал подготовлен командой ZAN (@zan_team), а также Cara из AntChain OpenLabs (@Cara6289).
На этой странице может содержаться сторонний контент, который предоставляется исключительно в информационных целях (не в качестве заявлений/гарантий) и не должен рассматриваться как поддержка взглядов компании Gate или как финансовый или профессиональный совет. Подробности смотрите в разделе «Отказ от ответственности» .
Серия по безопасности Web3: Можно ли вернуть средства, ошибочно переведённые на другой блокчейн?
В мире криптовалют одна случайная ошибка при нажатии может привести к «цифровой катастрофе». Одним из самых распространённых кошмаров является отправка активов на неправильный блокчейн. Например, вы хотели отправить ETH на адрес в тестовой сети Sepolia Ethereum, но по ошибке отправили его на адрес основной сети Ethereum. В таком случае, можно ли вернуть ошибочно переведённые средства с основной сети Ethereum? Возможно ли восстановить активы, зависит от типа получающего адреса. В этой статье мы проанализируем разные ситуации.
1. Сценарий 1: адрес получателя — EOA
EOA (Externally Owned Account) — это обычный кошелёк, управляемый приватным ключом или мнемонической фразой.
Предпосылки для возврата активов:
Способы возврата активов:
Владелец приватного ключа адреса EOA может напрямую вывести средства на целевой цепочке.
2. Сценарий 2: адрес получателя — контракт
Это один из самых безнадёжных сценариев. Поскольку адрес смарт-контракта не генерируется приватным ключом, никто не владеет приватным ключом этого контракта, и управлять им так же, как EOA, невозможно. Более того, если контракт изначально не содержит функции для обработки «ошибочного зачисления», то ошибочно переведённые средства могут быть навсегда заблокированы внутри контракта, и никто не сможет их вывести.
Однако в некоторых случаях всё же есть шанс. Далее мы построим сценарий, в котором ETH заблокированы на основной сети Ethereum, и расскажем, как можно спасти средства.
2.1. Описание сценария
Общий сценарий таков: пользователь хотел вызвать контракт в тестовой сети Sepolia и перевести ETH для чеканки токенов, но при отправке транзакции ошибочно подключился к основной сети, в результате ETH оказались заблокированы в контракте основной сети. Конкретный сценарий следующий:
1. В тестовой сети Ethereum Sepolia проект (EOA) развернул контракт, предположим, что этот контракт позволяет пользователю внести ETH для чеканки соответствующих AToken, примерно так реализована функция mintTokens. Адрес развертывания — A. Важно отметить, что в контракте A нет функции для прямого вывода ETH.
2. В тестовой сети Ethereum Sepolia проект (EOA) развернул фабричный контракт, который позволяет, передавая адрес реализуемого контракта и salt, развернуть прокси-контракт (например, с помощью функции deployProxyByImplementation), указывающий на реализуемый контракт A. Адрес развертывания — B. В данном случае вызывается deployProxyByImplementation с адресом A в качестве _implementation, что приводит к созданию прокси-контракта по адресу C.
3. Пользователь хочет в тестовой сети Sepolia через перевод ETH чеканить AToken, и инициирует вызов по адресу прокси-контракта C. Обычно, прокси вызывает функцию mintTokens в контракте A. Но пользователь ошибочно подключается к основной сети Ethereum и переводит ETH на адрес C в основной сети. В результате ETH попадают на адрес C в основной сети, где нет развернутого контракта, и приватный ключ этого адреса неизвестен — средства заблокированы на основном сетевом адресе C.
2.2. Ключевые моменты
Перед рассмотрением вариантов спасения, нужно понять базовые знания.
2.2.1. create & create2
create и create2 — это два способа развертывания контрактов в Solidity.
2.2.2. Минимальный прокси-контракт (Clones)
Минимальный прокси-контракт, также известный как клон (Clones), — это контракт, который с минимальными затратами (Gas) развертывает прокси, указывающий на целевой реализуемый контракт. В контракте Clones можно использовать create или create2 для развертывания прокси. Например, вызов cloneDeterministic использует create2.
В функции cloneDeterministic создаваемый прокси-контракт очень короткий, его байт-код примерно такой: 0x363d3d373d3d3d363d73<адрес_реализуемого_контракта>5af43d82803e903d91602b57fd5bf3. Адрес реализуемого контракта жестко закодирован в байт-коде, и все вызовы delegatecall идут на этот контракт.
Из функции cloneDeterministic видно, что адрес создаваемого прокси зависит от адреса создателя, salt и адреса реализуемого контракта, а не от байт-кода реализуемого контракта.
![])https://img-cdn.gateio.im/webp-social/moments-628ce0ce97dbf40349dc8b7a0c07eab3.webp(
)# 2.3. Варианты спасения
Теперь расскажем, как можно вернуть ETH, заблокированные на адресе C в основной сети. Основная идея — развернуть контракт на адресе C в основной сети, чтобы он взял на себя управление и вывел средства. Конкретные шаги:
![]###https://img-cdn.gateio.im/webp-social/moments-22428dd6bb1fbd62d2205538c0cc6c92.webp(
1. В основной сети развернуть такой же фабричный контракт, как в тестовой сети, на адресе B. Для этого нужно, чтобы адрес фабричного контракта совпадал. Для этого в основном сети повторно развернуть тот же контракт с теми же параметрами, что и в тестовой, учитывая nonce. Для этого можно посмотреть транзакцию развертывания в тестовой сети, определить nonce создателя, и в основной сети развернуть контракт с таким же nonce, чтобы получить тот же адрес B.
2. В основной сети развернуть контракт реализуемого контракта A на том же адресе. Поскольку адрес прокси зависит только от адреса создателя, salt и байт-кода, достаточно развернуть контракт A на нужном адресе. Для этого можно использовать тот же подход — развернуть контракт с тем же nonce, что и в тестовой, или использовать create2 с известным salt и байт-кодом.
3. В основной сети развернуть прокси-контракт на адресе C. Для этого нужно взять транзакцию развертывания прокси в тестовой сети, определить salt, и вызвать deployProxyByImplementation с теми же параметрами, чтобы получить адрес C.
4. Вызвать функцию withdraw у прокси-контракта C, чтобы вывести заблокированные ETH. После этого средства вернутся владельцу.
)# 2.4. Итог
Из этого следует, что для успешного спасения средств необходимо, чтобы:
Поэтому при работе с контрактами очень важно тщательно проверять адреса и параметры, а также использовать инструменты для анализа безопасности. В случае блокировки средств — не паниковать, а обратиться к специалистам по безопасности и аудитам.
Этот материал подготовлен командой ZAN (@zan_team), а также Cara из AntChain OpenLabs (@Cara6289).