Skip to content

Swader/rescuekit

Repository files navigation

RescueKit

TL;DR

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.

Threat model (short)

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.

Incident classification (required for rescue/fund)

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.

Private / atomic execution support

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.

Operational playbook

See spec-docs/PLAYBOOK.md for the short incident checklist and stop conditions.

LLM prompt pack

See prompts/ for triage, plan, and postmortem templates with safety constraints. Developer-only prompts live in dev-prompts/ and are excluded from release zips.

Build a single binary + hashes

bun run build:cli
bun run hash:cli

For 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

Setup

cd rescuekit
bun run setup

Place 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_ADDRESS

Key handling (safer defaults)

Prefer 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.json

If you pass secrets as CLI args (e.g. --owner-key), you must also pass --i-accept-i-might-leak-secrets.

What scan checks

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)

CLI usage

Supported chains

Use one of:

  • eth-mainnet
  • base-mainnet
  • polygon-mainnet
  • moonbeam-mainnet

Scan a single address

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 10

Scan 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.txt

By 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.

Triage approvals/allowances

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.

Revoke approvals

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...

Derive addresses from a seed phrase

bun run derive --mnemonic "word word ..." --count 10

To write results to a file:

bun run derive --mnemonic "word word ..." --count 10 --out ./derived.json

Including 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.json

Produce a rescue plan (scan + budget)

bun run plan --chain base-mainnet --private-key $HACKED_KEY --rescuer-key $NEW_KEY --gas-price-gwei 10

If you don’t want to provide a key, use NEW_ADDRESS or --rescuer-address for a read-only plan.

Fund a compromised address (auto force-send)

bun run fund --chain base-mainnet --incident inactive --target 0x... --value-eth 0.01

This 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.

Rescue ERC-20s (fund + transfer)

bun run rescue-erc20 --chain base-mainnet --incident inactive --token 0x... --amount 1.5 --rescuer-key $NEW_KEY

Use one of:

  • --amount (human units; decimals auto-detected)
  • --amount-raw (base units)
  • --transfer-all (full balance)
  • --dry-run (no transfer; prints balance + amount)

Rescue ERC-721s (fund + approve + transfer)

bun run rescue-erc721 --chain base-mainnet --incident inactive --token 0x... --token-ids ./token-ids.json --fund-amount-eth 0.01

If 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.

Export token IDs for a collection

bun run export-token-ids --chain base-mainnet --owner 0x... --collection 0x... --out ./token-ids.json

If 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...

Transfer-only ERC-721s (resume after approval)

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.

Rescue ERC-1155s (fund + approve + transfer)

bun run rescue-erc1155 --chain base-mainnet --incident inactive --token 0x... --token-ids ./token-ids.json --fund-amount-eth 0.01

Use --skip-receiver-check to bypass ERC1155Receiver checks for known-compatible contracts.

Transfer-only ERC-1155s (resume after approval)

bun run transfer-erc1155 --chain base-mainnet --token 0x... --token-ids ./token-ids.json --owner 0x...

Control exposure check (delegation + Safe owners)

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

Execute a Safe tx (sign + exec)

bun run safe-exec --chain base-mainnet --safe 0x... --safe-tx ./safe-tx.json --signer-key $HACKED_KEY --sender-key $NEW_KEY

This 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>

Notes

  • 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.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors