OpenZeppelin CLI not detecting deployed contracts to interact with

Hello guys!

First of all thanks for making this very useful software. I have been continuously impressed by how good the experience is.

:computer: Environment
Node: v12.16.1
openZeppelin CLI: 2.8.0 (for regular deployments)
Starter Kit: GSN Starter
Solc: 0.5.17
Deployment type: regular (non-upgradeable)

I am running into a bit of trouble interacting with a deployed contract. The first level of deployment is fine (using 2.8, npx oz deploy works as expected and I can interact with the contract in interactive mode). The issue is that one of the functions in that contract deploys another contract, and I am having trouble interacting with / sending transactions to that deployed contract. I understand why it might not show up in the interactive mode, but even trying to craft a non-interactive command does not work. I have an event which emits the address of the new contract (and the event fires, providing me with a contract address), but when I try to send a transaction (Where the first address is the contract address, and the second is the sender I want (the first provided account in ganache) :

npx oz call --to 0xcC5f0a600fD9dC5Dd8964581607E5CC0d22C5A78 --method "totalSupply()" --from 0x90F8bf6A479f320ead074411a4B0e79 44Ea8c9C1

to it, I get the error:

Contract at address 0xcC5f0a600fD9dC5Dd8964581607E5CC0d22C5A78 not found.

I have tried to use the npx oz add command to add an instance of the deployed contract to the interactive mode, but I don’t think that’s the correct usage of the command. (EDIT: is this something I could use the Contract Loader for?)

I can’t share my real code unfortunately but this is a miniaturized example (even though it does not make much sense on its own) which shows the same difficulty. I am happy to try to provide more details if there are any questions!

Thanks! (code below)

Interaction Flow:

:1234: Code to reproduce

Factory:


import "./ERC20.sol";

contract ERCFactory {

//----------------------------------------------------------**** EVENTS ****-----------------------------------------------------------//

    // A new ERC20 has been generated
    event NewERC20(address indexed ERC20Address);

//------------------------------------------------------**** CONTRACT STATE ****-------------------------------------------------------//

    mapping(address => address[]) public created;

//--------------------------------------------------------**** FUNCTIONS  ****---------------------------------------------------------//

    function createERC(
        uint256 _initialAmount,
        string memory _name,
        string memory _symbol,
        uint8 _decimals)
        public returns (address _newERC) {

        ERC20 newToken = new ERC20(address(this), _initialAmount, _name, _symbol, _decimals);
        created[msg.sender].push(address(newToken));
        //the factory will own the created tokens. You must transfer them.
        newToken.transfer(msg.sender, _initialAmount);
        emit NewERC20(address(newToken));
        return address(newToken);
    }
}

Deployed Instance:


import "@openzeppelin/contracts-ethereum-package/contracts/GSN/GSNRecipient.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Detailed.sol"; 
import "@openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol";

contract ERC20 is GSNRecipient, ERC20Detailed {

    using SafeMath for uint256;
    mapping (address => uint256) private _balances;
    mapping (address => mapping (address => uint256)) private _allowances;

    uint256 private _totalSupply;
    address public deployerAddress;

    constructor(address _deployerAddress, uint256 supply, string memory name, string memory symbol, uint8 decimals) ERC20Detailed() public {
        _mint(msg.sender, supply);
        deployerAddress = _deployerAddress;
    }


    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view returns (uint256) {
        return _totalSupply;
    }

//--------------------------------------------------------**** STANDARD ERC-20 FUNCTIONS  ****---------------------------------------------------------//


    function balanceOf(address account) public view returns (uint256) {
        return _balances[account];
    }

    function transfer(address recipient, uint256 amount) public returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    function allowance(address owner, address spender) public view returns (uint256) {
        return _allowances[owner][spender];
    }

    function approve(address spender, uint256 amount) public returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
        return true;
    }

    function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
        return true;
    }

    function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
        return true;
    }

    function _transfer(address sender, address recipient, uint256 amount) internal {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
    }

    function _mint(address account, uint256 amount) internal {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(address(0), account, amount);
    }

    function _burn(address account, uint256 amount) internal {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(account, address(0), amount);
    }

    function _approve(address owner, address spender, uint256 amount) internal {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    function _burnFrom(address account, uint256 amount) internal {
        _burn(account, amount);
        _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
    }

    function _beforeTokenTransfer(address from, address to, uint256 amount) internal { }


//---------------------------------------------------------**** STANDARD GSN FUNCTIONS  ****-----------------------------------------------------------//

function _postRelayedCall(bytes memory context, bool success, uint256 actualCharge, bytes32 preRetVal) internal { }

function _preRelayedCall(bytes memory context) internal returns (bytes32) { }

function acceptRelayedCall(
  address relay,
  address from,
  bytes calldata encodedFunction,
  uint256 transactionFee,
  uint256 gasPrice,
  uint256 gasLimit,
  uint256 nonce,
  bytes calldata approvalData,
  uint256 maxPossibleCharge
) external view returns (uint256, bytes memory) {
    bytes memory v;
    return (0, v);
    }
}

1 Like

Hi @doctor-gonzo,

Welcome to the community :wave:

Thanks for the feedback. :pray:

The CLI doesn’t support interacting with contracts that weren’t deployed via the CLI.

You are probably best to use a script to interact with the factory created contracts, as you say, you could use the Contract Loader.

There is an open issue https://github.com/OpenZeppelin/openzeppelin-sdk/issues/1264 to allow adding an already deployed contract so we can interact with it.

1 Like