Select a storage offer
This commit is contained in:
parent
cc57155792
commit
85b212c703
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in New Issue