1

I am working with the web3.js library on a small project that subscribes to events on a smart contract for an NFT to track all sales on that project through OpenSea. This is working fine when the sale is completed using ETH, but when a sale is made using another form of payment (USDC, DAI, WETH, etc) that OpenSea offers, the value on the transaction from web3.js is coming back as 0, and it shows 0 on etherscan.io as well. Here is an example transaction representing this issue: https://etherscan.io/tx/0x17f050e3fb6d8f0bbb4d9b4e8cd477f8197a87a3a68b360a60a028d7b1037532. Any ideas on how to get the correct value, no matter what type of currency is used?

Ex of

web3.eth.getTransaction('0x17f050e3fb6d8f0bbb4d9b4e8cd477f8197a87a3a68b360a60a028d7b1037532').then((response) => {
console.log(response);

});

Response

{
  accessList: [],
  blockHash: '0x6140d3cb5c271fb351e0a6e9e35b32cf0607ad526152f40f2d98107a97b0212b',
  blockNumber: 13545512,
  chainId: '0x1',
  from: '0x3F4D7b0Eba8CB40D94713023d9Dc02FdB0a5169C',
  gas: 391246,
  gasPrice: '157998874325',
  hash: '0x17f050e3fb6d8f0bbb4d9b4e8cd477f8197a87a3a68b360a60a028d7b1037532',
  input: '0xab834bab0000000000000000000000007be8076f4ea4a4ad08075c2508e481d6c946d12b00000000000000000000000074144fb8749f99382091118f7487f4a541be6d7700000000000000000000000000000000000000000000000000000000000000000000000000000000000000005b3256965e7c3cf26e11fcaf296dfc8807c01073000000000000000000000000bd3531da5cf5857e7cfaa92426877b022e612cf80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000007be8076f4ea4a4ad08075c2508e481d6c946d12b0000000000000000000000003f4d7b0eba8cb40d94713023d9dc02fdb0a5169c00000000000000000000000074144fb8749f99382091118f7487f4a541be6d770000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bd3531da5cf5857e7cfaa92426877b022e612cf80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000226000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000165a0bc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000618193b7000000000000000000000000000000000000000000000000000000006182e53b690c3f82a9b6570ef2de1af15e15942c931dba8a2bd9d5012b74b213beea4da400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000226000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000165a0bc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006182d5b70000000000000000000000000000000000000000000000000000000000000000065ae400a094fafb63173dcf4c7861ca0264920136639dbe27b5956aa6282bb00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006a0000000000000000000000000000000000000000000000000000000000000074000000000000000000000000000000000000000000000000000000000000007e0000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000001c08da96edfaed45809c68db44cabb487dba56fd196fe46d02d679f0161dee94293eaedf214e043bc4d2e3dd69cce82e21ead967d7f1cd8fc505c04781e2d144e308da96edfaed45809c68db44cabb487dba56fd196fe46d02d679f0161dee94293eaedf214e043bc4d2e3dd69cce82e21ead967d7f1cd8fc505c04781e2d144e35c5321ae45550685308a405827575e3d6b4a84aa000000000000000000000000000000000000000000000000000000000000000000000000000000000000006423b872dd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000074144fb8749f99382091118f7487f4a541be6d7700000000000000000000000000000000000000000000000000000000000008ff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006423b872dd0000000000000000000000003f4d7b0eba8cb40d94713023d9dc02fdb0a5169c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008ff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006400000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
  maxFeePerGas: '201893464202',
  maxPriorityFeePerGas: '1500000000',
  nonce: 19,
  r: '0x4c8e6fbe4a49439d957e0e725f3f7e897329557a972863143ff2a34458ab190',
  s: '0x25afd4caa4cdc1f93923b5f41a89bc191d0e52c27a36e51e3015ed08560fea5d',
  to: '0x7Be8076f4EA4A4AD08075C2508e481d6C946D12b',
  transactionIndex: 328,
  type: 2,
  v: '0x1',
  value: '0'
}
TylerH
  • 20,799
  • 66
  • 75
  • 101
Tyler Pashigian
  • 457
  • 4
  • 13

1 Answers1

7

