Web3 Security Series: If funds are mistakenly transferred to another chain, can they still be recovered?

In the crypto world, a single misclick can trigger a “digital disaster.” One of the most common nightmares is sending assets to the wrong blockchain. For example, intending to send ETH to an address on the Ethereum Sepolia testnet, but accidentally sending it to an address on the Ethereum mainnet. In such cases, is it still possible to recover the mistakenly transferred funds from the Ethereum mainnet? Whether assets can be recovered depends crucially on the type of the recipient address. This article analyzes different scenarios.

1. Scenario 1: The recipient address is an EOA

An (Externally Owned Account) is what we commonly refer to as a regular wallet address controlled directly by a private key or mnemonic phrase.

Prerequisites for asset recovery:

  • You transferred assets to an EOA address.
  • You possess the private key or mnemonic phrase of this target EOA address. (Usually your own other wallet address, or a friend’s, who is willing to cooperate.)
  • The target chain is an EVM-compatible chain.

Recovery method:

The holder of the private key of the recipient EOA address can directly withdraw funds on the target chain.

2. Scenario 2: The recipient address is a contract

This is one of the most despairing scenarios. Since a smart contract address is not generated by a private key, no one owns a private key for the contract. Therefore, control over the contract cannot be obtained in the same way as an EOA. Moreover, if the contract does not have a pre-written rescue function to handle “incorrect asset transfers,” then the mistakenly sent funds may be permanently locked in the contract, and no one can retrieve them.

However, in some cases, there is still a glimmer of hope. Next, we will construct a scenario where ETH is locked on the Ethereum mainnet, and then introduce how to rescue the funds.

2.1. Scenario description

In summary, this scenario involves a user intending to call a contract on the Sepolia testnet to transfer ETH into the contract to mint tokens, but during the transaction, they mistakenly connected to the mainnet, resulting in ETH being locked in a mainnet contract. The detailed process is as follows:

1. On the Ethereum Sepolia testnet, the project team (EOA) deploys a contract that implements the core functionality, such as allowing users to deposit ETH to mint corresponding AToken, with code roughly like the mintTokens function. Assume the deployment address is A. Note that there is no function in A that can directly withdraw ETH.

2. On the Sepolia testnet, the project team (EOA) deploys a factory contract that can deploy proxy contracts pointing to the implementation contract (like the deployProxyByImplementation function), using minimal proxy pattern (Clones) based on provided implementation address and salt. Assume the deployment address is B. Here, we call deployProxyByImplementation with the address of contract A as _implementation, resulting in a proxy contract at address C.

3. The user intends to mint AToken on Sepolia by transferring ETH, so they initiate a call to the proxy contract at address C. Normally, the proxy C would delegatecall to the implementation contract A’s mintTokens function to complete the operation. However, the user mistakenly connected to the mainnet during the call, and directly transferred ETH to address C on the mainnet. At this point, address C on the mainnet has no deployed contract, and no one owns its private key, so the ETH is temporarily locked in address C on the mainnet.

2.2. Key points

Before introducing specific rescue solutions, let’s review some basic knowledge points necessary for rescue.

2.2.1. create & create2

create and create2 are two common methods for deploying contracts in Solidity.

  • create deploys a contract with an address determined by the deployer’s address and their transaction nonce, regardless of the contract’s code.
  • create2 computes the contract address based on four parameters: 0xff, the deploying contract’s address, a salt (confusion value), and the init_code (creation bytecode). The address is independent of the contract’s code.

2.2.2. Minimal proxy contracts (Clones)

https://docs.openzeppelin.com/contracts/4.x/api/proxy#clones

Minimal proxy contracts, also known as Clones, are designed to deploy a very low-cost (gas-efficient) proxy contract that points to a specified implementation contract. In the Clones contract, deployment can be done via create or create2. For example, deploying a clone with cloneDeterministic uses create2.

The bytecode of the cloneDeterministic deployed proxy is very short, e.g., 0x363d3d373d3d3d363d73<implementation_address>5af43d82803e903d91602b57fd5bf3, which hardcodes the implementation address into the bytecode, and all calls to the proxy delegatecall to the implementation.

Since cloneDeterministic uses create2, the address of the deployed proxy depends on the deployer’s address, the salt, and the implementation address, but not on the implementation’s bytecode.

![])https://img-cdn.gateio.im/webp-social/moments-628ce0ce97dbf40349dc8b7a0c07eab3.webp(

)# 2.3. Rescue plan

Next, we introduce how to rescue ETH stuck at address C on the mainnet. The main idea is to deploy a contract code at address C on the mainnet, taking control of address C, and then extract the ETH. The specific steps are as follows:

![]###https://img-cdn.gateio.im/webp-social/moments-22428dd6bb1fbd62d2205538c0cc6c92.webp(

1. Deploy the same factory contract at address B on the mainnet as on the testnet. The reason for deploying at the same address is because the address of the proxy contract created via cloneDeterministic depends on the factory contract’s address. By examining the deployment transaction of the factory contract on Sepolia, we can find the deployer’s nonce. Then, on the mainnet, we set the deployer’s nonce to the same value as in the testnet before deploying the factory contract. Since the deployer’s address and nonce are the same, the factory contract on mainnet will have the same address B.

2. Deploy the implementation contract at address A on the mainnet, same as on the testnet. As mentioned in ), deploying a clone via cloneDeterministic computes the proxy address based on the implementation address and salt, independent of the implementation’s bytecode. Therefore, we only need to deploy a contract at address A that can handle ETH withdrawal. The code is as follows:

On the testnet, the implementation contract A is deployed by the project team (EOA). Its address depends only on the deployer’s address and nonce. By examining the deployment transaction, we can find the nonce, then set the mainnet deployer’s nonce to the same value, and deploy the implementation contract A at address A.

![]#最小代理合约(Clones)#https://img-cdn.gateio.im/webp-social/moments-75bfcbe9932d6fff8befe5ac282e022d.webp(

3. Deploy the proxy contract at address C on the mainnet, same as on the testnet. By examining the testnet deployment transaction of proxy C, we get the salt. Then, calling the factory’s deployProxyByImplementation with the implementation address A and the salt, we can deploy the proxy at address C on the mainnet.

4. Call the mainnet proxy contract C to withdraw. The project team (EOA) calls the withdraw function on proxy C, specifying the recipient, successfully retrieving the ETH frozen in proxy C, and returning it to the user.

)# 2.4. Summary

From the above rescue plan, we see that funds can only be recovered if many conditions are met simultaneously, such as the deployer’s nonce on the target chain not being used, the contract having a withdrawal function, or being upgradeable or using proxies like Clones that can be deployed with withdrawal functions.

Therefore, everyone should be extremely careful when making transactions, double-check each transaction before interacting with contracts, and consider using tools like ZAN’s AI SCAN vulnerability scanner to assess contract security. If funds are accidentally locked, don’t panic—contact ZAN’s contract security team for assistance in fund rescue.

This article is authored by ZANTeam (@zan_team) & AntChain OpenLabs (@AntChainOpenLab), with Cara (@Cara6289).

ETH0.86%
View Original
This page may contain third-party content, which is provided for information purposes only (not representations/warranties) and should not be considered as an endorsement of its views by Gate, nor as financial or professional advice. See Disclaimer for details.
  • Reward
  • Comment
  • Repost
  • Share
Comment
0/400
No comments
  • Pin
Trade Crypto Anywhere Anytime
qrCode
Scan to download Gate App
Community
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)