ReactJS array of arrays state - reactjs

I'd like to assign an array of arrays in a state.
I found a few elements about modifying an array of arrays, this is not what I want to do.
I'm first building the array of arrays (which won't change beyond construction) then try to set the state, but it stays empty ([]).
Here is the solidity part :
contract Test {
struct Bobject {
address owner;
uint amount;
}
struct Dobject {
uint id;
uint ddate;
string label;
uint quantity;
mapping(uint => Bobject) bobjs;
uint bCount;
}
uint public dCount = 0;
mapping(uint => Dobject) public dobjs;
function getBobj( uint did, uint bid) external view returns(Bobject memory) {
return dobjs[did].bobjs[bid];
}
}
And the react JS useEffect code I'm testing now, the "barray" contains the array of arrays of Bojects, no problem, but after setBobjs( bobjs => barray); bobjs is still [].
const [dobjs, setDobjs] = useState([]);
const [bobjs, setBobjs] = useState([]);
useEffect(() => {
setDobjs( dobjs => []);
setBobjs( bobjs => []);
const dCount = await contract.dCount();
const barray = Array();
for (let i = 1; i <= dCount; i++) {
const dobj = await contract.dobjects(i);
setDobjs(dobjs => [...dobjs, dobj]);
const bCount = dobj.bCount;
const sbarray = Array();
for( let j=1; j <= bCount; j++) {
let nobj = await contract.getBobj(i, j);
sbarray.push( nobj);
}
barray.push( sbarray);
}
setBobjs(bobjs => barray);
}
I came accross another consideration that is not 100% clear to me even after spending the full day in forums :
I chose to query the contract for the Bobjects and implemented a custom getter because the Dobjects I get with contract.dobjects(i); do not contain the "bobjs" mapping member
Maybe there is a better way to get the Bobjects ?
Anyway, any help is appreciated, I have to take a break ;)
Thanks !

Related

Jest not generating values correctly using crypto

