Skip to content

Commit 000810b

Browse files
authored
feat(gre): add wallet methods (#716)
Signed-off-by: Tomás Migone <[email protected]>
1 parent 5ab5aa2 commit 000810b

File tree

8 files changed

+167
-5
lines changed

8 files changed

+167
-5
lines changed

gre/README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,12 +212,18 @@ Returns an object with all the named accounts available in the network. Named ac
212212
'0xf1135bFF22512FF2A585b8d4489426CE660f204c'
213213
```
214214

215-
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.
215+
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. Accounts are already connected to the network provider.
216216

217217
**Account management: getTestAccounts**
218-
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.
218+
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. Accounts are already connected to the network provider.
219219

220220
**Account management: getDeployer**
221-
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.
221+
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. Deployer account is already connected to the network provider.
222222

223-
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.
223+
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.
224+
225+
**Account management: getWallets**
226+
Returns an object with wallets derived from the mnemonic or private key provided via hardhat network configuration. These wallets are not connected to a provider.
227+
228+
**Account management: getWallet**
229+
Returns a wallet derived from the mnemonic or private key provided via hardhat network configuration that matches a given address. This wallet is not connected to a provider.

gre/accounts.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { EthersProviderWrapper } from '@nomiclabs/hardhat-ethers/internal/ethers-provider-wrapper'
22
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
3+
import { derivePrivateKeys } from 'hardhat/internal/core/providers/util'
4+
import { Wallet } from 'ethers'
35
import { getItemValue, readConfig } from '../cli/config'
46
import { AccountNames, NamedAccounts } from './type-extensions'
7+
import { getNetworkName } from './config'
8+
import { HttpNetworkHDAccountsConfig, NetworksConfig } from 'hardhat/types'
59

610
const namedAccountList: AccountNames[] = [
711
'arbitrator',
@@ -53,3 +57,30 @@ export async function getTestAccounts(
5357
return !blacklist.includes(s.address)
5458
})
5559
}
60+
61+
export async function getWallets(
62+
networks: NetworksConfig,
63+
chainId: number,
64+
mainNetworkName: string,
65+
): Promise<Wallet[]> {
66+
const networkName = getNetworkName(networks, chainId, mainNetworkName)
67+
const accounts = networks[networkName].accounts
68+
const mnemonic = (accounts as HttpNetworkHDAccountsConfig).mnemonic
69+
70+
if (mnemonic) {
71+
const privateKeys = derivePrivateKeys(mnemonic, "m/44'/60'/0'/0/", 0, 20, '')
72+
return privateKeys.map((privateKey) => new Wallet(privateKey))
73+
}
74+
75+
return []
76+
}
77+
78+
export async function getWallet(
79+
networks: NetworksConfig,
80+
chainId: number,
81+
mainNetworkName: string,
82+
address: string,
83+
): Promise<Wallet | undefined> {
84+
const wallets = await getWallets(networks, chainId, mainNetworkName)
85+
return wallets.find((w) => w.address === address)
86+
}

gre/gre.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ import {
1111
GraphRuntimeEnvironmentOptions,
1212
} from './type-extensions'
1313
import { getChains, getProviders, getAddressBookPath, getGraphConfigPaths } from './config'
14-
import { getDeployer, getNamedAccounts, getTestAccounts } from './accounts'
14+
import { getDeployer, getNamedAccounts, getTestAccounts, getWallet, getWallets } from './accounts'
1515
import { logDebug, logWarn } from './logger'
1616
import path from 'path'
1717
import { EthersProviderWrapper } from '@nomiclabs/hardhat-ethers/internal/ethers-provider-wrapper'
18+
import { Wallet } from 'ethers'
1819

1920
// Graph Runtime Environment (GRE) extensions for the HRE
2021

@@ -53,12 +54,23 @@ extendEnvironment((hre: HardhatRuntimeEnvironment) => {
5354
isHHL1,
5455
)
5556

57+
// Wallet functions
58+
const l1GetWallets = () => getWallets(hre.config.networks, l1ChainId, hre.network.name)
59+
const l1GetWallet = (address: string) =>
60+
getWallet(hre.config.networks, l1ChainId, hre.network.name, address)
61+
const l2GetWallets = () => getWallets(hre.config.networks, l2ChainId, hre.network.name)
62+
const l2GetWallet = (address: string) =>
63+
getWallet(hre.config.networks, l2ChainId, hre.network.name, address)
64+
65+
// Build the Graph Runtime Environment (GRE)
5666
const l1Graph: GraphNetworkEnvironment | null = buildGraphNetworkEnvironment(
5767
l1ChainId,
5868
l1Provider,
5969
l1GraphConfigPath,
6070
addressBookPath,
6171
isHHL1,
72+
l1GetWallets,
73+
l1GetWallet,
6274
)
6375

6476
const l2Graph: GraphNetworkEnvironment | null = buildGraphNetworkEnvironment(
@@ -67,6 +79,8 @@ extendEnvironment((hre: HardhatRuntimeEnvironment) => {
6779
l2GraphConfigPath,
6880
addressBookPath,
6981
isHHL1,
82+
l2GetWallets,
83+
l2GetWallet,
7084
)
7185

7286
const gre: GraphRuntimeEnvironment = {
@@ -88,6 +102,8 @@ function buildGraphNetworkEnvironment(
88102
graphConfigPath: string | undefined,
89103
addressBookPath: string,
90104
isHHL1: boolean,
105+
getWallets: () => Promise<Wallet[]>,
106+
getWallet: (address: string) => Promise<Wallet>,
91107
): GraphNetworkEnvironment | null {
92108
if (graphConfigPath === undefined) {
93109
logWarn(
@@ -116,5 +132,7 @@ function buildGraphNetworkEnvironment(
116132
getDeployer: lazyFunction(() => () => getDeployer(provider)),
117133
getNamedAccounts: lazyFunction(() => () => getNamedAccounts(provider, graphConfigPath)),
118134
getTestAccounts: lazyFunction(() => () => getTestAccounts(provider, graphConfigPath)),
135+
getWallets: lazyFunction(() => () => getWallets()),
136+
getWallet: lazyFunction(() => (address: string) => getWallet(address)),
119137
}
120138
}

gre/test/accounts.test.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import chai, { expect } from 'chai'
2+
import chaiAsPromised from 'chai-as-promised'
3+
import { ethers } from 'ethers'
4+
import { GraphRuntimeEnvironment } from '../type-extensions'
5+
import { useEnvironment } from './helpers'
6+
7+
chai.use(chaiAsPromised)
8+
9+
const mnemonic = 'pumpkin orient can short never warm truth legend cereal tourist craft skin'
10+
11+
describe('GRE usage > account management', function () {
12+
// Tests that loop through all the wallets take more than the default timeout
13+
this.timeout(10_000)
14+
15+
useEnvironment('graph-config', 'hardhat')
16+
17+
let graph: GraphRuntimeEnvironment
18+
19+
beforeEach(function () {
20+
graph = this.hre.graph()
21+
})
22+
23+
describe('getWallets', function () {
24+
it('should return 20 wallets', async function () {
25+
const wallets = await graph.getWallets()
26+
expect(wallets.length).to.equal(20)
27+
})
28+
29+
it('should derive wallets from hardhat config mnemonic', async function () {
30+
const wallets = await graph.getWallets()
31+
32+
for (let i = 0; i < wallets.length; i++) {
33+
const derived = ethers.Wallet.fromMnemonic(mnemonic, `m/44'/60'/0'/0/${i}`)
34+
expect(wallets[i].address).to.equal(derived.address)
35+
}
36+
})
37+
38+
it('should return wallets capable of signing messages', async function () {
39+
const wallets = await graph.getWallets()
40+
41+
for (const wallet of wallets) {
42+
expect(wallet.signMessage('test')).to.eventually.be.fulfilled
43+
}
44+
})
45+
46+
it('should return wallets not connected to a provider', async function () {
47+
const wallets = await graph.getWallets()
48+
49+
for (const wallet of wallets) {
50+
expect(wallet.provider).to.be.null
51+
}
52+
})
53+
})
54+
55+
describe('getWallet', function () {
56+
it('should return wallet if provided address can be derived from mnemonic', async function () {
57+
for (let i = 0; i < 20; i++) {
58+
const derived = ethers.Wallet.fromMnemonic(mnemonic, `m/44'/60'/0'/0/${i}`)
59+
const wallet = await graph.getWallet(derived.address)
60+
expect(wallet.address).to.equal(derived.address)
61+
}
62+
})
63+
64+
it('should return wallet capable of signing messages', async function () {
65+
for (let i = 0; i < 20; i++) {
66+
const derived = ethers.Wallet.fromMnemonic(mnemonic, `m/44'/60'/0'/0/${i}`)
67+
const wallet = await graph.getWallet(derived.address)
68+
expect(wallet.signMessage('test')).to.eventually.be.fulfilled
69+
}
70+
})
71+
72+
it('should return wallet not connected to a provider', async function () {
73+
for (let i = 0; i < 20; i++) {
74+
const derived = ethers.Wallet.fromMnemonic(mnemonic, `m/44'/60'/0'/0/${i}`)
75+
const wallet = await graph.getWallet(derived.address)
76+
expect(wallet.provider).to.be.null
77+
}
78+
})
79+
80+
it('should return undefined if provided address cant be derived from mnemonic', async function () {
81+
const wallet = await graph.getWallet('0x0000000000000000000000000000000000000000')
82+
expect(wallet).to.be.undefined
83+
})
84+
})
85+
})

