By Marka.eth, Backseats.eth, and Jalil.eth
This repo illustrates how to use Foundry tests and scripts to simulate and deploy a contract to an address that has ETH in it.
It, of course, assumes that you deployed something to the same address with the stuck funds in it, from the same wallet on another network.
It also assumes that your nonce on other networks is lower than your nonce on the network where the contract is deployed. If the nonce on another network is higher, you can't deploy anything to the target address you're trying to reach.
-
Marka deployed a contract DegenerativeGames on ETH mainnet at nonce 88.
-
Because of a bug in the minting website that didn't force the user to switch to mainnet, people minted to other networks (which was really just a send with a function signature)
-
9.13 ETH was sitting in the same address on Base and Marka's nonce for the same address that deployed the mainnet wallet (0x16e23099cca4092C6c7ea3a56506aF6DCc58383A) was at 56.
This script shows you how to run N transactions to get to target nonce - 1 (in this case, 87), deploy a contract from the same address with the target nonce, and then withdraw that ETH.
By default, ETH deploys use the CREATE
opcode. CREATE
is deterministic, using keccak256(sender, nonce)
to calculate the address that the code will be deployed. Thats what we'll use here.
Many people are familiar with CREATE2
keccak256(0xFF, sender, salt, keccak256(bytecode))
which has a different function signature and can be used to deploy the same address across multiple networks, regardless of your nonce
since it uses a salt
and the contract's bytecode to determine the address.
So assuming you're using the same deployer wallet, you just need to do N transactions to get your nonce to be one less than the nonce you used on the other network. Then when you deploy on the new network with that same nonce, you'll get the same contract address, to which you can deploy any contract. It doesn't have to resemble the contract on the other network at all.
In our case, we used something very simple
contract Withdraw {
error Failed();
receive() external payable {}
function withdraw() public {
// Sends to marka.eth
(bool sent, ) = payable(0x16e23099cca4092C6c7ea3a56506aF6DCc58383A).call{value: address(this).balance}("");
if (!sent) revert Failed();
}
}
You should be enforcing the chain your users are using to interact with your contracts. If you don't already have this, please use something similar to the below. (this is an example from the wagmi docs)
import { useSwitchChain } from 'wagmi'
function App() {
const { chains, switchChain } = useSwitchChain()
return (
<div>
{chains.map((chain) => (
<button key={chain.id} onClick={() => switchChain({ chainId: chain.id })}>
{chain.name}
</button>
))}
</div>
)
}
-
Clone the repo
-
Copy
.env.example
into a new.env
file -
Add in the values and run
source .env
in your console to load in env vars -
forge build
-
See notes in the
test
andscript
dirs on how to run each.
Gaslite Drop is a great, gas-efficient resource. Also developed in part by Backseats.
Thank you to OptimizationFans (specifically WOK, Michael Amadi, and boredretard.eth) for their help as well.
Feel free to create a Pull Request or an Issue if necessary.
Just be excellent to everyone ✌🏻