Розробка веб-гаманця

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Розробка веб-гаманця
Середній
~1-2 тижні
Часті запитання

Напрямки блокчейн-розробки

Етапи блокчейн-розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1306
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1218
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    920
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1147
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    610
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    885

Розробка веб-криптовалютного гаманця

Криптовалютний гаманець — це не місце для зберігання монет. Монети зберігаються on-chain, гаманець зберігає приватний ключ для їх підпису. Це принципова відмінність від банківського рахунку і визначає всю архітектуру: де і як зберігати ключ, як підписувати транзакції, як захистити від компрометації.

Веб-гаманець — найбільш доступний варіант (не потрібно встановлювати розширення або програму), але також найбільш атакуваний: браузерне середовище, XSS загрози, атаки ланцюга поставок на JavaScript залежності. MetaMask, Coinbase Wallet, Rainbow — розширення браузера, а не чистий веб. Чистий веб-гаманець вимагає додаткових заходів безпеки.

Типи гаманців та архітектурний вибір

EOA vs Smart Account

EOA (Externally Owned Account) — класичний гаманець. Один приватний ключ → одна адреса. Просто, сумісно зі всім. Проблема: втрата ключа = постійна втрата доступу. Немає соціального відновлення, мультифакторної аутентифікації, обмежень витрат.

Smart Account (ERC-4337 Account Abstraction) — смарт-контракт гаманець. Логіка валідації транзакцій програмована. Можливості: соціальне відновлення (відновлення через довірені контакти), session keys (обмежені ключі для dApp без повного доступу), пакетні транзакції (кілька операцій в одному викликі), спонсорство газу (paymaster платить газ за користувача), верифікація підпису будь-яким алгоритмом (не тільки ECDSA secp256k1).

Для потребительського веб-гаманця у 2024-2025 — рекомендую ERC-4337. Учіть користувачів методам відновлення зрозумілим звичайній людині замість seed phrases.

Зберігання ключів у браузері

Ключове питання безпеки. Варіанти від найменш до найбільш безпечного:

localStorage / sessionStorage — ніколи не використовуйте для приватних ключів. Доступний будь-якому JavaScript на сторінці, будь-якому XSS.

IndexedDB з шифруванням — приватний ключ шифрується через Web Crypto API (AES-GCM 256-bit) з ключем виведеним з пароля користувача (PBKDF2 або Argon2 з високим cost factor). Зашифрований blob зберігається в IndexedDB. Це стандарт для браузерних гаманців.

async function encryptPrivateKey(
  privateKey: Uint8Array,
  password: string
): Promise<{ encrypted: ArrayBuffer; salt: Uint8Array; iv: Uint8Array }> {
  const salt = crypto.getRandomValues(new Uint8Array(32));
  const iv = crypto.getRandomValues(new Uint8Array(12)); // 96-bit для GCM
  
  // Дериворуємо ключ шифрування з пароля
  const keyMaterial = await crypto.subtle.importKey(
    "raw",
    new TextEncoder().encode(password),
    "PBKDF2",
    false,
    ["deriveKey"]
  );
  
  const encryptionKey = await crypto.subtle.deriveKey(
    {
      name: "PBKDF2",
      salt,
      iterations: 600000, // OWASP рекомендує 600k для SHA-256
      hash: "SHA-256",
    },
    keyMaterial,
    { name: "AES-GCM", length: 256 },
    false,
    ["encrypt"]
  );
  
  const encrypted = await crypto.subtle.encrypt(
    { name: "AES-GCM", iv },
    encryptionKey,
    privateKey
  );
  
  return { encrypted, salt, iv };
}

WebAuthn + апаратний ключ — найбільш безпечний варіант. Приватний ключ гаманця ніколи не покидає WebAuthn authenticator (платформеничний ключ в TPM/Secure Enclave, або апаратний ключ типу YubiKey). Операція підпису відбувається всередині пристрою.

Реалізація через Passkey: користувач створює passkey (WebAuthn credential), приватний ключ генерується в Secure Enclave iPhone/Android/Windows TPM. Для ERC-4337 гаманця — смарт-контракт може верифікувати P-256 (secp256r1) підписи WebAuthn. Daimo та Coinbase Smart Wallet використовують саме цей підхід.

MPC гаманці

Multi-Party Computation: приватний ключ ніколи не існує повністю в одному місці. Розділений на shares між кількома сторонами (користувач + сервер, або користувач + пристрій 1 + пристрій 2). Підпис вимагає threshold учасників, але жоден не знає повного ключа.

Постачальники MPC-as-a-service: Privy, Dynamic, Web3Auth, Fireblocks Web3. Для non-custodial MPC — користувач має один share, сервіс інший. Компрометація сервісу не означає компрометацію ключа.

Мінус MPC: підпис більш повільний (multi-round протокол) і вимагає доступності обох учасників. Якщо сервіс недоступний — не можна підписати.

Key Derivation та HD гаманці

BIP-39 та BIP-44

