zemse / hardhat-tracer

🕵️ allows you to see internal calls, events and storage operations in the console
MIT License
346 stars 36 forks source link

Provider events are not being forwarded by wrapper #73

Open fvictorio opened 1 month ago

fvictorio commented 1 month ago

hre.network.provider is an EventEmitter. One way Hardhat uses this is to emit events when the network is reset or a snapshot is reverted. But when hardhat-tracer is used, any listener already set is "removed". This is easier to understand with an example.

Take this hardhat.config.js:

extendEnvironment(hre => {
  hre.network.provider.on("hardhatNetworkReset", () => console.log("reset"))
})

require("hardhat-tracer")

module.exports = {
  solidity: "0.8.24",
};

Here the extendEnvironment before loading hardhat-tracer is used to represent a plugin that registers a listener on the provider. I'm placing it before loading hardhat-tracer, but it actually doesn't matter if it's after it (because of the way Hardhat executes its initailization).

If you open a console with npx hardhat console and run await hre.network.provider.send("hardhat_reset"), you should see reset being logged, but it's not. If you comment out the hardhat-tracer require, then it will work as expected.

I'm not sure what's the right fix here, but I suspect wrapProvider should somehow re-register any listeners already set in the existing provider.

zemse commented 1 month ago

I'm using BackwardsCompatibilityProviderAdapter, is this a problem with other plugins as well that use this adapter? In that case would it make sense for the BackwardsCompatibilityProviderAdapter to handle this? In case I need to do this I can do that.

fvictorio commented 1 month ago

So, as far as I understand, BackwardsCompatibilityProviderAdapter extends EventEmitterWrapper, which helps as a wrapper that forwards new registered events to the underlying provider. But I don't think it exposes existing registered listeners. Perhaps we could update it to do that, but it might be a bit risky since that's a very core component.

Since this bug happens with a very specific combination of ethers v5, hardhat-tracer, and fixtures/reset, IMO a less risky workaround at the hardhat-tracer would be ideal.

I can help with figuring out how to do it. Off the top of my head, you would need to do something like:

// get existing listeners and remove them from the provider
let eventListeners = {}
for (const eventName of provider.eventNames()) {
  eventListeners[eventName] = []
  for (const listener of provider.listeners(eventName)) {
    eventListeners[eventName].push(listener)
  }
  provider.removeAllListeners(eventName)
}

// wrap the provider as you are doing today
hre.network.provider = wrapProvider(...)

// re-register the listeners
for (const [eventName, listeners] of Object.entries(eventListeners)) {
  for (const listener of listeners) {
    hre.network.provider.on(eventName, listener)
  }
}

(I haven't tested this though)

zemse commented 2 weeks ago

I have included this in hardhat-tracer@3.1.0

fvictorio commented 2 weeks ago

Tested my reproduction steps with v3.1.0 but the behavior is the same :confused: