Реалізація внутриігрового NFT-кошелька в мобільній GameFi-грі
У GameFi нема «просто показати NFT». Внутриігровий кошелек — це транзакційний шар поверх ігрової механіки. Гравець екипірує меч — це on-chain виклик. Продає персонажа — це ERC-721 transfer. Отримує нагороду — це мінт токена. Кожна дія має газ, затримку підтвердження та ризик 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 — газ-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 — оптимістичне оновлення
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 кошелька потрібно показувати газ перед підтвердженням. 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 тижнів. Вартість залежить від обраної мережі, обсягу інтеграцій смарт-контрактів та вимог до безпеки.