Стандартний Web3 гаманець використовує seed phrase (мнемоніку) — 12 або 24 слова з BIP-39 словника. З seed через PBKDF2 (2048 ітерацій) виводиться master приватний ключ. З master ключа через BIP-32 derivation виводяться дочірні ключі по шляху.

BIP-44 визначає стандартний derivation path: m/purpose'/coin_type'/account'/change/address_index.

Для Ethereum: m/44'/60'/0'/0/0 — перший аккаунт. m/44'/60'/0'/0/1 — другий. Це дозволяє одній seed phrase керувати нескінченною кількістю адрес детерміністично. Імпортуйте seed в MetaMask, Rainbow, Ledger — отримайте ті ж адреси.

import { mnemonicToSeed } from "@scure/bip39";
import { HDKey } from "@scure/bip32";

async function deriveAccount(mnemonic: string, index: number) {
  const seed = await mnemonicToSeed(mnemonic);
  const masterKey = HDKey.fromMasterSeed(seed);
  
  const derivationPath = `m/44'/60'/0'/0/${index}`;
  const childKey = masterKey.derive(derivationPath);
  
  return {
    privateKey: childKey.privateKey!,
    address: computeAddress(childKey.publicKey!),
  };
}

Бібліотеки: @scure/bip39 та @scure/bip32 — сучасні, аудовані, tree-shakeable замінники noble-secp256k1 та bip39.

Архітектура веб-гаманця

Поділ відповідальності: UI vs Signing

Критичне правило: код, який має доступ до приватного ключа, повинен бути ізольований від коду, який взаємодіє з dApps та інтернетом. XSS в UI層не повинен компрометувати ключ.

Реалізація через Web Worker або Service Worker: операції підпису відбуваються в ізольованому worker, UI層не має прямого доступу до ключа. Worker отримує запит на підпис, показує користувачу що підписується, отримує підтвердження, повертає підпис (не ключ).

// Main thread — UI
async function signTransaction(txRequest: TransactionRequest): Promise<string> {
  return new Promise((resolve, reject) => {
    const worker = new Worker("/signing-worker.js");
    
    worker.postMessage({ type: "SIGN_TX", payload: txRequest });
    
    worker.onmessage = (e) => {
      if (e.data.type === "SIGNED") resolve(e.data.signature);
      if (e.data.type === "REJECTED") reject(new Error("User rejected"));
      if (e.data.type === "ERROR") reject(new Error(e.data.error));
    };
  });
}

// signing-worker.js — ізольований worker з доступом до ключа
self.onmessage = async (e) => {
  if (e.data.type === "SIGN_TX") {
    const approved = await requestUserApproval(e.data.payload);
    if (!approved) {
      self.postMessage({ type: "REJECTED" });
      return;
    }
    const signature = await signWithStoredKey(e.data.payload);
    self.postMessage({ type: "SIGNED", signature });
  }
};

WalletConnect та інтеграція dApp

WalletConnect v2 — стандарт для підключення гаманця до dApp. Працює через relay сервер: dApp створює паринг QR-код або deep link, гаманець сканує, встановлюється зашифрована сесія. Всі запити (eth_signTypedData, eth_sendTransaction) йдуть через цей канал.

Для веб-гаманця: @walletconnect/web3wallet SDK. Гаманець виступає як "wallet" (не "dapp") в WalletConnect терміології.

import { Web3Wallet } from "@walletconnect/web3wallet";
import { Core } from "@walletconnect/core";

const core = new Core({ projectId: WALLETCONNECT_PROJECT_ID });
const web3wallet = await Web3Wallet.init({
  core,
  metadata: {
    name: "My Wallet",
    description: "Custom Web3 Wallet",
    url: "https://mywallet.app",
    icons: ["https://mywallet.app/icon.png"],
  },
});

// Обробка вхідних запитів від dApps
web3wallet.on("session_request", async (event) => {
  const { id, topic, params } = event;
  const { request } = params;
  
  if (request.method === "eth_sendTransaction") {
    const approved = await showTransactionConfirmation(request.params[0]);
    if (approved) {
      const txHash = await sendTransaction(request.params[0]);
      await web3wallet.respondSessionRequest({
        topic,
        response: { id, result: txHash, jsonrpc: "2.0" },
      });
    } else {
      await web3wallet.respondSessionRequest({
        topic,
        response: { id, error: { code: 4001, message: "User rejected" }, jsonrpc: "2.0" },
      });
    }
  }
});

EIP-1193 Provider

Для прямої інтеграції dApp через window.ethereum: гаманець інжектує EIP-1193 сумісний provider в DOM. Це те, що робить MetaMask. Для веб-гаманця в tab (не розширення) — обмежено тим же origin, не ідеально.

Альтернатива: Service Worker як фоновий процес (Progressive Web App), який може інжектувати provider та обробляти запити від інших tabs того ж origin.

Реалізація ERC-4337 Account Abstraction

UserOperation flow

