1
- import { providers , utils , Contract , ContractFactory , ContractTransaction , Wallet } from 'ethers'
1
+ import { LinkReferences } from '@nomiclabs/buidler/types'
2
+
3
+ import { providers , utils , Contract , ContractFactory , ContractTransaction , Signer } from 'ethers'
2
4
import consola from 'consola'
3
5
4
6
import { AddressBook } from './address-book'
@@ -11,6 +13,14 @@ const logger = consola.create({})
11
13
12
14
const hash = ( input : string ) : string => keccak256 ( `0x${ input . replace ( / ^ 0 x / , '' ) } ` )
13
15
16
+ type DeployResult = {
17
+ contract : Contract
18
+ creationCodeHash : string
19
+ runtimeCodeHash : string
20
+ txHash : string
21
+ libraries ?: { [ libraryName : string ] : string }
22
+ }
23
+
14
24
// Simple sanity checks to make sure contracts from our address book have been deployed
15
25
export const isContractDeployed = async (
16
26
name : string ,
@@ -55,7 +65,7 @@ export const isContractDeployed = async (
55
65
}
56
66
57
67
export const sendTransaction = async (
58
- wallet : Wallet ,
68
+ sender : Signer ,
59
69
contract : Contract ,
60
70
fn : string ,
61
71
...params
@@ -79,8 +89,8 @@ export const sendTransaction = async (
79
89
}
80
90
logger . log ( `> Sent transaction ${ fn } : ${ params } , txHash: ${ tx . hash } ` )
81
91
// Wait for transaction to be mined
82
- const receipt = await wallet . provider . waitForTransaction ( tx . hash )
83
- const networkName = ( await wallet . provider . getNetwork ( ) ) . name
92
+ const receipt = await sender . provider . waitForTransaction ( tx . hash )
93
+ const networkName = ( await sender . provider . getNetwork ( ) ) . name
84
94
if ( networkName === 'kovan' || networkName === 'rinkeby' ) {
85
95
logger . success ( `Transaction mined 'https://${ networkName } .etherscan.io/tx/${ tx . hash } '` )
86
96
} else {
@@ -89,73 +99,109 @@ export const sendTransaction = async (
89
99
return receipt
90
100
}
91
101
92
- export const getContractFactory = ( name : string ) : ContractFactory => {
102
+ export const getContractFactory = (
103
+ name : string ,
104
+ libraries ?: { [ libraryName : string ] : string } ,
105
+ ) : ContractFactory => {
93
106
const artifact = loadArtifact ( name )
94
- return ContractFactory . fromSolidity ( artifact )
107
+ // Fixup libraries
108
+ if ( libraries && Object . keys ( libraries ) . length > 0 ) {
109
+ artifact . bytecode = linkLibraries ( artifact , libraries )
110
+ }
111
+ return new ContractFactory ( artifact . abi , artifact . bytecode )
95
112
}
96
113
97
114
export const getContractAt = ( name : string , address : string ) : Contract => {
98
- return getContractFactory ( name ) . attach ( address )
115
+ const artifact = loadArtifact ( name )
116
+ return new Contract ( address , artifact . abi )
99
117
}
100
118
101
119
export const deployContract = async (
102
120
name : string ,
103
121
args : Array < string > ,
104
- wallet : Wallet ,
105
- ) : Promise < Contract > => {
106
- const factory = getContractFactory ( name )
122
+ sender : Signer ,
123
+ autolink = true ,
124
+ silent = false ,
125
+ ) : Promise < DeployResult > => {
126
+ // This function will autolink, that means it will automatically deploy external libraries
127
+ // and link them to the contract
128
+ const libraries = { }
129
+ if ( autolink ) {
130
+ const artifact = loadArtifact ( name )
131
+ if ( artifact . linkReferences && Object . keys ( artifact . linkReferences ) . length > 0 ) {
132
+ for ( const fileReferences of Object . values ( artifact . linkReferences ) ) {
133
+ for ( const libName of Object . keys ( fileReferences ) ) {
134
+ const deployResult = await deployContract ( libName , [ ] , sender , false , silent )
135
+ libraries [ libName ] = deployResult . contract . address
136
+ }
137
+ }
138
+ }
139
+ }
107
140
108
- const contract = await factory . connect ( wallet ) . deploy ( ...args )
141
+ // Deploy
142
+ const factory = getContractFactory ( name , libraries )
143
+ const contract = await factory . connect ( sender ) . deploy ( ...args )
109
144
const txHash = contract . deployTransaction . hash
110
- logger . log ( `> Deploy ${ name } , txHash: ${ txHash } ` )
111
- await wallet . provider . waitForTransaction ( txHash )
145
+ if ( ! silent ) {
146
+ logger . log ( `> Deploy ${ name } , txHash: ${ txHash } ` )
147
+ }
148
+ await sender . provider . waitForTransaction ( txHash )
112
149
113
- logger . log ( '= CreationCodeHash: ' , hash ( factory . bytecode ) )
114
- logger . log ( '= RuntimeCodeHash: ' , hash ( await wallet . provider . getCode ( contract . address ) ) )
115
- logger . success ( `${ name } has been deployed to address: ${ contract . address } ` )
150
+ // Receipt
151
+ const creationCodeHash = hash ( factory . bytecode )
152
+ const runtimeCodeHash = hash ( await sender . provider . getCode ( contract . address ) )
153
+ if ( ! silent ) {
154
+ logger . log ( '= CreationCodeHash: ' , creationCodeHash )
155
+ logger . log ( '= RuntimeCodeHash: ' , runtimeCodeHash )
156
+ logger . success ( `${ name } has been deployed to address: ${ contract . address } ` )
157
+ }
116
158
117
- return contract
159
+ return { contract, creationCodeHash , runtimeCodeHash , txHash , libraries }
118
160
}
119
161
120
162
export const deployContractAndSave = async (
121
163
name : string ,
122
164
args : Array < { name : string ; value : string } > ,
123
- wallet : Wallet ,
165
+ sender : Signer ,
124
166
addressBook : AddressBook ,
125
167
) : Promise < Contract > => {
126
168
// Deploy the contract
127
- const contract = await deployContract (
169
+ const deployResult = await deployContract (
128
170
name ,
129
171
args . map ( ( a ) => a . value ) ,
130
- wallet ,
172
+ sender ,
131
173
)
132
174
133
175
// Save address entry
134
- const artifact = loadArtifact ( name )
135
176
addressBook . setEntry ( name , {
136
- address : contract . address ,
177
+ address : deployResult . contract . address ,
137
178
constructorArgs : args . length === 0 ? undefined : args ,
138
- creationCodeHash : hash ( artifact . bytecode ) ,
139
- runtimeCodeHash : hash ( await wallet . provider . getCode ( contract . address ) ) ,
140
- txHash : contract . deployTransaction . hash ,
179
+ creationCodeHash : deployResult . creationCodeHash ,
180
+ runtimeCodeHash : deployResult . runtimeCodeHash ,
181
+ txHash : deployResult . txHash ,
182
+ libraries :
183
+ deployResult . libraries && Object . keys ( deployResult . libraries ) . length > 0
184
+ ? deployResult . libraries
185
+ : undefined ,
141
186
} )
142
187
143
- return contract
188
+ return deployResult . contract
144
189
}
145
190
146
191
export const deployContractWithProxyAndSave = async (
147
192
name : string ,
148
193
args : Array < { name : string ; value : string } > ,
149
- wallet : Wallet ,
194
+ sender : Signer ,
150
195
addressBook : AddressBook ,
151
196
) : Promise < Contract > => {
152
197
// Deploy implementation
153
- const contract = await deployContractAndSave ( name , [ ] , wallet , addressBook )
198
+ const contract = await deployContractAndSave ( name , [ ] , sender , addressBook )
154
199
// Deploy proxy
155
- const proxy = await deployContract ( 'GraphProxy' , [ contract . address ] , wallet )
200
+ const deployResult = await deployContract ( 'GraphProxy' , [ contract . address ] , sender )
201
+ const proxy = deployResult . contract
156
202
// Implementation accepts upgrade
157
203
await sendTransaction (
158
- wallet ,
204
+ sender ,
159
205
contract ,
160
206
'acceptProxy' ,
161
207
...[ proxy . address , ...args . map ( ( a ) => a . value ) ] ,
@@ -168,7 +214,7 @@ export const deployContractWithProxyAndSave = async (
168
214
address : proxy . address ,
169
215
initArgs : args . length === 0 ? undefined : args ,
170
216
creationCodeHash : hash ( artifact . bytecode ) ,
171
- runtimeCodeHash : hash ( await wallet . provider . getCode ( proxy . address ) ) ,
217
+ runtimeCodeHash : hash ( await sender . provider . getCode ( proxy . address ) ) ,
172
218
txHash : proxy . deployTransaction . hash ,
173
219
proxy : true ,
174
220
implementation : contractEntry ,
@@ -177,3 +223,34 @@ export const deployContractWithProxyAndSave = async (
177
223
// Use interface of contract but with the proxy address
178
224
return contract . attach ( proxy . address )
179
225
}
226
+
227
+ export const linkLibraries = (
228
+ artifact : {
229
+ bytecode : string
230
+ linkReferences ?: LinkReferences
231
+ } ,
232
+ libraries ?: { [ libraryName : string ] : string } ,
233
+ ) : string => {
234
+ let bytecode = artifact . bytecode
235
+
236
+ if ( libraries ) {
237
+ if ( artifact . linkReferences ) {
238
+ for ( const [ fileName , fileReferences ] of Object . entries ( artifact . linkReferences ) ) {
239
+ for ( const [ libName , fixups ] of Object . entries ( fileReferences ) ) {
240
+ const addr = libraries [ libName ]
241
+ if ( addr === undefined ) {
242
+ continue
243
+ }
244
+
245
+ for ( const fixup of fixups ) {
246
+ bytecode =
247
+ bytecode . substr ( 0 , 2 + fixup . start * 2 ) +
248
+ addr . substr ( 2 ) +
249
+ bytecode . substr ( 2 + ( fixup . start + fixup . length ) * 2 )
250
+ }
251
+ }
252
+ }
253
+ }
254
+ }
255
+ return bytecode
256
+ }
0 commit comments