I need to check if a transaction is a ERC721/ERC1155 transaction and fetch information like receiving address, token address, value, etc. If I understand correctly, I have to load a contract for the transaction and check if it inherits ERC165 in order to confirm that it is a ERC721/ERC1155 transaction.
Problem: I don't understand how I can get a contract having a transaction object. I also haven't found a way to get token address.
I have an Ethereum node on Infura, I read blocks from there and iterate over transactions. I get a transaction and its receipt. My code looks like this:
var tr = web3j.ethGetTransactionByBlockNumberAndIndex(blockIdParam, transactionIndex).sendAsync().get();
var hash = tr.getTransaction().get().getHash();
var receipt = web3.ethGetTransactionReceipt(hash).send().getTransactionReceipt();
Right now I am working in the direction of reading transaction logs, checking their topics and verifying if they include Transfer
events. But transfer events are also emitted by ERC20 transactions, so I am a bit confused here.
You're going in the right direction checking the Transfer()
event logs. Even though both ERC20 and ERC721 use the same event signature, the ERC721 (NFT) standard defines the 3rd topic (token ID) as indexed
, which stores its value in the set of indexed topics. While the ERC20 defines the 3rd topic (amount) as non-indexed, making the total length of indexed topics set just 2.
I picked a random transaction that contains event logs of both ERC20 and ERC721 transfers. Looks like the logic behind it is payment in the form of an ERC20 token to mint a new ERC721 token.
Note: I'm not a Java dev, so I'll use a JS code in my answer that you can hopefully use as a reference to find the correct syntax and methods in the Java implementation of the Web3 library.
const Web3 = require("web3");
const web3 = new Web3("<provider_url>");
// keccak256 of string "Transfer(address,address,uint256)"
const TRANSFER_SIGNATURE = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
async function run() {
const txHash = "0xfb4dc20b4a8d0b72442a396aced0fc537de757c6aac69609ab3d09d19f9c9aa8";
const txReceipt = await web3.eth.getTransactionReceipt(txHash);
for (let log of txReceipt.logs) {
if (_isERC20Log(log)) {
// Unindexed topics are concatenated in the `data` field.
// There's only one unindexed topic in this case,
// so you don't need to do any parsing and just use the whole value.
const amount = web3.utils.hexToNumberString(log.data);
console.log("ERC20 token amount: ", amount);
console.log("ERC20 token contract: ", log.address);
} else if (_isERC721Log(log)) {
// 3rd indexed topic
// index 0 of the array is the event signature, see `_isERC721Log()`
const tokenId = web3.utils.hexToNumberString(log.topics[3]);
console.log("ERC721 token ID: ", tokenId);
console.log("ERC721 token contract: ", log.address);
}
}
}
function _isERC20Log(log) {
return (
log.topics[0] == TRANSFER_SIGNATURE
&& log.topics.length == 3 // index 0 is the signature, and then 2 indexed topics
);
}
function _isERC721Log(log) {
return (
log.topics[0] == TRANSFER_SIGNATURE
&& log.topics.length == 4 // index 0 is the signature, and then 3 indexed topics
);
}
run();
Output:
ERC20 token amount: 40000000000000000000
ERC20 token contract: 0x54a7cee7B02976ACE1bdd4aFad87273251Ed34Cf
ERC721 token ID: 12013
ERC721 token contract: 0x41cB4a771FdD019ADBF4685bd4885fbBeedE1784