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
}
}
}
我正在寻找一种在 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
}
}
}