8

The following code works for me to read Ether value from wallet:

var web3Instance =  new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

export async function readAccountEtherValue(address: string): Promise<number> {
    var balance = await web3Instance.eth.getBalance(address);
    return web3Instance.utils.fromWei(balance, "ether");
}

How can I read the values of all tokens from the wallet?

Alon
  • 277
  • 1
  • 3
  • 8

4 Answers4

3

Token balances are not stored in a user's wallet. They are stored in the corresponding Token contract. Assuming the token you want to read the balance from is an ERC20 token, then you would have to instantiate the token contract and then call balanceOf(address) to get the address' balance. For example:

let token = await ERC20Basic.at(_tokenAddress);
let balance = await token.balanceOf(accounts[0]);
pabloruiz55
  • 7,686
  • 2
  • 17
  • 38
  • 1
    OMG that means I have to write a different code for each token in the entire world? – Alon Dec 07 '17 at 15:53
  • Not necessarily, tokens that are ERC20 compliant all follow the specification and have these common functions, for example balanceOf(). What you would need to do in order to read each one is just change the address of the token you want to get the balance of. You could write a function that loops through a list of token addresses and returns each one's balance for a particular user. – pabloruiz55 Dec 07 '17 at 15:55
  • 1
    OK then how can I know which tokens implement ERC20, and how can I know all the existing tokens and know what's their interface? There are websites like etherscan.io that are able to read every existing token, even many tokens that are not listed on Coinmarketcap. – Alon Dec 07 '17 at 16:07
2

You can now use Amazon Managed Blockchain Query to easily retrieve a full list of token balances for a given EOA at finality (2 epochs on Ethereum) or from history (based on a timestamp). I've included some sample Node.js code to show you how to do this.

To run the sample code, the following prerequisites apply:

You must have node version manager (nvm) and Node.js installed on your machine. You can find installation instruction for your OS here.

Use the node - -version command and confirm that you are using Node version 14 or higher. If required, you can use the nvm install 14 command, followed by the nvm use 14 command to install version 14. NOTE: Node 14, 15, 16 are now at end of life.

The environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must contain the credentials that are associated with the AWS account and user with IAM permissions to access Amazon Managed Blockchain Query.

const axios = require('axios').default;
const SHA256 = require('@aws-crypto/sha256-js').Sha256
const defaultProvider = require('@aws-sdk/credential-provider-node').defaultProvider
const HttpRequest = require('@aws-sdk/protocol-http').HttpRequest
const SignatureV4 = require('@aws-sdk/signature-v4').SignatureV4

// define a signer object with AWS service name, credentials, and region const signer = new SignatureV4({ credentials: defaultProvider(), service: 'managedblockchain-query', region: 'us-east-1', sha256: SHA256, });

const queryRequest = async (path, data) => { //query endpoint let queryEndpoint = https://managedblockchain-query.us-east-1.amazonaws.com/${path};

// parse the URL into its component parts (e.g. host, path) const url = new URL(queryEndpoint);

// create an HTTP Request object const req = new HttpRequest({ hostname: url.hostname.toString(), path: url.pathname.toString(), body: JSON.stringify(data), method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept-Encoding': 'gzip', host: url.hostname, } });

// use AWS SignatureV4 utility to sign the request, extract headers and body const signedRequest = await signer.sign(req, { signingDate: new Date() });

try { //make the request using axios const response = await axios({...signedRequest, url: queryEndpoint, data: data})

console.log(response.data)

} catch (error) { console.error('Something went wrong: ', error) throw error }

}

let methodArg = 'list-token-balances';

let dataArg = { "ownerFilter": {"address":"<externally owned account address here"}, "tokenFilter":{"network":"ETHEREUM_MAINNET"} }

//Run the query request. queryRequest(methodArg, dataArg);

