Overview

The smart contract manager in @coinbase/onchaintestkit provides a robust, deterministic, and high-performance framework for deploying and interacting with Ethereum smart contracts in end-to-end (E2E) testing environments. These utilities are designed for use with local Ethereum nodes (e.g., Anvil), and leverage the viem library for fast, type-safe blockchain interactions.

Why Is This Important?

  • Deterministic Testing: Ensures contracts are always deployed at the same address for a given salt and bytecode, making tests reproducible and reliable.
  • Performance: Uses viem for fast, lightweight blockchain interactions, reducing test flakiness and runtime.
  • Parallelization: Designed for parallel test execution, avoiding port and state conflicts.
  • Automation: Automates complex setup steps, allowing focus on test logic rather than blockchain plumbing.
  • Integration: Seamlessly integrates with Playwright and the broader @coinbase/onchaintestkit E2E testing ecosystem.

Key Features

Architecture

Components

ProxyDeployer

A utility class to manage the deterministic deployment proxy contract, which enables CREATE2-based deployments at predictable addresses. The proxy ensures that contracts can be deployed to the same address across different test runs.

SmartContractManager

A high-level manager for deploying contracts (using CREATE2), executing contract calls, and orchestrating contract state for tests. It handles the complexity of contract deployment and interaction, allowing you to focus on test logic.

How It Works

Use Cases

Testing DeFi Protocols

Deploy and test complex DeFi protocols with deterministic addresses:
await scm.setContractState({
  deployments: [
    {
      name: 'Token',
      args: ['USDC', 'USDC', 6],
      salt: generateSalt('usdc'),
      deployer: accounts[0],
    },
    {
      name: 'LiquidityPool',
      args: [tokenAddress],
      salt: generateSalt('pool'),
      deployer: accounts[0],
    },
  ],
  calls: [
    {
      target: tokenAddress,
      functionName: 'mint',
      args: [poolAddress, parseUnits('1000000', 6)],
      account: accounts[0],
    },
  ],
}, node);

Testing NFT Marketplaces

Set up NFT contracts and marketplace listings:
const nftAddress = await scm.deployContract({
  name: 'NFTCollection',
  args: ['My NFTs', 'NFT'],
  salt: generateSalt('nft'),
  deployer: artist,
});

const marketAddress = await scm.deployContract({
  name: 'Marketplace',
  args: [feeRecipient, 250], // 2.5% fee
  salt: generateSalt('market'),
  deployer: deployer,
});

Best Practices

Always use deterministic salts for reproducible tests. Consider using a salt generation function that includes test names or IDs.
Remember that CREATE2 addresses depend on the bytecode. Contract upgrades or compiler changes will result in different addresses.

Next Steps