Test cases failing due to custom modifier - solidity

I am new to solidity and trying to code it on my own before using open Zepplin plugins.
Here is the contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.5;
contract LeagueWinners {
struct Winner {
bool exists;
bool claimed;
uint256 reward;
}
mapping(address=>Winner) public winners;
mapping (address => bool) private AuthAccounts;
modifier onlyAuthAccounts() {
require(AuthAccounts[msg.sender], "Auth: caller is not the authorized");
_;
}
constructor () {
AuthAccounts[_addr_1] = true;
AuthAccounts[_addr_2] = true;
}
function addWinner(address _address, uint256 _amount ) public {
Winner storage winner = winners[_address];
winner.exists = true;
winner.reward = _amount;
}
}
I know we have the Ownable plugin from openzepplin. but just trying with my own modifier as I want 2 users to add winners.
The contract works well. but I am facing issues in writing test cases.
const { expect } = require("chai");
const { ethers } = require("hardhat");
const hre = require("hardhat");
describe("LeagueWinners", function () {
before(async () => {
LeagueWinners = await ethers.getContractFactory("LeagueWinners");
leagueWiners = await LeagueWinners.deploy();
await leagueWiners.deployed();
[owner] = await ethers.getSigners();
});
it("Claim Tokens to be deployed and verify owner", async function () {
expect(await leagueWiners.owner()).to.equal(owner.address);
});
it("Add Winner", async function () {
winner = await leagueWiners
.connect(owner)
.addWinner(
"_addr",
"50000000000000000000"
);
});
});
Add winner is getting failed, not sure how to pass the AuthAccounts. Any guidance will be great help
Error
Error: VM Exception while processing transaction: reverted with reason string 'Auth: caller is not the authorized'

You don't pass any addresses to the constructor here:
constructor () {
AuthAccounts[_addr_1] = true;
AuthAccounts[_addr_2] = true;
}
You need to add the _addr_1 and _addr_2 into the constructor like this:
constructor (address _addr_1, address _addr_2) {
AuthAccounts[_addr_1] = true;
AuthAccounts[_addr_2] = true;
}
The compiler shouldn't let you compile this contract so I'm not sure how it lets you deploy it.
Then in your test you need to pass the arguments like this:
before(async () => {
LeagueWinners = await ethers.getContractFactory("LeagueWinners");
leagueWiners = await LeagueWinners.deploy(addr1, addr2); // HERE
await leagueWiners.deployed();
[owner] = await ethers.getSigners();
});

After spending a few days, made a solution in this way.
constructor () {
AuthAccounts[msg.sender] = true;
AuthAccounts[_addr_1] = true;
AuthAccounts[_addr_2] = true;
}
As i was hardcoding other addresses, it was not possible to get signer info in the test cases. Passing msg.sender in AuthAccounts helped to achieve that and test cases got passed.
Let me know if there are any other work arounds.

Related

Testing the safeMint function

I am trying to successfully write a unit test for my safeMint function below.
Here is my current test:
const assert = require("assert");
const { accounts } = require("#openzeppelin/test-environment");
const ComNFT = artifacts.require("ComNFT");
const { expect } = require("chai");
// describe('ComNFT', () => {
// let accounts;
let comNFT;
beforeEach(async () => {
accounts = await web3.eth.getAccounts();
comNFT = await ComNFT.new({ from: accounts[0] });
//comNFT = await ComNFT.at("");
// console.log(comNFT.address);
});
it('should fail when called by a non-owner account', async () => {
try {
await web3.eth.sendTransaction({
from: accounts[1], // The non-owner account
to: comNFT.address, // The contract address
data: comNFT.methods.safeMint(accounts[1], 'token URI').encodeABI() // The function call and arguments, encoded as ABI
});
assert.fail('Expected error not thrown');
} catch (error) {
assert(error.message.includes('onlyOwner'), 'Expected "onlyOwner" error message not found');
}
});
it('should be able to mint a new token', async () => {
await web3.eth.sendTransaction({
from: accounts[0], // The owner account
to: comNFT.address, // The contract address
data: comNFT.methods.safeMint(accounts[1], 'token URI').encodeABI() // The function call and arguments, encoded as ABI
});
const tokenURI = await comNFT.tokenURI(1); // Assume the token ID is 1
assert.equal(tokenURI, 'token URI');
});
function safeMint(address to, string memory uri) public onlyOwner {
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
}
I am getting a failure message after i run npx truffle test " 1) "before each" hook for "should fail when called by a non-owner account"
0 passing (3s)
1 failing
"before each" hook for "should fail when called by a non-owner account":
TypeError: Assignment to constant variable.
at Context. (test/ComNFT.js:11:12)"
Can someone suggest a way of me successfully writing a test that calls safeMint?
The other test "itshould fail when called by a non-owner account" is also not running?
Thanks
safeMint function has onlyowner modifir, you should call safeMint function with the owner address that is accounts[0] (not accounts[1])

how do I fix this error? ReferenceError: totalSupply is not defined

Im using a course, and I got this error:
ReferenceError: totalSupply is not defined
and Idk what it means by that, cause I never got this error before.
can somebody help me fix this error? thanks
and this is my code:
const { assert } = require("chai");
const KryptoBird = artifacts.require("KryptoBird");
// check for chai
require('chai')
.use(require('chai-as-promised'))
.should()
contract('KryptoBird', (accounts) => {
let contract
before( async () => {
contract = await KryptoBird.deployed()
})
describe('deployment', async() => {
it("deploys successfully", async () => {
const address = contract.address;
assert.notEqual(address, '')
assert.notEqual(address, null)
assert.notEqual(address, undefined)
assert.notEqual(address, 0x0)
})
it('has a name', async() => {
const name = await contract.name()
assert.equal(name, 'KryptoBird')
})
it('has a symbol', async() => {
const symbol = await contract.symbol()
assert.equal(symbol, 'KBIRDZ')
})
})
describe('minting', async ()=> {
it('creates a new token', async ()=> {
const result = await contract.mint('https...1')
const totalSupply = await contract.totalSupply();
assert.equal(totalSupply, 1)
const event = result.logs[0].args
assert.equal(event._from, '0x0000000000000000000000000000000000000000', 'from is the contract')
assert.equal(event._to, accounts[0], 'to is msg.sender')
await contract.mint('https...1').should.be.rejected
})
})
describe('indexing', async()=> {
it('lists KryptoBirdz', async()=> {
// Mint three new tokens
await contract.mint('https...2')
await contract.mint('https...3')
await contract.mint('https...4')
const totalSupply = await contract.totalSupply()
})
let result = []
let KryptoBird
for(i = 1; i <= totalSupply; i++) {
KryptoBird = await contract.kryptoBirdz(i)
result.push(KryptoBird)
}
let expected = ['https...1','https...2','https...3','https...4']
assert.equal(result.join(','), expected.join(','))
})
})
if u need another contract, just tell me, and if u could help me, thanks so much, because I need to finish this today. I've been trying to fix this for 2 hours, it didnt work. so please, if u could help me, it would mean a LOT to me.
here:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import './ERC721Connecter.sol';
contract KryptoBird is ERC721Connecter {
string[] public kryptoBirdz;
mapping(string => bool) _kryptoBirdzExists;
function mint(string memory _kryptoBird) public {
require(!_kryptoBirdzExists[_kryptoBird],
'Error - kryptoBird already exists');
// this is deprecated - uint _id = KryptoBirdz.push(_kryptoBird);
kryptoBirdz.push(_kryptoBird);
uint _id = kryptoBirdz.length - 1;
// .push no logner returns the length but a ref to the added element
_mint(msg.sender, _id);
_kryptoBirdzExists[_kryptoBird]=true;
}
constructor() ERC721Connecter('KryptoBird','KBIRDZ')
{}
}

How to transfer reward token to contract in Test Case

