0

I'm new to solidity. I am trying to access all elements in my Applicant struct. In my Donate function if the donor does not specify an organization to donate to, then the amount will be split across all organizations within applicants. I have looked online and apparently it is a bad idea to iterate through a mapping due to the high gas cost. How else can I achieve my goal then?

    pragma solidity ^0.4.2;

contract DonationContract
{
    address owner;

    struct Applicant
    {
        address Org;
        string Name;
        string OrgType;
        string Number;
        string Email;
        uint256 Wallet;
        uint256 DonationFunds;
        bool approved;
    }

    constructor() public 
    {
        owner = msg.sender;
        facilitators[msg.sender] = true;
    }

    mapping (address => Applicant) public applicants;

    mapping(address => bool) facilitators;

    modifier Owner() 
    {
        require(msg.sender == owner);
        _;
    }

    modifier OnlyFacilitator()
    {
        require(facilitators[msg.sender] == true);
        _;
    }

     modifier Approved(address Org) 
    {
        require(applicants[Org].approved == true);
        _;
    }

    function approve(address Org) OnlyFacilitator public
    {
        applicants[Org].approved = true;
    }

      function Donate(uint256 amount, address Org) Approved(Org)  public
    {
        if(Org==0)
        {
            //Split donation amount amongst all applicants donationfunds
        }
        else
        applicants[Org].DonationFunds += amount;

        if(applicants[Org].DonationFunds >= 10000)
        {
            applicants[Org].DonationFunds -=10000;
            applicants[Org].Wallet += 10000;
        }
    }

     function addFacilitator(address _addr) Owner public
    {
        facilitators[_addr] = true;
    }
}
1
  • The easiest solution would to maintain a separate array of the addresses which are keys to the mapping, and iterate that. Note that boundless iteration in general is not ideal, and you may reach a point where the contract fails because iteration costs more than the block gas limit Commented Aug 16, 2018 at 21:30

1 Answer 1

1

You cannot iterate through a mapping unless you have the key for each value stored somewhere.

The following are two ways you can do this. Both have their own issues.

  1. (Preferred/Decentralized way) Create a function that allows orgs to claim their donations that are sitting in the contract. This can be made easier with a simple web interface for them to use. The function would simply check if the user has funds stored in the contract, and would allow them to withdraw the appropriate amount. The following is an example that I have not tested, but should give you proper guidance. NOTE: This does not work if orgs are freely added/removed, but rather a known number of orgs exist and this does not change. This is only a general guide and must be modified given the specific logic you are trying to implement. There should be checks/requires/etc. that I have not included.

    uint256 public numOfOrgs;   // Number of orgs able to claim
    uint256 public claimIndex;  // Count of claimable donations
    mapping(uint256 => uint256) public claimAmount // The claimable amount per index
    mapping(address => mapping(uint256 => bool)) public isIndexClaimed; // Amount of ETH each org can claim per claimable event
    
    
    function donate()
    {
        // Donation logic where donator does not specify
        if donatorDidNotSpecify {
            addIndex(donationAmount)
        }
    }
    
    function addIndex(uint256 _donationAmount)
        internal
    {
        uint256 amountPerOrg = _donationAmount.div(numOfOrgs);
        claimAmount[claimIndex] = amountPerOrg;
        claimIndex += 1;           
    }
    
    function claim(uint256 _claimIndex)
        public
    {
        if (isIndexClaimed[msg.sender][_claimIndex] == false) {
            isIndexClaimed[msg.sender][_claimIndex] = true;  // Org cannot claim multiple times per index
            uint256 amount = claimAmount[_claimIndex];
            msg.sender.transfer(amount);
        }
    }
    
  2. Keep a database of addresses (the keys to the mapping) offchain and run a script that distributes the contents of the donation to each organization in the event of the donor not specifying the recipient. As you mentioned, this would cost you a certain amount of gas that adds up as more orgs join.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.