Returning an Array of Structs Solidity - blockchain

I want to return an array of structs because i want to output all my data.
//SPDX-License-Identifier:MIT
pragma solidity ^0.8.0;
contract MyContract
{
mapping(uint256 => People) dataList;
uint256[] countArr;
struct People{
string name;
uint256 favNum;
}
In this function, i set the data of my struct object and then include it in my mapping.
function setPerson(string memory _name,uint256 _id, uint256 _favNum) public {
dataList[_id]= People(_name,_favNum);
countArr.push(_id);
}
This function here gets me the data of my specified struct object.
function getPerson(uint _id) public view returns(string memory,uint256){
return (dataList[_id].name, dataList[_id].favNum);
}
Now here is the function i think that is causing me trouble because in this function i want to return not the data of a single People object but all of my data and whenever i run this function on my REMIX IDE console it shows me the error: call to MyContract.getAllData errored: VM error: revert. revert
The transaction has been reverted to the initial state.
Note: The called function should be payable if you send value and the value you send should be less than your current balance.
function getAllData()public view returns(People[] memory){
uint256 count = countArr.length;
uint256 i = 0;
People[] memory outputL= new People[](count);
while(count >= 0){
(string memory nam,uint256 num) = getPerson(count-1);
People memory temp = People(nam,num);
outputL[i]=temp;
count--;
i++;
}
return outputL;
}
}
Can anyone help and explain what is wrong and how can i get it running?

This version of the getAllData function works as you expect:
function getAllData() public view returns (People[] memory) {
uint256 count = countArr.length;
People[] memory outputL = new People[](count);
while(count > 0) {
count--;
(string memory nam, uint256 num) = getPerson(countArr[count]);
People memory temp = People(nam, num);
outputL[count] = temp;
}
return outputL;
}
Feel free to ask if you have any questions about the changes.

Related

How to get output list in solidity funct,i have a struct of Drug,and a table named drugs of Drug:i need to get a list/table of drugs having same name

This my code ..if someone can help ..thanks in advance
pragma solidity ^0.5.16;
pragma experimental ABIEncoderV2 ;
contract Users {
struct Drug { // Vehicle = Drug
string name;
uint Qt;
uint Qs;
uint Qr;
}
event DrugAdded(
string name,
uint Qt,
uint Qs,
uint Qr
);
Drug[] public drugs;
uint counter=0;
function addDrug(string memory _name, uint _Qt, uint _Qs, uint _Qr) public {
drugs.push(
Drug(_name, _Qt,_Qs,_Qr)
);
emit DrugAdded(_name, _Qt,_Qs,_Qr);
counter++;
}
function getdrugs(string memory _name) public view returns (Drug[] memory) {
for(uint i=0;i<counter;i++){
if (keccak256(abi.encodePacked((drugs[i].name))) == keccak256(abi.encodePacked((_name)))){
return drugs[i];
}
}
}
}
i get error in last line:
TypeError: Return argument type struct Users.Drug storage ref is not implicitly convertible to expected type (type of first return variable) struct Users.Drug memory[] memory.
return drugs[i];
^------^
Because of how arrays are stored in memory, Solidity is not able to resize memory arrays. So when you're returning a dynamic-length array with an unknown length, you need to find out and declare its length first (see _getCount() in the example below) - and then fill each of its items.
You can find another example of a very similar thing in this answer.
function getdrugs(string memory _name) public view returns (Drug[] memory) {
Drug[] memory drugsToReturn = new Drug[](_getCount(_name));
uint256 index = 0;
for(uint i=0;i<counter;i++) {
if (keccak256(abi.encodePacked((drugs[i].name))) == keccak256(abi.encodePacked((_name)))){
drugsToReturn[index] = drugs[i];
index++;
}
}
return drugsToReturn;
}
function _getCount(string memory _name) private view returns (uint256) {
uint256 count = 0;
for(uint i=0;i<counter;i++) {
if (keccak256(abi.encodePacked((drugs[i].name))) == keccak256(abi.encodePacked((_name)))){
count++;
}
}
return count;
}

Solidity Mapping Iteration

