5

I am new to Solidity and I'm having some trouble when calling a function of a smart contract from another one.

Basically I have smart contract A, which extends ERC20 and the Ownable contract from openzeppelin.

contract contractA is ERC20, Ownable {
    constructor() ERC20("MyToken", "MTK") {
    }

    function mint(address account, uint256 amount) external onlyOwner {
        _mint(account, amount);
    }
}

Then I have smart contract B:

contract contractB is Ownable {
  address contract_a;

  constructor(address contract_address) {
    contract_a = account;
  }

  function mint(address account, uint256 amount) public onlyOwner {
    (bool success, ) = contract_a.call(
      abi.encodeWithSignature("mint(address,uint256)", account, amount)
    );

    require(success, "call failed");
  }
}

Both smart contracts are being deployed by the same user address, and I want that to be the owner of both smart contracts. I firstly deploy contractA, and then deploy contractB using the contractA's address.

When calling the mint function in contractB, success is always false, and I have no idea why this is failing. If I remove the onlyOwner from the mint function in contractA it starts working, so I believe it has something to do with this.

Does anyone know what's wrong here? Thank you in advance.

1 Answer 1

4

You can think of the call from contractB to contractA as an additional "internal transaction", so it still has to adhere to the rules of contractA. In other words, since your mint function can only be called by the owner, and the owner of contractA is not contractB, the transaction fails.

To remedy this, you could create your own modifier that checks if the caller (msg.sender) is either the owner or contractB.

More specifically, the onlyOwner modifier checks that msg.sender is the owner, and msg.sender is the address of the calling account, which in your case is the address of contractB, not the address that signed the original transaction.

3
  • But, wouldn't the call method pass the same msg.sender? Or is there a way of passing that information to contractA, in a way that onlyOwner is truthy?
    – Slimas9
    Jul 25, 2022 at 19:43
  • 1
    No, msg.sender is the caller of the contract, not (necessarily) the signer of the transaction. For your example msg.sender is the address of your account in contractB, but msg.sender in contractA is the address of contractB. Likewise, if contractA called contractX, msg.sender in contractX would be the address of contractA.
    – onbjerg
    Jul 25, 2022 at 19:46
  • Ok, that makes sense, thank you!!
    – Slimas9
    Jul 25, 2022 at 19:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.