Замість звичайної Ethereum транзакції, ERC-4337 вводить UserOperation — структуру даних, яку підписує гаманець та відправляє Bundler (не безпосередньо в mempool):

interface UserOperation {
  sender: string;          // адреса smart account
  nonce: bigint;
  initCode: Hex;           // для створення нового аккаунту
  callData: Hex;           // що повинен виконати аккаунт
  callGasLimit: bigint;
  verificationGasLimit: bigint;
  preVerificationGas: bigint;
  maxFeePerGas: bigint;
  maxPriorityFeePerGas: bigint;
  paymasterAndData: Hex;   // paymaster для спонсорства газу
  signature: Hex;          // підпис власника
}

Bundler збирає UserOperations, упаковує в batch, відправляє через EntryPoint контракт. EntryPoint валідує підписи та виконує операції.

Гаманець взаємодіє з Bundler через JSON-RPC API (ERC-4337 специфічні методи: eth_sendUserOperation, eth_getUserOperationByHash). Постачальники bundler: Pimlico, Alchemy, Biconomy.

Session Keys

Session key — обмежений ключ без повного доступу до аккаунту. Користувач створює session key для конкретного dApp на конкретний період. dApp використовує цей ключ для підпису операцій — користувачу не потрібно підтверджувати кожну транзакцію.

Для GameFi: користувач створює session key на 8 годин ігрової сесії. Key може тільки викликати game-specific контракти, не може переводити ETH або ERC20. Після 8 годин — автоматично expires.

Реалізація: через SmartAccount validator module (ZeroDev Kernel, Safe modules). Session key policy зберігається в смарт-контракті.

Безпека

Загрози та мітигація

XSS атаки. Content Security Policy (сувора: script-src 'self' 'wasm-unsafe-eval'), Subresource Integrity для зовнішніх скриптів, уникаємо innerHTML, використовуємо React (автоматичний escaping). Регулярний npm audit та Snyk для аналізу ланцюга поставок.

Clipboard атаки. Користувач копіює адресу, malware замінює адресу в clipboard. Рішення: показуємо перші та останні N символів адреси, просимо візуально перевірити. Деякі гаманці показують адресу як identicon (унікальний аватар) — швидше помітити підміну.

Фішинг. Поддельний сайт гаманця. Рішення: PWA з верифікованим доменом, попередження на рівні браузера, ENS для верифікації.

Експозиція seed phrase. Показ seed phrase в UI — максимальний ризик. Ніколи не передавайте seed через мережу, показуйте тільки при створенні, вимагайте явної user action для перегляду, додайте попередження "чи ви самотні?".

Приватний ключ в пам'яті. Після розшифровки ключ живе в JavaScript heap. Garbage collector не гарантує негайне очищення. Мітигація: Uint8Array.fill(0) після використання, зберігайте ключ в Worker де GC більш передбачуваний.

Аудит залежностей

Гаманець використовує багато крипто-бібліотек. Критичні для перевірки:

  • @noble/secp256k1 або @noble/curves — підпис транзакцій
  • @scure/bip32, @scure/bip39 — HD derivation
  • ethers або viem — взаємодія з ланцюгом

Всі @noble/* та @scure/* бібліотеки від Paulmillr — аудовані, мінімалістичні, без залежностей. Віддавайте їм перевагу більш "корпоративним" альтернативам.

Мультиланцюгова підтримка

Ethereum-сумісні ланцюги (Polygon, Arbitrum, Base, Optimism) — одна адреса, різні RPC. Достатньо підтримувати EIP-155 chain ID та переключення RPC.

Bitcoin або Solana — різний алгоритм підпису (secp256k1 з різним форматом / ed25519), різний derivation path. Вимагає окремої ключової логіки.

Для мультиланцюгового гаманця: абстрагуйте ланцюг-специфічну логіку за загальним інтерфейсом. IChainSigner з методом signTransaction(tx) — кожен ланцюг має свою реалізацію.

Стек та графік

Компонент Технологія Графік
Управління ключами Web Crypto API + @scure 3-4 тижні
HD wallet (BIP-39/44) @scure/bip32 + bip39 1-2 тижні
Підпис транзакцій viem + ethers 2-3 тижні
ERC-4337 інтеграція permissionless.js + Pimlico 3-4 тижні
WalletConnect v2 @walletconnect/web3wallet 2-3 тижні
UI (React + TypeScript) React + Tailwind 5-7 тижнів
Security hardening CSP + Web Worker isolation 2-3 тижні
Мультиланцюг Chain abstraction layer 3-4 тижні
Тестування + підготовка аудиту Vitest + Playwright 3-4 тижні

MVP веб-гаманець (EOA, Ethereum, базовий UI): 8-10 тижнів. Production-ready з ERC-4337, мультиланцюгом, WalletConnect, WebAuthn: 6-9 місяців.

Security audit обов'язковий перед production запуском: гаманець зберігає ключі користувачів, одна вразливість = втрата коштів для всієї бази користувачів. Рекомендую аудит + bug bounty програму (Immunefi) з запуску.