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]
);