You can get the transaction receipt containing event logs. In this case, you're looking for the Transfer event emitted by the ERC-20 contract, where the ERC-20 token sender is the same as the NFT receiver.

const run = async () => {
    const receipt = await web3.eth.getTransactionReceipt('0x17f050e3fb6d8f0bbb4d9b4e8cd477f8197a87a3a68b360a60a028d7b1037532');
    const nftReceiver = _getNFTReceiver(receipt.logs);

    for (let log of receipt.logs) {
        // keccak256 of "Transfer(address,address,uint256)"
        if (log.topics[0] === '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
            // ERC20 sender (1st param of the Transfer event) is the same as the NFT receiver
            && web3.eth.abi.decodeParameter('address', log.topics[1]).toLowerCase() === nftReceiver.toLowerCase()
            // ERC-20 Transfer returns the value in `data`, while ERC-721 has the same signature but returns empty data
            && log.data !== '0x'
        ) {
            console.log('ERC-20 token contract: ', log.address);
            console.log('ERC-20 token amount (incl. decimals): ', web3.eth.abi.decodeParameter('uint256', log.data));
        }
    }
}

const _getNFTReceiver = (logs) => {
    for (let log of logs) {
        // keccak256 of "Transfer(address,address,uint256)"
        if (log.topics[0] === '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
            // ERC-20 Transfer returns the value in `data`, while ERC-721 has the same signature but returns empty data
            && log.data === '0x'
        ) {
            // 2nd parameter of the `Transfer()` event contains the NFT receiver
            return web3.eth.abi.decodeParameter('address', log.topics[2]);
        }
    }

    return null;
}

run();

prints:

Token contract:  0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Token amount (incl. decimals):  6000000000
Petr Hejda
  • 40,554
  • 8
  • 72
  • 100
  • you are a life saver, sir. What does that hard coded address in `_getNFTReceiver ()` represent? And is that value the same for all smart contracts? Really appreciate the help! – Tyler Pashigian Dec 04 '21 at 13:28
  • 1
    @TylerPashigian it's the keccak-256 hash of the string `Transfer(address,address,uint256)`. The string represents an event that both ERC-20 and ERC-721 token contracts are supposed to emit when they're transfering tokens. For all token contracts following either of these standards, the value is the same. – Petr Hejda Dec 04 '21 at 13:30
  • I see, thanks for the explanation! One last question, when converting the token amount to the correct decimal value, you need to get the number of decimals for that token, I assume theres no way to get token info without making a separate request to an api (etherscan for example) to get the token symbol, use value, decimal count, etc? – Tyler Pashigian Dec 04 '21 at 14:45
  • @TylerPashigian If the token contract follows the ERC20 standard, it should also implement the `decimals()` function that returns the number of decimal places, as well as `name()` and `symbol()` functions to get additional info about the token. So you can make a call to the token contract directly to retrieve the values. Another valid solution is to query a 3rd party API (such as Etherscan), as your comment is saying, which often also includes a current market price (not provided by the token contract alone) and other additional info. – Petr Hejda Dec 04 '21 at 15:54
  • Stupid question here, but how would I connect to a tokens contract when I don't have the ABI? Using the Web3.js library, I'd need to know the ABI and address (which I have) to access the contract (and therefore methods), correct? – Tyler Pashigian Dec 04 '21 at 20:16
  • 1
    @TylerPashigian Without ABI, it's complicated. If you're assuming that the contract implements a standard, you can use a generic ABI for the standard (such as ERC-20; search "ERC-20 ABI" on Google). It won't be able to access functions of the specific contract extending on the standard, but at least it's something... Otherwise, you can use a decompiler that generates a pseudocode from the (compiled) bytecode and build the ABI manually from this. See the "decompile" option on Etherscan/BSCScan address detail page... But generally, it's very hard to communicate with a contract without the ABI. – Petr Hejda Dec 04 '21 at 21:09
  • any way I could message you on LinkedIn? I have a question about hosting an application like this, I am having trouble with Google Cloud for some reason. Would really appreciate it, thanks! – Tyler Pashigian Dec 28 '21 at 14:53
  • @TylerPashigian Sure, you can find link to my LinkedIn on my StackOverflow profile. – Petr Hejda Dec 28 '21 at 15:46