1 | import debug from "debug";
|
2 |
|
3 | import {
|
4 | BuidlerArguments,
|
5 | BuidlerRuntimeEnvironment,
|
6 | EnvironmentExtender,
|
7 | EthereumProvider,
|
8 | ExperimentalBuidlerEVMMessageTraceHook,
|
9 | Network,
|
10 | ParamDefinition,
|
11 | ResolvedBuidlerConfig,
|
12 | RunSuperFunction,
|
13 | RunTaskFunction,
|
14 | TaskArguments,
|
15 | TaskDefinition,
|
16 | TasksMap,
|
17 | } from "../../types";
|
18 | import { MessageTrace } from "../buidler-evm/stack-traces/message-trace";
|
19 | import { lazyObject } from "../util/lazy";
|
20 |
|
21 | import { BuidlerError } from "./errors";
|
22 | import { ERRORS } from "./errors-list";
|
23 | import { createProvider } from "./providers/construction";
|
24 | import { OverriddenTaskDefinition } from "./tasks/task-definitions";
|
25 |
|
26 | const log = debug("buidler:core:bre");
|
27 |
|
28 | export class Environment implements BuidlerRuntimeEnvironment {
|
29 | private static readonly _BLACKLISTED_PROPERTIES: string[] = [
|
30 | "injectToGlobal",
|
31 | "_runTaskDefinition",
|
32 | ];
|
33 |
|
34 | |
35 |
|
36 |
|
37 | public ethereum: EthereumProvider;
|
38 |
|
39 | public network: Network;
|
40 |
|
41 | private readonly _extenders: EnvironmentExtender[];
|
42 |
|
43 | |
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 | constructor(
|
56 | public readonly config: ResolvedBuidlerConfig,
|
57 | public readonly buidlerArguments: BuidlerArguments,
|
58 | public readonly tasks: TasksMap,
|
59 | extenders: EnvironmentExtender[] = [],
|
60 | experimentalBuidlerEVMMessageTraceHooks: ExperimentalBuidlerEVMMessageTraceHook[] = []
|
61 | ) {
|
62 | log("Creating BuidlerRuntimeEnvironment");
|
63 |
|
64 | const networkName =
|
65 | buidlerArguments.network !== undefined
|
66 | ? buidlerArguments.network
|
67 | : config.defaultNetwork;
|
68 |
|
69 | const networkConfig = config.networks[networkName];
|
70 |
|
71 | if (networkConfig === undefined) {
|
72 | throw new BuidlerError(ERRORS.NETWORK.CONFIG_NOT_FOUND, {
|
73 | network: networkName,
|
74 | });
|
75 | }
|
76 |
|
77 | const provider = lazyObject(() => {
|
78 | log(`Creating provider for network ${networkName}`);
|
79 | return createProvider(
|
80 | networkName,
|
81 | networkConfig,
|
82 | config.solc.version,
|
83 | config.paths,
|
84 | experimentalBuidlerEVMMessageTraceHooks.map(
|
85 | (hook) => (trace: MessageTrace, isCallMessageTrace: boolean) =>
|
86 | hook(this, trace, isCallMessageTrace)
|
87 | )
|
88 | );
|
89 | });
|
90 |
|
91 | this.network = {
|
92 | name: networkName,
|
93 | config: config.networks[networkName],
|
94 | provider,
|
95 | };
|
96 |
|
97 | this.ethereum = provider;
|
98 | this._extenders = extenders;
|
99 |
|
100 | extenders.forEach((extender) => extender(this));
|
101 | }
|
102 |
|
103 | |
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 |
|
112 | public readonly run: RunTaskFunction = async (name, taskArguments = {}) => {
|
113 | const taskDefinition = this.tasks[name];
|
114 |
|
115 | log("Running task %s", name);
|
116 |
|
117 | if (taskDefinition === undefined) {
|
118 | throw new BuidlerError(ERRORS.ARGUMENTS.UNRECOGNIZED_TASK, {
|
119 | task: name,
|
120 | });
|
121 | }
|
122 |
|
123 | const resolvedTaskArguments = this._resolveValidTaskArguments(
|
124 | taskDefinition,
|
125 | taskArguments
|
126 | );
|
127 |
|
128 | return this._runTaskDefinition(taskDefinition, resolvedTaskArguments);
|
129 | };
|
130 |
|
131 | |
132 |
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 | public injectToGlobal(
|
139 | blacklist: string[] = Environment._BLACKLISTED_PROPERTIES
|
140 | ): () => void {
|
141 | const globalAsAny = global as any;
|
142 |
|
143 | const previousValues: { [name: string]: any } = {};
|
144 |
|
145 | for (const [key, value] of Object.entries(this)) {
|
146 | if (blacklist.includes(key)) {
|
147 | continue;
|
148 | }
|
149 |
|
150 | previousValues[key] = globalAsAny[key];
|
151 | globalAsAny[key] = value;
|
152 | }
|
153 |
|
154 | return () => {
|
155 | for (const [key, _] of Object.entries(this)) {
|
156 | if (blacklist.includes(key)) {
|
157 | continue;
|
158 | }
|
159 |
|
160 | globalAsAny[key] = previousValues[key];
|
161 | }
|
162 | };
|
163 | }
|
164 |
|
165 | private async _runTaskDefinition(
|
166 | taskDefinition: TaskDefinition,
|
167 | taskArguments: TaskArguments
|
168 | ) {
|
169 | let runSuperFunction: any;
|
170 |
|
171 | if (taskDefinition instanceof OverriddenTaskDefinition) {
|
172 | runSuperFunction = async (
|
173 | _taskArguments: TaskArguments = taskArguments
|
174 | ) => {
|
175 | log("Running %s's super", taskDefinition.name);
|
176 |
|
177 | return this._runTaskDefinition(
|
178 | taskDefinition.parentTaskDefinition,
|
179 | _taskArguments
|
180 | );
|
181 | };
|
182 |
|
183 | runSuperFunction.isDefined = true;
|
184 | } else {
|
185 | runSuperFunction = async () => {
|
186 | throw new BuidlerError(ERRORS.TASK_DEFINITIONS.RUNSUPER_NOT_AVAILABLE, {
|
187 | taskName: taskDefinition.name,
|
188 | });
|
189 | };
|
190 |
|
191 | runSuperFunction.isDefined = false;
|
192 | }
|
193 |
|
194 | const runSuper: RunSuperFunction<TaskArguments> = runSuperFunction;
|
195 |
|
196 | const globalAsAny = global as any;
|
197 | const previousRunSuper: any = globalAsAny.runSuper;
|
198 | globalAsAny.runSuper = runSuper;
|
199 |
|
200 | const uninjectFromGlobal = this.injectToGlobal();
|
201 |
|
202 | try {
|
203 | return await taskDefinition.action(taskArguments, this, runSuper);
|
204 | } finally {
|
205 | uninjectFromGlobal();
|
206 | globalAsAny.runSuper = previousRunSuper;
|
207 | }
|
208 | }
|
209 |
|
210 | |
211 |
|
212 |
|
213 |
|
214 |
|
215 |
|
216 |
|
217 |
|
218 |
|
219 |
|
220 |
|
221 |
|
222 |
|
223 | private _resolveValidTaskArguments(
|
224 | taskDefinition: TaskDefinition,
|
225 | taskArguments: TaskArguments
|
226 | ): TaskArguments {
|
227 | const { paramDefinitions, positionalParamDefinitions } = taskDefinition;
|
228 |
|
229 | const nonPositionalParamDefinitions = Object.values(paramDefinitions);
|
230 |
|
231 |
|
232 | const allTaskParamDefinitions = [
|
233 | ...nonPositionalParamDefinitions,
|
234 | ...positionalParamDefinitions,
|
235 | ];
|
236 |
|
237 | const initResolvedArguments: {
|
238 | errors: BuidlerError[];
|
239 | values: TaskArguments;
|
240 | } = { errors: [], values: {} };
|
241 |
|
242 | const resolvedArguments = allTaskParamDefinitions.reduce(
|
243 | ({ errors, values }, paramDefinition) => {
|
244 | try {
|
245 | const paramName = paramDefinition.name;
|
246 | const argumentValue = taskArguments[paramName];
|
247 | const resolvedArgumentValue = this._resolveArgument(
|
248 | paramDefinition,
|
249 | argumentValue
|
250 | );
|
251 | if (resolvedArgumentValue !== undefined) {
|
252 | values[paramName] = resolvedArgumentValue;
|
253 | }
|
254 | } catch (error) {
|
255 | errors.push(error);
|
256 | }
|
257 | return { errors, values };
|
258 | },
|
259 | initResolvedArguments
|
260 | );
|
261 |
|
262 | const { errors: resolveErrors, values: resolvedValues } = resolvedArguments;
|
263 |
|
264 |
|
265 | if (resolveErrors.length > 0) {
|
266 | throw resolveErrors[0];
|
267 | }
|
268 |
|
269 |
|
270 | const resolvedTaskArguments = { ...taskArguments, ...resolvedValues };
|
271 |
|
272 | return resolvedTaskArguments;
|
273 | }
|
274 |
|
275 | |
276 |
|
277 |
|
278 |
|
279 |
|
280 |
|
281 |
|
282 | private _resolveArgument(
|
283 | paramDefinition: ParamDefinition<any>,
|
284 | argumentValue: any
|
285 | ) {
|
286 | const { name, isOptional, defaultValue, type } = paramDefinition;
|
287 |
|
288 | if (argumentValue === undefined) {
|
289 | if (isOptional) {
|
290 |
|
291 | return defaultValue;
|
292 | }
|
293 |
|
294 |
|
295 | throw new BuidlerError(ERRORS.ARGUMENTS.MISSING_TASK_ARGUMENT, {
|
296 | param: name,
|
297 | });
|
298 | }
|
299 |
|
300 |
|
301 | this._checkTypeValidation(paramDefinition, argumentValue);
|
302 |
|
303 | return argumentValue;
|
304 | }
|
305 |
|
306 | |
307 |
|
308 |
|
309 |
|
310 |
|
311 |
|
312 |
|
313 |
|
314 | private _checkTypeValidation(
|
315 | paramDefinition: ParamDefinition<any>,
|
316 | argumentValue: any
|
317 | ) {
|
318 | const { name: paramName, type, isVariadic } = paramDefinition;
|
319 | if (type === undefined || type.validate === undefined) {
|
320 |
|
321 | return;
|
322 | }
|
323 |
|
324 |
|
325 |
|
326 | const argumentValueContainer = isVariadic ? argumentValue : [argumentValue];
|
327 |
|
328 | for (const value of argumentValueContainer) {
|
329 | type.validate(paramName, value);
|
330 | }
|
331 | }
|
332 | }
|