marketplace: transfer repair reward in vault

This commit is contained in:
Mark Spanbroek 2025-02-27 09:31:51 +01:00
parent 09a3cbcb09
commit ab5dad67af
4 changed files with 69 additions and 52 deletions

View File

@ -147,10 +147,14 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
_addToMyRequests(request.client, id);
TokensPerSecond pricePerSecond = request.ask.pricePerSecond();
uint128 price = pricePerSecond.accumulate(request.ask.duration);
FundId fund = id.asFundId();
AccountId account = _vault.clientAccount(request.client);
_vault.lock(fund, expiresAt, endsAt);
_transferToVault(request.client, fund, account, request.maxPrice());
_transferToVault(request.client, fund, account, price);
_vault.flow(fund, account, account, pricePerSecond);
emit StorageRequested(id, request.ask, expiresAt);
}
@ -195,25 +199,23 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
context.slotsFilled += 1;
// Collect collateral
uint128 collateralAmount = request.collateralPerSlot();
uint128 designatedAmount = _config.collateral.designatedCollateral(
collateralAmount
);
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 -= _config.collateral.repairReward(collateralAmount);
}
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, collateralAmount);
_vault.designate(fund, hostAccount, designatedAmount);
uint128 collateral = request.collateralPerSlot();
uint128 designated = _config.collateral.designatedCollateral(collateral);
if (slotState(slotId) == SlotState.Repair) {
// host gets a discount on its collateral, paid for by the repair reward
uint128 repairReward = _config.collateral.repairReward(collateral);
_vault.transfer(fund, clientAccount, hostAccount, repairReward);
collateral -= repairReward;
}
_transferToVault(slot.host, fund, hostAccount, collateral);
_vault.designate(fund, hostAccount, designated);
_vault.flow(fund, clientAccount, hostAccount, rate);
_addToMySlots(slot.host, slotId);
@ -334,12 +336,15 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
Request storage request = _requests[requestId];
TokensPerSecond rate = request.ask.pricePerSlotPerSecond();
uint128 collateral = request.collateralPerSlot();
uint128 repairReward = _config.collateral.repairReward(collateral);
FundId fund = requestId.asFundId();
AccountId hostAccount = _vault.hostAccount(slot.host, slot.slotIndex);
AccountId clientAccount = _vault.clientAccount(request.client);
TokensPerSecond rate = request.ask.pricePerSlotPerSecond();
_vault.flow(fund, hostAccount, clientAccount, rate);
_vault.transfer(fund, hostAccount, clientAccount, repairReward);
_vault.burnAccount(fund, hostAccount);
_removeFromMySlots(slot.host, slotId);

View File

@ -49,12 +49,19 @@ enum SlotState {
}
library AskHelpers {
using AskHelpers for Ask;
function pricePerSlotPerSecond(
Ask memory ask
) internal pure returns (TokensPerSecond) {
uint96 perByte = TokensPerSecond.unwrap(ask.pricePerBytePerSecond);
return TokensPerSecond.wrap(perByte * ask.slotSize);
}
function pricePerSecond(Ask memory ask) internal pure returns (TokensPerSecond) {
uint96 perSlot = TokensPerSecond.unwrap(ask.pricePerSlotPerSecond());
return TokensPerSecond.wrap(perSlot * ask.slots);
}
}
library Requests {
@ -89,11 +96,4 @@ library Requests {
result := ids
}
}
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);
}
}

View File

@ -28,7 +28,7 @@ const {
pricePerSlotPerSecond,
payoutForDuration,
} = require("./price")
const { collateralPerSlot } = require("./collateral")
const { collateralPerSlot, repairReward } = require("./collateral")
const {
snapshot,
revert,
@ -296,33 +296,39 @@ describe("Marketplace", function () {
expect(await marketplace.getHost(slotId(slot))).to.equal(host.address)
})
it("gives discount on the collateral for repaired slot", async function () {
await marketplace.reserveSlot(slot.request, slot.index)
await marketplace.fillSlot(slot.request, slot.index, proof)
await marketplace.freeSlot(slotId(slot))
expect(await marketplace.slotState(slotId(slot))).to.equal(
SlotState.Repair
)
describe("when repairing a slot", function () {
beforeEach(async function () {
await marketplace.reserveSlot(slot.request, slot.index)
await marketplace.fillSlot(slot.request, slot.index, proof)
await advanceTime(config.proofs.period + 1)
await marketplace.freeSlot(slotId(slot))
})
// We need to advance the time to next period, because filling slot
// must not be done in the same period as for that period there was already proof
// submitted with the previous `fillSlot` and the transaction would revert with "Proof already submitted".
await advanceTime(config.proofs.period + 1)
it("gives the host a discount on the collateral", async function () {
const collateral = collateralPerSlot(request)
const reward = repairReward(config, collateral)
const discountedCollateral = collateral - reward
await token.approve(marketplace.address, discountedCollateral)
const startBalance = await token.balanceOf(host.address)
const collateral = collateralPerSlot(request)
const discountedCollateral =
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)
expect(startBalance - endBalance).to.equal(discountedCollateral)
expect(await marketplace.slotState(slotId(slot))).to.equal(
SlotState.Filled
)
const startBalance = await token.balanceOf(host.address)
await marketplace.fillSlot(slot.request, slot.index, proof)
const endBalance = await token.balanceOf(host.address)
expect(startBalance - endBalance).to.equal(discountedCollateral)
})
it("tops up the host collateral with the repair reward", async function () {
const collateral = collateralPerSlot(request)
const reward = repairReward(config, collateral)
const discountedCollateral = collateral - reward
await token.approve(marketplace.address, discountedCollateral)
const startBalance = await marketplace.getSlotBalance(slotId(slot))
await marketplace.fillSlot(slot.request, slot.index, proof)
const endBalance = await marketplace.getSlotBalance(slotId(slot))
expect(endBalance - startBalance).to.equal(collateral)
})
})
it("fails to retrieve a request of an empty slot", async function () {
@ -850,8 +856,9 @@ describe("Marketplace", function () {
const hostPayouts = payouts.reduce((a, b) => a + b, 0)
const refund = payoutForDuration(request, freedAt, requestEnd)
const reward = repairReward(config, collateralPerSlot(request))
expect(endBalance - startBalance).to.equal(
maxPrice(request) - hostPayouts + refund
maxPrice(request) - hostPayouts + refund + reward
)
})
})

View File

@ -2,4 +2,9 @@ function collateralPerSlot(request) {
return request.ask.collateralPerByte * request.ask.slotSize
}
module.exports = { collateralPerSlot }
function repairReward(configuration, collateral) {
const percentage = configuration.collateral.repairRewardPercentage
return Math.round((collateral * percentage) / 100)
}
module.exports = { collateralPerSlot, repairReward }