An example script for watching for events emitted by a Celo contract via Forno
[{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"string","name":""}],"name":"name","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"bool","name":""}],"name":"approve","inputs":[{"type":"address","name":"spender"},{"type":"uint256","name":"value"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"validatorSignerAddressFromCurrentSet","inputs":[{"type":"uint256","name":"index"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"valueToUnits","inputs":[{"type":"uint256","name":"value"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":""}],"name":"initialized","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"totalSupply","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"setInflationParameters","inputs":[{"type":"uint256","name":"rate"},{"type":"uint256","name":"updatePeriod"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"bool","name":""}],"name":"transferFrom","inputs":[{"type":"address","name":"from"},{"type":"address","name":"to"},{"type":"uint256","name":"value"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":""}],"name":"checkProofOfPossession","inputs":[{"type":"address","name":"sender"},{"type":"bytes","name":"blsKey"},{"type":"bytes","name":"blsPop"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint8","name":""}],"name":"decimals","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"bool","name":""}],"name":"increaseAllowance","inputs":[{"type":"address","name":"spender"},{"type":"uint256","name":"value"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getEpochNumberOfBlock","inputs":[{"type":"uint256","name":"blockNumber"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"bool","name":""}],"name":"mint","inputs":[{"type":"address","name":"to"},{"type":"uint256","name":"value"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"bool","name":""}],"name":"burn","inputs":[{"type":"uint256","name":"value"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bytes32","name":""}],"name":"getVerifiedSealBitmapFromHeader","inputs":[{"type":"bytes","name":"header"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"debitGasFees","inputs":[{"type":"address","name":"from"},{"type":"uint256","name":"value"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"validatorSignerAddressFromSet","inputs":[{"type":"uint256","name":"index"},{"type":"uint256","name":"blockNumber"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bytes32","name":""}],"name":"hashHeader","inputs":[{"type":"bytes","name":"header"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"creditGasFees","inputs":[{"type":"address","name":"from"},{"type":"address","name":"feeRecipient"},{"type":"address","name":"gatewayFeeRecipient"},{"type":"address","name":"communityFund"},{"type":"uint256","name":"refund"},{"type":"uint256","name":"tipTxFee"},{"type":"uint256","name":"gatewayFee"},{"type":"uint256","name":"baseTxFee"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"balanceOf","inputs":[{"type":"address","name":"accountOwner"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"renounceOwnership","inputs":[],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"minQuorumSizeInCurrentSet","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"registry","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"initialize","inputs":[{"type":"string","name":"_name"},{"type":"string","name":"_symbol"},{"type":"uint8","name":"_decimals"},{"type":"address","name":"registryAddress"},{"type":"uint256","name":"inflationRate"},{"type":"uint256","name":"inflationFactorUpdatePeriod"},{"type":"address[]","name":"initialBalanceAddresses"},{"type":"uint256[]","name":"initialBalanceValues"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"numberValidatorsInCurrentSet","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getBlockNumberFromHeader","inputs":[{"type":"bytes","name":"header"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"owner","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":""}],"name":"isOwner","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"string","name":""}],"name":"symbol","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getEpochNumber","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"numberValidatorsInSet","inputs":[{"type":"uint256","name":"blockNumber"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"bool","name":""}],"name":"decreaseAllowance","inputs":[{"type":"address","name":"spender"},{"type":"uint256","name":"value"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""},{"type":"uint256","name":""},{"type":"uint256","name":""},{"type":"uint256","name":""}],"name":"getInflationParameters","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"bool","name":""}],"name":"transfer","inputs":[{"type":"address","name":"to"},{"type":"uint256","name":"value"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"setRegistry","inputs":[{"type":"address","name":"registryAddress"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"unitsToValue","inputs":[{"type":"uint256","name":"units"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"allowance","inputs":[{"type":"address","name":"accountOwner"},{"type":"address","name":"spender"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getEpochSize","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"bool","name":""}],"name":"transferWithComment","inputs":[{"type":"address","name":"to"},{"type":"uint256","name":"value"},{"type":"string","name":"comment"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"minQuorumSize","inputs":[{"type":"uint256","name":"blockNumber"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""},{"type":"uint256","name":""}],"name":"fractionMulExp","inputs":[{"type":"uint256","name":"aNumerator"},{"type":"uint256","name":"aDenominator"},{"type":"uint256","name":"bNumerator"},{"type":"uint256","name":"bDenominator"},{"type":"uint256","name":"exponent"},{"type":"uint256","name":"_decimals"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bytes32","name":""}],"name":"getParentSealBitmap","inputs":[{"type":"uint256","name":"blockNumber"}],"constant":true},{"type":"event","name":"InflationFactorUpdated","inputs":[{"type":"uint256","name":"factor","indexed":false},{"type":"uint256","name":"lastUpdated","indexed":false}],"anonymous":false},{"type":"event","name":"InflationParametersUpdated","inputs":[{"type":"uint256","name":"rate","indexed":false},{"type":"uint256","name":"updatePeriod","indexed":false},{"type":"uint256","name":"lastUpdated","indexed":false}],"anonymous":false},{"type":"event","name":"Transfer","inputs":[{"type":"address","name":"from","indexed":true},{"type":"address","name":"to","indexed":true},{"type":"uint256","name":"value","indexed":false}],"anonymous":false},{"type":"event","name":"TransferComment","inputs":[{"type":"string","name":"comment","indexed":false}],"anonymous":false},{"type":"event","name":"Approval","inputs":[{"type":"address","name":"owner","indexed":true},{"type":"address","name":"spender","indexed":true},{"type":"uint256","name":"value","indexed":false}],"anonymous":false},{"type":"event","name":"RegistrySet","inputs":[{"type":"address","name":"registryAddress","indexed":true}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","indexed":true},{"type":"address","name":"newOwner","indexed":true}],"anonymous":false}] |
// This script will watch for events emitted by contracts specified in the `watchedAddresses` array. | |
// Adding to the `topics` array will filter events | |
// More info on topics can be found here: https://solidity.readthedocs.io/en/develop/abi-spec.html#events | |
// At the time of writing, Forno websocket connections are disconnected after 20 minutes | |
// When the websocket connection is broken, the script will stop listening for 500ms before attempting to reconnect | |
// If a block is created during that gap, the relevant events in that block will be missed. Running two or more listeners concurrently will reduce the chance of missed blocks | |
const Web3 = require('web3') | |
const cUSD_abi = require('cUSD_abi.json') | |
let watchedAddresses: Array<string> = ["0x765DE816845861e75A25fCA122bb6898B8B1282a"] // cUSD contract | |
let topics: Array<null|string> = [] | |
let lastSeenBlock: null|Number = null | |
function setupProviderAndSubscriptions() { | |
let provider = new Web3.providers.WebsocketProvider('wss://forno.celo.org/ws') | |
let web3 = new Web3(provider) | |
let setupNewProvider = false | |
// Keeps track of the number of times we've retried to set up a new provider | |
// and subs without a successful header | |
let sequentialRetryCount = 0 | |
const setupNewProviderAndSubs = async () => { | |
// To prevent us from retrying too aggressively, wait a little if | |
// we try setting up multiple times in a row | |
const sleepTimeMs = sequentialRetryCount * 100 | |
console.log('sleeping', sleepTimeMs) | |
await sleep(sleepTimeMs) | |
sequentialRetryCount++ | |
// To avoid a situation where multiple error events are triggered | |
if (!setupNewProvider) { | |
setupNewProvider = true | |
setupProviderAndSubscriptions() | |
} | |
} | |
provider.on('error', async (error: any) => { | |
console.log('WebsocketProvider encountered an error', error) | |
await setupNewProviderAndSubs() | |
}) | |
provider.on('end', async () => { | |
console.log('WebsocketProvider has ended, will restart') | |
await setupNewProviderAndSubs() | |
}) | |
let headerSubscription = web3.eth.subscribe('newBlockHeaders') | |
headerSubscription.on('data', function(blockHeader: any) { | |
if (sequentialRetryCount > 0) { | |
sequentialRetryCount = 0 | |
} | |
lastSeenBlock = blockHeader.number | |
}) | |
let eventSubscription = web3.eth.subscribe('logs', { | |
address: watchedAddresses, | |
fromBlock: lastSeenBlock, | |
topics: topics | |
}) | |
eventSubscription.on('data', function (data: any) { | |
console.log(data) | |
}) | |
eventSubscription.on('error', async (error: any) => { | |
console.log('Block header subscription encountered an error', error) | |
await setupNewProviderAndSubs() | |
}) | |
} | |
setupProviderAndSubscriptions() | |
function sleep(ms: number, onSleep?: () => void): Promise<void> { | |
return new Promise((resolve) => { | |
setTimeout(resolve, ms) | |
if (onSleep) { | |
onSleep() | |
} | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
compile typescript and run with
$ node eventWatcher.js