QuiverMarketplaceV2
0x0b3b0d6af0e42d42d324c38bf6b09325642b9bdd
Verification
Verified
v0.8.34+commit.80d5c536
Type
Contract
15,702 bytes
ABI entries
43
13 read · 11 write
License
none
Contract information
- Address
- 0x0b3b0d6af0e42d42d324c38bf6b09325642b9bdd
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.34+commit.80d5c536
- Optimization
- Disabled
- Creator
- 0xC074e16d25…c89151535d
- Creation tx
- 0x528fe1119a…acf76f50c1
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (13)
BPS_DENOMINATOR() → uint96
MAX_TOTAL_FEE_BPS() → uint96
approvedCollections(address) → bool
cancelledOrders(bytes32) → bool
eip712Domain() → bytes1, string, string, uint256, address, bytes32, uint256[]
feeRecipient() → address
filledOrders(bytes32) → bool
hashOrder(tuple) → bytes32
minimumNonces(address) → uint256
owner() → address
paused() → bool
platformFeeBps() → uint96
royaltyFallbacks(address) → address, uint96
Events (11)
CollectionApprovalChangedEIP712DomainChangedFeeRecipientSetNonceIncrementedOrderCancelledOrderFilledOwnershipTransferredPausedPlatformFeeSetRoyaltyFallbackSetUnpaused
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "initialOwner",
"type": "address"
},
{
"internalType": "address payable",
"name": "initialFeeRecipient",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "EnforcedPause",
"type": "error"
},
{
"inputs": [],
"name": "ExpectedPause",
"type": "error"
},
{
"inputs": [],
"name": "InvalidShortString",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "OwnableInvalidOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "OwnableUnauthorizedAccount",
"type": "error"
},
{
"inputs": [],
"name": "ReentrancyGuardReentrantCall",
"type": "error"
},
{
"inputs": [
{
"internalType": "string",
"name": "str",
"type": "string"
}
],
"name": "StringTooLong",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "nft",
"type": "address"
},
{
"indexed": false,
"internalType": "bool",
"name": "approved",
"type": "bool"
}
],
"name": "CollectionApprovalChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [],
"name": "EIP712DomainChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "recipient",
"type": "address"
}
],
"name": "FeeRecipientSet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "seller",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
}
],
"name": "NonceIncremented",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "orderHash",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "seller",
"type": "address"
}
],
"name": "OrderCancelled",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "orderHash",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "nft",
"type": "address"
},
{
"indexed": true,
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "seller",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "buyer",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "price",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "royalty",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "platformFee",
"type": "uint256"
}
],
"name": "OrderFilled",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "Paused",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint96",
"name": "bps",
"type": "uint96"
}
],
"name": "PlatformFeeSet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "nft",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "receiver",
"type": "address"
},
{
"indexed": false,
"internalType": "uint96",
"name": "bps",
"type": "uint96"
}
],
"name": "RoyaltyFallbackSet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "Unpaused",
"type": "event"
},
{
"inputs": [],
"name": "BPS_DENOMINATOR",
"outputs": [
{
"internalType": "uint96",
"name": "",
"type": "uint96"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_TOTAL_FEE_BPS",
"outputs": [
{
"internalType": "uint96",
"name": "",
"type": "uint96"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "approvedCollections",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "seller",
"type": "address"
},
{
"internalType": "address",
"name": "nft",
"type": "address"
},
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "price",
"type": "uint256"
},
{
"internalType": "uint64",
"name": "startTime",
"type": "uint64"
},
{
"internalType": "uint64",
"name": "endTime",
"type": "uint64"
},
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "address",
"name": "taker",
"type": "address"
},
{
"internalType": "uint96",
"name": "maxPlatformFeeBps",
"type": "uint96"
},
{
"internalType": "uint96",
"name": "maxRoyaltyBps",
"type": "uint96"
}
],
"internalType": "struct QuiverMarketplaceV2.Order",
"name": "order",
"type": "tuple"
}
],
"name": "cancelOrder",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"name": "cancelledOrders",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "eip712Domain",
"outputs": [
{
"internalType": "bytes1",
"name": "fields",
"type": "bytes1"
},
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "version",
"type": "string"
},
{
"internalType": "uint256",
"name": "chainId",
"type": "uint256"
},
{
"internalType": "address",
"name": "verifyingContract",
"type": "address"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "uint256[]",
"name": "extensions",
"type": "uint256[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "feeRecipient",
"outputs": [
{
"internalType": "address payable",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"name": "filledOrders",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "seller",
"type": "address"
},
{
"internalType": "address",
"name": "nft",
"type": "address"
},
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "price",
"type": "uint256"
},
{
"internalType": "uint64",
"name": "startTime",
"type": "uint64"
},
{
"internalType": "uint64",
"name": "endTime",
"type": "uint64"
},
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "address",
"name": "taker",
"type": "address"
},
{
"internalType": "uint96",
"name": "maxPlatformFeeBps",
"type": "uint96"
},
{
"internalType": "uint96",
"name": "maxRoyaltyBps",
"type": "uint96"
}
],
"internalType": "struct QuiverMarketplaceV2.Order",
"name": "order",
"type": "tuple"
},
{
"internalType": "bytes",
"name": "signature",
"type": "bytes"
}
],
"name": "fulfillOrder",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "seller",
"type": "address"
},
{
"internalType": "address",
"name": "nft",
"type": "address"
},
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "price",
"type": "uint256"
},
{
"internalType": "uint64",
"name": "startTime",
"type": "uint64"
},
{
"internalType": "uint64",
"name": "endTime",
"type": "uint64"
},
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "address",
"name": "taker",
"type": "address"
},
{
"internalType": "uint96",
"name": "maxPlatformFeeBps",
"type": "uint96"
},
{
"internalType": "uint96",
"name": "maxRoyaltyBps",
"type": "uint96"
}
],
"internalType": "struct QuiverMarketplaceV2.Order",
"name": "order",
"type": "tuple"
}
],
"name": "hashOrder",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "incrementNonce",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "minimumNonces",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pause",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "paused",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "platformFeeBps",
"outputs": [
{
"internalType": "uint96",
"name": "",
"type": "uint96"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "royaltyFallbacks",
"outputs": [
{
"internalType": "address",
"name": "receiver",
"type": "address"
},
{
"internalType": "uint96",
"name": "bps",
"type": "uint96"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "nft",
"type": "address"
},
{
"internalType": "bool",
"name": "approved",
"type": "bool"
}
],
"name": "setCollectionApproved",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address payable",
"name": "recipient",
"type": "address"
}
],
"name": "setFeeRecipient",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint96",
"name": "bps",
"type": "uint96"
}
],
"name": "setPlatformFee",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "nft",
"type": "address"
},
{
"internalType": "address",
"name": "receiver",
"type": "address"
},
{
"internalType": "uint96",
"name": "bps",
"type": "uint96"
}
],
"name": "setRoyaltyFallback",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "unpause",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/interfaces/IERC2981.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
contract QuiverMarketplaceV2 is Ownable, Pausable, ReentrancyGuard, EIP712 {
uint96 public constant BPS_DENOMINATOR = 10000;
uint96 public constant MAX_TOTAL_FEE_BPS = 2000;
bytes32 private constant ORDER_TYPEHASH =
keccak256(
"Order(address seller,address nft,uint256 tokenId,uint256 price,uint64 startTime,uint64 endTime,uint256 nonce,bytes32 salt,address taker,uint96 maxPlatformFeeBps,uint96 maxRoyaltyBps)"
);
struct Order {
address seller;
address nft;
uint256 tokenId;
uint256 price;
uint64 startTime;
uint64 endTime;
uint256 nonce;
bytes32 salt;
address taker;
uint96 maxPlatformFeeBps;
uint96 maxRoyaltyBps;
}
struct RoyaltyFallback {
address receiver;
uint96 bps;
}
mapping(address => bool) public approvedCollections;
mapping(address => RoyaltyFallback) public royaltyFallbacks;
mapping(bytes32 => bool) public filledOrders;
mapping(bytes32 => bool) public cancelledOrders;
mapping(address => uint256) public minimumNonces;
uint96 public platformFeeBps = 500;
address payable public feeRecipient;
event CollectionApprovalChanged(address indexed nft, bool approved);
event RoyaltyFallbackSet(
address indexed nft,
address indexed receiver,
uint96 bps
);
event PlatformFeeSet(uint96 bps);
event FeeRecipientSet(address indexed recipient);
event OrderCancelled(bytes32 indexed orderHash, address indexed seller);
event NonceIncremented(address indexed seller, uint256 nonce);
event OrderFilled(
bytes32 indexed orderHash,
address indexed nft,
uint256 indexed tokenId,
address seller,
address buyer,
uint256 price,
uint256 royalty,
uint256 platformFee
);
constructor(address initialOwner, address payable initialFeeRecipient)
Ownable(initialOwner)
EIP712("Quiver Marketplace", "2")
{
require(initialFeeRecipient != address(0), "zero address");
feeRecipient = initialFeeRecipient;
}
function setCollectionApproved(address nft, bool approved) external onlyOwner {
require(nft != address(0), "zero address");
approvedCollections[nft] = approved;
emit CollectionApprovalChanged(nft, approved);
}
function setRoyaltyFallback(address nft, address receiver, uint96 bps)
external
onlyOwner
{
require(nft != address(0), "zero address");
require(bps <= MAX_TOTAL_FEE_BPS, "fee too high");
royaltyFallbacks[nft] = RoyaltyFallback({receiver: receiver, bps: bps});
emit RoyaltyFallbackSet(nft, receiver, bps);
}
function setPlatformFee(uint96 bps) external onlyOwner {
require(bps <= MAX_TOTAL_FEE_BPS, "fee too high");
platformFeeBps = bps;
emit PlatformFeeSet(bps);
}
function setFeeRecipient(address payable recipient) external onlyOwner {
require(recipient != address(0), "zero address");
feeRecipient = recipient;
emit FeeRecipientSet(recipient);
}
function pause() external onlyOwner {
_pause();
}
function unpause() external onlyOwner {
_unpause();
}
function hashOrder(Order calldata order) external view returns (bytes32) {
return _hashOrder(order);
}
function fulfillOrder(Order calldata order, bytes calldata signature)
external
payable
nonReentrant
whenNotPaused
{
IERC721 token = IERC721(order.nft);
bytes32 orderHash = _hashOrder(order);
_validateOrder(order, signature, token, orderHash);
(
address royaltyReceiver,
uint256 royalty,
uint256 platformFee,
uint256 totalFee
) = _fees(order);
filledOrders[orderHash] = true;
token.safeTransferFrom(order.seller, msg.sender, order.tokenId);
if (royalty > 0) {
_pay(payable(royaltyReceiver), royalty);
}
if (platformFee > 0) {
_pay(feeRecipient, platformFee);
}
_pay(payable(order.seller), order.price - totalFee);
emit OrderFilled(
orderHash,
order.nft,
order.tokenId,
order.seller,
msg.sender,
order.price,
royalty,
platformFee
);
}
function cancelOrder(Order calldata order) external {
require(order.seller == msg.sender, "not seller");
bytes32 orderHash = _hashOrder(order);
cancelledOrders[orderHash] = true;
emit OrderCancelled(orderHash, msg.sender);
}
function incrementNonce() external {
minimumNonces[msg.sender] += 1;
emit NonceIncremented(msg.sender, minimumNonces[msg.sender]);
}
function _validateOrder(
Order calldata order,
bytes calldata signature,
IERC721 token,
bytes32 orderHash
) private view {
require(order.seller != address(0), "zero seller");
require(approvedCollections[order.nft], "collection not approved");
require(order.price > 0, "price zero");
require(msg.value == order.price, "bad value");
require(order.startTime <= block.timestamp, "not started");
require(block.timestamp < order.endTime, "ended");
require(order.taker == address(0) || order.taker == msg.sender, "not taker");
require(!filledOrders[orderHash], "filled");
require(!cancelledOrders[orderHash], "cancelled");
require(order.nonce >= minimumNonces[order.seller], "nonce too low");
require(order.maxPlatformFeeBps >= platformFeeBps, "platform fee too high");
require(token.ownerOf(order.tokenId) == order.seller, "not owner");
require(_isApproved(token, order.tokenId, order.seller), "not approved");
require(
SignatureChecker.isValidSignatureNow(
order.seller,
orderHash,
signature
),
"bad signature"
);
}
function _fees(Order calldata order)
private
view
returns (
address royaltyReceiver,
uint256 royalty,
uint256 platformFee,
uint256 totalFee
)
{
(royaltyReceiver, royalty) = _royalty(
order.nft,
order.tokenId,
order.price
);
uint256 maxRoyalty = (order.price * order.maxRoyaltyBps) /
BPS_DENOMINATOR;
require(royalty <= maxRoyalty, "royalty too high");
platformFee = _platformFee(order.price);
totalFee = royalty + platformFee;
uint256 maxFee = (order.price * MAX_TOTAL_FEE_BPS) / BPS_DENOMINATOR;
require(totalFee <= maxFee, "fee too high");
}
function _hashOrder(Order calldata order) private view returns (bytes32) {
return
_hashTypedDataV4(
keccak256(
abi.encode(
ORDER_TYPEHASH,
order.seller,
order.nft,
order.tokenId,
order.price,
order.startTime,
order.endTime,
order.nonce,
order.salt,
order.taker,
order.maxPlatformFeeBps,
order.maxRoyaltyBps
)
)
);
}
function _isApproved(
IERC721 token,
uint256 tokenId,
address owner
) private view returns (bool) {
return
token.getApproved(tokenId) == address(this) ||
token.isApprovedForAll(owner, address(this));
}
function _platformFee(uint256 price) private view returns (uint256) {
return (price * platformFeeBps) / BPS_DENOMINATOR;
}
function _royalty(
address nft,
uint256 tokenId,
uint256 price
) private view returns (address receiver, uint256 amount) {
try IERC165(nft).supportsInterface(type(IERC2981).interfaceId) returns (
bool supported
) {
if (supported) {
try IERC2981(nft).royaltyInfo(tokenId, price) returns (
address royaltyReceiver,
uint256 royaltyAmount
) {
if (royaltyReceiver != address(0) && royaltyAmount > 0) {
return (royaltyReceiver, royaltyAmount);
}
} catch {}
}
} catch {}
RoyaltyFallback memory fallbackRoyalty = royaltyFallbacks[nft];
if (
fallbackRoyalty.receiver == address(0) || fallbackRoyalty.bps == 0
) {
return (address(0), 0);
}
return (
fallbackRoyalty.receiver,
(price * fallbackRoyalty.bps) / BPS_DENOMINATOR
);
}
function _pay(address payable to, uint256 amount) private {
if (amount == 0) {
return;
}
(bool sent, ) = to.call{value: amount}("");
require(sent, "payout failed");
}
}
Chain explorer3565msChain node74ms