Web3 module to interact with Ethereum smart contracts with TypeScript type safety
—
Deploy new smart contracts with constructor arguments and deployment options using the DeployerMethodClass.
Handles contract deployment operations with type-safe constructor argument handling.
/**
* Class for deploying contracts with constructor arguments
*/
class DeployerMethodClass<FullContractAbi extends ContractAbi> {
constructor(
parentContext: Web3Context<EthExecutionAPI>,
parentOptions: ContractOptions & { address: undefined },
contractAbi: FullContractAbi
);
/**
* Deploy a new contract instance
* @param options - Deployment options including bytecode and constructor arguments
* @returns Deployment method object with send, estimateGas, and createAccessList
*/
deploy(options: {
/** Contract bytecode (hex string) */
data: Bytes;
/** Constructor arguments (if contract has constructor) */
arguments?: ContractConstructorArgs<FullContractAbi>;
}): {
/** Deploy the contract and return a PromiEvent resolving to Contract instance */
send(options?: PayableTxOptions): ContractDeploySend<FullContractAbi>;
/** Estimate gas required for deployment */
estimateGas(options?: PayableTxOptions): Promise<number>;
/** Create access list for deployment transaction */
createAccessList(options?: PayableTxOptions): Promise<AccessListResult>;
};
}Access deployment functionality through the contract's deploy method.
class Contract<Abi extends ContractAbi> {
/**
* Deploy the contract to the blockchain
* @param deployOptions - Deployment options including bytecode and constructor arguments
* @returns DeployerMethodClass instance for deployment operations
*/
deploy(deployOptions?: {
/** Contract bytecode */
data?: HexString;
/** Alternative to data */
input?: HexString;
/** Constructor arguments */
arguments?: ContractConstructorArgs<Abi>;
}): DeployerMethodClass<Abi>;
}/**
* PromiEvent for contract deployment that resolves to deployed Contract instance
*/
type ContractDeploySend<Abi extends ContractAbi> = Web3PromiEvent<
Contract<Abi>,
SendTransactionEvents<DataFormat>
>;
/**
* Constructor arguments type based on contract ABI
*/
type ContractConstructorArgs<Abi extends ContractAbi> =
Abi extends readonly [infer A, ...any[]]
? A extends AbiConstructorFragment
? ContractMethodInputParameters<A['inputs']>
: never
: never;import { Contract } from "web3-eth-contract";
// Contract ABI with constructor
const abi = [
{
inputs: [
{ name: "_initialValue", type: "uint256" },
{ name: "_name", type: "string" }
],
stateMutability: "nonpayable",
type: "constructor"
},
{
inputs: [],
name: "getValue",
outputs: [{ name: "", type: "uint256" }],
stateMutability: "view",
type: "function"
}
] as const;
// Contract bytecode (normally from compilation)
const bytecode = "0x608060405234801561001057600080fd5b506040516101a03803806101a0833981810160405281019061003291906100b7565b81600081905550806001908051906020019061004f9291906100fe565b50505061027a565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b61007e8161006b565b811461008957600080fd5b50565b60008151905061009b81610075565b92915050565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6100fd826100b4565b810181811067ffffffffffffffff8211171561011c5761011b6100c5565b5b80604052505050565b600061012f610056565b905061013b82826100f4565b919050565b600067ffffffffffffffff82111561015b5761015a6100c5565b5b610164826100b4565b9050602081019050919050565b60005b8381101561018f578082015181840152602081019050610174565b8381111561019e576000848401525b50505050565b60006101b76101b284610140565b610125565b9050828152602081018484840111156101d3576101d26100af565b5b6101de848285610171565b509392505050565b600082601f8301126101fb576101fa6100aa565b5b815161020b8482602086016101a4565b91505092915050565b60008060408385031215610227576102266100fe565b5b600061023585828601610089565b925050602083015167ffffffffffffffff8111156102565761025561010b565b5b610262858286016101e6565b9150509250929050565b610108806102796000396000f3fe";
// Create contract instance (no address yet)
const contract = new Contract(abi);
// Deploy with constructor arguments
const deployedContract = await contract.deploy({
data: bytecode,
arguments: [42, "MyContract"] // _initialValue: 42, _name: "MyContract"
}).send({
from: "0x1234567890123456789012345678901234567890",
gas: 1000000,
gasPrice: "20000000000"
});
console.log("Contract deployed at:", deployedContract.options.address);
// Use the deployed contract
const value = await deployedContract.methods.getValue().call();
console.log("Initial value:", value); // Should be 42// Monitor deployment progress
const deploymentPromiEvent = contract.deploy({
data: bytecode,
arguments: [100, "TestContract"]
}).send({
from: "0x1234567890123456789012345678901234567890",
gas: 1500000
});
deploymentPromiEvent
.on('transactionHash', (hash) => {
console.log('Deployment transaction sent:', hash);
})
.on('receipt', (receipt) => {
console.log('Deployment confirmed in block:', receipt.blockNumber);
console.log('Gas used:', receipt.gasUsed);
})
.on('confirmation', (confirmationNumber, receipt) => {
console.log('Confirmation', confirmationNumber, 'received');
})
.on('error', (error) => {
console.error('Deployment failed:', error);
});
try {
const deployedContract = await deploymentPromiEvent;
console.log('Deployment successful!');
console.log('Contract address:', deployedContract.options.address);
} catch (error) {
console.error('Deployment error:', error);
}// Estimate gas before deployment
const gasEstimate = await contract.deploy({
data: bytecode,
arguments: [42, "MyContract"]
}).estimateGas({
from: "0x1234567890123456789012345678901234567890"
});
console.log('Estimated gas for deployment:', gasEstimate);
// Deploy with estimated gas plus buffer
const deployedContract = await contract.deploy({
data: bytecode,
arguments: [42, "MyContract"]
}).send({
from: "0x1234567890123456789012345678901234567890",
gas: Math.floor(gasEstimate * 1.2) // Add 20% buffer
});// Create access list for deployment (EIP-2930)
const accessList = await contract.deploy({
data: bytecode,
arguments: [42, "MyContract"]
}).createAccessList({
from: "0x1234567890123456789012345678901234567890"
});
console.log('Access list:', accessList.accessList);
console.log('Gas with access list:', accessList.gasUsed);
// Deploy with access list
const deployedContract = await contract.deploy({
data: bytecode,
arguments: [42, "MyContract"]
}).send({
from: "0x1234567890123456789012345678901234567890",
accessList: accessList.accessList,
type: '0x1' // EIP-2930 transaction type
});// Contract with no constructor arguments
const simpleAbi = [
{
inputs: [],
stateMutability: "nonpayable",
type: "constructor"
},
{
inputs: [],
name: "getValue",
outputs: [{ name: "", type: "uint256" }],
stateMutability: "view",
type: "function"
}
] as const;
const simpleContract = new Contract(simpleAbi);
// Deploy without arguments
const deployedSimpleContract = await simpleContract.deploy({
data: simpleBytecode
// No arguments needed
}).send({
from: "0x1234567890123456789012345678901234567890",
gas: 500000
});// Contract with complex constructor
const complexAbi = [
{
inputs: [
{ name: "_addresses", type: "address[]" },
{ name: "_amounts", type: "uint256[]" },
{ name: "_config", type: "tuple",
components: [
{ name: "enabled", type: "bool" },
{ name: "threshold", type: "uint256" }
]
}
],
stateMutability: "nonpayable",
type: "constructor"
}
] as const;
const complexContract = new Contract(complexAbi);
// Deploy with complex arguments
const deployedComplexContract = await complexContract.deploy({
data: complexBytecode,
arguments: [
["0x1111...", "0x2222...", "0x3333..."], // address array
[100, 200, 300], // uint256 array
{ enabled: true, threshold: 50 } // tuple/struct
]
}).send({
from: "0x1234567890123456789012345678901234567890",
gas: 2000000
});async function deployWithErrorHandling() {
try {
// Validate inputs before deployment
if (!bytecode || !bytecode.startsWith('0x')) {
throw new Error('Invalid bytecode format');
}
// Estimate gas first
const gasEstimate = await contract.deploy({
data: bytecode,
arguments: [42, "TestContract"]
}).estimateGas({
from: deployer
});
// Deploy with proper error handling
const deployedContract = await contract.deploy({
data: bytecode,
arguments: [42, "TestContract"]
}).send({
from: deployer,
gas: Math.floor(gasEstimate * 1.3),
gasPrice: await web3.eth.getGasPrice()
});
console.log('Deployment successful:', deployedContract.options.address);
return deployedContract;
} catch (error) {
if (error.message.includes('revert')) {
console.error('Contract deployment reverted:', error.message);
} else if (error.message.includes('out of gas')) {
console.error('Deployment failed due to insufficient gas');
} else {
console.error('Deployment failed:', error.message);
}
throw error;
}
}// Deploy with custom transaction options
const deployedContract = await contract.deploy({
data: bytecode,
arguments: [42, "MyContract"]
}).send({
from: "0x1234567890123456789012345678901234567890",
gas: 1000000,
gasPrice: "30000000000", // 30 gwei
value: "0", // No ether sent (unless constructor is payable)
nonce: await web3.eth.getTransactionCount(deployer),
// EIP-1559 options (alternative to gasPrice)
// maxFeePerGas: "40000000000",
// maxPriorityFeePerGas: "2000000000"
});class ContractFactory {
private contract: Contract<typeof abi>;
private bytecode: string;
constructor(abi: typeof abi, bytecode: string) {
this.contract = new Contract(abi);
this.bytecode = bytecode;
}
async deploy(
constructorArgs: any[],
options: PayableTxOptions
): Promise<Contract<typeof abi>> {
return await this.contract.deploy({
data: this.bytecode,
arguments: constructorArgs
}).send(options);
}
async estimateDeploymentGas(
constructorArgs: any[],
from: string
): Promise<number> {
return await this.contract.deploy({
data: this.bytecode,
arguments: constructorArgs
}).estimateGas({ from });
}
}
// Usage
const factory = new ContractFactory(abi, bytecode);
const deployedContract = await factory.deploy(
[42, "MyContract"],
{ from: deployer, gas: 1000000 }
);Install with Tessl CLI
npx tessl i tessl/npm-web3-eth-contract