Soldity: Iterate through address mapping - blockchain

I am looking for a way to iterate through a mapping in Solidity. For example I have this mapping:
mapping (address => uint) private shares;
And I want to iterate in a function through all addresses and send them ether according to their shares.
Something like:
function giveOutEth() onlyOwner returns (bool success){
for(uint i=0; i < shares.length ; i++){
//get the address and send a value
}
}
How can I achieve this?
Thanks

I recieved an answer by drlecks:
contract Holders{
uint _totalHolders; // you should initialize this to 0 in the constructor
mapping (uint=> address ) private holders;
mapping (address => uint) private shares;
function GetShares(uint shares) public {
...
holders[_totalHolders] = msg.sender;
shares[msg.sender] = shares;
_totalHolders++;
...
}
function PayOut() public {
...
uint shares;
for(uint i = 0 ; i<_totalHolders; i++) {
shares = shares[holders[i]];
...
}
...
}
}
but keep in mind that it will consume gas, and maybe its better that the stake holders withdraw their ETH and pay for gas themselfs.

If you want something more general, you can use a library. I've included one I'm using below. It could probably use some improvements (ie, Element should be changed to an interface) and it may be overkill (plus, TBH I haven't done any gas consumption comparisons yet). Coming from a more object-oriented background, I prefer using reusable libraries like this, but this is the best I could come up with given Solidity's limitations.
Feel free to use it and/or improve on it.
pragma solidity ^0.4.19;
pragma experimental "ABIEncoderV2";
// experimental encoder needed due to https://github.com/ethereum/solidity/issues/3069
library SetLib {
using SetLib for Set;
struct Set {
mapping(address => IndexData) _dataMap;
uint16 _size;
IndexData[] _dataIndex;
}
struct IndexData {
uint16 _index;
bool _isDeleted;
Element _element;
}
struct Element {
address _value;
uint8 _status;
}
function add(Set storage self, Element element) internal returns (bool) {
if (element._value == 0x0 || self.contains(element)) {
return false;
}
IndexData memory data;
data._index = uint16(self._dataIndex.length);
data._element = element;
self._dataMap[element._value] = data;
self._dataIndex.push(data);
self._size++;
return true;
}
function update(Set storage self, Element element) internal {
if (element._value != 0x0) {
IndexData storage data = self._dataMap[element._value];
if (data._element._value == element._value && !data._isDeleted && element._status != data._element._status)
data._element._status = element._status;
}
}
function getByIndex(Set storage self, uint16 index) internal constant returns (Element) {
IndexData storage data = self._dataIndex[index];
if (!data._isDeleted) {
return data._element;
}
}
function get(Set storage self, address addr) internal constant returns (Element) {
IndexData storage data = self._dataMap[addr];
if (!data._isDeleted) {
return data._element;
}
}
function contains(Set storage self, Element element) internal constant returns (bool) {
return self.contains(element._value);
}
function contains(Set storage self, address addr) internal constant returns (bool) {
if (addr != 0x0) {
IndexData storage data = self._dataMap[addr];
return data._index > 0 && !data._isDeleted;
}
return false;
}
function remove(Set storage self, uint16 index) internal returns (Element) {
IndexData storage data = self._dataIndex[index];
if (data._element._value != 0x0 && !data._isDeleted) {
data._isDeleted = true;
self._size--;
return data._element;
}
}
function remove(Set storage self, address addr) internal returns (Element) {
if (addr != 0x0) {
IndexData storage data = self._dataMap[addr];
if (data._element._value != 0x0 && !data._isDeleted) {
data._isDeleted = true;
self._size--;
return data._element;
}
}
}
function size(Set storage self) internal constant returns (uint16) {
return self._size;
}
}
library IteratorLib {
using SetLib for SetLib.Set;
struct Iterator {
bool _started; // using bool instead of making _curIndex int32 for initial state.
uint16 _curIndex;
uint16 _size;
}
function iterator(SetLib.Set storage set) internal constant returns (IteratorLib.Iterator) {
return IteratorLib.Iterator(false, 0, set.size());
}
function hasNext(Iterator self, SetLib.Set storage set) internal constant returns (bool) {
uint16 testIndex = self._curIndex;
while (testIndex < self._size) {
if (set._dataIndex[testIndex]._element._value != 0x0 && !set._dataIndex[testIndex]._isDeleted)
return true;
testIndex++;
}
return false;
}
function next(Iterator self, SetLib.Set storage set) internal constant returns (SetLib.Element) {
SetLib.Element memory element;
do {
if (self._started) {
self._curIndex++;
}
else {
self._started = true;
}
element = set.getByIndex(self._curIndex);
}
while (element._value != 0x0 && self._curIndex < self._size);
return element;
}
}

A similar template recently:
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
struct IndexValue {
uint256 keyIndex;
uint256 value;
}
struct KeyFlag {
address key;
bool deleted;
}
struct ItMap {
mapping(address => IndexValue) data;
KeyFlag[] keys;
uint256 size;
}
library IterableMapping {
function insert(
ItMap storage self,
address key,
uint256 value
) internal returns (bool replaced) {
uint256 keyIndex = self.data[key].keyIndex;
self.data[key].value = value;
if (keyIndex > 0) return true;
else {
keyIndex = self.keys.length;
self.keys.push();
self.data[key].keyIndex = keyIndex + 1;
self.keys[keyIndex].key = key;
self.size++;
return false;
}
}
function remove(ItMap storage self, address key)
internal
returns (bool success)
{
uint256 keyIndex = self.data[key].keyIndex;
if (keyIndex == 0) return false;
delete self.data[key];
self.keys[keyIndex - 1].deleted = true;
self.size--;
}
function contains(ItMap storage self, address key)
internal
view
returns (bool)
{
return self.data[key].keyIndex > 0;
}
function start(ItMap storage self)
internal
view
returns (uint256 keyIndex)
{
uint256 index = next(self, type(uint256).min);
return index - 1;
}
function valid(ItMap storage self, uint256 keyIndex)
internal
view
returns (bool)
{
return keyIndex < self.keys.length;
}
function next(ItMap storage self, uint256 keyIndex)
internal
view
returns (uint256)
{
keyIndex++;
while (keyIndex < self.keys.length && self.keys[keyIndex].deleted)
keyIndex++;
return keyIndex;
}
function get(ItMap storage self, uint256 keyIndex)
internal
view
returns (address key, uint256 value)
{
key = self.keys[keyIndex].key;
value = self.data[key].value;
}
}
contract Demo {
using IterableMapping for ItMap;
ItMap shares;
function test() public payable {
for (uint256 i = shares.start(); shares.valid(i); i = shares.next(i)) {
(address k, uint256 v) = shares.get(i);
// get the address and send a value
}
}
}

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

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!

How to conditionally iterate through a mapping of structs and only return some structs - Solidity

I'm learning solidity by writing a contract to store and retrieve patient medical records. Does anyone know why the functions "getRecordByAddressMap" and "getRecordByAddressStruct" don't work? They compile, but the transactions revert.
The logic is quite simple, the functions iterate through the mapping (or array in the other case), checks if the addresses match, adds the structs to an array, then returns that array.
What am I missing here?
contract PatientRecords{
uint256 public recordID = 1;
mapping (uint256 => mapping (address => Records)) records;
struct Records {
address patient;
address hospital;
uint256 admissionDate;
uint256 dischargeDate;
uint256 visitReason;
}
Records[] recordsarray;
Records[] getstructs;
constructor() {
addRecord(0x3719dB98b075Ff10886Fc29431Ffc2006fFF0005,
0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, 20220320, 20220330, 1);
addRecord(0x3719dB98b075Ff10886Fc29431Ffc2006fFF0005,
0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, 20220333, 20220333, 2);
addRecord(0x60814DB6b62fE178d7F91239078e3c20fB857E04,
0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, 20220310, 20220311, 3);
}
// Add a record to the mapping
function addRecord (
address _patientAddress,
address _hospital,
uint256 _admissionDate,
uint256 _dischargeDate,
uint256 _visitReason)
public
{
records[recordID][_patientAddress].patient = _patientAddress;
records[recordID][_patientAddress].hospital = _hospital;
records[recordID][_patientAddress].admissionDate = _admissionDate;
records[recordID][_patientAddress].dischargeDate = _dischargeDate;
records[recordID][_patientAddress].visitReason = _visitReason;
recordsarray.push(Records(_patientAddress, _hospital, _admissionDate, _dischargeDate, _visitReason));
recordID++;
}
// Retrieve a record by the patient address and record ID (count)
function getRecordByID(address _patientAddress, uint256 _recordID) public view returns(Records memory) {
return records[_recordID][_patientAddress];
}
// Retrieve a record by the patient address (Mapping method)
function getRecordByAddressMap(address _patientAddress) public view returns (Records[] memory){
Records[] memory rec = new Records[](recordID);
for (uint i = 1; i < recordID; i++) {
if (_patientAddress == records[i][_patientAddress].patient == true) {
rec[i] = records[i][_patientAddress];
} else {
continue;
}
}
return rec;
}
// Retrieve a record by the patient address (Struct method)
function getRecordByAddressStruct(address _patientAddress) public returns(Records[] memory) {
Records[] storage _getstructs = getstructs;
for (uint i = 1; i < recordID; i++) {
if (_patientAddress == recordsarray[i].patient == true) {
Records memory newRecord = Records({
patient: recordsarray[i].patient,
hospital: recordsarray[i].hospital,
admissionDate: recordsarray[i].admissionDate,
dischargeDate: recordsarray[i].dischargeDate,
visitReason: recordsarray[i].visitReason
});
_getstructs.push(newRecord);
} else {
continue;
}
}
return _getstructs;
}
}
When you iterate with for keyword, you don't use record Id try to use [array].lenght.
In your case, in getRecordByAddressMap function change it in this way:
// Retrieve a record by the patient address (Mapping method)
function getRecordByAddressMap(address _patientAddress) public view returns (Records[] memory){
Records[] memory rec = new Records[](recordID);
for (uint i = 1; i <= rec.length; i++) {
if (_patientAddress == records[i][_patientAddress].patient == true) {
rec[i] = records[i][_patientAddress];
} else {
continue;
}
}
return rec;
}
And in getRecordByAddressStruct function change it with this:
// Retrieve a record by the patient address (Struct method)
function getRecordByAddressStruct(address _patientAddress) public returns(Records[] memory) {
Records[] storage _getstructs = getstructs;
for (uint i = 1; i < _getstructs.length; i++) {
if (_patientAddress == recordsarray[i].patient == true) {
Records memory newRecord = Records({
patient: recordsarray[i].patient,
hospital: recordsarray[i].hospital,
admissionDate: recordsarray[i].admissionDate,
dischargeDate: recordsarray[i].dischargeDate,
visitReason: recordsarray[i].visitReason
});
_getstructs.push(newRecord);
} else {
continue;
}
}
return _getstructs;
}

Smart Contract not producing results, how do I see both results or get the total count of both results

When I deploy the contract I get the count result of only one entity not both entities. Can you help me? How do I get the result of both entities.
Here is part of the code.
function doVote(bool _choice)
public
inState(State.Voting)
returns (bool voted)
{
bool found = false;
if (bytes(voterRegister[msg.sender].voterName).length != 0
&& !voterRegister[msg.sender].voted){
voterRegister[msg.sender].voted = true;
vote memory v;
v.voterAddress = msg.sender;
v.choice = _choice;
if (_choice){
countResult++; //counting on the go
}
else {
countRe++;
}
votes[totalVote] = v;
totalVote++;
found = true;
}
emit voteDone(msg.sender);
return found;
}
//end votes
function endVote()
public
inState(State.Voting)
onlyOfficial
{
state = State.Ended;
finalResult = countResult; //move result from private countResult to public finalResult
totalFalse = countRe;
emit voteEnded(finalResult, totalFalse);
}
}

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!