Phrase Trade NFT Marketplace

Overview

The PhraseTradeNFTMarketplace is a Solidity smart contract that implements a decentralized marketplace for trading Non-Fungible Tokens (NFTs). This contract allows users to list their NFTs for sale, buy listed NFTs, and delist their own NFTs. It supports payments in both Ether (ETH) and ERC-20 tokens.

Contract Details

  • License: BSUL

  • Solidity Version: ^0.8.0

  • Inherits From:

    • Ownable: Provides basic authorization control functions

    • IPhraseTradeNFTMarketplace: Interface defining the contract's structure

Key Structures

Listing

struct Listing {
    address seller;
    address paymentToken;
    uint256 price;
}
  • seller: Address of the NFT seller

  • paymentToken: Address of the token to be used for payment (0x0 for ETH)

  • price: Price of the NFT in the specified payment token

State Variables

listings

mapping(address => mapping(uint256 => Listing)) public listings;

A nested mapping that stores NFT listings. The first key is the NFT contract address, the second key is the token ID, and the value is the Listing struct.

Functions

listNFT

function listNFT(
    address nftContract,
    uint256 tokenId,
    address paymentToken,
    uint256 price
) external

Lists an NFT for sale.

Parameters:

  • nftContract: Address of the NFT contract

  • tokenId: ID of the NFT

  • paymentToken: Address of the token to be used for payment (0x0 for ETH)

  • price: Price of the NFT in the specified payment token

Functionality:

  1. Transfers the NFT from the seller to the contract

  2. Creates a new Listing and stores it in the listings mapping

  3. Emits an NFTListed event

Security Considerations:

  • Ensure that the contract has approval to transfer the NFT before calling this function

buyNFT

function buyNFT(address nftContract, uint256 tokenId) external payable

Allows a user to purchase a listed NFT.

Parameters:

  • nftContract: Address of the NFT contract

  • tokenId: ID of the NFT

Functionality:

  1. Retrieves the listing information

  2. Handles payment in ETH or ERC-20 tokens

  3. Transfers the NFT to the buyer

  4. Deletes the listing

  5. Emits an NFTSold event

Security Considerations:

  • Checks if the NFT is listed for sale

  • Ensures sufficient payment is sent for ETH transactions

  • Uses the transferFrom function for ERC-20 payments, requiring prior approval

delistNFT

function delistNFT(address nftContract, uint256 tokenId) external

Allows the seller to remove their NFT from the marketplace.

Parameters:

  • nftContract: Address of the NFT contract

  • tokenId: ID of the NFT

Functionality:

  1. Verifies that the caller is the seller

  2. Transfers the NFT back to the seller

  3. Deletes the listing

  4. Emits an NFTDelisted event

Security Considerations:

  • Only the original seller can delist the NFT

withdrawETH

function withdrawETH(uint256 amount) external onlyOwner

Allows the contract owner to withdraw ETH from the contract.

Parameters:

  • amount: Amount of ETH to withdraw

Security Considerations:

  • Only the contract owner can call this function

  • Checks if the contract has sufficient balance before withdrawal

withdrawERC20

function withdrawERC20(address token, uint256 amount) external onlyOwner

Allows the contract owner to withdraw ERC-20 tokens from the contract.

Parameters:

  • token: Address of the ERC-20 token

  • amount: Amount of tokens to withdraw

Security Considerations:

  • Only the contract owner can call this function

  • Assumes the contract has sufficient token balance

Events

The contract emits the following events:

  • NFTListed: When an NFT is listed for sale

  • NFTSold: When an NFT is sold

  • NFTDelisted: When an NFT is delisted

Usage Examples

Listing an NFT for sale

// Assume 'nftContract' is the address of your NFT contract
// and 'tokenId' is the ID of your NFT

// First, approve the marketplace contract to transfer your NFT
IERC721(nftContract).approve(marketplaceAddress, tokenId);

// Then, list the NFT for sale
marketplaceContract.listNFT(nftContract, tokenId, address(0), 1 ether);

Buying an NFT with ETH

// Assume 'nftContract' and 'tokenId' identify the NFT you want to buy

// Send ETH with the transaction
marketplaceContract.buyNFT{value: 1 ether}(nftContract, tokenId);

Buying an NFT with an ERC-20 token

// Assume 'nftContract' and 'tokenId' identify the NFT you want to buy
// and 'erc20TokenAddress' is the address of the payment token

// First, approve the marketplace contract to spend your tokens
IERC20(erc20TokenAddress).approve(marketplaceAddress, 1000);

// Then, buy the NFT
marketplaceContract.buyNFT(nftContract, tokenId);

Security Considerations

  1. Reentrancy: The contract uses the "checks-effects-interactions" pattern to prevent reentrancy attacks.

  2. Access Control: The Ownable contract is used to restrict access to sensitive functions.

  3. Input Validation: The contract checks for valid inputs and sufficient payments.

  4. Token Approvals: Users must approve the contract to transfer their NFTs and ERC-20 tokens before listing or buying.

  5. ETH Handling: The contract uses transfer for sending ETH, which has a gas stipend and can fail silently.

Gas Optimization

  1. The contract uses delete to remove listings, which can save gas by clearing storage.

  2. It uses _msgSender() instead of msg.sender, which is more flexible for potential future upgrades.

Best Practices

  1. The contract follows the Checks-Effects-Interactions pattern for better security.

  2. It uses OpenZeppelin's Ownable contract for standardized ownership management.

  3. The contract emits events for important state changes, allowing for off-chain tracking.

  4. It supports both ETH and ERC-20 token payments, increasing flexibility for users.

Potential Improvements

  1. Implement a fee system for the marketplace.

  2. Add functionality to update listing prices.

  3. Implement batch listing and buying functions for gas efficiency.

  4. Add a mechanism to pause the contract in case of emergencies.

Last updated