Разработка системы embedded wallets
Embedded wallet — кошелёк, который живёт внутри приложения: пользователь не знает о seed phrase, не устанавливает MetaMask, не думает о private keys. Он просто входит через Google/email и получает возможность владеть активами. Это ключевой паттерн для onboarding массовой аудитории в Web3.
Проблема, которую решают embedded wallets
Требование установить MetaMask убивает конверсию. По данным исследований, 99%+ потенциальных пользователей Web3-приложений никогда не взаимодействовали с крипто. Embedded wallet убирает этот барьер: приложение само управляет кошельком, предоставляя знакомый UX.
Ключевые требования к embedded wallet:
- Non-custodial (или verifiably MPC-based) — приложение не может украсть средства
- Recoverable — пользователь не теряет доступ при смене устройства
- Exportable — пользователь может при желании перейти на self-custody
- Seamless — не прерывает UX приложения
Технические подходы к хранению ключей
MPC (Multi-Party Computation)
Ключ разделён между пользовательским устройством, сервером приложения и (опционально) третьей стороной. Подпись требует взаимодействия минимум двух участников.
Реализации: Privy (Shamir Secret Sharing + key sharding), Dynamic (Turnkey под капотом), Particle Network (собственный MPC-TSS), Web3Auth (threshold signatures).
Key Share 1: User device (localStorage encrypted / SecureEnclave)
Key Share 2: Provider server (HSM)
Key Share 3: Recovery factor (email/social provider)
Подпись = MPC протокол между Share 1 + Share 2
Восстановление = MPC между Share 2 + Share 3
Никто из участников не может восстановить полный ключ в одиночку.
TEE (Trusted Execution Environment)
Ключ генерируется и хранится в защищённой анклавной среде (Intel SGX, AWS Nitro Enclaves). Turnkey использует этот подход. Код в TEE верифицируем (attestation), оператор TEE не может получить доступ к данным внутри.
Шифрование на клиенте + облачный backup
Простейший подход: key pair генерируется в браузере, шифруется паролем пользователя, зашифрованный blob хранится в облаке. При входе — скачивается blob, расшифровывается паролем. Privy использует вариацию этого в качестве fallback.
Минус: безопасность = сложность пароля + безопасность облака.
Реализация через Privy
Privy — наиболее зрелое решение для embedded wallets. Поддерживает social login, embedded wallet с MPC, export ключа.
import { PrivyProvider, usePrivy, useWallets } from "@privy-io/react-auth";
function App() {
return (
<PrivyProvider
appId="YOUR_APP_ID"
config={{
loginMethods: ["email", "google", "twitter", "wallet"],
embeddedWallets: {
createOnLogin: "users-without-wallets",
requireUserPasswordOnCreate: false,
showWalletUIs: true,
},
appearance: {
theme: "dark",
accentColor: "#6366f1",
},
}}
>
<Main />
</PrivyProvider>
);
}
function Main() {
const { login, authenticated, user } = usePrivy();
const { wallets } = useWallets();
const embeddedWallet = wallets.find(w => w.walletClientType === "privy");
async function signMessage() {
if (!embeddedWallet) return;
const provider = await embeddedWallet.getEthereumProvider();
const signature = await provider.request({
method: "personal_sign",
params: ["Hello World", embeddedWallet.address],
});
return signature;
}
return authenticated ? (
<div>
<p>Address: {embeddedWallet?.address}</p>
<button onClick={signMessage}>Sign</button>
</div>
) : (
<button onClick={login}>Login</button>
);
}
Собственная реализация на базе Web3Auth
Web3Auth предоставляет MPC infrastructure которую можно интегрировать напрямую без лишних прослоек:
import { Web3Auth } from "@web3auth/modal";
import { CHAIN_NAMESPACES, WEB3AUTH_NETWORK } from "@web3auth/base";
const web3auth = new Web3Auth({
clientId: "YOUR_CLIENT_ID",
web3AuthNetwork: WEB3AUTH_NETWORK.SAPPHIRE_MAINNET,
chainConfig: {
chainNamespace: CHAIN_NAMESPACES.EIP155,
chainId: "0x1",
rpcTarget: "https://rpc.ankr.com/eth",
},
uiConfig: {
appName: "My App",
mode: "dark",
loginMethodsOrder: ["google", "twitter", "email_passwordless"],
},
});
await web3auth.initModal();
// Пользователь нажимает кнопку → выбирает social provider → получает кошелёк
const provider = await web3auth.connect();
const accounts = await provider.request({ method: "eth_accounts" });
Web3Auth использует Shamir Secret Sharing: ключ разделён на shares между узлами сети (threshold network). Минимум T из N узлов должны согласиться, чтобы восстановить ключ. Архитектура проверяема через attestation.
Кастомный флоу восстановления
Embedded wallet без recovery = катастрофа при смене устройства. Нужно реализовать:
Email recovery. При входе с нового устройства — код подтверждения на email → восстановление key share.
Social recovery. Владелец назначает guardians (другие адреса). При потере доступа — guardians голосуют за смену владельца. Реализуется через смарт-контракт поверх embedded wallet.
Passkey (WebAuthn). Биометрическая аутентификация как второй фактор. Face ID/Touch ID как key share. Поддерживается в iOS Safari, Chrome на Android, macOS.
// Регистрация passkey
async function registerPasskey() {
const credential = await navigator.credentials.create({
publicKey: {
challenge: await getChallenge(),
rp: { name: "My App", id: "myapp.com" },
user: {
id: new TextEncoder().encode(userId),
name: userEmail,
displayName: userName,
},
pubKeyCredParams: [{ type: "public-key", alg: -7 }], // ES256
authenticatorSelection: {
authenticatorAttachment: "platform",
userVerification: "required",
residentKey: "required",
},
},
});
// Сохраняем credential ID и public key
await savePasskeyCredential(credential);
}
Session Keys для бесшовного UX
Для приложений с частыми транзакциями (игры, trading) — session keys позволяют подписывать транзакции без подтверждения пользователем каждой:
interface SessionKeyConfig {
expiresAt: number; // unix timestamp
allowedContracts: string[]; // только эти контракты
maxValuePerTx: bigint; // лимит ETH на транзакцию
dailySpendLimit: bigint; // суточный лимит
allowedFunctions: string[]; // function selectors
}
Пользователь один раз подтверждает сессию (как "разрешить приложению X операции в течение Y"), дальше приложение использует session key без запросов.
Сравнение провайдеров
| Провайдер | Подход | Export ключа | Self-hosted | Passkeys |
|---|---|---|---|---|
| Privy | MPC (Shamir) | Да | Нет | Да |
| Dynamic | Turnkey (TEE) | Да | Нет | Да |
| Web3Auth | MPC (threshold) | Да | Частично | Да |
| Magic | DKMS (HSM) | Pro план | Нет | Нет |
| Particle Network | MPC-TSS | Да | Да (enterprise) | Да |
Безопасность и compliance
Key export. Должен быть реализован: пользователь нажимает «Экспортировать ключ», проходит дополнительную верификацию (email OTP + пароль), получает private key или seed phrase. Это критично для non-custodial позиционирования.
Server-side validation. Сервер не должен иметь возможности провести транзакцию без согласия пользователя. Архитектура MPC должна это гарантировать — и это должно быть верифицируемо (open-source SDK, attestation).
Audit trail. Все операции логируются (без private key данных). Пользователь может просмотреть историю действий со своим кошельком.
Стек
Готовые провайдеры: Privy, Dynamic, Web3Auth, Particle Network Смарт-контракты для social recovery: Safe{Core} Recovery Module, ERC-4337 совместимые аккаунты Session keys: Kernel (ZeroDev), Alchemy Modular Account Passkeys: SimpleWebAuthn (сервер), navigator.credentials (браузер)
Сроки
- Базовая интеграция (Privy/Dynamic + social login + embedded wallet): 1-2 недели
- Кастомный UI + recovery flows: +1-2 недели
- Session keys + gasless транзакции: +1-2 недели
- Собственная MPC инфраструктура (enterprise): 3-4 месяца