The response will follow this format, paginating results listing tokens (ERC20, native ETH, ERC721/1155 NFTs) for an EOA:

    {
   "nextToken": "string", // pagination
   "tokenBalances": [ 
      { 
         "atBlockchainInstant": { 
            "time": number //timestamp 
         },
         "balance": "string", // balance for a token as a string
         "lastUpdatedTime": { 
            "time": number // timestamp of when data was last updated
         },
         "ownerIdentifier": { 
            "address": "string" // EOA address
         },
         "tokenIdentifier": { 
            "contractAddress": "string", // contract address for token
            "network": "string", // network (e.g. Ethereum mainnet)
            "tokenId": "string" // token ID if it's an NFT
         }
      }
   ]
}

Forrest
  • 31
  • 1
2

You could instead use the event Transfer to filter by that. You will probably need an almost full node:

const Web3 = require("web3");
const web3 = new Web3(new Web3.providers.HttpProvider("http://127.0.0.1:8545"));
const transferEventTopic = web3.sha3("Transfer(address,address,uint256)");
const myAddress = "0x0000000000000000000000003f5ce5fbfe3e9af3971dd833d26ba9b5c936f0be";
const fromBlock = 4647513;
const myBalances = {};

const filter = web3.eth.filter({
        fromBlock: fromBlock,
        topics: [ transferEventTopic ]
    });

filter.watch((err, obj) => {
        if (err) {
            console.error(err);
        } else {
            if (typeof myBalances[obj.address] === "undefined") {
                myBalances[obj.address] = web3.toBigNumber(0);
            }
            let message = "Token " + obj.address;
            let changed = false;
            if (obj.topics[1] == myAddress) {
                changed = true;
                myBalances[obj.address] = myBalances[obj.address].minus(obj.data);
                message += ", Sent " + web3.toBigNumber(obj.data).toString(10);
            } else if (obj.topics[2] == myAddress) {
                changed = true;
                myBalances[obj.address] = myBalances[obj.address].plus(obj.data);
                message += ", Received " + web3.toBigNumber(obj.data).toString(10);
            }
            if (changed) {
                message += ", Balance " + myBalances[obj.address].toString(10);
                console.log(message);
            }
        }
    });

process.on('SIGINT', () => {
    console.log("Stop watching");
    filter.stopWatching(console.log);
    process.exit();
});

If you only want the events that denote "receive", then replace the topics as topics: [ transferEventTopic, null, myAddress ].

If you only want the events that denote "send", then replace it is topics: [ transferEventTopic, myAddress ].

  • Does that code iterate all the wallets in the entire world until it finds mine? – Alon Dec 08 '17 at 05:01
  • 1
    That code iterates all the events and filters for your wallet. So yes, heavy. To be faster and less heavy, use the 2 filters listed at the bottom. – Xavier Leprêtre B9lab Dec 08 '17 at 13:08
  • Well it seems that maybe blockchain is not such a great architecture after all. While I assume your code is the formally correct answer, I seriously consider using a workaround, like reading from a website that has already calculated my token balances. How long do you think it's going to take a program to iterate all the blocks ever since 4647513, as your code does? (I'm not cynical, I'm asking seriously in order to make a decision) – Alon Dec 10 '17 at 03:23
  • That would take a sizeable fraction of an hour, I reckon. But this only needs be done once, for all balances. You are correct, blockchain cannot satisfy all your db needs, its needs external aggregators. The benefit you get from these Transfer events is the certainty they happened. – Xavier Leprêtre B9lab Dec 11 '17 at 10:24
0

I just recently wrote a smart contract and library for this, and put it into an NPM package. You can find the package here: https://www.npmjs.com/package/eth-balance-checker

The code would look like this, but you should read the README for that package to make sure you know how it works:

import { getAddressBalances } from 'eth-balance-checker/lib/web3';

const address = '<your_address_here>';
const tokens = ['<token>', '<addresses>', '<here>'];
getAddressBalances(web3, address, tokens).then(balances => {
  console.log(balances); // { "0x0": "100", "0x456...": "200" }
});

You can find lists of tokens to use in your project in various places. MyCrypto has a pretty comprehensive list: https://github.com/MyCryptoHQ/MyCrypto/blob/develop/common/config/tokens/eth.json

wbobeirne
  • 131
  • 4