Advanced framework for testing smart contracts with enhanced chai matchers, mock contracts, and testing utilities.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Comprehensive Solidity compilation system including library linking, project compilation, TypeChain integration, and advanced configuration management for smart contract development workflows.
High-level functions for compiling entire Solidity projects with automatic configuration detection and compilation pipeline management.
/**
* Compile entire project using waffle configuration
* @param configPath - Optional path to waffle config file
* @returns Promise that resolves when compilation completes
*/
function compileProject(configPath?: string): Promise<void>;
/**
* Compile contracts and save artifacts to output directory
* @param input - Compilation configuration options
* @returns Promise that resolves when compilation and save complete
*/
function compileAndSave(input: InputConfig): Promise<void>;
/**
* Compile contracts and return compilation results
* @param input - Compilation configuration options
* @returns Promise resolving to compilation output
*/
function compile(input: InputConfig): Promise<any>;Usage Examples:
import { compileProject, compileAndSave, compile } from "@ethereum-waffle/compiler";
// Compile project with default config
await compileProject();
// Compile with custom config file
await compileProject("./waffle-config.json");
// Compile and save with custom options
await compileAndSave({
sourceDirectory: "./contracts",
outputDirectory: "./build",
compilerType: "solcjs",
compilerVersion: "0.8.19"
});
// Compile and get results without saving
const compilationResults = await compile({
sourceDirectory: "./contracts",
compilerType: "native"
});
console.log("Compiled contracts:", compilationResults.contracts);Comprehensive configuration system supporting multiple compiler types, output formats, and TypeChain integration.
/**
* Complete configuration interface for Waffle compilation
*/
interface Config {
/** Directory containing Solidity source files */
sourceDirectory: string;
/** Directory to output compiled artifacts */
outputDirectory: string;
/** Directory to output flattened contracts */
flattenOutputDirectory: string;
/** Directory containing node_modules for import resolution */
nodeModulesDirectory: string;
/** Directory for compilation cache */
cacheDirectory: string;
/** Type of Solidity compiler to use */
compilerType: 'native' | 'dockerized-solc' | 'solcjs' | 'dockerized-vyper';
/** Specific compiler version to use */
compilerVersion: string;
/** Additional paths allowed for imports */
compilerAllowedPaths: string[];
/** Raw compiler options passed to solc */
compilerOptions: any;
/** Generate human-readable ABI files */
outputHumanReadableAbi: boolean;
/** Format of compilation output */
outputType: 'multiple' | 'combined' | 'all' | 'minimal';
/** Enable TypeChain type generation */
typechainEnabled: boolean;
/** Output directory for TypeChain types */
typechainOutputDir: string;
}
/**
* Partial configuration for input - all fields optional
*/
type InputConfig = Partial<Config>;
/**
* Convert input configuration to complete configuration with defaults
* @param input - Partial configuration options
* @returns Complete configuration with defaults applied
*/
function inputToConfig(input: InputConfig): Config;Usage Examples:
import { inputToConfig, Config } from "@ethereum-waffle/compiler";
// Create config with defaults
const config = inputToConfig({
sourceDirectory: "./src/contracts",
compilerType: "solcjs",
typechainEnabled: true
});
// Full configuration example
const fullConfig: Config = {
sourceDirectory: "./contracts",
outputDirectory: "./artifacts",
flattenOutputDirectory: "./flattened",
nodeModulesDirectory: "./node_modules",
cacheDirectory: "./cache",
compilerType: "native",
compilerVersion: "0.8.19",
compilerAllowedPaths: ["./lib", "./interfaces"],
compilerOptions: {
optimizer: {
enabled: true,
runs: 200
}
},
outputHumanReadableAbi: true,
outputType: "multiple",
typechainEnabled: true,
typechainOutputDir: "./typechain"
};
// Use configuration for compilation
await compileAndSave(fullConfig);Automatic TypeScript type generation for compiled contracts enabling full type safety in contract interactions.
/**
* Generate TypeScript types for compiled contracts
* @param config - Configuration containing TypeChain settings
* @returns Promise that resolves when type generation completes
*/
function generateTypes(config: Config): Promise<void>;Usage Examples:
import { generateTypes, inputToConfig } from "@ethereum-waffle/compiler";
// Generate types after compilation
const config = inputToConfig({
sourceDirectory: "./contracts",
outputDirectory: "./artifacts",
typechainEnabled: true,
typechainOutputDir: "./typechain"
});
await compileAndSave(config);
await generateTypes(config);
// Use generated types in testing
import { MyContract__factory } from "./typechain";
const contractFactory = new MyContract__factory(wallet);
const contract = await contractFactory.deploy(constructorArg);
// contract is now fully typed with all methods and events
await contract.myMethod(typedParameter);Function for linking deployed library addresses to contract bytecode, enabling contracts to use external library functions during deployment.
/**
* Link library addresses to contract bytecode placeholders
* @param contract - Contract with linkable bytecode
* @param libraryName - Name of the library to link
* @param libraryAddress - Deployed address of the library
*/
function link(
contract: LinkableContract,
libraryName: string,
libraryAddress: string
): void;
/**
* Interface for contracts that support library linking
*/
interface LinkableContract {
evm: {
bytecode: {
object: any;
};
};
}Usage Examples:
import { link, deployContract } from "ethereum-waffle";
// Deploy library contract first
const mathLibrary = await deployContract(wallet, MathLibraryJson);
// Load main contract JSON
const mainContractJson = require("./artifacts/MainContract.json");
// Link library to main contract
link(mainContractJson, "MathLibrary", mathLibrary.address);
// Deploy linked contract
const mainContract = await deployContract(wallet, mainContractJson, [constructorArgs]);
// Contract can now use library functions
await mainContract.useLibraryFunction(42);Complex library linking patterns for contracts with multiple library dependencies and nested library relationships.
Multiple Library Linking:
import { link, deployContract } from "ethereum-waffle";
// Deploy multiple libraries
const stringLibrary = await deployContract(wallet, StringLibraryJson);
const mathLibrary = await deployContract(wallet, MathLibraryJson);
const utilsLibrary = await deployContract(wallet, UtilsLibraryJson);
// Load contract that uses multiple libraries
const complexContractJson = require("./artifacts/ComplexContract.json");
// Link all required libraries
link(complexContractJson, "StringLibrary", stringLibrary.address);
link(complexContractJson, "MathLibrary", mathLibrary.address);
link(complexContractJson, "UtilsLibrary", utilsLibrary.address);
// Deploy contract with all libraries linked
const complexContract = await deployContract(wallet, complexContractJson);Nested Library Dependencies:
import { link, deployContract } from "ethereum-waffle";
// Deploy base library first
const baseLibrary = await deployContract(wallet, BaseLibraryJson);
// Load dependent library and link base library
const dependentLibraryJson = require("./artifacts/DependentLibrary.json");
link(dependentLibraryJson, "BaseLibrary", baseLibrary.address);
// Deploy dependent library
const dependentLibrary = await deployContract(wallet, dependentLibraryJson);
// Load main contract and link both libraries
const mainContractJson = require("./artifacts/MainContract.json");
link(mainContractJson, "BaseLibrary", baseLibrary.address);
link(mainContractJson, "DependentLibrary", dependentLibrary.address);
// Deploy main contract
const mainContract = await deployContract(wallet, mainContractJson);Library Linking with TypeChain:
import { link } from "ethereum-waffle";
import { MyContract__factory, MyLibrary__factory } from "./typechain";
// Deploy library using TypeChain factory
const libraryFactory = new MyLibrary__factory(wallet);
const library = await libraryFactory.deploy();
// Get contract JSON for linking (if needed for complex scenarios)
const contractJson = require("./artifacts/contracts/MyContract.sol/MyContract.json");
link(contractJson, "MyLibrary", library.address);
// Deploy main contract with linked library
const mainContract = await deployContract(wallet, contractJson);The linking process modifies contract bytecode by replacing library placeholders with actual deployed addresses:
Understanding Bytecode Placeholders:
// Before linking - bytecode contains placeholders
const contractJson = require("./artifacts/Contract.json");
console.log(contractJson.evm.bytecode.object);
// Output: "608060405234801561001057600080fd5b50__MathLibrary_________________..."
// ^^^^^^^^^^^^^^^^^^^^^^^^^
// Library placeholder
// After linking
link(contractJson, "MathLibrary", "0x1234567890123456789012345678901234567890");
console.log(contractJson.evm.bytecode.object);
// Output: "608060405234801561001057600080fd5b501234567890123456789012345678901234567890..."
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Actual library addressError Handling:
import { link, deployContract } from "ethereum-waffle";
try {
const contractJson = require("./artifacts/Contract.json");
// This will fail if library name doesn't match placeholder
link(contractJson, "WrongLibraryName", libraryAddress);
} catch (error) {
console.log("Library linking failed:", error.message);
// Handle linking error appropriately
}
// Verify contract has required libraries before deployment
function hasUnlinkedLibraries(contractJson: LinkableContract): boolean {
const bytecode = contractJson.evm.bytecode.object;
return typeof bytecode === 'string' && bytecode.includes('__') && bytecode.includes('_');
}
if (hasUnlinkedLibraries(contractJson)) {
throw new Error("Contract has unlinked libraries");
}Common patterns for integrating library linking into contract deployment workflows:
import { link, deployContract, MockProvider } from "ethereum-waffle";
async function deployContractWithLibraries(
wallet: Wallet,
contractJson: any,
libraries: Record<string, string>,
constructorArgs: any[] = []
) {
// Clone contract JSON to avoid modifying original
const linkedContract = JSON.parse(JSON.stringify(contractJson));
// Link all required libraries
for (const [libraryName, libraryAddress] of Object.entries(libraries)) {
link(linkedContract, libraryName, libraryAddress);
}
// Deploy with linked libraries
return deployContract(wallet, linkedContract, constructorArgs);
}
// Usage example
const provider = new MockProvider();
const [wallet] = provider.getWallets();
// Deploy libraries
const mathLib = await deployContract(wallet, MathLibraryJson);
const stringLib = await deployContract(wallet, StringLibraryJson);
// Deploy main contract with libraries
const mainContract = await deployContractWithLibraries(
wallet,
MainContractJson,
{
"MathLibrary": mathLib.address,
"StringLibrary": stringLib.address
},
[constructorArg1, constructorArg2]
);