AI-NFT Marketplace

Mongo DB

Express.js

React.js

Node.js

Paypal Checkout

Solidity

Next.js

Viem

AI

Wagmi

Reown

Tailwind

HTML5

CSS3

TypeScript


AI-NFT Marketplace (WIP)

A revolutionary NFT marketplace that combines artificial intelligence with blockchain technology to create a unique digital asset ecosystem. Users can generate stunning artwork through AI-powered image generation, mint them as NFTs, and trade them in a secure marketplace. The platform features a native token for transactions and rewards, plus an engaging game that lets players earn while they create and collect.

The marketplace stands out by offering an intuitive interface for AI image generation, allowing artists and collectors to create unique digital assets from text prompts. Each generated image can be instantly minted as an NFT, with full ownership rights and the ability to trade or sell in the marketplace.

Platform Features:

NFT Marketplace Features

The platform's core features include an advanced AGENTIC AI image generator that transforms text descriptions into unique artwork, a secure marketplace for trading NFTs, and a play-to-earn game that rewards users with the platform's native token. The marketplace implements smart contracts for secure transactions and ownership verification, while the gaming component adds an engaging layer of interaction and potential earnings. Users can stake their NFTs, participate in community events, and earn rewards through various platform activities. The native token serves as both a utility token for platform features and a governance token for community decisions, creating a comprehensive ecosystem for digital asset creation and trading.

Core Contract:

// SPDX-License-Identifier: MIT
/\*
- ███╗   ██╗ ██████╗ ██╗███████╗████████╗     █████╗  ██████╗ ████████╗
- ████╗  ██║██╔════╝ ██║██╔════╝╚══██╔══╝     ██╔══██╗██╔══██╗╚══██╔══╝
- ██╔██╗ ██║██║  ███╗██║█████╗     ██║        ███████║██████╔╝   ██║
- ██║╚██╗██║██║   ██║██║██╔══╝     ██║        ██╔══██║██╔══██╗   ██║
- ██║ ╚████║╚██████╔╝██║██║        ██║  ██║   ██║  ██║██║  ██║   ██║
- ╚═╝  ╚═══╝ ╚═════╝ ╚═╝╚═╝        ╚═╝  ╚═╝   ╚═╝  ╚═╝╚═╝  ╚═╝   ╚═╝
\*/
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/common/ERC2981.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";

contract NgiFTArt is ERC721, ERC721URIStorage, Ownable, ERC2981, EIP712 {
// ================= STATE VARIABLES =================
using ECDSA for bytes32;
uint256 private \_tokenIdCounter;
address public platformAddress;
uint256 public constant ROYALTY_PERCENTAGE = 3; // 3% royalty fee
IERC20 public immutable usdtToken; // Immutable for gas optimization

    // NFT data structure
    struct NFTData {
        uint256 intrinsicValueUSDT;
        uint256 salePriceUSDT;
        bool isListed;
        bool isFirstTransfer;
    }

    // Mappings for NFT data and transfer tracking
    mapping(uint256 => NFTData) public nftData;

    // ========== CUSTOM ERRORS ==========
    error PaymentFailed();
    error InsufficientAllowance();
    error RoyaltyTransferFailed();
    error SellerPaymentFailed();

    // ================= EVENTS =================
    event NFTMinted(address indexed owner, uint256 tokenId, string tokenURI);
    event USDTWithdrawn(uint256 tokenId, address indexed owner, uint256 amount);
    event NFTListed(uint256 tokenId, uint256 salePrice);
    event NFTUnlisted(uint256 tokenId);
    event NFTSold(uint256 tokenId, address buyer, address seller, uint256 price);
    event NFTTransferred(uint256 tokenId, address from, address to);

    // ================= CONSTRUCTOR =================
    constructor(address _usdtToken) ERC721("NgiFT Art", "NGIFT") Ownable(msg.sender) EIP712("NgiFTArt", "1") {
        platformAddress = msg.sender;
        usdtToken = IERC20(_usdtToken);
        _setDefaultRoyalty(platformAddress, uint96(ROYALTY_PERCENTAGE * 100));
    }

    // ================= CORE FUNCTIONS =================
    /// @notice Mints a new NFT and transfers it to specified address
    function mintNFT(address to, string memory uri) external onlyOwner {
        uint256 newTokenId = _tokenIdCounter++;
        _safeMint(platformAddress, newTokenId);
        _setTokenURI(newTokenId, uri);
        _transfer(platformAddress, to, newTokenId);
        emit NFTMinted(to, newTokenId, uri);
    }

    // ================= SALES MANAGEMENT =================
    /// @notice Lists an NFT for sale with specified price
    function listForSale(uint256 tokenId, uint256 salePriceUSDT) external {
        require(ownerOf(tokenId) == msg.sender, "Not NFT owner");
        require(!nftData[tokenId].isListed, "Already listed");
        require(salePriceUSDT > 0, "Invalid price");

        nftData[tokenId].salePriceUSDT = salePriceUSDT;
        nftData[tokenId].isListed = true;
        emit NFTListed(tokenId, salePriceUSDT);
    }

    /// @notice Unlists an NFT from sale
    function unlistNFT(uint256 tokenId) external {
        require(ownerOf(tokenId) == msg.sender, "Not owner");
        require(nftData[tokenId].isListed, "Not listed");

        nftData[tokenId].isListed = false;
        nftData[tokenId].salePriceUSDT = 0;
        emit NFTUnlisted(tokenId);
    }

    /// @notice Completes NFT purchase and distributes funds
      function buyNFT(uint256 tokenId) external {
        require(nftData[tokenId].isListed, "NFT not listed for sale");
        address seller = ownerOf(tokenId);
        require(seller != msg.sender, "Owner cannot buy their own NFT");
        uint256 price = nftData[tokenId].salePriceUSDT;

        // Verify allowance before de transfer
        if (usdtToken.allowance(msg.sender, address(this)) < price) {
            revert InsufficientAllowance();
        }

        // Transfer USDT from buyer to contract
        if (!usdtToken.transferFrom(msg.sender, address(this), price)) {
          revert PaymentFailed();
        }

        // Transfer royalty
        uint256 royalty = (price * ROYALTY_PERCENTAGE) / 100;
        if (!usdtToken.transfer(platformAddress, royalty)) {
          revert RoyaltyTransferFailed();
        }

        // Transfer payment to seller
        if (!usdtToken.transfer(seller, price - royalty)) {
          revert SellerPaymentFailed();
        }

        // Update state and transfer NFT
        nftData[tokenId].isListed = false;
        delete nftData[tokenId].salePriceUSDT;
        _transfer(seller, msg.sender, tokenId);
        emit NFTSold(tokenId, msg.sender, seller, price);
    }

    // ================= USDT MANAGEMENT =================
    /// @notice Adds USDT to NFT's intrinsic value
    function addUSDT(uint256 tokenId, uint256 amount) external {
        require(ownerOf(tokenId) == msg.sender, "Not owner");
        require(!nftData[tokenId].isListed, "Listed NFTs locked");
        require(usdtToken.transferFrom(msg.sender, address(this), amount), "Transfer failed");
        nftData[tokenId].intrinsicValueUSDT += amount;
    }

    /// @notice Withdraws USDT from NFT's intrinsic value
    function withdrawUSDT(uint256 tokenId) external {
        require(ownerOf(tokenId) == msg.sender, "Not owner");
        require(!nftData[tokenId].isListed, "Listed NFTs locked");
        require(nftData[tokenId].intrinsicValueUSDT > 0, "No balance");

        uint256 amount = nftData[tokenId].intrinsicValueUSDT;
        delete nftData[tokenId].intrinsicValueUSDT;

        require(usdtToken.transfer(msg.sender, amount), "Withdrawal failed");
        emit USDTWithdrawn(tokenId, msg.sender, amount);
    }

    // ================= TRANSFERING NFT =================

    /// @notice First transfer (Platform pays gas fee)
    function transferFirstTimeWithSignature(
       address to,
       uint256 tokenId,
       bytes memory signature
    ) internal {

      // Build hash with EIP712
      bytes32 structHash = keccak256(abi.encode(
          keccak256("Transfer(address from,address to,uint256 tokenId)"),
          ownerOf(tokenId),
          to,
          tokenId
      ));
      bytes32 digest = _hashTypedDataV4(structHash); // Generarte sign hash

      // Recover signer address
      address signer = ECDSA.recover(digest, signature);
      require(signer == ownerOf(tokenId), "Invalid signature");

      // Platform makes transfer
      _transfer(ownerOf(tokenId), to, tokenId);

      // Mark first nft transfer as done
      nftData[tokenId].isFirstTransfer = true;

      emit NFTTransferred(tokenId, ownerOf(tokenId), to);

}

     /// @notice Normal NFT transfer (User pays gas fee)
    function transferNFT(
        address to,
        uint256 tokenId,
        bytes memory signature
    ) external {
        require(!nftData[tokenId].isListed, "NFT is listed, cannot transfer.");

        if (!nftData[tokenId].isFirstTransfer) {
            require(msg.sender == platformAddress, "Not authorized to make the first transfer.");
            transferFirstTimeWithSignature(to, tokenId, signature);
        } else {
            require(ownerOf(tokenId) == msg.sender, "Not authorized, only owner can execute transfer.");
            _transfer(msg.sender, to, tokenId);
        }
    }

// ================= PROPER OVERRIDES FOR OZ 5.2.0 =================

    /// @notice Required balance tracking override
    function _increaseBalance(address account, uint128 amount) internal override(ERC721) {
        super._increaseBalance(account, amount);
    }

    /// @notice Returns token URI with proper overrides
    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

    /// @notice ERC165 interface support check
    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721URIStorage, ERC2981)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }

}