3

edit) VM execution error was a bug on Web3, confirmed my code works fine.

I am trying to fetch random addresses from blockchain and trying to categorize them if it is an ERC20 contract address or not.

I've tried with web3.eth.call to check if the contract has a method totalSupply() but there was no success on that thing, with the single address it works fine however at the production stage it returns an odd (node:10722) UnhandledPromiseRejectionWarning: Error: Returned error: VM execution error.

Referring related documents or codes wouldn't help with me unless with web3 1.0.

Here is my code that doesn't work

var Web3 = require('web3');
var web3 = new Web3(new Web3.providers.HttpProvider("http://127.0.0.1:8545"));

var ABI = [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}];

var myContract = new web3.eth.Contract(ABI, "0x085b0fdf115aa9e16ae1bddd396ce1f993c52220");

async function checkAddress() {
  var call = await web3.eth.call({ to: "0x085fb4f24031eaedbc2b611aa528f22343eb52db", data:web3.utils.sha3("totalSupply()")});
  if (call != '0x') {
    console.log('ERC20 token detected');
  } else {
    console.log('Not an ERC20 token address');
  }
}
checkAddress();
7
  • with the single address it works fine - what does that suppose to mean? Mar 20, 2019 at 13:50
  • Side note: Not every contract which implements function totalSupply is an ERC20 contract. Mar 20, 2019 at 13:51
  • @goodvibration Got it, however totalsupply is needed, Mar 20, 2019 at 13:54
  • For "with the single address it works fine", single query with single address works fine, however multiple address query will always return an error Mar 20, 2019 at 13:55
  • Why don't you share your multiple address query code then? If that is where the problem is, then we need to see it, don't you think? Also, be advised that you are calling checkAddress without awaiting for it to complete. It will work fine as long as you don't do anything else after that (which depends on this function-call). But if you do several things (for example, calling it multiple times), then you may possibly need to await for each function call to complete. Without seeing your code, it would be difficult to know for sure. Mar 20, 2019 at 14:12

3 Answers 3

2

I think you should check a few conditions with different ABIs:

  1. Events Transfer(address,address,uint256) and Approval(address,address,uint256) should be emitted in case of any existing transactions to this smart contract

  2. Methods name(), symbol() could return bytes32 or string

  3. Method decimals() could return uint8 or uint256

  4. Method totalSupply() should return uint256

  5. Method balanceOf(address) should return uint256 for any argument

7
  • The deployed EVM code does not have any metadata associated with it. You do not know what functions code entry point exposes to the transactions. Mar 22, 2019 at 11:09
  • Furthermore your points 1, 2 and 3 are a bit incorrect: those are not required by the standard. (Yes, the event signatures are required but the standard doesn't require you to actually emit them) Mar 22, 2019 at 18:24
  • @LauriPeltonen my answer contains the most practical recommendations, do you agree?
    – k06a
    Mar 23, 2019 at 19:18
  • 1
    @MikkoOhtamaa you should form those ABIs on your own.
    – k06a
    Mar 23, 2019 at 19:22
  • 1
    @LauriPeltonen eips.ethereum.org/EIPS/eip-20 it is clearly stated in the standard that events MUST be fired on successful transfers and approvals. Mar 24, 2019 at 0:39
1

This answer is not exactly for your web3 needs but more of a generic answer. I asked a very similar question some time ago here: How is a contract determined to be ERC20 compliant?

So, in the end, if the signatures are correct then it's considered an ERC20 contract. The actual implementation doesn't matter as long as the signatures are correct.

As for how to finding signatures from a deployed contract: as I see it you have two options:

1) Reverse-engineer the bytecode and try to find the signatures from there. This isn't necessarily possible or very easy: How can you decompile a smart contract?

2) Issue all the relevant transactions (calling each ERC20 function) to the contract and see if they go through. This basically requires that you guess the ABI, part by part (it's easy enough to guess if it's ERC20 contract).

3
  • And how to verify the signatures? Using call method or something else? Mar 21, 2019 at 4:07
  • Can you share me a code so I can understand? Mar 21, 2019 at 4:08
  • Edited my answer to issue your questions. But not, I don't have any ready code available. Mar 21, 2019 at 4:26
1

If you want to use an API it's dead simple. Amberdata's /addresses/:hash/information endpoint returns of list of contract types like so...

Request:

curl --request GET \
  --url https://web3api.io/api/v1/addresses/0x06012c8cf97bead5deae237070f9587f8e7a266d/information \
  --header 'x-api-key: <x-api_key>'

Response:

{
  "status": 200,
  "title": "OK",
  "description": "Successful request",
  "payload": [
    { 
      "firstSeen": "2017-11-23T05:41:19.000Z",
      "type": "contract",
      "contractTypes": [
        "ERC721"      // <-- Here's what you are looking for
      ],
      "probability": 1,
      "network": {
        "name": "Ethereum",
        "network": "Mainnet",
        "slug": "ethereum-mainnet",
        "blockchainId": "1c9c969065fcd1cf",
        "link": "https://ethereum-mainnet.amberdata.io/addresses/0x06012c8cf97bead5deae237070f9587f8e7a266d"
      }
    },
    ...
  ]
}

This is the Cryptokitties address and as you can see it returns a contract type of ERC712.

If you have an API key (it's free) you can try it out here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.