i'm new with solidity and i'm currently struggling iterating through a mapping index to get the values from a Struct
struct Stakes {
uint256 stakeDate;
uint256 stakeAmount;
}
mapping (address => Stakes[]) public addressToStaked;
function stake (uint256 _amount) public {
require(_amount > 0, "You must stake more than 0");
addressToStaked[msg.sender].push(Stakes(block.timestamp, _amount));
}
function returnStaked () public view returns (uint256[][] memory) {
uint256[][] memory result;
for (uint256 i = 0; i < 2; i++){
result[i] = [addressToStaked[msg.sender][i].stakeDate, addressToStaked[msg.sender][i].stakeAmount];
}
return result;
}
As shown on the code, i'm able to push the Struct Values into the mapping, but when i try to read the values as in the returnStaked function i get the error:
TypeError: Type uint256[2] memory is not implicitly convertible to expected type uint256[] memory.
I am not sure but i think you can't iterate through mapping to get values.. if you want to get the stake for the particular address .. below code will work..
function returnStaked (address addressStaked) public view returns (Stakes[] memory) {
return addressToStaked[addressStaked];
}
I will list some links for your references and if i found the way to do it.. i will update here...
https://solidity-by-example.org/app/iterable-mapping/
https://ethereum.stackexchange.com/questions/15337/can-we-get-all-elements-stored-in-a-mapping-in-the-contract
https://programtheblockchain.com/posts/2018/03/09/understanding-ethereum-smart-contract-storage/

How to test for an empty structure being returned via a Solidity function()?

Here is a sample Solidity contract where I have a function which returns an empty struct.
I want to test from a function inside the contract for the empty struct, but I am having trouble figuring out the correct syntax ...
contract MyNFTShop is ERC721 {
struct NFTCardAttributes {
uint256 cardIndex;
string name;
string imageURI;
}
NFTCardAttributes[] defaultCards;
constructor(
string[] memory cardNames,
string[] memory cardImageURIs,
) ERC721("NFT", "NFTC") {
for (uint256 i = 0; i < cardNames.length; i += 1) {
defaultCards.push(
NFTCardAttributes({
cardIndex: i,
name: cardNames[i],
imageURI: cardImageURIs[i],
})
);
NFTCardAttributes memory c = defaultCards[i];
}
}
function checkIfUserHasNFTCard() public view returns (NFTCardAttributes memory) {
uint256 userNFTCardNftTokenId = nftCardHolders[msg.sender];
if(userNFTCardNftTokenId > 0 ) {
return nftCardHolderAttributes[userNFTCardNftTokenId];
} else {
NFTCardAttributes memory emptyStruct;
return emptyStruct;
}
}
function do_something() public () {
if (getAllDefaultCards() == EMPTY) { <---- NEED HELP HERE
// sendMoney to whatever
}
}
}
Help with this Solidity function. I know it returns (0,0,0) when called from brownie/python but I don't know how to test for it. [ the struct returned is indeed empty aka (0,0,0) using Solidity. ]
function do_something() public () {
if (getAllDefaultCards() == EMPTY) { <---- NEED HELP HERE
// sendMoney to whatever
}
}
A WORKAROUND
I basically split the function into two separate functions to get the desired affect:
function checkIfUserHasNFTCard() public view returns (bool)
function getUserHasNFTCard() public view returns (NFTCardAttributes)
Although, the primary question still stands and will update once I figure out the answer!

I am getting "The called function should be payable if you send value and the value you send should be less than your current balance."

