Contract Deployment – How to Deploy a Contract from Another Contract Using Hardhat

contract-deploymenthardhathardhat-deploy

I have two contracts, one serves as a contract factory (A) for the other (B). When I try to deploy (B), I don't receive an error, but the contract doesn't get deployed.

contract A {

    function CreateB() public returns (address) {
        B b = new B(msg.sender, address(this));
        return address(b);
    }
}

contract B{

    address public parentA;

    address public owner;

    constructor(
        address OwnerOfB,
        address A,
    ) {
        owner = OwnerOfB;
        parentA = A;
    }
}

Here's the deploy function (I use hardhat local chain)

async function main(){
      const A = await ethers.getContractFactory("A");
      const a = await Planter.deploy();
    
      await planted.deployed();

      const B = await a.CreateTree();
      console.log(B);
    }

Here's what I get as the output:

{hash: '0x0438b599c92a0a13bac6eea048aa4275a52684e4e55642ccfbeae9423eef76bc',
  type: 2,
  accessList: [],
  blockHash: '0x9950bec678b71bee5f52cdbfe34967cf72c89b5e0f3ed4ca0b082843a203dda4',
  blockNumber: 2,
  transactionIndex: 0,
  confirmations: 1,
  from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
  gasPrice: BigNumber { value: "1771848095" },
  maxPriorityFeePerGas: BigNumber { value: "1000000000" },
  maxFeePerGas: BigNumber { value: "2543696190" },
  gasLimit: BigNumber { value: "29021272" },
  to: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
  value: BigNumber { value: "0" },
  nonce: 1,
  data: '0x506e9463',
  r: '0xe79ae3bd10730473eb41ba4fced3d231375605598b83156e9f25ccd4155e8b86',
  s: '0x072d8e6ff0582fa4f11cf2cd89075114033de2159de94926fb25792c143c9bb1',
  v: 0,
  creates: null,
  chainId: 31337,
  wait: [Function (anonymous)]
}

Best Answer

that's right cuz it is transaction (blockchain state has been modified and return value is visible only in EVM). If you want to get address of contract B there are 2 possible solutions that are comming to my mind.

  1. Create a event and emit it at the end of CreateB() and listen for event in your deploy script.
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

contract A2 {
    event ContractBCreated(address contractBAddress); //creates event

    function CreateB() public {
        B b = new B(msg.sender, address(this));
        emit ContractBCreated(address(b)); //allows to read value from event
    }
}

contract B{

    address public parentA;
    address public owner;

    constructor(address _ownerOfB, address _a) {
        owner = _ownerOfB;
        parentA = _a;
    }
}
async function main() {
  const A = await ethers.getContractFactory("A2");
  const a = await A.deploy();
  
  const tx = await a.CreateB();
  const receipt = await tx.wait();
  console.log(receipt.events[0].args.contractBAddress); //read value from event
}
  1. Create return view function that returns address and call it. It will work cuz this is not a transaction (blockchain won't be modified).
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

contract A {
    B contractB;

    function CreateB() public {
        contractB = new B(msg.sender, address(this));
    }

    function getContracBAddress() public view returns(address) {
        return address(contractB);
    }
}

contract B{

    address public parentA;
    address public owner;

    constructor(address _ownerOfB, address _a) {
        owner = _ownerOfB;
        parentA = _a;
    }
}
async function main() {
  const A = await ethers.getContractFactory("A");
  const a = await A.deploy();
  
  await a.CreateB();
  console.log(await a.getContracBAddress());
}