Hello guys!
First of all thanks for making this very useful software. I have been continuously impressed by how good the experience is.
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:
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);
}
}