I am trying to decode the Approval event of an ERC721 contract.
I fetch the logs for the contract with the alchemy API and then try to decode them
with web3js.
The code looks like this:
const logs = await alchemy.core.getLogs({
address: contractAddress,
topics: ["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"],
fromBlock: firstNumber,
toBlock: secondNumber
})
for(let key in logs){
const data = logs[key].data
const topics = logs[key].topics
let result = web3.eth.abi.decodeLog(
[
{type: 'address', name: 'owner', indexed: true},
{type: 'address', name:'approved', indexed: true},
{type: 'uint256', name:'tokenId', indexed: true},
],
[data],
topics
)}
This results in the following error:
Uncaught (in promise) null: value out of range (argument="value", value=20, code=INVALID_ARGUMENT, version=bytes/5.7.0)
I tried to use toString() on the data and the topic before, as it worked with decoding other events, but this resulted in this error:
Uncaught (in promise) Error: invalid arrayify value (argument="value", value="{topic values}", code=INVALID_ARGUMENT, version=bytes/5.7.0
Looking at the data and topics I get from the logs, I noticed that data always is just 0x. The topics are the actual value.
Looking up the transactions on etherscan I also noticed that the data value is also 0x so that seems correct?
What am I doing wrong?
Best Answer
dev advocate at Chainstack here.
When you retrieve the logs, you receive an array of different elements. Let's take the Approval event from an ERC-721 contract as an example since it's what you need.
In this case, this is how it looks in the smart contract:
As you can see, this event has 3 indexed parameters, which means that those values will be returned into the
topics
array in the logs object. You can only mark up to 3 elements asindexed
; the non-indexed parameters will be returned in a separate array nameddata
.A typical
topics
array for the approval event looks like this:As you can see, there are 4 elements because the first element is the event signature (like you have in your code). The last 3 elements correspond to what's returned by the event:
These elements are encoded as 32-byte size, and to take the addresses, you have to take the last 20 bytes; in this case, the last 20 bytes equal to the last 40 characters as they are in hex format 2 hex digits make up one byte.
I'm not very familiar with the Alchemy SDK, but here is a script in
web3.js
to retrieve and parse theApproval
event from an ERC-721 contract using the getPastLogs method.Everything in the
getLogs
function is similar to what you already do. TheparseLogs
function extracts the topics, decodes them, and prints them on the screen.Running it will give you something like this: