0

I have a main contract deployed that exposes a method mintSubContract:

function mintSubContract(uint256 initialAmount) public returns (address) {
    SubContract sub = new SubContract(msg.sender, address(this));
    address subAddress = address(sub);
    if (initialAmount > 0) {
        _transfer(msg.sender, address(sub), initialAmount);
    }
    _subAddresses.push(address(sub));
    sub.mint(msg.sender, 0);
    return address(sub);
}

When I call this function in Hardhat's ethers like so:

const subContract address = await mainContract.mintSubContract(500);

At first I expected subContract to hold the address. Now because that contract method is not (and can't be) a view, it actually holds a transaction.

Right now this is how I end up getting the address of the deployed sub-contract:

const transaction = await mainContract.mintSubContract(500);
const result = await transaction.wait();

At this point the value in result is this:

{
  "to": "0x0a", /* The main contract address */
  "from": "0x01", /* The sender of the transaction */,
  "more keys": "That are irrelevant at this point for this matter (I think)",
  "events": [
    { /* The transfer event in case there is one */
      "transactionIndex": 0,
      "blockNumber": 18,
      "transactionHash": "0x...",
      "address": "0x...",
      "topics": [ /* ... */ ],
      "data": "0x01e4",
      "logIndex": 0,
      "blockHash": "0x...",
      "args": [
        "0x01", /* The sender of the transaction */
        "0x0b", /* The newly deployed contract */
        {
          "type": "BigNumber",
          "hex": "0x01f4" /* The amount (500) */
        }
      ],
      "event": "Transfer",
      "eventSignature": "Transfer(address,address,uint256)"
    },
    { /* <------ This event has no name! */
      "transactionIndex": 0,
      "blockNumber": 18,
      "transactionHash": "0x...",
      "address": "0x0b", /* The newly deployed contract */
      "topics": [ /* ... */ ],
      "data": "0x",
      "logIndex": 1,
      "blockHash": "0x4569d6a315dad67d218e3dc2055467da7bd69b6cd6c5c49084c11a6976286dd1"
    }
  ]
}

In this scenario we have an initialAmount so we have a Transfer event in that transaction (from ERC20 specs). The other event is what I suppose is the Contract Creation. At this point I'm just guessing. Right now I get the address of the newly created contract like this:

const subContractEvent = result.events.find(e => e.event === undefined); // Get the event with no name
const subContractAddress = subContractEvent.address;

This feels very hacky and I can think of a few ways it could go wrong. So my question is: Is there a better way? If this is the only way, does it even make sense to try to return the address of the subContract from the mintSubContract() method?

Mouradif
  • 289
  • 1
  • 9

1 Answers1

1

There is, indeed, a faster way: use 2 different contract interaction type, one static call (to get the returned value, will not mine the transaction) and one regular transaction (as you were doing, but you can then discard the wait() result):

const address_of_sub = await mainContract.callStatic.mintSubContract(500); //no state change/no gas
await mainContract.mintSubContract(500); //the actual tx

See for instance this discussion in ethers repo: https://github.com/ethers-io/ethers.js/issues/1188

And callStatic ethersjs docs: https://docs.ethers.io/v5/api/contract/contract/#contract-callStatic

This is perhaps more clear in web3js, where you have a send() and call() methods (i.e. my_contract.foo.call(args) and my_contract.foo.send(args)).

DrGorilla.eth
  • 1,276
  • 8
  • 19