Розробка внутрішньоігрового маркетплейсу NFT

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Розробка внутрішньоігрового маркетплейсу NFT
Складний
~1-2 тижні
Часті запитання

Напрямки блокчейн-розробки

Етапи блокчейн-розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1285
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1198
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    902
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1121
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    589
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    858

Розробка внутриігрового маркетплейса NFT

Внутриігровой NFT маркетплейс відрізняється від загальних NFT маркетплейсів (OpenSea, Blur) спеціалізованим контентом, ігровим контекстом і вимогою глибокої інтеграції з ігровою механікою. Предмет відображається не просто як JPEG — показуються його атрибути, рівень, історія використання, сумісність з класом персонажа.

Архітектурні рішення

On-chain vs кастомний маркетплейс

Можна використовувати Seaport (OpenSea protocol) як базу — він production-ready, audited, підтримує batch orders, criteria-based orders (продажа будь-якого токена з колекції). Це економить 2-3 місяці розробки.

Кастомний маркетплейс потрібен коли: специфічні механіки (auction з in-game валютою, bundle deals «продати персонажа разом з інвентарем»), royalty розподіл між кількома стейкхолдерами, рейтингові листинги (не просто ціна, а price/power ratio).

In-game валюта vs ETH/USDC

Критичний вибір: приймати ЕТН чи тільки ігровий токен.

Тільки in-game валюта: створює sink для токена, утримує економіку всередину гри, простіше податково. Мінус: користувач повинен спочатку купити токен.

ETH/USDC: ширший ринок покупців, ліквідність. Мінус: «уход» вартості з ігрової економіки.

Hybrid: листинги в in-game токені, але кнопка «Купити за USDC» автоматично робить swap токена → USDC через DEX. UX seamless, обидва ринки довольні.

Смарт-контракт маркетплейса

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract GameNFTMarketplace {
    IERC20 public gameToken;
    IERC1155 public gameItems;
    
    uint256 public marketFeePercent = 250; // 2.5%
    uint256 public royaltyPercent = 500;   // 5% творцям
    address public treasury;
    address public developersWallet;
    
    struct Listing {
        address seller;
        uint256 itemTypeId;
        uint256 amount;
        uint256 pricePerUnit;       // в gameToken
        uint256 minimumPurchase;    // мінімальна покупка
        bool acceptsBundle;         // приймає bundle пропозиції
        uint256 expiresAt;
        ListingType listingType;
    }
    
    enum ListingType { FIXED_PRICE, ENGLISH_AUCTION, DUTCH_AUCTION }
    
    struct Auction {
        address seller;
        uint256 itemTypeId;
        uint256 tokenId;
        uint256 startPrice;
        uint256 currentBid;
        address currentBidder;
        uint256 endTime;
        uint256 minBidIncrement;
    }
    
    mapping(uint256 => Listing) public listings;
    mapping(uint256 => Auction) public auctions;
    
    // Fixed price покупка
    function buyItem(uint256 listingId, uint256 amount) external {
        Listing storage listing = listings[listingId];
        require(listing.seller != address(0), "Listing not found");
        require(block.timestamp <= listing.expiresAt, "Listing expired");
        require(amount >= listing.minimumPurchase, "Below minimum purchase");
        
        uint256 totalPrice = listing.pricePerUnit * amount;
        uint256 fee = (totalPrice * marketFeePercent) / 10000;
        uint256 royalty = (totalPrice * royaltyPercent) / 10000;
        uint256 sellerProceeds = totalPrice - fee - royalty;
        
        // Платежі
        gameToken.transferFrom(msg.sender, listing.seller, sellerProceeds);
        gameToken.transferFrom(msg.sender, treasury, fee);
        gameToken.transferFrom(msg.sender, developersWallet, royalty);
        
        // Передача предметів
        listing.amount -= amount;
        if (listing.amount == 0) delete listings[listingId];
        
        gameItems.safeTransferFrom(listing.seller, msg.sender, listing.itemTypeId, amount, "");
        
        emit ItemSold(listingId, msg.sender, amount, totalPrice);
    }
    
    // English auction
    function placeBid(uint256 auctionId, uint256 bidAmount) external {
        Auction storage auction = auctions[auctionId];
        require(block.timestamp < auction.endTime, "Auction ended");
        require(bidAmount >= auction.currentBid + auction.minBidIncrement, "Bid too low");
        
        // Повернення попередньому bidder
        if (auction.currentBidder != address(0)) {
            gameToken.transfer(auction.currentBidder, auction.currentBid);
        }
        
        // Новий bid в escrow
        gameToken.transferFrom(msg.sender, address(this), bidAmount);
        auction.currentBid = bidAmount;
        auction.currentBidder = msg.sender;
        
        // Anti-snipe: розширюємо якщо bid < 5 хв до кінця
        if (auction.endTime - block.timestamp < 5 minutes) {
            auction.endTime += 5 minutes;
        }
        
        emit BidPlaced(auctionId, msg.sender, bidAmount);
    }
    
    // Dutch auction: ціна зменшується зі часом
    function getDutchPrice(uint256 listingId) public view returns (uint256) {
        Listing storage listing = listings[listingId];
        // ... linearly interpolate price from startPrice to endPrice over duration
    }
}

