Solidity:遍历地址映射

Soldity: Iterate through address mapping

我正在寻找一种在 Solidity 中迭代映射的方法。例如我有这个映射:

mapping (address => uint) private shares;

我想在一个函数中迭代所有地址,并根据它们的份额向它们发送以太币。

类似于:

function giveOutEth() onlyOwner returns (bool success){
for(uint i=0; i < shares.length ; i++){
//get the address and send a value
}

}

我怎样才能做到这一点?

谢谢

我收到了 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]];
        ...
    }
    ... 
} 

}

但请记住,它会消耗 gas,也许利益相关者提取他们的 ETH 并自己支付 gas 费用会更好。

如果你想要更通用的东西,你可以使用库。我在下面包含了一个我正在使用的。它可能会使用一些改进(即,Element 应该更改为一个接口)并且它可能有点矫枉过正(另外,TBH 我还没有做过任何耗油量比较)。来自更 object-oriented 的背景,我更喜欢使用这样的可重用库,但鉴于 Solidity 的局限性,这是我能想到的最好的。

欢迎使用and/or改进它。

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

最近有一个类似的模板:

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