πŸ›’Marketplace

OnChainMarketplace

Git Source

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

Name
Type
Description

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

Name
Type
Description

_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

Name
Type
Description

<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

Name
Type
Description

_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

Name
Type
Description

<none>

bytes4

bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")) if transfer is allowed

onERC1155BatchReceived

Parameters

Name
Type
Description

_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

Name
Type
Description

_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

Name
Type
Description

_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

Name
Type
Description

_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

Name
Type
Description

_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

Name
Type
Description

_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

Name
Type
Description

_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

Name
Type
Description

_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

Name
Type
Description

_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

Name
Type
Description

_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

Name
Type
Description

_orderId

uint256

The ID of the order to cancel.

updateETHOrderCommission

Sets the commission for an order.

Parameters

Name
Type
Description

_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

Name
Type
Description

_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

Name
Type
Description

_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

Name
Type
Description

_orderId

uint256

The ID of the order.

Returns

Name
Type
Description

<none>

Order

Order The order details.

getETHOffer

getUSDCOrder

getUSDCOffer

supportsInterface

See {IERC165-supportsInterface}.

Checks if the contract supports a specific interface.

Parameters

Name
Type
Description

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

Name
Type
Description

<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

Name
Type
Description

_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

Name
Type
Description

_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

Name
Type
Description

_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

Name
Type
Description

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

Name
Type
Description

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

Name
Type
Description

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

Name
Type
Description

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

Name
Type
Description

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

Name
Type
Description

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

Name
Type
Description

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

Name
Type
Description

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

Name
Type
Description

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

Git Source

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

Name
Type
Description

_USDC

address

_AggregatorV3Interface

address

feeRecipient_

address

address (multisig) controlled by Ninfa that will receive any market fees

_salesFeeBps_

uint256

lazyBuy

Parameters

Name
Type
Description

_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