mirror of
https://github.com/status-im/dagger-contracts.git
synced 2025-01-15 00:57:26 +00:00
[marketplace] replace mySlots(requestId) by mySlots()
Allows a host to get all slots that it participates in in a single call.
This commit is contained in:
parent
7f59e545b2
commit
c0a1e11b87
@ -12,6 +12,7 @@ import "./libs/Utils.sol";
|
|||||||
contract Marketplace is Collateral, Proofs {
|
contract Marketplace is Collateral, Proofs {
|
||||||
using EnumerableSet for EnumerableSet.Bytes32Set;
|
using EnumerableSet for EnumerableSet.Bytes32Set;
|
||||||
using EnumerableSet for EnumerableSet.AddressSet;
|
using EnumerableSet for EnumerableSet.AddressSet;
|
||||||
|
using Utils for EnumerableSet.Bytes32Set;
|
||||||
using SetMap for SetMap.Bytes32SetMap;
|
using SetMap for SetMap.Bytes32SetMap;
|
||||||
using SetMap for SetMap.AddressBytes32SetMap;
|
using SetMap for SetMap.AddressBytes32SetMap;
|
||||||
using SetMap for SetMap.Bytes32AddressSetMap;
|
using SetMap for SetMap.Bytes32AddressSetMap;
|
||||||
@ -25,8 +26,8 @@ contract Marketplace is Collateral, Proofs {
|
|||||||
mapping(RequestId => RequestContext) private requestContexts;
|
mapping(RequestId => RequestContext) private requestContexts;
|
||||||
mapping(SlotId => Slot) private slots;
|
mapping(SlotId => Slot) private slots;
|
||||||
SetMap.AddressBytes32SetMap private activeRequestsForClients; // purchasing
|
SetMap.AddressBytes32SetMap private activeRequestsForClients; // purchasing
|
||||||
SetMap.Bytes32AddressSetMap private activeRequestsForHosts; // purchasing
|
mapping(address => EnumerableSet.Bytes32Set) private slotsPerHost; // sales
|
||||||
SetMap.Bytes32SetMap private activeSlots; // sales
|
mapping(SlotId => RequestId) private requestForSlot;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
IERC20 _token,
|
IERC20 _token,
|
||||||
@ -44,7 +45,9 @@ contract Marketplace is Collateral, Proofs {
|
|||||||
|
|
||||||
function myRequests() public view returns (RequestId[] memory) {
|
function myRequests() public view returns (RequestId[] memory) {
|
||||||
SetMap.AddressBytes32SetMapKey key = _toAddressSetMapKey(msg.sender);
|
SetMap.AddressBytes32SetMapKey key = _toAddressSetMapKey(msg.sender);
|
||||||
RequestId[] memory requestIds = _toRequestIds(activeRequestsForClients.values(key));
|
RequestId[] memory requestIds = _toRequestIds(
|
||||||
|
activeRequestsForClients.values(key)
|
||||||
|
);
|
||||||
RequestId[] memory result = new RequestId[](requestIds.length);
|
RequestId[] memory result = new RequestId[](requestIds.length);
|
||||||
uint8 counter = 0;
|
uint8 counter = 0;
|
||||||
for (uint8 i = 0; i < requestIds.length; i++) {
|
for (uint8 i = 0; i < requestIds.length; i++) {
|
||||||
@ -56,40 +59,16 @@ contract Marketplace is Collateral, Proofs {
|
|||||||
return _toRequestIds(Utils._resize(_toBytes32s(result), counter));
|
return _toRequestIds(Utils._resize(_toBytes32s(result), counter));
|
||||||
}
|
}
|
||||||
|
|
||||||
function requestsForHost(address host) public view returns(RequestId[] memory) {
|
function isActive(bytes32 slot) private view returns (bool) {
|
||||||
EnumerableSet.Bytes32Set storage keys = activeRequestsForHosts.keys();
|
RequestState s = state(requestForSlot[SlotId.wrap(slot)]);
|
||||||
uint256 keyLength = keys.length();
|
return
|
||||||
RequestId[] memory result = new RequestId[](keyLength);
|
s == RequestState.New ||
|
||||||
|
s == RequestState.Started ||
|
||||||
uint8 counter = 0; // should be big enough
|
s == RequestState.Finished;
|
||||||
for (uint8 i = 0; i < keyLength; i++) {
|
|
||||||
RequestId requestId = RequestId.wrap(keys.at(i));
|
|
||||||
SetMap.Bytes32AddressSetMapKey key = _toBytes32AddressSetMapKey(requestId);
|
|
||||||
if (activeRequestsForHosts.contains(key, host) &&
|
|
||||||
!_isCancelled(requestId))
|
|
||||||
{
|
|
||||||
result[counter] = requestId;
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return _toRequestIds(Utils._resize(_toBytes32s(result), counter));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function mySlots(RequestId requestId)
|
function mySlots() public view returns (SlotId[] memory) {
|
||||||
public
|
return _toSlotIds(slotsPerHost[msg.sender].filter(isActive));
|
||||||
view
|
|
||||||
returns (SlotId[] memory)
|
|
||||||
{
|
|
||||||
// There may exist slots that are still "active", but are part of a request
|
|
||||||
// that is expired but has not been set to the cancelled state yet. In that
|
|
||||||
// case, return an empty array.
|
|
||||||
if (_isCancelled(requestId)) {
|
|
||||||
SlotId[] memory result;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
bytes32[] memory slotIds =
|
|
||||||
activeSlots.values(_toBytes32SetMapKey(requestId), msg.sender);
|
|
||||||
return _toSlotIds(slotIds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _equals(RequestId a, RequestId b) internal pure returns (bool) {
|
function _equals(RequestId a, RequestId b) internal pure returns (bool) {
|
||||||
@ -111,8 +90,10 @@ contract Marketplace is Collateral, Proofs {
|
|||||||
context.endsAt = block.timestamp + request.ask.duration;
|
context.endsAt = block.timestamp + request.ask.duration;
|
||||||
_setProofEnd(_toEndId(id), context.endsAt);
|
_setProofEnd(_toEndId(id), context.endsAt);
|
||||||
|
|
||||||
activeRequestsForClients.add(_toAddressSetMapKey(request.client),
|
activeRequestsForClients.add(
|
||||||
RequestId.unwrap(id));
|
_toAddressSetMapKey(request.client),
|
||||||
|
RequestId.unwrap(id)
|
||||||
|
);
|
||||||
|
|
||||||
_createLock(_toLockId(id), request.expiry);
|
_createLock(_toLockId(id), request.expiry);
|
||||||
|
|
||||||
@ -148,11 +129,10 @@ contract Marketplace is Collateral, Proofs {
|
|||||||
slot.requestId = requestId;
|
slot.requestId = requestId;
|
||||||
RequestContext storage context = _context(requestId);
|
RequestContext storage context = _context(requestId);
|
||||||
context.slotsFilled += 1;
|
context.slotsFilled += 1;
|
||||||
activeSlots.add(_toBytes32SetMapKey(requestId),
|
|
||||||
slot.host,
|
slotsPerHost[slot.host].add(SlotId.unwrap(slotId));
|
||||||
SlotId.unwrap(slotId));
|
requestForSlot[slotId] = requestId;
|
||||||
activeRequestsForHosts.add(_toBytes32AddressSetMapKey(requestId),
|
|
||||||
slot.host);
|
|
||||||
emit SlotFilled(requestId, slotIndex, slotId);
|
emit SlotFilled(requestId, slotIndex, slotId);
|
||||||
if (context.slotsFilled == request.ask.slots) {
|
if (context.slotsFilled == request.ask.slots) {
|
||||||
context.state = RequestState.Started;
|
context.state = RequestState.Started;
|
||||||
@ -166,7 +146,7 @@ contract Marketplace is Collateral, Proofs {
|
|||||||
internal
|
internal
|
||||||
slotMustAcceptProofs(slotId)
|
slotMustAcceptProofs(slotId)
|
||||||
marketplaceInvariant
|
marketplaceInvariant
|
||||||
// TODO: restrict senders that can call this function
|
// TODO: restrict senders that can call this function
|
||||||
{
|
{
|
||||||
Slot storage slot = _slot(slotId);
|
Slot storage slot = _slot(slotId);
|
||||||
RequestId requestId = slot.requestId;
|
RequestId requestId = slot.requestId;
|
||||||
@ -179,14 +159,8 @@ contract Marketplace is Collateral, Proofs {
|
|||||||
|
|
||||||
_unexpectProofs(_toProofId(slotId));
|
_unexpectProofs(_toProofId(slotId));
|
||||||
|
|
||||||
SetMap.Bytes32SetMapKey requestIdKey = _toBytes32SetMapKey(requestId);
|
slotsPerHost[slot.host].remove(SlotId.unwrap(slotId));
|
||||||
activeSlots.remove(requestIdKey,
|
|
||||||
slot.host,
|
|
||||||
SlotId.unwrap(slotId));
|
|
||||||
if (activeSlots.length(requestIdKey, slot.host) == 0) {
|
|
||||||
activeRequestsForHosts.remove(_toBytes32AddressSetMapKey(requestId),
|
|
||||||
slot.host);
|
|
||||||
}
|
|
||||||
slot.host = address(0);
|
slot.host = address(0);
|
||||||
slot.requestId = RequestId.wrap(0);
|
slot.requestId = RequestId.wrap(0);
|
||||||
context.slotsFilled -= 1;
|
context.slotsFilled -= 1;
|
||||||
@ -201,10 +175,10 @@ contract Marketplace is Collateral, Proofs {
|
|||||||
context.state = RequestState.Failed;
|
context.state = RequestState.Failed;
|
||||||
_setProofEnd(_toEndId(requestId), block.timestamp - 1);
|
_setProofEnd(_toEndId(requestId), block.timestamp - 1);
|
||||||
context.endsAt = block.timestamp - 1;
|
context.endsAt = block.timestamp - 1;
|
||||||
activeRequestsForClients.remove(_toAddressSetMapKey(request.client),
|
activeRequestsForClients.remove(
|
||||||
RequestId.unwrap(requestId));
|
_toAddressSetMapKey(request.client),
|
||||||
activeRequestsForHosts.clear(_toBytes32AddressSetMapKey(requestId));
|
RequestId.unwrap(requestId)
|
||||||
activeSlots.clear(_toBytes32SetMapKey(requestId));
|
);
|
||||||
emit RequestFailed(requestId);
|
emit RequestFailed(requestId);
|
||||||
|
|
||||||
// TODO: burn all remaining slot collateral (note: slot collateral not
|
// TODO: burn all remaining slot collateral (note: slot collateral not
|
||||||
@ -221,16 +195,16 @@ contract Marketplace is Collateral, Proofs {
|
|||||||
RequestContext storage context = _context(requestId);
|
RequestContext storage context = _context(requestId);
|
||||||
Request storage request = _request(requestId);
|
Request storage request = _request(requestId);
|
||||||
context.state = RequestState.Finished;
|
context.state = RequestState.Finished;
|
||||||
activeRequestsForClients.remove(_toAddressSetMapKey(request.client),
|
activeRequestsForClients.remove(
|
||||||
RequestId.unwrap(requestId));
|
_toAddressSetMapKey(request.client),
|
||||||
|
RequestId.unwrap(requestId)
|
||||||
|
);
|
||||||
SlotId slotId = _toSlotId(requestId, slotIndex);
|
SlotId slotId = _toSlotId(requestId, slotIndex);
|
||||||
Slot storage slot = _slot(slotId);
|
Slot storage slot = _slot(slotId);
|
||||||
require(!slot.hostPaid, "Already paid");
|
require(!slot.hostPaid, "Already paid");
|
||||||
activeSlots.remove(_toBytes32SetMapKey(requestId),
|
|
||||||
slot.host,
|
slotsPerHost[slot.host].remove(SlotId.unwrap(slotId));
|
||||||
SlotId.unwrap(slotId));
|
|
||||||
activeRequestsForHosts.remove(_toBytes32AddressSetMapKey(requestId),
|
|
||||||
slot.host);
|
|
||||||
uint256 amount = pricePerSlot(requests[requestId]);
|
uint256 amount = pricePerSlot(requests[requestId]);
|
||||||
funds.sent += amount;
|
funds.sent += amount;
|
||||||
funds.balance -= amount;
|
funds.balance -= amount;
|
||||||
@ -251,10 +225,11 @@ contract Marketplace is Collateral, Proofs {
|
|||||||
// Update request state to Cancelled. Handle in the withdraw transaction
|
// 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
|
// as there needs to be someone to pay for the gas to update the state
|
||||||
context.state = RequestState.Cancelled;
|
context.state = RequestState.Cancelled;
|
||||||
activeRequestsForClients.remove(_toAddressSetMapKey(request.client),
|
activeRequestsForClients.remove(
|
||||||
RequestId.unwrap(requestId));
|
_toAddressSetMapKey(request.client),
|
||||||
activeRequestsForHosts.clear(_toBytes32AddressSetMapKey(requestId));
|
RequestId.unwrap(requestId)
|
||||||
activeSlots.clear(_toBytes32SetMapKey(requestId));
|
);
|
||||||
|
|
||||||
emit RequestCancelled(requestId);
|
emit RequestCancelled(requestId);
|
||||||
|
|
||||||
// TODO: To be changed once we start paying out hosts for the time they
|
// TODO: To be changed once we start paying out hosts for the time they
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
pragma solidity ^0.8.8;
|
pragma solidity ^0.8.8;
|
||||||
|
|
||||||
|
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
|
||||||
|
|
||||||
library Utils {
|
library Utils {
|
||||||
|
using EnumerableSet for EnumerableSet.Bytes32Set;
|
||||||
|
|
||||||
function _resize(bytes32[] memory array, uint8 newSize)
|
function _resize(bytes32[] memory array, uint8 newSize)
|
||||||
internal
|
internal
|
||||||
pure
|
pure
|
||||||
@ -20,4 +24,23 @@ library Utils {
|
|||||||
return sized;
|
return sized;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function filter(
|
||||||
|
EnumerableSet.Bytes32Set storage set,
|
||||||
|
function(bytes32) internal view returns (bool) include
|
||||||
|
) internal view returns (bytes32[] memory result) {
|
||||||
|
bytes32[] memory selected = new bytes32[](set.length());
|
||||||
|
uint256 amount = 0;
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < set.length(); i++) {
|
||||||
|
if (include(set.at(i))) {
|
||||||
|
selected[amount++] = set.at(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = new bytes32[](amount);
|
||||||
|
for (uint256 i = 0; i < result.length; i++) {
|
||||||
|
result[i] = selected[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -738,56 +738,6 @@ describe("Marketplace", function () {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("list of active requests for host", function () {
|
|
||||||
beforeEach(async function () {
|
|
||||||
switchAccount(client)
|
|
||||||
await token.approve(marketplace.address, price(request))
|
|
||||||
await marketplace.requestStorage(request)
|
|
||||||
switchAccount(host)
|
|
||||||
await token.approve(marketplace.address, collateral)
|
|
||||||
await marketplace.deposit(collateral)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("is empty when no slot is filled", async function () {
|
|
||||||
expect(await marketplace.requestsForHost(host.address)).to.deep.equal([])
|
|
||||||
})
|
|
||||||
|
|
||||||
it("adds request to list when filling slot", async function () {
|
|
||||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
|
||||||
expect(await marketplace.requestsForHost(host.address)).to.deep.equal([
|
|
||||||
slot.request,
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
it("removes request from list when cancelled", async function () {
|
|
||||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
|
||||||
await waitUntilCancelled(request)
|
|
||||||
expect(await marketplace.requestsForHost(host.address)).to.deep.equal([])
|
|
||||||
})
|
|
||||||
|
|
||||||
it("removes request from list when funds are withdrawn", async function () {
|
|
||||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
|
||||||
await waitUntilCancelled(request)
|
|
||||||
switchAccount(client)
|
|
||||||
await marketplace.withdrawFunds(slot.request)
|
|
||||||
expect(await marketplace.requestsForHost(host.address)).to.deep.equal([])
|
|
||||||
})
|
|
||||||
|
|
||||||
it("removes request from list when request fails", async function () {
|
|
||||||
await waitUntilStarted(marketplace, request, proof)
|
|
||||||
await waitUntilFailed(marketplace, request, slot)
|
|
||||||
expect(await marketplace.requestsForHost(host.address)).to.deep.equal([])
|
|
||||||
})
|
|
||||||
|
|
||||||
it("removes request from list when request finishes", async function () {
|
|
||||||
switchAccount(host)
|
|
||||||
await waitUntilStarted(marketplace, request, proof)
|
|
||||||
await waitUntilFinished(marketplace, requestId(request))
|
|
||||||
await marketplace.payoutSlot(slot.request, slot.index)
|
|
||||||
expect(await marketplace.requestsForHost(host.address)).to.deep.equal([])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("list of active slots", function () {
|
describe("list of active slots", function () {
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
switchAccount(client)
|
switchAccount(client)
|
||||||
@ -802,7 +752,7 @@ describe("Marketplace", function () {
|
|||||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||||
let slot1 = { ...slot, index: slot.index + 1 }
|
let slot1 = { ...slot, index: slot.index + 1 }
|
||||||
await marketplace.fillSlot(slot.request, slot1.index, proof)
|
await marketplace.fillSlot(slot.request, slot1.index, proof)
|
||||||
expect(await marketplace.mySlots(slot.request)).to.deep.equal([
|
expect(await marketplace.mySlots()).to.have.members([
|
||||||
slotId(slot),
|
slotId(slot),
|
||||||
slotId(slot1),
|
slotId(slot1),
|
||||||
])
|
])
|
||||||
@ -813,9 +763,7 @@ describe("Marketplace", function () {
|
|||||||
let slot1 = { ...slot, index: slot.index + 1 }
|
let slot1 = { ...slot, index: slot.index + 1 }
|
||||||
await marketplace.fillSlot(slot.request, slot1.index, proof)
|
await marketplace.fillSlot(slot.request, slot1.index, proof)
|
||||||
await marketplace.freeSlot(slotId(slot))
|
await marketplace.freeSlot(slotId(slot))
|
||||||
expect(await marketplace.mySlots(slot.request)).to.deep.equal([
|
expect(await marketplace.mySlots()).to.have.members([slotId(slot1)])
|
||||||
slotId(slot1),
|
|
||||||
])
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("returns no slots when cancelled", async function () {
|
it("returns no slots when cancelled", async function () {
|
||||||
@ -823,7 +771,7 @@ describe("Marketplace", function () {
|
|||||||
let slot1 = { ...slot, index: slot.index + 1 }
|
let slot1 = { ...slot, index: slot.index + 1 }
|
||||||
await marketplace.fillSlot(slot.request, slot1.index, proof)
|
await marketplace.fillSlot(slot.request, slot1.index, proof)
|
||||||
await waitUntilCancelled(request)
|
await waitUntilCancelled(request)
|
||||||
expect(await marketplace.mySlots(slot.request)).to.deep.equal([])
|
expect(await marketplace.mySlots()).to.have.members([])
|
||||||
})
|
})
|
||||||
|
|
||||||
it("removes active slots for all hosts in a request when it fails", async function () {
|
it("removes active slots for all hosts in a request when it fails", async function () {
|
||||||
@ -843,9 +791,9 @@ describe("Marketplace", function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await waitUntilFailed(marketplace, request, slot)
|
await waitUntilFailed(marketplace, request, slot)
|
||||||
expect(await marketplace.mySlots(slot.request)).to.deep.equal([])
|
expect(await marketplace.mySlots()).to.have.members([])
|
||||||
switchAccount(host)
|
switchAccount(host)
|
||||||
expect(await marketplace.mySlots(slot.request)).to.deep.equal([])
|
expect(await marketplace.mySlots()).to.have.members([])
|
||||||
})
|
})
|
||||||
|
|
||||||
it("doesn't remove active slots for hosts in request that didn't fail", async function () {
|
it("doesn't remove active slots for hosts in request that didn't fail", async function () {
|
||||||
@ -872,19 +820,14 @@ describe("Marketplace", function () {
|
|||||||
let id = slotId(expectedSlot)
|
let id = slotId(expectedSlot)
|
||||||
expected.push(id)
|
expected.push(id)
|
||||||
}
|
}
|
||||||
expect(await marketplace.mySlots(slot.request)).to.deep.equal([])
|
expect(await marketplace.mySlots()).to.have.members(expected)
|
||||||
expect(await marketplace.mySlots(expectedSlot.request)).to.deep.equal(
|
|
||||||
expected
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("removes slots from list when request finishes", async function () {
|
it("removes slot from list when slot is paid out", async function () {
|
||||||
await waitUntilStarted(marketplace, request, proof)
|
await waitUntilStarted(marketplace, request, proof)
|
||||||
await waitUntilFinished(marketplace, requestId(request))
|
await waitUntilFinished(marketplace, requestId(request))
|
||||||
await marketplace.payoutSlot(slot.request, slot.index)
|
await marketplace.payoutSlot(slot.request, slot.index)
|
||||||
expect(await marketplace.mySlots(slot.request)).to.not.contain(
|
expect(await marketplace.mySlots()).to.not.contain(slotId(slot))
|
||||||
slotId(slot)
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user