(Note, all examples are tested with web3 1.0.0-beta*)
ERC20 Transfer Event Signature
ERC20 has the event Transfer(address,address,uint256)
you can use to track token transferring logs. In order to filter all Transfer events, you need to get the event signature through one of these two ways:
const ERC20_TRANSFER_EVENT_ABI = {
name: 'Transfer',
type: 'event',
inputs: [{
type: 'address',
name: 'from',
indexed: true
}, {
type: 'address',
name: 'to',
indexed: true
}, {
type: 'uint256',
name: 'value',
}]
};
const ERC20_TRANSFER_EVENT = Web3.utils.sha3('Transfer(address,address,uint256)');
console.log("ERC20_TRANSFER_EVENT hash", ERC20_TRANSFER_EVENT);
const ERC20_TRANSFER_EVENT2 = web3.eth.abi.encodeEventSignature(ERC20_TRANSFER_EVENT_ABI);
console.log("ERC20_TRANSFER_EVENT2 hash", ERC20_TRANSFER_EVENT2);
Historical logs
To track historical logs, you need to connect to a full archive node first. You could also use infura.io as a provider.
Once you connect to the provider with full archive, you can then use web3 getPastLogs api:
function toHex(n) {
return '0x' + Number(n).toString(16);
}
async function list() {
for (let i = FROM_BLOCK/100; i < TO_BLOCK/100; ++i) {
let f = i * 100;
let t = f + 99;
await web3.eth.getPastLogs({
fromBlock: toHex(f),
toBlock: toHex(t),
address: YOUR_TOKEN_ADDRESS,
topics: [
ERC20_TRANSFER_EVENT
]
}, (error, result) => {
if (error) { console.error(error); return; }
console.log(`[${f}:${t}] ${result.length}`);
result.forEach(log => {
let dlog = web3.eth.abi.decodeLog(ERC20_TRANSFER_EVENT_ABI.inputs, log.data, log.topics.slice(1))
console.log(` ${dlog.from} -> ${dlog.to} : ${dlog.value}`);
});
});
}
}
Live Log Subscription
You need to connect to a provider with the subscription support, and also through a WebSocket transport. Once you manage to do that, you can subscribe to live log updates:
function subscribe_erc20_logs() {
let sub = web3.eth.subscribe('logs', {
address: YOUR_TOKEN_ADDRESS,
topics: [
ERC20_TRANSFER_EVENT
]
}, function (error, log) {
if (error) { console.error(error); return; }
let dlog = web3.eth.abi.decodeLog(ERC20_TRANSFER_EVENT_ABI.inputs, log.data, log.topics.slice(1))
console.log(`new log: ${dlog.from} -> ${dlog.to} : ${dlog.value}`);
});
}
Offchain database
Regarding your question about offchain database, I think you could also keep a address -> token_amount
mapping in one of your token contract. Keeping a offchain database is fine for other more complex view of the blockchain history, as long as you handle properly the potential bugs which could lead to inconsistency between blockchain and your database.