mirror of
https://github.com/logos-storage/logos-storage-contracts-eth.git
synced 2026-01-05 14:53:08 +00:00
marketplace: use Timestamp, Duration and TokensPerSecond types
This commit is contained in:
parent
284b54e575
commit
83a59d8227
@ -2,12 +2,13 @@
|
||||
pragma solidity 0.8.28;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import "./Timestamps.sol";
|
||||
|
||||
struct MarketplaceConfig {
|
||||
CollateralConfig collateral;
|
||||
ProofConfig proofs;
|
||||
SlotReservationsConfig reservations;
|
||||
uint64 requestDurationLimit;
|
||||
Duration requestDurationLimit;
|
||||
}
|
||||
|
||||
struct CollateralConfig {
|
||||
|
||||
@ -13,7 +13,7 @@ contract FuzzMarketplace is Marketplace {
|
||||
CollateralConfig(10, 5, 10, 20),
|
||||
ProofConfig(10, 5, 64, 67, ""),
|
||||
SlotReservationsConfig(20),
|
||||
60 * 60 * 24 * 30 // 30 days
|
||||
Duration.wrap(60 * 60 * 24 * 30) // 30 days
|
||||
),
|
||||
new Vault(new TestToken()),
|
||||
new TestVerifier()
|
||||
|
||||
@ -50,6 +50,8 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
using VaultHelpers for Vault;
|
||||
using VaultHelpers for RequestId;
|
||||
using VaultHelpers for Request;
|
||||
using Timestamps for Timestamp;
|
||||
using Tokens for TokensPerSecond;
|
||||
|
||||
Vault private immutable _vault;
|
||||
MarketplaceConfig private _config;
|
||||
@ -71,9 +73,9 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
/// that would require all the slots to be filled at the same block as the request was created.
|
||||
uint256 fundsToReturnToClient;
|
||||
uint64 slotsFilled;
|
||||
uint64 startedAt;
|
||||
uint64 endsAt;
|
||||
uint64 expiresAt;
|
||||
Timestamp startedAt;
|
||||
Timestamp endsAt;
|
||||
Timestamp expiresAt;
|
||||
}
|
||||
|
||||
struct Slot {
|
||||
@ -82,7 +84,7 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
/// @notice Timestamp that signals when slot was filled
|
||||
/// @dev Used for calculating payouts as hosts are paid
|
||||
/// based on time they actually host the content
|
||||
uint64 filledAt;
|
||||
Timestamp filledAt;
|
||||
uint64 slotIndex;
|
||||
/// @notice Tracks the current amount of host's collateral that is
|
||||
/// to be payed out at the end of Slot's lifespan.
|
||||
@ -145,12 +147,14 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
if (_requests[id].client != address(0)) {
|
||||
revert Marketplace_RequestAlreadyExists();
|
||||
}
|
||||
if (request.expiry == 0 || request.expiry >= request.ask.duration)
|
||||
revert Marketplace_InvalidExpiry();
|
||||
if (
|
||||
request.expiry == Duration.wrap(0) ||
|
||||
request.expiry >= request.ask.duration
|
||||
) revert Marketplace_InvalidExpiry();
|
||||
if (request.ask.slots == 0) revert Marketplace_InsufficientSlots();
|
||||
if (request.ask.maxSlotLoss > request.ask.slots)
|
||||
revert Marketplace_InvalidMaxSlotLoss();
|
||||
if (request.ask.duration == 0) {
|
||||
if (request.ask.duration == Duration.wrap(0)) {
|
||||
revert Marketplace_InsufficientDuration();
|
||||
}
|
||||
if (request.ask.proofProbability == 0) {
|
||||
@ -159,7 +163,7 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
if (request.ask.collateralPerByte == 0) {
|
||||
revert Marketplace_InsufficientCollateral();
|
||||
}
|
||||
if (request.ask.pricePerBytePerSecond == 0) {
|
||||
if (request.ask.pricePerBytePerSecond == TokensPerSecond.wrap(0)) {
|
||||
revert Marketplace_InsufficientReward();
|
||||
}
|
||||
if (bytes(request.content.cid).length == 0) {
|
||||
@ -169,15 +173,15 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
revert Marketplace_DurationExceedsLimit();
|
||||
}
|
||||
|
||||
Timestamp currentTime = Timestamps.currentTime();
|
||||
|
||||
_requests[id] = request;
|
||||
_requestContexts[id].endsAt =
|
||||
uint64(block.timestamp) +
|
||||
request.ask.duration;
|
||||
_requestContexts[id].expiresAt = uint64(block.timestamp) + request.expiry;
|
||||
_requestContexts[id].endsAt = currentTime.add(request.ask.duration);
|
||||
_requestContexts[id].expiresAt = currentTime.add(request.expiry);
|
||||
|
||||
_addToMyRequests(request.client, id);
|
||||
|
||||
uint128 amount = uint128(request.maxPrice());
|
||||
uint128 amount = request.maxPrice();
|
||||
_requestContexts[id].fundsToReturnToClient = amount;
|
||||
_marketplaceTotals.received += amount;
|
||||
|
||||
@ -185,8 +189,8 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
AccountId account = _vault.clientAccount(request.client);
|
||||
_vault.lock(
|
||||
fund,
|
||||
Timestamp.wrap(uint40(_requestContexts[id].expiresAt)),
|
||||
Timestamp.wrap(uint40(_requestContexts[id].endsAt))
|
||||
_requestContexts[id].expiresAt,
|
||||
_requestContexts[id].endsAt
|
||||
);
|
||||
_transferToVault(request.client, fund, account, amount);
|
||||
|
||||
@ -229,8 +233,10 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
_startRequiringProofs(slotId);
|
||||
submitProof(slotId, proof);
|
||||
|
||||
Timestamp currentTime = Timestamps.currentTime();
|
||||
|
||||
slot.host = msg.sender;
|
||||
slot.filledAt = uint64(block.timestamp);
|
||||
slot.filledAt = currentTime;
|
||||
|
||||
context.slotsFilled += 1;
|
||||
context.fundsToReturnToClient -= _slotPayout(requestId, slot.filledAt);
|
||||
@ -252,9 +258,10 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
FundId fund = requestId.asFundId();
|
||||
AccountId clientAccount = _vault.clientAccount(request.client);
|
||||
AccountId hostAccount = _vault.hostAccount(slot.host, slotIndex);
|
||||
TokensPerSecond rate = request.ask.pricePerSlotPerSecond();
|
||||
|
||||
_transferToVault(slot.host, fund, hostAccount, uint128(collateralAmount));
|
||||
_vault.flow(fund, clientAccount, hostAccount, request.slotPrice());
|
||||
_vault.flow(fund, clientAccount, hostAccount, rate);
|
||||
|
||||
_marketplaceTotals.received += collateralAmount;
|
||||
slot.currentCollateral = collateralPerSlot; // Even if he has collateral discounted, he is operating with full collateral
|
||||
@ -269,8 +276,8 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
context.state == RequestState.New // Only New requests can "start" the requests
|
||||
) {
|
||||
context.state = RequestState.Started;
|
||||
context.startedAt = uint64(block.timestamp);
|
||||
_vault.extendLock(fund, Timestamp.wrap(uint40(context.endsAt)));
|
||||
context.startedAt = currentTime;
|
||||
_vault.extendLock(fund, context.endsAt);
|
||||
emit RequestFulfilled(requestId);
|
||||
}
|
||||
}
|
||||
@ -361,10 +368,7 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
_marketplaceTotals.sent += validatorRewardAmount;
|
||||
|
||||
FundId fund = slot.requestId.asFundId();
|
||||
AccountId hostAccount = _vault.hostAccount(
|
||||
slot.host,
|
||||
slot.slotIndex
|
||||
);
|
||||
AccountId hostAccount = _vault.hostAccount(slot.host, slot.slotIndex);
|
||||
AccountId validatorAccount = _vault.validatorAccount(msg.sender);
|
||||
_vault.transfer(
|
||||
fund,
|
||||
@ -400,18 +404,17 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
Request storage request = _requests[requestId];
|
||||
|
||||
FundId fund = requestId.asFundId();
|
||||
AccountId hostAccount = _vault.hostAccount(
|
||||
slot.host,
|
||||
slot.slotIndex
|
||||
);
|
||||
AccountId hostAccount = _vault.hostAccount(slot.host, slot.slotIndex);
|
||||
AccountId clientAccount = _vault.clientAccount(request.client);
|
||||
_vault.flow(fund, hostAccount, clientAccount, request.slotPrice());
|
||||
TokensPerSecond rate = request.ask.pricePerSlotPerSecond();
|
||||
|
||||
_vault.flow(fund, hostAccount, clientAccount, rate);
|
||||
_vault.burnAccount(fund, hostAccount);
|
||||
|
||||
_removeFromMySlots(slot.host, slotId);
|
||||
delete _reservations[slotId]; // We purge all the reservations for the slot
|
||||
slot.state = SlotState.Repair;
|
||||
slot.filledAt = 0;
|
||||
slot.filledAt = Timestamp.wrap(0);
|
||||
slot.currentCollateral = 0;
|
||||
slot.host = address(0);
|
||||
context.slotsFilled -= 1;
|
||||
@ -574,7 +577,7 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
return _slots[slotId].state == SlotState.Free;
|
||||
}
|
||||
|
||||
function requestEnd(RequestId requestId) public view returns (uint64) {
|
||||
function requestEnd(RequestId requestId) public view returns (Timestamp) {
|
||||
RequestState state = requestState(requestId);
|
||||
if (
|
||||
state == RequestState.New ||
|
||||
@ -586,11 +589,12 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
if (state == RequestState.Cancelled) {
|
||||
return _requestContexts[requestId].expiresAt;
|
||||
}
|
||||
return
|
||||
uint64(Math.min(_requestContexts[requestId].endsAt, block.timestamp));
|
||||
Timestamp currentTime = Timestamps.currentTime();
|
||||
Timestamp end = _requestContexts[requestId].endsAt;
|
||||
return Timestamps.earliest(end, currentTime);
|
||||
}
|
||||
|
||||
function requestExpiry(RequestId requestId) public view returns (uint64) {
|
||||
function requestExpiry(RequestId requestId) public view returns (Timestamp) {
|
||||
return _requestContexts[requestId].expiresAt;
|
||||
}
|
||||
|
||||
@ -598,33 +602,27 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
* @notice Calculates the amount that should be paid out to a host that successfully finished the request
|
||||
* @param requestId RequestId of the request used to calculate the payout
|
||||
* amount.
|
||||
* @param startingTimestamp timestamp indicating when a host filled a slot and
|
||||
* @param start timestamp indicating when a host filled a slot and
|
||||
* started providing proofs.
|
||||
*/
|
||||
function _slotPayout(
|
||||
RequestId requestId,
|
||||
uint64 startingTimestamp
|
||||
Timestamp start
|
||||
) private view returns (uint256) {
|
||||
return
|
||||
_slotPayout(
|
||||
requestId,
|
||||
startingTimestamp,
|
||||
_requestContexts[requestId].endsAt
|
||||
);
|
||||
return _slotPayout(requestId, start, _requestContexts[requestId].endsAt);
|
||||
}
|
||||
|
||||
/// @notice Calculates the amount that should be paid out to a host based on the specified time frame.
|
||||
function _slotPayout(
|
||||
RequestId requestId,
|
||||
uint64 startingTimestamp,
|
||||
uint64 endingTimestamp
|
||||
Timestamp start,
|
||||
Timestamp end
|
||||
) private view returns (uint256) {
|
||||
Request storage request = _requests[requestId];
|
||||
if (startingTimestamp >= endingTimestamp)
|
||||
if (end <= start) {
|
||||
revert Marketplace_StartNotBeforeExpiry();
|
||||
return
|
||||
(endingTimestamp - startingTimestamp) *
|
||||
request.ask.pricePerSlotPerSecond();
|
||||
}
|
||||
return request.ask.pricePerSlotPerSecond().accumulate(start.until(end));
|
||||
}
|
||||
|
||||
function getHost(SlotId slotId) public view returns (address) {
|
||||
@ -635,15 +633,15 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
RequestId requestId
|
||||
) public view requestIsKnown(requestId) returns (RequestState) {
|
||||
RequestContext storage context = _requestContexts[requestId];
|
||||
Timestamp currentTime = Timestamps.currentTime();
|
||||
if (
|
||||
context.state == RequestState.New &&
|
||||
uint64(block.timestamp) > requestExpiry(requestId)
|
||||
requestExpiry(requestId) < currentTime
|
||||
) {
|
||||
return RequestState.Cancelled;
|
||||
} else if (
|
||||
(context.state == RequestState.Started ||
|
||||
context.state == RequestState.New) &&
|
||||
uint64(block.timestamp) > context.endsAt
|
||||
context.state == RequestState.New) && context.endsAt < currentTime
|
||||
) {
|
||||
return RequestState.Finished;
|
||||
} else {
|
||||
@ -681,7 +679,7 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
(request.ask.proofProbability * (256 - _config.proofs.downtime)) / 256;
|
||||
}
|
||||
|
||||
event StorageRequested(RequestId requestId, Ask ask, uint64 expiry);
|
||||
event StorageRequested(RequestId requestId, Ask ask, Timestamp expiry);
|
||||
event RequestFulfilled(RequestId indexed requestId);
|
||||
event RequestFailed(RequestId indexed requestId);
|
||||
event SlotFilled(RequestId indexed requestId, uint64 slotIndex);
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.28;
|
||||
|
||||
import "./Timestamps.sol";
|
||||
import "./Tokens.sol";
|
||||
|
||||
type RequestId is bytes32;
|
||||
type SlotId is bytes32;
|
||||
|
||||
@ -8,17 +11,17 @@ struct Request {
|
||||
address client;
|
||||
Ask ask;
|
||||
Content content;
|
||||
uint64 expiry; // amount of seconds since start of the request at which this request expires
|
||||
Duration expiry; // amount of seconds since start of the request at which this request expires
|
||||
bytes32 nonce; // random nonce to differentiate between similar requests
|
||||
}
|
||||
|
||||
struct Ask {
|
||||
uint256 proofProbability; // how often storage proofs are required
|
||||
uint256 pricePerBytePerSecond; // amount of tokens paid per second per byte to hosts
|
||||
TokensPerSecond pricePerBytePerSecond; // amount of tokens paid per second per byte to hosts
|
||||
uint256 collateralPerByte; // amount of tokens per byte required to be deposited by the hosts in order to fill the slot
|
||||
uint64 slots; // the number of requested slots
|
||||
uint64 slotSize; // amount of storage per slot (in number of bytes)
|
||||
uint64 duration; // how long content should be stored (in seconds)
|
||||
Duration duration; // how long content should be stored (in seconds)
|
||||
uint64 maxSlotLoss; // Max slots that can be lost without data considered to be lost
|
||||
}
|
||||
|
||||
@ -52,12 +55,14 @@ library AskHelpers {
|
||||
|
||||
function pricePerSlotPerSecond(
|
||||
Ask memory ask
|
||||
) internal pure returns (uint256) {
|
||||
return ask.pricePerBytePerSecond * ask.slotSize;
|
||||
) internal pure returns (TokensPerSecond) {
|
||||
uint96 perByte = TokensPerSecond.unwrap(ask.pricePerBytePerSecond);
|
||||
return TokensPerSecond.wrap(perByte * ask.slotSize);
|
||||
}
|
||||
}
|
||||
|
||||
library Requests {
|
||||
using Tokens for TokensPerSecond;
|
||||
using AskHelpers for Ask;
|
||||
|
||||
function id(Request memory request) internal pure returns (RequestId) {
|
||||
@ -89,10 +94,10 @@ library Requests {
|
||||
}
|
||||
}
|
||||
|
||||
function maxPrice(Request memory request) internal pure returns (uint256) {
|
||||
return
|
||||
request.ask.slots *
|
||||
request.ask.duration *
|
||||
request.ask.pricePerSlotPerSecond();
|
||||
function maxPrice(Request memory request) internal pure returns (uint128) {
|
||||
uint64 slots = request.ask.slots;
|
||||
TokensPerSecond rate = request.ask.pricePerSlotPerSecond();
|
||||
Duration duration = request.ask.duration;
|
||||
return slots * rate.accumulate(duration);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,8 +5,6 @@ pragma solidity 0.8.28;
|
||||
/// since 1970). Uses a uint40 to facilitate efficient packing in structs. A
|
||||
/// uint40 allows times to be represented for the coming 30 000 years.
|
||||
type Timestamp is uint40;
|
||||
/// Represents a duration of time in seconds
|
||||
type Duration is uint40;
|
||||
|
||||
using {_timestampEquals as ==} for Timestamp global;
|
||||
using {_timestampNotEqual as !=} for Timestamp global;
|
||||
@ -29,12 +27,39 @@ function _timestampAtMost(Timestamp a, Timestamp b) pure returns (bool) {
|
||||
return Timestamp.unwrap(a) <= Timestamp.unwrap(b);
|
||||
}
|
||||
|
||||
/// Represents a duration of time in seconds
|
||||
type Duration is uint40;
|
||||
|
||||
using {_durationEquals as ==} for Duration global;
|
||||
using {_durationGreaterThan as >} for Duration global;
|
||||
using {_durationAtLeast as >=} for Duration global;
|
||||
|
||||
function _durationEquals(Duration a, Duration b) pure returns (bool) {
|
||||
return Duration.unwrap(a) == Duration.unwrap(b);
|
||||
}
|
||||
|
||||
function _durationGreaterThan(Duration a, Duration b) pure returns (bool) {
|
||||
return Duration.unwrap(a) > Duration.unwrap(b);
|
||||
}
|
||||
|
||||
function _durationAtLeast(Duration a, Duration b) pure returns (bool) {
|
||||
return Duration.unwrap(a) >= Duration.unwrap(b);
|
||||
}
|
||||
|
||||
library Timestamps {
|
||||
/// Returns the current block timestamp converted to a Timestamp type
|
||||
function currentTime() internal view returns (Timestamp) {
|
||||
return Timestamp.wrap(uint40(block.timestamp));
|
||||
}
|
||||
|
||||
// Adds a duration to a timestamp
|
||||
function add(
|
||||
Timestamp begin,
|
||||
Duration duration
|
||||
) internal pure returns (Timestamp) {
|
||||
return Timestamp.wrap(Timestamp.unwrap(begin) + Duration.unwrap(duration));
|
||||
}
|
||||
|
||||
/// Calculates the duration from start until end
|
||||
function until(
|
||||
Timestamp start,
|
||||
@ -42,4 +67,16 @@ library Timestamps {
|
||||
) internal pure returns (Duration) {
|
||||
return Duration.wrap(Timestamp.unwrap(end) - Timestamp.unwrap(start));
|
||||
}
|
||||
|
||||
/// Returns the earliest of the two timestamps
|
||||
function earliest(
|
||||
Timestamp a,
|
||||
Timestamp b
|
||||
) internal pure returns (Timestamp) {
|
||||
if (a <= b) {
|
||||
return a;
|
||||
} else {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,11 +43,4 @@ library VaultHelpers {
|
||||
function asFundId(RequestId requestId) internal pure returns (FundId) {
|
||||
return FundId.wrap(RequestId.unwrap(requestId));
|
||||
}
|
||||
|
||||
function slotPrice(
|
||||
Request memory request
|
||||
) internal pure returns (TokensPerSecond) {
|
||||
uint256 price = request.ask.pricePerBytePerSecond * request.ask.slotSize;
|
||||
return TokensPerSecond.wrap(uint96(price));
|
||||
}
|
||||
}
|
||||
|
||||
@ -499,9 +499,9 @@ describe("Marketplace", function () {
|
||||
it("sets the request end time to now + duration", async function () {
|
||||
await marketplace.reserveSlot(slot.request, slot.index)
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await expect(
|
||||
(await marketplace.requestEnd(requestId(request))).toNumber()
|
||||
).to.equal(requestTime + request.ask.duration)
|
||||
await expect(await marketplace.requestEnd(requestId(request))).to.equal(
|
||||
requestTime + request.ask.duration
|
||||
)
|
||||
})
|
||||
|
||||
it("sets request end time to the past once cancelled", async function () {
|
||||
@ -605,9 +605,7 @@ describe("Marketplace", function () {
|
||||
it("pays the host when contract was cancelled", async function () {
|
||||
// Lets advance the time more into the expiry window
|
||||
const filledAt = (await currentTime()) + Math.floor(request.expiry / 3)
|
||||
const expiresAt = (
|
||||
await marketplace.requestExpiry(requestId(request))
|
||||
).toNumber()
|
||||
const expiresAt = await marketplace.requestExpiry(requestId(request))
|
||||
|
||||
const startBalance = await token.balanceOf(host.address)
|
||||
await marketplace.reserveSlot(slot.request, slot.index)
|
||||
@ -822,9 +820,7 @@ describe("Marketplace", function () {
|
||||
it("withdraws to the client for cancelled requests lowered by hosts payout", async function () {
|
||||
// Lets advance the time more into the expiry window
|
||||
const filledAt = (await currentTime()) + Math.floor(request.expiry / 3)
|
||||
const expiresAt = (
|
||||
await marketplace.requestExpiry(requestId(request))
|
||||
).toNumber()
|
||||
const expiresAt = await marketplace.requestExpiry(requestId(request))
|
||||
|
||||
await marketplace.reserveSlot(slot.request, slot.index)
|
||||
await setNextBlockTimestamp(filledAt)
|
||||
|
||||
@ -2,10 +2,10 @@ const { ethers } = require("hardhat")
|
||||
const { keccak256, defaultAbiCoder } = ethers.utils
|
||||
|
||||
function requestId(request) {
|
||||
const Ask = "tuple(uint256, uint256, uint256, uint64, uint64, uint64, int64)"
|
||||
const Ask = "tuple(uint256, uint96, uint256, uint64, uint64, uint40, uint64)"
|
||||
const Content = "tuple(bytes, bytes32)"
|
||||
const Request =
|
||||
"tuple(address, " + Ask + ", " + Content + ", uint64, bytes32)"
|
||||
"tuple(address, " + Ask + ", " + Content + ", uint40, bytes32)"
|
||||
return keccak256(defaultAbiCoder.encode([Request], requestToArray(request)))
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ const { payoutForDuration } = require("./price")
|
||||
const { collateralPerSlot } = require("./collateral")
|
||||
|
||||
async function waitUntilCancelled(contract, request) {
|
||||
const expiry = (await contract.requestExpiry(requestId(request))).toNumber()
|
||||
const expiry = await contract.requestExpiry(requestId(request))
|
||||
// We do +1, because the expiry check in contract is done as `>` and not `>=`.
|
||||
await advanceTimeTo(expiry + 1)
|
||||
}
|
||||
@ -13,7 +13,7 @@ async function waitUntilSlotsFilled(contract, request, proof, token, slots) {
|
||||
let collateral = collateralPerSlot(request)
|
||||
await token.approve(contract.address, collateral * slots.length)
|
||||
|
||||
let requestEnd = (await contract.requestEnd(requestId(request))).toNumber()
|
||||
let requestEnd = await contract.requestEnd(requestId(request))
|
||||
const payouts = []
|
||||
for (let slotIndex of slots) {
|
||||
await contract.reserveSlot(requestId(request), slotIndex)
|
||||
@ -40,7 +40,7 @@ async function waitUntilStarted(contract, request, proof, token) {
|
||||
}
|
||||
|
||||
async function waitUntilFinished(contract, requestId) {
|
||||
const end = (await contract.requestEnd(requestId)).toNumber()
|
||||
const end = await contract.requestEnd(requestId)
|
||||
// We do +1, because the end check in contract is done as `>` and not `>=`.
|
||||
await advanceTimeTo(end + 1)
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
const { Assertion } = require("chai")
|
||||
const { currentTime } = require("./evm")
|
||||
|
||||
const RequestState = {
|
||||
New: 0,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user