3

I have a contract that contains a simple mapping; let's say mapping(address => uint256) balances;

I want to be able to somehow get all the participants of this smart contract. The simplest way seems to have an additional dynamic array of addresses and iterate over them. But searching for an address on each token transfer or payable function call looks overcomplicated.

Is there a way to have something like a set() in Python, i.e. an array of unique addresses?

4

There's no native set in solidity but you can work something simpler with an array and a mapping.

It is very simple, but have the disadvantage it is append only set, you cannot easily delete an element from it.

contract SimpleSet {
    // Mapping from address to position in the array
    // 0 means the address is not in the array
    mapping (address => uint) index;

    // Array with address 
    address[] store;

    function SimpleSet() public {
        // We will use position 0 to flag invalid address
        store.push(0x0);
    }

    function addToArray(address who) public {
        if (!inArray(who)) {
            // Append
            index[who] = store.length;
            store.push(who);
        }
    }

    function inArray(address who) public view returns (bool) {
        // address 0x0 is not valid if pos is 0 is not in the array
        if (who != 0x0 && index[who] > 0) {
            return true;
        }
        return false;
    }

    function getPosition(uint pos) public view returns (address) {
        // Position 0 is not valid
        require(pos > 0); 
        return store[pos];
    }
}
2

In your payable function, add the sender to an array if she's not there already. Given that you have a balances mapping, you can check that for free:

mapping(address => uint) balances;
address[] participants;

function deposit() payable {
  if (balances[msg.sender] == 0) {
    participants.push(msg.sender);
  }
  balances[msg.sender] += msg.value;
}

For your token transfer function, you might want to remove participants when their balance goes back to zero. In that case you need an extra mapping to store the participants' indices when you add them:

mapping(address => uint) balances;
address[] participants;
mapping(address => uint) indices;

function transfer(address recipient, uint amount) {
  if (balances[recipient] == 0) {
    participants.push(recipient);
    indices[recipient] = participants.length - 1;
  }
  // insert transfer logic here
  if (balances[msg.sender] == 0) {
    delete participants[indices[msg.sender]];
  }
}

If you have more than one of these functions, consider using modifiers.

0

No, there are no sets in solidity. You will need to add each member only once to this mapping, and then implement a counter to increment/decrement the length of the mapping every time a member joins or leaves.

Loops are very frowned upon in solidity and I would avoid as much as you can. You will eventually hit the gas limit and break your contract when the length gets too large.

  • 1
    Given that it's a mapping, members are added only once already. The problem is, you can't retrieve its keys so it's impossible to derive an array of unique addresses from it. That's why you need more than a counter: you need an actual array. As for loops, I don't think they are "very frowned upon"; like in every other programming language, they add a layer of complexity but are often unavoidable. – e18r Mar 24 '18 at 15:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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