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

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Разработка внутриигрового маркетплейса NFT
Сложная
~1-2 недели
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1258
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1170
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    873
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1092
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    563
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    830

Разработка внутриигрового маркетплейса 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 currency, bundle deals «продать персонажа вместе с инвентарём»), royalty распределение между несколькими стейкхолдерами, рейтинговые листинги (не просто цена, а price/power ratio).

In-game currency vs ETH/USDC

Критичный выбор: принимать ли ETH или только игровой токен.

Только in-game currency: создаёт sink для токена, удерживает экономику внутри игры, проще налогово. Минус: пользователь должен сначала купить токен.

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

Hybrid: листинги в in-game token, но кнопка «Купить за USDC» автоматически делает swap token → 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 purchase
    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 месяцев