Skip to main content

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

PunksCreateFork

Step 2: Add the CryptoPunks market contract

If you want to copy-paste the address, 0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB is the contract address for the CryptoPunks marketplace

PunksAddContract

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

Follow Along

Here is a link to the modified code for your reference: CryptoPunks

Declare a new event GhostPunkBidAccepted

PunksEventDeclare

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

PunksEventEmit

Click on 'Compile and Save'

Step 4: Simulate to test new code

PunksRunSimulation

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.

PunksSimulate

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

PunksDeployFork

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'
}