[marketplace] introduce MarketplaceConfiguration struct
Container for all configuration values, replaces separate constructor parameters and getters.
This commit is contained in:
parent
91ccc82d49
commit
ae70fd7c6f
|
@ -0,0 +1,22 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8.8;
|
||||||
|
|
||||||
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
|
|
||||||
|
struct MarketplaceConfig {
|
||||||
|
CollateralConfig collateral;
|
||||||
|
ProofConfig proofs;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CollateralConfig {
|
||||||
|
uint256 initialAmount; // amount of collateral necessary to fill a slot
|
||||||
|
uint256 minimumAmount; // frees slot when collateral drops below this minimum
|
||||||
|
uint256 slashCriterion; // amount of proofs missed that lead to slashing
|
||||||
|
uint256 slashPercentage; // percentage of the collateral that is slashed
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ProofConfig {
|
||||||
|
uint256 period; // proofs requirements are calculated per period (in seconds)
|
||||||
|
uint256 timeout; // mark proofs as missing before the timeout (in seconds)
|
||||||
|
uint8 downtime; // ignore this much recent blocks for proof requirements
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ pragma solidity ^0.8.8;
|
||||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
import "@openzeppelin/contracts/utils/math/Math.sol";
|
import "@openzeppelin/contracts/utils/math/Math.sol";
|
||||||
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
|
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
|
||||||
|
import "./Configuration.sol";
|
||||||
import "./Requests.sol";
|
import "./Requests.sol";
|
||||||
import "./Collateral.sol";
|
import "./Collateral.sol";
|
||||||
import "./Proofs.sol";
|
import "./Proofs.sol";
|
||||||
|
@ -13,10 +14,7 @@ contract Marketplace is Collateral, Proofs, StateRetrieval {
|
||||||
using EnumerableSet for EnumerableSet.Bytes32Set;
|
using EnumerableSet for EnumerableSet.Bytes32Set;
|
||||||
using Requests for Request;
|
using Requests for Request;
|
||||||
|
|
||||||
uint256 public immutable collateral;
|
MarketplaceConfig public config;
|
||||||
uint256 public immutable minCollateralThreshold;
|
|
||||||
uint256 public immutable slashMisses;
|
|
||||||
uint256 public immutable slashPercentage;
|
|
||||||
|
|
||||||
MarketplaceFunds private funds;
|
MarketplaceFunds private funds;
|
||||||
mapping(RequestId => Request) private requests;
|
mapping(RequestId => Request) private requests;
|
||||||
|
@ -24,23 +22,10 @@ contract Marketplace is Collateral, Proofs, StateRetrieval {
|
||||||
mapping(SlotId => Slot) private slots;
|
mapping(SlotId => Slot) private slots;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
IERC20 _token,
|
IERC20 token,
|
||||||
uint256 _collateral,
|
MarketplaceConfig memory configuration
|
||||||
uint256 _minCollateralThreshold,
|
) Collateral(token) Proofs(configuration.proofs) marketplaceInvariant {
|
||||||
uint256 _slashMisses,
|
config = configuration;
|
||||||
uint256 _slashPercentage,
|
|
||||||
uint256 _proofPeriod,
|
|
||||||
uint256 _proofTimeout,
|
|
||||||
uint8 _proofDowntime
|
|
||||||
)
|
|
||||||
Collateral(_token)
|
|
||||||
Proofs(_proofPeriod, _proofTimeout, _proofDowntime)
|
|
||||||
marketplaceInvariant
|
|
||||||
{
|
|
||||||
collateral = _collateral;
|
|
||||||
minCollateralThreshold = _minCollateralThreshold;
|
|
||||||
slashMisses = _slashMisses;
|
|
||||||
slashPercentage = _slashPercentage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isWithdrawAllowed() internal view override returns (bool) {
|
function isWithdrawAllowed() internal view override returns (bool) {
|
||||||
|
@ -82,7 +67,10 @@ contract Marketplace is Collateral, Proofs, StateRetrieval {
|
||||||
|
|
||||||
require(slotState(slotId) == SlotState.Free, "Slot is not free");
|
require(slotState(slotId) == SlotState.Free, "Slot is not free");
|
||||||
|
|
||||||
require(balanceOf(msg.sender) >= collateral, "Insufficient collateral");
|
require(
|
||||||
|
balanceOf(msg.sender) >= config.collateral.initialAmount,
|
||||||
|
"Insufficient collateral"
|
||||||
|
);
|
||||||
|
|
||||||
_startRequiringProofs(slotId, request.ask.proofProbability);
|
_startRequiringProofs(slotId, request.ask.proofProbability);
|
||||||
submitProof(slotId, proof);
|
submitProof(slotId, proof);
|
||||||
|
@ -120,10 +108,10 @@ contract Marketplace is Collateral, Proofs, StateRetrieval {
|
||||||
require(slotState(slotId) == SlotState.Filled, "Slot not accepting proofs");
|
require(slotState(slotId) == SlotState.Filled, "Slot not accepting proofs");
|
||||||
_markProofAsMissing(slotId, period);
|
_markProofAsMissing(slotId, period);
|
||||||
address host = getHost(slotId);
|
address host = getHost(slotId);
|
||||||
if (missingProofs(slotId) % slashMisses == 0) {
|
if (missingProofs(slotId) % config.collateral.slashCriterion == 0) {
|
||||||
_slash(host, slashPercentage);
|
_slash(host, config.collateral.slashPercentage);
|
||||||
|
|
||||||
if (balanceOf(host) < minCollateralThreshold) {
|
if (balanceOf(host) < config.collateral.minimumAmount) {
|
||||||
// When the collateral drops below the minimum threshold, the slot
|
// When the collateral drops below the minimum threshold, the slot
|
||||||
// needs to be freed so that there is enough remaining collateral to be
|
// needs to be freed so that there is enough remaining collateral to be
|
||||||
// distributed for repairs and rewards (with any leftover to be burnt).
|
// distributed for repairs and rewards (with any leftover to be burnt).
|
||||||
|
|
|
@ -1,21 +1,16 @@
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
pragma solidity ^0.8.8;
|
pragma solidity ^0.8.8;
|
||||||
|
|
||||||
|
import "./Configuration.sol";
|
||||||
import "./Requests.sol";
|
import "./Requests.sol";
|
||||||
import "./Periods.sol";
|
import "./Periods.sol";
|
||||||
|
|
||||||
abstract contract Proofs is Periods {
|
abstract contract Proofs is Periods {
|
||||||
uint256 public immutable proofTimeout;
|
ProofConfig private config;
|
||||||
uint8 private immutable downtime;
|
|
||||||
|
|
||||||
constructor(
|
constructor(ProofConfig memory _config) Periods(_config.period) {
|
||||||
uint256 __period,
|
|
||||||
uint256 __timeout,
|
|
||||||
uint8 __downtime
|
|
||||||
) Periods(__period) {
|
|
||||||
require(block.number > 256, "Insufficient block height");
|
require(block.number > 256, "Insufficient block height");
|
||||||
proofTimeout = __timeout;
|
config = _config;
|
||||||
downtime = __downtime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mapping(SlotId => uint256) private slotStarts;
|
mapping(SlotId => uint256) private slotStarts;
|
||||||
|
@ -26,10 +21,6 @@ abstract contract Proofs is Periods {
|
||||||
|
|
||||||
function slotState(SlotId id) internal view virtual returns (SlotState);
|
function slotState(SlotId id) internal view virtual returns (SlotState);
|
||||||
|
|
||||||
function proofPeriod() public view returns (uint256) {
|
|
||||||
return secondsPerPeriod;
|
|
||||||
}
|
|
||||||
|
|
||||||
function missingProofs(SlotId slotId) public view returns (uint256) {
|
function missingProofs(SlotId slotId) public view returns (uint256) {
|
||||||
return missed[slotId];
|
return missed[slotId];
|
||||||
}
|
}
|
||||||
|
@ -79,7 +70,7 @@ abstract contract Proofs is Periods {
|
||||||
}
|
}
|
||||||
pointer = getPointer(id, period);
|
pointer = getPointer(id, period);
|
||||||
bytes32 challenge = getChallenge(pointer);
|
bytes32 challenge = getChallenge(pointer);
|
||||||
uint256 probability = (probabilities[id] * (256 - downtime)) / 256;
|
uint256 probability = (probabilities[id] * (256 - config.downtime)) / 256;
|
||||||
isRequired = uint256(challenge) % probability == 0;
|
isRequired = uint256(challenge) % probability == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +81,7 @@ abstract contract Proofs is Periods {
|
||||||
bool isRequired;
|
bool isRequired;
|
||||||
uint8 pointer;
|
uint8 pointer;
|
||||||
(isRequired, pointer) = _getProofRequirement(id, period);
|
(isRequired, pointer) = _getProofRequirement(id, period);
|
||||||
return isRequired && pointer >= downtime;
|
return isRequired && pointer >= config.downtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isProofRequired(SlotId id) public view returns (bool) {
|
function isProofRequired(SlotId id) public view returns (bool) {
|
||||||
|
@ -101,7 +92,7 @@ abstract contract Proofs is Periods {
|
||||||
bool isRequired;
|
bool isRequired;
|
||||||
uint8 pointer;
|
uint8 pointer;
|
||||||
(isRequired, pointer) = _getProofRequirement(id, blockPeriod());
|
(isRequired, pointer) = _getProofRequirement(id, blockPeriod());
|
||||||
return isRequired && pointer < downtime;
|
return isRequired && pointer < config.downtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
function submitProof(SlotId id, bytes calldata proof) public {
|
function submitProof(SlotId id, bytes calldata proof) public {
|
||||||
|
@ -112,9 +103,9 @@ abstract contract Proofs is Periods {
|
||||||
}
|
}
|
||||||
|
|
||||||
function _markProofAsMissing(SlotId id, Period missedPeriod) internal {
|
function _markProofAsMissing(SlotId id, Period missedPeriod) internal {
|
||||||
uint256 periodEnd = periodEnd(missedPeriod);
|
uint256 end = periodEnd(missedPeriod);
|
||||||
require(periodEnd < block.timestamp, "Period has not ended yet");
|
require(end < block.timestamp, "Period has not ended yet");
|
||||||
require(block.timestamp < periodEnd + proofTimeout, "Validation timed out");
|
require(block.timestamp < end + config.timeout, "Validation timed out");
|
||||||
require(!received[id][missedPeriod], "Proof was submitted, not missing");
|
require(!received[id][missedPeriod], "Proof was submitted, not missing");
|
||||||
require(isProofRequired(id, missedPeriod), "Proof was not required");
|
require(isProofRequired(id, missedPeriod), "Proof was not required");
|
||||||
require(!missing[id][missedPeriod], "Proof already marked as missing");
|
require(!missing[id][missedPeriod], "Proof already marked as missing");
|
||||||
|
|
|
@ -6,26 +6,9 @@ import "./Marketplace.sol";
|
||||||
// exposes internal functions of Marketplace for testing
|
// exposes internal functions of Marketplace for testing
|
||||||
contract TestMarketplace is Marketplace {
|
contract TestMarketplace is Marketplace {
|
||||||
constructor(
|
constructor(
|
||||||
IERC20 _token,
|
IERC20 token,
|
||||||
uint256 _collateral,
|
MarketplaceConfig memory config
|
||||||
uint256 _minCollateralThreshold,
|
) Marketplace(token, config) // solhint-disable-next-line no-empty-blocks
|
||||||
uint256 _slashMisses,
|
|
||||||
uint256 _slashPercentage,
|
|
||||||
uint256 _proofPeriod,
|
|
||||||
uint256 _proofTimeout,
|
|
||||||
uint8 _proofDowntime
|
|
||||||
)
|
|
||||||
Marketplace(
|
|
||||||
_token,
|
|
||||||
_collateral,
|
|
||||||
_minCollateralThreshold,
|
|
||||||
_slashMisses,
|
|
||||||
_slashPercentage,
|
|
||||||
_proofPeriod,
|
|
||||||
_proofTimeout,
|
|
||||||
_proofDowntime
|
|
||||||
)
|
|
||||||
// solhint-disable-next-line no-empty-blocks
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,16 +7,8 @@ import "./Proofs.sol";
|
||||||
contract TestProofs is Proofs {
|
contract TestProofs is Proofs {
|
||||||
mapping(SlotId => SlotState) private states;
|
mapping(SlotId => SlotState) private states;
|
||||||
|
|
||||||
constructor(
|
|
||||||
uint256 __period,
|
|
||||||
uint256 __timeout,
|
|
||||||
uint8 __downtime
|
|
||||||
)
|
|
||||||
Proofs(__period, __timeout, __downtime)
|
|
||||||
// solhint-disable-next-line no-empty-blocks
|
// solhint-disable-next-line no-empty-blocks
|
||||||
{
|
constructor(ProofConfig memory config) Proofs(config) {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function slotState(SlotId slotId) internal view override returns (SlotState) {
|
function slotState(SlotId slotId) internal view override returns (SlotState) {
|
||||||
return states[slotId];
|
return states[slotId];
|
||||||
|
|
|
@ -3,7 +3,7 @@ const { hexlify, randomBytes } = ethers.utils
|
||||||
const { AddressZero } = ethers.constants
|
const { AddressZero } = ethers.constants
|
||||||
const { BigNumber } = ethers
|
const { BigNumber } = ethers
|
||||||
const { expect } = require("chai")
|
const { expect } = require("chai")
|
||||||
const { exampleRequest } = require("./examples")
|
const { exampleConfiguration, exampleRequest } = require("./examples")
|
||||||
const { periodic, hours } = require("./time")
|
const { periodic, hours } = require("./time")
|
||||||
const { requestId, slotId, askToArray } = require("./ids")
|
const { requestId, slotId, askToArray } = require("./ids")
|
||||||
const { RequestState } = require("./requests")
|
const { RequestState } = require("./requests")
|
||||||
|
@ -26,14 +26,8 @@ const {
|
||||||
} = require("./evm")
|
} = require("./evm")
|
||||||
|
|
||||||
describe("Marketplace", function () {
|
describe("Marketplace", function () {
|
||||||
const collateral = 100
|
|
||||||
const minCollateralThreshold = 40
|
|
||||||
const slashMisses = 3
|
|
||||||
const slashPercentage = 10
|
|
||||||
const proofPeriod = 10
|
|
||||||
const proofTimeout = 5
|
|
||||||
const proofDowntime = 64
|
|
||||||
const proof = hexlify(randomBytes(42))
|
const proof = hexlify(randomBytes(42))
|
||||||
|
const config = exampleConfiguration()
|
||||||
|
|
||||||
let marketplace
|
let marketplace
|
||||||
let token
|
let token
|
||||||
|
@ -54,16 +48,7 @@ describe("Marketplace", function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Marketplace = await ethers.getContractFactory("TestMarketplace")
|
const Marketplace = await ethers.getContractFactory("TestMarketplace")
|
||||||
marketplace = await Marketplace.deploy(
|
marketplace = await Marketplace.deploy(token.address, config)
|
||||||
token.address,
|
|
||||||
collateral,
|
|
||||||
minCollateralThreshold,
|
|
||||||
slashMisses,
|
|
||||||
slashPercentage,
|
|
||||||
proofPeriod,
|
|
||||||
proofTimeout,
|
|
||||||
proofDowntime
|
|
||||||
)
|
|
||||||
|
|
||||||
request = await exampleRequest()
|
request = await exampleRequest()
|
||||||
request.client = client.address
|
request.client = client.address
|
||||||
|
@ -136,8 +121,8 @@ describe("Marketplace", function () {
|
||||||
await token.approve(marketplace.address, price(request))
|
await token.approve(marketplace.address, price(request))
|
||||||
await marketplace.requestStorage(request)
|
await marketplace.requestStorage(request)
|
||||||
switchAccount(host)
|
switchAccount(host)
|
||||||
await token.approve(marketplace.address, collateral)
|
await token.approve(marketplace.address, config.collateral.initialAmount)
|
||||||
await marketplace.deposit(collateral)
|
await marketplace.deposit(config.collateral.initialAmount)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("emits event when slot is filled", async function () {
|
it("emits event when slot is filled", async function () {
|
||||||
|
@ -160,7 +145,7 @@ describe("Marketplace", function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it("is rejected when collateral is insufficient", async function () {
|
it("is rejected when collateral is insufficient", async function () {
|
||||||
let insufficient = collateral - 1
|
let insufficient = config.collateral.initialAmount - 1
|
||||||
await marketplace.withdraw()
|
await marketplace.withdraw()
|
||||||
await token.approve(marketplace.address, insufficient)
|
await token.approve(marketplace.address, insufficient)
|
||||||
await marketplace.deposit(insufficient)
|
await marketplace.deposit(insufficient)
|
||||||
|
@ -236,8 +221,8 @@ describe("Marketplace", function () {
|
||||||
await marketplace.requestStorage(request)
|
await marketplace.requestStorage(request)
|
||||||
requestTime = await currentTime()
|
requestTime = await currentTime()
|
||||||
switchAccount(host)
|
switchAccount(host)
|
||||||
await token.approve(marketplace.address, collateral)
|
await token.approve(marketplace.address, config.collateral.initialAmount)
|
||||||
await marketplace.deposit(collateral)
|
await marketplace.deposit(config.collateral.initialAmount)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("sets the request end time to now + duration", async function () {
|
it("sets the request end time to now + duration", async function () {
|
||||||
|
@ -289,8 +274,8 @@ describe("Marketplace", function () {
|
||||||
await token.approve(marketplace.address, price(request))
|
await token.approve(marketplace.address, price(request))
|
||||||
await marketplace.requestStorage(request)
|
await marketplace.requestStorage(request)
|
||||||
switchAccount(host)
|
switchAccount(host)
|
||||||
await token.approve(marketplace.address, collateral)
|
await token.approve(marketplace.address, config.collateral.initialAmount)
|
||||||
await marketplace.deposit(collateral)
|
await marketplace.deposit(config.collateral.initialAmount)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("fails to free slot when slot not filled", async function () {
|
it("fails to free slot when slot not filled", async function () {
|
||||||
|
@ -328,8 +313,8 @@ describe("Marketplace", function () {
|
||||||
await token.approve(marketplace.address, price(request))
|
await token.approve(marketplace.address, price(request))
|
||||||
await marketplace.requestStorage(request)
|
await marketplace.requestStorage(request)
|
||||||
switchAccount(host)
|
switchAccount(host)
|
||||||
await token.approve(marketplace.address, collateral)
|
await token.approve(marketplace.address, config.collateral.initialAmount)
|
||||||
await marketplace.deposit(collateral)
|
await marketplace.deposit(config.collateral.initialAmount)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("pays the host when contract has finished", async function () {
|
it("pays the host when contract has finished", async function () {
|
||||||
|
@ -382,8 +367,8 @@ describe("Marketplace", function () {
|
||||||
await token.approve(marketplace.address, price(request))
|
await token.approve(marketplace.address, price(request))
|
||||||
await marketplace.requestStorage(request)
|
await marketplace.requestStorage(request)
|
||||||
switchAccount(host)
|
switchAccount(host)
|
||||||
await token.approve(marketplace.address, collateral)
|
await token.approve(marketplace.address, config.collateral.initialAmount)
|
||||||
await marketplace.deposit(collateral)
|
await marketplace.deposit(config.collateral.initialAmount)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("emits event when all slots are filled", async function () {
|
it("emits event when all slots are filled", async function () {
|
||||||
|
@ -421,8 +406,8 @@ describe("Marketplace", function () {
|
||||||
await token.approve(marketplace.address, price(request))
|
await token.approve(marketplace.address, price(request))
|
||||||
await marketplace.requestStorage(request)
|
await marketplace.requestStorage(request)
|
||||||
switchAccount(host)
|
switchAccount(host)
|
||||||
await token.approve(marketplace.address, collateral)
|
await token.approve(marketplace.address, config.collateral.initialAmount)
|
||||||
await marketplace.deposit(collateral)
|
await marketplace.deposit(config.collateral.initialAmount)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("rejects withdraw when request not yet timed out", async function () {
|
it("rejects withdraw when request not yet timed out", async function () {
|
||||||
|
@ -476,8 +461,8 @@ describe("Marketplace", function () {
|
||||||
await token.approve(marketplace.address, price(request))
|
await token.approve(marketplace.address, price(request))
|
||||||
await marketplace.requestStorage(request)
|
await marketplace.requestStorage(request)
|
||||||
switchAccount(host)
|
switchAccount(host)
|
||||||
await token.approve(marketplace.address, collateral)
|
await token.approve(marketplace.address, config.collateral.initialAmount)
|
||||||
await marketplace.deposit(collateral)
|
await marketplace.deposit(config.collateral.initialAmount)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("locks collateral of host when it fills a slot", async function () {
|
it("locks collateral of host when it fills a slot", async function () {
|
||||||
|
@ -504,8 +489,8 @@ describe("Marketplace", function () {
|
||||||
await token.approve(marketplace.address, price(request))
|
await token.approve(marketplace.address, price(request))
|
||||||
await marketplace.requestStorage(request)
|
await marketplace.requestStorage(request)
|
||||||
switchAccount(host)
|
switchAccount(host)
|
||||||
await token.approve(marketplace.address, collateral)
|
await token.approve(marketplace.address, config.collateral.initialAmount)
|
||||||
await marketplace.deposit(collateral)
|
await marketplace.deposit(config.collateral.initialAmount)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("changes state to Cancelled when client withdraws funds", async function () {
|
it("changes state to Cancelled when client withdraws funds", async function () {
|
||||||
|
@ -572,15 +557,15 @@ describe("Marketplace", function () {
|
||||||
let period, periodOf, periodEnd
|
let period, periodOf, periodEnd
|
||||||
|
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
period = (await marketplace.proofPeriod()).toNumber()
|
period = config.proofs.period
|
||||||
;({ periodOf, periodEnd } = periodic(period))
|
;({ periodOf, periodEnd } = periodic(period))
|
||||||
|
|
||||||
switchAccount(client)
|
switchAccount(client)
|
||||||
await token.approve(marketplace.address, price(request))
|
await token.approve(marketplace.address, price(request))
|
||||||
await marketplace.requestStorage(request)
|
await marketplace.requestStorage(request)
|
||||||
switchAccount(host)
|
switchAccount(host)
|
||||||
await token.approve(marketplace.address, collateral)
|
await token.approve(marketplace.address, config.collateral.initialAmount)
|
||||||
await marketplace.deposit(collateral)
|
await marketplace.deposit(config.collateral.initialAmount)
|
||||||
})
|
})
|
||||||
|
|
||||||
async function waitUntilProofWillBeRequired(id) {
|
async function waitUntilProofWillBeRequired(id) {
|
||||||
|
@ -652,15 +637,15 @@ describe("Marketplace", function () {
|
||||||
let period, periodOf, periodEnd
|
let period, periodOf, periodEnd
|
||||||
|
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
period = (await marketplace.proofPeriod()).toNumber()
|
period = config.proofs.period
|
||||||
;({ periodOf, periodEnd } = periodic(period))
|
;({ periodOf, periodEnd } = periodic(period))
|
||||||
|
|
||||||
switchAccount(client)
|
switchAccount(client)
|
||||||
await token.approve(marketplace.address, price(request))
|
await token.approve(marketplace.address, price(request))
|
||||||
await marketplace.requestStorage(request)
|
await marketplace.requestStorage(request)
|
||||||
switchAccount(host)
|
switchAccount(host)
|
||||||
await token.approve(marketplace.address, collateral)
|
await token.approve(marketplace.address, config.collateral.initialAmount)
|
||||||
await marketplace.deposit(collateral)
|
await marketplace.deposit(config.collateral.initialAmount)
|
||||||
})
|
})
|
||||||
|
|
||||||
async function waitUntilProofIsRequired(id) {
|
async function waitUntilProofIsRequired(id) {
|
||||||
|
@ -687,14 +672,16 @@ describe("Marketplace", function () {
|
||||||
describe("slashing when missing proofs", function () {
|
describe("slashing when missing proofs", function () {
|
||||||
it("reduces collateral when too many proofs are missing", async function () {
|
it("reduces collateral when too many proofs are missing", async function () {
|
||||||
const id = slotId(slot)
|
const id = slotId(slot)
|
||||||
|
const { slashCriterion, slashPercentage, initialAmount } =
|
||||||
|
config.collateral
|
||||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||||
for (let i = 0; i < slashMisses; i++) {
|
for (let i = 0; i < slashCriterion; i++) {
|
||||||
await waitUntilProofIsRequired(id)
|
await waitUntilProofIsRequired(id)
|
||||||
let missedPeriod = periodOf(await currentTime())
|
let missedPeriod = periodOf(await currentTime())
|
||||||
await advanceTime(period)
|
await advanceTime(period)
|
||||||
await marketplace.markProofAsMissing(id, missedPeriod)
|
await marketplace.markProofAsMissing(id, missedPeriod)
|
||||||
}
|
}
|
||||||
const expectedBalance = (collateral * (100 - slashPercentage)) / 100
|
const expectedBalance = (initialAmount * (100 - slashPercentage)) / 100
|
||||||
expect(await marketplace.balanceOf(host.address)).to.equal(
|
expect(await marketplace.balanceOf(host.address)).to.equal(
|
||||||
expectedBalance
|
expectedBalance
|
||||||
)
|
)
|
||||||
|
@ -707,8 +694,8 @@ describe("Marketplace", function () {
|
||||||
|
|
||||||
await waitUntilStarted(marketplace, request, proof)
|
await waitUntilStarted(marketplace, request, proof)
|
||||||
|
|
||||||
// max slashes before dropping below collateral threshold
|
const maxSlashes = 10 // slashes before going below collateral minimum
|
||||||
const maxSlashes = 10
|
const slashMisses = config.collateral.slashCriterion
|
||||||
for (let i = 0; i < maxSlashes; i++) {
|
for (let i = 0; i < maxSlashes; i++) {
|
||||||
for (let j = 0; j < slashMisses; j++) {
|
for (let j = 0; j < slashMisses; j++) {
|
||||||
await waitUntilProofIsRequired(id)
|
await waitUntilProofIsRequired(id)
|
||||||
|
@ -731,8 +718,8 @@ describe("Marketplace", function () {
|
||||||
describe("list of active requests", function () {
|
describe("list of active requests", function () {
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
switchAccount(host)
|
switchAccount(host)
|
||||||
await token.approve(marketplace.address, collateral)
|
await token.approve(marketplace.address, config.collateral.initialAmount)
|
||||||
await marketplace.deposit(collateral)
|
await marketplace.deposit(config.collateral.initialAmount)
|
||||||
switchAccount(client)
|
switchAccount(client)
|
||||||
await token.approve(marketplace.address, price(request))
|
await token.approve(marketplace.address, price(request))
|
||||||
})
|
})
|
||||||
|
@ -781,8 +768,8 @@ describe("Marketplace", function () {
|
||||||
await token.approve(marketplace.address, price(request))
|
await token.approve(marketplace.address, price(request))
|
||||||
await marketplace.requestStorage(request)
|
await marketplace.requestStorage(request)
|
||||||
switchAccount(host)
|
switchAccount(host)
|
||||||
await token.approve(marketplace.address, collateral)
|
await token.approve(marketplace.address, config.collateral.initialAmount)
|
||||||
await marketplace.deposit(collateral)
|
await marketplace.deposit(config.collateral.initialAmount)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("adds slot to list when filling slot", async function () {
|
it("adds slot to list when filling slot", async function () {
|
||||||
|
|
|
@ -27,7 +27,7 @@ describe("Proofs", function () {
|
||||||
await snapshot()
|
await snapshot()
|
||||||
await ensureMinimumBlockHeight(256)
|
await ensureMinimumBlockHeight(256)
|
||||||
const Proofs = await ethers.getContractFactory("TestProofs")
|
const Proofs = await ethers.getContractFactory("TestProofs")
|
||||||
proofs = await Proofs.deploy(period, timeout, downtime)
|
proofs = await Proofs.deploy({ period, timeout, downtime })
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(async function () {
|
afterEach(async function () {
|
||||||
|
|
|
@ -3,6 +3,20 @@ const { hours } = require("./time")
|
||||||
const { currentTime } = require("./evm")
|
const { currentTime } = require("./evm")
|
||||||
const { hexlify, randomBytes } = ethers.utils
|
const { hexlify, randomBytes } = ethers.utils
|
||||||
|
|
||||||
|
const exampleConfiguration = () => ({
|
||||||
|
collateral: {
|
||||||
|
initialAmount: 100,
|
||||||
|
minimumAmount: 40,
|
||||||
|
slashCriterion: 3,
|
||||||
|
slashPercentage: 10,
|
||||||
|
},
|
||||||
|
proofs: {
|
||||||
|
period: 10,
|
||||||
|
timeout: 5,
|
||||||
|
downtime: 64,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
const exampleRequest = async () => {
|
const exampleRequest = async () => {
|
||||||
const now = await currentTime()
|
const now = await currentTime()
|
||||||
return {
|
return {
|
||||||
|
@ -31,4 +45,4 @@ const exampleRequest = async () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { exampleRequest }
|
module.exports = { exampleConfiguration, exampleRequest }
|
||||||
|
|
Loading…
Reference in New Issue