Реализация внутриигрового NFT-кошелька в мобильной GameFi-игре
В GameFi нет «просто показать NFT». Внутриигровой кошелёк — это транзакционный слой поверх игровой механики. Игрок экипирует меч — это on-chain вызов. Продаёт персонажа — это ERC-721 transfer. Получает награду — это минт токена. Каждое действие имеет gas, задержку подтверждения и возможность fail. Проектировать UX, не учитывая это — гарантированно получить негативные отзывы в App Store.
Архитектурные решения, которые определяют всё
Custodial vs Non-custodial
Это выбор, который нужно сделать в начале, потому что он влияет на всё.
Custodial (ключи хранятся на сервере): проще для пользователя — нет сид-фразы, нет Lost Access. Но требует KYC в ряде юрисдикций и доверие к серверу. Для мобильной игры с массовой аудиторией — часто предпочтительнее.
Non-custodial (ключи на устройстве): пользователь контролирует активы. Ключи в Android Keystore или iOS Secure Enclave. Сложнее онбординг — нужно объяснить сид-фразу человеку, который пришёл просто поиграть.
Embedded wallet (компромисс): библиотеки типа Privy, Magic, Dynamic генерируют кошелёк через email/social login, приватный ключ хранится в HSM и реконструируется через MPC. На мобильных используется через WebView или нативные SDK. Это де-факто стандарт для casual GameFi в 2024–2025.
L2 и газ
Основные сети для GameFi: Immutable X (StarkEx), Polygon, Ronin, Arbitrum, Base. Gas на mainnet Ethereum неприемлем для игровых микротранзакций. На Immutable X — gas-free для NFT-трансферов. На Polygon — <0.001 MATIC за транзакцию.
Локальное хранение NFT-данных
Метаданные NFT загружаем через tokenURI (ERC-721) или IPFS-гейт. Кэшируем локально — Room / Core Data. Изображения NFT — отдельный кэш через Glide (Android) / Kingfisher (iOS) с IPFS URL.
@Entity(tableName = "nft_items")
data class NftItem(
@PrimaryKey val tokenId: String,
val contractAddress: String,
val name: String,
val imageUrl: String,
val metadata: String, // JSON
val isEquipped: Boolean = false,
val cachedAt: Long = System.currentTimeMillis()
)
IPFS URLs типа ipfs://QmXxx... нужно транслировать через публичный или собственный gateway: https://ipfs.io/ipfs/QmXxx.... Собственный gateway — стабильнее для продакшена.
Транзакционный UX — самое сложное
Транзакция в блокчейне — не HTTP-запрос. Она отправляется, попадает в mempool, ждёт включения в блок (10 секунд на Polygon, мгновенно на Ronin). За это время пользователь продолжает играть. Нельзя блокировать игру на время ожидания подтверждения.
Паттерн: Optimistic UI + фоновый мониторинг транзакции.
// iOS — optimistic update
func equipItem(_ nft: NftItem) {
// Сразу обновляем UI
store.dispatch(EquipAction(tokenId: nft.tokenId))
// Фоновая транзакция
Task {
do {
let txHash = try await walletService.equip(nft)
await monitor(txHash: txHash, onFail: {
store.dispatch(UnequipAction(tokenId: nft.tokenId))
showError("Транзакция не прошла")
})
} catch {
store.dispatch(UnequipAction(tokenId: nft.tokenId))
}
}
}
Мониторинг транзакции — polling через WebSocket или JSON-RPC eth_getTransactionReceipt каждые 3–5 секунд. На Immutable X — через IMX REST API. На Polygon — через Alchemy или Infura WebSocket subscription.
Gas estimation и fee UI
Для non-custodial кошелька нужно показывать gas перед подтверждением. eth_estimateGas → умножить на gasPrice → конвертировать в USD через price feed (CoinGecko API или Chainlink oracle). Не показывать нативный токен без USD-эквивалента — пользователь не знает, сколько стоит 0.0023 MATIC.
Безопасность приватных ключей
Если non-custodial: приватный ключ только в Android Keystore или iOS Secure Enclave. Никогда в SharedPreferences, UserDefaults, или локальной БД в открытом виде. Подпись транзакции происходит внутри Keystore — ключ не покидает защищённое хранилище.
// Android — подпись через Keystore
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
val privateKey = keyStore.getKey(KEY_ALIAS, null) as PrivateKey
val signature = Signature.getInstance("SHA256withECDSA")
signature.initSign(privateKey)
signature.update(transactionHash)
val signedBytes = signature.sign()
NFT-инвентарь и фильтрация
Игровой инвентарь с 200+ NFT должен фильтроваться мгновенно — локально, не через API. Room запрос с фильтрами по типу, редкости, экипированности. LazyColumn (Jetpack Compose) или UICollectionView с DiffableDataSource — для плавного скролла.
Сортировка по rarity: атрибут rarity_score из метаданных NFT, считается через сумму весов редких трейтов. Логику подсчёта реализуем на стороне клиента или получаем с сервера.
App Store / Google Play и Web3
Apple принимает NFT-приложения, но запрещает покупку NFT через сторонние платёжные системы без комиссии App Store (30%). Допустимо: показывать NFT, transferить между кошельками, использовать в игре. Запрещено: продавать за фиатные деньги без In-App Purchase. Для marketplace-функциональности — только WebView с внешним сайтом.
Google Play с 2023 разрешил NFT-приложения явно, но те же правила по платёжным системам применяются к фиатным продажам.
Что входит в работу
- Выбор и интеграция типа кошелька (embedded / non-custodial)
- Смарт-контракт интеграция (ERC-721 / ERC-1155 на целевой L2)
- Локальный кэш NFT-метаданных и изображений
- Optimistic UI для игровых транзакций
- Фоновый мониторинг транзакций с rollback
- Безопасное хранение ключей (Keystore / Secure Enclave)
- Gas estimation и конвертация в USD
- NFT-инвентарь с фильтрацией и сортировкой по rarity
Сроки
Custodial кошелёк с просмотром NFT и базовыми трансферами: 1–2 недели. Full-featured non-custodial с игровой механикой, optimistic UI и marketplace WebView: 3–5 недель. Стоимость зависит от выбранной сети, объёма смарт-контракт интеграций и требований к безопасности.







