diff --git a/contracts/Marketplace.sol b/contracts/Marketplace.sol index 69abb66..ace74df 100644 --- a/contracts/Marketplace.sol +++ b/contracts/Marketplace.sol @@ -10,6 +10,8 @@ import "./libs/SetMap.sol"; contract Marketplace is Collateral, Proofs { using EnumerableSet for EnumerableSet.Bytes32Set; + using EnumerableSet for EnumerableSet.AddressSet; + using SetMap for SetMap.Bytes32SetMap; using SetMap for SetMap.AddressSetMap; type RequestId is bytes32; @@ -20,8 +22,9 @@ contract Marketplace is Collateral, Proofs { mapping(RequestId => Request) private requests; mapping(RequestId => RequestContext) private requestContexts; mapping(SlotId => Slot) private slots; - mapping(address => EnumerableSet.Bytes32Set) private activeRequests; - SetMap.AddressSetMap private activeSlots; + // mapping(address => EnumerableSet.Bytes32Set) private activeRequests; + SetMap.AddressSetMap private activeRequests; + SetMap.Bytes32SetMap private activeSlots; constructor( IERC20 _token, @@ -38,11 +41,12 @@ contract Marketplace is Collateral, Proofs { } function myRequests() public view returns (RequestId[] memory) { - return _toRequestIds(activeRequests[msg.sender].values()); + SetMap.AddressSetMapKey key = _toAddressSetMapKey(msg.sender); + return _toRequestIds(activeRequests.values(key)); } function allRequests() public view returns(RequestId[] memory) { - return _toRequestIds(activeSlots.keys()); + return _toRequestIds(activeRequests.values()); } function mySlots(RequestId requestId) @@ -50,8 +54,8 @@ contract Marketplace is Collateral, Proofs { view returns (SlotId[] memory) { - bytes32[] memory slotIds = activeSlots.values(_toSetMapKey(requestId), - msg.sender); + bytes32[] memory slotIds = + activeSlots.values(_toBytes32SetMapKey(requestId), msg.sender); return _toSlotIds(slotIds); } @@ -74,7 +78,8 @@ contract Marketplace is Collateral, Proofs { context.endsAt = block.timestamp + request.ask.duration; _setProofEnd(_toEndId(id), context.endsAt); - activeRequests[request.client].add(RequestId.unwrap(id)); + activeRequests.add(_toAddressSetMapKey(request.client), + RequestId.unwrap(id)); _createLock(_toLockId(id), request.expiry); @@ -110,7 +115,7 @@ contract Marketplace is Collateral, Proofs { slot.requestId = requestId; RequestContext storage context = _context(requestId); context.slotsFilled += 1; - activeSlots.add(_toSetMapKey(requestId), + activeSlots.add(_toBytes32SetMapKey(requestId), slot.host, SlotId.unwrap(slotId)); emit SlotFilled(requestId, slotIndex, slotId); @@ -139,7 +144,7 @@ contract Marketplace is Collateral, Proofs { _unexpectProofs(_toProofId(slotId)); - activeSlots.remove(_toSetMapKey(requestId), + activeSlots.remove(_toBytes32SetMapKey(requestId), slot.host, SlotId.unwrap(slotId)); slot.host = address(0); @@ -156,8 +161,9 @@ contract Marketplace is Collateral, Proofs { context.state = RequestState.Failed; _setProofEnd(_toEndId(requestId), block.timestamp - 1); context.endsAt = block.timestamp - 1; - activeRequests[request.client].remove(RequestId.unwrap(requestId)); - activeSlots.clear(_toSetMapKey(requestId)); + activeRequests.remove(_toAddressSetMapKey(request.client), + RequestId.unwrap(requestId)); + activeSlots.clear(_toBytes32SetMapKey(requestId)); emit RequestFailed(requestId); // TODO: burn all remaining slot collateral (note: slot collateral not @@ -174,11 +180,12 @@ contract Marketplace is Collateral, Proofs { RequestContext storage context = _context(requestId); Request storage request = _request(requestId); context.state = RequestState.Finished; - activeRequests[request.client].remove(RequestId.unwrap(requestId)); + activeRequests.remove(_toAddressSetMapKey(request.client), + RequestId.unwrap(requestId)); SlotId slotId = _toSlotId(requestId, slotIndex); Slot storage slot = _slot(slotId); require(!slot.hostPaid, "Already paid"); - activeSlots.remove(_toSetMapKey(requestId), + activeSlots.remove(_toBytes32SetMapKey(requestId), slot.host, SlotId.unwrap(slotId)); uint256 amount = pricePerSlot(requests[requestId]); @@ -201,7 +208,8 @@ contract Marketplace is Collateral, Proofs { // Update request state to Cancelled. Handle in the withdraw transaction // as there needs to be someone to pay for the gas to update the state context.state = RequestState.Cancelled; - activeRequests[request.client].remove(RequestId.unwrap(requestId)); + activeRequests.remove(_toAddressSetMapKey(request.client), + RequestId.unwrap(requestId)); emit RequestCancelled(requestId); // TODO: To be changed once we start paying out hosts for the time they @@ -379,7 +387,7 @@ contract Marketplace is Collateral, Proofs { } } - function _toRequestIds(SetMap.Key[] memory array) + function _toRequestIds(SetMap.Bytes32SetMapKey[] memory array) private pure returns (RequestId[] memory result) @@ -421,12 +429,20 @@ contract Marketplace is Collateral, Proofs { return EndId.wrap(RequestId.unwrap(requestId)); } - function _toSetMapKey(RequestId requestId) + function _toBytes32SetMapKey(RequestId requestId) internal pure - returns (SetMap.Key) + returns (SetMap.Bytes32SetMapKey) { - return SetMap.Key.wrap(RequestId.unwrap(requestId)); + return SetMap.Bytes32SetMapKey.wrap(RequestId.unwrap(requestId)); + } + + function _toAddressSetMapKey(address addr) + internal + pure + returns (SetMap.AddressSetMapKey) + { + return SetMap.AddressSetMapKey.wrap(addr); } function _notEqual(RequestId a, uint256 b) internal pure returns (bool) { diff --git a/contracts/libs/SetMap.sol b/contracts/libs/SetMap.sol index 6fdf257..a7a1484 100644 --- a/contracts/libs/SetMap.sol +++ b/contracts/libs/SetMap.sol @@ -5,28 +5,29 @@ import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; library SetMap { using EnumerableSet for EnumerableSet.Bytes32Set; + using EnumerableSet for EnumerableSet.AddressSet; - type Key is bytes32; + type Bytes32SetMapKey is bytes32; - struct AddressSetMap { - mapping(Key => + struct Bytes32SetMap { + mapping(Bytes32SetMapKey => mapping(address => mapping(uint8 => EnumerableSet.Bytes32Set))) _values; - mapping(Key => uint8) _index; + mapping(Bytes32SetMapKey => uint8) _index; EnumerableSet.Bytes32Set _keys; } /// @notice Returns the EnumerableSet.Bytes32 containing the values for a key - /// and address in an AddressSetMap + /// and address in an Bytes32SetMap /// @dev This is used internally to the library only. `.values()` should only /// be called on its return value in a view/pure function. - /// @param map AddressSetMap to list values + /// @param map Bytes32SetMap to list values /// @param key key of the values to be listed /// @param addr address of the values to be listed /// @return bytes32[] array of bytes32 values - function _set(AddressSetMap storage map, - Key key, + function _set(Bytes32SetMap storage map, + Bytes32SetMapKey key, address addr) private view @@ -36,13 +37,13 @@ library SetMap { return map._values[key][addr][id]; } - /// @notice Lists all values for a key and address in an AddressSetMap - /// @param map AddressSetMap to list values + /// @notice Lists all values for a key and address in an Bytes32SetMap + /// @param map Bytes32SetMap to list values /// @param key key of the values to be listed /// @param addr address of the values to be listed /// @return bytes32[] array of bytes32 values - function values(AddressSetMap storage map, - Key key, + function values(Bytes32SetMap storage map, + Bytes32SetMapKey key, address addr) internal view @@ -54,7 +55,7 @@ library SetMap { function _toKeys(bytes32[] memory array) private pure - returns (Key[] memory result) + returns (Bytes32SetMapKey[] memory result) { // solhint-disable-next-line no-inline-assembly assembly { @@ -62,44 +63,44 @@ library SetMap { } } - /// @notice Lists all keys for an AddressSetMap - /// @param map AddressSetMap to list keys + /// @notice Lists all keys for an Bytes32SetMap + /// @param map Bytes32SetMap to list keys /// @return bytes32[] array of bytes32 values - function keys(AddressSetMap storage map) + function keys(Bytes32SetMap storage map) internal view - returns (Key[] memory) + returns (Bytes32SetMapKey[] memory) { return _toKeys(map._keys.values()); } - /// @notice Adds a single value to an AddressSetMap - /// @param map AddressSetMap to add the value to + /// @notice Adds a single value to an Bytes32SetMap + /// @param map Bytes32SetMap to add the value to /// @param key key of the value to be added /// @param addr address of the value to be added /// @param value the value to be added /// @return true if the value was added to the set, that is if it was not /// already present. - function add(AddressSetMap storage map, - Key key, + function add(Bytes32SetMap storage map, + Bytes32SetMapKey key, address addr, bytes32 value) internal returns (bool) { - map._keys.add(Key.unwrap(key)); + map._keys.add(Bytes32SetMapKey.unwrap(key)); return _set(map, key, addr).add(value); } - /// @notice Removes a single value from an AddressSetMap - /// @param map AddressSetMap to remove the value from + /// @notice Removes a single value from an Bytes32SetMap + /// @param map Bytes32SetMap to remove the value from /// @param key key of the value to be removed /// @param addr address of the value to be removed /// @param value the value to be removed /// @return true if the value was removed from the set, that is if it was /// present. - function remove(AddressSetMap storage map, - Key key, + function remove(Bytes32SetMap storage map, + Bytes32SetMapKey key, address addr, bytes32 value) internal @@ -108,7 +109,7 @@ library SetMap { EnumerableSet.Bytes32Set storage set = _set(map, key, addr); bool success = set.remove(value); if (success && set.length() == 0) { - map._keys.remove(Key.unwrap(key)); + map._keys.remove(Bytes32SetMapKey.unwrap(key)); } return success; } @@ -116,10 +117,133 @@ library SetMap { /// @notice Clears values for a key. /// @dev Does not clear the addresses for the key, simply updates an index /// such that the next time values for that key and address are - /// retrieved, it will return an empty array. + /// retrieved, it will reference a new EnumerableSet. + /// @param map Bytes32SetMap for which to clear values + /// @param key key for which to clear values + function clear(Bytes32SetMap storage map, Bytes32SetMapKey key) + internal + { + map._index[key]++; + } + + type AddressSetMapKey is address; + + struct AddressSetMap { + mapping(AddressSetMapKey => + mapping(uint8 => + EnumerableSet.Bytes32Set)) _values; + mapping(AddressSetMapKey => uint8) _index; + EnumerableSet.AddressSet _keys; + EnumerableSet.Bytes32Set _allValues; + } + + /// @notice Returns the EnumerableSet.AddressSet containing the values for a + /// key in an AddressSetMap. + /// @dev This is used internally to the library only. `.values()` should only + /// be called on its return value in a view/pure function. + /// @param map AddressSetMap containing the set to be retrieved. + /// @param key key of the set to be retrieved. + /// @return bytes32[] array of bytes32 values. + function _set(AddressSetMap storage map, + AddressSetMapKey key) + private + view + returns (EnumerableSet.Bytes32Set storage) + { + uint8 id = map._index[key]; + return map._values[key][id]; + } + + /// @notice Lists all values contained in an AddressSetMap, regardless of + /// the key. + /// @param map AddressSetMap to list values + /// @return bytes32[] array of bytes32 values + function values(AddressSetMap storage map) + internal + view + returns (bytes32[] memory) + { + return map._allValues.values(); + } + + /// @notice Lists all values for a key in an AddressSetMap + /// @param map AddressSetMap to list values + /// @param key key of the values to be listed + /// @return bytes32[] array of bytes32 values + function values(AddressSetMap storage map, AddressSetMapKey key) + internal + view + returns (bytes32[] memory) + { + return _set(map, key).values(); + } + + function _toAddressSetMapKeys(address[] memory array) + private + pure + returns (AddressSetMapKey[] memory result) + { + // solhint-disable-next-line no-inline-assembly + assembly { + result := array + } + } + + /// @notice Lists all keys for an Bytes32SetMap. + /// @param map AddressSetMap to list keys. + /// @return bytes32[] array of bytes32 values. + function keys(AddressSetMap storage map) + internal + view + returns (AddressSetMapKey[] memory) + { + return _toAddressSetMapKeys(map._keys.values()); + } + + /// @notice Adds a single value to an AddressSetMap + /// @param map AddressSetMap to add the value to. + /// @param key key of the value to be added. + /// @param value the value to be added. + /// @return true if the value was added to the set, that is if it was not + /// already present. + function add(AddressSetMap storage map, + AddressSetMapKey key, + bytes32 value) + internal + returns (bool) + { + map._keys.add(AddressSetMapKey.unwrap(key)); + map._allValues.add(value); + return _set(map, key).add(value); + } + + /// @notice Removes a single value from an AddressSetMap + /// @param map AddressSetMap to remove the value from + /// @param key key of the value to be removed + /// @param value the value to be removed + /// @return true if the value was removed from the set, that is if it was + /// present. + function remove(AddressSetMap storage map, + AddressSetMapKey key, + bytes32 value) + internal + returns (bool) + { + EnumerableSet.Bytes32Set storage set = _set(map, key); + bool success = set.remove(value); + if (success && set.length() == 0) { + map._keys.remove(AddressSetMapKey.unwrap(key)); + } + map._allValues.remove(value); + return success; + } + + /// @notice Clears values for a key. + /// @dev Updates an index such that the next time values for that key are + /// retrieved, it will reference a new EnumerableSet. /// @param map AddressSetMap for which to clear values /// @param key key for which to clear values - function clear(AddressSetMap storage map, Key key) + function clear(AddressSetMap storage map, AddressSetMapKey key) internal { map._index[key]++;