Error: missing argument: in Contract constructor - ethereum

Error message: Error: missing argument: in Contract constructor (count=0, expectedCount=1, code=MISSING_ARGUMENT, version=contracts/5.6.2)
contract:
contract KBMarket is ReentrancyGuard {
using Counters for Counters.Counter;
constructor() {
owner = payable(msg.sender);
}
and here is hardhat test.js:
describe("KBMarket", function () {
it("Should Mint And Trade NFTs", async function () {
const Market = await ethers.getContractFactory('KBMarket')
const market = await Market.deploy()
await market.deployed()
const marketAddress = market.adderss
}
}
Thanks in advance.

Your constructor should have an argument:
constructor(uint _feePercent)
In deploy.js,
const market = await Market.deploy(1)
you need to pass an argument here because the marketplace's constructor accepts the _feePercent as an argument.

Related

hardhat & waffle - deploy a contract from an address

I'm trying to test a factory contract using hardhat and waffle. I have a contract called Domain:
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import "hardhat/console.sol";
contract Domain {
string private publicKey;
address[] public children;
constructor(string memory _publicKey) {
console.log("Deploying a domain using public key: ", _publicKey);
publicKey = _publicKey;
}
function getChildren() public view returns (address[] memory){
return children;
}
}
And a factory for deploying this contract:
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import "./Domain.sol";
import "hardhat/console.sol";
contract DomainFactory {
Domain[] private _domains;
function createDomain(
string memory _publicKey
) public returns (address){
Domain domain = new Domain(
_publicKey
);
_domains.push(domain);
return address(domain);
}
function allDomains(uint256 limit, uint256 offset)
public
view
returns (Domain[] memory coll)
{
return coll;
}
}
I have the following tests defined, where this refers to a context object defined in a "world" file (using cucumber.js.
When('the holder of this public key creates a domain', async function () {
this.domain = await this.factory.createDomain('<public_key>');
});
Then('a child of this domain has the name name', async function () {
const children = this.domain.getChildren();
const childrenWithName = children.find((child:any) => {
return child.getNames().some((childName:any) => {
return childName === 'name';
})
})
expect(childrenWithName).to.be.an('array').that.is.not.empty;
});
Ideally in the when step, I could define this.domain as the result of deploying a contract, and thereafter test the methods of the contract I deploy:
// world.ts
import { setWorldConstructor, setDefaultTimeout } from '#cucumber/cucumber'
import {deployContract, MockProvider, solidity} from 'ethereum-waffle';
import {use} from "chai";
import DomainContract from "../../../artifacts/contracts/Domain.sol/Domain.json";
import DomainFactoryContract from "../../../artifacts/contracts/DomainFactory.sol/DomainFactory.json";
import { Domain, DomainFactory } from "../../../typechain-types";
import {Wallet} from "ethers";
use(solidity);
setDefaultTimeout(20 * 1000);
class DomainWorld {
public owner: string
public wallets: Wallet[]
public factory: DomainFactory | undefined
public domain: Domain | undefined
public ready: boolean = false
private _initialized: Promise<boolean>
async deployContractByAddress(address, ...args){
return await deployContract(this.wallets[0], address, ...args);
}
constructor() {
this.wallets = new MockProvider().getWallets();
this.owner = this.wallets[0].address
const that = this
this._initialized = new Promise(async (resolve, reject) => {
try {
that.factory = (await deployContract(that.wallets[0], DomainFactoryContract, [])) as DomainFactory;
that.ready = true
resolve(true)
}catch (err) {
reject(err)
}
})
}
}
setWorldConstructor(DomainWorld);
My problem is, hardhat's deployContract function isn't expecting a contract address, which is what is returned by my DomainFactory's create method. How can I test contracts deployed via my factory if the return value is an address?
I've made a quick hardhat project for you to test it.
Here are the highlights:
The easiest way I find to get this returned valued of the contract offchain (and by offchain in this case, I mean inside your test environment) is by emitting an Event. So, I made the following change to your code.
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import "./Domain.sol";
import "hardhat/console.sol";
contract DomainFactory {
Domain[] private _domains;
event CreatedDomain(address domainAddress);
function createDomain(string memory _publicKey) public returns (address) {
Domain domain = new Domain(_publicKey);
_domains.push(domain);
emit CreatedDomain(address(domain));
return address(domain);
}
function allDomains(uint256 limit, uint256 offset)
public
view
returns (Domain[] memory coll)
{
return coll;
}
}
I just simply emit a CreatedDomain() event containing the deployed Domain address.
One more thing: if I remember correctly, you can only retrieve values direct from function returns offchain, if your function is of type view. Otherwise, you will need to emit the event and then find it later.
In order to test the Domain deployed by the DomainFactory take a look at this test script:
import { expect } from "chai";
import { ethers } from "hardhat";
import { Domain, DomainFactory } from "../typechain";
describe("Domain", function () {
let domainFactory: DomainFactory;
let domain: Domain;
let domainAddress: string;
it("Should deploy a DomainFactory ", async () => {
const DomainFactory = await ethers.getContractFactory("DomainFactory");
domainFactory = await DomainFactory.deploy();
await domainFactory.deployed();
});
it("deploy a Domain using DomainFactory ", async () => {
const tx = await domainFactory.createDomain("public string here");
const rc = await tx.wait();
const event = rc.events?.find((event) => event.event === "CreatedDomain");
const args = event?.args;
if (args) domainAddress = args[0];
});
it("attach an abi interface to the deployed domain", async () => {
const Domain = await ethers.getContractFactory("Domain");
domain = await Domain.attach(domainAddress);
});
it("get data from Domain deployed by DomainFactory ", async () => {
const res = await domain.getChildren();
console.log(res);
});
});
It deploys a DomainFactory then uses the createDomain() method, fetches the deployed address from the functino events, then use it to attach the ABI to the deployed Domain.
Full code here:
https://github.com/pedrohba1/stackoverflow/tree/main/Domain
Anything else related to running it I will be adding in the comments.

getting error when i deploy the NFT with ETH

I am new in NFT, i am trying to create test NFT, when i am trying to deploy that NFT, i am getting this error,insufficient funds for intrinsic transaction cost, even though in my account have 1 ETH balance here i have attached my whole code of it, can anyone please help me, how to resolve this issue ?
MyNFT.sol
//Contract based on [https://docs.openzeppelin.com/contracts/3.x/erc721](https://docs.openzeppelin.com/contracts/3.x/erc721)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
import "#openzeppelin/contracts/utils/Counters.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
import "#openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
contract MyNFT is ERC721URIStorage, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
constructor() ERC721("MyNFT", "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;
}
}
hardhat.config.js
/**
* #type import('hardhat/config').HardhatUserConfig
*/
require('dotenv').config();
require("#nomiclabs/hardhat-ethers");
const { API_URL, PRIVATE_KEY } = process.env;
//console.log(PRIVATE_KEY);
module.exports = {
solidity: "0.8.1",
defaultNetwork: "ropsten",
networks: {
hardhat: {},
ropsten: {
url: API_URL,
accounts: [`0x${PRIVATE_KEY}`]
}
},
}
deploy.js
async function main() {
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)
})
That error is clear. you do not have sufficient funds. This is how you are getting the account information:
const { API_URL, PRIVATE_KEY } = process.env;
I had an issue with webpack once destructuring process.env. Try this
// ASSUMING you pass correct private key here
const PRIVATE_KEY = process.env.PRIVATE_KEY;
console.log the private key.
If it does not get resolved, that means you are not passing the correct PRIVATE_KEY.

Can I make my contract be available to any account?

I am developing a dapp where users can upload photographs and mint them to NFTs. My application uses web3( Alchemy web3), Alchemy, Ropsten and Metamask. I also use Hardhat in order to deploy my contract with the command:
npx hardhat run --network ropsten scripts/deploy.js
First of all I have installed Metamask in my browser(Firefox) and I have created my wallet. I have also used a Ropsten Faucet in order to get some ether. In order to deploy my contract I use my account's private key in hardhat.config.js. The problem is that in this way only my account is able to use the contract. My application is supposed to accept multiple users, each with their own Metamask wallet or configuration, performing their own transactions.
Therefore, can I deploy or change my contract so that it can be used by any user and not just the one who deployed it?
Here is my contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
import "#openzeppelin/contracts/utils/Counters.sol";
contract NFTminter is ERC721, Ownable {
using Counters for Counters.Counter;
mapping (uint256 => string) private _tokenURIs;
mapping(string => uint256) private hashes;
Counters.Counter private _tokenIdCounter;
constructor() ERC721("MyToken", "PcI") {
}
function _baseURI() internal pure override returns (string memory) {
return "ipfs://";
}
function safeMint(address to, string memory metadataURI) public onlyOwner returns (uint256) {
require(hashes[metadataURI] != 1 , "This metadataURI already exists.");
hashes[metadataURI] = 1;
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(to, tokenId);
_setTokenURI(tokenId, metadataURI);
return tokenId;
}
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token");
_tokenURIs[tokenId] = _tokenURI;
}
}
Here is my hardhat.config.js:
require("#nomiclabs/hardhat-waffle");
const { MNEMONIC, ALCHEMY_HTTP } = require("./alchemy_secrets.json");
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
const accounts = await hre.ethers.getSigners();
for (const account of accounts) {
console.log(account.address);
}
});
module.exports = {
networks: {
ropsten: {
url: ALCHEMY_HTTP,
accounts: ['PRIVATEKEYGOESHERE']
},
},
solidity: "0.8.4",
};
And here is the code which calls the contract and sends the transaction:
import Web3 from "web3";
import CONTRACT_ABI from "../ethContractABI";
import { AbiItem } from "web3-utils";
import {
ROPSTEN_CONTRACT_ADDRESS,
NFT_STORAGE_KEY,
ALCHEMY_API_KEY,
} from "../constants";
import { NFTStorage } from "nft.storage";
import { Contract } from "web3-eth-Contract";
import { AlchemyWeb3, createAlchemyWeb3 } from "#alch/alchemy-web3";
declare global {
interface Window {
ethereum: any;
web3: Web3;
}
}
interface postcardReturn {
ipfsLink?: string | undefined;
tokenID?: number | undefined;
errorMessage?: string | undefined;
}
async function convertToNft(
imageToUpload: File,
etherAddress: string,
privateKey: string
): Promise<postcardReturn> {
try {
await CreateWeb3Object();
const web3 = createAlchemyWeb3(ALCHEMY_API_KEY);
const metadata = await GetNFTmetadata(imageToUpload);
const NFTminter = createNftContract(web3, etherAddress);
//CheckIfTokenExists(NFTminter, metadata, etherAddress);
const receipt = await mintToken(
NFTminter,
metadata,
etherAddress,
web3,
privateKey
);
return {
ipfsLink: metadata.data.image.href,
tokenID: receipt!.events!.Transfer.returnValues.tokenId,
};
} catch (error: any) {
return returnError(error);
}
}
async function GetNFTmetadata(imageToUpload: File) {
const client = new NFTStorage({ token: NFT_STORAGE_KEY });
const metadata = await client.store({
name: "From: User",
description: "IMage to be converted to nft",
image: imageToUpload,
});
return metadata;
}
async function CreateWeb3Object() {
if (window.ethereum) {
try {
const enable = window.ethereum.enable();
return;
} catch (error) {
console.log(error);
}
}
}
async function CheckIfTokenExists(
NFTminter: Contract,
metadata: any,
etherAddress: string
) {
const check = await NFTminter.methods
.safeMint(etherAddress, metadata.url)
.estimateGas((error: any, gasAmount: any) => {
if (error) {
console.error(error);
return "An error has occured";
}
});
}
function createNftContract(web3: any, etherAddress: string) {
const NFTminter = new web3.eth.Contract(
CONTRACT_ABI,
ROPSTEN_CONTRACT_ADDRESS
);
return NFTminter;
}
async function mintToken(
NFTminter: Contract,
metadata: any,
etherAddress: string,
web3: AlchemyWeb3,
privateKey: string
) {
const nonce = await web3.eth.getTransactionCount(etherAddress, "latest");
const tx = {
from: etherAddress,
to: ROPSTEN_CONTRACT_ADDRESS,
nonce: nonce,
gas: 2000000,
maxPriorityFeePerGas: 1999999987,
data: NFTminter.methods
.safeMint(etherAddress, metadata.data.image.href)
.encodeABI(),
};
const signedTx = await web3.eth.accounts.signTransaction(tx, privateKey);
web3.eth.sendSignedTransaction(
signedTx.rawTransaction!
).then(console.log)
.catch(console.log);
const transactionReceipt = await web3.eth.sendSignedTransaction(
signedTx.rawTransaction!
);
console.log(transactionReceipt);
return transactionReceipt;
}
function returnError(error: any) {
if (error.message.includes("Internal JSON-RPC error."))
return {
errorMessage: "Internal JSON-RPC error.",
};
return {
errorMessage: error.message,
};
}
export default convertToNft;
The privateKey variable is given by the user before he clicks the upload button. If I change the account that I use, the exception is thrown in this line:
const signedTx = await web3.eth.accounts.signTransaction(tx, privateKey);
The exception says that the Caller is not the contract owner. I think that what I described as the problem is the issue, because I redeployed my contract with the Metamask account that I use now and I can normally send and sign transactions. However, If I change the account I receive an error.
Your safeMint function has onlyOwner modifier. That's why only contract deployer can use it.

is not contract address during truffle test

I have a stupid smart contract like this:
pragma solidity ^0.4.24;
contract ProdottoFactory {
function foo() view returns(string nome){
return "foo";
}
}
And I want to test it with chai
var Prodotto = artifacts.require("ProdottoFactory");
expect = require("chai").expect;
contract("descrizione primo test", function () {
describe("test 2", function () {
it("blablabla", function () {
return Prodotto.new().then(
istance => {
prodottoContract = istance;
}
)
})
})
})
contract("descrizione primo test2", function () {
describe("test 2 2", function () {
it("blablabla2",function () {
return prodottoContract.foo().then(function (res) {
expect(res.toString()).to.be.equal("foo")
})
})
})
})
When I run the command
truffle test
I have this error
Error: Attempting to run transaction which calls a contract function, but recipient address 0xe8f29e5c4ca41c5b40ed989439ddeae4d9384984 is not a contract address
truffle.js
module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: 7545, // Ganache GUI
network_id: "*" // Match any network id
}
}
};
contracts/ProdottoFactory.sol
pragma solidity ^0.4.24;
contract ProdottoFactory {
function foo() pure public returns(string nome){
return "foo";
}
}
test/ProdottoFactory.js
var pf = artifacts.require("ProdottoFactory");
contract('ProdottoFactory', function(accounts) {
var pfInstance;
before(function() {
return pf.new()
.then(function(instance) {
pfInstance = instance;
});
});
it("should return foo", function() {
return pfInstance.foo.call()
.then(function(str) {
assert.equal(str, "foo");
});
});
});
I made 2 small changes in your contract:
I added public keyword. It's good practice to always define the visibility of your function.
I replaced view to pure. When you are not reading from blockchain/state variable, use pure. More info can be found inside the docs here.
FYI, you don't have to require chai or mocha library. It's already there when you init a Truffle project using truffle init command. The before keyword is part of Mocha library. You can read more about it here.
Lastly, if you want to know the differences between new and deployed keyword in Truffle, read my thread here.

