Efficient way of checking and inserting array address that is unqiue











up vote
1
down vote

favorite












I have a following requirement that need to be cater in dapp.




  1. Insert array of address


  2. The array need to be unique. If one of the array address already exists, reject and revert entire operation.


  3. dapp must able to retrieve the user address list


  4. dapp must able to detect if a user address exists based on a user address input


Note:




  1. The above requirement must be satisfied.



  2. Full Validation checking must be done by smart contract.(cannot rely on client side checking alone).



Trying to use 3 for loop doesn't seems be a very clever way for this as it become a cost expensive operation. Unfortunately this is the only solution i can think of to satisfy above condition. Anyone have experience on dealing with this?



// probably only one of mapping or list is needed
address public addressList;
mapping (address => bool) public userAddr;


function insertAddress(address addressUser) public returns (bool) {
// loop addressUser and check if it is unique
// loop addressUser and check if it exists in mapping
// loop and push addressUser to addressList + insert addressUser to userAddr


return true;
}









share|improve this question




























    up vote
    1
    down vote

    favorite












    I have a following requirement that need to be cater in dapp.




    1. Insert array of address


    2. The array need to be unique. If one of the array address already exists, reject and revert entire operation.


    3. dapp must able to retrieve the user address list


    4. dapp must able to detect if a user address exists based on a user address input


    Note:




    1. The above requirement must be satisfied.



    2. Full Validation checking must be done by smart contract.(cannot rely on client side checking alone).



    Trying to use 3 for loop doesn't seems be a very clever way for this as it become a cost expensive operation. Unfortunately this is the only solution i can think of to satisfy above condition. Anyone have experience on dealing with this?



    // probably only one of mapping or list is needed
    address public addressList;
    mapping (address => bool) public userAddr;


    function insertAddress(address addressUser) public returns (bool) {
    // loop addressUser and check if it is unique
    // loop addressUser and check if it exists in mapping
    // loop and push addressUser to addressList + insert addressUser to userAddr


    return true;
    }









    share|improve this question


























      up vote
      1
      down vote

      favorite









      up vote
      1
      down vote

      favorite











      I have a following requirement that need to be cater in dapp.




      1. Insert array of address


      2. The array need to be unique. If one of the array address already exists, reject and revert entire operation.


      3. dapp must able to retrieve the user address list


      4. dapp must able to detect if a user address exists based on a user address input


      Note:




      1. The above requirement must be satisfied.



      2. Full Validation checking must be done by smart contract.(cannot rely on client side checking alone).



      Trying to use 3 for loop doesn't seems be a very clever way for this as it become a cost expensive operation. Unfortunately this is the only solution i can think of to satisfy above condition. Anyone have experience on dealing with this?



      // probably only one of mapping or list is needed
      address public addressList;
      mapping (address => bool) public userAddr;


      function insertAddress(address addressUser) public returns (bool) {
      // loop addressUser and check if it is unique
      // loop addressUser and check if it exists in mapping
      // loop and push addressUser to addressList + insert addressUser to userAddr


      return true;
      }









      share|improve this question















      I have a following requirement that need to be cater in dapp.




      1. Insert array of address


      2. The array need to be unique. If one of the array address already exists, reject and revert entire operation.


      3. dapp must able to retrieve the user address list


      4. dapp must able to detect if a user address exists based on a user address input


      Note:




      1. The above requirement must be satisfied.



      2. Full Validation checking must be done by smart contract.(cannot rely on client side checking alone).



      Trying to use 3 for loop doesn't seems be a very clever way for this as it become a cost expensive operation. Unfortunately this is the only solution i can think of to satisfy above condition. Anyone have experience on dealing with this?



      // probably only one of mapping or list is needed
      address public addressList;
      mapping (address => bool) public userAddr;


      function insertAddress(address addressUser) public returns (bool) {
      // loop addressUser and check if it is unique
      // loop addressUser and check if it exists in mapping
      // loop and push addressUser to addressList + insert addressUser to userAddr


      return true;
      }






      solidity contract-development contract-design gas arrays






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 18 at 9:13

























      asked Nov 18 at 8:43









      vincentsty

      1414




      1414






















          2 Answers
          2






          active

          oldest

          votes

















          up vote
          2
          down vote



          accepted










          Seeing your requirement I would do :



          address public addressList;
          mapping (address => bool) public userAddr;

          function insertAddress(address addressUser) public returns (bool) {
          // used to check adressUser uniqueness
          mapping (address => bool) memory uniq;
          for(uint i = 0; i < addressUser.length, i++) {
          address addr = addressUser[i];
          // check if addressUser is unique
          require(uniq[addr] == false);
          uniq[addr] = true;
          // if addr is not already list
          if (userAddr[addr] == false) {
          userAddr[addr] = true;
          addressList.push(addr);
          }
          }

          return true;
          }


          Edit:



          After seeing another contract you could use the method here and require that all address are sent in increasing order. It is probably less costly in term of gas since less memory allocation.



          That would do :



          function insertAddress(address addressUser) public returns (bool) {
          // used to check adressUser uniqueness
          address lastAddr = addres(0);
          for(uint i = 0; i < addressUser.length, i++) {
          address addr = addressUser[i];
          // check if addressUser is unique
          // by forcing all address to be sent
          // in increasing order
          require(addr > lastAddr);
          lastAddr = addr;
          // if addr is not already in list
          if (userAddr[addr] == false) {
          userAddr[addr] = true;
          addressList.push(addr);
          }
          }

          return true;
          }





          share|improve this answer






























            up vote
            1
            down vote













            I have created contracts like this before and have seen many of the issues you are having. In short, these operations you are attempting to perform are expensive. Thus, the best way to do this is simply do it efficiently.



            In order to satisfy the requirements, I would put the entire function in a loop that iterates over each address that is passed in. In order to make it as efficient as possible, you can use an if statement to check if the address already exists. If it does, you can simply move onto the next address without performing more calculations on the current one. Depending on how you are passing in addresses, this may have huge gas savings.



            As an example, you could do:



            address public addressList;
            mapping (address => bool) public userAddr;


            function insertAddress(address addressUser) public returns (bool)
            {
            for (uint256 i = 0; i < addressUser.length; i++) {
            if (address does not exist) {
            push addressUser to addressList
            insert addressUser to userAddr
            }
            }

            return true;
            }


            Edit based on the new requirements




            The array need to be unique. If one of the array address already exists, reject and revert entire operation.




            If this is the case, then the above does not apply and what you originally had is the best way to do it.



            Edit based on comments



            Thinking about it logically, in order to check the array you must check every address. This requires opcodes that check each item, and you cannot get around this. After these checks are performed, you must write each item to the blockchain, which also requires opcodes for each of these. This whole process is computationally expensive, and is the reason why a lot of this logic is generally suggested off-chain.



            One thing you can do is compare the hashes of the arrays (one that you are submitting and one that is being checked). You can take the keccack256 of each array in order to ensure a unique hash. You can store this hash in the smart contract and submit a hash to be checked, rather than the entire array. By doing this, you are now only performing one check, as opposed to N checks (N being the number of items in the array).



            You will still be required to loop to add all the items to the smart contract, but now you have effectively removed one loop.



            An example would be:



            address public addressList;
            mapping (address => bool) public userAddr;
            mapping (bytes32 => bool) public doesHashExist;


            function insertAddress(address addressUser) public returns (bool)
            {
            bytes32 newHash = keccak256(addressUser);
            require(!doesHashExist[newHash]);
            for (uint256 i = 0; i < addressUser.length; i++) {
            push addressUser to addressList
            insert addressUser to userAddr
            }
            }
            doesHashExist[newHash] = True;
            return true;
            }


            You will see that there is a new mapping that is used to store hashes that exist on the contract. The function now only does a single check (as well as a hashing) to confirm existence of the array. Finally, it saves the hash.






            share|improve this answer



















            • 1




              The above u array is to skip and insert non unique address which i have seen it is being practiced a lot. but the requirement for the dapp is to reject entire operation even if one of it is unique.
              – vincentsty
              Nov 18 at 9:03








            • 1




              Yup, i know it works, but it require at least 3 for loop (one for checking if the array pass in itself is unique, one for checking whether the array pass in have existing value, one for inserting). I am trying to look for a more cost effective way.
              – vincentsty
              Nov 18 at 9:06








            • 1




              The requirement must be satisfied in the smart contract itself as per the requirement. It should not be reliant on external validation. Between, is there a way to reduce 3 for loop to 2? (eg: for loop to checking array itself is unique can be checked with other more efficient way?)
              – vincentsty
              Nov 18 at 9:11








            • 1




              Why would it be suggested to do offchain. Offchain validation can be bypass easily by directly calling to the smart contract function from user wallet address
              – vincentsty
              Nov 18 at 9:22








            • 1




              You can take the keccak256 of each array in order to ensure a unique hash. Didn't really understand how this will reduce one for loop. keccak256(array addressList1) === keccak256(array addressList2) only can check if two array list is same (in terms of length, value and order of value). It cannot identified if an item of an array does exist in another array Or i misunderstand the implementation.
              – vincentsty
              Nov 18 at 9:31













            Your Answer








            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "642"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














             

            draft saved


            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fethereum.stackexchange.com%2fquestions%2f62572%2fefficient-way-of-checking-and-inserting-array-address-that-is-unqiue%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            2 Answers
            2






            active

            oldest

            votes








            2 Answers
            2






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            2
            down vote



            accepted










            Seeing your requirement I would do :



            address public addressList;
            mapping (address => bool) public userAddr;

            function insertAddress(address addressUser) public returns (bool) {
            // used to check adressUser uniqueness
            mapping (address => bool) memory uniq;
            for(uint i = 0; i < addressUser.length, i++) {
            address addr = addressUser[i];
            // check if addressUser is unique
            require(uniq[addr] == false);
            uniq[addr] = true;
            // if addr is not already list
            if (userAddr[addr] == false) {
            userAddr[addr] = true;
            addressList.push(addr);
            }
            }

            return true;
            }


            Edit:



            After seeing another contract you could use the method here and require that all address are sent in increasing order. It is probably less costly in term of gas since less memory allocation.



            That would do :



            function insertAddress(address addressUser) public returns (bool) {
            // used to check adressUser uniqueness
            address lastAddr = addres(0);
            for(uint i = 0; i < addressUser.length, i++) {
            address addr = addressUser[i];
            // check if addressUser is unique
            // by forcing all address to be sent
            // in increasing order
            require(addr > lastAddr);
            lastAddr = addr;
            // if addr is not already in list
            if (userAddr[addr] == false) {
            userAddr[addr] = true;
            addressList.push(addr);
            }
            }

            return true;
            }





            share|improve this answer



























              up vote
              2
              down vote



              accepted










              Seeing your requirement I would do :



              address public addressList;
              mapping (address => bool) public userAddr;

              function insertAddress(address addressUser) public returns (bool) {
              // used to check adressUser uniqueness
              mapping (address => bool) memory uniq;
              for(uint i = 0; i < addressUser.length, i++) {
              address addr = addressUser[i];
              // check if addressUser is unique
              require(uniq[addr] == false);
              uniq[addr] = true;
              // if addr is not already list
              if (userAddr[addr] == false) {
              userAddr[addr] = true;
              addressList.push(addr);
              }
              }

              return true;
              }


              Edit:



              After seeing another contract you could use the method here and require that all address are sent in increasing order. It is probably less costly in term of gas since less memory allocation.



              That would do :



              function insertAddress(address addressUser) public returns (bool) {
              // used to check adressUser uniqueness
              address lastAddr = addres(0);
              for(uint i = 0; i < addressUser.length, i++) {
              address addr = addressUser[i];
              // check if addressUser is unique
              // by forcing all address to be sent
              // in increasing order
              require(addr > lastAddr);
              lastAddr = addr;
              // if addr is not already in list
              if (userAddr[addr] == false) {
              userAddr[addr] = true;
              addressList.push(addr);
              }
              }

              return true;
              }





              share|improve this answer

























                up vote
                2
                down vote



                accepted







                up vote
                2
                down vote



                accepted






                Seeing your requirement I would do :



                address public addressList;
                mapping (address => bool) public userAddr;

                function insertAddress(address addressUser) public returns (bool) {
                // used to check adressUser uniqueness
                mapping (address => bool) memory uniq;
                for(uint i = 0; i < addressUser.length, i++) {
                address addr = addressUser[i];
                // check if addressUser is unique
                require(uniq[addr] == false);
                uniq[addr] = true;
                // if addr is not already list
                if (userAddr[addr] == false) {
                userAddr[addr] = true;
                addressList.push(addr);
                }
                }

                return true;
                }


                Edit:



                After seeing another contract you could use the method here and require that all address are sent in increasing order. It is probably less costly in term of gas since less memory allocation.



                That would do :



                function insertAddress(address addressUser) public returns (bool) {
                // used to check adressUser uniqueness
                address lastAddr = addres(0);
                for(uint i = 0; i < addressUser.length, i++) {
                address addr = addressUser[i];
                // check if addressUser is unique
                // by forcing all address to be sent
                // in increasing order
                require(addr > lastAddr);
                lastAddr = addr;
                // if addr is not already in list
                if (userAddr[addr] == false) {
                userAddr[addr] = true;
                addressList.push(addr);
                }
                }

                return true;
                }





                share|improve this answer














                Seeing your requirement I would do :



                address public addressList;
                mapping (address => bool) public userAddr;

                function insertAddress(address addressUser) public returns (bool) {
                // used to check adressUser uniqueness
                mapping (address => bool) memory uniq;
                for(uint i = 0; i < addressUser.length, i++) {
                address addr = addressUser[i];
                // check if addressUser is unique
                require(uniq[addr] == false);
                uniq[addr] = true;
                // if addr is not already list
                if (userAddr[addr] == false) {
                userAddr[addr] = true;
                addressList.push(addr);
                }
                }

                return true;
                }


                Edit:



                After seeing another contract you could use the method here and require that all address are sent in increasing order. It is probably less costly in term of gas since less memory allocation.



                That would do :



                function insertAddress(address addressUser) public returns (bool) {
                // used to check adressUser uniqueness
                address lastAddr = addres(0);
                for(uint i = 0; i < addressUser.length, i++) {
                address addr = addressUser[i];
                // check if addressUser is unique
                // by forcing all address to be sent
                // in increasing order
                require(addr > lastAddr);
                lastAddr = addr;
                // if addr is not already in list
                if (userAddr[addr] == false) {
                userAddr[addr] = true;
                addressList.push(addr);
                }
                }

                return true;
                }






                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited 2 days ago

























                answered Nov 18 at 10:23









                dievardump

                1364




                1364






















                    up vote
                    1
                    down vote













                    I have created contracts like this before and have seen many of the issues you are having. In short, these operations you are attempting to perform are expensive. Thus, the best way to do this is simply do it efficiently.



                    In order to satisfy the requirements, I would put the entire function in a loop that iterates over each address that is passed in. In order to make it as efficient as possible, you can use an if statement to check if the address already exists. If it does, you can simply move onto the next address without performing more calculations on the current one. Depending on how you are passing in addresses, this may have huge gas savings.



                    As an example, you could do:



                    address public addressList;
                    mapping (address => bool) public userAddr;


                    function insertAddress(address addressUser) public returns (bool)
                    {
                    for (uint256 i = 0; i < addressUser.length; i++) {
                    if (address does not exist) {
                    push addressUser to addressList
                    insert addressUser to userAddr
                    }
                    }

                    return true;
                    }


                    Edit based on the new requirements




                    The array need to be unique. If one of the array address already exists, reject and revert entire operation.




                    If this is the case, then the above does not apply and what you originally had is the best way to do it.



                    Edit based on comments



                    Thinking about it logically, in order to check the array you must check every address. This requires opcodes that check each item, and you cannot get around this. After these checks are performed, you must write each item to the blockchain, which also requires opcodes for each of these. This whole process is computationally expensive, and is the reason why a lot of this logic is generally suggested off-chain.



                    One thing you can do is compare the hashes of the arrays (one that you are submitting and one that is being checked). You can take the keccack256 of each array in order to ensure a unique hash. You can store this hash in the smart contract and submit a hash to be checked, rather than the entire array. By doing this, you are now only performing one check, as opposed to N checks (N being the number of items in the array).



                    You will still be required to loop to add all the items to the smart contract, but now you have effectively removed one loop.



                    An example would be:



                    address public addressList;
                    mapping (address => bool) public userAddr;
                    mapping (bytes32 => bool) public doesHashExist;


                    function insertAddress(address addressUser) public returns (bool)
                    {
                    bytes32 newHash = keccak256(addressUser);
                    require(!doesHashExist[newHash]);
                    for (uint256 i = 0; i < addressUser.length; i++) {
                    push addressUser to addressList
                    insert addressUser to userAddr
                    }
                    }
                    doesHashExist[newHash] = True;
                    return true;
                    }


                    You will see that there is a new mapping that is used to store hashes that exist on the contract. The function now only does a single check (as well as a hashing) to confirm existence of the array. Finally, it saves the hash.






                    share|improve this answer



















                    • 1




                      The above u array is to skip and insert non unique address which i have seen it is being practiced a lot. but the requirement for the dapp is to reject entire operation even if one of it is unique.
                      – vincentsty
                      Nov 18 at 9:03








                    • 1




                      Yup, i know it works, but it require at least 3 for loop (one for checking if the array pass in itself is unique, one for checking whether the array pass in have existing value, one for inserting). I am trying to look for a more cost effective way.
                      – vincentsty
                      Nov 18 at 9:06








                    • 1




                      The requirement must be satisfied in the smart contract itself as per the requirement. It should not be reliant on external validation. Between, is there a way to reduce 3 for loop to 2? (eg: for loop to checking array itself is unique can be checked with other more efficient way?)
                      – vincentsty
                      Nov 18 at 9:11








                    • 1




                      Why would it be suggested to do offchain. Offchain validation can be bypass easily by directly calling to the smart contract function from user wallet address
                      – vincentsty
                      Nov 18 at 9:22








                    • 1




                      You can take the keccak256 of each array in order to ensure a unique hash. Didn't really understand how this will reduce one for loop. keccak256(array addressList1) === keccak256(array addressList2) only can check if two array list is same (in terms of length, value and order of value). It cannot identified if an item of an array does exist in another array Or i misunderstand the implementation.
                      – vincentsty
                      Nov 18 at 9:31

















                    up vote
                    1
                    down vote













                    I have created contracts like this before and have seen many of the issues you are having. In short, these operations you are attempting to perform are expensive. Thus, the best way to do this is simply do it efficiently.



                    In order to satisfy the requirements, I would put the entire function in a loop that iterates over each address that is passed in. In order to make it as efficient as possible, you can use an if statement to check if the address already exists. If it does, you can simply move onto the next address without performing more calculations on the current one. Depending on how you are passing in addresses, this may have huge gas savings.



                    As an example, you could do:



                    address public addressList;
                    mapping (address => bool) public userAddr;


                    function insertAddress(address addressUser) public returns (bool)
                    {
                    for (uint256 i = 0; i < addressUser.length; i++) {
                    if (address does not exist) {
                    push addressUser to addressList
                    insert addressUser to userAddr
                    }
                    }

                    return true;
                    }


                    Edit based on the new requirements




                    The array need to be unique. If one of the array address already exists, reject and revert entire operation.




                    If this is the case, then the above does not apply and what you originally had is the best way to do it.



                    Edit based on comments



                    Thinking about it logically, in order to check the array you must check every address. This requires opcodes that check each item, and you cannot get around this. After these checks are performed, you must write each item to the blockchain, which also requires opcodes for each of these. This whole process is computationally expensive, and is the reason why a lot of this logic is generally suggested off-chain.



                    One thing you can do is compare the hashes of the arrays (one that you are submitting and one that is being checked). You can take the keccack256 of each array in order to ensure a unique hash. You can store this hash in the smart contract and submit a hash to be checked, rather than the entire array. By doing this, you are now only performing one check, as opposed to N checks (N being the number of items in the array).



                    You will still be required to loop to add all the items to the smart contract, but now you have effectively removed one loop.



                    An example would be:



                    address public addressList;
                    mapping (address => bool) public userAddr;
                    mapping (bytes32 => bool) public doesHashExist;


                    function insertAddress(address addressUser) public returns (bool)
                    {
                    bytes32 newHash = keccak256(addressUser);
                    require(!doesHashExist[newHash]);
                    for (uint256 i = 0; i < addressUser.length; i++) {
                    push addressUser to addressList
                    insert addressUser to userAddr
                    }
                    }
                    doesHashExist[newHash] = True;
                    return true;
                    }


                    You will see that there is a new mapping that is used to store hashes that exist on the contract. The function now only does a single check (as well as a hashing) to confirm existence of the array. Finally, it saves the hash.






                    share|improve this answer



















                    • 1




                      The above u array is to skip and insert non unique address which i have seen it is being practiced a lot. but the requirement for the dapp is to reject entire operation even if one of it is unique.
                      – vincentsty
                      Nov 18 at 9:03








                    • 1




                      Yup, i know it works, but it require at least 3 for loop (one for checking if the array pass in itself is unique, one for checking whether the array pass in have existing value, one for inserting). I am trying to look for a more cost effective way.
                      – vincentsty
                      Nov 18 at 9:06








                    • 1




                      The requirement must be satisfied in the smart contract itself as per the requirement. It should not be reliant on external validation. Between, is there a way to reduce 3 for loop to 2? (eg: for loop to checking array itself is unique can be checked with other more efficient way?)
                      – vincentsty
                      Nov 18 at 9:11








                    • 1




                      Why would it be suggested to do offchain. Offchain validation can be bypass easily by directly calling to the smart contract function from user wallet address
                      – vincentsty
                      Nov 18 at 9:22








                    • 1




                      You can take the keccak256 of each array in order to ensure a unique hash. Didn't really understand how this will reduce one for loop. keccak256(array addressList1) === keccak256(array addressList2) only can check if two array list is same (in terms of length, value and order of value). It cannot identified if an item of an array does exist in another array Or i misunderstand the implementation.
                      – vincentsty
                      Nov 18 at 9:31















                    up vote
                    1
                    down vote










                    up vote
                    1
                    down vote









                    I have created contracts like this before and have seen many of the issues you are having. In short, these operations you are attempting to perform are expensive. Thus, the best way to do this is simply do it efficiently.



                    In order to satisfy the requirements, I would put the entire function in a loop that iterates over each address that is passed in. In order to make it as efficient as possible, you can use an if statement to check if the address already exists. If it does, you can simply move onto the next address without performing more calculations on the current one. Depending on how you are passing in addresses, this may have huge gas savings.



                    As an example, you could do:



                    address public addressList;
                    mapping (address => bool) public userAddr;


                    function insertAddress(address addressUser) public returns (bool)
                    {
                    for (uint256 i = 0; i < addressUser.length; i++) {
                    if (address does not exist) {
                    push addressUser to addressList
                    insert addressUser to userAddr
                    }
                    }

                    return true;
                    }


                    Edit based on the new requirements




                    The array need to be unique. If one of the array address already exists, reject and revert entire operation.




                    If this is the case, then the above does not apply and what you originally had is the best way to do it.



                    Edit based on comments



                    Thinking about it logically, in order to check the array you must check every address. This requires opcodes that check each item, and you cannot get around this. After these checks are performed, you must write each item to the blockchain, which also requires opcodes for each of these. This whole process is computationally expensive, and is the reason why a lot of this logic is generally suggested off-chain.



                    One thing you can do is compare the hashes of the arrays (one that you are submitting and one that is being checked). You can take the keccack256 of each array in order to ensure a unique hash. You can store this hash in the smart contract and submit a hash to be checked, rather than the entire array. By doing this, you are now only performing one check, as opposed to N checks (N being the number of items in the array).



                    You will still be required to loop to add all the items to the smart contract, but now you have effectively removed one loop.



                    An example would be:



                    address public addressList;
                    mapping (address => bool) public userAddr;
                    mapping (bytes32 => bool) public doesHashExist;


                    function insertAddress(address addressUser) public returns (bool)
                    {
                    bytes32 newHash = keccak256(addressUser);
                    require(!doesHashExist[newHash]);
                    for (uint256 i = 0; i < addressUser.length; i++) {
                    push addressUser to addressList
                    insert addressUser to userAddr
                    }
                    }
                    doesHashExist[newHash] = True;
                    return true;
                    }


                    You will see that there is a new mapping that is used to store hashes that exist on the contract. The function now only does a single check (as well as a hashing) to confirm existence of the array. Finally, it saves the hash.






                    share|improve this answer














                    I have created contracts like this before and have seen many of the issues you are having. In short, these operations you are attempting to perform are expensive. Thus, the best way to do this is simply do it efficiently.



                    In order to satisfy the requirements, I would put the entire function in a loop that iterates over each address that is passed in. In order to make it as efficient as possible, you can use an if statement to check if the address already exists. If it does, you can simply move onto the next address without performing more calculations on the current one. Depending on how you are passing in addresses, this may have huge gas savings.



                    As an example, you could do:



                    address public addressList;
                    mapping (address => bool) public userAddr;


                    function insertAddress(address addressUser) public returns (bool)
                    {
                    for (uint256 i = 0; i < addressUser.length; i++) {
                    if (address does not exist) {
                    push addressUser to addressList
                    insert addressUser to userAddr
                    }
                    }

                    return true;
                    }


                    Edit based on the new requirements




                    The array need to be unique. If one of the array address already exists, reject and revert entire operation.




                    If this is the case, then the above does not apply and what you originally had is the best way to do it.



                    Edit based on comments



                    Thinking about it logically, in order to check the array you must check every address. This requires opcodes that check each item, and you cannot get around this. After these checks are performed, you must write each item to the blockchain, which also requires opcodes for each of these. This whole process is computationally expensive, and is the reason why a lot of this logic is generally suggested off-chain.



                    One thing you can do is compare the hashes of the arrays (one that you are submitting and one that is being checked). You can take the keccack256 of each array in order to ensure a unique hash. You can store this hash in the smart contract and submit a hash to be checked, rather than the entire array. By doing this, you are now only performing one check, as opposed to N checks (N being the number of items in the array).



                    You will still be required to loop to add all the items to the smart contract, but now you have effectively removed one loop.



                    An example would be:



                    address public addressList;
                    mapping (address => bool) public userAddr;
                    mapping (bytes32 => bool) public doesHashExist;


                    function insertAddress(address addressUser) public returns (bool)
                    {
                    bytes32 newHash = keccak256(addressUser);
                    require(!doesHashExist[newHash]);
                    for (uint256 i = 0; i < addressUser.length; i++) {
                    push addressUser to addressList
                    insert addressUser to userAddr
                    }
                    }
                    doesHashExist[newHash] = True;
                    return true;
                    }


                    You will see that there is a new mapping that is used to store hashes that exist on the contract. The function now only does a single check (as well as a hashing) to confirm existence of the array. Finally, it saves the hash.







                    share|improve this answer














                    share|improve this answer



                    share|improve this answer








                    edited Nov 18 at 9:39

























                    answered Nov 18 at 8:56









                    shane

                    1,6693630




                    1,6693630








                    • 1




                      The above u array is to skip and insert non unique address which i have seen it is being practiced a lot. but the requirement for the dapp is to reject entire operation even if one of it is unique.
                      – vincentsty
                      Nov 18 at 9:03








                    • 1




                      Yup, i know it works, but it require at least 3 for loop (one for checking if the array pass in itself is unique, one for checking whether the array pass in have existing value, one for inserting). I am trying to look for a more cost effective way.
                      – vincentsty
                      Nov 18 at 9:06








                    • 1




                      The requirement must be satisfied in the smart contract itself as per the requirement. It should not be reliant on external validation. Between, is there a way to reduce 3 for loop to 2? (eg: for loop to checking array itself is unique can be checked with other more efficient way?)
                      – vincentsty
                      Nov 18 at 9:11








                    • 1




                      Why would it be suggested to do offchain. Offchain validation can be bypass easily by directly calling to the smart contract function from user wallet address
                      – vincentsty
                      Nov 18 at 9:22








                    • 1




                      You can take the keccak256 of each array in order to ensure a unique hash. Didn't really understand how this will reduce one for loop. keccak256(array addressList1) === keccak256(array addressList2) only can check if two array list is same (in terms of length, value and order of value). It cannot identified if an item of an array does exist in another array Or i misunderstand the implementation.
                      – vincentsty
                      Nov 18 at 9:31
















                    • 1




                      The above u array is to skip and insert non unique address which i have seen it is being practiced a lot. but the requirement for the dapp is to reject entire operation even if one of it is unique.
                      – vincentsty
                      Nov 18 at 9:03








                    • 1




                      Yup, i know it works, but it require at least 3 for loop (one for checking if the array pass in itself is unique, one for checking whether the array pass in have existing value, one for inserting). I am trying to look for a more cost effective way.
                      – vincentsty
                      Nov 18 at 9:06








                    • 1




                      The requirement must be satisfied in the smart contract itself as per the requirement. It should not be reliant on external validation. Between, is there a way to reduce 3 for loop to 2? (eg: for loop to checking array itself is unique can be checked with other more efficient way?)
                      – vincentsty
                      Nov 18 at 9:11








                    • 1




                      Why would it be suggested to do offchain. Offchain validation can be bypass easily by directly calling to the smart contract function from user wallet address
                      – vincentsty
                      Nov 18 at 9:22








                    • 1




                      You can take the keccak256 of each array in order to ensure a unique hash. Didn't really understand how this will reduce one for loop. keccak256(array addressList1) === keccak256(array addressList2) only can check if two array list is same (in terms of length, value and order of value). It cannot identified if an item of an array does exist in another array Or i misunderstand the implementation.
                      – vincentsty
                      Nov 18 at 9:31










                    1




                    1




                    The above u array is to skip and insert non unique address which i have seen it is being practiced a lot. but the requirement for the dapp is to reject entire operation even if one of it is unique.
                    – vincentsty
                    Nov 18 at 9:03






                    The above u array is to skip and insert non unique address which i have seen it is being practiced a lot. but the requirement for the dapp is to reject entire operation even if one of it is unique.
                    – vincentsty
                    Nov 18 at 9:03






                    1




                    1




                    Yup, i know it works, but it require at least 3 for loop (one for checking if the array pass in itself is unique, one for checking whether the array pass in have existing value, one for inserting). I am trying to look for a more cost effective way.
                    – vincentsty
                    Nov 18 at 9:06






                    Yup, i know it works, but it require at least 3 for loop (one for checking if the array pass in itself is unique, one for checking whether the array pass in have existing value, one for inserting). I am trying to look for a more cost effective way.
                    – vincentsty
                    Nov 18 at 9:06






                    1




                    1




                    The requirement must be satisfied in the smart contract itself as per the requirement. It should not be reliant on external validation. Between, is there a way to reduce 3 for loop to 2? (eg: for loop to checking array itself is unique can be checked with other more efficient way?)
                    – vincentsty
                    Nov 18 at 9:11






                    The requirement must be satisfied in the smart contract itself as per the requirement. It should not be reliant on external validation. Between, is there a way to reduce 3 for loop to 2? (eg: for loop to checking array itself is unique can be checked with other more efficient way?)
                    – vincentsty
                    Nov 18 at 9:11






                    1




                    1




                    Why would it be suggested to do offchain. Offchain validation can be bypass easily by directly calling to the smart contract function from user wallet address
                    – vincentsty
                    Nov 18 at 9:22






                    Why would it be suggested to do offchain. Offchain validation can be bypass easily by directly calling to the smart contract function from user wallet address
                    – vincentsty
                    Nov 18 at 9:22






                    1




                    1




                    You can take the keccak256 of each array in order to ensure a unique hash. Didn't really understand how this will reduce one for loop. keccak256(array addressList1) === keccak256(array addressList2) only can check if two array list is same (in terms of length, value and order of value). It cannot identified if an item of an array does exist in another array Or i misunderstand the implementation.
                    – vincentsty
                    Nov 18 at 9:31






                    You can take the keccak256 of each array in order to ensure a unique hash. Didn't really understand how this will reduce one for loop. keccak256(array addressList1) === keccak256(array addressList2) only can check if two array list is same (in terms of length, value and order of value). It cannot identified if an item of an array does exist in another array Or i misunderstand the implementation.
                    – vincentsty
                    Nov 18 at 9:31




















                     

                    draft saved


                    draft discarded



















































                     


                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fethereum.stackexchange.com%2fquestions%2f62572%2fefficient-way-of-checking-and-inserting-array-address-that-is-unqiue%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    Plaza Victoria

                    In PowerPoint, is there a keyboard shortcut for bulleted / numbered list?

                    How to put 3 figures in Latex with 2 figures side by side and 1 below these side by side images but in...