gre/test/fixture-projects/graph-config/hardhat.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ module.exports = {
99
networks: {
1010
hardhat: {
1111
chainId: 1337,
12+
accounts: {
13+
mnemonic: 'pumpkin orient can short never warm truth legend cereal tourist craft skin',
14+
},
1215
},
1316
mainnet: {
1417
chainId: 1,

gre/type-extensions.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { AddressBook } from '../cli/address-book'
33
import { NetworkContracts } from '../cli/contracts'
44

55
import { EthersProviderWrapper } from '@nomiclabs/hardhat-ethers/internal/ethers-provider-wrapper'
6+
import { Wallet } from 'ethers'
67

78
export interface GraphRuntimeEnvironmentOptions {
89
addressBook?: string
@@ -32,6 +33,8 @@ export interface GraphNetworkEnvironment {
3233
getNamedAccounts: () => Promise<NamedAccounts>
3334
getTestAccounts: () => Promise<SignerWithAddress[]>
3435
getDeployer: () => Promise<SignerWithAddress>
36+
getWallets: () => Promise<Wallet[]>
37+
getWallet: (address: string) => Promise<Wallet>
3538
}
3639

3740
export interface GraphRuntimeEnvironment extends GraphNetworkEnvironment {

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"@typechain/ethers-v5": "^7.0.0",
3030
"@typechain/hardhat": "^2.0.0",
3131
"@types/bs58": "^4.0.1",
32+
"@types/chai-as-promised": "^7.1.5",
3233
"@types/dotenv": "^8.2.0",
3334
"@types/glob": "^7.2.0",
3435
"@types/inquirer": "^7.3.1",
@@ -42,6 +43,7 @@
4243
"@urql/core": "^2.1.3",
4344
"bignumber.js": "^9.0.0",
4445
"chai": "^4.3.4",
46+
"chai-as-promised": "^7.1.1",
4547
"cli-table": "^0.3.6",
4648
"dotenv": "^9.0.0",
4749
"eslint": "^7.24.0",

yarn.lock

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1477,6 +1477,13 @@
14771477
dependencies:
14781478
base-x "^3.0.6"
14791479

1480+
"@types/chai-as-promised@^7.1.5":
1481+
version "7.1.5"
1482+
resolved "https://registry.yarnpkg.com/@types/chai-as-promised/-/chai-as-promised-7.1.5.tgz#6e016811f6c7a64f2eed823191c3a6955094e255"
1483+
integrity sha512-jStwss93SITGBwt/niYrkf2C+/1KTeZCZl1LaeezTlqppAKeoQC7jxyqYuP72sxBGKCIbw7oHgbYssIRzT5FCQ==
1484+
dependencies:
1485+
"@types/chai" "*"
1486+
14801487
"@types/chai@*":
14811488
version "4.3.1"
14821489
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.1.tgz#e2c6e73e0bdeb2521d00756d099218e9f5d90a04"
@@ -3320,6 +3327,13 @@ cbor@^8.0.0:
33203327
dependencies:
33213328
nofilter "^3.1.0"
33223329

3330+
chai-as-promised@^7.1.1:
3331+
version "7.1.1"
3332+
resolved "https://registry.yarnpkg.com/chai-as-promised/-/chai-as-promised-7.1.1.tgz#08645d825deb8696ee61725dbf590c012eb00ca0"
3333+
integrity sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==
3334+
dependencies:
3335+
check-error "^1.0.2"
3336+
33233337
chai@^4.3.4:
33243338
version "4.3.6"
33253339
resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.6.tgz#ffe4ba2d9fa9d6680cc0b370adae709ec9011e9c"

0 commit comments

Comments
 (0)