dagger-contracts/contracts/Marketplace.sol

162 lines
4.7 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./Collateral.sol";
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)
Collateral(_token)
marketplaceInvariant
{
collateral = _collateral;
}
function requestStorage(Request calldata request)
public
marketplaceInvariant
{
require(request.client == msg.sender, "Invalid client address");
bytes32 id = keccak256(abi.encode(request));
require(requests[id].client == address(0), "Request already exists");
requests[id] = request;
_createLock(id, request.expiry);
funds.received += request.ask.maxPrice;
funds.balance += request.ask.maxPrice;
transferFrom(msg.sender, request.ask.maxPrice);
emit StorageRequested(id, request.ask);
}
function offerStorage(Offer calldata offer) public marketplaceInvariant {
require(offer.host == msg.sender, "Invalid host address");
require(balanceOf(msg.sender) >= collateral, "Insufficient collateral");
Request storage request = requests[offer.requestId];
require(request.client != address(0), "Unknown request");
require(request.expiry > block.timestamp, "Request expired");
require(offer.price <= request.ask.maxPrice, "Price too high");
bytes32 id = keccak256(abi.encode(offer));
require(offers[id].host == address(0), "Offer already exists");
offers[id] = offer;
_lock(msg.sender, offer.requestId);
emit StorageOffered(id, offer, offer.requestId);
}
function selectOffer(bytes32 id) public marketplaceInvariant {
Offer storage offer = offers[id];
require(offer.host != address(0), "Unknown offer");
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.selectedOffer == bytes32(0), "Offer already selected");
state.selectedOffer = id;
_createLock(id, offer.expiry);
_lock(offer.host, id);
_unlock(offer.requestId);
uint256 difference = request.ask.maxPrice - offer.price;
funds.sent += difference;
funds.balance -= difference;
token.transfer(request.client, difference);
emit OfferSelected(id, offer.requestId);
}
function _request(bytes32 id) internal view returns (Request storage) {
return requests[id];
}
function _offer(bytes32 id) internal view returns (Offer storage) {
return offers[id];
}
function _selectedOffer(bytes32 requestId) internal view returns (bytes32) {
return requestState[requestId].selectedOffer;
}
uint256 private constant POR_SECTORS = 10; // amount of sectors in PoR scheme
struct Request {
address client;
Ask ask;
Content content;
uint256 expiry; // time at which this request expires
bytes32 nonce; // random nonce to differentiate between similar requests
}
struct Ask {
uint256 size; // size of requested storage in number of bytes
uint256 duration; // how long content should be stored (in seconds)
uint256 proofProbability; // how often storage proofs are required
uint256 maxPrice; // maximum price client will pay (in number of tokens)
}
struct Content {
string cid; // content id (if part of a larger set, the chunk cid)
Erasure erasure; // Erasure coding attributes
PoR por; // Proof of Retrievability parameters
}
struct Erasure {
uint64 totalChunks; // the total number of chunks in the larger data set
uint64 totalNodes; // the total number of nodes that store the data set
uint64 nodeId; // index of this node in the list of total nodes
}
struct PoR {
bytes1[POR_SECTORS * 48] u; // parameters u_1..u_s
bytes1[96] publicKey; // public key
bytes1[512] name; // random name
}
struct RequestState {
bytes32 selectedOffer;
}
struct Offer {
address host;
bytes32 requestId;
uint256 price;
uint256 expiry;
}
event StorageRequested(bytes32 requestId, Ask ask);
event StorageOffered(bytes32 offerId, Offer offer, bytes32 indexed requestId);
event OfferSelected(bytes32 offerId, bytes32 indexed requestId);
modifier marketplaceInvariant() {
MarketplaceFunds memory oldFunds = funds;
_;
assert(funds.received >= oldFunds.received);
assert(funds.sent >= oldFunds.sent);
assert(funds.received == funds.balance + funds.sent);
}
struct MarketplaceFunds {
uint256 balance;
uint256 received;
uint256 sent;
}
}