I am working on a sample contract which should provide a reward for the user. The winner gets the reward as Token.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.5;
contract LeagueWinners {
struct Winner {
bool exists;
bool claimed;
uint256 reward;
}
mapping(address=>Winner) public winners;
mapping (address => bool) private AuthAccounts;
IERC20 private rewardTokenAddr;
modifier onlyAuthAccounts() {
require(AuthAccounts[msg.sender], "Auth: caller is not the authorized");
_;
}
constructor (address _rewardTokenAddr) {
rewardTokenAddr = IERC20(_rewardTokenAddr);
AuthAccounts[msg.sender] = true;
AuthAccounts[_addr_1] = true;
AuthAccounts[_addr_2] = true;
}
function addWinner(address _address, uint256 _amount ) public {
Winner storage winner = winners[_address];
winner.exists = true;
winner.reward = _amount;
}
function claimPrize() public {
Winner storage winner = winners[msg.sender];
require(winner.exists, "Not a winner");
require(!winner.claimed, "Winner already claimed");
winner.claimed = true;
rewardTokenAddr.safeTransfer(msg.sender, winner.tokenAmount);
}
}
Test cases is failing when the user is trying to claim the prize. I assume there is no reward token in the contract so its failing.
const { expect } = require("chai");
const { ethers } = require("hardhat");
const hre = require("hardhat");
describe("LeagueWinners", function () {
let rewardTokenAddr = "0xAddr.....";
before(async () => {
LeagueWinners = await ethers.getContractFactory("LeagueWinners");
leagueWiners = await LeagueWinners.deploy(rewardTokenAddr);
await leagueWiners.deployed();
[owner, winner1, winner2, nonwinner] = await ethers.getSigners();
});
it("Claim Tokens to be deployed and verify owner", async function () {
expect(await leagueWiners.owner()).to.equal(owner.address);
});
it("Add Winner", async function () {
winner = await leagueWiners
.connect(owner)
.addWinner(
"winner1.address",
"50000000000000000000"
);
});
it("Confirm Winner Added with proper reward", async function () {
winner = await leagueWiners.winners(winner1.address);
expect(winner.reward).to.equal("50000000000000000000");
});
it("Non winner cannot claim", async function () {
await expect(
leagueWiners.connect(nonwinner).claimReward()).to.be.revertedWith("Not a winner");
});
it("Winner to claim", async function () {
await leagueWiners.connect(winner1).claimPrize();
winner = await leagueWiners.winners(winner1.address);
expect(winner.claimed).to.equal(true);
});
});
Error:
Error: VM Exception while processing transaction: reverted with reason string 'Address: call to non-contract'
This line is causing the error
await leagueWiners.connect(winner1).claimReward();
From the smart contract source that you provided I see that the name of function is claimPrize() not claimReward()
Maybe can work if you change to:
leagueWiners.connect(winner1).claimPrize();

How can I link library and contract in one file?

