Solidity function implementing `public onlyOwner` cannot be called even by the owner - ethereum

I am following along the documentation here: https://docs.alchemyapi.io/alchemy/tutorials/how-to-create-an-nft/how-to-mint-a-nft. And have a smart contract of form:
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
import "#openzeppelin/contracts/utils/Counters.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
contract NFTA is ERC721, Ownable {
using Counters for Counters.Counter;
Counters.Counter public _tokenIds;
mapping (uint256 => string) public _tokenURIs;
mapping(string => uint8) public hashes;
constructor() public ERC721("NFTA", "NFT") {}
function mintNFT(address recipient, string memory tokenURI)
public onlyOwner
returns (uint256)
{
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(recipient, newItemId);
_setTokenURI(newItemId, tokenURI);
return newItemId;
}
/**
* #dev Sets `_tokenURI` as the tokenURI of `tokenId`.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token");
_tokenURIs[tokenId] = _tokenURI;
}
}
When I attempt to estimate the gas cost of minting with this:
const MY_PUBLIC_KEY = '..'
const MY_PRIVATE_KEY = '..'
const ALCHEMY = {
http: '',
websocket:'',
}
const { createAlchemyWeb3 } = require("#alch/alchemy-web3");
const web3 = createAlchemyWeb3(ALCHEMY.http);
const NFTA = require("../artifacts/contracts/OpenSea.sol/NFTA.json");
const address_a = '0x...';
const nft_A = new web3.eth.Contract(NFTA.abi, address_a);
async function mint({ tokenURI, run }){
const nonce = await web3.eth.getTransactionCount(MY_PUBLIC_KEY, 'latest');
const fn = nft_A.methods.mintNFT(MY_PUBLIC_KEY, '')
console.log( 'fn: ', fn.estimateGas() )
}
mint({ tokenURI: '', run: true })
I receive error:
(node:29262) UnhandledPromiseRejectionWarning: Error: Returned error: execution reverted: Ownable: caller is not the owner
Presumably because mintNFT is public onlyOwner. However, when I check Etherscan, the From field is the same as MY_PUBLIC_KEY, and I'm not sure what else can be done to sign the transaction as from MY_PUBLIC_KEY. The easy way to solve this is to remove the onlyOwner from function mintNFT, and everything runs as expected. But suppose we want to keep onlyOwner, how would I sign the transaction beyond what is already written above.
Note I'm using hardHat to compile the contracts and deploying them. That is:
npx hardhat compile
npx hardhat run scripts/deploy.js
=============================================
addendum
The exact code given by alchemy to deploy the mint is:
async function mintNFT(tokenURI) {
const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest'); //get latest nonce
//the transaction
const tx = {
'from': PUBLIC_KEY,
'to': contractAddress,
'nonce': nonce,
'gas': 500000,
'data': nftContract.methods.mintNFT(PUBLIC_KEY, tokenURI).encodeABI()
};
Note in the transaction the from field is PUBLIC_KEY, the same PUBLIC_KEY that deployed the contract, and in this case the nftContract has public onlyOwner specified. This is exactly what I have done. So conceptually who owns this NFT code? On etherscan is it the to address ( the contract address ), or the from address, which is my public key, the address that deployed the contract, and the one that is calling mint, which is now failing with caller is not the owner error.
Search the internet, I see others have encountered this problem here: https://ethereum.stackexchange.com/questions/94114/erc721-testing-transferfrom, for Truffle you can specify the caller with extra field:
await nft.transferFrom(accounts[0], accounts[1], 1, { from: accounts[1] })
Extra parameters is not an option here because I'm using hardhat.

OpenZeppelin's Ownable.sol defines the default owner value as the contract deployer. You can later change it by calling transferOwnership() or renounce the owner (i.e. set to 0x0) by calling renounceOwnership().
The onlyOwner modifier reverts the transaction if it's not sent by the current owner. (see the code)
So you need to call the mintNFT() function from the same address that deployed the contract, because that's the current owner. Or you can change the owner first by calling transferOwnership() (from the current owner address).
Removing the onlyOwner modifier from the mintNFT() function would allow anyone to call the function.

Answering this for anyone else who stumbles across the issue while using the Alchemy tutorial:
In the tutorial, it says to init the contract in your mint method like this:
const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json");
const contractAddress = "0x81c587EB0fE773404c42c1d2666b5f557C470eED";
const nftContract = new web3.eth.Contract(contract.abi, contractAddress);
However, if you attempt to call estimateGas() or encodeABI() it will fail with the onlyOwner error.
The solution is to change the third line to:
const nftContract = new web3.eth.Contract(contract.abi, contractAddress, {
from: PUBLIC_KEY
});
This will set a default value of "From" so that when you call estimateGas() on a mint function marked onlyOwner, it will be able to use that from field to see that its the owner calling the estimateGas.
Took forever to figure this out.

I finally figured it out, the contract does not initialize the way I deploy it. So you have to initialize it after deployment.

Related

Issues using a delegateCall (proxy contract with Solidity) and using variables inside the delegate contract

I've written a simple proxy contract with solidity and I've got an issue with the variables inside the delegate contract. When I delegateCall, all my variables are equal to 0, except if there are constant. Is there any reason for that or am I missing something ?
My proxy contract :
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;
contract Proxy {
mapping(string => address) public strategies;
function addStrategy(string memory id, address implementation) external {
strategies[id] = implementation;
}
function removeStrategy(string memory id) external {
delete strategies[id];
}
function displayVar(string memory strategyId) external {
address strategy = strategies[strategyId];
require(strategy != address(0x0), "Strategy not found..");
(bool success, bytes memory data) = strategy.delegatecall(
abi.encodeWithSignature("displayVar()")
);
}
}
The deleguate contract :
pragma solidity ^0.8.3;
import "hardhat/console.sol";
contract Delegate {
mapping(string => address) public strategies;
address public constant CRV = 0xD533a949740bb3306d119CC777fa900bA034cd52;
address public curve = 0x90E00ACe148ca3b23Ac1bC8C240C2a7Dd9c2d7f5;
address public constant cvx = 0xF403C135812408BFbE8713b5A23a04b3D48AAE31;
address public constant CVX = 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B;
function displayVar() external returns (bool) {
console.log(CRV);
console.log(curve);
console.log(cvx);
console.log(CVX);
}
}
the test with HardHat :
import { Contract, ContractFactory } from "ethers";
import { ethers } from "hardhat";
describe("test via proxy", function () {
let Proxy: ContractFactory, proxy: Contract;
let Delegate: ContractFactory, delegate: Contract;
const stratName = "test";
before(async function () {
Proxy = await ethers.getContractFactory("Proxy");
proxy = await Proxy.deploy();
await proxy.deployed();
Delegate = await ethers.getContractFactory("Delegate");
delegate = await Delegate.deploy();
await delegate.deployed();
await proxy.addStrategy(stratName, delegate.address);
});
it("should display", async function () {
const [owner] = await ethers.getSigners();
await proxy.connect(owner).displayVar(stratName);
});
});
And finally the output is :
0xd533a949740bb3306d119cc777fa900ba034cd52
0x0000000000000000000000000000000000000000
0xf403c135812408bfbe8713b5a23a04b3d48aae31
0x4e3fbd56cd56c3e72c1403e103b45db9da5b9d2b
A quick intro:
When you use delegatecall, you are “using” the targeted contract’s code (in your case Delegate) but keeping the storage of the proxy. In other words, the storage of the proxy is completely independent (that is the purpose of the proxy, to be upgradable and / or to save gas on deployment.
With this in mind, your proxy contract can only use the code of delegate, but maintaining its own storage. But, constant and immutable variables do not occupy a storage slot, they are injected in the bytecode at compile time. That is why your proxy also has them. But all the other variables are defaulted to 0 (depending on the type).
The point of using Proxy contract is to keep the state of different implementation versions in the same contract via the delegatecall. Therefore using constructor is not a safe method inside the implementation contract because its state inside the constuctor is set inside the implementation contract when we deploy the contract: Solidity: Why use Initialize function instead of constructor?
Initializing storage variables is not safe either because those state variables are set inside the implementation contract when we deploy them like the constructor.
// this is not safe
// you should move the assignment inside an initializer function
// this is not set inside the proxy
address public curve = 0x90E00ACe148ca3b23Ac1bC8C240C2a7Dd9c2d7f5;
However, if you have constant and immutable variables, those variables will be inside the bytecode of your contract and your proxy needs those variable, it will point to bytecode and get the code. Using constant and immutable is believed to be safe in this case. However, if you set those variables in V1 and then over time you upgraded to V2,V3 etc. If your V1 had selfdestruct function and you killed the contract in the future, V1's bytecode will be deleted and therefore your proxy will no longer have access to those variables.

How to call a method from a contract that was instantiated from another contract via truffle console?

I'm trying to call a method from a contract that was instantiated from another contract. I use truffle console.
Here are the details: (I think you can gloss over the code, since this question is aimed at using truffle console, not fixing any bugs)
I have a contract called MyCoinSupply.sol:
pragma solidity ^0.8.0;
import "./MyCoin.sol";
contract MyCoinSupply is MyCoin("MyCoin", "MYC") // MyCoin is ERC20
{
constructor() public // gives 1000 tokens to the owner
{
_mint(msg.sender, 1000);
}
function getCoinbase() public returns (address) // for debugging purposes
{
return block.coinbase;
}
function _mintMinerReward() internal // gives 20 tokens to the miner
{
// _mint(block.coinbase, 20); // for some reason, block.coinbase is address 0
}
function _transfer(address from, address to, uint256 value) override internal
{
_mintMinerReward();
super._transfer(from, to, value);
}
}
I instantiate MyCoinSupply.sol within MyCoinDEX.sol:
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./MyCoinSupply.sol";
contract MyCoinDEX
{
IERC20 public token;
event Bought(uint256 amount);
event Sold(uint256 amount);
constructor() public
{
token = new MyCoinSupply();
}
function showSender() public view returns (address) // for debugging purposes
{
return (msg.sender);
}
function buy(uint256 amountTobuy) payable public // send ether and get tokens in exchange; 1 token == 1 ether
{
uint256 dexBalance = token.balanceOf(address(this));
require(amountTobuy > 0, "You need to send some ether");
require(amountTobuy <= dexBalance, "Not enough tokens in the reserve");
token.transfer(msg.sender, amountTobuy);
emit Bought(amountTobuy);
}
function sell(uint256 amount) public // send tokens to get ether back
{
require(amount > 0, "You need to sell at least some tokens");
uint256 allowance = token.allowance(msg.sender, address(this));
require(allowance >= amount, "Check the token allowance");
token.transferFrom(msg.sender, address(this), amount);
payable(msg.sender).transfer(amount);
emit Sold(amount);
}
}
Here's my migration for deploying contracts:
const MyCoinSupply = artifacts.require("MyCoinSupply");
const MyCoinDEX = artifacts.require("MyCoinDEX");
module.exports = function(deployer) {
deployer.deploy(MyCoinSupply);
deployer.deploy(MyCoinDEX);
};
Here's my problem:
When I try to call functions that belong to MyCoinDEX, all works well:
truffle(development)> let instance = await MyCoinDEX.deployed()
undefined
truffle(development)> instance.buy(1)
All is good here.
However, when I try the following I get an error:
truffle(development)> instance.token.balanceOf("0xE1994C1054f9c4171B8ae9a7E7a68F404c2bF829")
evalmachine.<anonymous>:0
instance.token.balanceOf("0xE1994C1054f9c4171B8ae9a7E7a68F404c2bF829")
^
Uncaught TypeError: instance.token.balanceOf is not a function
I Googled this error (I read this solution and this solution), but I didn't really stumble upon a solution. Can someone tell me how can I call token's methods from MyCoinDEX?
Thank you in advance!

Setting a mint price for a contract extending a open zeplin ERC721URIStorage contract

I have the following contract in Solidity that was working up until I added the line
require(msg.value == mintPrice, "Not Enough Ether");
// contracts/NFT.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "#openzeppelin/contracts/utils/Counters.sol";
contract NFT is ERC721URIStorage {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
uint256 mintPrice = 0.025 ether;
constructor() ERC721("NFT", "ITM"){}
function mint(address user, string memory tokenURI)
public
payable
returns (uint256)
{
require(msg.value == mintPrice, "Not Enough Ether");
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(user, newItemId);
_setTokenURI(newItemId, tokenURI);
return newItemId;
}
}
I have the following chai test that fails saying not enough ether, but the address I am using is a hardhat address with tons of ether in it
describe("NFT", function () {
it("Should return a transaction hash, async function", async function () {
const NFT = await ethers.getContractFactory("NFT");
const nft = await NFT.deploy();
await nft.deployed();
expect(await nft.mint('0xf3...fFb92266', "/Users/.../web3/nft-next-minter/public/test.json")).to.have.any.keys('hash');
expect(await nft.tokenURI(1)).to.equal('/Users/.../web3/nft-next-minter/public/test.json');
});
})
I am running npx hardhat test --network localhost
Not quite sure why I am getting this error, any help would be greatly appreciated.
Thanks ahead of time.
await nft.mint(
'0xf3...fFb92266',
"/Users/.../web3/nft-next-minter/public/test.json"
)
This JS snippet doesn't specify any value of the transaction, so it's sent with 0 value by default.
And since the contract is expecting the value to equal mintPrice (0.025 ether), it fails the require() condition, effectively reverting the transaction.
You can specify the value in the overrides param (docs).
await nft.mint(
'0xf3...fFb92266',
"/Users/.../web3/nft-next-minter/public/test.json",
{value: ethers.utils.parseEther('0.025')}
)

What is the use of Uniswap Router Initialization in a Token Contract

I just started on building Tokens using ETH & BSC, this is one statement which I see in many Contracts.
Inside the Constructor method, the Uniswap router is iniliazed probably with the V2 version. What is the use of this?
constructor () public {
_rOwned[_msgSender()] = _rTotal;
IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(0x10ED43C718714eb63d5aA57B78B54704E256024E);
// Create a uniswap pair for this new token
uniswapV2Pair = IUniswapV2Factory(_uniswapV2Router.factory())
.createPair(address(this), _uniswapV2Router.WETH());
// set the rest of the contract variables
uniswapV2Router = _uniswapV2Router;
Why is this initialization required? What is the functionality of this?
Appreciate if someone could help.
Thanks
IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(0x10ED43C718714eb63d5aA57B78B54704E256024E);
This line initializes a pointer to the 0x10ED... address and expects the contract (deployed at the 0x10ED... address) to implement the IUniswapV2Router02 interface.
The interface is defined somewhere with the caller contract source code.
It allows you to execute and call functions defined by the interface instead of building low-level calls. It also allows you to use the returned datatypes instead of parsing the returned binary.
Example:
pragma solidity ^0.8.5;
interface IRemote {
function foo() external view returns (bool);
}
contract MyContract {
IRemote remote;
constructor() {
remote = IRemote(address(0x123));
}
function getFoo() external view returns (bool) {
bool returnedValue = remote.foo();
return returnedValue;
}
}

How to test a contract which is generated by another deployed contract?

Currently, I want to test a contract called B which is generated from another contract Registry by using truffle. For example:
Contract Registry
pragma solidity >=0.4.17;
import "./B.sol";
contract Registry is Owned {
function createContract(
address userAddress)
public
returns(
bool res
)
{
// create a contract B which the owner is the user.
B b = new B(userAddress);
return true;
}
Contract B
pragma solidity >=0.4.17;
contract B {
address private owner;
constructor (
address _owner
)
public
{
owner = _owner;
}
function logData(
bytes32 data,
uint timestamp
)
public
returns(bool res)
{
...
}
...
testing script B.js in truffle/test:
const Registry = artifacts.require('./Registry.sol');
const B = artifacts.require('./B.sol');
const truffleAssert = require('truffle-assertions');
const assert = require('assert');
contract('Registry', (accounts) => {
let registry;
let B;
beforeEach(async () => {
// create a reportSummary contract from registry contract.
registry = await Registry.deployed();
// How can I retrieve the contract B's instance?
});
Because I am a newbie in solidity, thus I cannot find any way to test the function logData or other functions in contract B which depends on the contract Registry. Is there any suitable way or should I change the contract designed? Thanks.
You can check my this answer it has example too about accessing the function from imported contract
Solidity: Can a Parent contract see data updates from a Child contract?
To access the function through web3, just use the instance of Registry contract and you can call the function of Contract B