mirror of
https://github.com/logos-storage/logos-storage-contracts-eth.git
synced 2026-01-07 15:53:07 +00:00
Offer storage using Marketplace contract
This commit is contained in:
parent
f9cc73d62f
commit
b349b76ab7
@ -2,19 +2,19 @@
|
|||||||
pragma solidity ^0.8.0;
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
|
import "./Collateral.sol";
|
||||||
|
|
||||||
contract Marketplace {
|
contract Marketplace is Collateral {
|
||||||
IERC20 public immutable token;
|
uint256 public immutable collateral;
|
||||||
MarketplaceFunds private funds;
|
MarketplaceFunds private funds;
|
||||||
mapping(bytes32 => Request) private requests;
|
mapping(bytes32 => Request) private requests;
|
||||||
|
mapping(bytes32 => Offer) private offers;
|
||||||
|
|
||||||
constructor(IERC20 _token) marketplaceInvariant {
|
constructor(IERC20 _token, uint256 _collateral)
|
||||||
token = _token;
|
Collateral(_token)
|
||||||
}
|
marketplaceInvariant
|
||||||
|
{
|
||||||
function transferFrom(address sender, uint256 amount) private {
|
collateral = _collateral;
|
||||||
address receiver = address(this);
|
|
||||||
require(token.transferFrom(sender, receiver, amount), "Transfer failed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function requestStorage(Request calldata request)
|
function requestStorage(Request calldata request)
|
||||||
@ -31,6 +31,19 @@ contract Marketplace {
|
|||||||
emit StorageRequested(id, request);
|
emit StorageRequested(id, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function offerStorage(Offer calldata offer) public marketplaceInvariant {
|
||||||
|
bytes32 id = keccak256(abi.encode(offer));
|
||||||
|
Request storage request = requests[offer.requestId];
|
||||||
|
require(balanceOf(msg.sender) >= collateral, "Insufficient collateral");
|
||||||
|
require(request.size != 0, "Unknown request");
|
||||||
|
require(offers[id].expiry == 0, "Offer already exists");
|
||||||
|
// solhint-disable-next-line not-rely-on-time
|
||||||
|
require(offer.expiry > block.timestamp, "Offer expired");
|
||||||
|
require(offer.price <= request.maxPrice, "Price too high");
|
||||||
|
offers[id] = offer;
|
||||||
|
emit StorageOffered(id, offer);
|
||||||
|
}
|
||||||
|
|
||||||
struct Request {
|
struct Request {
|
||||||
uint256 duration;
|
uint256 duration;
|
||||||
uint256 size;
|
uint256 size;
|
||||||
@ -41,7 +54,14 @@ contract Marketplace {
|
|||||||
bytes32 nonce;
|
bytes32 nonce;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Offer {
|
||||||
|
bytes32 requestId;
|
||||||
|
uint256 price;
|
||||||
|
uint256 expiry;
|
||||||
|
}
|
||||||
|
|
||||||
event StorageRequested(bytes32 id, Request request);
|
event StorageRequested(bytes32 id, Request request);
|
||||||
|
event StorageOffered(bytes32 id, Offer offer);
|
||||||
|
|
||||||
modifier marketplaceInvariant() {
|
modifier marketplaceInvariant() {
|
||||||
MarketplaceFunds memory oldFunds = funds;
|
MarketplaceFunds memory oldFunds = funds;
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
const { ethers } = require("hardhat")
|
const { ethers } = require("hardhat")
|
||||||
const { expect } = require("chai")
|
const { expect } = require("chai")
|
||||||
const { exampleRequest } = require("./examples")
|
const { exampleRequest, exampleOffer } = require("./examples")
|
||||||
|
const { now, hours } = require("./time")
|
||||||
const { keccak256, defaultAbiCoder } = ethers.utils
|
const { keccak256, defaultAbiCoder } = ethers.utils
|
||||||
|
|
||||||
describe("Marketplace", function () {
|
describe("Marketplace", function () {
|
||||||
const request = exampleRequest()
|
const request = exampleRequest()
|
||||||
|
const offer = { ...exampleOffer(), requestId: requestId(request) }
|
||||||
|
const collateral = 100
|
||||||
|
|
||||||
let marketplace
|
let marketplace
|
||||||
let token
|
let token
|
||||||
@ -14,7 +17,7 @@ describe("Marketplace", function () {
|
|||||||
const TestToken = await ethers.getContractFactory("TestToken")
|
const TestToken = await ethers.getContractFactory("TestToken")
|
||||||
token = await TestToken.deploy()
|
token = await TestToken.deploy()
|
||||||
const Marketplace = await ethers.getContractFactory("Marketplace")
|
const Marketplace = await ethers.getContractFactory("Marketplace")
|
||||||
marketplace = await Marketplace.deploy(token.address)
|
marketplace = await Marketplace.deploy(token.address, collateral)
|
||||||
accounts = await ethers.getSigners()
|
accounts = await ethers.getSigners()
|
||||||
await token.mint(accounts[0].address, 1000)
|
await token.mint(accounts[0].address, 1000)
|
||||||
})
|
})
|
||||||
@ -51,6 +54,60 @@ describe("Marketplace", function () {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("offering storage", function () {
|
||||||
|
beforeEach(async function () {
|
||||||
|
await token.approve(marketplace.address, request.maxPrice)
|
||||||
|
await marketplace.requestStorage(request)
|
||||||
|
await token.approve(marketplace.address, collateral)
|
||||||
|
await marketplace.deposit(collateral)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("emits event when storage is offered", async function () {
|
||||||
|
await expect(marketplace.offerStorage(offer))
|
||||||
|
.to.emit(marketplace, "StorageOffered")
|
||||||
|
.withArgs(offerId(offer), offerToArray(offer))
|
||||||
|
})
|
||||||
|
|
||||||
|
it("rejects offer for unknown request", async function () {
|
||||||
|
let unknown = exampleRequest()
|
||||||
|
let invalid = { ...offer, requestId: requestId(unknown) }
|
||||||
|
await expect(marketplace.offerStorage(invalid)).to.be.revertedWith(
|
||||||
|
"Unknown request"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("rejects an expired offer", async function () {
|
||||||
|
let expired = { ...offer, expiry: now() - hours(1) }
|
||||||
|
await expect(marketplace.offerStorage(expired)).to.be.revertedWith(
|
||||||
|
"Offer expired"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("rejects an offer that exceeds the maximum price", async function () {
|
||||||
|
let invalid = { ...offer, price: request.maxPrice + 1 }
|
||||||
|
await expect(marketplace.offerStorage(invalid)).to.be.revertedWith(
|
||||||
|
"Price too high"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("rejects resubmission of offer", async function () {
|
||||||
|
await marketplace.offerStorage(offer)
|
||||||
|
await expect(marketplace.offerStorage(offer)).to.be.revertedWith(
|
||||||
|
"Offer already exists"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("rejects offer with insufficient collateral", async function () {
|
||||||
|
let insufficient = collateral - 1
|
||||||
|
await marketplace.withdraw()
|
||||||
|
await token.approve(marketplace.address, insufficient)
|
||||||
|
await marketplace.deposit(insufficient)
|
||||||
|
await expect(marketplace.offerStorage(offer)).to.be.revertedWith(
|
||||||
|
"Insufficient collateral"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
function requestId(request) {
|
function requestId(request) {
|
||||||
@ -70,6 +127,15 @@ function requestId(request) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function offerId(offer) {
|
||||||
|
return keccak256(
|
||||||
|
defaultAbiCoder.encode(
|
||||||
|
["bytes32", "uint256", "uint256"],
|
||||||
|
offerToArray(offer)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function requestToArray(request) {
|
function requestToArray(request) {
|
||||||
return [
|
return [
|
||||||
request.duration,
|
request.duration,
|
||||||
@ -81,3 +147,7 @@ function requestToArray(request) {
|
|||||||
request.nonce,
|
request.nonce,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function offerToArray(offer) {
|
||||||
|
return [offer.requestId, offer.price, offer.expiry]
|
||||||
|
}
|
||||||
|
|||||||
@ -17,9 +17,14 @@ const exampleBid = () => ({
|
|||||||
bidExpiry: now() + hours(1),
|
bidExpiry: now() + hours(1),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const exampleOffer = () => ({
|
||||||
|
price: 42,
|
||||||
|
expiry: now() + hours(1),
|
||||||
|
})
|
||||||
|
|
||||||
const exampleLock = () => ({
|
const exampleLock = () => ({
|
||||||
id: hexlify(randomBytes(32)),
|
id: hexlify(randomBytes(32)),
|
||||||
expiry: now() + hours(1),
|
expiry: now() + hours(1),
|
||||||
})
|
})
|
||||||
|
|
||||||
module.exports = { exampleRequest, exampleBid, exampleLock }
|
module.exports = { exampleRequest, exampleOffer, exampleBid, exampleLock }
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user