I am writing smart contract in solidity with hardhat and facing error like missing links for the following libraries.
I think I should do something with sample-nft-test.js, but I don't have any idea what to do.
Does any body know how to solve the problem?
% bin/npm-install
% bin/test
SampleNft
1) "before each" hook for "should be able to return token name by token id"
0 passing (2s)
1 failing
1) SampleNft
"before each" hook for "should be able to return token name by token id":
NomicLabsHardhatPluginError: The contract SampleNft is missing links for the following libraries:
* contracts/SampleNft.sol:TokenTrait
Learn more about linking contracts at https://hardhat.org/plugins/nomiclabs-hardhat-ethers.html#library-linking
at collectLibrariesAndLink (node_modules/#nomiclabs/hardhat-ethers/src/internal/helpers.ts:258:11)
at getContractFactoryFromArtifact (node_modules/#nomiclabs/hardhat-ethers/src/internal/helpers.ts:149:32)
at getContractFactory (node_modules/#nomiclabs/hardhat-ethers/src/internal/helpers.ts:93:12)
at Context.<anonymous> (test/sample-nft-test.js:13:29)
Source codes to reproduce the error
ArakakiEth/SampleNft
sample-nft-test.js
const {
expect,
} = require("chai");
const {
ethers,
} = require("hardhat");
describe("SampleNft", () => {
beforeEach(async () => {
signers = await ethers.getSigners();
const contractFactory = await ethers.getContractFactory("SampleNft", {
signer: signers[0],
});
contract = await contractFactory.deploy();
});
it("should be able to return token name by token id", async () => {
await contract.deployed();
await contract.getName();
});
});
SampleNft.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;
struct Token {
string name;
}
library TokenTrait {
function getName(Token memory token) external pure returns (string memory) {
return token.name;
}
}
contract SampleNft {
using TokenTrait for Token;
uint tokenId;
mapping(uint => Token) private _tokenIdTokenMap;
constructor() {
tokenId = 0;
}
function mint() external {
Token memory token = Token("First Token");
_tokenIdTokenMap[tokenId] = token;
}
function getName() external view returns (string memory) {
Token memory token = _tokenIdTokenMap[tokenId];
return token.getName();
}
}
library TokenTrait {
function getName(Token memory token) external pure returns (string memory) {
return token.name;
}
}
You set the visibility of the function as external.
In this case, you have to deploy the library and then need to specify a link for the library in contract deployment.
The way to fix your issue is to make the function visibility internal.
The library link should be added when deploying contract.
Please replace with the following code for beforeEach part
beforeEach(async () => {
signers = await ethers.getSigners();
const Lib = await ethers.getContractFactory("TokenTrait");
const lib = await Lib.deploy();
await lib.deployed();
const contractFactory = await ethers.getContractFactory("SampleNft", {
signer: signers[0],
libraries: {
TokenTrait: lib.address,
},
});
contract = await contractFactory.deploy();
});

Can't send MATIC to smart contract using ethers library

I deployed my solidity smart contract in polygon testnet(mumbai network).
I tested my smart contract works well in my react frontend except one payable function.
function getLotto() payable external {
require(msg.value == 100000000000000000, "Pay 0.1MATIC");
...
}
here's my frontend code. it will be called when some button is pressed.
of course, I set the signer and abi in the contract.
const handler = () => {
const options = {
gasPrice: 800000,
value: ethers.BigNumber.from("100000000000000000"),
};
await myContract.getLotto(options);
}
I also tried this code but it also didn't work.
value: ethers.utils.parseEther("0.1")
here's the error message.
---edited------
export class MarioNft {
constructor(rpc, contractAddr, abi) {
this.rpc = rpc;
this.contractAddr = contractAddr;
this.abi = abi;
this.isSigned = false;
this.provider = new ethers.providers.JsonRpcProvider(rpc);
this.contract = new ethers.Contract(contractAddr, abi, this.provider);
this.baseUri = null;
}
...
setContractWithSigner(signer) {
this.contract = new ethers.Contract(this.contractAddr, this.abi, signer);
this.isSigned = true;
}
async lotto() {
if (this.isSigned) {
const res = await this.contract.getLotto();
console.log("lotto res: ", res); /////
} else {
///////
console.log("from marioNft class: signer is false");
}
}
}
const lottoHandler = async () => {
if (metaProvider === null) {
alert("Need to connect to MetaMask");
} else {
if (!marioNft.checkIsSigned()) {
marioNft.setContractWithSigner(metaSigner);
}
const options = {
gasPrice: 800000,
value: ethers.BigNumber.from("100000000000000000"),
};
await marioNft.lotto(options);
};
-------------edited2-----------------
const updateEthers = () => {
let tempProvider = new ethers.providers.Web3Provider(window.ethereum);
setMetaProvider(tempProvider);
// this tempSigner will be used in marioNft.setContractWithSigner()
let tempSigner = tempProvider.getSigner();
setMetaSigner(tempSigner);
};
when I print tempSigner
Your async lotto function inside of the MarioNFT class does not pass on the options object:
async lotto() {
if (this.isSigned) {
const res = await this.contract.getLotto();
console.log("lotto res: ", res); /////
} else {
///////
console.log("from marioNft class: signer is false");
}
}
So it should be:
async lotto(options) {
if (this.isSigned) {
const res = await this.contract.getLotto(options);
console.log("lotto res: ", res); /////
} else {
///////
console.log("from marioNft class: signer is false");
}
}