Розробка мультиланцюгового гаманця
Мультиланцюговий гаманець — це не просто «підключити кілька мереж». Це комплексна система яка має коректно обробляти принципово різні блокчейн-архітектури: EVM-сумісні ланцюги (Ethereum, Arbitrum, Polygon, BSC), EVM-несумісні (Solana, Sui, Aptos), Bitcoin з UTXO-моделлю, Cosmos екосистему з IBC. Кожна має свою криптографію, формат транзакцій, fee-механізм та модель аккаунтів.
Ключовий вибір: деривація ключів
HD Wallet та BIP-44 стандарт
Все сучасні мультиланцюгові гаманці будуються на BIP-32/BIP-39/BIP-44 стандартах. Одна seed-фраза (12-24 слова) → один мастер ключ → дерево дочірніх ключів для кожної мережі.
BIP-44 шлях деривації: m / purpose' / coin_type' / account' / change / index
import { HDNodeWallet, Mnemonic } from 'ethers';
import { derivePath } from 'ed25519-hd-key';
import * as bip39 from 'bip39';
// Генерація seed phrase
const mnemonic = Mnemonic.fromEntropy(crypto.getRandomValues(new Uint8Array(16)));
const seed = await bip39.mnemonicToSeed(mnemonic.phrase);
// EVM ланцюги (Ethereum, Arbitrum, Polygon) — secp256k1, coin_type = 60
const evmWallet = HDNodeWallet.fromSeed(seed).derivePath("m/44'/60'/0'/0/0");
console.log('EVM address:', evmWallet.address);
// Solana — ed25519, coin_type = 501
const solanaPath = "m/44'/501'/0'/0'";
const { key: solanaPrivKey } = derivePath(solanaPath, seed.toString('hex'));
// ... створення Solana Keypair
Різні криві для різних ланцюгів
| Ланцюг | Крива | BIP-44 coin_type |
|---|---|---|
| Ethereum та EVM | secp256k1 | 60 |
| Bitcoin | secp256k1 | 0 |
| Solana | Ed25519 | 501 |
| Cosmos/ATOM | secp256k1 | 118 |
| Sui | Ed25519 | 784 |
| Near | Ed25519 | 397 |
Неможливо використовувати одну бібліотеку для всіх ланцюгів. EVM — ethers.js/viem, Solana — @solana/web3.js, Cosmos — cosmjs, Bitcoin — bitcoinjs-lib. Архітектура має це враховувати.
Архітектура гаманця
Chain Abstraction Layer
interface ChainAdapter {
chainId: string;
chainName: string;
getAddress(publicKey: Uint8Array): string;
getBalance(address: string): Promise<bigint>;
buildTransaction(params: TxParams): Promise<UnsignedTx>;
signTransaction(tx: UnsignedTx, privateKey: Uint8Array): Promise<SignedTx>;
broadcastTransaction(tx: SignedTx): Promise<string>;
getTransactionStatus(txHash: string): Promise<TxStatus>;
estimateFee(tx: UnsignedTx): Promise<FeeEstimate>;
}
// EVM Реалізація
class EVMAdapter implements ChainAdapter {
private client: PublicClient;
constructor(rpcUrl: string, public chainId: string, public chainName: string) {
this.client = createPublicClient({ transport: http(rpcUrl) });
}
async getBalance(address: string): Promise<bigint> {
return this.client.getBalance({ address: address as `0x${string}` });
}
async buildTransaction(params: TxParams): Promise<UnsignedTx> {
const nonce = await this.client.getTransactionCount({ address: params.from as `0x${string}` });
const feeData = await this.client.estimateFeesPerGas();
return {
to: params.to,
value: params.value ?? 0n,
data: params.data ?? '0x',
nonce,
maxFeePerGas: feeData.maxFeePerGas,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
chainId: BigInt(this.chainId),
};
}
}
Мультиланцюгове управління станом
Гаманець одночасно працює з 10+ мережами. Баланси, транзакції, pending операції — для кожної мережі незалежно. Погана реалізація: послідовні запити — повільно. Добра: паралельні запити з per-chain станом.
async function loadAllBalances(
adapters: ChainAdapter[],
addresses: Map<string, string>
): Promise<Map<string, bigint>> {
const results = await Promise.allSettled(
adapters.map(adapter => {
const address = addresses.get(adapter.chainId);
if (!address) return Promise.resolve([adapter.chainId, 0n] as [string, bigint]);
return adapter.getBalance(address)
.then(balance => [adapter.chainId, balance] as [string, bigint]);
})
);
const balances = new Map<string, bigint>();
for (const result of results) {
if (result.status === 'fulfilled') {
const [chainId, balance] = result.value;
balances.set(chainId, balance);
}
}
return balances;
}
Безпека: зберігання приватних ключів
Мобільний гаманець
iOS: Secure Enclave для генерації та зберігання ключів. Приватний ключ фізично ніколи не покидає чип. Підпис транзакцій відбувається всередині Enclave, результат — тільки підписана транзакція.
Обмеження: Secure Enclave підтримує тільки P-256 (secp256r1), не secp256k1 (Ethereum). Рішення: зберігати seed зашифрованим у iOS Keychain з біометрією, розшифровувати тільки для підпису в захищеній пам'яті.
Android: StrongBox (якщо підтримується залізом) або Android Keystore. Такі ж обмеження — HSM на Android теж не підтримує secp256k1 безпосередньо.
// iOS: зберігання seed у Secure Enclave-protected Keychain
func storeSeed(_ seed: Data) throws {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "wallet_seed",
kSecValueData as String: seed,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
]
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else { throw KeychainError.unhandledError(status) }
}
Web/Extension гаманець
Найбільш уразливе середовище. Головні ризики: шкідливі розширення отримують доступ до тієї ж сторінки, XSS в dApp може спробувати отримати доступ до extension API.
Поділ: background + content script + popup:
- Приватні ключі зберігаються тільки в background service worker
- Content script інжектується в сторінки (надає window.ethereum), немає доступу до ключів
- Усі операції з ключами — повідомлення до background через
chrome.runtime.sendMessage
Шифрування: seed шифрується з паролем користувача через scrypt/argon2 + AES-256-GCM. В пам'яті тільки коли розблокований, auto-очищується після N хвилин.
Fee Estimation в мультиланцюговому контексті
Кожна мережа має свою fee модель:
EVM з EIP-1559: baseFee + priorityFee. Протокол-встановлений baseFee, priorityFee — tip для validator. Монітор газ ціни в реальному часі, рекомендувати slow/average/fast.
async function getEVMFeeOptions(client: PublicClient): Promise<FeeOptions> {
const [block, feeHistory] = await Promise.all([
client.getBlock(),
client.getFeeHistory({ blockCount: 5, rewardPercentiles: [25, 50, 75] })
]);
const baseFee = block.baseFeePerGas ?? 0n;
return {
slow: { maxFeePerGas: baseFee * 110n / 100n, maxPriorityFeePerGas: ... },
average: { maxFeePerGas: baseFee * 120n / 100n, maxPriorityFeePerGas: ... },
fast: { maxFeePerGas: baseFee * 150n / 100n, maxPriorityFeePerGas: ... },
};
}
Solana: fee = lamports per signature × кількість підписів. Відносно стабільно. Врахувати priority fees (compute units) при високому навантаженню.
Bitcoin: fee = satoshis per virtual byte. Залежить від розміру UTXO набору (більше inputs → більше байтів → дорожче). Потрібен UTXO selection алгоритм.
Управління токенами та підтримка NFT
Token Discovery
Користувачі не повинні вручну додавати кожен токен. Автоматичне виявлення:
EVM: Etherscan/Covalent Token API або сканування Transfer подій на адресу користувача. TokenList стандарт (Uniswap) для whitelisted токенів.
Solana: getParsedTokenAccountsByOwner — повертає всі SPL токени в одному RPC викликі.
Підтримка NFT
ERC-721 та ERC-1155 на EVM: Alchemy NFT API або Moralis для агрегації метаданих. IPFS завантаження з fallback (багато NFT мають зламані IPFS посилання).
async function getUserNFTs(address: string, chainId: number) {
const alchemy = new Alchemy({
apiKey: process.env.ALCHEMY_KEY,
network: chainIdToNetwork(chainId),
});
const nfts = await alchemy.nft.getNftsForOwner(address);
return nfts.ownedNfts.map(nft => ({
contractAddress: nft.contract.address,
tokenId: nft.tokenId,
name: nft.name,
imageUrl: nft.image.cachedUrl,
collection: nft.contract.name,
}));
}
WalletConnect та інтеграція dApp
WalletConnect v2 — стандартний протокол для підключення гаманця до dApp. Sign API для підпису, Auth API для аутентифікації.
import { Web3Wallet } from '@walletconnect/web3wallet';
const web3wallet = await Web3Wallet.init({
core: new Core({ projectId: process.env.WALLETCONNECT_PROJECT_ID }),
metadata: {
name: 'My Wallet',
description: 'Multichain Wallet',
url: 'https://mywallet.io',
icons: ['https://mywallet.io/icon.png'],
},
});
web3wallet.on('session_request', async (event) => {
const { topic, params } = event;
const { request } = params;
if (request.method === 'eth_sendTransaction') {
const confirmed = await showTransactionConfirmation(request.params[0]);
if (confirmed) {
const signedTx = await signEVMTransaction(request.params[0]);
await web3wallet.respondSessionRequest({
topic,
response: { id: event.id, jsonrpc: '2.0', result: signedTx }
});
}
}
});
Стек та графік розробки
| Компонент | Технології | Графік |
|---|---|---|
| Core HD wallet | bip39 + ethers.js + @solana/web3.js | 2-3 тижні |
| EVM мультиланцюг | viem, 10+ мереж | 2-3 тижні |
| Solana інтеграція | @solana/web3.js + Metaplex | 2 тижні |
| Мобільне (RN) | React Native + Expo SecureStore | 4-6 тижнів |
| Розширення (Chrome) | MV3 + chrome.storage | 3-4 тижні |
| WalletConnect v2 | @walletconnect/web3wallet | 1-2 тижні |
| NFT + token discovery | Alchemy/Moralis API | 2-3 тижні |
| UI/UX (повна) | React Native / React | 6-10 тижнів |
Мінімальний production-ready мультиланцюговий гаманець (EVM + Solana, mobile-first) — 4-5 місяців. Додавання Bitcoin (UTXO модель) — ще 4-6 тижнів окремо через принципово іншу архітектуру. Розширення на Cosmos — 3-4 тижні через cosmjs.
Безпека вимагає зовнішнього аудиту перед публічним релізом — гаманець зберігає ключи користувачів безпосередньо, максимальний ризик.







