How can I link library and contract in one file? - solidity

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();
});

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();

Test cases failing due to custom modifier

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.

How to send wei/eth to contract address? (using truffle javascript test)

I'm trying to send wei/eth to the address of my solidity contract which have an external payable fallback function. My truffle javascript test below doesn't result in the balance of instance.address getting any wei. Is not instance.address the smart contract address receiving wei? Can anyone spot why console.logging the balance results in 0? Or spot what I'm missing?
Thanks!
const TestContract = artifacts.require("TestContract");
contract('TestContract', async (accounts) => {
it('should send 1 ether to TestContract', async () => {
let instance = await TestContract.deployed();
instance.send({from: accounts[1], value: 1000000000000000000});
let balance = await web3.eth.getBalance(instance.address);
console.log('instance.address balance: ' + parseInt(balance));
)}
Since you are not providing the contract in your question, I'm making an assumption here that your contract looks like below.
File path:
./contracts/TestContract.sol
pragma solidity ^0.4.23;
contract TestContract {
// all logic goes here...
function() public payable {
// payable fallback to receive and store ETH
}
}
With that, if you want to send ETH from accounts[1] to the TestContract using JS, here is how to do it:
File path:
./test/TestContract.js
const tc = artifacts.require("TestContract");
contract('TestContract', async (accounts) => {
let instance;
// Runs before all tests in this block.
// Read about .new() VS .deployed() here:
// https://twitter.com/zulhhandyplast/status/1026181801239171072
before(async () => {
instance = await tc.new();
})
it('TestContract balance should starts with 0 ETH', async () => {
let balance = await web3.eth.getBalance(instance.address);
assert.equal(balance, 0);
})
it('TestContract balance should has 1 ETH after deposit', async () => {
let one_eth = web3.toWei(1, "ether");
await web3.eth.sendTransaction({from: accounts[1], to: instance.address, value: one_eth});
let balance_wei = await web3.eth.getBalance(instance.address);
let balance_ether = web3.fromWei(balance_wei.toNumber(), "ether");
assert.equal(balance_ether, 1);
})
})
See my comment in above code to learn more about the differences between .new() and .deployed() keyword in Truffle.
Full source code for my solution can be found here.
Solved! I forgot I had to send a transaction via web3 and eth like this:
web3.eth.sendTransaction({})
Thanks anyway!
You myst send to an address, not an object.
instance.send({from: accounts[1], value: 1000000000000000000});
For using Web3.js v1.4.0 in your truffle test files.
const SolidityTest = artifacts.require('SolidityTest');
contract('SolidityTest', (accounts) => {
let solidityTest;
before(async () => {
solidityTest = await SolidityTest.new();
})
it('Test something', async () => {
// Send 100 wei to the contract.
// `sendEth` is your payable method.
await solidityTest.sendEth({from: accounts[1], value: 100});
// Check account 1 balance.
let acc1Balance = await web3.eth.getBalance(accounts[1]);
// Convert the unit from wei to eth
acc1Balance = web3.utils.fromWei(acc1Balance, 'ether')
console.log('acc1 balance:', acc1Balance)
// Check the contract balance.
let contractBalance = await web3.eth.getBalance(solidityTest.address);
contractBalance = web3.utils.fromWei(contractBalance, 'ether')
console.log('contract balance:', contractBalance)
});
});
Hello you just forgot the await before the instance.send, thus making the get balance call to do not “see” the ether sent, hope it will help future readers