1
+ import { BuidlerPluginError } from "@nomiclabs/buidler/internal/core/errors" ;
1
2
import debug from "debug" ;
2
3
import { URL } from "url" ;
3
4
4
5
const log = debug ( "buidler:plugin:ganache-service" ) ;
5
- log . color = "6" ;
6
+
7
+ declare interface GanacheOptions {
8
+ url : string ;
9
+ keepAliveTimeout ?: number ;
10
+ accountKeysPath ?: string ; // Translates to: account_keys_path
11
+ accounts ?: object [ ] ;
12
+ allowUnlimitedContractSize ?: boolean ;
13
+ blockTime ?: number ;
14
+ dbPath ?: string ; // Translates to: db_path
15
+ debug ?: boolean ;
16
+ defaultBalanceEther ?: number ; // Translates to: default_balance_ether
17
+ fork ?: string | object ;
18
+ forkBlockNumber ?: string | number ; // Translates to: fork_block_number
19
+ gasLimit ?: number ;
20
+ gasPrice ?: string | number ;
21
+ hardfork ?: "byzantium" | "constantinople" | "petersburg" ;
22
+ hdPath ?: string ; // Translates to: hd_path
23
+ hostname ?: string ;
24
+ locked ?: boolean ;
25
+ logger ?: {
26
+ log ( msg : string ) : void ;
27
+ } ;
28
+ mnemonic ?: string ;
29
+ networkId ?: number ;
30
+ port ?: number ;
31
+ seed ?: any ;
32
+ time ?: any ; // Date
33
+ totalAccounts ?: number ; // Translates to: total_accounts
34
+ unlockedAccounts ?: string [ ] ; // Translates to: unlocked_accounts
35
+ verbose ?: boolean ;
36
+ vmErrorsOnRPCResponse ?: boolean ;
37
+ ws ?: boolean ;
38
+ }
39
+
40
+ const DEFAULT_PORT = 7545 ;
6
41
7
42
export class GanacheService {
8
- public static error : Error ;
43
+ public static error ? : Error ;
9
44
public static optionValidator : any ;
10
45
11
46
public static getDefaultOptions ( ) : GanacheOptions {
12
47
return {
13
- url : " http://127.0.0.1:8545" ,
48
+ url : ` http://127.0.0.1:${ DEFAULT_PORT } ` ,
14
49
gasPrice : 20000000000 ,
15
50
gasLimit : 6721975 ,
16
51
defaultBalanceEther : 100 ,
@@ -54,13 +89,20 @@ export class GanacheService {
54
89
} catch ( e ) {
55
90
// Verify the expected error or throw it again
56
91
if ( e . name === "TypeError" ) {
57
- e . message = "One or more invalid values in ganache network options" ;
58
- if ( ! GanacheService . error ) {
59
- log ( e . message || e ) ;
60
- GanacheService . error = e ;
92
+ if ( GanacheService . error === undefined ) {
93
+ const error = new BuidlerPluginError (
94
+ `Ganache plugin config is invalid: ${ e . message } ` ,
95
+ e
96
+ ) ;
97
+
98
+ log ( "Failed to initialize GanacheService\n" , error ) ;
99
+ GanacheService . error = error ;
61
100
}
62
101
} else {
63
- throw e ;
102
+ throw new BuidlerPluginError (
103
+ `Failed to initialize GanacheService: ${ e . message } ` ,
104
+ e
105
+ ) ;
64
106
}
65
107
}
66
108
}
@@ -78,15 +120,31 @@ export class GanacheService {
78
120
79
121
// Start server with current configs (port and hostname)
80
122
await new Promise ( ( resolve , reject ) => {
81
- this . _server . once ( "listening" , resolve ) ;
82
- this . _server . once ( "error" , reject ) ;
123
+ let onError : ( err : Error ) => void ;
124
+
125
+ const onListening = ( ) => {
126
+ this . _server . removeListener ( "error" , onError ) ;
127
+ resolve ( ) ;
128
+ } ;
129
+
130
+ onError = err => {
131
+ this . _server . removeListener ( "listening" , onListening ) ;
132
+ reject ( err ) ;
133
+ } ;
134
+
135
+ this . _server . once ( "listening" , onListening ) ;
136
+ this . _server . once ( "error" , onError ) ;
83
137
this . _server . listen ( port , hostname ) ;
84
138
} ) ;
85
139
} catch ( e ) {
86
- e . message = `Start Server > ${ e . message } ` ;
87
- if ( ! GanacheService . error ) {
88
- log ( e . message || e ) ;
89
- GanacheService . error = e ;
140
+ const error = new BuidlerPluginError (
141
+ `Failed to start GanacheService: ${ e . message } ` ,
142
+ e
143
+ ) ;
144
+
145
+ if ( GanacheService . error === undefined ) {
146
+ log ( "Failed to start GanacheService\n" , error ) ;
147
+ GanacheService . error = error ;
90
148
}
91
149
}
92
150
@@ -104,22 +162,25 @@ export class GanacheService {
104
162
// Stop server and Wait for it
105
163
await new Promise ( ( resolve , reject ) => {
106
164
this . _server . close ( ( err : Error ) => {
107
- if ( err ) {
165
+ if ( err !== undefined && err !== null ) {
108
166
reject ( err ) ;
109
167
} else {
110
168
resolve ( ) ;
111
169
}
112
170
} ) ;
113
171
} ) ;
114
172
} catch ( e ) {
115
- e . message = `Stop Server > ${ e . message } ` ;
116
- if ( ! GanacheService . error ) {
117
- log ( e . message || e ) ;
118
- GanacheService . error = e ;
173
+ const error = new BuidlerPluginError (
174
+ `Failed to stop GanacheService: ${ e . message } ` ,
175
+ e
176
+ ) ;
177
+
178
+ if ( GanacheService . error === undefined ) {
179
+ log ( "Failed to stop GanacheService\n" , error ) ;
180
+ GanacheService . error = error ;
119
181
}
120
182
}
121
183
122
- // Verify service state before continue (TODO Maybe extract this to a decorator)
123
184
this . _checkForServiceErrors ( ) ;
124
185
}
125
186
@@ -129,25 +190,33 @@ export class GanacheService {
129
190
// Validate and parse hostname and port from URL (this validation is priority)
130
191
const url = new URL ( options . url ) ;
131
192
if ( url . hostname !== "locahost" && url . hostname !== "127.0.0.1" ) {
132
- throw new Error ( "Config: ganache.hostname must resolve to locahost") ;
193
+ throw new BuidlerPluginError ( "Ganache network only works with locahost") ;
133
194
}
134
195
135
196
// Validate all options agains validator
136
197
try {
137
198
GanacheService . optionValidator . check ( options ) ;
138
199
} catch ( e ) {
139
- e . message = e . message . replace ( "value." , "Config: ganache." ) ;
140
- throw e ;
200
+ throw new BuidlerPluginError (
201
+ `Ganache network config is invalid: ${ e . message } ` ,
202
+ e
203
+ ) ;
141
204
}
142
205
143
206
// Test for unsupported commands
144
207
if ( options . accounts !== undefined ) {
145
- throw new Error ( "Config: ganache.accounts unsupported for this network" ) ;
208
+ throw new BuidlerPluginError (
209
+ "Config: ganache.accounts unsupported for this network"
210
+ ) ;
146
211
}
147
212
148
213
// Transform needed options to Ganache core server (not using SnakeCase lib for performance)
149
214
validatedOptions . hostname = url . hostname ;
150
- validatedOptions . port = Number ( url . port ) || 80 ;
215
+
216
+ validatedOptions . port =
217
+ url . port !== undefined && url . port !== ""
218
+ ? parseInt ( url . port , 10 )
219
+ : DEFAULT_PORT ;
151
220
152
221
const optionsToInclude = [
153
222
"accountsKeyPath" ,
@@ -157,7 +226,7 @@ export class GanacheService {
157
226
"unlockedAccounts"
158
227
] ;
159
228
for ( const [ key , value ] of Object . entries ( options ) ) {
160
- if ( value && optionsToInclude . includes ( key ) ) {
229
+ if ( value !== undefined && optionsToInclude . includes ( key ) ) {
161
230
validatedOptions [ this . _snakeCase ( key ) ] = value ;
162
231
delete validatedOptions [ key ] ;
163
232
}
@@ -171,45 +240,27 @@ export class GanacheService {
171
240
172
241
// Add listener for general server errors
173
242
server . on ( "error" , function ( err : any ) {
174
- if ( ! GanacheService . error && err ) {
175
- log ( err . message || err ) ;
243
+ if (
244
+ GanacheService . error === undefined &&
245
+ err !== undefined &&
246
+ err !== null
247
+ ) {
248
+ log ( "An error occurred in GanacheService\n" , err ) ;
176
249
GanacheService . error = err ;
177
250
}
178
251
} ) ;
179
-
180
- // Add listener for process uncaught errors (warning: this may catch non plugin related errors)
181
- // process.on("uncaughtException", function(e) {
182
- // log("Uncaught Exception", e.message);
183
- // server.close(function(err: any) {
184
- // if (!GanacheService.error && err) {
185
- // log(err.message || err.stack || err);
186
- // GanacheService.error = err;
187
- // }
188
- // });
189
- // });
190
-
191
- // Add listener for standard POSIX signal SIGINT (usually generated with <Ctrl>+C)
192
- // process.on("SIGINT", function() {
193
- // log("SIGINT detected");
194
- // server.close(function(err: any) {
195
- // if (!GanacheService.error && err) {
196
- // log(err.message || err.stack || err);
197
- // GanacheService.error = err;
198
- // }
199
- // });
200
- // });
201
-
202
- // TODO Maybe in the future, in new threat, some kind of ping checker to the server (every 30 seg)
203
252
}
204
253
205
254
private _checkForServiceErrors ( ) {
206
- if ( GanacheService . error ) {
207
- // Close server (if needed)
208
- if ( this . _server ) {
255
+ if ( GanacheService . error !== undefined ) {
256
+ if ( this . _server !== undefined ) {
209
257
this . _server . close ( ) ;
210
258
}
211
259
212
- throw new Error ( GanacheService . error . message ) ;
260
+ throw new BuidlerPluginError (
261
+ `An error occurred in GanacheService: ${ GanacheService . error . message } ` ,
262
+ GanacheService . error
263
+ ) ;
213
264
}
214
265
}
215
266
0 commit comments