ethers.js: Contract events not being emitted reliably

Ethers Version

5.6.7

Search Terms

alchemy, event, eventfilter, optimism

Describe the Problem

Hey all,

I’m having a problem with contract events being not reliable as in they are mostly not emitted but sometimes are emitted.

Events from a smart contract (Gnosis Safe contract, SignMsg but true for other events as well) events usually are not being emitted through **ethers** on **optimism** even though I can see they are actually emitted from the contract on etherscan. I’m using Alchemy as the RPC provider and it seems there are no outages or rate limiting on there as well.

In the below code, you can see how i wait for the events.

This only happens on Optimism. Goerli works perfectly fine.

I’m curious why this piece of code fails to receive the events sometimes. Any clue or suggestion is appreciated 🙏🙏🙏

Thanks in advance.

Code Snippet

const waitSmartContractSignature = async (signer: Signer, hash: string) => {
    const walletContract = new Contract(await signer.getAddress(), EIP1271.abi, signer)
    const internalHash = await walletContract.getMessageHash(hash)
    const eventFilter = walletContract.filters.SignMsg(internalHash)
    return await resolveOnContractEvent(walletContract, eventFilter)
}

const resolveOnContractEvent = (contract: Contract, eventFilter: EventFilter) =>
    new Promise<void>((resolve, reject) => contract.once(eventFilter, resolve).once('error', reject))


### Contract ABI

```shell
{
    "abi": [
        {
            "anonymous": false,
            "inputs": [
                {
                    "indexed": true,
                    "internalType": "bytes32",
                    "name": "msgHash",
                    "type": "bytes32"
                }
            ],
            "name": "SignMsg",
            "type": "event"
        },
        {
            "inputs": [
                {
                    "internalType": "bytes",
                    "name": "message",
                    "type": "bytes"
                }
            ],
            "name": "getMessageHash",
            "outputs": [
                {
                    "internalType": "bytes32",
                    "name": "",
                    "type": "bytes32"
                }
            ],
            "stateMutability": "view",
            "type": "function"
        },
        {
            "inputs": [
                {
                    "internalType": "bytes",
                    "name": "_data",
                    "type": "bytes"
                }
            ],
            "name": "signMessage",
            "outputs": [],
            "stateMutability": "nonpayable",
            "type": "function"
        },
        {
            "constant": true,
            "inputs": [
                {
                    "internalType": "bytes32",
                    "name": "_hash",
                    "type": "bytes32"
                },
                {
                    "internalType": "bytes",
                    "name": "_signature",
                    "type": "bytes"
                }
            ],
            "name": "isValidSignature",
            "outputs": [
                {
                    "internalType": "bytes4",
                    "name": "",
                    "type": "bytes4"
                }
            ],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
        }
    ]
}

Errors

No response

Environment

Browser (Chrome, Safari, etc)

Environment (Other)

optimism, alchemy

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Comments: 23 (8 by maintainers)

Most upvoted comments

@Siegrift One thing I noticed with one of your above examples, is that you are not waiting for the event to start before triggering the events. The .on requires some time to start up (you can await on it, but that won’t include the subscriber starting, although I’ve been considering adding that). So it isn’t skipping events, you are simply not yet subscribed, since there are many steps involved in subscribing that are async, which is why you only get the last 11.

The window isn’t about skipping blocks. It expands the search window.

The problem is that that a node might respond to getBlockNumber of 1000, but a getLogs(1000, latest) might return an empty list (even though there are events), because the node hasn’t indexed the events yet (but has updated its internal block height seen). So, the internal maxFilterBlockRange is used so, when polling, it will continue asking for logs in block 1000 up until 1010 (i.e. 1000 + maxFilterBlockRange).

It internally updates the base block when an event is returned for that block, so if no events have been seen for block 1000 yet, but getLogs(1000, 1005) has an event for block 1000, the next update will only then use getLogs(1001, 1005).

If that makes sense. So, nothing is skipped. It is just used to determine how long to allow a node to return inconsistent results (in this case, inconsistent means returning a block height X for which it hasn’t added the events from block X to its db yet).

Thanks! I will probably add this to the Network config as a plug-in then. It was originally added for Polygon which was the first network to have inconsistent log reads. I will talk to the optimism team too to figure out if there is an upper bound, or if it is a function of polling interval.