mirror of
https://github.com/logos-storage/logos-storage-contracts-eth.git
synced 2026-03-29 14:53:21 +00:00
Feat: price per byte (#208)
* changes reward => pricePerByte * collateral => collateralPerByte * updates tests * introduces AskHelpers to compute price and collateral per slot * adds public view function returning currentCollateral for the slot * updates names for price and collateral * uses pricePerSlotPerSecond in maxPriceHelper * adds collateralPerSlot helper * makes sure that the intended use of the <<currentCollateral>> view function is demonstrated in tests * formatting * fix comment * mints more tokens so that it can be used with contracts tests in nim-codex * Renaming <<collateral>> and <<reward>> to <<collateralPerByte>> and <<pricePerBytePerSecond>> respectively (merged in the meantime to the master)
This commit is contained in:
parent
d04acafde2
commit
e74d3397a1
@ -42,6 +42,7 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
using EnumerableSet for EnumerableSet.Bytes32Set;
|
||||
using EnumerableSet for EnumerableSet.AddressSet;
|
||||
using Requests for Request;
|
||||
using AskHelpers for Ask;
|
||||
|
||||
IERC20 private immutable _token;
|
||||
MarketplaceConfig private _config;
|
||||
@ -72,14 +73,20 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
SlotState state;
|
||||
RequestId requestId;
|
||||
/// @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
|
||||
/// @dev Used for calculating payouts as hosts are paid
|
||||
/// based on time they actually host the content
|
||||
uint256 filledAt;
|
||||
uint256 slotIndex;
|
||||
/// @notice Tracks the current amount of host's collateral that is to be payed out at the end of Slot's lifespan.
|
||||
/// @dev When Slot is filled, the collateral is collected in amount of request.ask.collateral
|
||||
/// @dev When Host is slashed for missing a proof the slashed amount is reflected in this variable
|
||||
/// @notice Tracks the current amount of host's collateral that is
|
||||
/// to be payed out at the end of Slot's lifespan.
|
||||
/// @dev When Slot is filled, the collateral is collected in amount
|
||||
/// of request.ask.collateralPerByte * request.ask.slotSize
|
||||
/// (== request.ask.collateralPerSlot() when using the AskHelpers library)
|
||||
/// @dev When Host is slashed for missing a proof the slashed amount is
|
||||
/// reflected in this variable
|
||||
uint256 currentCollateral;
|
||||
address host; // address used for collateral interactions and identifying hosts
|
||||
/// @notice address used for collateral interactions and identifying hosts
|
||||
address host;
|
||||
}
|
||||
|
||||
struct ActiveSlot {
|
||||
@ -120,6 +127,10 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
return _token;
|
||||
}
|
||||
|
||||
function currentCollateral(SlotId slotId) public view returns (uint256) {
|
||||
return _slots[slotId].currentCollateral;
|
||||
}
|
||||
|
||||
function requestStorage(Request calldata request) public {
|
||||
RequestId id = request.id();
|
||||
|
||||
@ -137,10 +148,10 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
if (request.ask.proofProbability == 0) {
|
||||
revert Marketplace_InsufficientProofProbability();
|
||||
}
|
||||
if (request.ask.collateral == 0) {
|
||||
if (request.ask.collateralPerByte == 0) {
|
||||
revert Marketplace_InsufficientCollateral();
|
||||
}
|
||||
if (request.ask.reward == 0) {
|
||||
if (request.ask.pricePerBytePerSecond == 0) {
|
||||
revert Marketplace_InsufficientReward();
|
||||
}
|
||||
if (bytes(request.content.cid).length == 0) {
|
||||
@ -205,20 +216,20 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
|
||||
// Collect collateral
|
||||
uint256 collateralAmount;
|
||||
uint256 collateralPerSlot = request.ask.collateralPerSlot();
|
||||
if (slotState(slotId) == SlotState.Repair) {
|
||||
// Host is repairing a slot and is entitled for repair reward, so he gets "discounted collateral"
|
||||
// in this way he gets "physically" the reward at the end of the request when the full amount of collateral
|
||||
// is returned to him.
|
||||
collateralAmount =
|
||||
request.ask.collateral -
|
||||
((request.ask.collateral * _config.collateral.repairRewardPercentage) /
|
||||
100);
|
||||
collateralPerSlot -
|
||||
((collateralPerSlot * _config.collateral.repairRewardPercentage) / 100);
|
||||
} else {
|
||||
collateralAmount = request.ask.collateral;
|
||||
collateralAmount = collateralPerSlot;
|
||||
}
|
||||
_transferFrom(msg.sender, collateralAmount);
|
||||
_marketplaceTotals.received += collateralAmount;
|
||||
slot.currentCollateral = request.ask.collateral; // Even if he has collateral discounted, he is operating with full collateral
|
||||
slot.currentCollateral = collateralPerSlot; // Even if he has collateral discounted, he is operating with full collateral
|
||||
|
||||
_addToMySlots(slot.host, slotId);
|
||||
|
||||
@ -326,7 +337,7 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
// TODO: Reward for validator that calls this function
|
||||
|
||||
if (missingProofs(slotId) % _config.collateral.slashCriterion == 0) {
|
||||
uint256 slashedAmount = (request.ask.collateral *
|
||||
uint256 slashedAmount = (request.ask.collateralPerSlot() *
|
||||
_config.collateral.slashPercentage) / 100;
|
||||
slot.currentCollateral -= slashedAmount;
|
||||
if (
|
||||
@ -586,7 +597,9 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
|
||||
Request storage request = _requests[requestId];
|
||||
if (startingTimestamp >= endingTimestamp)
|
||||
revert Marketplace_StartNotBeforeExpiry();
|
||||
return (endingTimestamp - startingTimestamp) * request.ask.reward;
|
||||
return
|
||||
(endingTimestamp - startingTimestamp) *
|
||||
request.ask.pricePerSlotPerSecond();
|
||||
}
|
||||
|
||||
function getHost(SlotId slotId) public view returns (address) {
|
||||
|
||||
@ -17,8 +17,8 @@ struct Ask {
|
||||
uint256 slotSize; // amount of storage per slot (in number of bytes)
|
||||
uint256 duration; // how long content should be stored (in seconds)
|
||||
uint256 proofProbability; // how often storage proofs are required
|
||||
uint256 reward; // amount of tokens paid per second per slot to hosts
|
||||
uint256 collateral; // amount of tokens required to be deposited by the hosts in order to fill the slot
|
||||
uint256 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 maxSlotLoss; // Max slots that can be lost without data considered to be lost
|
||||
}
|
||||
|
||||
@ -45,7 +45,21 @@ enum SlotState {
|
||||
Repair // when slot slot was forcible freed (host was kicked out from hosting the slot because of too many missed proofs) and needs to be repaired
|
||||
}
|
||||
|
||||
library AskHelpers {
|
||||
function collateralPerSlot(Ask memory ask) internal pure returns (uint256) {
|
||||
return ask.collateralPerByte * ask.slotSize;
|
||||
}
|
||||
|
||||
function pricePerSlotPerSecond(
|
||||
Ask memory ask
|
||||
) internal pure returns (uint256) {
|
||||
return ask.pricePerBytePerSecond * ask.slotSize;
|
||||
}
|
||||
}
|
||||
|
||||
library Requests {
|
||||
using AskHelpers for Ask;
|
||||
|
||||
function id(Request memory request) internal pure returns (RequestId) {
|
||||
return RequestId.wrap(keccak256(abi.encode(request)));
|
||||
}
|
||||
@ -76,6 +90,9 @@ library Requests {
|
||||
}
|
||||
|
||||
function maxPrice(Request memory request) internal pure returns (uint256) {
|
||||
return request.ask.slots * request.ask.duration * request.ask.reward;
|
||||
return
|
||||
request.ask.slots *
|
||||
request.ask.duration *
|
||||
request.ask.pricePerSlotPerSecond();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,16 +1,31 @@
|
||||
const MINTED_TOKENS = 1_000_000_000
|
||||
const MINTED_TOKENS = 1_000_000_000_000_000
|
||||
|
||||
module.exports = async ({ deployments, getNamedAccounts, getUnnamedAccounts, network }) => {
|
||||
module.exports = async ({
|
||||
deployments,
|
||||
getNamedAccounts,
|
||||
getUnnamedAccounts,
|
||||
network,
|
||||
}) => {
|
||||
const { deployer } = await getNamedAccounts()
|
||||
const tokenDeployment = await deployments.deploy("TestToken", { from: deployer })
|
||||
const token = await hre.ethers.getContractAt("TestToken", tokenDeployment.address)
|
||||
const tokenDeployment = await deployments.deploy("TestToken", {
|
||||
from: deployer,
|
||||
})
|
||||
const token = await hre.ethers.getContractAt(
|
||||
"TestToken",
|
||||
tokenDeployment.address
|
||||
)
|
||||
|
||||
const accounts = [...Object.values(await getNamedAccounts()), ...(await getUnnamedAccounts())]
|
||||
const accounts = [
|
||||
...Object.values(await getNamedAccounts()),
|
||||
...(await getUnnamedAccounts()),
|
||||
]
|
||||
if (network.tags.local) {
|
||||
for (const account of accounts) {
|
||||
console.log(`Minting ${MINTED_TOKENS} tokens to address ${account}`)
|
||||
|
||||
const transaction = await token.mint(account, MINTED_TOKENS, { from: deployer })
|
||||
const transaction = await token.mint(account, MINTED_TOKENS, {
|
||||
from: deployer,
|
||||
})
|
||||
await transaction.wait()
|
||||
}
|
||||
console.log()
|
||||
|
||||
@ -23,7 +23,12 @@ const {
|
||||
waitUntilSlotFailed,
|
||||
patchOverloads,
|
||||
} = require("./marketplace")
|
||||
const { maxPrice, payoutForDuration } = require("./price")
|
||||
const {
|
||||
maxPrice,
|
||||
pricePerSlotPerSecond,
|
||||
payoutForDuration,
|
||||
} = require("./price")
|
||||
const { collateralPerSlot } = require("./collateral")
|
||||
const {
|
||||
snapshot,
|
||||
revert,
|
||||
@ -35,7 +40,7 @@ const {
|
||||
} = require("./evm")
|
||||
const { arrayify } = require("ethers/lib/utils")
|
||||
|
||||
const ACCOUNT_STARTING_BALANCE = 1_000_000_000
|
||||
const ACCOUNT_STARTING_BALANCE = 1_000_000_000_000_000
|
||||
|
||||
describe("Marketplace constructor", function () {
|
||||
let Marketplace, token, verifier, config
|
||||
@ -257,14 +262,14 @@ describe("Marketplace", function () {
|
||||
})
|
||||
|
||||
it("is rejected when insufficient collateral", async function () {
|
||||
request.ask.collateral = 0
|
||||
request.ask.collateralPerByte = 0
|
||||
await expect(marketplace.requestStorage(request)).to.be.revertedWith(
|
||||
"Marketplace_InsufficientCollateral"
|
||||
)
|
||||
})
|
||||
|
||||
it("is rejected when insufficient reward", async function () {
|
||||
request.ask.reward = 0
|
||||
request.ask.pricePerBytePerSecond = 0
|
||||
await expect(marketplace.requestStorage(request)).to.be.revertedWith(
|
||||
"Marketplace_InsufficientReward"
|
||||
)
|
||||
@ -284,7 +289,7 @@ describe("Marketplace", function () {
|
||||
await token.approve(marketplace.address, maxPrice(request))
|
||||
await marketplace.requestStorage(request)
|
||||
switchAccount(host)
|
||||
await token.approve(marketplace.address, request.ask.collateral)
|
||||
await token.approve(marketplace.address, collateralPerSlot(request))
|
||||
})
|
||||
|
||||
it("emits event when slot is filled", async function () {
|
||||
@ -315,10 +320,12 @@ describe("Marketplace", function () {
|
||||
await advanceTimeForNextBlock(config.proofs.period + 1)
|
||||
|
||||
const startBalance = await token.balanceOf(host.address)
|
||||
const collateral = collateralPerSlot(request)
|
||||
const discountedCollateral =
|
||||
request.ask.collateral -
|
||||
(request.ask.collateral * config.collateral.repairRewardPercentage) /
|
||||
100
|
||||
collateral -
|
||||
Math.round(
|
||||
(collateral * config.collateral.repairRewardPercentage) / 100
|
||||
)
|
||||
await token.approve(marketplace.address, discountedCollateral)
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
const endBalance = await token.balanceOf(host.address)
|
||||
@ -404,7 +411,7 @@ describe("Marketplace", function () {
|
||||
const lastSlot = request.ask.slots - 1
|
||||
await token.approve(
|
||||
marketplace.address,
|
||||
request.ask.collateral * lastSlot
|
||||
collateralPerSlot(request) * lastSlot
|
||||
)
|
||||
await token.approve(marketplace.address, maxPrice(request) * lastSlot)
|
||||
for (let i = 0; i <= lastSlot; i++) {
|
||||
@ -432,7 +439,7 @@ describe("Marketplace", function () {
|
||||
})
|
||||
|
||||
it("is rejected when approved collateral is insufficient", async function () {
|
||||
let insufficient = request.ask.collateral - 1
|
||||
let insufficient = collateralPerSlot(request) - 1
|
||||
await token.approve(marketplace.address, insufficient)
|
||||
await marketplace.reserveSlot(slot.request, slot.index)
|
||||
await expect(
|
||||
@ -441,12 +448,12 @@ describe("Marketplace", function () {
|
||||
})
|
||||
|
||||
it("collects only requested collateral and not more", async function () {
|
||||
await token.approve(marketplace.address, request.ask.collateral * 2)
|
||||
await token.approve(marketplace.address, collateralPerSlot(request) * 2)
|
||||
const startBalance = await token.balanceOf(host.address)
|
||||
await marketplace.reserveSlot(slot.request, slot.index)
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
const endBalance = await token.balanceOf(host.address)
|
||||
expect(startBalance - endBalance).to.eq(request.ask.collateral)
|
||||
expect(startBalance - endBalance).to.eq(collateralPerSlot(request))
|
||||
})
|
||||
})
|
||||
|
||||
@ -456,7 +463,8 @@ describe("Marketplace", function () {
|
||||
await token.approve(marketplace.address, maxPrice(request))
|
||||
await marketplace.requestStorage(request)
|
||||
switchAccount(host)
|
||||
await token.approve(marketplace.address, request.ask.collateral)
|
||||
const collateral = collateralPerSlot(request)
|
||||
await token.approve(marketplace.address, collateral)
|
||||
await marketplace.reserveSlot(slot.request, slot.index)
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await advanceTimeForNextBlock(config.proofs.period)
|
||||
@ -494,7 +502,8 @@ describe("Marketplace", function () {
|
||||
await marketplace.requestStorage(request)
|
||||
requestTime = await currentTime()
|
||||
switchAccount(host)
|
||||
await token.approve(marketplace.address, request.ask.collateral)
|
||||
const collateral = collateralPerSlot(request)
|
||||
await token.approve(marketplace.address, collateral)
|
||||
})
|
||||
|
||||
it("sets the request end time to now + duration", async function () {
|
||||
@ -551,7 +560,8 @@ describe("Marketplace", function () {
|
||||
await token.approve(marketplace.address, maxPrice(request))
|
||||
await marketplace.requestStorage(request)
|
||||
switchAccount(host)
|
||||
await token.approve(marketplace.address, request.ask.collateral)
|
||||
const collateral = collateralPerSlot(request)
|
||||
await token.approve(marketplace.address, collateral)
|
||||
})
|
||||
|
||||
it("fails to free slot when slot not filled", async function () {
|
||||
@ -589,7 +599,8 @@ describe("Marketplace", function () {
|
||||
await token.approve(marketplace.address, maxPrice(request))
|
||||
await marketplace.requestStorage(request)
|
||||
switchAccount(host)
|
||||
await token.approve(marketplace.address, request.ask.collateral)
|
||||
const collateral = collateralPerSlot(request)
|
||||
await token.approve(marketplace.address, collateral)
|
||||
})
|
||||
|
||||
it("finished request pays out reward based on time hosted", async function () {
|
||||
@ -611,8 +622,9 @@ describe("Marketplace", function () {
|
||||
const endBalanceHost = await token.balanceOf(host.address)
|
||||
|
||||
expect(expectedPayouts[slot.index]).to.be.lt(maxPrice(request))
|
||||
const collateral = collateralPerSlot(request)
|
||||
expect(endBalanceHost - startBalanceHost).to.equal(
|
||||
expectedPayouts[slot.index] + request.ask.collateral
|
||||
expectedPayouts[slot.index] + collateral
|
||||
)
|
||||
})
|
||||
|
||||
@ -625,6 +637,10 @@ describe("Marketplace", function () {
|
||||
hostCollateralRecipient.address
|
||||
)
|
||||
|
||||
const collateralToBeReturned = await marketplace.currentCollateral(
|
||||
slotId(slot)
|
||||
)
|
||||
|
||||
await marketplace.freeSlot(
|
||||
slotId(slot),
|
||||
hostRewardRecipient.address,
|
||||
@ -638,8 +654,9 @@ describe("Marketplace", function () {
|
||||
const endBalanceHost = await token.balanceOf(host.address)
|
||||
expect(endBalanceHost).to.equal(startBalanceHost)
|
||||
expect(endBalanceCollateral - startBalanceCollateral).to.equal(
|
||||
request.ask.collateral
|
||||
collateralPerSlot(request)
|
||||
)
|
||||
expect(collateralToBeReturned).to.equal(collateralPerSlot(request))
|
||||
})
|
||||
|
||||
it("pays reward to host reward address if specified", async function () {
|
||||
@ -679,7 +696,8 @@ describe("Marketplace", function () {
|
||||
await waitUntilCancelled(request)
|
||||
await marketplace.freeSlot(slotId(slot))
|
||||
|
||||
const expectedPartialPayout = (expiresAt - filledAt) * request.ask.reward
|
||||
const expectedPartialPayout =
|
||||
(expiresAt - filledAt) * pricePerSlotPerSecond(request)
|
||||
const endBalance = await token.balanceOf(host.address)
|
||||
expect(endBalance - ACCOUNT_STARTING_BALANCE).to.be.equal(
|
||||
expectedPartialPayout
|
||||
@ -704,13 +722,19 @@ describe("Marketplace", function () {
|
||||
const startBalanceCollateral = await token.balanceOf(
|
||||
hostCollateralRecipient.address
|
||||
)
|
||||
|
||||
const collateralToBeReturned = await marketplace.currentCollateral(
|
||||
slotId(slot)
|
||||
)
|
||||
|
||||
await marketplace.freeSlot(
|
||||
slotId(slot),
|
||||
hostRewardRecipient.address,
|
||||
hostCollateralRecipient.address
|
||||
)
|
||||
|
||||
const expectedPartialPayout = (expiresAt - filledAt) * request.ask.reward
|
||||
const expectedPartialPayout =
|
||||
(expiresAt - filledAt) * pricePerSlotPerSecond(request)
|
||||
|
||||
const endBalanceReward = await token.balanceOf(
|
||||
hostRewardRecipient.address
|
||||
@ -726,8 +750,10 @@ describe("Marketplace", function () {
|
||||
hostCollateralRecipient.address
|
||||
)
|
||||
expect(endBalanceCollateral - startBalanceCollateral).to.be.equal(
|
||||
request.ask.collateral
|
||||
collateralPerSlot(request)
|
||||
)
|
||||
|
||||
expect(collateralToBeReturned).to.be.equal(collateralPerSlot(request))
|
||||
})
|
||||
|
||||
it("does not pay when the contract hasn't ended", async function () {
|
||||
@ -777,21 +803,23 @@ describe("Marketplace", function () {
|
||||
await token.approve(marketplace.address, maxPrice(request))
|
||||
await marketplace.requestStorage(request)
|
||||
switchAccount(host)
|
||||
await token.approve(marketplace.address, request.ask.collateral)
|
||||
const collateral = collateralPerSlot(request)
|
||||
await token.approve(marketplace.address, collateral)
|
||||
})
|
||||
|
||||
it("emits event when all slots are filled", async function () {
|
||||
const lastSlot = request.ask.slots - 1
|
||||
await token.approve(
|
||||
marketplace.address,
|
||||
request.ask.collateral * lastSlot
|
||||
collateralPerSlot(request) * lastSlot
|
||||
)
|
||||
for (let i = 0; i < lastSlot; i++) {
|
||||
await marketplace.reserveSlot(slot.request, i)
|
||||
await marketplace.fillSlot(slot.request, i, proof)
|
||||
}
|
||||
|
||||
await token.approve(marketplace.address, request.ask.collateral)
|
||||
const collateral = collateralPerSlot(request)
|
||||
await token.approve(marketplace.address, collateral)
|
||||
await marketplace.reserveSlot(slot.request, lastSlot)
|
||||
await expect(marketplace.fillSlot(slot.request, lastSlot, proof))
|
||||
.to.emit(marketplace, "RequestFulfilled")
|
||||
@ -799,7 +827,8 @@ describe("Marketplace", function () {
|
||||
})
|
||||
it("sets state when all slots are filled", async function () {
|
||||
const slots = request.ask.slots
|
||||
await token.approve(marketplace.address, request.ask.collateral * slots)
|
||||
const collateral = collateralPerSlot(request)
|
||||
await token.approve(marketplace.address, collateral * slots)
|
||||
for (let i = 0; i < slots; i++) {
|
||||
await marketplace.reserveSlot(slot.request, i)
|
||||
await marketplace.fillSlot(slot.request, i, proof)
|
||||
@ -812,7 +841,7 @@ describe("Marketplace", function () {
|
||||
const lastSlot = request.ask.slots - 1
|
||||
await token.approve(
|
||||
marketplace.address,
|
||||
request.ask.collateral * (lastSlot + 1)
|
||||
collateralPerSlot(request) * (lastSlot + 1)
|
||||
)
|
||||
for (let i = 0; i <= lastSlot; i++) {
|
||||
await marketplace.reserveSlot(slot.request, i)
|
||||
@ -830,7 +859,8 @@ describe("Marketplace", function () {
|
||||
await token.approve(marketplace.address, maxPrice(request))
|
||||
await marketplace.requestStorage(request)
|
||||
switchAccount(host)
|
||||
await token.approve(marketplace.address, request.ask.collateral)
|
||||
const collateral = collateralPerSlot(request)
|
||||
await token.approve(marketplace.address, collateral)
|
||||
})
|
||||
|
||||
it("rejects withdraw when request not yet timed out", async function () {
|
||||
@ -852,7 +882,7 @@ describe("Marketplace", function () {
|
||||
const lastSlot = request.ask.slots - 1
|
||||
await token.approve(
|
||||
marketplace.address,
|
||||
request.ask.collateral * (lastSlot + 1)
|
||||
collateralPerSlot(request) * (lastSlot + 1)
|
||||
)
|
||||
for (let i = 0; i <= lastSlot; i++) {
|
||||
await marketplace.reserveSlot(slot.request, i)
|
||||
@ -915,7 +945,7 @@ describe("Marketplace", function () {
|
||||
// at the time of expiry and hence the user would get the full "expiry window" reward back.
|
||||
expect(endBalancePayout - startBalancePayout).to.be.gt(0)
|
||||
expect(endBalancePayout - startBalancePayout).to.be.lte(
|
||||
request.expiry * request.ask.reward
|
||||
request.expiry * pricePerSlotPerSecond(request)
|
||||
)
|
||||
})
|
||||
|
||||
@ -974,7 +1004,7 @@ describe("Marketplace", function () {
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await waitUntilCancelled(request)
|
||||
const expectedPartialhostRewardRecipient =
|
||||
(expiresAt - filledAt) * request.ask.reward
|
||||
(expiresAt - filledAt) * pricePerSlotPerSecond(request)
|
||||
|
||||
switchAccount(client)
|
||||
await marketplace.withdrawFunds(
|
||||
@ -1018,7 +1048,8 @@ describe("Marketplace", function () {
|
||||
await token.approve(marketplace.address, maxPrice(request))
|
||||
await marketplace.requestStorage(request)
|
||||
switchAccount(host)
|
||||
await token.approve(marketplace.address, request.ask.collateral)
|
||||
const collateral = collateralPerSlot(request)
|
||||
await token.approve(marketplace.address, collateral)
|
||||
})
|
||||
|
||||
it("is 'New' initially", async function () {
|
||||
@ -1056,7 +1087,7 @@ describe("Marketplace", function () {
|
||||
it("does not change to 'Failed' before it is started", async function () {
|
||||
await token.approve(
|
||||
marketplace.address,
|
||||
request.ask.collateral * (request.ask.maxSlotLoss + 1)
|
||||
collateralPerSlot(request) * (request.ask.maxSlotLoss + 1)
|
||||
)
|
||||
for (let i = 0; i <= request.ask.maxSlotLoss; i++) {
|
||||
await marketplace.reserveSlot(slot.request, i)
|
||||
@ -1098,7 +1129,8 @@ describe("Marketplace", function () {
|
||||
await token.approve(marketplace.address, maxPrice(request))
|
||||
await marketplace.requestStorage(request)
|
||||
switchAccount(host)
|
||||
await token.approve(marketplace.address, request.ask.collateral)
|
||||
const collateral = collateralPerSlot(request)
|
||||
await token.approve(marketplace.address, collateral)
|
||||
})
|
||||
|
||||
async function waitUntilProofIsRequired(id) {
|
||||
@ -1185,7 +1217,8 @@ describe("Marketplace", function () {
|
||||
await token.approve(marketplace.address, maxPrice(request))
|
||||
await marketplace.requestStorage(request)
|
||||
switchAccount(host)
|
||||
await token.approve(marketplace.address, request.ask.collateral)
|
||||
const collateral = collateralPerSlot(request)
|
||||
await token.approve(marketplace.address, collateral)
|
||||
})
|
||||
|
||||
async function waitUntilProofWillBeRequired(id) {
|
||||
@ -1276,7 +1309,8 @@ describe("Marketplace", function () {
|
||||
await token.approve(marketplace.address, maxPrice(request))
|
||||
await marketplace.requestStorage(request)
|
||||
switchAccount(host)
|
||||
await token.approve(marketplace.address, request.ask.collateral)
|
||||
const collateral = collateralPerSlot(request)
|
||||
await token.approve(marketplace.address, collateral)
|
||||
})
|
||||
|
||||
async function waitUntilProofIsRequired(id) {
|
||||
@ -1315,8 +1349,10 @@ describe("Marketplace", function () {
|
||||
await advanceTimeForNextBlock(period + 1)
|
||||
await marketplace.markProofAsMissing(id, missedPeriod)
|
||||
}
|
||||
const expectedBalance =
|
||||
(request.ask.collateral * (100 - slashPercentage)) / 100
|
||||
const collateral = collateralPerSlot(request)
|
||||
const expectedBalance = Math.round(
|
||||
(collateral * (100 - slashPercentage)) / 100
|
||||
)
|
||||
|
||||
expect(
|
||||
BigNumber.from(expectedBalance).eq(
|
||||
@ -1327,9 +1363,10 @@ describe("Marketplace", function () {
|
||||
})
|
||||
|
||||
it("frees slot when collateral slashed below minimum threshold", async function () {
|
||||
const collateral = collateralPerSlot(request)
|
||||
const minimum =
|
||||
request.ask.collateral -
|
||||
(request.ask.collateral *
|
||||
collateral -
|
||||
(collateral *
|
||||
config.collateral.maxNumberOfSlashes *
|
||||
config.collateral.slashPercentage) /
|
||||
100
|
||||
@ -1352,9 +1389,10 @@ describe("Marketplace", function () {
|
||||
})
|
||||
|
||||
it("free slot when minimum reached and resets missed proof counter", async function () {
|
||||
const collateral = collateralPerSlot(request)
|
||||
const minimum =
|
||||
request.ask.collateral -
|
||||
(request.ask.collateral *
|
||||
collateral -
|
||||
(collateral *
|
||||
config.collateral.maxNumberOfSlashes *
|
||||
config.collateral.slashPercentage) /
|
||||
100
|
||||
@ -1386,7 +1424,8 @@ describe("Marketplace", function () {
|
||||
describe("list of active requests", function () {
|
||||
beforeEach(async function () {
|
||||
switchAccount(host)
|
||||
await token.approve(marketplace.address, request.ask.collateral)
|
||||
const collateral = collateralPerSlot(request)
|
||||
await token.approve(marketplace.address, collateral)
|
||||
switchAccount(client)
|
||||
await token.approve(marketplace.address, maxPrice(request))
|
||||
})
|
||||
@ -1440,14 +1479,16 @@ describe("Marketplace", function () {
|
||||
await token.approve(marketplace.address, maxPrice(request))
|
||||
await marketplace.requestStorage(request)
|
||||
switchAccount(host)
|
||||
await token.approve(marketplace.address, request.ask.collateral)
|
||||
const collateral = collateralPerSlot(request)
|
||||
await token.approve(marketplace.address, collateral)
|
||||
})
|
||||
|
||||
it("adds slot to list when filling slot", async function () {
|
||||
await marketplace.reserveSlot(slot.request, slot.index)
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
let slot1 = { ...slot, index: slot.index + 1 }
|
||||
await token.approve(marketplace.address, request.ask.collateral)
|
||||
const collateral = collateralPerSlot(request)
|
||||
await token.approve(marketplace.address, collateral)
|
||||
await marketplace.reserveSlot(slot.request, slot1.index)
|
||||
await marketplace.fillSlot(slot.request, slot1.index, proof)
|
||||
expect(await marketplace.mySlots()).to.have.members([
|
||||
@ -1460,10 +1501,11 @@ describe("Marketplace", function () {
|
||||
await marketplace.reserveSlot(slot.request, slot.index)
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
let slot1 = { ...slot, index: slot.index + 1 }
|
||||
await token.approve(marketplace.address, request.ask.collateral)
|
||||
const collateral = collateralPerSlot(request)
|
||||
await token.approve(marketplace.address, collateral)
|
||||
await marketplace.reserveSlot(slot.request, slot1.index)
|
||||
await marketplace.fillSlot(slot.request, slot1.index, proof)
|
||||
await token.approve(marketplace.address, request.ask.collateral)
|
||||
await token.approve(marketplace.address, collateral)
|
||||
await marketplace.freeSlot(slotId(slot))
|
||||
expect(await marketplace.mySlots()).to.have.members([slotId(slot1)])
|
||||
})
|
||||
@ -1473,7 +1515,8 @@ describe("Marketplace", function () {
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
let slot1 = { ...slot, index: slot.index + 1 }
|
||||
|
||||
await token.approve(marketplace.address, request.ask.collateral)
|
||||
const collateral = collateralPerSlot(request)
|
||||
await token.approve(marketplace.address, collateral)
|
||||
await marketplace.reserveSlot(slot.request, slot1.index)
|
||||
await marketplace.fillSlot(slot.request, slot1.index, proof)
|
||||
await waitUntilCancelled(request)
|
||||
|
||||
5
test/collateral.js
Normal file
5
test/collateral.js
Normal file
@ -0,0 +1,5 @@
|
||||
function collateralPerSlot(request) {
|
||||
return request.ask.collateralPerByte * request.ask.slotSize
|
||||
}
|
||||
|
||||
module.exports = { collateralPerSlot }
|
||||
@ -29,9 +29,9 @@ const exampleRequest = async () => {
|
||||
slotSize: 1 * 1024 * 1024 * 1024, // 1 Gigabyte
|
||||
duration: hours(10),
|
||||
proofProbability: 4, // require a proof roughly once every 4 periods
|
||||
reward: 84,
|
||||
pricePerBytePerSecond: 1,
|
||||
maxSlotLoss: 2,
|
||||
collateral: 200,
|
||||
collateralPerByte: 1,
|
||||
},
|
||||
content: {
|
||||
cid: "zb2rhheVmk3bLks5MgzTqyznLu1zqGH5jrfTA1eAZXrjx7Vob",
|
||||
|
||||
@ -15,8 +15,8 @@ function askToArray(ask) {
|
||||
ask.slotSize,
|
||||
ask.duration,
|
||||
ask.proofProbability,
|
||||
ask.reward,
|
||||
ask.collateral,
|
||||
ask.pricePerBytePerSecond,
|
||||
ask.collateralPerByte,
|
||||
ask.maxSlotLoss,
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
const { advanceTimeToForNextBlock, currentTime } = require("./evm")
|
||||
const { slotId, requestId } = require("./ids")
|
||||
const { maxPrice, payoutForDuration } = require("./price")
|
||||
const { collateralPerSlot } = require("./collateral")
|
||||
|
||||
/**
|
||||
* @dev This will not advance the time right on the "expiry threshold" but will most probably "overshoot it"
|
||||
@ -15,7 +16,8 @@ async function waitUntilCancelled(request) {
|
||||
}
|
||||
|
||||
async function waitUntilSlotsFilled(contract, request, proof, token, slots) {
|
||||
await token.approve(contract.address, request.ask.collateral * slots.length)
|
||||
let collateral = collateralPerSlot(request)
|
||||
await token.approve(contract.address, collateral * slots.length)
|
||||
|
||||
let requestEnd = (await contract.requestEnd(requestId(request))).toNumber()
|
||||
const payouts = []
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
function pricePerSlotPerSecond(request) {
|
||||
return request.ask.pricePerBytePerSecond * request.ask.slotSize
|
||||
}
|
||||
|
||||
function maxPrice(request) {
|
||||
return request.ask.slots * request.ask.duration * request.ask.reward
|
||||
return (
|
||||
request.ask.slots * request.ask.duration * pricePerSlotPerSecond(request)
|
||||
)
|
||||
}
|
||||
|
||||
function payoutForDuration(request, start, end) {
|
||||
return (end - start) * request.ask.reward
|
||||
return (end - start) * pricePerSlotPerSecond(request)
|
||||
}
|
||||
|
||||
module.exports = { maxPrice, payoutForDuration }
|
||||
module.exports = { maxPrice, pricePerSlotPerSecond, payoutForDuration }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user