I made a function to generate a complex password using window.crypto lib, this work perfectly and return values like jQzPN%c#tr71ie6Dt^C8.
Here is my function :
const genPwd = (length: number): string => {
const regex = /^(?=.*[A-Z])(?=.*[!##$%^&*])(?=.*[0-9]).{8,}$/;
const charset =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~!##$%^&*()_-+={[}]|:;<,>.?/';
let result = '';
let values = new Uint32Array(length);
window.crypto.getRandomValues(values);
for (let i = 0; i < length; i++) {
result += charset[values[i] % charset.length];
}
if (!regex.test(result)) {
console.log('result', result);
return genPwd(length);
} else {
return result;
}
};
But when I try to test this function using Jest, it falls everytime in the last condition because it doesn't match the regex, the console.log shows "AAAAAAAAAAAAAAAAAAAA", like nothing is generating correctly, so it doesn't match the regex and looping.
Here is my test :
it('should generate a x length password', () => {
const mGetRandomValues = jest.fn().mockReturnValueOnce(new Uint32Array(20));
Object.defineProperty(globalThis, 'crypto', {
value: { getRandomValues: mGetRandomValues },
});
const valueToConvert = utils.genPwd(20);
const valueToFind = 20;
expect(valueToConvert).toHaveLength(valueToFind);
expect(mGetRandomValues).toBeCalledWith(new Uint32Array(valueToFind));
});
Does anyone have a solution ?
I have no idea about this issue

call revert exception when calling a view function with Panic code 50

I'm getting an error when calling a view function of my solidity contract from my frontend.
The error is listed in the docs as error 32:
0x32: If you access an array, bytesN or an array slice at an out-of-bounds or negative index (i.e. x[i] where i >= x.length or i < 0).
My contract:
address public owner;
struct FoodItem {
address owner;
string url;
string name;
string originCountry;
}
FoodItem[] public foodItems;
function addFoodItem(
string memory url,
string memory name,
string memory originCountry
) public {
foodItems.push(FoodItem(msg.sender, name, url, originCountry));
}
function getFoodItemsByOwner() public view returns (FoodItem[] memory) {
uint256 itemCount = 0;
for (uint256 i = 0; i < foodItems.length; i++) {
if (foodItems[i].owner == msg.sender) {
itemCount += 1;
}
}
FoodItem[] memory myfoods = new FoodItem[](itemCount);
for (uint256 i = 0; i < foodItems.length; i++) {
if (foodItems[i].owner == msg.sender) {
myfoods[i] = foodItems[i];
}
}
return myfoods;
}
And my function from react:
const getDishesByUser = async () => {
const { ethereum } = window;
if(ethereum) {
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const contract = new ethers.Contract(abiFoodAddress, Food.abi, signer);
const data = await contract.getFoodItemsByOwner();
console.log(data);
setDishesByuser(data)
//router.push('/');
}
};
useEffect(() => {
getDishesByUser();
}, []);
Complete error output in the console:
index.js?dd68:224 Uncaught (in promise) Error: call revert exception; VM Exception while processing transaction: reverted with panic code 50 [ See: https://links.ethers.org/v5-errors-CALL_EXCEPTION ] (method="getFoodItemsByOwner()", data="0x4e487b710000000000000000000000000000000000000000000000000000000000000032", errorArgs=[{"type":"BigNumber","hex":"0x32"}], errorName="Panic", errorSignature="Panic(uint256)", reason=null, code=CALL_EXCEPTION, version=abi/5.7.0)
You can console log foodItems in addFoodItem function to be sure that you actually pushed the items. If not, you may try:
Mapping(address => FoodItem[]) public foodItems;
So you can do:
foodItems[owner].push(FoodItem(msg.sender, name, url, originCountry));
But you have to specify who the owner is

How to display NFTs using Nextjs and Solidity

I recently started studying how to develop web3 dapps and I am building a NFT martketplace.
I have been following some tutorials using solidity and web3/ethers and I managed to display the NFTs of the currently connected wallet.
My next step is to display the NFTs of any given address (not of the connected wallet) as in a gallery. I am trying to build this gallery from the code I have that displays the NFTs of the connected wallet, but I don't fully understand the code, and hence don't know how/what to change.
This is the function to load the NFTs on the page of the connected wallet:
const web3Modal = new Web3Modal({
network: "mainnet",
cacheProvider: true,
});
const connection = await web3Modal.connect()
const provider = new ethers.providers.Web3Provider(connection)
const signer = provider.getSigner()
const marketContract = new ethers.Contract(nftmarketaddress, Market.abi, signer)
const tokenContract = new ethers.Contract(nftaddress, NFT.abi, provider)
const data = await marketContract.fetchMyNFTs()
const items = await Promise.all(data.map(async i => {
const tokenUri = await tokenContract.tokenURI(i.tokenId)
const meta = await axios.get(tokenUri)
let price = Web3.utils.fromWei(i.price.toString(), 'ether');
let item = {
price,
tokenId: i.tokenId.toNumber(),
seller: i.seller,
owner: i.owner,
image: meta.data.image,
}
return item
}))
setNfts(items)
}
nfts will have all the metadata of the NFTs.
The function fetchMyNFTs, defined in the smart contract, is as follows:
uint totalItemCount = _itemIds.current();
uint itemCount = 0;
uint currentIndex = 0;
for (uint i = 0; i < totalItemCount; i++) {
if (idToMarketItem[i + 1].owner == msg.sender) {
itemCount += 1;
}
}
MarketItem[] memory items = new MarketItem[](itemCount);
for (uint i = 0; i < totalItemCount; i++) {
if (idToMarketItem[i + 1].owner == msg.sender) {
uint currentId = idToMarketItem[i + 1].itemId;
MarketItem storage currentItem = idToMarketItem[currentId];
items[currentIndex] = currentItem;
currentIndex += 1;
}
}
return items;
}
So I guess I have two questions:
Do we always need to use the smart contract when fetching NFTs metadata?
How can we display the NFTs of any given account, like zapper.fi or context.app?
I understand that this can be a very broad question but any help or direction to tutorials would be great!
Thanks!
well, most of nft contracts have structures or functions that you can use to get the information you need, like ownerOf that is usually a function that receive the token id and returns the owner, usually the contracts have mappings of the token id to the owner or the address of the owner to an array of the tokens, in this case the contract you are using have a function to return the tokens of the owner but most contracts don't have one, so you will need to understand how all this data is stored and the relation with each other

How is useState() updating my data here? STRANGE

I have data from an movie-api I want to sort based on a select menu, either by year in descending order or title in alphabetical order.
Although Im only updating state in the sort function, not using the variable any where, the data thats already mapped out, and in a different array, updates accordingly. I guess its somehow related to the first change in state for each of the two differen variables?
Any idea how I should solve this correctly and why this is happening?
const sortData = (e) => {
if (e === "year"){
const yearData = data.sort(function(a, b) {
const yearA = a.Year;
const yearB = b.Year;
if (yearA < yearB) {
return -1;
}
if (yearA > yearB) {
return 1;
}
return 0;
});
setYearData(yearData);
}
if (e === "title") {
const titleData = data.sort(function(a, b) {
const titleA = a.Title.toUpperCase();
const titleB = b.Title.toUpperCase();
if (titleA < titleB) {
return -1;
}
if (titleA > titleB) {
return 1;
}
return 0;
});
setTitleData(titleData);
}
}
The sort() method sorts the elements of an array in place, so the data(state) changed without using setState (It may cause some unpredictability happened in the execution)
You can use the sort() method on a copy of the array, so it doesn't affect your state array, I guess this change may work:
use [...data].sort(...) instead of data.sort(...)
Array.sort(), in your case data.sort() updates the original array in addition to returning it. Seems like your data variable is some sort of global that gets changed during sort().

How to convert JSON to URL params string as per my expected value?

I am working on an angular project. For a method getting a response JSON to convert stringify and POST a body to an API is done. Now the problem is for another one function I should send this value as a URL parameter I tried some ways but didn't get expected result. Please find the below codes and help me out. Thanks
Here is my JSON format value
const bodyJSON = [{FullPackageIDs:[11,7],
PartialPkg:[
{PackageID:4,
FormsList:[
{Form_Name:"Form name One"},
{Form_Name:"Form name Two"}]},
{PackageID:6,
FormsList:[
{Form_Name:"Form name Three"},
{Form_Name:"Form name Four"},
{Form_Name:"Form name Five"}
]
}
]
}]
My expected URL string value like below
http://localhost:4200/DownloadPackage?FullPackageIDs[0]=11&FullPackageIDs[1]=7&PartialPkg[0].PackageID=4&PartialPkg[0].FormsList[0].Form_Name=Form name One&PartialPkg[0].FormsList[1].Form_Name=Form name Two&PartialPkg[1].PackageID=6&PartialPkg[0].FormsList[0].Form_Name=Form name Three&PartialPkg[1].FormsList[1].Form_Name=Form name Four&PartialPkg[2].FormsList[2].Form_Name=Form name Five
I tried via forloop but didnt get expected result. Here is the code for what I tried.
for (let i = 0; i < getSelectedId.length; i++) {
fullPackageParams = `${fullPackageParams}FullPackageIDs[${i}]=${getSelectedId[i]}&`;
for (let j = 0; j < getPartialId.length; j++) {
// const getPartialName = this.partialPackage.map(res => res[i].FormsList);
const getPartialName = getPartialId[j].FormsList;
partialPackageIDParams = `${partialPackageIDParams}PartialPkg[${j}].PackageID=${getPartialId[j].PackageID}&`;
for (let index = 0; index < getPartialName.length; index++) {
partialPackageNameParams = `PartialPkg[${index}].FormsList[${index}].Form_Name=${getPartialName[index].Form_Name}&`;
}
}
}
console.log('params for full packages', fullPackageParams + partialPackageIDParams + partialPackageNameParams);
even if it seems kinda strange to me that you need to pass all of those params using query, you can try this
it just uses ES6 map, reduce functions to create your query string
let URLQuery = bodyJSON.map(value => {
const packageIDs = value.FullPackageIDs.map((v, i) => `FullPackageIDs[${i}]=${encodeURIComponent(v)}`);
const partialPkgs = value.PartialPkg.map((v, i) => {
const startKey = `PartialPkg[${i}]`;
return [
`${startKey}.PackageID=${v.PackageID}`
].concat(
v.FormsList.map((v, i) => `${startKey}.FormsList[${i}].Form_Name=${encodeURIComponent(v.Form_Name)}`)
);
}).reduce((arr, v) => {
return arr.concat(v)
}, []);
return packageIDs.concat(partialPkgs);
}).reduce((arr, v) => {
return arr.concat(v);
}, []).join("&");
const fullURL = `https://example.com?${URLQuery}`;

Resources