Skip to content

Commit 0352cdc

Browse files
authored
feat(gre): several improvements (#917)
* feat(gre): allow configuring address book via hardhat network config Signed-off-by: Tomás Migone <[email protected]> * feat(gre): fix tx logging and enable by default Signed-off-by: Tomás Migone <[email protected]> * fix: remove unneeded console logs Signed-off-by: Tomás Migone <[email protected]>
1 parent 510409a commit 0352cdc

File tree

13 files changed

+148
-97
lines changed

13 files changed

+148
-97
lines changed

packages/contracts/hardhat.config.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ const config: HardhatUserConfig = {
162162
url: 'http://127.0.0.1:8545',
163163
accounts:
164164
process.env.FORK === 'true' ? getAccountsKeys() : { mnemonic: DEFAULT_TEST_MNEMONIC },
165+
graphConfig: 'config/graph.localhost.yml',
166+
addressBook: 'addresses-local.json',
165167
},
166168
localnitrol1: {
167169
chainId: 1337,
@@ -178,8 +180,8 @@ const config: HardhatUserConfig = {
178180
},
179181
graph: {
180182
addressBook: process.env.ADDRESS_BOOK ?? 'addresses.json',
181-
l1GraphConfig: process.env.L1_GRAPH_CONFIG ?? 'config/graph.localhost.yml',
182-
l2GraphConfig: process.env.L2_GRAPH_CONFIG ?? 'config/graph.arbitrum-localhost.yml',
183+
l1GraphConfig: process.env.L1_GRAPH_CONFIG ?? 'config/graph.mainnet.yml',
184+
l2GraphConfig: process.env.L2_GRAPH_CONFIG ?? 'config/graph.arbitrum-one.yml',
183185
fork: process.env.FORK === 'true',
184186
disableSecureAccounts: process.env.DISABLE_SECURE_ACCOUNTS === 'true',
185187
},

packages/contracts/package.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,8 @@
8989
"build": "SKIP_LOAD=true scripts/build",
9090
"clean": "rm -rf build/ cache/ dist/",
9191
"compile": "hardhat compile",
92-
"deploy": "yarn predeploy && yarn build && yarn predeploy && hardhat migrate",
93-
"deploy-localhost": "yarn deploy --force --network localhost --graph-config config/graph.localhost.yml",
94-
"deploy-goerli": "yarn deploy --force --network goerli --graph-config config/graph.goerli.yml",
95-
"deploy-arbitrum-goerli": "yarn deploy --force --network arbitrum-goerli --graph-config config/graph.arbitrum-goerli.yml",
92+
"deploy": "yarn predeploy && yarn build && hardhat migrate",
93+
"deploy-localhost": "yarn build && hardhat migrate --force --skip-confirmation --disable-secure-accounts --network localhost --graph-config config/graph.localhost.yml --address-book addresses-local.json",
9694
"predeploy": "scripts/predeploy",
9795
"test": "scripts/test",
9896
"test:e2e": "scripts/e2e",
Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
1-
import {
2-
GraphChainId,
3-
GraphNetworkGovernedContractNameList,
4-
acceptOwnership,
5-
deployGraphNetwork,
6-
setPausedProtocol,
7-
} from '@graphprotocol/sdk'
1+
import { GraphChainId, deployGraphNetwork } from '@graphprotocol/sdk'
82
import { greTask } from '@graphprotocol/sdk/gre'
9-
import { ContractTransaction } from 'ethers'
103

114
greTask('migrate', 'Deploy protocol contracts')
125
.addFlag('skipConfirmation', 'Skip confirmation prompt on write actions')
@@ -29,20 +22,4 @@ greTask('migrate', 'Deploy protocol contracts')
2922
buildAcceptTx: taskArgs.buildAcceptTx,
3023
},
3124
)
32-
33-
if (!taskArgs.skipPostDeploy) {
34-
// Governor accepts ownership of contracts
35-
const governor = (await graph.getNamedAccounts()).governor
36-
const txs: ContractTransaction[] = []
37-
for (const contract of GraphNetworkGovernedContractNameList) {
38-
const tx = await acceptOwnership(graph.contracts, governor, { contractName: contract })
39-
if (tx) {
40-
txs.push()
41-
}
42-
}
43-
await Promise.all(txs.map((tx) => tx.wait()))
44-
45-
// Governor unpauses the protocol
46-
await setPausedProtocol(graph.contracts, governor, { paused: false })
47-
}
4825
})

