Search code examples
blockchainethereumsoliditysmartcontracts

Member "transfer" not found or not visible


I have two contract that are separately deployed.

FirstContract.sol

contract FirstContract is ERC721 {
    using Counters for Counters.Counter;
    Counters.Counter public _id;
    address payable _admin;

    constructor(address admin) ERC721("Token Name", "TOKEN") {
        _admin = payable(admin);
    }

    function incrementToken() public {
        _id.increment();
    }
}

SecondContract.sol

import "./FirstContract.sol";

contract SecondContract {
    FirstContract firstContract;

    constructor(address _address) {
        firstContract = FirstContract(_address);
    }

    function increment() external {
        firstContract.incrementToken(); 
    }

    function transferValue(uint value) external {
        firstContract._admin.transfer(value); // error here
    }
}

I'm getting the error:

Member "transfer" not found or not visible after argument-dependent lookup in function () view external returns (address payable).

I'm not sure why this error occurs because the function is marked public here.


Solution

  • FirstContract derives from ERC721 but your link at the end of the question points at ERC20 contract. So the definition of transfer() in the ERC20 is not relevant in this context.


    firstContract._admin.transfer(value);
    

    This snippet is attempting to use the Solidity native transfer() member of the address payable type, i.e. transfer ETH (not an ERC20 token). But it's failing because the firstContract._admin is not visible.

    It would be visible if the FirstContract was a parent of SecondContract, as well as if the _admin had public visibility modifier. Since it doesn't have any visibility modifier specified, the default value internal is used.

    1. Give the _admin property the public visibility modifier
    address payable public _admin;
    

    Then you need to change the call of firstContract._admin (property) to firstContract._admin() (function) because of the way that the compiler handles public properties in external contracts. See more about autogenerated getter functions in the docs.

    1. Change the call from the property to the function
    firstContract._admin().transfer(value);
    

    Mind that the SecondContract doesn't hold any ETH and currently has no way to receive any. So if you were trying to transfer() more than the current balance of the SecondContract (which is 0), the transaction would revert.

    For testing purposes, you can add the payable modifier to the constructor and send it some ETH with the deployment transaction, so that you can test the transferValue().

    constructor(address _address) payable {