Skip to content

Commit e74d339

Browse files
authored
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)
1 parent d04acaf commit e74d339

File tree

9 files changed

+179
-78
lines changed

9 files changed

+179
-78
lines changed

contracts/Marketplace.sol

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
4242
using EnumerableSet for EnumerableSet.Bytes32Set;
4343
using EnumerableSet for EnumerableSet.AddressSet;
4444
using Requests for Request;
45+
using AskHelpers for Ask;
4546

4647
IERC20 private immutable _token;
4748
MarketplaceConfig private _config;
@@ -72,14 +73,20 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
7273
SlotState state;
7374
RequestId requestId;
7475
/// @notice Timestamp that signals when slot was filled
75-
/// @dev Used for calculating payouts as hosts are paid based on time they actually host the content
76+
/// @dev Used for calculating payouts as hosts are paid
77+
/// based on time they actually host the content
7678
uint256 filledAt;
7779
uint256 slotIndex;
78-
/// @notice Tracks the current amount of host's collateral that is to be payed out at the end of Slot's lifespan.
79-
/// @dev When Slot is filled, the collateral is collected in amount of request.ask.collateral
80-
/// @dev When Host is slashed for missing a proof the slashed amount is reflected in this variable
80+
/// @notice Tracks the current amount of host's collateral that is
81+
/// to be payed out at the end of Slot's lifespan.
82+
/// @dev When Slot is filled, the collateral is collected in amount
83+
/// of request.ask.collateralPerByte * request.ask.slotSize
84+
/// (== request.ask.collateralPerSlot() when using the AskHelpers library)
85+
/// @dev When Host is slashed for missing a proof the slashed amount is
86+
/// reflected in this variable
8187
uint256 currentCollateral;
82-
address host; // address used for collateral interactions and identifying hosts
88+
/// @notice address used for collateral interactions and identifying hosts
89+
address host;
8390
}
8491

8592
struct ActiveSlot {
@@ -120,6 +127,10 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
120127
return _token;
121128
}
122129