Sending a transaction fails

I am using web3.js v1.0 with solidity ^0.4.17 with Ganache v1.1.0. I am trying to call a send transaction and it fails with the error message below.
Returned error: Error: Error: [ethjs-query] while formatting outputs
from RPC 'undefined' for method 'sendRawTransaction' Error:
[ethjs-format] hex string 'undefined' must be an alphanumeric 66 utf8
byte hex (chars: a-fA-F) string, is 0 bytes
MyContract.sol
function createStarCard(string name, uint price) public {
require(msg.sender == owner);
uint starCardId = starCards.push(StarCard(name, price));
starCardIdToOwner[starCardId] = owner;
}
App.js
createStarCard = ({ name, price }) => {
window.web3.eth.getAccounts().then((accounts) => {
this.state.ContractInstance.methods.createStarCard(name, price).send({
from: accounts[0],
gas: 300000,
}).then((receipt) => {
console.log(receipt)
}).catch((err) => {
console.log(err.message) <-- Caught error message
})
})
}
Google search results with the error message pointed me to the following issues, but they weren't helpful in my case:
https://github.com/MetaMask/metamask-extension/issues/1870
https://ethereum.stackexchange.com/questions/23679/callback-contain-no-result-error-error-ethjs-query-while-formatting-outputs
Update: sharing my constructor for App.js
constructor(props) {
super(props)
if (typeof window.web3 !== 'undefined') {
this.web3Provider = window.web3.currentProvider;
} else {
console.log("Use Ganache web3")
this.web3Provider = new Web3.providers.HttpProvider('http://localhost:7545');
}
window.web3 = new Web3(this.web3Provider);
const contractAddress = "0x1bdaf0cd259887258bc13a92c0a6da92698644c0"
const ContractInstance = new window.web3.eth.Contract(Abi, contractAddress);
this.state = {
ContractInstance,
}
}
It looks like the problem is with the Ganache mac app. I solved this by using ganache-cli instead.
I solved this by reinstalling Metamask.