CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-web3-eth-contract

Web3 module to interact with Ethereum smart contracts with TypeScript type safety

Pending
Overview
Eval results
Files

contract-deployment.mddocs/

Contract Deployment

Deploy new smart contracts with constructor arguments and deployment options using the DeployerMethodClass.

Capabilities

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>;
  };
}

Contract Deploy Method

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>;
}

Deployment Return Types

/**
 * 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;

Usage Examples

Basic Contract Deployment

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

Deployment with Event Monitoring

// 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);
}

Gas Estimation for Deployment

// 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
});

Access List for Deployment

// 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
});

Constructor Without Arguments

// 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
});

Complex Constructor Arguments

// 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
});

Error Handling

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;
  }
}

Deployment with Custom Options

// 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"
});

Contract Factory Pattern

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

docs

contract-deployment.md

contract-management.md

encoding-utilities.md

event-handling.md

index.md

method-execution.md

tile.json