-
Notifications
You must be signed in to change notification settings - Fork 156
gre: refactor to allow using L1 and L2 simultaneously #682
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
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
c9c4f72
feat: refactor GRE to support L1 and L2 simultaneously
tmigone 39e03e8
feat: allow using graphConfig for hh network
tmigone 1343eac
chore: add debug based logger to GRE
tmigone 632e184
chore: add tests to GRE
tmigone 3a9d38d
fix: use ethers provider for GRE
tmigone a440a1d
chore: cleanup provider types in GRE
tmigone 5ac8a3f
fix: e2e deployment types
tmigone d003545
chore: add readme to GRE
tmigone 37cddd3
chore: add provider to GRE objects
tmigone File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"require": "ts-node/register/files", | ||
"ignore": ["test/fixture-projects/**/*"], | ||
"timeout": 6000 | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
# Graph Runtime Environment (GRE) | ||
|
||
GRE is a hardhat plugin that extends hardhat's runtime environment to inject additional functionality related to the usage of the Graph Protocol. | ||
|
||
### Features | ||
|
||
- Provides a simple interface to interact with protocol contracts | ||
- Exposes protocol configuration via graph config file and address book | ||
- Provides account management methods for convenience | ||
- Multichain! Supports both L1 and L2 layers of the protocol simultaneously | ||
|
||
### Usage | ||
|
||
#### Example | ||
Import GRE using `import './gre/gre'` on your hardhat config file and then: | ||
|
||
```js | ||
// Use L2 governor account to set the L1 token address on the L2 gateway | ||
const { l1, l2 } = hre.graph() | ||
|
||
const { GraphToken } = l1.contracts | ||
|
||
const { L2GraphTokenGateway } = l2.contracts | ||
const { governor } = await l2.getNamedAccounts() | ||
|
||
const tx = L2GraphTokenGateway.connect(governor).setL1TokenAddress(GraphToken.address) | ||
``` | ||
|
||
#### Network selection | ||
|
||
GRE supports both the L1 and L2 networks of the Graph Protocol by default. It will use hardhat's network defined via `--network` as the "main" network and then automatically detect which is the appropriate counterpart network in L1 or L2. | ||
|
||
Example: | ||
|
||
```bash | ||
# L1: goerli and L2: arbitrum-goerli | ||
hh console --network goerli | ||
|
||
# L1: mainnet and L2: arbitrum-one | ||
hh console --network arbitrum-one | ||
|
||
# L1: mainnet and L2: arbitrum-one > same as previous | ||
hh console --network mainnet | ||
``` | ||
|
||
#### Configuration | ||
|
||
To use GRE you'll need to configure the target networks. That is done via either hardhat's config file using the `networks` [config field](https://hardhat.org/hardhat-runner/docs/config#json-rpc-based-networks) or by passing the appropriate arguments to `hre.graph()` initializer. | ||
|
||
__Note__: The "main" network, defined by hardhat's `--network` flag _MUST_ be properly configured for GRE to initialize successfully. It's not necessary to configure the counterpart network if you don't plan on using it. | ||
|
||
**Hardhat: Network config** | ||
```js | ||
networks: { | ||
goerli: { | ||
chainId: 5, | ||
url: `https://goerli.infura.io/v3/123456` | ||
accounts: { | ||
mnemonic: 'test test test test test test test test test test test test', | ||
}, | ||
graphConfig: 'config/graph.goerli.yml' | ||
}, | ||
} | ||
``` | ||
|
||
Fields: | ||
- **(_REQUIRED_) chainId**: the chainId of the network. This field is not required by hardhat but it's used by GRE to simplify the API. | ||
- **(_REQUIRED_) url**: the RPC endpoint of the network. | ||
- **(_OPTIONAL_) accounts**: the accounts to use on the network. These will be used by the account management functions on GRE. | ||
- **(_OPTIONAL_) graphConfig**: the path to the graph config file for the network. | ||
|
||
**Hardhat: Graph config** | ||
|
||
Additionally, the plugin adds a new config field to hardhat's config file: `graphConfig`. This can be used used to define defaults for the graph config file. | ||
|
||
|
||
```js | ||
... | ||
networks: { | ||
... | ||
}, | ||
graph: { | ||
addressBook: 'addresses.json' | ||
l1GraphConfig: 'config/graph.mainnet.yml' | ||
l2GraphConfig: 'config/graph.arbitrum-one.yml' | ||
} | ||
... | ||
``` | ||
|
||
Fields: | ||
- **(_OPTIONAL_) addressBook**: the path to the address book. | ||
- **(_REQUIRED_) l1GraphConfig**: default path to the graph config file for L1 networks. This will be used if the `graphConfig` field is not defined on the network config. | ||
- **(_REQUIRED_) l2GraphConfig**: default path to the graph config file for L2 networks. This will be used if the `graphConfig` field is not defined on the network config. | ||
|
||
**Options: Graph initializer** | ||
|
||
The GRE initializer also allows you to set the address book and the graph config files like so: | ||
```js | ||
const graph = hre.graph({ | ||
addressBook: 'addresses.json', | ||
l1GraphConfig: 'config/graph.mainnet.yml' | ||
l2GraphConfig: 'config/graph.arbitrum-one.yml' | ||
}) | ||
|
||
// Here graphConfig will apply only to the "main" network given by --network | ||
const graph = hre.graph({ | ||
addressBook: 'addresses.json', | ||
graphConfig: 'config/graph.mainnet.yml' | ||
}) | ||
``` | ||
|
||
**Config priority** | ||
|
||
The path to the graph config and the address book can be set in multiple ways. The plugin will use the following order to determine the path to the graph config file: | ||
|
||
1) `hre.graph({ ... })` init parameters `l1GraphConfigPath` and `l2GraphConfigPath` | ||
2) `hre.graph({ ...})` init parameter graphConfigPath (but only for the "main" network) | ||
3) `networks.<NETWORK_NAME>.graphConfig` network config parameter `graphConfig` in hardhat config file | ||
4) `graph.l<X>GraphConfig` graph config parameters `l1GraphConfig` and `l2GraphConfig` in hardhat config file | ||
|
||
The priority for the address book is: | ||
1) `hre.graph({ ... })` init parameter `addressBook` | ||
2) `graph.addressBook` graph config parameter `addressBook` in hardhat config file | ||
|
||
### API | ||
|
||
GRE exposes functionality via a simple API: | ||
|
||
```js | ||
const graph = hre.graph() | ||
|
||
// To access the L1 object | ||
graph.l1 | ||
|
||
// To access the L2 object | ||
graph.l2 | ||
``` | ||
|
||
The interface for both `l1` and `l2` objects looks like this: | ||
|
||
```ts | ||
export interface GraphNetworkEnvironment { | ||
chainId: number | ||
contracts: NetworkContracts | ||
graphConfig: any | ||
addressBook: AddressBook | ||
getNamedAccounts: () => Promise<NamedAccounts> | ||
getTestAccounts: () => Promise<SignerWithAddress[]> | ||
getDeployer: () => Promise<SignerWithAddress> | ||
} | ||
``` | ||
|
||
**ChainId** | ||
|
||
The chainId of the network. | ||
|
||
**Contracts** | ||
|
||
Returns an object with all the contracts available in the network. Connects using a provider created with the URL specified in hardhat's network configuration (it doesn't use the usual hardhat `hre.ethers.provider`). | ||
|
||
```js | ||
> const graph = hre.graph() | ||
|
||
// Print curation default reserve ratio on L1 | ||
> await g.l1.contracts.Curation.defaultReserveRatio() | ||
500000 | ||
``` | ||
|
||
**Graph Config** | ||
|
||
Returns an object that grants raw access to the graph config file for the protocol. The graph config file is a YAML file that contains all the parameters with which the protocol was deployed. | ||
|
||
> TODO: add better APIs to interact with the graph config file. | ||
|
||
**Address Book** | ||
|
||
Returns an object that allows interacting with the address book. | ||
|
||
```js | ||
> const graph = hre.graph() | ||
> graph.l1.addressBook.getEntry('Curation') | ||
{ | ||
address: '0xE59B4820dDE28D2c235Bd9A73aA4e8716Cb93E9B', | ||
initArgs: [ | ||
'0x48eD7AfbaB432d1Fc6Ea84EEC70E745d9DAcaF3B', | ||
'0x2DFDC3e11E035dD96A4aB30Ef67fab4Fb6EC01f2', | ||
'0x8bEd0a89F18a801Da9dEA994D475DEa74f75A059', | ||
'500000', | ||
'10000', | ||
'1000000000000000000' | ||
], | ||
creationCodeHash: '0x25a7b6cafcebb062169bc25fca9bcce8f23bd7411235859229ae3cc99b9a7d58', | ||
runtimeCodeHash: '0xaf2d63813a0e5059f63ec46e1b280eb9d129d5ad548f0cdd1649d9798fde10b6', | ||
txHash: '0xf1b1f0f28b80068bcc9fd6ef475be6324a8b23cbdb792f7344f05ce00aa997d7', | ||
proxy: true, | ||
implementation: { | ||
address: '0xAeaA2B058539750b740E858f97159E6856948670', | ||
creationCodeHash: '0x022576ab4b739ee17dab126ea7e5a6814bda724aa0e4c6735a051b38a76bd597', | ||
runtimeCodeHash: '0xc7b1f9bef01ef92779aab0ae9be86376c47584118c508f5b4e612a694a4aab93', | ||
txHash: '0x400bfb7b6c384363b859a66930590507ddca08ebedf64b20c4b5f6bc8e76e125' | ||
} | ||
} | ||
``` | ||
|
||
**Account management: getNamedAccounts** | ||
Returns an object with all the named accounts available in the network. Named accounts are accounts that have special roles in the protocol, they are defined in the graph config file. | ||
|
||
```js | ||
> const graph = hre.graph() | ||
> const namedAccounts = await g.l1.getNamedAccounts() | ||
> namedAccounts.governor.address | ||
'0xf1135bFF22512FF2A585b8d4489426CE660f204c' | ||
``` | ||
|
||
The accounts are initialized from the graph config file but if the correct mnemonic or private key is provided via hardhat network configuration then they will be fully capable of signing transactions. | ||
|
||
**Account management: getTestAccounts** | ||
Returns an object with accounts which can be used for testing/interacting with the protocol. These are obtained from hardhat's network configuration using the provided mnemonic or private key. | ||
|
||
**Account management: getDeployer** | ||
Returns an object with the would-be deployer account. The deployer is by convention the first (index 0) account derived from the mnemonic or private key provided via hardhat network configuration. | ||
|
||
It's important to note that the deployer is not a named account as it's derived from the provided mnemonic so it won't necessarily match the actual deployer for a given deployment. It's the account that would be used to deploy the protocol with the current configuration. It's not possible at the moment to recover the actual deployer account from a deployed protocol. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { EthersProviderWrapper } from '@nomiclabs/hardhat-ethers/internal/ethers-provider-wrapper' | ||
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' | ||
import { getItemValue, readConfig } from '../cli/config' | ||
import { AccountNames, NamedAccounts } from './type-extensions' | ||
|
||
const namedAccountList: AccountNames[] = [ | ||
'arbitrator', | ||
'governor', | ||
'authority', | ||
'availabilityOracle', | ||
'pauseGuardian', | ||
'allocationExchangeOwner', | ||
] | ||
|
||
export async function getNamedAccounts( | ||
provider: EthersProviderWrapper, | ||
graphConfigPath: string, | ||
): Promise<NamedAccounts> { | ||
const namedAccounts = namedAccountList.reduce(async (accountsPromise, name) => { | ||
const accounts = await accountsPromise | ||
const address = getItemValue(readConfig(graphConfigPath, true), `general/${name}`) | ||
accounts[name] = await SignerWithAddress.create(provider.getSigner(address)) | ||
return accounts | ||
}, Promise.resolve({} as NamedAccounts)) | ||
|
||
return namedAccounts | ||
} | ||
|
||
export async function getDeployer(provider: EthersProviderWrapper): Promise<SignerWithAddress> { | ||
const signer = provider.getSigner(0) | ||
return SignerWithAddress.create(signer) | ||
} | ||
|
||
export async function getTestAccounts( | ||
provider: EthersProviderWrapper, | ||
graphConfigPath: string, | ||
): Promise<SignerWithAddress[]> { | ||
// Get list of privileged accounts we don't want as test accounts | ||
const namedAccounts = await getNamedAccounts(provider, graphConfigPath) | ||
const blacklist = namedAccountList.map((a) => { | ||
const account = namedAccounts[a] | ||
return account.address | ||
}) | ||
blacklist.push((await getDeployer(provider)).address) | ||
|
||
// Get signers and filter out blacklisted accounts | ||
const accounts = await provider.listAccounts() | ||
const signers = await Promise.all( | ||
accounts.map(async (account) => await SignerWithAddress.create(provider.getSigner(account))), | ||
) | ||
|
||
return signers.filter((s) => { | ||
return !blacklist.includes(s.address) | ||
}) | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
What is this? :)
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.
I knew you would ask ;)
The plugin tests run with mocha (and not with hh test), this is needed for mocha to support typescript files.
On a more general note, I based the GRE folder on hardhat's plugin boilerplate https://github.com/NomicFoundation/hardhat-ts-plugin-boilerplate/, once GRE is mature I think it makes sense to fork it into it's own repository so each repo is nice and tidy and not polluted with too much stuff.