Risk Warning: Beware of illegal fundraising in the name of 'virtual currency' and 'blockchain'. — Five departments including the Banking and Insurance Regulatory Commission
Information
Discover
Search
Login
简中
繁中
English
日本語
한국어
ภาษาไทย
Tiếng Việt
BTC
ETH
HTX
SOL
BNB
View Market

Web3 Security Series: Can funds mistakenly transferred to other blockchains be recovered?

ZAN Team
特邀专栏作者
2025-11-22 03:00
This article is about 3399 words, reading the full article takes about 5 minutes
Sending cryptocurrency to the wrong blockchain? Don't despair! Your assets might still be salvageable! This article will reveal the key to recovering funds mistakenly transferred across blockchains.

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, you might intend to send ETH to an address on the Ethereum Sepolia testnet, but accidentally send it to an address on the Ethereum mainnet. In this situation, is it possible to recover the mistakenly transferred funds from the Ethereum mainnet? Whether the assets can be recovered depends on the type of receiving address. This article will analyze different scenarios.

1. Scenario 1: The receiving address is an EOA

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

Prerequisites for asset recovery:

  • You have transferred your assets to an EOA address.
  • You possess the private key or mnemonic phrase for this target EOA address. (This is usually another wallet address of your own, or a friend's address that they are willing to cooperate).
  • The target chain is an EVM-compatible chain.

Methods to recover assets:

The holder of the private key to the receiving EOA address can directly withdraw funds on the target blockchain.

2. Scenario 2: The receiving address is the contract.

This is one of the most desperate scenarios. Because the smart contract's address is not generated by the private key, no one owns the smart contract's private key and therefore cannot control the contract in the same way they control the EOA. Furthermore, if the contract does not have a pre-written rescue function to handle "accidentally transferred assets," the mistakenly transferred funds may be permanently locked in the contract, and no one can retrieve them.

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

2.1. Scene Introduction

In summary, this scenario involves a user intending to invoke a contract on the Sepolia testnet to transfer ETH into the contract for token minting. However, during the transaction initiation, an incorrect connection was made to the mainnet, resulting in the ETH being locked in the mainnet contract. The specific scenario construction process is as follows:

1. On the Ethereum Sepolia testnet, the project team (EOA) deployed an implementation contract . Assume the main function of this contract is for users to deposit ETH to mint corresponding ATokens, with code similar to the "mintTokens" function. Assume the deployment address is A. Note that there is no function in A that allows direct ETH withdrawal.

2. On the Ethereum Sepolia testnet, the project team (EOA) deployed a factory contract . This contract's function is to deploy a proxy contract pointing to the implementation contract (as shown in the function "deployProxyByImplementation") using minimal proxy contracts (Clones) based on the provided implementation contract address and salt. Assume the deployment address is B. Here, we call the "deployProxyByImplementation" function, passing the implementation contract A address as `_implementation`, to deploy a proxy contract pointing to A at address C.

3. A user wants to mint ATokens on the Sepolia testnet by transferring ETH. The user initiates a call to the proxy contract C. Normally, proxy contract C would further call the "mintTokens" function, which implements contract A, to complete the user's operation. However, during the call, the user incorrectly connects to the Ethereum mainnet. Consequently, the user directly transfers ETH to address C on the Ethereum mainnet. At this point, no contract is deployed on address C on the Ethereum mainnet, and no one owns the private key for address C. Therefore, the user's funds are temporarily locked in address C on the mainnet.

2.2. Key Knowledge Points

Before introducing the specific rescue plan, let's first introduce some basic knowledge points needed for rescue.

2.2.1. create & create2

`create` and `create2` are two common ways to deploy contracts in Solidity.

  • When deploying a contract using the `create` function, the contract address is determined by the address of the transaction initiator and the account's transaction count (nonce), and is unrelated to the contract's content.
  • When deploying a contract using create2, the calculation of the contract address no longer depends on the transaction initiator's nonce, but is related to the following four parameters.
  • 0xff
  • The contract address for creating a new contract.
  • The obfuscation value (salt) used as a parameter
  • The creation bytecode (init_code) of the contract to be created.

2.2.2. Minimal Agent Contracts (Clones)

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

Minimal proxy contracts, also known as clone contracts, are based on the idea of deploying a proxy contract with extremely low cost (Gas) that points to a specified implementation contract. In a clone contract, the proxy contract can be deployed using either the `create` or `create2` method. For example, deploying a proxy contract using the `cloneDeterministic` function employs the `create2` method.

In the "cloneDeterministic" function, the bytecode of the created proxy contract is very short, in the format: "0x363d3d373d3d3d363d73<implementation contract address>5af43d82803e903d91602b57fd5bf3". The address of the implementation contract is directly hard-coded into the bytecode, and all calls to the proxy contract are delegated to the implementation contract.

As can be seen from the "cloneDeterministic" function, it uses the create2 method to create a proxy contract. The address of the created proxy contract is related to the address of the contract creator, the salt, the address of the implementing contract, and a fixed string of bytecode, but it is unrelated to the bytecode of the implementing contract.

2.3. Rescue Plan

Next, we'll explain how to rescue a user's ETH held in the mainnet C address. The main idea is to deploy contract code on the Ethereum mainnet C address to take over the address and extract the ETH. The specific steps are as follows:

1. Deploy a factory contract on the mainnet with the same address B as on the testnet. The reason for needing the same factory contract address is that when subsequently calling "cloneDeterministic" to deploy the proxy contract, the address calculation of the proxy contract is related to the factory contract address. By examining the transaction deploying the factory contract on the Sepolia testnet, obtain the nonce of the deployer (project address) in this transaction. On the mainnet, advance the nonce of the project owner's (EOA) address to the nonce before deploying the factory contract. Then deploy the factory contract on the mainnet. Since the deployer's address and nonce are the same as the deployment transaction on the testnet, the factory contract address deployed on the mainnet will also be B.

2. Deploy the implementation contract on the mainnet at the same address A as on the testnet. As mentioned in the #Minimum Proxy Contract (Clones)# section, deploying a proxy contract using the "cloneDeterministic" function of the Clones contract calculates the proxy contract address. The calculated proxy contract address depends on the input parameter `salt` and the implementation contract address, but is independent of the implementation contract's bytecode. Therefore, we only need to deploy one contract on address A; the specific content of the contract does not affect the calculation of the proxy contract address. We can then directly deploy a contract with ETH extraction functionality on address A, as shown in the code below.

On the testnet, implementation contract A is deployed by the project owner's address (EOA). Therefore, the address of implementation contract A is only related to the transaction initiator and its nonce. Thus, by observing the transactions that deploy implementation contract A on the testnet, finding the relevant nonce, pushing the project owner's address (EOA) on the mainnet to the specified nonce, and then deploying implementation contract A, you can proceed.

3. Deploy a proxy contract on the mainnet at the same address C as the testnet. Observe the transactions of the proxy contract C deployed on the testnet, obtain the salt information, and call the "deployProxyByImplementation" function of the factory contract B, passing the address of the implementation contract A and the salt as parameters. This will deploy the proxy contract at address C on the mainnet.

4. The mainnet proxy contract C is invoked to withdraw funds. The project address (EOA) calls the withdraw function of proxy contract C, specifies the recipient of funds, successfully withdraws the frozen ETH from proxy contract C, and then returns it to the relevant user.

2.4. Summary

As can be seen from the above rescue plan, the funds can only be recovered if many conditions are met simultaneously, such as the contract deployer's relevant nonce on the target chain not being used, the contract trapping the funds having a withdrawal function or being able to deploy a withdrawal function in various ways (the contract can be upgraded or a proxy such as Clones can be used, etc.).

Therefore, everyone must be extremely careful when trading, meticulously verifying each transaction before interacting with the contract. Before engaging with the contract, you can use ZAN's AI SCAN vulnerability scanning tool to check its security. If your funds are accidentally locked, don't panic; you can contact ZAN's contract security audit team to try and help you recover your funds.

This article was written by Cara ( @Cara6289 ) of ZAN Team (X account @zan_team ) & AntChain OpenLabs (X account @AntChainOpenLab ).

Safety
smart contract
Welcome to Join Odaily Official Community