Overview
The SmartContractManager is 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 rather than blockchain plumbing.
Constructor
new SmartContractManager(projectRoot: string)
Creates a new SmartContractManager instance.
Path to the root of the project (used to locate contract artifacts)
Properties
Root directory for artifact lookup
viem public client (initialized in initialize)
viem wallet client (initialized in initialize)
Map of deployed contract addresses to their ABIs
Instance of ProxyDeployer for managing the deployment proxy
Methods
initialize()
async initialize(node: LocalNodeManager): Promise<void>
Initializes viem clients and ensures the proxy is deployed. Must be called before using other methods.
The local node manager instance
deployContract()
async deployContract(deployment: ContractDeployment): Promise<Address>
Deploys a contract using CREATE2 and stores its ABI.
deployment
ContractDeployment
required
Contract deployment configuration
Returns: The deployed contract address
executeCall()
async executeCall(call: ContractCall): Promise<Hex>
Executes a contract function call as a transaction.
Contract call configuration
Returns: The transaction hash
setContractState()
async setContractState(
config: SetupConfig,
node: LocalNodeManager
): Promise<void>
Deploys contracts and executes calls as specified in a setup config. This is useful for setting up complex test scenarios.
Setup configuration with deployments and calls
The local node manager instance
predictContractAddress()
predictContractAddress(
salt: Hex,
bytecode: Hex,
args: readonly unknown[]
): Address
Predicts the address for a CREATE2 deployment without actually deploying.
args
readonly unknown[]
required
Constructor arguments
Returns: The predicted contract address
Contract Artifacts
The SmartContractManager loads contract artifacts from your project’s build directory. It supports:
- Foundry: Looks for artifacts in
out/ directory
- Hardhat: Looks for artifacts in
artifacts/ directory
Artifact Structure
Artifacts should contain:
{
abi: [...], // Contract ABI
bytecode: "0x..." // Contract bytecode
}
Complete Examples
Basic Token Deployment
import { SmartContractManager } from '@coinbase/onchaintestkit/contracts/SmartContractManager';
import { LocalNodeManager } from '@coinbase/onchaintestkit/node/LocalNodeManager';
async function deployToken() {
const node = new LocalNodeManager({ chainId: 31337 });
await node.start();
try {
const scm = new SmartContractManager(process.cwd());
await scm.initialize(node);
// Deploy ERC20 token
const tokenAddress = await scm.deployContract({
name: 'ERC20Token',
args: ['Test Token', 'TEST', 1000000n * 10n ** 18n],
salt: '0x' + '01'.repeat(32),
deployer: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
});
console.log(`Token deployed at: ${tokenAddress}`);
// Mint tokens
await scm.executeCall({
target: tokenAddress,
functionName: 'mint',
args: ['0x70997970C51812dc3A010C7d01b50e0d17dc79C8', 1000n * 10n ** 18n],
account: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
});
} finally {
await node.stop();
}
}
Best Practices
Initialize before use
Always call initialize() before using other methods
Use deterministic salts
Generate salts based on test names or contract purposes for reproducibility
Handle deployment errors
Wrap deployments in try-catch blocks to handle failures gracefully
Clean up resources
Always stop the node after tests complete
Tips and Tricks
Use the predictContractAddress method to get addresses before deployment. This is useful for setting up circular dependencies.
The SmartContractManager keeps track of deployed contracts’ ABIs, making it easier to interact with them later.
Contract addresses depend on bytecode. Changing compiler settings or contract code will result in different addresses.
See Also