In hardhat unit testing how do you assert multiple return values from a contract - solidity

I have this solidity contract function that returns multiple values of multiple types. How do I assert them in my unit tests?
I tried something like
var [x, y] = await contractA.getAll()
etc. but that doesn’t work.

Assuming the Solidity return variables are not named, you can access them using their position indexes.
function getAll() external pure returns (uint8, bool) {
return (1, true);
}
const returnedValues = await contract.getAll(); // [ 1, true ]
const number = returnedValues[0];
const status = returnedValues[1];
And if they are named, you can also access them with object keys.
function getAll() external pure returns (uint8 number, bool status) {
return (1, true);
}
const returnedValues = await contract.getAll(); // [ 1, true, number: 1, status: true ]
const number = returnedValues.number;
const status = returnedValues.status;

Related

Some contract functions runs on hardhat network While others don't

I Got following error while testing my contract
$ npx hardhat test
NftyplayLicense
buyLicense
done!
1) should buy the license successfully
0 passing (2s)
1 failing
1) NftyplayLicense
buyLicense
should buy the license successfully:
Error: invalid address or ENS name (argument="name", value=undefined, code=INVALID_ARGUMENT, version=contracts/5.7.0)
at Logger.makeError (node_modules/#ethersproject/logger/src.ts/index.ts:269:28)
at Logger.throwError (node_modules/#ethersproject/logger/src.ts/index.ts:281:20)
at Logger.throwArgumentError (node_modules/#ethersproject/logger/src.ts/index.ts:285:21)
at /home/bhavesh/Documents/boilerplate/node_modules/#ethersproject/contracts/src.ts/index.ts:123:16
at step (node_modules/#ethersproject/contracts/lib/index.js:48:23)
at Object.next (node_modules/#ethersproject/contracts/lib/index.js:29:53)
at fulfilled (node_modules/#ethersproject/contracts/lib/index.js:20:58)
at processTicksAndRejections (node:internal/process/task_queues:95:5)
Following is the Test that I have written
const { assert, expect } = require("chai")
const { network, ethers, deployments } = require("hardhat")
const { developmentChains } = require("../helper-hardhat-config")
!developmentChains.includes(network.name)
? describe.skip()
: describe("NftyplayLicense", function () {
let nftyplayLicenseContract, nftyPlayLicense, deployer, player, param
beforeEach(async function () {
const accounts = await ethers.getSigners()
deployer = accounts[0]
player = accounts[1]
await deployments.fixture(["all"])
nftyplayLicenseContract = await ethers.getContract("NftyPlayLicensing")
nftyPlayLicense = nftyplayLicenseContract.connect(deployer)
param = {
name: "NftyPlayLicensing",
category: "1",
durationInDays: "1",
licenseType: "1",
nftAddress: "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC",
nonce: 735,
price: "1000000000000000000",
r: "0xb07df588e0674bc28050a58a7f2cfd315f7e0aec136d0ebcf89fdcd9fa8aa928",
s: "0x109a8b6ff70709d2fecdba8b385097f2f979738518e4c444d4cf95b7e2c9621b",
signer: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
tokenId: "1",
v: 27,
}
})
describe("buyLicense", function () {
it("should buy the license successfully", async function () {
nftyPlayLicense.whitelist("0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC")
console.log("done!")
nftyPlayLicense = nftyplayLicenseContract.connect(player)
await expect(
nftyPlayLicense.buyLicense(param, "https://uri.com", {
value: ethers.utils.parseEther("1"),
})
).to.emit("NftyPlayLicensing", "LicenseBought")
const result = await nftyPlayLicense.getExecutedOrder(735)
assert.equal(result, true)
})
})
})
Note that
nftyPlayLicense.whitelist("0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC")
is running fine, not not in case of
nftyPlayLicense.buyLicense(param, "https://uri.com", {
value: ethers.utils.parseEther("1"),
})
I've made order and signature object by metamask and stored it locally and then using that i am calling buyLicense function from contract using that object as params.
this is my contract function:
function buyLicense(OrderTypes.LicenseOrder calldata order, string memory uri) public payable {
require(isOrderExecuted[order.nonce] == false, "Order is already Executed");
require(isCancelled[order.signer][order.nonce] == false, "Listing is Cancelled");
bytes32 orderHash = SignatureVerifier.hashMakerOrder(order, ORDER_TYPE_HASH);
validateMakerOrder(order, orderHash);
uint256 licenseTokenId = mintLicenseToken(msg.sender, uri, order);
isOrderExecuted[order.nonce] = true;
emit LicenseBought(
licenseTokenId,
order.price,
order.category,
order.tokenId,
order.signer,
order.licenseType,
order.nftContract
);
}
But I am unable to run that function

Getting a Runtime error when passing an Integer into a Solidity function that take uint256 as parameter

My Solidity smart contract has this function:
function getPlayerAddress(uint256 index) public view returns(address) {
return s_players[index];
}
And I am trying to pass an Integer as parameter via my frontend React application.
This is what the State looks like:
const [playerIndex, setPlayerIndex] = useState(0)
This is the function where I am trying to pass the value:
const updatePlayer = async () => {
const provider = new ethers.providers.Web3Provider(window.ethereum)
const contract = new ethers.Contract(stallionRunAddress, abi, provider)
**const newPlayer = await contract.getPlayerAddress(playerIndex)**
const newPlayerLevelBI = await contract.ownedHorseLevel(newPlayer)
const newPlayerLevel = parseInt(newPlayerLevelBI)
const newPlayerHorseArr = await contract.ownedHorseName(newPlayer)
const newPlayerHorse = newPlayerHorseArr[0].toString()
setPlayers(...players, {address: newPlayer, level: newPlayerLevel, horse: newPlayerHorse})
}
I get this error:
Unhandled Runtime Error
Error: call revert exception; VM Exception while processing transaction: reverted with panic code 50 [ See: https://links.ethers.org/v5-errors-CALL_EXCEPTION ] (method="getPlayerAddress(uint256)", data="0x4e487b710000000000000000000000000000000000000000000000000000000000000032", errorArgs=[{"type":"BigNumber","hex":"0x32"}], errorName="Panic", errorSignature="Panic(uint256)", reason=null, code=CALL_EXCEPTION, version=abi/5.7.0)
I have tried passing it as a value with 18 decimals (wei format):
playerIndex * 1e18
But it still doesn't fix the error. How do i fix this?
Solidity uint256 integers can be passed by web3js should be BigNumber instead of js integers. Also you can use string as well.

Error: invalid address (argument="address", value=undefined, code=INVALID_ARGUMENT, version=address/5.1.0)

I'm Getting this error when trying to deploy a smart contract with a function:
Error: invalid address (argument="address", value=undefined, code=INVALID_ARGUMENT, version=address/5.1.0) (argument="tokenAddress", value=undefined, code=INVALID_ARGUMENT, version=abi/5.0.7)
Here is my code:
const handlePresale = async (e) => {
e.preventDefault();
const web3 = await getWeb3();
const abiData = SafeHubPresaleAbi;
const contractAddress = "0x4498F943E0a13D70B28e7565CF4E33bF443e6Bf9";
const duration = {
seconds: function (val) {
return val;
},
minutes: function (val) {
return val * this.seconds(60);
},
hours: function (val) {
return val * this.minutes(60);
},
days: function (val) {
return val * this.hours(24);
},
weeks: function (val) {
return val * this.days(7);
},
years: function (val) {
return val * this.days(365);
},
};
const latestTime = new Date().getTime();
const openingTime = latestTime + duration.minutes(1);
const closingTime = openingTime + duration.minutes(10);
const liqAddingTime = closingTime + duration.minutes(5);
let _info = {
address : "0x4498F943E0a13D70B28e7565CF4E33bF443e6Bf9",
tokenPrice : web3.utils.toWei("10", "ether"),
hardCap: web3.utils.toWei("100", "ether"),
softCap: web3.utils.toWei("30", "ether"),
minInv: web3.utils.toWei("0.1", "ether"),
maxInv: web3.utils.toWei("5", "ether"),
openingTime: openingTime,
closingTime: closingTime,
};
let _uniInfo = {
listingPrice: web3.utils.toWei("20", "ether"),
liqTime: liqAddingTime,
lockTime: 365,
precentLock: 25,
};
let _stringInfo = {
listingName: "WER sale",
};
const preslaeContract = await new web3.eth.Contract(
abiData,
contractAddress
);
// console.log(preslaeContract.methods)
preslaeContract.methods
.createPresale(_info,_uniInfo,_stringInfo)
.call((err, result) => {
console.log(result), console.log(err);
});
}
Solidity constructor:
constructor(address _safuFactoryAddress, address _safuDevAddress) public {
require(_safuFactoryAddress != address(0));
require(_safuDevAddress != address(0));
safuFactoryAddress = payable(_safuFactoryAddress);
safuDevAddress = payable(_safuDevAddress);
}
since there are still no answers yet, but over 1k views though, I'll leave here a solution if anyone else is facing the same error.
The solidity constructor in your example expects 2 arguments of type address. This means if you deploy your contract you also need to provide those 2.
As for the deployment:
No matter how you deploy your contract, providing all constructor arguments should solve your problem and your contract should be able to get deployed now.
If you work with truffle, you need to adjust your deployer .../migrations/2_deploy_contracts.js and pass 2 addresses to satisfy the constructor parameters.
var SafeHubPresale = artifacts.require("./SafeHubPresale.sol");
module.exports = function(deployer) {
deployer.deploy(SafeHubPresale, [address1], [address2]);
};
The same applies for test:
let's say:
var myContract = await SafeHubPresale.new(accounts[1], accounts[2], {from: accounts[0], gas: 0});
For those in Remix, select the drop down next to "deploy" and you will need to provide a "to" & "from" address for this to work
In your smart contract, more than one argument may present and cannot fulfill its value when you deploy. When you deploy you take these values and call deploy() it's working correctly.
In your constructor
constructor(address _safuFactoryAddress, address _safuDevAddress)
more than one address value present there you may not provide this value when you deploy.

How to count the number of elements in this particular object

I am making a call to an API and the response is somehow what I expect. However, I want to count the number of elements returned and I can not do it. This is what I think is important from the code.
Call in Vue component
data(){
return {
messages: {}
}
},
loadMessages(){
axios.get("api/messagesmenu")
.then((data) => { this.messages = data.data})
}
Api controller
public function index(){
$messages = Message::all()->where('read_at', NULL);
if(isset($messages)){
foreach($messages as $message){
$from = User::find($message->from_id);
$message->fromPrenom = $from->first_name;
$message->fromNom = $from->last_name;
$message->fromImage = $from->user_image;
}
}else{
$messages = [];
}
return $messages;
}
Type of response from the API
{"3":{"id":560,"from_id":2,"to_id":1,"content":"tgr","created_at":"2019-07-15 16:59:03","read_at":null,"fromPrenom":"abdel1","fromNom":"Hidalgo","fromImage":"user2-160x160.png"}}
I want to count the number of objects I obtain. if (in vue component) I do
this.messages.length
it returns undefined
Try this:
const messages = {"3":{"id":560,"from_id":2,"to_id":1,"content":"tgr","created_at":"2019-07-15 16:59:03","read_at":null,"fromPrenom":"abdel1","fromNom":"Hidalgo","fromImage":"user2-160x160.png"}}
console.log(Object.keys(messages).length) // 1
Or in your code:
...
.then((data) => {
this.messages = data.data
console.log(Object.keys(this.messages).length)
})

How to Improve The axios.spread

The below code i use for doing multiple HTTP calls depending on the studentList.
It works well; however, I think the axios spread is not necessary
export default {
getFee (studentList: { studentId: string }[]) {
if (studentList.length < 1) {
Promise.resolve()
}
let promises = []
for (const student of studentList) {
if (!student.studentId) {
Promise.resolve()
}
var url = `${API_URL}/${student.studentId}`
promises.push(Axios.get(url))
}
return Axios.all(promises)
.then(Axios.spread((...args) => {
// customise the response here
return args
.map(response => response.data)
.map(data => {
// #ts-ignore
data.totalMark = data.markinPhysics + data.markinMaths + data.markinChemistry // total mark sum of marks in differnet discplines
return data
})
}))
.catch(error => {
switch (error.response.status) {
case 400:
console.log('student not found')
break
case 500:
console.log('error invoking')
break
default:
console.log('unknown error')
I have to do multiple network calls in Vue and I am using Axios.
I got it working by axios, all and axios.spread, but I think the code can be improved.
The logic is to do multiple calls for the student list and get the outputs back
Can anyone help?
Axios.all
as well as Promise.all accepts array of promises and returns a new Promise which is resolved whenever all of the given promises are resolved with an array with the result of each promise
e.g.
const promise1 = Promise.resolve('data1');
const promise2 = Promise.resolve('data2');
Promise.all([
promise1,
promise2,
]).then(results => {
// results is an array with 2 elements
console.log(results[0]); // data1
console.log(results[1]); // data2
});
you can use Axios.spread to to assign each result to a variable like this:
Promise.all([
promise1,
promise2,
]).then(Axios.spread(( result1, result2 ) => {
// args is an array with 2 elements
console.log(result1); // data1
console.log(result2); // data2
});
alternatively you can use ES6 Destructuring assignment:
Promise.all([
promise1,
promise2,
]).then(([ result1, result2 ]) => {
// args is an array with 2 elements
console.log(result1); // data1
console.log(result2); // data2
});
Unnecessary Promise.resolve()
Your Promise.resolve() function calls have no effect on the getFee method since you're not returning them
What would my implementation be
async function getFee(studentList) {
try {
const promises = studentList.reduce((acc, student) =>
student.studentId
? acc.concat(Axios.get(`${API_URL}/${student.studentId}`))
: acc
, []);
const responses = await Axios.all(promises);
return responses
.map(response => response.data)
.map(data => ({
// return new object
// with data's properties
// instead of assinging the new ones directly to the data
...data,
// total mark sum of marks in differnet discplines
totalMark: data.markinPhysics + data.markinMaths + data.markinChemistry,
}));
} catch (error) {
switch (error.response.status) {
case 400:
console.log("student not found");
break;
case 500:
console.log("error invoking");
break;
default:
console.log("unknown error");
}
}
}
export default {
getFee
}
Since you're only using args as an array, you could remove axios.spread.
axios.spread() might only be useful in older browsers now that ES2015 introduced its own spread operator. The main purpose of axios.spread() is to expand the result of axios.all() into an argument list, such that you could do:
axios.all(promiseArray).then(axios.spread(function(arg1, arg2, arg3) {
/*...*/
}))
instead of:
axios.all(promiseArray).then(function(args) {
var arg1 = args[0]
var arg2 = args[1]
var arg3 = args[2]
/*...*/
})
ES2015's rest operator does the inverse of axios.spread(), so when you combine them (as seen below), you end up with the result above, as if axios.spread() and the rest operator weren't even used:
axios.all(promiseArray).then(axios.spread(function(...args) {
var arg1 = args[0]
var arg2 = args[1]
var arg3 = args[2]
/*...*/
}))
// or newer syntax:
axios.all(promiseArray).then(axios.spread((...args) => {
const arg1 = args[0]
const arg2 = args[1]
const arg3 = args[2]
/*...*/
}))
To avoid promise chaining and improve readability, I think below can be used.
const [arg1, arg2] = await Promise.all(promises)