cd rescuekit
bun run setup
bun run scan --chain base-mainnet --owner 0x...WARNING: RescueKit does NOT beat a live key-compromise bot without private/atomic execution. If an attacker is actively racing you in the mempool, a public fund -> approve -> transfer flow is likely to fail.
RescueKit is designed for:
- fast triage and reporting
- best-effort automation when the attacker is NOT actively racing
- rescues from Safe/multisig flows with uncompromised quorum
RescueKit is NOT designed to:
- outpace a live mempool adversary without private/atomic execution
- safely operate on a compromised machine
- replace a full incident response playbook
Never share seed phrases or private keys with anyone. Prefer running on a clean machine.
For commands that fund or rescue assets, you must set --incident active|inactive (or INCIDENT=active|inactive). If you mark the attacker as active, the CLI will refuse public funding/rescue flows.
RescueKit does not currently submit private or bundled transactions. All on-chain actions are public and sequential.
Supported private execution: none (eth-mainnet, base-mainnet, polygon-mainnet, moonbeam-mainnet).
If you need atomic execution against a live attacker, use a private relay/builder for funding + approvals + transfers, or switch to a sponsored/meta-tx flow.
See spec-docs/PLAYBOOK.md for the short incident checklist and stop conditions.
See prompts/ for triage, plan, and postmortem templates with safety constraints.
Developer-only prompts live in dev-prompts/ and are excluded from release zips.
bun run build:cli
bun run hash:cliFor release, use bun run release:cli and then sign rescuekit.sha256 with your preferred signing tool (for example GPG).
RescueKit is a lightweight SDK and CLI for rapid wallet triage and asset rescue planning. It is designed to work alongside an AI assistant and focuses on:
- scanning NFTs and ERC-20s via Alchemy
- checking compromised accounts (including EIP-7702 delegated accounts)
- handling private keys or leaked seed phrases (derivation + inspection)
- generating a safe, fast rescue plan with budget estimates
cd rescuekit
bun run setupPlace your environment variables in rescuekit/.env (recommended). The CLI will also read rescuekit/sdk/.env if present.
export ALCHEMY_KEY=...
export ETH_RPC_URL=...
export BASE_RPC_URL=...
export POLYGON_RPC_URL=...
export MOONBEAM_RPC_URL=...
export HACKED_KEY=...
export RESCUER_KEY=...
export RESCUER_ADDRESS=...
export MNEMONIC=...
export SEED_PHRASE=...
export NEW_KEY=... # legacy alias for RESCUER_KEY
export NEW_ADDRESS=... # legacy alias for RESCUER_ADDRESSPrefer env vars or stdin for secrets. To read keys from stdin:
printf "%s\n" "$HACKED_KEY" | bun run rescue-erc721 --owner-key-stdin --incident inactive --token 0x... --token-ids ./token-ids.jsonIf you pass secrets as CLI args (e.g. --owner-key), you must also pass --i-accept-i-might-leak-secrets.
scan inspects:
- address balance + code presence (EOA vs contract)
- EIP-7702 delegation bytecode (detection only; no remediation)
- ERC-20 balances via Alchemy
- NFT collections + counts via Alchemy (optionally token IDs)
Use one of:
eth-mainnetbase-mainnetpolygon-mainnetmoonbeam-mainnet
bun run scan --chain base-mainnet --owner 0x...If HACKED_KEY is set in .env, --owner is optional (address is derived automatically).
If SEED_PHRASE is set, scan will derive and scan a batch of addresses:
bun run scan --chain base-mainnet --count 10Scan a list of addresses (read-only, no keys required):
bun run scan --chain base-mainnet --addresses 0x...,0x...Or from a file (one address per line):
bun run scan --chain base-mainnet --addresses-file ./addresses.txtBy default, scan writes a Markdown report per address to rescuekit/reports/{address}-report.md.
Use --stdout to print to terminal or --out to set a custom output path (file for single, folder for batch).
Use --json or --md to force the output format (JSON produces a single scan.json file when writing to disk).
Use --exclude-spam or --exclude-airdrops to filter NFT spam/airdrops (may require a paid Alchemy tier).
Use --max-concurrency and --max-pages to control scan rate and NFT pagination.
bun run triage-approvals --chain base-mainnet --owner 0x... --erc20 0x... --spenders 0x...Use --erc721 / --erc1155 with --operators to check NFT approvals. File variants are supported (for example --erc20-file, --spenders-file).
Output includes nonZero/approved arrays sorted by risk.
bun run revoke-erc20 --chain base-mainnet --token 0x... --spender 0x...
bun run revoke-erc721 --chain base-mainnet --token 0x... --operator 0x...
bun run revoke-erc1155 --chain base-mainnet --token 0x... --operator 0x...bun run derive --mnemonic "word word ..." --count 10To write results to a file:
bun run derive --mnemonic "word word ..." --count 10 --out ./derived.jsonIncluding private keys requires an explicit acknowledgement and file output:
bun run derive --mnemonic "word word ..." --count 10 --include-private-keys --i-accept-i-might-lose-funds --out ./derived.jsonbun run plan --chain base-mainnet --private-key $HACKED_KEY --rescuer-key $NEW_KEY --gas-price-gwei 10If you don’t want to provide a key, use NEW_ADDRESS or --rescuer-address for a read-only plan.
bun run fund --chain base-mainnet --incident inactive --target 0x... --value-eth 0.01This is used to temporarily give a compromised address gas so it can approve transfers. When the address has code or EIP-7702 delegation, the funding is force-sent to bypass drainers that auto-forward ETH.
bun run rescue-erc20 --chain base-mainnet --incident inactive --token 0x... --amount 1.5 --rescuer-key $NEW_KEYUse one of:
--amount(human units; decimals auto-detected)--amount-raw(base units)--transfer-all(full balance)--dry-run(no transfer; prints balance + amount)
bun run rescue-erc721 --chain base-mainnet --incident inactive --token 0x... --token-ids ./token-ids.json --fund-amount-eth 0.01If the recipient is a contract that does not implement ERC721Receiver, use --unsafe-transfer to send via transferFrom, or --skip-receiver-check if you know the contract can receive.
bun run export-token-ids --chain base-mainnet --owner 0x... --collection 0x... --out ./token-ids.jsonIf you already ran scan --include-token-ids --json, you can reuse that output:
bun run export-token-ids --from-scan ./scan.json --collection 0x... --out ./token-ids.json --owner 0x...bun run transfer-erc721 --chain base-mainnet --token 0x... --token-ids ./token-ids.json --owner 0x...Use --unsafe-transfer if the recipient is a contract without ERC721Receiver, or --skip-receiver-check to bypass the check.
bun run rescue-erc1155 --chain base-mainnet --incident inactive --token 0x... --token-ids ./token-ids.json --fund-amount-eth 0.01Use --skip-receiver-check to bypass ERC1155Receiver checks for known-compatible contracts.
bun run transfer-erc1155 --chain base-mainnet --token 0x... --token-ids ./token-ids.json --owner 0x...bun run control --chain base-mainnet --private-key $HACKED_KEY --targets 0x...,0x...Read-only batch mode (no keys):
bun run control --chain base-mainnet --compromised 0x...,0x... --targets 0x...,0x...Targets are any addresses you want to evaluate for exposure (e.g., protocol safes, treasuries, vaults, or contracts you control). The check reports whether compromised accounts can control them directly, via delegation, or via multisig ownership.
Control scoring rules:
direct: compromised address is the target (full control)delegated: target is delegated to a compromised address (full control)safe-full: compromised owners >= threshold (full control)safe-partial: compromised owners < threshold (partial control)none: no direct control found
bun run safe-exec --chain base-mainnet --safe 0x... --safe-tx ./safe-tx.json --signer-key $HACKED_KEY --sender-key $NEW_KEYThis is an advanced command; Safe signature formats and ordering still matter.
If the Safe rejects the auto-signature, pass a signature produced by:
cast wallet sign --no-hash --private-key $HACKED_KEY <safeTxHash>- If an address has code or EIP-7702 bytecode, use force-send funding.
- Token balances use
alchemy_getTokenBalances, which requires Alchemy RPC. - ERC-721/1155 transfers skip non-transferable (soulbound/locked) tokens instead of failing the batch.