Contract method invalid number of arguments - unit-testing

I just started playing around in Truffle and Solidity and wrote my first basic contract. I also wrote a test but it keeps failing giving me the following message:
Error: Invalid number of arguments to Solidity function
Now, the issue seems quite straight forward, I'm not pushing the right amount af arguments... Except as far as I can see I am.
This is my relevant contract code:
pragma solidity ^0.4.18;
contract FundEth {
mapping (uint => Project) _projects;
struct Project {
uint id;
uint targetWei;
uint targetBlock;
uint balanceWei;
string name;
string description;
bool payedOut;
}
function fund(uint projectId) public payable
{
_projects[projectId].balanceWei += msg.value;
}
function create(uint targetWei, uint blocks, string name, string description)
public
returns (uint)
{
Project memory p = Project({
id: ++_indexCounter,
targetWei: targetWei,
targetBlock: block.number + blocks,
balanceWei: 0,
name: name,
description: description,
payedOut: false
});
_projects[p.id] = p;
return p.id;
}
function getProjectName(uint projectId)
public
view
returns (string)
{
return "FOO";
}
function getProjectBalance(uint projectId)
public
view
returns (uint)
{
return 10000000;
}
...
}
And this is my test code:
const FundEth = artifacts.require("./FundEth.sol");
contract('FundEth', accounts => {
var _id;
var _fundEth;
it("should create a project", () => {
return FundEth.deployed()
.then(fundEth => {
_fundEth = fundEth;
return fundEth.create(1000000000000000000 /* 1 Eth */ , 5, "FOO", "We want to fund this for testing.")
}).then(id => {
_id = id;
return _fundEth.getProjectName.call(_id)
}).then(name => {
assert.equal(name, "FOO", "Has not created a valid project.");
});
});
it("should fund a project", () => {
return FundEth.deployed()
.then(fundEth => {
assert.notEqual(_id, 0);
_fundEth = fundEth;
_fundEth.fund.sendTransaction(_id, { from: accounts[0], value: 10000000 }); << SEEMS TO FAIL HERE.
}).then(() => {
return _fundEth.getProjectBalance.call(_id);;
}).then(balance => {
assert.equal(balance, 10000000, "Balance of test project was not 1 ether.");
});
});
});
I know the contract is not very useful right now, but I don't see why it fails. The full error:
1) Contract: FundEth
should fund a project:
Uncaught Error: Invalid number of arguments to Solidity function
at Object.InvalidNumberOfSolidityArgs (/usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/errors.js:25:1)
at SolidityFunction.validateArgs (/usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/function.js:74:1)
at SolidityFunction.toPayload (/usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/function.js:90:1)
at SolidityFunction.sendTransaction (/usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/function.js:163:1)
at /usr/local/lib/node_modules/truffle/build/webpack:/~/truffle-contract/contract.js:135:1
at new Promise (<anonymous>)
at /usr/local/lib/node_modules/truffle/build/webpack:/~/truffle-contract/contract.js:126:1
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:118:7)

Changing the line to
return _fundEth.fund(_id, { from: accounts[0], value: 10000000 });
seems to fix the issue. However I also needed to remove the assert before the call in order to have a working test.

Related

Assertion error whilst running truffle test

