-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(pg): Improve reliability of internal hardhat simulation in monorepos #1710
Conversation
0289669
to
ee5e275
Compare
72245d4
to
b90553e
Compare
/** | ||
* Sphinx: We modified this class to accept an entire Network configuration object | ||
* instead of just the name. We later pass this class to the associated signer so that | ||
* it does not need to read from the hre global variable. | ||
*/ | ||
private readonly _network: Network | ||
) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comment.
// Account index | ||
if (typeof address === 'number') { | ||
const accounts: string[] = await accountsPromise | ||
if (address >= accounts.length) { | ||
throw new AccountIndexOutOfRange(address, accounts.length) | ||
} | ||
return InProcessEthersSigner.create( | ||
this, | ||
accounts[address], | ||
// Sphinx: Here we pass the network object to the signer | ||
this._network | ||
) | ||
} | ||
|
||
if (typeof address === 'string') { | ||
// Sphinx: Here we pass the network object to the signer | ||
return InProcessEthersSigner.create(this, address, this._network) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comments.
There are no other changes to this class.
b90553e
to
99dc133
Compare
// Sphinx: We accept a network object here which is then used to configure the signer instead of reading from the HRE. | ||
network: Network |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comment
/** | ||
* This class is a fork of the Nomic Foundation HardhatEthersProvider, credit to them: | ||
* https://github.com/NomicFoundation/hardhat/blob/3e32ba0e16bdbf52b8aeb7e696461a221a435dc2/packages/hardhat-ethers/src/internal/hardhat-ethers-provider.ts | ||
* We've also forked the associated HardhatEthersSigner class. | ||
* | ||
* Sphinx needs in-process network for its internal simulation step. This network is created using the `createProvider` function of hardhat which | ||
* creates an isolated network suitable for this use case: | ||
* https://github.com/NomicFoundation/hardhat/blob/3e32ba0e16bdbf52b8aeb7e696461a221a435dc2/packages/hardhat-core/src/internal/core/providers/construction.ts#L49 | ||
* | ||
* The `createProvider` function returns an `EthereumProvider` object which is not immediately compatible with our execution logic which uses ethers. So to make | ||
* the execution process work with this provider, we must convert it a compatible provider. For that purpose, we've created this fork of the `HardhatEthersProvider`. | ||
* The `HardhatEthersProvider` is perfect for our use case since it is designed to make the in-process hardhat node compatible with ethers. However, the standard | ||
* implementation requires a hardhat project to be used which is problematic for our situation. | ||
* | ||
* So this fork makes a minor modification to the `HardhatEthersProvider` and `HardhatEthersSigner` classes to allow them to be used outside of a hardhat project. | ||
* Where the original implementation relied on reading the hardhat configuration options directly from the `HardhatRuntimeEnvironment` global var, this implementation | ||
* accepts a `network` object in the `InProcessEthersProvider` constructor which includes all of the necessary configuration options. | ||
* | ||
* To review the exact changes we made to this class, you can search `Sphinx` in this file and in the associated `hardhat-signer.ts` file. | ||
* | ||
* You might wonder why we made a full copy of the both classes just to make these modifications. The reason is that `HardhatEthersSigner` only implements a private | ||
* constructor which makes it impossible for us to use subclasses to make the required changes. | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please read the whole comment carefully.
I don't recommend reviewing much else in this file, as it's just a copy of the HardhatEthersProvider with minimal modifications. I've left comments indicating my specific changes, though, if you'd like to look through those quickly.
07687eb
to
f3137ad
Compare
@@ -65,7 +64,7 @@ | |||
"typescript": "^5.1.3", | |||
"yargs": "^17.7.2", | |||
"yesno": "^0.4.0", | |||
"hardhat": "2.20.1" | |||
"hardhat": "2.22.3" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fixes CHU-1067.
7dc24f8
to
9e9aa64
Compare
4c703de
to
b592ac2
Compare
0a348fd
to
bf9e254
Compare
let getaddrinfoErrors = 0 | ||
// If an error occurs, we write the error message and stack trace to `stdout` then exit the process | ||
// with exit code `1`. We write the error to `stdout` instead of `stderr` because `stderr` may | ||
// contain warnings that were written via `console.warn`, which are indistinguishable from the | ||
// actual error message in `stderr`. By using `stdout`, we can throw an error that doesn't contain | ||
// warnings in the parent process. | ||
process.on('uncaughtException', (error) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We still need to catch getaddrinfo ENOTFOUND
errors, but since we no longer invoke the hardhat runner as a separate script, we set this up when the CLI is first called.
9f2db5b
to
ab8df0f
Compare
ab8df0f
to
2cdad04
Compare
2cdad04
to
e65fb7a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great
Purpose
hardhat.config.js
file used to spin up our internal simulation.Solution
We no longer rely on a hardhat task to create the in-process hardhat node. Instead, we create the internal hardhat process provider from scratch and then convert it into an ethers-compatible provider, which we then use for the simulation. This required creating a slightly modified version of the
EthersHardhatProvider
, which is functionally equivalent but does not require a hardhat project to work. This setup eliminates the brittle process of invoking a hardhat subtask in a child process using thehardhatRunner
script and an actualhardhat.config.js
configuration file.Riders
This PR also includes a number of changes that resolve issues recently introduced by updates to Foundry and Hardat.
CHU-1067
by updating to hardhat2.22.3
.2.22.3
, a bug that causes the simulation to fail in certain situations when running against local nodes was introduced. I tried to resolve that issue but could not, so I disabled the simulation when the target network is a local node. This also required removing/updating some tests that relied on the simulation. I've created a ticket to reverse these changes if/when hardhat fixes the bug.