Реализация маркетплейса внутриигровых предметов в мобильной GameFi-игре
NFT-маркетплейс внутри мобильной игры — это одна из самых сложных задач в GameFi-разработке. Здесь сталкиваются два мира: привычный мобильный UX (быстро, интуитивно, без seed-фраз) и блокчейн-реальность (gas fees, время подтверждения транзакций, ownership через кошелёк). Если не решить это противоречие — маркетплейс просто не будут использовать.
Архитектура: on-chain vs off-chain listing
Полностью on-chain маркетплейс (как OpenSea v1) — каждый листинг это транзакция. На мобильных устройствах это нецелесообразно: пользователь не будет платить $0.50–5 газа за каждое выставление предмета на продажу.
Правильная архитектура для GameFi-мобайла — off-chain листинги с on-chain расчётами:
- Продавец подписывает листинг оффчейн (
signTypedData/ EIP-712) — транзакции нет, газа нет - Листинг хранится в базе данных приложения
- Покупатель нажимает «Купить» — только в этот момент происходит on-chain транзакция
- Смарт-контракт верифицирует подпись продавца, переводит NFT покупателю и оплату продавцу атомарно
// Минимальная структура листинга EIP-712
struct Listing {
address seller;
address nftContract;
uint256 tokenId;
address paymentToken; // USDC, GOLD или нативный токен
uint256 price;
uint256 expiry; // срок действия листинга
uint256 nonce; // защита от replay attack
}
bytes32 public constant LISTING_TYPEHASH = keccak256(
"Listing(address seller,address nftContract,uint256 tokenId,address paymentToken,uint256 price,uint256 expiry,uint256 nonce)"
);
function buyItem(Listing calldata listing, bytes calldata signature) external {
require(block.timestamp < listing.expiry, "Listing expired");
require(!usedNonces[listing.seller][listing.nonce], "Nonce used");
bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(LISTING_TYPEHASH, listing)));
require(ECDSA.recover(digest, signature) == listing.seller, "Invalid signature");
usedNonces[listing.seller][listing.nonce] = true;
IERC20(listing.paymentToken).transferFrom(msg.sender, listing.seller, listing.price);
IERC721(listing.nftContract).safeTransferFrom(listing.seller, msg.sender, listing.tokenId);
}
Этот подход используют Seaport (OpenSea v2) и Blur — проверенная схема.
Мобильный UX: отображение предметов
Каждый NFT-предмет — это tokenId + метаданные с tokenURI на IPFS или centralised CDN. Загрузка метаданных напрямую с IPFS на мобильном — медленно и нестабильно. Решение: бэкенд индексирует метаданные в базу и отдаёт через REST API. Мобильный клиент никогда не обращается к IPFS напрямую.
Изображения предметов — через CDN (CloudFront / Cloudflare), с кэшированием на клиенте через Kingfisher (iOS) или Coil (Android). Lazy loading в списке — LazyColumn с AsyncImage в Compose, LazyVGrid в SwiftUI.
// SwiftUI: грид предметов маркетплейса
struct MarketplaceGridView: View {
@StateObject var viewModel: MarketplaceViewModel
let columns = [GridItem(.adaptive(minimum: 160), spacing: 12)]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 12) {
ForEach(viewModel.items) { item in
NFTItemCard(item: item)
.onAppear {
if item == viewModel.items.last {
viewModel.loadNextPage()
}
}
}
}
.padding()
}
}
}
Фильтрация и поиск
Фильтры: редкость (Rare/Epic/Legendary), тип предмета (оружие/броня/питомец), ценовой диапазон, актив оплаты. Сортировка: по цене, по дате листинга, по редкости. Полнотекстовый поиск по имени предмета — через PostgreSQL tsvector или Elasticsearch на бэкенде.
Атрибуты NFT-предметов индексируются в реляционную таблицу при минте — не парсятся из IPFS JSON при каждом запросе.
Процесс покупки на мобильном
Главная проблема — как пользователь подписывает транзакцию покупки без seed-фразы в приложении.
Вариант 1: Account Abstraction. Smart account пользователя подписывает UserOperation через биометрию (Face ID / Touch ID). Paymaster спонсирует газ — пользователь платит только в игровом токене. Лучший UX, сложнее в реализации.
Вариант 2: WalletConnect. Deep Link на MetaMask или Phantom, пользователь подтверждает транзакцию во внешнем кошельке и возвращается в игру. Знакомо крипто-пользователям, непривычно для гейминг-аудитории.
Вариант 3: Кастодиальный кошелёк. Приложение управляет ключами (через Fireblocks MPC или Privy). Транзакции подписываются server-side или через Shamir's Secret Sharing. Самый простой UX, жёсткие требования к безопасности.
Для GameFi с миллионной аудиторией — Account Abstraction через Privy или Dynamic с embedded wallet. Пользователь входит через Google/Apple — получает smart account автоматически.
Роялти для разработчика игры
При каждой P2P-продаже на вторичном рынке — автоматическое отчисление роялти (2–5%) в кошелёк студии. ERC-2981 (NFT Royalty Standard) задаёт royaltyInfo(tokenId, salePrice) — смарт-контракт маркетплейса вызывает это перед расчётом и удерживает комиссию.
Модерация и блокировка предметов
Система жалоб на подозрительные листинги (чит-предметы, дубликаты). Административный endpoint POST /listings/{id}/delist — удаляет листинг из базы. On-chain ничего не меняется (транзакции не было), предмет просто перестаёт отображаться.
Блокировка краденых токенов (если аккаунт взломан) — blacklist на уровне смарт-контракта: blockedTokens[tokenId] = true с проверкой в buyItem.
Этапы и сроки
| Этап | Срок |
|---|---|
| Смарт-контракт маркетплейса + аудит | 2 недели |
| Бэкенд: индексирование, листинги, поиск | 2 недели |
| Мобильный клиент: грид, карточка, покупка | 2–3 недели |
| Кошелёк (WalletConnect или Account Abstraction) | 1 неделя |
| Фильтры, сортировка, история сделок | 1 неделя |
Итого: 8–10 недель. Стоимость рассчитывается индивидуально после анализа требований.







