Розробка мобільного криптовалютного гаманця
Мобільний гаманець — це не просто «програма яка показує баланс». Це система управління ключами, інтерфейс до блокчейн-мереж та security-critical продукт, де помилка в key derivation або storage може коштувати користувачу всіх коштів. Розробка вимагає чіткого розуміння криптографії (BIP-32, BIP-39, BIP-44), особливостей мобільних Secure Enclave/Keystore та WalletConnect/EIP протоколів.
Повна картина від ключової ієрархії до signing flow, з акцентом на security-критичні деталі.
Управління ключами: HD Wallet архітектура
BIP-39: мнемонічна фраза
Відправна точка — 128-256 бітів випадковості, перетворена в 12-24 слова з BIP-39 словника (2048 слів). Слова — людино-читабельний бекап ключів. Критично: ентропія повинна бути згенерована криптографічно безпечним CSPRNG (iOS: SecRandomCopyBytes, Android: SecureRandom).
// iOS: генерація 128 бітів ентропії
var entropy = [UInt8](repeating: 0, count: 16)
let result = SecRandomCopyBytes(kSecRandomDefault, entropy.count, &entropy)
guard result == errSecSuccess else { throw WalletError.entropyGenerationFailed }
// Перетворення в мнемоніку через бібліотеку (WalletKit, BitcoinKit, TrustWalletCore)
let mnemonic = try Mnemonic.generate(from: entropy)
// ["abandon", "ability", "able", "about", "above", ...]
BIP-32/BIP-44: ієрархічна деривація ключів
З seed (отриманого з мнемоніки через PBKDF2) будується дерево ключів. BIP-44 задає стандартний шлях: m / purpose' / coin_type' / account' / change / address_index.
Приклади шляхів:
- Ethereum:
m/44'/60'/0'/0/0— перша ETH адреса - Bitcoin:
m/44'/0'/0'/0/0— перша BTC адреса - Solana:
m/44'/501'/0'/0'
import { HDKey } from '@scure/bip32';
import { mnemonicToSeedSync } from '@scure/bip39';
const seed = mnemonicToSeedSync(mnemonic);
const root = HDKey.fromMasterSeed(seed);
// Деривація першого ETH аккаунту
const ethKey = root.derive("m/44'/60'/0'/0/0");
const privateKey = ethKey.privateKey!; // Uint8Array
const address = computeAddress(privateKey); // ethers.js або viem
Важливо: ' означає hardened derivation — дочірні ключі не можна вичислити з батьківського публічного ключа. Рівні purpose, coin_type, account — завжди hardened. Рівні change та index — не hardened (дозволяє генерувати публічні ключі без приватного ключа для watch-only функції).
Secure Storage: ключи в пам'яті пристрою
Приватні ключі та seed ніколи не зберігаються в plaintext. Стратегії за рівнем безпеки:
iOS Keychain + Secure Enclave. iOS Keychain — зашифроване сховище системи. Для максимальної безпеки: ключ створюється прямо в Secure Enclave (окремий процесор, ключ ніколи не покидає чип). Операції підпису відбуваються всередині Enclave — програма отримує тільки результат підпису, але не приватний ключ.
Android Keystore. Аналог Keychain, ключі в Hardware-backed keystore (TEE). Підтримує EC ключі, включаючи secp256k1 (Android 9+). KeyStore.getInstance("AndroidKeyStore").
Додатковий рівень шифрування. Поверх OS keychain: seed шифрується через AES-256-GCM з ключем захищеним у Keychain. Double encryption додає захист при гіпотетичній компрометації Keychain API.
Transaction Signing Flow
Побудова транзакції
Для EVM мереж: nonce, to, value, data, gasLimit, maxFeePerGas, maxPriorityFeePerGas, chainId (формат EIP-1559). Формування відбувається в програмі, підпис — в безпечному контексті:
// viem: побудова та підпис транзакції
import { createWalletClient, http, parseEther } from 'viem';
import { mainnet } from 'viem/chains';
const transaction = {
to: recipientAddress,
value: parseEther('0.1'),
chainId: 1,
};
// Оцінка газу
const gasEstimate = await publicClient.estimateGas(transaction);
// Отримання поточних даних про комісії
const feeData = await publicClient.estimateFeesPerGas();
const signedTx = await walletClient.signTransaction({
...transaction,
gas: gasEstimate,
maxFeePerGas: feeData.maxFeePerGas,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
});
EIP-712 для підписування структурованих даних
Більшість DeFi взаємодій — не просто ETH передачі. approve, permit, signTypedData — всі це EIP-712 типовані підписи. Гаманець повинен:
- Парсити структуру EIP-712 з запиту dApp
- Відображати людино-читабельні дані користувачу (не raw hex)
- Після підтвердження — підписати та повернути signature
Декодування та відображення EIP-712 — нетривіальне завдання. ethers.js v6 та viem мають вбудовану підтримку. Для коректного відображення в UI потрібно рекурсивно розгортати типовані структури.
Інтеграція WalletConnect v2
WalletConnect v2 — протокол комунікації між dApp та гаманцем через зашифрований relay. Гаманець сканує QR-код або отримує deep link → встановлює зашифрований канал → отримує JSON-RPC запити від dApp.
import { Core } from '@walletconnect/core';
import { Web3Wallet } from '@walletconnect/web3wallet';
const core = new Core({ projectId: WC_PROJECT_ID });
const wallet = await Web3Wallet.init({ core, metadata: { name: 'MyWallet', ... } });
// Обробка session proposal (користувач сканує QR)
wallet.on('session_proposal', async (proposal) => {
const { id, params } = proposal;
// Показуємо користувачу що запрашує dApp
const approved = await showApprovalUI(params.proposer.metadata, params.requiredNamespaces);
if (approved) {
await wallet.approveSession({
id,
namespaces: {
eip155: {
accounts: [`eip155:1:${userAddress}`, `eip155:137:${userAddress}`],
methods: ['eth_sendTransaction', 'personal_sign', 'eth_signTypedData_v4'],
events: ['accountsChanged', 'chainChanged'],
},
},
});
}
});
// Обробка запитів на підпис
wallet.on('session_request', async (request) => {
const { topic, params } = request;
const { method, params: callParams } = params.request;
if (method === 'eth_sendTransaction') {
const tx = callParams[0];
const approved = await showTransactionUI(tx);
if (approved) {
const signedTx = await signTransaction(tx);
const txHash = await broadcastTransaction(signedTx);
await wallet.respondSessionRequest({ topic, response: { id: request.id, result: txHash } });
}
}
});
WalletConnect v2 підтримує мультиланцюг: одна сесія може керувати адресами на Ethereum, Polygon, Arbitrum одночасно.
Мультиланцюгова архітектура
Chain Registry та управління RPC
Сучасний гаманець підтримує 10-50+ мереж. Паттерн: chain registry з конфігурацією кожної мережі:
interface ChainConfig {
chainId: number;
name: string;
rpcUrls: string[]; // кілька для failover
fallbackRpcUrls: string[]; // публічні RPC як fallback
nativeCurrency: { symbol: string; decimals: number };
blockExplorer: string;
isTestnet: boolean;
}
// RPC з автоматичним failover
class RobustProvider {
private providers: JsonRpcProvider[];
private currentIndex = 0;
async call(method: string, params: any[]) {
for (let i = 0; i < this.providers.length; i++) {
try {
return await this.providers[this.currentIndex].send(method, params);
} catch (err) {
this.currentIndex = (this.currentIndex + 1) % this.providers.length;
}
}
throw new Error('All RPC endpoints failed');
}
}
Token Discovery та отримання балансів
Moralis API, Alchemy Token API, Ankr Advanced API — спеціалізовані endpoints для отримання всіх токенів та NFT адреси без ітерації всіма можливими ERC-20 контрактами. Набагато швидше ніж окремі balanceOf виклики.
Для production гаманця: комбінація Alchemy/Moralis для основних мереж + власний кеш балансів в SQLite (React Native з expo-sqlite або MMKV).
Безпека на рівні програми
Jailbreak/Root detection
На зламаному пристрої (jailbreak/root) Keychain/Keystore не гарантують захист — інші програми з root доступом можуть читати захищені дані. Визначення jailbreak:
iOS: перевірка наявності /Applications/Cydia.app, /private/var/lib/apt, здатності писати /private/test-jailbreak. Android: перевірка su бінарників, Magisk/SuperSU програм, наявності тестових ключів складання.
Реакція: блокування деяких функцій (seed backup), попередження користувача, опціонально — повна заборона (спірно: деякі гаманці дозволяють з попередженням).
Захист від запису екрану
Екрани з seed фразою та приватним ключем повинні бути недоступні для скриншотів та запису:
// iOS: захист від скриншотів для конкретного view
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Додавляємо secure text field як overlay — системний механізм захисту
let field = UITextField()
field.isSecureTextEntry = true
self.view.addSubview(field)
field.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
field.sendActions(for: .editingDidBegin)
}
Android: WindowManager.LayoutParams.FLAG_SECURE для активітів з чутливими даними.
Безпека Clipboard
Копіювання seed або приватного ключа в clipboard — ризик: інші програми можуть його читати. Попередження користувача, автоматичне очищення clipboard через 60 секунд:
await Clipboard.setStringAsync(seedPhrase);
setTimeout(async () => {
await Clipboard.setStringAsync(''); // очистити через 60 секунд
}, 60_000);
Стек розробки
React Native — кросс-платформена розробка iOS + Android з одного codebase. Expo спрощує нативні модулі. Але для крипто-операцій: нативні модулі критичні (React Native JavaScript bridge занадто повільний для крипто, особливо BIP-32 деривації).
TrustWallet Core — нативна бібліотека (C++), підтримує 60+ блокчейнів: HD Wallet, підпис, деривація адреси. Bindings для Swift, Kotlin, TypeScript. Позбавляє від необхідності самостійно реалізувати BIP-32/BIP-44 для кожного ланцюга.
| Компонент | Технологія | Складність |
|---|---|---|
| Управління ключами | TrustWallet Core + Keychain/Keystore | Висока |
| EVM підпис | viem / ethers.js v6 | Середня |
| WalletConnect v2 | @walletconnect/web3wallet | Середня |
| Мультиланцюгова підтримка | Chain registry + RPC failover | Середня |
| Token discovery | Alchemy/Moralis API | Низька |
| Secure storage | iOS Keychain / Android Keystore | Висока |
| Біометрична аутентифікація | expo-local-authentication | Низька |
| Відображення NFT | Alchemy NFT API + expo-image | Середня |
Графік. MVP з базовими функціями (створення/імпорт гаманця, ETH/ERC-20 відправка/отримання, WalletConnect) — 3-4 місяці. Повнофункціональний гаманець з мультиланцюгом, NFT, інтеграцією DeFi, security hardening — 6-9 місяців.
Обов'язкові етапи: security review перед публікацією в App Store/Google Play + penetration testing на реальних пристроях. Apple та Google перевіряють гаманці суворіше за звичайні програми — вимоги до privacy policy, документації управління ключами, відповідності місцевим фінансовим регуляціям.
Вартість розраховується після детальної специфікації цільових мереж та функціонального scope.