I got an error while running truffle test on a smart contract, can anyone help me with this?
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
})
})
})
this is my code, and when I run it, it gives me an error that I don't understand
this is the error:
1 ) Contract : KryptoBird
minting
creates a new token :
AssertionError : expected promise to be rejected but it was fulfilled with { Object ( tx , receipt , ... ) }
error message
please help me, I've been stuck on this for a day.
heres my smart contract code, if u could help me
thanks a lot! :
// 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);
}
constructor() ERC721Connecter('KryptoBird','KBIRDZ')
{}
}
The problem is not you are not adding entry into the _kryptoBirdzExists mapping. before you mint, you are checking this condition
require(!_kryptoBirdzExists[_kryptoBird],
So after minting, you should be updating the mapping
_kryptoBirdzExists[_kryptoBird]=true;
Your test is not failing because you never added the 'https...1' to the mapping so require stament was passing so you were minting again

Cannot test addition of object to an array of struct

I am trying to test addition of an object to an array of struct like so:
describe("Create Horse", async function() {
it("horses struct array initialized with 0 horses", async function() {
expect(await stallionRace.horses.length).to.equal(0)
})
it.only("creates a horse and adds it to the struct array", async function() {
await stallionRace.createHorse("pony", 1, 1000000000)
expect(stallionRace.horses.length).to.be.greaterThan(0)
})
})
The function in the contract file looks like this:
function createHorse(string memory _name, uint8 _level, uint256 _price) public onlyOwner {
if(horses.length > 0) {
require(keccak256(bytes(horses[horses.length - 1].name)) != keccak256(bytes(_name)), "Another horse with this name already exists");
}
s_horseId = s_horseId + 1;
horses.push(Horse(s_horseId, _price, _level, _name));
emit HorseCreated(s_horseId, _price, _level, _name);
}
I am getting this error while testing with Chai:
AssertionError: expected +0 to be above +0
What could be the issue? Why is it not adding it to the array? Does it have something to do with the 'onlyOwner' modifier?
I tried this too:
const result = await stallionRace.createHorse("pony", 1, 1000000000, {from: this.currentOwner})
await result.wait(1)
But still failed.

TypeError: Descriptor for property round is non-configurable and non-writable - smart contracts

I have multiple cloud functions for my contracts and I am trying to create some unit tests using sinon. However, I have issues when trying to simulate some of the cloud of the functions. I want just to stub them and inside the methods I have called some properties of the smart contracts and want it to return a specified value. In order to understand what am I doing, here is a code snippet of the contract (partial):
contract RoundOracle is Ownable, AccessControl, Pausable {
uint8 public round; // Starts from 1. Updated after each round has finished.
uint8 public constant START = 0;
uint8 public constant END = 1;
event LogSetRoundBounds(uint8 round, uint256 startRound, uint256 endRound);
constructor() {
round = 1;
}
}
Then I want to stub this property round and assert that its value is equal to 2, it gives me the aforementioned error. Here is the code:
describe("NFT Rounds Test", () => {
context("NFT Rounds positive testing", () => {
before(function () {
initializeENV();
});
after(function () {
sandbox.restore();
});
it.only("Test the web3 calls", async() => {
console.log("hit b4 web3");
var web = Web3.getInstance();
console.log("hit after web3")
var newSandbox = sinon.createSandbox();
newSandbox.stub(web.roundOracle, "round").value(2);
expect(web.roundOracle.round).to.equal(2, "not equal to 2");
console.log("finally passed this stubbing ---");
});
});
});
I understand that it is because the property is non-writable, but is it possible somehow to test such scenarios even if they are not writable or configurable?
I figured out how can I make a workaround. First, I removed the objects (cleared them) and then defined new properties and when mocking then, just return the value that is desired.
// remove the desired objects (properties/functions)
removeRef(name, object) {
for (let i = 0; i < this[name].length; i++) {
delete object[this[name][i]];
}
for (let i = 0; i < this[name].length; i++) {
object[this[name][i]] = {};
}
}
// setting the function callback and the properties that I want to be true
mockFunction(o, k, cb) {
Object.defineProperty(o, k, {
enumerable: true,
value: cb,
writable: true,
configurable: true,
});
}
Afterwards, I am just calling the removeRef in the before() hook and then in the test functions calling it like this:
prerequisites.mockFunction(<object-instance>, "<method/property-name>", () => "hash");
the object-instance = the class where the properties and the functions are declared.

Solidity - Array.length is 0 from inherited contract

I'm using Solidity/Truffle. I have two contracts called Category and Post. Post is inherited from Category (Post is Category ).
Contract Category has an array called categories. I have a function in contract Category called isCategoryExists.
When I try to call this function from contract Category, everything is ok. But when I want to call this function from contract Post, I receive false because categories.length is 0.
Category.sol:
ragma solidity >=0.4.22 <0.9.0;
import "./ICategory.sol";
/// #title Create, edit and manage categories
contract Category is ICategory {
// State variables
uint256 currentIndex;
CategoryStruct[] private categories;
mapping(address => CategoryStruct[]) categoriesByUser;
// Modifiers
modifier onlyValidInput(CategoryInputStruct memory _input) {
bytes memory errCode;
bytes memory title = bytes(_input.title);
if (title.length == 0) {
errCode = bytes("invalidTitle");
}
if (errCode.length > 0) {
revert(string(errCode));
}
_;
}
modifier onlyValidIndex(uint256 index) {
if (isCategoryExists(index)) {
_;
} else {
revert("Invalid index.");
}
}
// Constructor
constructor() {
currentIndex = categories.length;
}
// Functions
/// #notice Check if category exists
function isCategoryExists(uint256 index) public view returns (bool) {
if (index >= categories.length) {
return false;
}
return categories[index].isExist;
}
/// #notice This function creates a category.
/// #dev Before this function, the entered data is validated with onlyValidInput modifier.
function createCategory(
CategoryInputStruct memory _input,
LocationStruct memory _location
) external onlyValidInput(_input) returns (bool) {
CategoryStruct memory newCategory = CategoryStruct({
id: currentIndex,
user: msg.sender,
title: _input.title,
isExist: true
});
categories.push(newCategory);
categoriesByUser[msg.sender].push(newCategory);
emit CategoryCreated(currentIndex);
currentIndex++;
return true;
}
}
Post.sol:
pragma solidity >=0.4.22 <0.9.0;
import "../category/Category.sol";
/// #title Create, edit and manage posts
contract Post is Category {
// State variables
uint256 currentPostIndex;
struct PostStruct {
uint256 id;
address user;
string title;
string body;
uint256 categoryId;
}
struct PostInputStruct {
string title;
string body;
uint256 categoryId;
}
PostStruct[] private posts;
mapping(address => uint256[]) postIndexesByUser; // example: 0x01234 => [3, 5, 24, 112, 448]
// Modifiers
modifier onlyValidPostInput(PostInputStruct memory _input) {
bytes memory errCode;
bytes memory title = bytes(_input.title);
bytes memory body = bytes(_input.body);
uint256 categoryId = uint256(_input.categoryId);
if (title.length == 0) {
errCode = bytes("invalidTitle");
} else if (body.length == 0) {
errCode = bytes("invalidBody");
}
if (errCode.length > 0) {
revert(string(errCode));
}
_;
}
// Constructor
constructor() {
currentPostIndex = posts.length;
}
// Functions
/// #notice This function creates a post.
/// #dev Before this function, the entered data is validated with onlyValidPostInput modifier.
function createPost(PostInputStruct memory _input)
external
onlyValidPostInput(_input)
returns (bool)
{
bool isExist = isCategoryExists(_input.categoryId);
PostStruct memory newPost = PostStruct({
id: currentPostIndex,
user: msg.sender,
title: _input.title,
body: _input.body,
categoryId: _input.categoryId
});
posts.push(newPost);
postIndexesByUser[msg.sender].push(currentPostIndex);
currentPostIndex++;
return true;
}
}
I make this call from the JS test environment in Truffle.
What I guess:
I think everything resets every time I run the tests. I'm not sure yet. This is a bit strange and has wasted a few days of my time.
Category.test.js:
const Chance = require("chance");
const Category = artifacts.require("Category");
const chance = new Chance();
contract("Category", (accounts) => {
// Setup 1 account.
const accountOne = accounts[0];
const accountTwo = accounts[1];
let categoryInstance;
before(async () => {
categoryInstance = await Category.deployed();
});
it("should create a category", async () => {
const title = chance.sentence();
// Create category
const categoryCreated = await categoryInstance.createCategory([title], {
from: accountOne,
});
// Get a category by array index
const category = await categoryInstance.getCategoryByIndex(0);
assert.equal(category.id, 0, "There is no data for this index");
assert.equal(category.title, title, "title is not equal");
});
it("Should return true because a category exists", async () => {
const isExists = await categoryInstance.isCategoryExists(0, {
from: accountOne,
});
assert.equal(isExists, true, "Category exists but result is false.");
});
});
Post.test.js
const Chance = require("chance");
const Category = artifacts.require("Category");
const Post = artifacts.require("post");
const chance = new Chance();
contract("Post", (accounts) => {
// Setup 1 account.
const accountOne = accounts[0];
const accountTwo = accounts[1];
let categoryInstance;
let postInstance;
before(async () => {
// Create a sample category
categoryInstance = await Category.deployed();
const title = chance.sentence();
// Create category
const categoryCreated = await categoryInstance.createCategory(
[title, purpose, area],
[polygon],
{
from: accountOne,
}
);
postInstance = await Post.deployed();
});
it("should create a post", async () => {
// Generate sample data
const title = chance.sentence();
const body = chance.paragraph();
const thumbnail = chance.url();
const categoryId = 0;
// Create post
const postCreated = await postInstance.createPost(
[title, body, thumbnail, categoryId],
{
from: accountOne,
}
);
// Get a post by array index
const post = await postInstance.getPostByIndex(0);
assert.equal(post.id, 0, "There is no data for this index");
assert.equal(post.title, title, "title is not equal");
assert.equal(post.categoryId, categoryId, "categoryId is not equal");
});
});
The problem is that you have misunderstood the concept of inheritance. The fact that Post inherits from Category does not mean that instances of Post are sharing the state with instances of Category. What it means is that Post is also a Category, so an object/instance of Post has that same state (variables) and behavior (functions/methods) of a Category contained within itself. In your case, this actually means an object of Post has its own array categories and that one is being checked when you call createPost, and it will of course be empty as you have never added a category using that object. The only way that array can be non-empty is if you call createCategory from postInstance, not from categoryInstance.
P.S.
Just to quickly clarify what I meant by "nothing should be persisted". The test suite should be created in a way that each test is independent, no test should ever depend on the execution of some other test. One should be able to run any test from the suite on its own and have it passing. I initially thought this was the problem, as you did not share the entire code.

Why am I receiving the following Chainlink error: cannot estimate gas

I am trying to follow the Chainlink VRF tutorial found here: https://docs.chain.link/docs/intermediates-tutorial/ with hardhat and am running into this issue when calling the rollDice function:
Error: cannot estimate gas; transaction may fail or may require manual gas limit (error={"reason":"cannot estimate gas; transaction may fail or may require manual gas limit","code":"UNPREDICTABLE_GAS_LIMIT","method":"estimateGas","transaction":{"from":"0x014Da1D627E6ceB555975F09D26B048644382Ac6","maxPriorityFeePerGas":{"type":"BigNumber","hex":"0x9502f900"},"maxFeePerGas":{"type":"BigNumber","hex":"0x9502f90e"},"to":"0x5887946875A01D1BB79d6Fb357BceeA5A0096D2e","data":"0xdd02d9e5000000000000000000000000014da1d627e6ceb555975f09d26b048644382ac6","type":2,"accessList":null}}, tx={"data":"0xdd02d9e5000000000000000000000000014da1d627e6ceb555975f09d26b048644382ac6","to":{},"from":"0x014Da1D627E6ceB555975F09D26B048644382Ac6","type":2,"maxFeePerGas":{"type":"BigNumber","hex":"0x9502f90e"},"maxPriorityFeePerGas":{"type":"BigNumber","hex":"0x9502f900"},"nonce":{},"gasLimit":{},"chainId":{}}, code=UNPREDICTABLE_GAS_LIMIT, version=abstract-signer/5.5.0)
at Logger.makeError (/Users/matt/Desktop/hardhat/randomDay/node_modules/#ethersproject/logger/src.ts/index.ts:225:28)
at Logger.throwError (/Users/matt/Desktop/hardhat/randomDay/node_modules/#ethersproject/logger/src.ts/index.ts:237:20)
at /Users/matt/Desktop/hardhat/randomDay/node_modules/#ethersproject/abstract-signer/src.ts/index.ts:301:31
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async Promise.all (index 7)
I am able to deploy to the Kovan testnet, I was able to verify the contract, and I have sent the contract LINK tokens, but am still running into the issue. The contract can be viewed here: https://kovan.etherscan.io/address/0x7b72d80670512c87605ab8ac7e6113fda9c57de4#code
I am using version 0.8 of the Chainlink Contracts.
RandomDay.sol
pragma solidity ^0.8.9;
import "#chainlink/contracts/src/v0.8/VRFConsumerBase.sol";
contract RandomDay is VRFConsumerBase {
uint256 private constant ROLL_IN_PROGRESS = 42;
bytes32 private s_keyHash;
uint256 private s_fee;
mapping(bytes32 => address) private s_rollers;
mapping(address => uint256) private s_results;
event DiceRolled(bytes32 indexed requestId, address indexed roller);
event DiceLanded(bytes32 indexed requestId, uint256 indexed result);
constructor(address vrfCoordinator, address link, bytes32 keyHash, uint256 fee) VRFConsumerBase(vrfCoordinator, link) {
s_keyHash = keyHash;
s_fee = fee;
}
function rollDice (address roller) public returns (bytes32 requestId) {
require(LINK.balanceOf(address(this)) >= s_fee, "Not enough LINK to pay fee");
require(s_results[roller] == 0, "Already rolled");
requestId = requestRandomness(s_keyHash, s_fee);
s_rollers[requestId] = roller;
s_results[roller] = ROLL_IN_PROGRESS;
emit DiceRolled(requestId, roller);
return requestId;
}
function fulfillRandomness (bytes32 requestId, uint256 randomness) internal override {
uint256 dayOfWeek = (randomness % 7) + 1;
s_results[s_rollers[requestId]] = dayOfWeek;
emit DiceLanded(requestId, dayOfWeek);
}
function weekday (address player) public view returns (string memory) {
require(s_results[player] != 0, "Dice not rolled");
require(s_results[player] != ROLL_IN_PROGRESS, "Roll in progress");
return getWeekdayName(s_results[player]);
}
function getWeekdayName (uint256 id) private pure returns (string memory) {
string[7] memory weekdays = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
return weekdays[id - 1];
}
}
hardhat.config.js
/**
* #type import('hardhat/config').HardhatUserConfig
*/
require("#nomiclabs/hardhat-waffle")
require("#nomiclabs/hardhat-etherscan")
const ALCHEMY_API_KEY = "*************************";
const ROPSTEN_PRIVATE_KEY = "*********************";
module.exports = {
solidity: "0.8.9",
networks: {
kovan: {
url: `https://eth-kovan.alchemyapi.io/v2/${ALCHEMY_API_KEY}`,
accounts: [`0x${ROPSTEN_PRIVATE_KEY}`],
gas: 2700000000,
maxFeePerGas: 30000000000,
}
},
etherscan: {
// Your API key for Etherscan
// Obtain one at https://etherscan.io/
apiKey: "****************************"
}
};
deploy.js
const { ethers } = require("hardhat");
async function main() {
const [deployer] = await ethers.getSigners();
console.log("Deploying contracts with the account:", deployer.address);
console.log("Account balance:", (await deployer.getBalance()).toString());
const Token = await ethers.getContractFactory("RandomDay");
const token = await Token.deploy("0xa36085F69e2889c224210F603D836748e7dC0088", "0xdD3782915140c8f3b190B5D67eAc6dc5760C46E9", "0x6c3699283bda56ad74f6b855546325b68d482e983852a7a82979cc4807b641f4", "100000000000000000");
console.log("Token address:", token.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
quickRun.js
var ethers = require('ethers');
var provider = ethers.providers.getDefaultProvider('kovan');
var address = '0x7b72d80670512c87605aB8aC7E6113Fda9c57de4';
var abi = [{"inputs":[{"internalType":"address","name":"vrfCoordinator","type":"address"},{"internalType":"address","name":"link","type":"address"},{"internalType":"bytes32","name":"keyHash","type":"bytes32"},{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"requestId","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"result","type":"uint256"}],"name":"DiceLanded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"requestId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"roller","type":"address"}],"name":"DiceRolled","type":"event"},{"inputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"},{"internalType":"uint256","name":"randomness","type":"uint256"}],"name":"rawFulfillRandomness","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"roller","type":"address"}],"name":"rollDice","outputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"}],"name":"weekday","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}];
var privateKey = '*******************************';
var wallet = new ethers.Wallet(privateKey, provider);
var contract = new ethers.Contract(address, abi, wallet);
var sendPromise = contract.rollDice('0x014Da1D627E6ceB555975F09D26B048644382Ac6');
sendPromise.then(function(transaction){
console.log(transaction);
});
I believe the addresses are off:
Constructor Arguments of your contract:
-----Decoded View---------------
Arg [0] : vrfCoordinator (address): 0xa36085f69e2889c224210f603d836748e7dc0088
Arg [1] : link (address): 0xdd3782915140c8f3b190b5d67eac6dc5760c46e9
Arg [2] : keyHash (bytes32): 0x6c3699283bda56ad74f6b855546325b68d482e983852a7a82979cc4807b641f4
Arg [3] : fee (uint256): 100000000000000000
Via Chainlinks Docs:
LINK 0xa36085F69e2889c224210F603D836748e7dC0088
VRF Coordinator 0xdD3782915140c8f3b190B5D67eAc6dc5760C46E9
Key Hash 0x6c3699283bda56ad74f6b855546325b68d482e983852a7a82979cc4807b641f4
Fee 0.1 LINK
Switched Link and VRF are probably the culprit.
9 out of 10 is because you passed something wrong in the constructor or something in the contructor is not initialized propertly. this kind of things aren't detected by compiler.