I am trying to write a smart contract for jackpot game which calculates users' rate and randomly selects one user but I am getting "The called function should be payable if you send value and the value you send should be less than your current balance." error when I try to execute sendToWinner() function.
Note: Please don't mind the logical errors I am aware of the logical errors this is for training my Solidity writing skills.
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
contract Jackpot {
uint256 public pot;
mapping (address => uint256) public balances;
address[] public participators;
address public minter;
constructor(){
minter = msg.sender;
}
function addToPot() public payable {
balances[msg.sender] += msg.value;
pot += msg.value;
participators.push(msg.sender);
}
modifier onlyOwner {
require(msg.sender == minter);
_;
}
function random() public view returns(uint){
return uint(keccak256(abi.encodePacked(block.difficulty, block.timestamp)))%100;
}
function sendToWinner() payable public onlyOwner {
uint8[100] memory participatorsRates;
uint8 rate;
uint8 participatorsRatesIndex=0;
for(uint8 participatorIndex = 0; participatorIndex<participators.length; participatorIndex++){
rate = uint8(balances[participators[participatorIndex]]*100/pot);
for(; participatorIndex<rate ; participatorsRatesIndex++){
participatorsRates[participatorsRatesIndex] = participatorIndex;
}
balances[participators[participatorIndex]]=0;
}
uint8 winningParticipatorIndex = participatorsRates[random()];
payable(participators[winningParticipatorIndex]).transfer(pot);
}
}
1) payable it's a modifier, so you have to write after "public"
`function sendToWinner() public payable onlyOwner {}`
2) don't use .transfer, use call{value: amount}("") instead.
so it will be:
function sendToWinner() public payable onlyOwner {
uint8[100] memory participatorsRates;
uint8 rate;
uint8 participatorsRatesIndex=0;
for(uint8 participatorIndex = 0; participatorIndex<participators.length; participatorIndex++){
rate = uint8(balances[participators[participatorIndex]]*100/pot);
for(; participatorIndex<rate ; participatorsRatesIndex++){
participatorsRates[participatorsRatesIndex] = participatorIndex;
}
balances[participators[participatorIndex]]=0;
}
uint8 winningParticipatorIndex = participatorsRates[random()];
(bool success, ) = payable(participators[winningParticipatorIndex]).call{value:pot}("");
require(success, "Transfer failed.");
}
see:
Is transfer() still safe after the Istanbul update?
https://diligence.consensys.net/blog/2019/09/stop-using-soliditys-transfer-now/
3) be sure to have enough balance
I just found out that I have used wrong variable at nested loop in sendToWinner() function:
for(uint8 participatorIndex = 0; participatorIndex<participators.length; participatorIndex++){
rate = uint8(balances[participators[participatorIndex]]*100/pot);
for(; participatorIndex<rate ; participatorsRatesIndex++){
participatorsRates[participatorsRatesIndex] = participatorIndex;
}
balances[participators[participatorIndex]]=0;
}
For for(; participatorIndex<rate ; participatorsRatesIndex++){ instead participatorIndex I should have written participatorsRatesIndex. Because it became infinite loop it couldn't work but now it is working.

Adding one new field to Solidity struct makes Ethereum Contract stop working

In Ethereum private network (geth) I do have very simple contract (in Solidity).
version 1:
contract T {
string log;
function getLastLog() constant returns (string lastLog) { return log; }
function T() { log = "[call end]: T()\n"; }
struct TData {
uint amount;
}
mapping (address => uint) balance;
mapping (address => TData) mystructmap;
function setBalance(address _user, uint _balance) {
log = "[call start]: setBalance()\n";
balance[_user] = _balance;
mystructmap[_user] = TData({amount: 42});
log = "[call end]: setBalance()\n";
}
function getBalance(address _user) constant returns (uint _balance) {
return balance[_user];
}
function get42(address _user) constant returns (uint _fourtytwo) {
return mystructmap[_user].amount;
}
}
I do deploy contract and then call it like this (from web3.js):
contract.getLog()
contract.setBalance(valid_address, 55)
contract.getLog()
contract.getBalance(address)
contract.get42(address)
And I get as output result:
[call end]: T()
[call end]: setBalance()
55
42
Now I just add one new field to TData structure:
version 2:
contract T {
string log;
function getLastLog() constant returns (string lastLog) { return log; }
function T() { log = "[call end]: T()\n"; }
struct TData {
uint somedata;
uint amount;
}
mapping (address => uint) balance;
mapping (address => TData) mystructmap;
function setBalance(address _user, uint _balance) {
log = "[call start]: setBalance()\n";
balance[_user] = _balance;
mystructmap[_user] = TData({somedata: 11, amount: 42});
log = "[call end]: setBalance()\n";
}
function getBalance(address _user) external constant returns (uint _balance) {
return balance[_user];
}
function get42(address _user) external constant returns (uint _fourtytwo) {
return mystructmap[_user].amount;
}
}
I do the same calls as above:
contract.getLog()
contract.setBalance(valid_address, 55)
contract.getLog()
contract.getBalance(address)
contract.get42(address)
But now I get:
[call end]: T()
[call end]: T()
0
0
Seems like 'setBalance()' function is not executed (or exited somewhere) and state in storage is not changed.
Please help!
Thanks.
I had the same problem earlier. I'm pretty sure that it has to do with the amount of gas that you are sending with your requests. Web3 will guess but this has failed me before. Try manually sending different amounts of gas along with your request.
Here's something I'm doing:
store
.changeProduct(d.id, d.name, d.price, d.description, d.quantity,d.enabled, {from: account, gas:1000000})
Hardcoding Gas for any operation is a bad idea. You will keep getting into this kind of errors if you do that. You should check for Gas before firing the method
Use the estimate Gas API -
https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethestimategas
As and when you take your DApp public, you have to be cognizant of the fact that miners can adjust Gas prices in the geth console.
miner.setGasPrice(gasPrice);
Hope that helps!