packages/sdk/src/deployments/lib/contracts/load.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Contract, Signer, providers } from 'ethers'
2-
import { getWrappedConnect, wrapCalls } from './tx-log'
32
import { AddressBook } from '../address-book'
43
import { loadArtifact } from '../deploy/artifacts'
54

65
import type { ContractList } from '../types/contract'
6+
import { getWrappedConnect, wrapCalls } from './wrap'
77

88
/**
99
* Loads a contract instance for a given contract name and address
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import fs from 'fs'
2+
3+
import type { ContractReceipt, ContractTransaction } from 'ethers'
4+
import type { ContractParam } from '../types/contract'
5+
import { logInfo } from '../../logger'
6+
7+
export function logContractCall(
8+
tx: ContractTransaction,
9+
contractName: string,
10+
fn: string,
11+
args: Array<ContractParam>,
12+
) {
13+
const msg: string[] = []
14+
msg.push(`> Sending transaction: ${contractName}.${fn}`)
15+
msg.push(` = Sender: ${tx.from}`)
16+
msg.push(` = Contract: ${tx.to}`)
17+
msg.push(` = Params: [ ${args} ]`)
18+
msg.push(` = TxHash: ${tx.hash}`)
19+
20+
logToConsoleAndFile(msg)
21+
}
22+
23+
export function logContractDeploy(
24+
tx: ContractTransaction,
25+
contractName: string,
26+
args: Array<ContractParam>,
27+
) {
28+
const msg: string[] = []
29+
msg.push(`> Deploying contract: ${contractName}`)
30+
msg.push(` = Sender: ${tx.from}`)
31+
msg.push(` = Params: [ ${args} ]`)
32+
msg.push(` = TxHash: ${tx.hash}`)
33+
logToConsoleAndFile(msg)
34+
}
35+
36+
export function logContractDeployReceipt(
37+
receipt: ContractReceipt,
38+
creationCodeHash: string,
39+
runtimeCodeHash: string,
40+
) {
41+
const msg: string[] = []
42+
msg.push(` = CreationCodeHash: ${creationCodeHash}`)
43+
msg.push(` = RuntimeCodeHash: ${runtimeCodeHash}`)
44+
logToConsoleAndFile(msg)
45+
logContractReceipt(receipt)
46+
}
47+
48+
export function logContractReceipt(receipt: ContractReceipt) {
49+
const msg: string[] = []
50+
msg.push(receipt.status ? ` ✔ Transaction succeeded!` : ` ✖ Transaction failed!`)
51+
logToConsoleAndFile(msg)
52+
}
53+
54+
export function logToConsoleAndFile(msg: string[]) {
55+
const isoDate = new Date().toISOString()
56+
const fileName = `tx-${isoDate.substring(0, 10)}.log`
57+
58+
msg.map((line) => {
59+
logInfo(line)
60+
fs.appendFileSync(fileName, `[${isoDate}] ${line}\n`)
61+
})
62+
}
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
1-
import fs from 'fs'
21
import lodash from 'lodash'
32

4-
import type {
5-
Contract,
6-
ContractFunction,
7-
ContractReceipt,
8-
ContractTransaction,
9-
Signer,
10-
} from 'ethers'
3+
import type { Contract, ContractFunction, ContractTransaction, Signer } from 'ethers'
114
import type { Provider } from '@ethersproject/providers'
125
import type { ContractParam } from '../types/contract'
6+
import { logContractCall, logContractReceipt } from './log'
137

148
class WrappedContract {
159
// The meta-class properties
1610
[key: string]: ContractFunction | any
1711
}
1812

13+
function isContractTransaction(call: ContractTransaction | any): call is ContractTransaction {
14+
return typeof call === 'object' && (call as ContractTransaction).hash !== undefined
15+
}
16+
1917
/**
2018
* Modifies a contract connect function to return a contract wrapped with {@link wrapCalls}
2119
*
@@ -53,16 +51,24 @@ export function wrapCalls(contract: Contract, contractName: string): Contract {
5351
const wrappedContract = lodash.cloneDeep(contract) as WrappedContract
5452

5553
for (const fn of Object.keys(contract.functions)) {
56-
const call: ContractFunction<ContractTransaction> = contract.functions[fn]
57-
const override = async (...args: Array<ContractParam>): Promise<ContractTransaction> => {
58-
// Make the call
59-
const tx = await call(...args)
60-
logContractCall(tx, contractName, fn, args)
54+
const call = contract.functions[fn]
55+
const override = async (...args: Array<ContractParam>): Promise<ContractTransaction | any> => {
56+
const response = await call(...args)
57+
58+
// If it's a read only call, return the response
59+
if (!isContractTransaction(response)) {
60+
return Array.isArray(response) && response.length === 1 ? response[0] : response
61+
}
62+
63+
// Otherwise it's a tx, log the details
64+
logContractCall(response, contractName, fn, args)
6165

62-
// Wait for confirmation
63-
const receipt = await contract.provider.waitForTransaction(tx.hash)
64-
logContractReceipt(tx, receipt)
65-
return tx
66+
// And wait for confirmation
67+
const receipt = await contract.provider.waitForTransaction(response.hash)
68+
logContractReceipt(receipt)
69+
70+
// Finally return the tx response
71+
return response
6672
}
6773

6874
wrappedContract[fn] = override
@@ -71,38 +77,3 @@ export function wrapCalls(contract: Contract, contractName: string): Contract {
7177

7278
return wrappedContract as Contract
7379
}
74-
75-
function logContractCall(
76-
tx: ContractTransaction,
77-
contractName: string,
78-
fn: string,
79-
args: Array<ContractParam>,
80-
) {
81-
const msg: string[] = []
82-
msg.push(`> Sent transaction ${contractName}.${fn}`)
83-
msg.push(` sender: ${tx.from}`)
84-
msg.push(` contract: ${tx.to}`)
85-
msg.push(` params: [ ${args} ]`)
86-
msg.push(` txHash: ${tx.hash}`)
87-
88-
logToConsoleAndFile(msg)
89-
}
90-
91-
function logContractReceipt(tx: ContractTransaction, receipt: ContractReceipt) {
92-
const msg: string[] = []
93-
msg.push(
94-
receipt.status ? `✔ Transaction succeeded: ${tx.hash}` : `✖ Transaction failed: ${tx.hash}`,
95-
)
96-
97-
logToConsoleAndFile(msg)
98-
}
99-
100-
function logToConsoleAndFile(msg: string[]) {
101-
const isoDate = new Date().toISOString()
102-
const fileName = `tx-${isoDate.substring(0, 10)}.log`
103-
104-
msg.map((line) => {
105-
console.log(line)
106-
fs.appendFileSync(fileName, `[${isoDate}] ${line}\n`)
107-
})
108-
}

packages/sdk/src/deployments/lib/deploy/contract.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
DeployFunction,
1111
DeployAddressBookFunction,
1212
} from '../types/deploy'
13+
import { logContractDeploy, logContractDeployReceipt } from '../contracts/log'
1314

1415
/**
1516
* Deploys a contract
@@ -59,15 +60,13 @@ export const deployContract: DeployFunction = async (
5960
const factory = getContractFactory(name, libraries)
6061
const contract = await factory.connect(sender).deploy(...args)
6162
const txHash = contract.deployTransaction.hash
62-
logInfo(`> Deploy ${name}, txHash: ${txHash}`)
63-
await sender.provider.waitForTransaction(txHash)
63+
logContractDeploy(contract.deployTransaction, name, args)
64+
const receipt = await sender.provider.waitForTransaction(txHash)
6465

6566
// Receipt
6667
const creationCodeHash = hashHexString(factory.bytecode)
6768
const runtimeCodeHash = hashHexString(await sender.provider.getCode(contract.address))
68-
logInfo(`= CreationCodeHash: ${creationCodeHash}`)
69-
logInfo(`= RuntimeCodeHash: ${runtimeCodeHash}`)
70-
logInfo(`${name} has been deployed to address: ${contract.address}`)
69+
logContractDeployReceipt(receipt, creationCodeHash, runtimeCodeHash)
7170

7271
return { contract, creationCodeHash, runtimeCodeHash, txHash, libraries }
7372
}

packages/sdk/src/deployments/network/deployment/contracts/deploy.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { acceptOwnership } from '../../actions/governed'
2929
import { setPausedProtocol } from '../../actions/pause'
3030
import { GraphNetworkContracts, loadGraphNetworkContracts } from './load'
3131
import { setCode } from '../../../../helpers/code'
32+
import { logContractCall, logContractReceipt } from '../../../lib/contracts/log'
3233

3334
export async function deployGraphNetwork(
3435
addressBookPath: string,
@@ -42,6 +43,7 @@ export async function deployGraphNetwork(
4243
forceDeploy?: boolean
4344
buildAcceptTx?: boolean
4445
l2Deploy?: boolean
46+
enableTxLogging?: boolean
4547
},
4648
): Promise<GraphNetworkContracts | undefined> {
4749
// Opts
@@ -50,6 +52,7 @@ export async function deployGraphNetwork(
5052
const forceDeploy = opts?.forceDeploy ?? false
5153
const buildAcceptTx = opts?.buildAcceptTx ?? false
5254
const l2Deploy = opts?.l2Deploy ?? false
55+
const enableTxLogging = opts?.enableTxLogging ?? true
5356

5457
// Snapshot deployer
5558
const beforeDeployerNonce = await deployer.getTransactionCount()
@@ -151,7 +154,12 @@ export async function deployGraphNetwork(
151154
const params = loadCallParams(call.params, addressBook, deployer.address)
152155
logDebug(`- Params: ${params.join(', ')}`)
153156
const overrides = process.env.CI ? { gasLimit: 2_000_000 } : {}
154-
await entry.contract.contract.connect(deployer).functions[call.fn](...params, overrides)
157+
const response: ContractTransaction = await entry.contract.contract
158+
.connect(deployer)
159+
.functions[call.fn](...params, overrides)
160+
logContractCall(response, entry.name, call.fn, params)
161+
const receipt = await entry.contract.contract.provider.waitForTransaction(response.hash)
162+
logContractReceipt(receipt)
155163
} catch (error) {
156164
// TODO: can we clean this up?
157165
// Fallback for StakingExtension methods
@@ -184,6 +192,7 @@ export async function deployGraphNetwork(
184192
////////////////////////////////////////
185193
const loadedContracts = loadGraphNetworkContracts(addressBookPath, chainId, provider, undefined, {
186194
l2Load: l2Deploy,
195+
enableTxLogging: enableTxLogging,
187196
})
188197

189198
////////////////////////////////////////

packages/sdk/src/deployments/network/deployment/contracts/load.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export function loadGraphNetworkContracts(
105105
opts?: {
106106
enableTxLogging?: boolean
107107
strictAssert?: boolean
108-
l2Load: boolean
108+
l2Load?: boolean
109109
},
110110
): GraphNetworkContracts {
111111
artifactsPath = artifactsPath ?? getArtifactsPath()
@@ -117,7 +117,7 @@ export function loadGraphNetworkContracts(
117117
addressBook,
118118
artifactsPath,
119119
signerOrProvider,
120-
opts?.enableTxLogging ?? false,
120+
opts?.enableTxLogging ?? true,
121121
GraphNetworkOptionalContractNameList as unknown as GraphNetworkContractName[], // This is ugly but safe
122122
)
123123

@@ -144,7 +144,7 @@ export function loadGraphNetworkContracts(
144144
addressBook,
145145
artifactsPath,
146146
signerOrProvider,
147-
opts?.enableTxLogging ?? false,
147+
opts?.enableTxLogging ?? true,
148148
new Contract(
149149
staking.address,
150150
mergeABIs(

packages/sdk/src/gre/README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ GRE is a hardhat plugin that extends hardhat's runtime environment to inject add
77
- Provides a simple interface to interact with protocol contracts
88
- Exposes protocol configuration via graph config file and address book
99
- Provides account management methods for convenience
10+
- Detailed logging of transactions to file
1011
- Multichain! Supports both L1 and L2 layers of the protocol simultaneously
1112
- Integrates seamlessly with [hardhat-secure-accounts](https://www.npmjs.com/package/hardhat-secure-accounts)
1213
- Convenience method to create tasks that use GRE
@@ -154,6 +155,7 @@ OPTIONS:
154155

155156
--address-book Path to the address book file.
156157
--disable-secure-accounts Disable secure accounts.
158+
--enable-tx-logging Enable transaction logging.
157159
--graph-config Path to the graph config file for the network specified using --network.
158160
--l1-graph-config Path to the graph config file for the L1 network.
159161
--l2-graph-config Path to the graph config file for the L2 network.
@@ -163,6 +165,27 @@ hello-world: Say hi!
163165
For global options help run: hardhat help
164166
```
165167

168+
### Transaction Logging
169+
170+
By default all transactions executed via GRE will be logged to a file. The file will be created on the first transaction with the following convention `tx-<yyyy-mm-dd>.log`. Here is a sample log file:
171+
172+
```
173+
[2024-01-15T14:33:26.747Z] > Sending transaction: GraphToken.addMinter
174+
[2024-01-15T14:33:26.747Z] = Sender: 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1
175+
[2024-01-15T14:33:26.747Z] = Contract: 0x428aAe4Fa354c21600b6ec0077F2a6855C7dcbC8
176+
[2024-01-15T14:33:26.747Z] = Params: [ 0x05eA50dc2C0389117A067D393e0395ACc32c53b6 ]
177+
[2024-01-15T14:33:26.747Z] = TxHash: 0xa9096e5f9f9a2208202ac3a8b895561dc3f781fa7e19350b0855098a08d193f7
178+
[2024-01-15T14:33:26.750Z] ✔ Transaction succeeded!
179+
[2024-01-15T14:33:26.777Z] > Sending transaction: GraphToken.renounceMinter
180+
[2024-01-15T14:33:26.777Z] = Sender: 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1
181+
[2024-01-15T14:33:26.777Z] = Contract: 0x428aAe4Fa354c21600b6ec0077F2a6855C7dcbC8
182+
[2024-01-15T14:33:26.777Z] = Params: [ ]
183+
[2024-01-15T14:33:26.777Z] = TxHash: 0x48233b256a1f98cb3fecc3dd48d7f7c0175042142e1ca7b9b1f9fc91169bb588
184+
[2024-01-15T14:33:26.780Z] ✔ Transaction succeeded!
185+
```
186+
187+
If you want to disable transaction logging you can do so by setting the `enableTxLogging` option to `false` when initializing GRE: ```g=graph({ disableSecureAccounts: true })```
188+
166189
## API
167190

168191
GRE exposes functionality via a simple API:

0 commit comments

Comments
 (0)