Відображення атрибутів предметів

Специфіка ігрового маркетплейса — багатий контекст кожного NFT:

interface GameItemListing {
  tokenId: number;
  itemType: {
    id: number;
    name: string;
    rarity: "common" | "rare" | "epic" | "legendary";
    category: "weapon" | "armor" | "consumable" | "companion";
    imageUrl: string;
  };
  attributes: {
    level: number;
    damage?: number;
    defense?: number;
    speed?: number;
    durability: number;  // поточний стан
    upgradeCount: number;
    enchantments: string[];
  };
  gameContext: {
    compatibleClasses: string[];  // для яких класів персонажів підходить
    compatibleGames: string[];    // якщо NFT крос-ігровий
    requiredLevel: number;        // мінімальний рівень персонажа
    lastUsedInBattle?: Date;
    totalBattlesUsed: number;
  };
  listing: {
    price: bigint;
    currency: "GGD" | "USDC";
    seller: string;
    listedAt: Date;
    expiresAt: Date;
  };
  priceHistory: Array<{ price: bigint; date: Date }>;
  floorPrice: bigint;  // мінімальна ціна в цій категорії
  pricePower: number;  // price / power ratio vs floor
}

Поиск і фільтрація

interface MarketplaceFilters {
  itemCategory?: string[];
  rarities?: string[];
  minPrice?: bigint;
  maxPrice?: bigint;
  minLevel?: number;
  maxLevel?: number;
  compatibleClass?: string;
  hasEnchantment?: string;
  currency?: "GGD" | "USDC";
  sortBy?: "price_asc" | "price_desc" | "recently_listed" | "ending_soon" | "price_power";
}

// Elasticsearch або PostgreSQL з GIN індексами для JSONB атрибутів
async function searchListings(filters: MarketplaceFilters, page: number) {
  const query = db("listings")
    .where("status", "active")
    .where("expires_at", ">", new Date());
  
  if (filters.itemCategory?.length) {
    query.whereIn("item_category", filters.itemCategory);
  }
  
  if (filters.minLevel) {
    query.where("attributes->>'level'", ">=", filters.minLevel.toString());
  }
  
  if (filters.hasEnchantment) {
    query.whereRaw("attributes->'enchantments' @> ?", [JSON.stringify([filters.hasEnchantment])]);
  }
  
  return query
    .orderBy(getSortColumn(filters.sortBy))
    .limit(PAGE_SIZE)
    .offset(page * PAGE_SIZE);
}

Bundle deals

Ігровой контекст дозволяє реалізувати bundle продажи: «Продати персонажа разом з екіпіруванням»:

struct BundleListing {
    address seller;
    uint256[] itemTypeIds;
    uint256[] amounts;
    uint256 bundlePrice;       // скидка порівняно з сумою окремих
    bool requireAllItems;      // всі предмети або опціонально
}

NFT рентинг

Для дорогих предметів — механіка аренди: тримач NFT здає його в аренду, орендар використовує в грі й платить daily/weekly rent fee. Після терміну — предмет автоматично повертається.

// ERC-4907 стандарт для rentable NFT
interface IERC4907 {
    function setUser(uint256 tokenId, address user, uint64 expires) external;
    function userOf(uint256 tokenId) external view returns (address);
    function userExpires(uint256 tokenId) external view returns (uint256);
}

Сроки

  • Базовий маркетплейс (fixed price + simple auction): 4-6 тижнів
  • Розширений (dutch auction, bundle, rent): +3-4 тижні
  • Пошук і індексація (backend + Elasticsearch): +2-3 тижні
  • Frontend з rich item UI: 4-6 тижнів
  • Security audit: +3-4 тижні
  • Итого: 3-5 місяців