Skip to content

Instantly share code, notes, and snippets.

@itherunder
Last active March 6, 2022 08:35
Show Gist options
  • Save itherunder/6c80a67e5eb6260a6c1c653d4264f46c to your computer and use it in GitHub Desktop.
Save itherunder/6c80a67e5eb6260a6c1c653d4264f46c to your computer and use it in GitHub Desktop.
get all holders of a token `fromBlock` to `toBlock`, see more details in readme.md

0x00 Infura

create a infura project at https://infura.io/

0x01 install Web3js

use Web3js to create a Contract object

npm install web3

0x02 install csv-writer

npm install csv-writer

0x03 config file config.json

{
  "provider": "https://mainnet.infura.io/v3/your_token",
  "tokenAddress": "0x7A58c0Be72BE218B41C608b7Fe7C5bB630736C71",
  "fromBlock": "0",
  "toBlock": "latest",
  "abi": []
}

0x04 run

node ./holders.js

0x05 result in holders_0x...csv

holder,balance
0x534Db4d2f6715D9c7023Bd938b0f62D72eE871eF,8
0xDd386096048683378E87FA626C75C2b548fd5e7e,8
...
0x3317AD9eDa6942b5a7BE5BA83346C0Ea82C3C26C,2
0xA62F8ABb12094F5651C8bA7222A0dC1034Ca4B20,6
{
"provider": "https://mainnet.infura.io/v3/your_token",
"tokenAddress": "0x7A58c0Be72BE218B41C608b7Fe7C5bB630736C71",
"fromBlock": "0",
"toBlock": "latest",
"abi": []
}
const fs = require('fs');
const Web3 = require('web3');
const createCsvWriter = require('csv-writer').createObjectCsvWriter;
var config = require('./config.json');
const web3 = new Web3(config['provider']);
var tokenAddr = config['contractAddress'];
var subAddr = tokenAddr.substr(0,4) + '...' + tokenAddr.substr(40,2);
var fromBlock = config['fromBlock'];
var toBlock = config['toBlock'];
var tokenABI = config['abi'];
var token = new web3.eth.Contract(tokenABI, tokenAddr);
var addrs = [];
const csvWriter = createCsvWriter({
path: 'holders_' + subAddr + '.csv',
header: [
{id: 'addr', title: 'holder'},
{id: 'balance', title: 'balance'},
]
});
const SplitGetEvents = async (start, end) => {
var evs = []
try {
evs = await token.getPastEvents('Transfer', {fromBlock:start, toBlock:end});
} catch (err) {
if (err.message === 'Returned error: query returned more than 10000 results') {
const middle = Math.round((start + end) / 2);
console.log('Infura 10000 limit [' + start + '..' + end + '] ' +
'-> [' + start + '..' + middle + '] ' +
'and [' + (middle + 1) + '..' + end + ']');
var levs = await SplitGetEvents(start, middle);
var revs = await SplitGetEvents(middle + 1, end);
return levs.concat(revs);
}
}
return evs;
}
const TokenHolders = async () => {
if (toBlock == 'latest') {
let number = await web3.eth.getBlockNumber()
toBlock = number;
console.log(toBlock);
}
console.log('get all token holders of ' + subAddr + ' from block ' + fromBlock + ' to ' + toBlock);
var evs = await SplitGetEvents(fromBlock, toBlock);
evs.forEach(e => {
addrs.push(e.returnValues.to);
});
addrs = Array.from(new Set(addrs));
return addrs;
}
TokenHolders().then(res => {
fs.writeFile('holders_' + subAddr + '.txt', res.join('\n'), err => {
if (err) {
console.error(err)
return
}
})
var records = [];
var tasks = [];
res.forEach(addr => {
tasks.push(token.methods.balanceOf(addr).call().then(balance => {
if (balance == 0) return;
// console.log(addr, balance);
records.push({'addr': addr, 'balance': balance});
}));
})
Promise.all(tasks).then(() => {
csvWriter.writeRecords(records).then(() => console.log('The CSV file was written successfully'));
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment