Skip to main content

An interactive Ethereum CLI for recovering assets and contract control from an account whose private key or seed phrase has been compromised.

Project description

Ethereum Rescue

eth-rescue is an interactive Ethereum CLI for recovering assets and contract control from an account whose private key or seed phrase has been compromised.

When a compromised account is watched by a sweeper, sending it ETH for gas can give the attacker an opportunity to take the funds first. This tool instead sends an ordered private Flashbots bundle that can clear an EIP-7702 delegation when necessary, fund the compromised account, execute one or more rescue actions, and sweep a conservative amount of ETH to a safe wallet.

[!WARNING] A clean relay simulation is required before submission, but it does not guarantee inclusion or recovery. An attacker with the same private key can change the nonce or move assets first, builders may decline the bundle, and private submission cannot make a contested account trustworthy. Rehearse on Sepolia with disposable accounts before attempting a mainnet rescue.

[!CAUTION] This is experimental software provided “as is,” without warranty of any kind. Use it entirely at your own risk. To the fullest extent permitted by law, the authors, maintainers, and contributors are not liable for lost funds or assets, failed or partial rescues, transaction fees, compromised credentials, or any other damages arising from its use. See the MIT License for the governing terms.

User guide

Requirements

  • uv. uv will obtain a compatible Python version when needed.
  • Access to the public RPC and Flashbots relay endpoints configured by the tool

Foundry is not required to run a rescue. The CLI encodes calldata in Python. Foundry and Anvil are only required for the local integration tests.

Install and run

First, install uv for your operating system. Then choose either a one-time run or a persistent installation; cloning the repository is not required.

Run the latest version directly in an isolated environment:

uvx eth-rescue

Or install it as a persistent command:

uv tool install eth-rescue
eth-rescue

If uv reports that its tool directory is not on PATH, run uv tool update-shell, restart your shell, and then run eth-rescue.

Before you start

Prepare three distinct account roles:

  • Compromised wallet: holds the assets or authority being rescued. You must have its private key.
  • Gas wallet: pays for the optional undelegation, victim funding, and rescue transactions. Enter an existing private key or let the CLI create a new wallet.
  • Safe wallet: receives rescued assets and the ETH sweep. It must differ from both the compromised and gas wallets.

The Flashbots signing identity is generated automatically and needs no funds. Compromised and gas-wallet private keys are entered through hidden interactive prompts and held in memory; the tool does not read keys from keystores or .env files and does not write them to disk.

If the CLI generates a gas wallet, it displays the private key once. Save it securely before continuing and retain it until any unused gas-wallet balance has been recovered. The CLI does not include private keys in JSON plans; configs/ and .env are git-ignored.

Supported networks

  • Ethereum mainnet
  • Sepolia testnet

The selection controls the expected chain ID, public RPC endpoint, Flashbots relay, and builder routing. The CLI refuses to continue when the connected RPC reports the wrong chain.

Rescue workflow

  1. Choose the network. Use Sepolia for a rehearsal or Ethereum mainnet for the live rescue.
  2. Set up accounts. Enter the compromised-wallet key, then enter or generate the separate gas wallet. Enter the safe-wallet address.
  3. Build the plan. Load a user-authored JSON plan or add one or more ordered actions with the guided wizard. Wizard-built plans can be reviewed before continuing.
  4. Preview fees and funding. Enter an optional extra priority fee. The CLI estimates each action with a 25% gas buffer, uses a conservative action-specific fallback if estimation fails, verifies whether the victim has an EIP-7702 delegation, and shows the maximum estimated cost.
  5. Fund the gas wallet. The CLI shows the required balance, including a safety buffer, and waits for you to fund the gas wallet. It rechecks only when you press a key.
  6. Simulate. A bundle is built from fresh fees, balances, and both account nonces for the next block. The exact signed transactions must simulate successfully through the relay before submission is offered.
  7. Submit and monitor. After explicit confirmation, the CLI submits the bundle for the relay's allowed block range. Each new attempt rebuilds and simulates with current chain state. After 25 missed blocks, it asks whether to keep trying.

When the compromised account has an EIP-7702 delegation, the first bundle transaction is a gas-wallet-sponsored type-4 transaction that authorizes the victim to clear its delegation. Plain externally owned accounts skip that transaction. Unexpected contract code on the victim is rejected rather than overwritten.

The remaining order is gas wallet funds victim, victim executes every rescue action, then victim sweeps a conservative guaranteed amount of ETH to the safe wallet. The sweep is omitted when its value would be zero. Unused-gas savings can remain in the compromised wallet, so treat any residual balance as at risk.

If simulation fails, the CLI identifies a failing rescue action when possible. You can remove or rebuild actions, change the extra priority fee, retry against fresh state, or cancel. No bundle is broadcast without a clean simulation and a separate send confirmation. A bundle that was already submitted may still be considered by its target builders even if a later retry or interaction is cancelled.

Supported actions

