Hello,
I've resumed a project I worked on 7 months ago and I've issue minting NFT on the Goerli network.
Since my old tests were on Rinkeby I had to re-publish the contract but this time I got errors I haven't got last time.
I got Error: Returned error: execution reverted: Ownable: caller is not the owner at Object.ErrorRe… … but what I don't understand is that the signer of the contract is the same that signs the NFT.
Here's my contract code (generated with openzeppelin wizard)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract MyToken is ERC721, ERC721Enumerable, ERC721URIStorage, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIdCounter;
constructor() ERC721("myNFT", "myNFT") {}
function safeMint(address to, string memory uri) public onlyOwner {
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
}
// The following functions are overrides required by Solidity.
function _beforeTokenTransfer(address from, address to, uint256 tokenId)
internal
override(ERC721, ERC721Enumerable)
{
super._beforeTokenTransfer(from, to, tokenId);
}
function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
super._burn(tokenId);
}
function tokenURI(uint256 tokenId)
public
view
override(ERC721, ERC721URIStorage)
returns (string memory)
{
return super.tokenURI(tokenId);
}
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721, ERC721Enumerable)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
}
After this I've done npx hardhat compile
and I got Compiled 14 Solidity files successfully
Then I deployed the contract via npx hardhat run scripts/deploy.js --network goerli
This is the deploy script I used
const MyNFT = await ethers.getContractFactory("myNft")
// Start deployment, returning a promise that resolves to a contract object
const myNFT = await MyNFT.deploy()
await myNFT.deployed()
console.log("Contract deployed to address:", myNFT.address)
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error)
process.exit(1)
})
The contract has the following address on the blockchain
Contract deployed to address: 0x8FB28B7cF48534D4f1A19f47997752dc59B79b13
This is the code I use to mint the NFT (and where you see the ** is where it throws the exception)
require("dotenv").config();
const secretManager = require("./secretManager");
const apiUrl = process.env.API_URL;
const publicKey = process.env.PUBLIC_KEY;
const privateKey = process.env.PRIVATE_KEY;
const gasLimitOffset = process.env.GAS_LIMIT_OFFSET;
const waitTimeout = process.env.WAIT_TIMEOUT;
const { createAlchemyWeb3 } = require("@alch/alchemy-web3");
const web3 = createAlchemyWeb3(apiUrl);
const aws = require('aws-sdk');
exports.mintNFT = async (event) => {
console.log(event);
var contractAddress = await secretManager.handler("Blockchain_Contract_Address");
var blockChainAddress = contractAddress.secrets.BlockChainAddress;
try {
console.log("BlockChainAddress", blockChainAddress);
// var key= web3.eth.accounts.create();
var balance = await web3.eth.getBalance(blockChainAddress);
console.log("Balance", balance);
const nonce = await web3.eth.getTransactionCount("0x294262758f44Df35e856AEb7a2dA9b3830232A12", "latest"); //get latest nonce
console.log("nonce", nonce);
const noncePending = await web3.eth.getTransactionCount("0x294262758f44Df35e856AEb7a2dA9b3830232A12", "pending"); //get latest nonce
console.log("noncePending", noncePending);
var nonceToUse = Math.max(nonce, noncePending);
var metadataUri = event.AssetUri;
var userToken = event.UserToken;
var token = event.Token;
aws.config.region = process.env.AWS_REGION;
//make metadata
let s3 = new aws.S3({ region: process.env.AWS_REGION });
var params = { Bucket: process.env.BUCKET, Key: process.env.KEY };
console.log(params);
const fileContent = (await (s3.getObject(params).promise())).Body.toString('utf-8')
console.log(fileContent);
var contract = JSON.parse(fileContent);
var nftContract = new web3.eth.Contract(contract.abi, "0x8FB28B7cF48534D4f1A19f47997752dc59B79b13");
console.log("NFT");
var gasEstimate = await nftContract.methods.safeMint(publicKey, metadataUri).estimateGas();
// console.log("ci passo");
//var g = await gasEstimate.estimateGas();
//var gasValue = getGasValueWithOffset(gasEstimate);
// console.log("gasEstimate", gasEstimate);
var contractData = nftContract.methods.safeMint(publicKey, metadataUri).encodeABI();
//the transaction
const tx = {
from: publicKey,
to: "0x294262758f44Df35e856AEb7a2dA9b3830232A12",
nonce: nonceToUse,
gas: 30000,
data: contractData
}
try {
var signPromise = await web3.eth.accounts.signTransaction(tx, privateKey);
await web3.eth.sendSignedTransaction(
signPromise.rawTransaction);
console.info("updating Token: " + token);
await updateMintInfomation(token, signPromise.transactionHash, nonceToUse, 'IN_PROGRESS', userToken);
await sleep(waitTimeout); //This is used to wait that the promise has been sent
console.info("The mint function has completed. Waiting for Blockchain response");
} catch (err) {
console.log(err);
throw err;
}
}
catch (errMain) {
console.log(errMain);
}
}
getGasValueWithOffset = (gasEstimate)=>
{
if (gasLimitOffset == null || gasLimitOffset == 0) gasLimitOffset = 1;
var offset = (gasEstimate / 100) * gasLimitOffset;
return Math.round(gasEstimate + offset);
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
getCurrentMaxNonCeFromDynamoDB = async () => {
var docClient = new aws.DynamoDB.DocumentClient();
var params = {
"TableName": process.env.NFT_MINTING_TABLE_NAME,
"IndexName": "Nonce-index",
"ScanIndexForward": false,
"FilterExpression": "#s = :s",
ExpressionAttributeNames: {
"#s": "Status"
},
ExpressionAttributeValues: {
":s": "IN_PROGRESS"
}
};
try {
var res = await docClient.scan(params).promise();
var item = res.Items[0];
if (item == null || item.Nonce == null) return 0;
return item.Nonce + 1;
} catch (error) {
console.error(error);
return 0;
}
};
updateMintInfomation = async (token, hash, nonce, status, userToken) => {
var docClient = new aws.DynamoDB.DocumentClient();
try {
//I update the main table
var timestampUpdate = new Date().toISOString();
var params = {
"TableName": process.env.NFT_MINTING_TABLE_NAME,
Key: { Token: token },
UpdateExpression: "set #s = :status, #ts_up = :update_timestamp, #th= :transaction_hash, Nonce = :nonce",
ExpressionAttributeValues: {
":status": status,
":update_timestamp": timestampUpdate,
":transaction_hash": hash,
":token": token,
":nonce": nonce
},
ExpressionAttributeNames: {
"#s": "Status",
"#ts_up": "UpdateTimestamp",
"#th": "TransactionHash",
"#t": "Token"
},
ConditionExpression: "#t = :token"
};
await docClient.update(params).promise();
console.info("Token " + token + "Inserted with hash " + hash + " with nonce " + nonce + "for the user" + userToken);
//I update the mapping table
} catch (error) {
throw new Error("Error in dynamoDB: " + JSON.stringify(error));
}
}
Do you see any particular issue?
Thanks