Collections
A collection lives in storage. A collection function takes the collection as its first argument, as a storage path such as storage.members, and reads and writes it in place.
import "std/evm";
import "std/collections/set";
import "std/collections/set_enumerable";
import "std/collections/map_enumerable";
import "std/collections/bitmap";
import "std/collections/queue";
contract Airdrop is {
use Bitmap;
storage is record {
admins : Set(address);
holders : SetEnumerable(address);
claims : MapEnumerable(address, uint);
claimed : Bitmap;
waiting : Queue(address);
}
external function claim(const index : uint) is {
if not Bitmap.get(storage.claimed, index) then {
Bitmap.set(storage.claimed, index);
const _ : bool := SetEnumerable.add(storage.holders, Evm.sender());
const _ : bool := MapEnumerable.set(storage.claims, Evm.sender(), index);
}
}
external function claimRange(const start : uint; const amount : uint) is {
Bitmap.setBatch(storage.claimed, start, amount);
}
external view function claimedIn(const start : uint; const amount : uint) : uint is
Bitmap.popCount(storage.claimed, start, amount)
external view function claimOf(const who : address) : uint
raises EnumerableMapNonexistentKey
is
MapEnumerable.get(storage.claims, who)
external function wait() is {
Queue.push(storage.waiting, Evm.sender());
}
external view function holderCount() : uint is
SetEnumerable.length(storage.holders)
external view function isAdmin(const who : address) : bool is
Set.contains(storage.admins, who)
}| Module | Type | Functions |
|---|---|---|
Set(T) | a map from T to bool | contains, add, remove |
SetEnumerable(T) | a list and an index map | contains, add, remove, length, at |
MapEnumerable(K, V) | an enumerable set of keys and a map of values | contains, set, remove, get, tryGet, length, at |
Bitmap | Bitmap, 256 flags a slot | get, set, unset, setTo, setBatch, unsetBatch, popCount |
Queue(T) | a map and two counters | push, pop, front, back, at, length, isEmpty |
SetandSetEnumerabletake aMapKey: an integer,bool,address,bytesN, or a nominal type over one.SetEnumerablecosts more to write, because it keeps a list as well as the map.SetEnumerable.addandremove, andMapEnumerable.setandremove, answer whether they changed membership, as OpenZeppelin's do:addandsetwhether the element or key is new,removewhether it was there. A call statement must return nothing, so bind an answer you don't need to_, as inconst _ : bool := SetEnumerable.add(storage.holders, who);. An answer bound to_isn't computed: the code is the same as for a function that answers nothing.removeinSetEnumerableandMapEnumerablemoves the last element into the gap, so order isn't kept.MapEnumerableis OpenZeppelin'sEnumerableMapfor any key and value.getraises itsEnumerableMapNonexistentKey, with the key's ABI word as thebytes32.tryGetanswers an option instead, andat(index)a(key, value)pair, raisingOutOfBoundspast the end.removeleaves the old value in storage, where nothing reads it: its slot is cold by then, so deleting it would cost more than its refund, and setting the key again costs less.Bitmap.setBatch(bits, start, amount)andunsetBatchchange theamountflags fromstart, a whole slot at a time, andpopCount(bits, start, amount)counts those that are set, as Solady'sLibBitmapdoes.Queue.popdeletes the element it returns, which refunds its slots: about 1,800 gas a pop for a one-slotT.