130+
function currentCollateral(SlotId slotId) public view returns (uint256) {
131+
return _slots[slotId].currentCollateral;
132+
}
133+
123134
function requestStorage(Request calldata request) public {
124135
RequestId id = request.id();
125136

@@ -137,10 +148,10 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
137148
if (request.ask.proofProbability == 0) {
138149
revert Marketplace_InsufficientProofProbability();
139150
}
140-
if (request.ask.collateral == 0) {
151+
if (request.ask.collateralPerByte == 0) {
141152
revert Marketplace_InsufficientCollateral();
142153
}
143-
if (request.ask.reward == 0) {
154+
if (request.ask.pricePerBytePerSecond == 0) {
144155
revert Marketplace_InsufficientReward();
145156
}
146157
if (bytes(request.content.cid).length == 0) {
@@ -205,20 +216,20 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
205216

206217
// Collect collateral
207218
uint256 collateralAmount;
219+
uint256 collateralPerSlot = request.ask.collateralPerSlot();
208220
if (slotState(slotId) == SlotState.Repair) {
209221
// Host is repairing a slot and is entitled for repair reward, so he gets "discounted collateral"
210222
// in this way he gets "physically" the reward at the end of the request when the full amount of collateral
211223
// is returned to him.
212224
collateralAmount =
213-
request.ask.collateral -
214-
((request.ask.collateral * _config.collateral.repairRewardPercentage) /
215-
100);
225+
collateralPerSlot -
226+
((collateralPerSlot * _config.collateral.repairRewardPercentage) / 100);
216227
} else {
217-
collateralAmount = request.ask.collateral;
228+
collateralAmount = collateralPerSlot;
218229
}
219230
_transferFrom(msg.sender, collateralAmount);
220231
_marketplaceTotals.received += collateralAmount;
221-
slot.currentCollateral = request.ask.collateral; // Even if he has collateral discounted, he is operating with full collateral
232+
slot.currentCollateral = collateralPerSlot; // Even if he has collateral discounted, he is operating with full collateral
222233

223234
_addToMySlots(slot.host, slotId);
224235

@@ -326,7 +337,7 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
326337
// TODO: Reward for validator that calls this function
327338

328339
if (missingProofs(slotId) % _config.collateral.slashCriterion == 0) {
329-
uint256 slashedAmount = (request.ask.collateral *
340+
uint256 slashedAmount = (request.ask.collateralPerSlot() *
330341
_config.collateral.slashPercentage) / 100;
331342
slot.currentCollateral -= slashedAmount;
332343
if (
@@ -586,7 +597,9 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
586597
Request storage request = _requests[requestId];
587598
if (startingTimestamp >= endingTimestamp)
588599
revert Marketplace_StartNotBeforeExpiry();
589-
return (endingTimestamp - startingTimestamp) * request.ask.reward;
600+
return
601+
(endingTimestamp - startingTimestamp) *
602+
request.ask.pricePerSlotPerSecond();
590603
}
591604

592605
function getHost(SlotId slotId) public view returns (address) {

contracts/Requests.sol

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ struct Ask {
1717
uint256 slotSize; // amount of storage per slot (in number of bytes)
1818
uint256 duration; // how long content should be stored (in seconds)
1919
uint256 proofProbability; // how often storage proofs are required
20-
uint256 reward; // amount of tokens paid per second per slot to hosts
21-
uint256 collateral; // amount of tokens required to be deposited by the hosts in order to fill the slot
20+
uint256 pricePerBytePerSecond; // amount of tokens paid per second per byte to hosts
21+
uint256 collateralPerByte; // amount of tokens per byte required to be deposited by the hosts in order to fill the slot
2222
uint64 maxSlotLoss; // Max slots that can be lost without data considered to be lost
2323
}
2424

@@ -45,7 +45,21 @@ enum SlotState {
4545
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
4646
}
4747

48+
library AskHelpers {
49+
function collateralPerSlot(Ask memory ask) internal pure returns (uint256) {
50+
return ask.collateralPerByte * ask.slotSize;
51+
}
52+
53+
function pricePerSlotPerSecond(
54+
Ask memory ask
55+
) internal pure returns (uint256) {
56+
return ask.pricePerBytePerSecond * ask.slotSize;
57+
}
58+
}
59+
4860
library Requests {
61+
using AskHelpers for Ask;
62+
4963
function id(Request memory request) internal pure returns (RequestId) {
5064
return RequestId.wrap(keccak256(abi.encode(request)));
5165
}
@@ -76,6 +90,9 @@ library Requests {
7690
}
7791

7892
function maxPrice(Request memory request) internal pure returns (uint256) {
79-
return request.ask.slots * request.ask.duration * request.ask.reward;
93+
return
94+
request.ask.slots *
95+
request.ask.duration *
96+
request.ask.pricePerSlotPerSecond();
8097
}
8198
}

deploy/token.js

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,31 @@
1-
const MINTED_TOKENS = 1_000_000_000
1+
const MINTED_TOKENS = 1_000_000_000_000_000
22

3-
module.exports = async ({ deployments, getNamedAccounts, getUnnamedAccounts, network }) => {
3+
module.exports = async ({
4+
deployments,
5+
getNamedAccounts,
6+
getUnnamedAccounts,
7+
network,
8+
}) => {
49
const { deployer } = await getNamedAccounts()
5-
const tokenDeployment = await deployments.deploy("TestToken", { from: deployer })
6-
const token = await hre.ethers.getContractAt("TestToken", tokenDeployment.address)
10+
const tokenDeployment = await deployments.deploy("TestToken", {
11+
from: deployer,
12+
})
13+
const token = await hre.ethers.getContractAt(
14+
"TestToken",
15+
tokenDeployment.address
16+
)
717

8-
const accounts = [...Object.values(await getNamedAccounts()), ...(await getUnnamedAccounts())]
18+
const accounts = [
19+
...Object.values(await getNamedAccounts()),
20+
...(await getUnnamedAccounts()),
21+
]
922
if (network.tags.local) {
1023
for (const account of accounts) {
1124
console.log(`Minting ${MINTED_TOKENS} tokens to address ${account}`)
1225

13-
const transaction = await token.mint(account, MINTED_TOKENS, { from: deployer })
26+
const transaction = await token.mint(account, MINTED_TOKENS, {
27+
from: deployer,
28+
})
1429
await transaction.wait()
1530
}
1631
console.log()

0 commit comments

Comments
 (0)