Select a storage offer

This commit is contained in:
Mark Spanbroek 2022-02-21 11:31:37 +01:00 committed by markspanbroek
parent cc57155792
commit 85b212c703
3 changed files with 102 additions and 5 deletions

View File

@ -8,6 +8,7 @@ contract Marketplace is Collateral {
uint256 public immutable collateral;
MarketplaceFunds private funds;
mapping(bytes32 => Request) private requests;
mapping(bytes32 => RequestState) private requestState;
mapping(bytes32 => Offer) private offers;
constructor(IERC20 _token, uint256 _collateral)
@ -45,6 +46,25 @@ contract Marketplace is Collateral {
emit StorageOffered(id, offer);
}
function selectOffer(bytes32 id) public marketplaceInvariant {
Offer storage offer = offers[id];
require(offer.host != address(0), "Unknown offer");
// solhint-disable-next-line not-rely-on-time
require(offer.expiry > block.timestamp, "Offer expired");
Request storage request = requests[offer.requestId];
require(request.client == msg.sender, "Only client can select offer");
RequestState storage state = requestState[offer.requestId];
require(!state.offerSelected, "Offer already selected");
state.offerSelected = true;
_createLock(id, offer.expiry);
_lock(offer.host, id);
_unlock(offer.requestId);
uint256 difference = request.maxPrice - offer.price;
funds.sent += difference;
funds.balance -= difference;
token.transfer(request.client, difference);
}
struct Request {
address client;
uint256 duration;
@ -57,6 +77,10 @@ contract Marketplace is Collateral {
bytes32 nonce;
}
struct RequestState {
bool offerSelected;
}
struct Offer {
address host;
bytes32 requestId;

View File

@ -9,16 +9,18 @@ describe("Marketplace", function () {
let marketplace
let token
let client, host
let client, host, host1, host2, host3
let request, offer
beforeEach(async function () {
;[client, host] = await ethers.getSigners()
;[client, host1, host2, host3] = await ethers.getSigners()
host = host1
const TestToken = await ethers.getContractFactory("TestToken")
token = await TestToken.deploy()
await token.mint(client.address, 1000)
await token.mint(host.address, 1000)
for (account of [client, host1, host2, host3]) {
await token.mint(account.address, 1000)
}
const Marketplace = await ethers.getContractFactory("Marketplace")
marketplace = await Marketplace.deploy(token.address, collateral)
@ -133,6 +135,76 @@ describe("Marketplace", function () {
)
})
})
describe("selecting an offer", async function () {
beforeEach(async function () {
switchAccount(client)
await token.approve(marketplace.address, request.maxPrice)
await marketplace.requestStorage(request)
for (host of [host1, host2, host3]) {
switchAccount(host)
let hostOffer = { ...offer, host: host.address }
await token.approve(marketplace.address, collateral)
await marketplace.deposit(collateral)
await marketplace.offerStorage(hostOffer)
}
switchAccount(client)
})
it("returns price difference to client", async function () {
let difference = request.maxPrice - offer.price
let before = await token.balanceOf(client.address)
await marketplace.selectOffer(offerId(offer))
let after = await token.balanceOf(client.address)
expect(after - before).to.equal(difference)
})
it("unlocks collateral of hosts that weren't chosen", async function () {
await marketplace.selectOffer(offerId(offer))
switchAccount(host2)
await expect(marketplace.withdraw()).not.to.be.reverted
switchAccount(host3)
await expect(marketplace.withdraw()).not.to.be.reverted
})
it("locks collateral of host that was chosen", async function () {
await marketplace.selectOffer(offerId(offer))
switchAccount(host1)
await expect(marketplace.withdraw()).to.be.revertedWith("Account locked")
})
it("rejects selection of unknown offer", async function () {
let unknown = exampleOffer()
await expect(
marketplace.selectOffer(offerId(unknown))
).to.be.revertedWith("Unknown offer")
})
it("rejects selection of expired offer", async function () {
let expired = { ...offer, expiry: now() - hours(1) }
switchAccount(host1)
await marketplace.offerStorage(expired)
switchAccount(client)
await expect(
marketplace.selectOffer(offerId(expired))
).to.be.revertedWith("Offer expired")
})
it("rejects reselection of offer", async function () {
let secondOffer = { ...offer, host: host2.address }
await marketplace.selectOffer(offerId(offer))
await expect(
marketplace.selectOffer(offerId(secondOffer))
).to.be.revertedWith("Offer already selected")
})
it("rejects selection by anyone other than the client", async function () {
switchAccount(host1)
await expect(marketplace.selectOffer(offerId(offer))).to.be.revertedWith(
"Only client can select offer"
)
})
})
})
function requestId(request) {

View File

@ -9,7 +9,7 @@ const exampleRequest = () => ({
contentHash: sha256("0xdeadbeef"),
proofPeriod: 8, // 8 blocks ≈ 2 minutes
proofTimeout: 4, // 4 blocks ≈ 1 minute
maxPrice: 42,
maxPrice: 84,
expiry: now() + hours(1),
nonce: hexlify(randomBytes(32)),
})
@ -20,6 +20,7 @@ const exampleBid = () => ({
})
const exampleOffer = () => ({
requestId: hexlify(randomBytes(32)),
host: hexlify(randomBytes(20)),
price: 42,
expiry: now() + hours(1),