Skip to content

Instantly share code, notes, and snippets.

@critesjosh
Last active December 26, 2022 13:27
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save critesjosh/a230e7b2eb54c8d330ca57db1f6239db to your computer and use it in GitHub Desktop.
Save critesjosh/a230e7b2eb54c8d330ca57db1f6239db to your computer and use it in GitHub Desktop.
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()
}
})
}
@critesjosh
Copy link
Author

compile typescript and run with $ node eventWatcher.js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment