πMarketplace
OnChainMarketplace
Inherits: Owned, RoyaltyEngineV1
Author: cosimo.demedici.eth (https://github.com/ninfa-labs/ninfa-contracts)
NFT marketplace featuring on-chain orders and offers. An order's quote/underlyer currency or token can be set to either ETH or USDC. If the currency/token used to pay is different from the quote one, the exchange rate calculated by the marketplace by using "Chainlink" price feeds.
State Variables
_dataFeed
utility library for address type
utility library for auto-incrementing Counters
Chainlink price feed aggregator interface
The Address library is used to check if an address is a contract, and to send ETH to an address.
functions available for Counter type are .increment() and .current()
The price feed is used to determine the price of NFTs in USDC
AggregatorV3Interface private immutable _dataFeed;_USDC
USDC contract address
_TOTAL_BPS
10,000 basis points = 100% shares sale price
ETHOrderCount
auto-incrementing counter for ETH orders (NFT price is set in ETH)
USDCOrderCount
auto-incrementing counter for USDC orders (NFT price is set in USDC)
ETHOfferCount
auto-incrementing counter for ETH offers (NFT offer paid with ETH)
USDCOfferCount
auto-incrementing counter for USDC offers (NFT offer paid with USDC)
_salesFeeRecipient
address for receiving fees generated by the marketplace
_salesFeeBps
primary market sales fee in basis points
_ETHOrders
mapping from ETHOrderCount counter to Order struct.
_ETHOrders needs an explicit getter function in order to return the content of its arrays, see getETHOrder()
_USDCOrders
mapping from USDCOrderCount counter to Order struct.
_USDCOrders needs an explicit getter function in order to return the content of its arrays, see getUSDCOrder()
_ETHOffers
mapping from ETHOfferCount counter to Offer struct.
_ETHOffers needs an explicit getter function in order to return the content of its arrays, see getETHOffer()
_USDCOffers
mapping from USDCOfferCount counter to Offer struct.
_USDCOffers needs an explicit getter function in order to return the content of its arrays, see getUSDCOffer()
Functions
constructor
Constructor for the OnChainMarketplace contract
Initializes the contract with a fee recipient and fee basis points
Parameters
USDC_
address
ETHUSDPriceFeed_
address
feeRecipient_
address
The address controlled by Ninfa that will receive any market fees
_salesFeeBps_
uint256
The fee in basis points
onERC721Received
This function is called whenever an {IERC721} tokenId token is transferred to this contract via {IERC721-safeTransferFrom} by operator from from.
It must return its Solidity selector to confirm the token transfer. If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. The selector can be obtained in Solidity with IERC721.onERC721Received.selector.
Parameters
_operator
address
The address which called safeTransferFrom function
_from
address
The address which previously owned the token
_tokenId
uint256
The NFT identifier which is being transferred
_data
bytes
Additional data with no specified format, decoded to offerId, unitPrice, commissionBps, and commissionRecipient
Returns
<none>
bytes4
bytes4 0x150b7a02 to confirm the token transfer
onERC1155Received
Handles the receipt of a single ERC1155 token type.
This function is called at the end of a safeTransferFrom after the balance has been updated. To accept the transfer, this must return bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")) (i.e. 0xf23a6e61, or its own function selector).
Parameters
_operator
address
The address which initiated the transfer (i.e. msg.sender or whoever called safeTransferFrom on the ERC1155 contract)
_from
address
The address which previously owned the token
_tokenId
uint256
The ID of the token being transferred
_value
uint256
The amount of tokens being transferred
_data
bytes
Additional data with no specified format
Returns
<none>
bytes4
bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")) if transfer is allowed
onERC1155BatchReceived
Parameters
_operator
address
_from
address
_ids
uint256[]
_values
uint256[]
_data
bytes
fillETHOrder
The collector calls this function to buy an NFT at the ask price, only if an order exists.
comparing array length of _data and _ids is not needed as it is not an exploitable flaw, this saves gas to the user i.e. require((len1 ^ len2 ^ len3 ^ len4) == 0, "Array lengths are not equal");
If someone has an open offer but calls fillETHOrder, the offer will remain open. They will need to call cancelOffer() to get a refund. This is unlikely, as users will likely be aware of this and use the refund in order to pay for part of the order.
MUST: msg.value must be equal to _ETHOrders[_orderId].unitPrice * buyAmount and _ETHOrders[_orderId].sellAmount >= buyAmount.
Parameters
_id
uint256
This parameter avoids having to store a mapping to order id like the deprecated mapping(address => mapping(uint256 => uint256)) private _tokenToOrderId which would not have worked for erc1155 as each token has a supply. _orderId does not constitute a vulnerability as it is user provided, since a regular user will go through the frontend which gets orderId from events.
_buyer
address
This parameter is needed in order to integrate Wert payment solution, because in every txn Wert is the msg.sender, although using msg.sender would cost less gas. It is possible for the buyer to use this parameter simply in order to transfer the NFT to an address other than their own, this can be useful for external contract buying NFTs.
_ERC1155Value
uint256
This is the market order amount (total or partial fill). _ERC1155Value == 0 corresponds to one erc721 tokenId, _ERC1155Value > 0 for erc1155 tokenIds.
fillUSDCOrder
The collector calls this function to buy an NFT at the ask price, only if an order exists.
If someone has an open offer but calls fillUSDCOrder, the offer will remain open. They will need to call cancelOffer() to get a refund. This is unlikely, as users will likely be aware of this and use the refund in
createOffer
This function allows users to create offers for NFTs.
Offers can be made independently of whether the token is on sale or not. The msg.value is used to determine the offer amount, so no function parameter is needed for that.
Parameters
_tokenId
uint256
The token id for which the offer is made.
_unitPrice
uint256
Price for each ERC721 or ERC1155 token. If ERC1155, then msg.value must equal _unitPrice * _ERC1155Value.
_ERC1155Value
uint256
The token value desired. _ERC1155Value == 0 for ERC721 and _ERC1155Value > 0 for ERC1155.
_collection
address
Address of the NFT implementation or proxy contract.
_from
address
This parameter is mainly needed in order to integrate third-party credit card payment providers, as in every transaction the payment provider's contract is the msg.sender. The buyer may also use this parameter simply in order to transfer the NFT to an address other than their own.
createMultiOffer
checking that msg.value == _unitPrice saves gas as it removes the need for the statement require(msg.value == _unitPrice), which would have to be added if the below if check read msg.value > 0 instead. this implies that if msg.value is different from the specified _unitPrice, the execution flow will enter the else block and attempt to transfer _unitValue worth of USDC to this contract, which will revert due to "insufficient balance" as ethereum has 18 decimals whereas USDC only has 6.
acceptETHOffer
Called when a seller accepts an offer for a token that is on escrow on the marketplace. Commissions if any are set on the corresponding sell order.
XOR-ing the same numbers will cancel them out to 0. If any pair is not equal, the XOR will not be zero.
This function allows partial fills of offers. For ERC1155 tokens, the requested offer token value does not have to be fulfilled in its entirety.
Parameters
_orderId
uint256
The ID of the order.
_offerId
uint256
The ID of the offer.
_ERC1155Value
uint256
Specifies the token value to sell from the order for partial fills of offers. It must be set to a positive integer for offers on ERC1155 tokens and left as 0 for ERC721 tokens. However, it cannot be greater than the offer's token value itself.
acceptUSDCOffer
deleteETHOffer
Cancels an offer and refunds ETH back to the bidder.
When an order gets filled, the offer isn't marked as cancelled, in order to allow users to claim back their money.
Parameters
_offerId
uint256
The ID of the offer.
deleteUSDCOffer
Cancels an offer and refunds USDC back to the bidder.
When an order gets filled, the offer isn't marked as cancelled, in order to allow users to claim back their money.
Parameters
_offerId
uint256
The ID of the offer.
raiseETHOfferPrice
Raises the price of an existing offer.
This is one of two functions called by a buyer to modify their offer. The other function is lowerOffer(). This function requires a msg.value which will be added to the old offer amount. The frontend needs to calculate the difference between the old and new offer.
Parameters
_offerId
uint256
The ID of the offer.
_newERC1155Value
uint256
The new value for the ERC1155 token.
_newUnitPrice
uint256
The new unit price.
raiseUSDCOfferPrice
lowerETHOfferPrice
Lowers the price of an existing offer.
This is one of two functions called by a buyer to modify their offer. The other function is raiseOffer(). This function requires a uint parameter representing the new (lower) offer; the buyer will get refunded the difference.
Parameters
_offerId
uint256
The ID of the offer.
_newERC1155Value
uint256
The new amount for the ERC1155 token.
_newUnitPrice
uint256
The new unit price.
lowerUSDCOfferPrice
updateETHOrder
Updates or sets the order's unit price and value of ERC1155 token. This function is only for ERC1155. To increase the order's value, call onERC1155Received via the safeTransferFrom function of the ERC1155 token.
Parameters
_orderId
uint256
The ID of the order to update.
_ERC1155RedeemAmount
uint256
The amount of ERC1155 token value to redeem, i.e., subtract from the order. // if the order is an ERC721 token, there is no _ERC1155RedeemAmount, in order to withdraw the token deleteOrder should be called
_newUnitPrice
uint256
The new unit price that will replace the old one. It can be set to 0, but this might indicate a mistake by the seller.
updateUSDCOrder
warning, make changes to storage BEFORE making external calls for transfering the NFT back to the seller (check-effects-interactions pattern)
deleteETHOrder
Cancels an order and transfers the NFT back to the owner.
warning, make changes to storage BEFORE making external calls for transfering the NFT back to the seller (check-effects-interactions pattern)
Deletes _ETHOrders[_orderId] from storage before making external calls to transfer the NFT back to the seller. This follows the check-effects-interactions pattern. This function does not check whether the order exists or not.
Parameters
_orderId
uint256
The ID of the order to cancel.
deleteUSDCOrder
Cancels an order and transfers the NFT back to the owner.
Deletes _USDCOrders[_orderId] from storage before making external calls to transfer the NFT back to the seller. This follows the check-effects-interactions pattern. This function does not check whether the order exists or not.
Parameters
_orderId
uint256
The ID of the order to cancel.
updateETHOrderCommission
Sets the commission for an order.
Parameters
_orderId
uint256
The ID of the order to update.
_commissionBps
uint256[]
The commission rates in basis points.
_commissionRecipient
address[]
The addresses of the commission receivers. This function does not check if the total commission is less than 10000. This should revert in the trade function anyway.
updateUSDCOrderCommission
setFeeRecipient
Setter function only callable by contract admin. Used to change the address to which fees are paid.
Parameters
_newFeeRecipient
address
The address owned by NINFA that will collect sales fees.
setMarketFee
Sets market sale fees.
Can only be called by the contract owner.
Parameters
_newFeeBps
uint256
Fee BPS for primary and secondary market orders.
getETHOrder
Required getter for reading arrays returned by Order struct. Declaring variable public does not return the array's content.
the function returns even if the order of offer does not exist or has been deleted, the frontend should handle this case by checking that the operator is not the zero address.
Parameters
_orderId
uint256
The ID of the order.
Returns
<none>
Order
Order The order details.
getETHOffer
getUSDCOrder
getUSDCOffer
supportsInterface
See {IERC165-supportsInterface}.
Checks if the contract supports a specific interface.
Parameters
interfaceId
bytes4
The ID of the interface. - For IERC165, interfaceId == 0x01ffc9a7. - For IERC721Receiver, interfaceId == 0x150b7a02. A wallet/broker/auction application MUST implement the wallet interface if it will accept safe transfers. - Return value from onERC1155Received call if a contract accepts receipt (i.e bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))). In all other cases the ERC1155TokenReceiver rules MUST be followed as appropriate for the implementation (i.e. safe, custom and/or hybrid).
Returns
<none>
bool
bool True if the contract supports the interface, false otherwise.
_handleERC1155OrderOrOffer
_transferNFT
ERC-721 tokens are transferred to the buyer via transferFrom rather than safeTransferFrom. The caller is responsible to confirm that the recipient if a contract is capable of receiving and handling ERC721 and ERC1155 tokens If the minter is a smart contract, it needs to implement onERC721Received (see ERC721-_mint), however this is not the case here.
_createETHOffer
_createUSDCOffer
_handleETHTrade
Handles the trade process, including payment of marketplace fee, royalties, commissions, and seller.
This function is private and can only be called within this contract.
Parameters
_order
Order
The order to be processed.
_id
uint256
The id of the order.
_handleUSDCTrade
Handles the trade process, including payment of marketplace fee, royalties, commissions, and seller.
This function is private and can only be called within this contract.
Parameters
_order
Order
The order to be processed.
_id
uint256
The id of the order.
_createETHOrder
Create a new order on the marketplace by transferring an NFT to it.
Can only be called by transferring a token to this contract via hooks.
Parameters
_operator
address
The address that called safeTransferFrom on the token contract (owner or operator).
_from
address
The seller's address.
_id
uint256
The ID of the NFT.
_value
uint256
The value in case it is an ERC1155.
_unitPrice
uint256
The total price if ERC721, unit price if ERC1155.
_commissionBps
uint256[]
Array of commission basis points.
_commissionRecipient
address[]
Array of addresses to receive commissions.
_createUSDCOrder
Events
OrderCreated
Emitted when a new order is created, includes the ID of the new order.
Parameters
orderId
uint256
The ID of the new order.
quoteToken
QuoteToken
The token used to quote the price of the NFT.
OrderUpdated
Emitted when an existing order is updated, includes the ID of the updated order.
Parameters
orderId
uint256
The ID of the updated order.
quoteToken
QuoteToken
The token used to quote the price of the NFT.
OrderDeleted
Emitted when an order is deleted, includes the ID of the deleted order.
Parameters
orderId
uint256
The ID of the deleted order.
quoteToken
QuoteToken
The token used to quote the price of the NFT.
OfferCreated
Emitted when a new offer is created, includes the ID of the new offer.
Parameters
offerId
uint256
The ID of the new offer.
quoteToken
QuoteToken
The token used to quote the price of the NFT.
OfferUpdated
Emitted when an existing offer is updated, includes the ID of the updated offer.
Parameters
offerId
uint256
The ID of the updated offer.
quoteToken
QuoteToken
The token used to quote the price of the NFT.
OfferDeleted
Emitted when an offer is deleted, includes the ID of the deleted offer.
Parameters
offerId
uint256
The ID of the deleted offer.
quoteToken
QuoteToken
The token used to quote the price of the NFT.
Trade
This event is emitted when a trade occurs.
It is needed because once an order/offer is filled, it is deleted from storage. Hence backends cannot read data using public getters after the event has been emitted.
Parameters
collection
address
The address of the NFT collection.
from
address
The address of the seller of the NFT.
tokenId
uint256
The ID of the token traded.
id
uint256
The ID of the order or offer.
price
uint256
The total ETH value transacted.
ERC1155Value
uint256
The value for ERC1155 tokens. It is 0 for ERC721 and >1 for ERC1155.
quoteToken
QuoteToken
The token used to quote the price of the NFT.
Structs
Order
the Order struct is used both for storing order information, as well as trade information when passed as a function parameter to the private _handleETHTrade function
Properties
tokenId
uint256
collection's token id
unitPrice
uint256
ERC-1155 unit price in ETH, or total price for an ERC-721 token. When the Order struct is passed as a function parameter to _handleETHTrade, unitPrice always refers to the total price of ERC-1155 tokens, i.e. token value * unit price
ERC1155Value
uint256
the token amount, the quantity of ERC-1155 tokens involved in the order. This value is 0 for ERC-721 tokens, since each ERC-721 token is unique and non-divisible
collection
address
address of the ERC721 or ERC1155 contract.
from
address
always refers to the seller, who either creater an order or is accepting an offer.
operator
address
the address of an authorized operator, who is responsible for creating the order. This is used for access control.
commissionBps
uint256[]
the commission amount for the sale, expressed in basis points (0 - 10000, where 10000 basis points equals 100%)
commissionRecipient
address[]
the addresses of the recipients who will receive the commission from the sale. This can include both primary and secondary recipients
Offer
Defines an offer for a token in a collection.
Properties
tokenId
uint256
The ID of the token in the collection.
unitPrice
uint256
The price for each token, applicable to both ERC721 and ERC1155 tokens.
ERC1155Value
uint256
The value for ERC1155 tokens. A value of 0 indicates an ERC721 token, while a value greater than 0 indicates the amount of ERC1155 tokens.
collection
address
The address of the NFT contract.
from
address
The address of the buyer, i.e., the address creating the offer.
Enums
QuoteToken
Enum for the type of token used in the marketplace
HybridMarketplace
Inherits: OnChainMarketplace, EIP712
Author: cosimo.demedici.eth (https://github.com/ninfa-labs/ninfa-contracts)
On-chain (escrowed) and off-chain (lazy) NFT marketplace, * allowing users to create orders without locking their NFTs. *
*inherits from OnChainMarketplace and EIP712 **
State Variables
_VOUCHER_TYPEHASH
for verifying EOA signatures, i.e. recover(bytes32 _digest, bytes memory _signature)
for verifying EIP-1271 signatures, i.e. isValidSignatureNow(address _signer, bytes32 _hash, bytes memory _signature)
for sending value to an address, i.e. sendValue(address _receiver, uint256 _amount)
Functions
constructor
Parameters
_USDC
address
_AggregatorV3Interface
address
feeRecipient_
address
address (multisig) controlled by Ninfa that will receive any market fees
_salesFeeBps_
uint256
lazyBuy
Parameters
_voucher
EncodeType.MarketplaceVoucher
_signature
bytes
_to
address
contains the address to which the NFT will be sent, this is usually the same as msg.sender but sometimes different like in the case of credit card payment providers
_value
uint256
getTypedDataDigest
_lazyBuy outside of if statement as it contains require statements that should be executed regardless of price
returns hashStruct(s : π) = keccak256(typeHash β encodeData(s)) where typeHash = keccak256(encodeType(typeOf(s)))
name
required Solidity override needed for eip712 domain
_lazyBuy
Events
VoucherVoided
Last updated