Серия по безопасности Web3: Можно ли вернуть средства, ошибочно переведённые на другой блокчейн?

В мире криптовалют одна случайная ошибка при нажатии может привести к «цифровой катастрофе». Одним из самых распространённых кошмаров является отправка активов на неправильный блокчейн. Например, вы хотели отправить 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 — адрес контракта вычисляется по формуле, которая зависит от:
    • 0xff
    • адрес создателя (address)
    • значение salt
    • байт-код создаваемого контракта (init_code)

2.2.2. Минимальный прокси-контракт (Clones)

https://docs.openzeppelin.com/contracts/4.x/api/proxy#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. Итог

Из этого следует, что для успешного спасения средств необходимо, чтобы:

  • Контракт развернут на целевом адресе (или его можно было развернуть с теми же параметрами).
  • Владелец или разработчик знает параметры развертывания (salt, nonce).
  • В контракте есть функция для вывода средств или есть возможность развернуть управляемый контракт.

Поэтому при работе с контрактами очень важно тщательно проверять адреса и параметры, а также использовать инструменты для анализа безопасности. В случае блокировки средств — не паниковать, а обратиться к специалистам по безопасности и аудитам.

Этот материал подготовлен командой ZAN (@zan_team), а также Cara из AntChain OpenLabs (@Cara6289).

ETH0,56%
Посмотреть Оригинал
На этой странице может содержаться сторонний контент, который предоставляется исключительно в информационных целях (не в качестве заявлений/гарантий) и не должен рассматриваться как поддержка взглядов компании Gate или как финансовый или профессиональный совет. Подробности смотрите в разделе «Отказ от ответственности» .
  • Награда
  • комментарий
  • Репост
  • Поделиться
комментарий
0/400
Нет комментариев
  • Закрепить