0
# Plugin System
1
2
Buidler's plugin system enables extensible functionality through modular packages that can register tasks, extend the runtime environment, modify configuration, and integrate external tools. Plugins follow a standard pattern for loading, configuration, and integration.
3
4
## Capabilities
5
6
### Plugin Loading
7
8
Load and integrate plugins into the Buidler environment.
9
10
```typescript { .api }
11
/**
12
* Load a Buidler plugin by name
13
* @param pluginName - Name of the plugin package to load
14
*/
15
function usePlugin(pluginName: string): void;
16
17
/**
18
* Load a plugin from a specific file path
19
* @param absolutePluginFilePath - Absolute path to plugin file
20
*/
21
function loadPluginFile(absolutePluginFilePath: string): void;
22
23
/**
24
* Ensure a plugin is loaded via usePlugin (for validation)
25
*/
26
function ensurePluginLoadedWithUsePlugin(): void;
27
```
28
29
**Usage Examples:**
30
31
```typescript
32
// buidler.config.ts
33
import { usePlugin } from "@nomiclabs/buidler/config";
34
35
// Load official plugins
36
usePlugin("@nomiclabs/buidler-ethers");
37
usePlugin("@nomiclabs/buidler-waffle");
38
usePlugin("@nomiclabs/buidler-web3");
39
40
// Load community plugins
41
usePlugin("buidler-gas-reporter");
42
usePlugin("buidler-contract-sizer");
43
44
// Load local plugin file
45
import { loadPluginFile } from "@nomiclabs/buidler/plugins-testing";
46
loadPluginFile(path.join(__dirname, "plugins", "custom-plugin.js"));
47
```
48
49
### Plugin Error Handling
50
51
Specialized error class for plugin-related errors with context information.
52
53
```typescript { .api }
54
/**
55
* Error class for plugin-related failures
56
*/
57
class BuidlerPluginError extends Error {
58
/**
59
* Create a plugin error with context
60
* @param pluginName - Name of the plugin that caused the error
61
* @param message - Error message
62
* @param parent - Optional parent error for error chaining
63
*/
64
constructor(
65
pluginName: string,
66
message: string,
67
parent?: Error
68
);
69
70
/** Name of the plugin that caused the error */
71
readonly pluginName: string;
72
/** Optional parent error */
73
readonly parent?: Error;
74
}
75
```
76
77
**Usage Examples:**
78
79
```typescript
80
// In a plugin
81
import { BuidlerPluginError } from "@nomiclabs/buidler/plugins";
82
83
task("my-task", async (taskArgs, hre) => {
84
try {
85
// Plugin operation that might fail
86
await someOperation();
87
} catch (error) {
88
throw new BuidlerPluginError(
89
"my-plugin",
90
"Failed to execute operation",
91
error
92
);
93
}
94
});
95
96
// Error handling in user code
97
try {
98
await hre.run("my-task");
99
} catch (error) {
100
if (error instanceof BuidlerPluginError) {
101
console.error(`Plugin ${error.pluginName} failed: ${error.message}`);
102
if (error.parent) {
103
console.error("Caused by:", error.parent.message);
104
}
105
}
106
}
107
```
108
109
### Environment Extension
110
111
Plugins can extend the runtime environment to add custom functionality.
112
113
```typescript { .api }
114
/**
115
* Extend the Buidler runtime environment
116
* @param extender - Function to modify the environment
117
*/
118
function extendEnvironment(extender: EnvironmentExtender): void;
119
120
/**
121
* Environment extender function signature
122
* @param env - Runtime environment to extend
123
*/
124
type EnvironmentExtender = (env: BuidlerRuntimeEnvironment) => void;
125
```
126
127
**Usage Examples:**
128
129
```typescript
130
// Plugin extending environment with Ethers.js
131
import { extendEnvironment } from "@nomiclabs/buidler/config";
132
import { ethers } from "ethers";
133
134
extendEnvironment((hre) => {
135
// Add ethers to runtime environment
136
hre.ethers = {
137
provider: new ethers.providers.Web3Provider(hre.network.provider),
138
139
getContractFactory: async (name: string) => {
140
const artifact = await hre.artifacts.readArtifact(name);
141
return new ethers.ContractFactory(
142
artifact.abi,
143
artifact.bytecode,
144
hre.ethers.provider.getSigner()
145
);
146
},
147
148
getContract: async (name: string, address: string) => {
149
const artifact = await hre.artifacts.readArtifact(name);
150
return new ethers.Contract(
151
address,
152
artifact.abi,
153
hre.ethers.provider.getSigner()
154
);
155
}
156
};
157
});
158
159
// Usage in tasks
160
task("deploy", async (taskArgs, hre) => {
161
const ContractFactory = await hre.ethers.getContractFactory("MyContract");
162
const contract = await ContractFactory.deploy();
163
await contract.deployed();
164
console.log("Contract deployed to:", contract.address);
165
});
166
```
167
168
### Configuration Extension
169
170
Plugins can modify and extend the Buidler configuration.
171
172
```typescript { .api }
173
/**
174
* Extend the Buidler configuration
175
* @param extender - Function to modify configuration
176
*/
177
function extendConfig(extender: ConfigExtender): void;
178
179
/**
180
* Configuration extender function signature
181
* @param config - Resolved configuration to modify
182
* @param userConfig - User configuration (read-only)
183
*/
184
type ConfigExtender = (
185
config: ResolvedBuidlerConfig,
186
userConfig: DeepReadonly<BuidlerConfig>
187
) => void;
188
```
189
190
**Usage Examples:**
191
192
```typescript
193
// Plugin adding custom configuration
194
import { extendConfig } from "@nomiclabs/buidler/config";
195
196
extendConfig((config, userConfig) => {
197
// Add default network if not specified
198
if (!config.networks.development) {
199
config.networks.development = {
200
url: "http://localhost:8545",
201
gas: "auto",
202
gasPrice: "auto"
203
};
204
}
205
206
// Modify Solidity settings based on user config
207
if (userConfig.customOptimizer) {
208
config.solc.optimizer.enabled = true;
209
config.solc.optimizer.runs = userConfig.customOptimizer.runs || 200;
210
}
211
212
// Add custom paths
213
config.paths.deployments = path.join(config.paths.root, "deployments");
214
});
215
```
216
217
### Task Registration
218
219
Plugins register new tasks and subtasks to extend Buidler functionality.
220
221
**Usage Examples:**
222
223
```typescript
224
// Plugin registering tasks
225
import { task, internalTask, types } from "@nomiclabs/buidler/config";
226
227
// Register a new public task
228
task("verify", "Verify contracts on Etherscan")
229
.addParam("contract", "Contract name to verify", undefined, types.string)
230
.addOptionalParam("address", "Contract address", undefined, types.string)
231
.addFlag("force", "Force verification even if already verified")
232
.setAction(async (taskArgs, hre) => {
233
console.log(`Verifying ${taskArgs.contract}...`);
234
// Verification logic
235
});
236
237
// Register internal subtask
238
internalTask("verify:prepare", "Prepare verification data", async (taskArgs, hre) => {
239
// Preparation logic
240
});
241
242
// Override existing task
243
task("compile", "Extended compile with verification prep", async (taskArgs, hre, runSuper) => {
244
// Run original compile
245
await runSuper(taskArgs);
246
247
// Additional plugin functionality
248
await hre.run("verify:prepare");
249
});
250
```
251
252
### Lazy Loading Utilities
253
254
Utilities for lazy loading heavy dependencies in plugins.
255
256
```typescript { .api }
257
/**
258
* Create a lazy-loaded object proxy
259
* @param objectCreator - Function that creates the object when first accessed
260
* @returns Proxy object that loads on first property access
261
*/
262
function lazyObject<T extends object>(objectCreator: () => T): T;
263
264
/**
265
* Create a lazy-loaded function proxy
266
* @param functionCreator - Function that creates the function when first called
267
* @returns Proxy function that loads on first call
268
*/
269
function lazyFunction<T extends Function>(functionCreator: () => T): T;
270
```
271
272
**Usage Examples:**
273
274
```typescript
275
import { lazyObject, lazyFunction } from "@nomiclabs/buidler/plugins";
276
277
// Lazy load heavy dependency
278
const ethers = lazyObject(() => require("ethers"));
279
280
// Lazy load function
281
const compile = lazyFunction(() => require("solc").compile);
282
283
extendEnvironment((hre) => {
284
// Heavy objects are only loaded when accessed
285
hre.ethers = ethers;
286
hre.compile = compile;
287
});
288
```
289
290
### Common Plugin Patterns
291
292
Standard patterns for plugin development and integration.
293
294
**Plugin Entry Point Pattern:**
295
296
```typescript
297
// plugin-entry.ts
298
import { extendEnvironment, task } from "@nomiclabs/buidler/config";
299
import { BuidlerPluginError } from "@nomiclabs/buidler/plugins";
300
301
// Extend environment
302
extendEnvironment((hre) => {
303
hre.myPlugin = {
304
doSomething: async () => {
305
try {
306
// Plugin functionality
307
} catch (error) {
308
throw new BuidlerPluginError("my-plugin", "Operation failed", error);
309
}
310
}
311
};
312
});
313
314
// Register tasks
315
task("my-plugin:task", "Description")
316
.setAction(async (taskArgs, hre) => {
317
await hre.myPlugin.doSomething();
318
});
319
```
320
321
**Plugin Configuration Pattern:**
322
323
```typescript
324
import { extendConfig } from "@nomiclabs/buidler/config";
325
326
// Extend user config interface
327
declare module "@nomiclabs/buidler/types" {
328
interface BuidlerConfig {
329
myPlugin?: {
330
option1?: string;
331
option2?: boolean;
332
};
333
}
334
}
335
336
// Apply plugin configuration
337
extendConfig((config, userConfig) => {
338
const pluginConfig = userConfig.myPlugin || {};
339
340
// Set defaults
341
config.myPlugin = {
342
option1: pluginConfig.option1 || "default",
343
option2: pluginConfig.option2 !== false
344
};
345
});
346
```
347
348
**Testing Plugin Pattern:**
349
350
```typescript
351
import { resetBuidlerContext } from "@nomiclabs/buidler/plugins-testing";
352
353
describe("My Plugin", () => {
354
beforeEach(() => {
355
// Reset context between tests
356
resetBuidlerContext();
357
});
358
359
it("should extend environment", async () => {
360
// Load plugin
361
require("../src/plugin");
362
363
// Test plugin functionality
364
const hre = await import("@nomiclabs/buidler");
365
expect(hre.myPlugin).to.exist;
366
});
367
});
368
```
369
370
### Plugin Package Structure
371
372
Standard structure for Buidler plugin packages:
373
374
```
375
my-buidler-plugin/
376
├── package.json
377
├── src/
378
│ ├── index.ts # Plugin entry point
379
│ ├── tasks/ # Task definitions
380
│ ├── types.ts # Type extensions
381
│ └── utils/ # Utility functions
382
├── test/
383
│ └── plugin.test.ts # Plugin tests
384
└── README.md
385
```
386
387
**Package.json example:**
388
389
```json
390
{
391
"name": "buidler-my-plugin",
392
"version": "1.0.0",
393
"main": "dist/index.js",
394
"types": "dist/index.d.ts",
395
"keywords": ["buidler", "plugin"],
396
"peerDependencies": {
397
"@nomiclabs/buidler": "^1.0.0"
398
}
399
}
400
```