A CLI tool to replay Ethereum transactions with patched contract bytecode. Perfect for verifying vulnerability patches by replaying historical transactions with fixed contract code.
PonDeReplay allows you to:
- Replay historical transactions - Fetch any transaction from the blockchain
- Patch contract bytecode - Replace the vulnerable contract with your patched version
- Verify patches - Execute the transaction with the new code to confirm the fix works
- Review in context - Analyze how transactions would behave with your security patch
You have a deployed contract with a vulnerability. You've written a patch, but want to verify that:
- All historical transactions would work correctly with the patch
- The patch doesn't break existing functionality
- The patch handles edge cases properly
- Python 3.8+
- Access to an Ethereum RPC endpoint
# Install in development mode
pip install -e .
# Or install with dev dependencies
pip install -e ".[dev]"export ETH_RPC_URL="https://eth-mainnet.alchemyapi.io/v2/YOUR_KEY"pondereplay replay \
--rpc-url $ETH_RPC_URL \
--tx-hash 0x1234567890abcdef... \
--contract-address 0xAbCdEf... \
--bytecode-file ./patched-contract.hexPre-transaction state: by default, all replays execute on the state at block \(N-1\), i.e. the block immediately before the original transaction's block \(N\).
You can also omit --bytecode-file / --bytecode-hex to replay using the original
on-chain bytecode at block \(N-1\). This is equivalent to a sanity-style replay.
Before trusting replay results with patched bytecode, verify the replay mechanism works correctly:
pondereplay sanity-check \
--rpc-url $ETH_RPC_URL \
--tx-hash 0x1234567890abcdef... \
--contract-address 0xAbCdEf... \
--verboseThis command:
- Fetches the original contract bytecode
- Replays the transaction with the original bytecode
- Verifies the result matches the original transaction receipt
If the sanity check fails, something is wrong with your replay setup. If it passes, you can trust replays with patched bytecode.
-
Sanity check (optional but recommended):
pondereplay sanity-check \ --tx-hash $TX_HASH \ --contract-address $CONTRACT_ADDRESS \ --verbose
-
Compile your patched contract with Foundry:
forge build jq '.bytecode.object' out/MyContract.sol/MyContract.json > patched.json
-
Find a transaction to replay (from Etherscan, your logs, etc.)
-
Replay with the patch:
pondereplay replay \ --tx-hash $TX_HASH \ --contract-address $CONTRACT_ADDRESS \ --bytecode-file patched.json \ --verbose
-
Review results - Check if the transaction succeeds with your patch
PonDeReplay can also replay a full history of transactions for a vulnerable contract. Each transaction is executed independently on the state at block \(N-1\) for that tx, with optional patched bytecode applied.
pondereplay replay-history \
--rpc-url $ETH_RPC_URL \
--contract-address $CONTRACT \
--etherscan-api-key $ETHERSCAN_API_KEY \
--etherscan-network sepolia \
--bytecode-file patched.json \
--attack-tx 0xATTACKTX... \
--verbosepondereplay replay-history \
--rpc-url $ETH_RPC_URL \
--contract-address $CONTRACT \
--tx-list-file txs.txt \
--bytecode-file patched.json \
--verboseWhere txs.txt contains either:
- One tx hash per line (blank lines and
#comments ignored), or - JSON:
["0x...", "0x...", ...]or{"tx_hashes": ["0x...", ...]}
If you omit --bytecode-file / --bytecode-hex, replay-history will use the
original contract bytecode for each transaction at its own block \(N-1\).
Sometimes you want to inspect or select transactions first, before replaying.
tx-list fetches the transaction history for a contract from Etherscan and writes
it to a JSON file.
pondereplay tx-list \
--rpc-url $ETH_RPC_URL \
--contract-address 0xCONTRACT \
--etherscan-api-key $ETHERSCAN_API_KEY \
--etherscan-network mainnet \
--start-block 0 \
--end-block latest \
--limit 100 \
--output contract-txs.jsonNotes:
- Output file:
- If you omit
--output, the file is named<contract-address>.json(for example0x418c24191ae947a78c99fdc0e45a1f96afb254be.json). - If you pass
--output path.json, it writes there instead.
- If you omit
- Limiting results:
--limit Nreturns only the firstNtransactions (oldest-first ordering).- If you omit
--limit, the command returns as many transactions as Etherscan allows for that address (subject to API caps).
- JSON structure:
contract_address: the address you queriedcount: number of hashes included intx_hashestx_hashes: array of transaction hashes as 0x-prefixed stringssource:"etherscan"network:"mainnet","sepolia", or"holesky"
You can then feed hashes from this JSON into pondereplay replay or
pondereplay replay-history (via a small script or a tx-list-file).
- Raw hex:
0x608060405234801561001057600080fd5b50... - JSON artifacts (Foundry, Hardhat, Truffle)
- Compiled contract JSON output
- ✅ Fast replay using
eth_callwith state overrides - ✅ Works with any Ethereum RPC provider
- ✅ No blockchain modification
- ✅ Supports multiple bytecode formats
- ✅ Batch replay capability
- ✅ Verbose debugging mode
- ✅ Python API for integration
See full documentation in the README sections below, or use:
pondereplay --help
pondereplay replay --helpPonDeReplay uses the eth_call RPC method with state overrides:
- Fetch the original transaction details
- Use
eth_callwithstate_overrideparameter to patch the contract bytecode - Execute the transaction with the patched code
- Capture return values, logs, gas usage, and execution status
This approach:
- Doesn't require special node methods
- Works with standard RPC providers (Alchemy, Infura, etc.)
- Completes in a single JSON-RPC call
- Doesn't modify blockchain state
Before trusting your patched bytecode results, PonDeReplay provides a sanity check mechanism:
Original TX
↓
Fetch orig. bytecode
↓
Replay with orig. bytecode
↓
Compare with original receipt
↓
✓ If matches: Replay mechanism is working correctly
✗ If differs: Something is wrong with your setup
The sanity check:
- Fetches the original contract bytecode from the blockchain
- Replays the transaction using only the original bytecode (no patch)
- Compares the result with the original transaction receipt
- Confirms the replay mechanism works
This proves your replay mechanism is correct before testing patches. If the sanity check passes, you can trust patched bytecode results are accurate.
from pondereplay import TransactionReplayer
replayer = TransactionReplayer("https://eth-mainnet.example.com")
result = replayer.replay_transaction(
tx_hash="0x...",
contract_address="0x...",
new_bytecode="0x...",
verbose=True
)
if result.success:
print(f"✓ Success! Return value: {result.return_value}")
else:
print(f"✗ Failed: {result.error}")# Validate replay mechanism works with original bytecode
result, matches = replayer.sanity_check(
tx_hash="0x...",
contract_address="0x...",
verbose=True
)
if matches:
print("✓ Sanity check passed - replay mechanism is working!")
else:
print("✗ Sanity check failed - replay output doesn't match original")for tx_hash in transaction_list:
result = replayer.replay_transaction(
tx_hash=tx_hash,
contract_address=VULNERABLE_CONTRACT,
new_bytecode=PATCH_BYTECODE,
)
print(f"{tx_hash}: {'✓' if result.success else '✗'}"){
"success": true,
"tx_hash": "0xabcd1234...",
"block_number": 17564900,
"return_value": "0x0000000000000000000000000000000000000000000000000000000000000001",
"gas_used": 50000,
"output": "0x0000000000000000000000000000000000000000000000000000000000000001",
"logs": []
}# Install dev dependencies
pip install -e ".[dev]"
# Run tests
pytest tests/
# Run tests with coverage
pytest tests/ --cov=pondereplay --cov-report=html
# Format code
black pondereplay/ tests/
# Type check (optional)
mypy pondereplay/See tests/README.md for detailed testing documentation.
MIT
Contributions welcome! Open an issue or PR for bugs, features, or improvements.