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


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);
        //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 to allow adding an already deployed contract so we can interact with it.

1 Like