Web3 Security Series: Can funds mistakenly transferred to another chain be recovered?

CN
8 hours ago

In the world of cryptocurrency, a single click mistake 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 this case, can the mistakenly transferred funds be retrieved from the Ethereum mainnet? Whether assets can be recovered depends on the type of receiving address. This article will analyze different scenarios.

1. Scenario One: Receiving Address is EOA

EOA (Externally Owned Account) refers to the ordinary wallet address that is directly controlled by a private key or mnemonic phrase.

Prerequisites for asset recovery:

  • You sent the assets to an EOA address.
  • You possess the private key or mnemonic phrase for the target EOA address. (Usually, this is another wallet address of yours or a friend's address, and they are willing to cooperate).
  • The target chain is an EVM-compatible chain.

Method to recover assets:

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

2. Scenario Two: Receiving Address is a Contract

This is one of the most desperate scenarios. Since the address of a smart contract is not generated by a private key, no one owns the private key of the smart contract, making it impossible to control the contract like an EOA. Moreover, if the contract does not have a pre-written rescue function to handle "erroneously transferred assets," the mistakenly transferred funds may be permanently locked in the contract, and no one can retrieve them.

However, in certain cases, there may still be 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 Introduction

In summary, this scenario involves a user who intended to call a contract on the Sepolia testnet to deposit ETH into the contract to mint tokens, but mistakenly connected to the mainnet when initiating the transaction, resulting in ETH being locked in the contract on the mainnet. The specific scenario construction process is as follows:

1. On the Ethereum Sepolia testnet, the project team (EOA) deployed an implementation contract, assuming the main function of this contract is for users to deposit ETH to mint corresponding ATokens, roughly as shown in the "mintTokens" function. It should be noted that there is no function in A that can directly withdraw ETH.

2. On the Ethereum Sepolia testnet, the project team (EOA) deployed a factory contract, which functions to deploy a proxy contract pointing to the implementation contract using the provided implementation contract address and salt, in a minimal proxy contract (Clones) manner (as shown in the "deployProxyByImplementation" function). Assume the deployed address is B. Here, we assume that by calling the "deployProxyByImplementation" function, we pass the implementation contract A address as _implementation, deploying a proxy contract pointing to A, with the address C.

3. The user wants to mint AToken by depositing ETH on the Sepolia testnet, so the user initiated a call to the proxy contract at address C. Under normal circumstances, the proxy contract C would further call the "mintTokens" function of the implementation contract A to complete the user's operation. However, the user mistakenly connected to the Ethereum mainnet when calling. As a result, the user directly deposited ETH into the Ethereum mainnet address C. At this point, no contract has been deployed at the Ethereum mainnet address C, and no one owns the private key for that C address, so the user's funds are temporarily locked at the mainnet address C.

2.2. Key Knowledge Points

Before introducing specific rescue plans, let's first cover the basic knowledge points needed for the rescue.

2.2.1. create & create2

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

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

2.2.2. Minimal Proxy Contract (Clones)

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

A minimal proxy contract, often referred to as a clone contract (Clones), is based on the core idea of deploying a proxy contract that points to a specified implementation contract at a very low cost (Gas). In the Clones contract, proxy contracts can be deployed using either create or create2 methods. For example, deploying a proxy contract through the "cloneDeterministic" function uses the create2 method.

In the "cloneDeterministic" function, the bytecode of the created proxy contract is very short, formatted as: "0x363d3d373d3d3d363d735af43d82803e903d91602b57fd5bf3", directly hardcoding the address of the implementation contract into the bytecode and delegating all calls to that proxy contract to the implementation contract.

From the "cloneDeterministic" function, it can be seen that it uses the create2 method to create the proxy contract, and the address of the created proxy contract is related to the address of the contract creator, salt, the address of the implementation contract, and a fixed string of bytecode, unrelated to the bytecode of the implementation contract.

2.3. Rescue Plan

Next, we will introduce how to rescue the ETH locked at the mainnet address C. The main idea is to deploy the contract code at the Ethereum mainnet address C to take over the mainnet C address and withdraw the ETH. The specific steps are as follows:

1. Deploy the same factory contract at the mainnet address B as on the testnet. The reason for needing the same factory contract address is that the address calculation of the proxy contract deployed in the subsequent "cloneDeterministic" call is related to the factory contract address. By checking the transaction that deployed the factory contract on the Sepolia testnet, we can obtain the nonce of the deployer (project team address) in that transaction. On the mainnet, we advance the nonce of the project team (EOA) address to the nonce before deploying the factory contract, and then deploy the factory contract on the mainnet. Since both the deployer's address and nonce are the same as those in the testnet deployment transaction, the factory contract address deployed on the mainnet will also be B.

2. Deploy the same implementation contract at the mainnet address A as on the testnet. As mentioned in the #Minimal Proxy Contract (Clones)# section, the address of the proxy contract calculated by the "cloneDeterministic" function is related to the salt and implementation contract address, and is unrelated to the bytecode of the implementation contract. Therefore, we only need to deploy a contract at address A, and the specific content of the contract does not affect the calculation of the proxy contract address. We can directly deploy a contract with the function to withdraw ETH at address A, as shown in the code below.

On the testnet, the implementation contract A was deployed by the project team address (EOA), so the address of the implementation contract A is only related to the transaction initiator and its nonce. Therefore, by observing the transaction that deployed the implementation contract A on the testnet, we can find the relevant nonce, advance the nonce of the project team address (EOA) on the mainnet to the specified nonce, and then deploy the implementation contract A.

3. Deploy the same proxy contract at the mainnet address C as on the testnet. By observing the transaction that deployed the proxy contract C on the testnet, we can 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 to deploy the proxy contract at the mainnet address C.

4. Call the mainnet proxy contract C to withdraw funds. The project team address (EOA) calls the withdraw function of the proxy contract C, specifying the recipient of the funds, successfully withdrawing the frozen ETH from the proxy contract C, and then returning it to the relevant user.

2.4. Summary

From the above rescue plan, it can be seen that for funds to be rescued, many conditions must be met, such as the relevant nonce of the contract deployer on the target chain not being used, the contract that locked the funds having a withdrawal function, or being able to deploy a withdrawal function through various means (the contract can be upgraded or using proxies like Clones, etc.).

Therefore, everyone must be extremely careful when trading, carefully verifying each transaction initiated. Before interacting with contracts, you can use the vulnerability scanning tool AI SCAN provided by ZAN to check the security of the contract. If funds are accidentally locked, do not panic; you can contact ZAN's contract security audit team to try to assist you in rescuing the funds.

This article was written by ZANTeam (X account @zan_team) & AntChain OpenLabs (X account @AntChainOpenLab)'s Cara (X account @Cara6289).

免责声明:本文章仅代表作者个人观点,不代表本平台的立场和观点。本文章仅供信息分享,不构成对任何人的任何投资建议。用户与作者之间的任何争议,与本平台无关。如网页中刊载的文章或图片涉及侵权,请提供相关的权利证明和身份证明发送邮件到support@aicoin.com,本平台相关工作人员将会进行核查。

Share To
APP

X

Telegram

Facebook

Reddit

CopyLink