CryptoPunks
Walkthrough of a CryptoPunks bug and how GhostLogs fixes it
The CryptoPunks contract has a bug in the acceptBidForPunk function. When a bid is accepted, right
before the PunkBought event is emitted, the values are inadvertently reset as shown below.
If the PunkBought event had been 2 lines up it would have been emitted correctly but instead the bidder
is erroneously logged as the zero address (0x0000000000000000000000000000000000000000) and the value is
shown as 0. (This only happens when the punk is purchased through acceptBidForPunk)
We can confirm by looking at etherscan that these values are being emitted incorrectly.
Let's fix this with GhostLogs.
Step 1: Create a fork

Step 2: Add the CryptoPunks market contract
If you want to copy-paste the address, 0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB is the contract address
for the CryptoPunks marketplace

In the next screen you'll be able to set the version since GhostLogs supports multiple versions of a single contract and allows you to switch between them.
For now, insert "1" as the version and click 'Create'
Step 3: Edit the contract
Here is a link to the modified code for your reference: CryptoPunks
Declare a new event GhostPunkBidAccepted

In the acceptBidForPunk function, emit your newly declared event above the line where the punkBids
storage value changes

Click on 'Compile and Save'
Step 4: Simulate to test new code

Let's simulate the same transaction
that we showed above and which had a wrong value and toAddress. Click on 'Add Tx Hash' and input a transaction
that you want to simulate against your newly added gasless events.

As we can see, the value and toAddress are now correct! Now that we are satisfied with our modifications
let's deploy the Fork to activate RPC and WS endpoints.
Step 5: Spin up a GhostFork RPC endpoint
Back on the Fork details page, click on 'Deploy fork' to activate your RPC and WS endpoints

In the deployments page you can find the endpoints for each deployed fork. Then you can connect using libraries like ethers, viem, alloy, etc using the Supported Methods
Let's look at how to synchronously fetch a few logs with getLogs.
(Or check out a websocket subscription example)
import { EventFragment, Interface, JsonRpcProvider } from 'ethers'
const RPC_URL = 'https://rpc.ghostlogs.xyz/<FORK_KEY_FROM_DASHBOARD>'
const PUNKS_ADDRESS = '0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB'
async function run() {
// You can also use the ABI, but let's write it out new event here to be explicit
const bidAccepted = EventFragment.from(
`GhostPunkBidAccepted(uint indexed punkIndex, uint value,
address indexed fromAddress, address indexed toAddress)`
)
const iface = new Interface([bidAccepted])
const provider = new JsonRpcProvider(RPC_URL)
const logs = await provider.getLogs({
address: PUNKS_ADDRESS,
fromBlock: 18903200,
toBlock: 18903300,
topics: [[bidAccepted.topicHash]],
})
for (let log of logs) {
const decodedLog = iface.decodeEventLog(bidAccepted, log.data, log.topics)
console.log({
block: log.blockNumber,
txHash: log.transactionHash,
txIdx: log.transactionIndex,
punkTokenId: decodedLog.punkIndex,
value: decodedLog.value,
from: decodedLog.fromAddress,
to: decodedLog.toAddress,
})
}
}
if (require.main === module) {
;(async () => {
await run()
})()
}
Output:
{
block: 18903238,
txHash: '0x028399088834d783207824dbe291285981be42e94c44d86c3859676ce5247613',
txIdx: 8,
punkTokenId: 5256n,
value: 53690000000000000000n,
from: '0x65967071bA921D1d3F650420a3e814d13994FA8e',
to: '0xd4f7335eaDfE176117B7719b323cf31A7f273310'
}