The guided wizard can add multiple actions in execution order:

  • ERC-20: calls transfer(address,uint256); amounts are integer base units, not display units.
  • ERC-721: calls transferFrom(address,address,uint256) from the compromised wallet.
  • ERC-1155: calls safeTransferFrom(address,address,uint256,uint256,bytes).
  • Contract ownership: calls transferOwnership(address).
  • Transient Auction House ERC-721: looks up ownerOf(tokenId), requires the owner to be the auction-house address recognized by this version of the tool, then orders delist(address,uint256) before the ERC-721 transfer.
  • Custom: accepts a target address, canonical function signature, and JSON array of ABI-compatible arguments.

Entering cancel, back, or exit at a cancel-aware action prompt abandons that action and returns to plan construction.

JSON plans

Advanced users can choose Load a saved JSON config file to supply a user-authored, ordered, non-empty JSON array. Each action has this shape:

{
  "address": "0xTargetContractAddress",
  "function_signature": "transfer(address,uint256)",
  "args": ["0xSafeWalletAddress", 1000000000000000000],
  "gas_estimate": 70000
}
Field Requirement
address Required valid Ethereum contract address. It is converted to checksum form when loaded.
function_signature Required string containing ( and ending in ), such as transfer(address,uint256).
args Required JSON array in the same order as the function parameters. Token amounts must be integer base units.
gas_estimate Optional positive integer used only as the fallback when RPC estimation fails. Defaults to the generic fallback.

The loader rejects an empty plan, malformed JSON, missing required fields, invalid addresses or signatures, non-array arguments, and invalid fallback gas values. ABI encoding errors such as an argument-count or type mismatch are reported while preparing the plan.

Example plans:

The examples contain placeholder addresses and amounts; review and replace every value before use.

Sepolia rehearsal checklist

Use disposable accounts and test assets:

  1. Fund a victim with test ETH and a test asset, then configure an unrelated gas wallet and safe wallet.
  2. Complete a transfer and confirm the receipt order: optional undelegation, funding, rescue actions, then sweep.
  3. Force an action revert and confirm submission remains blocked until the action is edited or removed.
  4. Change the victim nonce between attempts and confirm the next bundle is rebuilt and simulated with the new nonce.
  5. Cancel after a failed simulation and confirm nothing was submitted.

A successful rehearsal checks current endpoint compatibility and operating procedure; it cannot guarantee mainnet inclusion or prevent a nonce race.

Developer guide

Clone and set up

Contributors should work from a clone:

git clone https://github.com/mpeyfuss/eth-rescue.git
cd eth-rescue
uv sync
uv run eth-rescue

The project requires Python 3.13 or newer. uv sync creates the project environment and installs the locked runtime and development dependencies.

Repository layout

The main package is under src/eth_rescue/. Fast tests live directly under tests/; Anvil-backed tests and their Solidity fixtures live under tests/integration/. Example rescue plans live under examples/.

Development commands

Common commands:

make fmt               # format Python with Ruff
make lint              # run Ruff lint with automatic fixes
make test-unit         # unit tests; this is also the default `make test`
make test-integration  # compile fixtures and run Anvil-backed tests
make test-all          # run unit and enabled integration tests

Integration tests

The integration suite requires forge and anvil from Foundry. make test-integration compiles the fixture contracts, sets RUN_ANVIL_INTEGRATION=1, and starts a fresh Anvil process for each test. It defaults to the Osaka execution fork; override it only when intentionally checking another fork:

ANVIL_HARDFORK=<fork-name> make test-integration

The integration tests exercise EIP-7702 undelegation, funding, ERC-20/721/1155 transfers, ownership transfer, auction delisting, ETH sweeping, simulation rollback, signing, and transaction ordering. Their local relay double snapshots Anvil for simulation and broadcasts transactions sequentially for execution.

That double does not model Flashbots privacy, builder selection, same-block execution, bundle competition, exact builder base fees, or atomic all-or-nothing inclusion. The local suite does not contact the configured public RPC or relay endpoints.

License

This project is available under the MIT License.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

eth_rescue-0.1.1.tar.gz (20.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

eth_rescue-0.1.1-py3-none-any.whl (23.8 kB view details)

Uploaded Python 3

File details

Details for the file eth_rescue-0.1.1.tar.gz.

File metadata

  • Download URL: eth_rescue-0.1.1.tar.gz
  • Upload date:
  • Size: 20.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for eth_rescue-0.1.1.tar.gz
Algorithm Hash digest
SHA256 9bd4be8d1339cffeae39325624e993d09a6cabac2d272bee92b5e70b335bcc10
MD5 b968ba59a0e2e8a5db80fa1d99e082b1
BLAKE2b-256 a166364c8879ef8bbf5c1c07229d58c1ac595a501d4e4b2323b5a9a7df3792a2

See more details on using hashes here.

File details

Details for the file eth_rescue-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: eth_rescue-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 23.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for eth_rescue-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d7a50b277324afc8d529b893b01e8462d846c5c9144190ff297bdd3667898b20
MD5 82556fe0350b7f57bb7640d026419261
BLAKE2b-256 b8c9596a245390f536e2c4382e7508fd63d9525d2f1193b92da369ea77d97422

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page