Protocol Warnings 🚨

Making Payments and Receiving NFTs As A Contract

  1. The NiftyApes Seller Financing protocol abides by the ERC721 safeTransferFrom() pattern. All contracts interacting with the system must implement onERC721Receive() in compliance with ERC721 if they expect to receive NFTS. For example, if the buyer is a contract that has not implemented onERC721Receive() it will be able to call makePayment() and make payments on a seller financing loan but will NOT be able to fully repay and receive the NFT at the close of the loan. This may result in a default and asset seizure by the seller.

Accepting Payments As A Contract

  1. Any contract holding a seller ticket and expecting to receive payments MUST be able to receive ETH. If the holder of a seller ticket cannot receive ETH at the time of a loan payment by a buyer they will be slashed for the value of the payment. All payment value sent will be deducted from the remaining loan principal, possibly closing the loan, no funds will be sent to the holder of the seller ticket (as the actor cannot receive ETH), and all funds will be returned to the msg.sender.

This dynamic prevents a nefarious seller from sending the seller ticket to a contract that cannot accept ETH, barring the buyer from making a payment in earnest, and forcing an eventual default whereby the seller could seize the underlying NFT.

Privileged Roles and Ownership

  1. The owner of the Seller Financing contract has the ability to call these functions which modify some limited aspects of state and state addresses:

    • updateRoyaltiesEngineContractAddress()
    • updateDelegateRegistryContractAddress()
    • updateSeaportContractAddress()
    • updateWethContractAddress()
    • pause(), unpause()
    • pauseSanctions(), unpauseSanctions()
  2. The owner of any Marketplace Integration contract has the ability to call these functions which modify some limited aspects of state and state addresses:

    • updateSellerFinancingContractAddress()
    • updateMarketplaceFeeRecipient()
    • updateMarketplaceFeeBps()
    • pause(), unpause()
    • pauseSanctions(), unpauseSanctions()

Limited Protocol and Loan Time Horizon

  1. The NiftyApes Seller Financing Protocol v1.0 utilizes uint32 timestamps throughout the protocol. This has been done to reduce the compile time contract size and runtime gas usage. This means that the protocol will cease to function and no loans can go beyond January 19, 2038 at 3:14:07 UTC, roughly 15 years from the time of writing. The intention is for this first version of the protocol to be phased out in the future in favor of a second version that does support loans beyond this January 19, 2038 at 3:14:07 UTC.

withdrawOfferSignature() Vulnerable to Front-Running

  1. withdrawOfferSignature() is vulnerable to front-running. If a seller submits a transaction calling withdrawOfferSignature() to the mempool on an undesirable existing offer, a savvy witnesser could see this and buy the offer prior to the cancellation. This can result in sellers being forced to finance NFTs on Offers that they don't intend to keep or are no longer desirable due to market conditions. We suggest utilizing a short expiration for offers and resubmitting offers often or submitting a withdrawOfferSignature() transaction via a service such as Flashbots as a way to mitigate this issue as well as market volatility and foster price discovery.

Seller Cannot Make The Exact Same Offer Twice

  1. If an offer's signature gets used once or has been previously canceled, the seller will be unable to make the same offer again. We suggest simply incrementing the expiration timestamp by one second to mitigate this issue.

Excessive Ether Funds are Refunded to the Buyer Rather than Msg.sender

  1. In buyWithFinancing(), if msg.value exceeds offer.downPaymentAmount(), the refund is issued to the buyer ticket holder rather than the msg.sender. This is done so that buyers receive their refund for purchases made through the MarketplaceIntegration contract, which is deployed by a 3rd party and collects a marketplace fee before making an external call to SellerFinancing.buyWithFinancing(). Users interacting directly with the SellerFinancing contract should be aware that excessive payments are sent directly to the buyer ticket holder.

Purchase of Defaulted Buyer Tickets

  1. Buyer and seller tickets are transferable at any time, even after a loan is in default. A purchaser of a buyer ticket should be aware of the state of the debt obligation they are purchasing before executing any transaction, as it may be in default and open to immediate asset seizure.