פיתוח ארנק קריפטו מבוסס דפדפן
בפעם האחרונה שהזנתם ביטוי גיבוי (seed phrase) בטופס אינטרנטי? זו מלכודת XSS. ארנק מבוסס דפדפן הוא החוליה הפגיעה ביותר, אך גם הנגישה ביותר. אנחנו פותרים את הבעיה: כיצד לאחסן מפתחות בדפדפן ללא סיכון של פריצה תוך הבטחת נוחות למשתמש. עם 7 שנות ניסיון בפיתוח בלוקצ'יין ויותר מ-50 ארנקים שיושמו — מארנקי EOA פשוטים ועד חוזים חכמים עם ERC-4337 — אנחנו מספקים פתרונות ברמת ייצור. העלות מוערכת באופן אישי לאחר ניתוח הדרישות שלכם.
הדילמה המרכזית: אבטחה מול חוויית משתמש. ארנקי EOA מסורתיים מאבדים כספים אם מפתח נגנב. חשבונות חכמים (ERC-4337) מוסיפים שחזור חברתי ומפתחות הפעלה (session keys) אך דורשים יותר גז. בפועל, לאימוץ המוני, הבחירה ברורה — Account Abstraction. נראה לכם כיצד לבנות ארנק שלא מתפשר על אף אחד מההיבטים.
החלטות ארכיטקטוניות מרכזיות לארנק קריפטו מבוסס דפדפן
EOA מול חשבון חכם
EOA (חשבון בבעלות חיצונית) הוא ארנק קלאסי עם מפתח פרטי יחיד. אובדן מפתח משמעותו אובדן גישה לצמיתות. חשבון חכם (ERC-4337) הוא לוגיקת חתימה ניתנת לתכנות: שחזור חברתי, מפתחות הפעלה, עסקאות מצטברות. בפועל, חשבון חכם מפחית את סיכון אובדן הכספים עד פי 5 באמצעות שחזור דרך גורמים מהימנים. עם זאת, EOA זול יותר ב-10-15% בגז — תעדיפו אבטחה או עלות בהתאם.
כיצד להגן על מפתחות פרטיים בדפדפן?
אפשרויות מהבטוחה לפחות:
-
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)); 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, 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 }; }/@scure/bip39— לעולם לא. נגיש לכל JavaScript. -
@scure/bip32עם הצפנת AES-GCM 256-bit באמצעות Web Crypto API. המפתח נגזר מסיסמה (PBKDF2, 600,000 איטרציות). זהו התקן לארנקי דפדפן.
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));
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,
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 + מפתח חומרה — הבטוח ביותר. המפתח הפרטי לעולם לא עוזב את TPM/Secure Enclave. החתימה מתבצעת בתוך המכשיר. Daimo ו-Coinbase Smart Wallet משתמשות בגישה זו. האבטחה שלה גבוהה פי 3 מ-IndexedDB.
- MPC — המפתח לעולם לא קיים בשלמותו. מחולק לחלקים, החתימה דורשת סף. חסרונות: איטי ב-30-50%.
גזירת מפתחות וארנקי HD
BIP-39 ו-BIP-44: ביטוי גיבוי → מפתח ראשי → מפתחות ילדים. הנתיב 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!), }; } הוא חשבון Ethereum הראשון. ספריות: // 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)); }; }); } ו-@walletconnect/web3wallet — מבוקרות, ניתנות לגיזום עץ.
דוגמת קוד לגזירה
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!),
};
} ארכיטקטורת ארנק מבוסס דפדפן
הפרדת תחומי אחריות: ממשק משתמש מול חתימה
כלל קריטי: קוד עם גישה למפתח מבודד מממשק המשתמש באמצעות Web Worker. XSS בממשק המשתמש אינו פוגע במפתח.
// 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));
};
});
}WalletConnect ואינטגרציית dApp אנו משתמשים ב-SDK 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"], }, }); 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" }, }); } } }); גרסה 2.
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"],
},
});
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" },
});
}
}
}); מה כלול בעבודה שלנו
| תוצר | תיאור |
|---|---|
| תיעוד ארכיטקטורה | דיאגרמות, תיאורי מודולים, מודל איומים |
| קוד מקור מלא | מאגר ארנק מלא |
| אינטגרציית שירותים חיצוניים | WalletConnect, RPC, bundler, paymaster |
| בדיקות יחידה ואינטגרציה | כיסוי תרחישים מרכזיים |
| ביקורת אבטחה | ביקורת קוד וחוזים על ידי צד שלישי |
| פריסה ותמיכה | השקה לייצור, 3 חודשי תמיכה לאחר ההשקה |
שלבי פיתוח ארנק מבוסס דפדפן
- ארכיטקטורה ומודל איומים (1-2 שבועות)
- ניהול מפתחות וארנק HD (3-4 שבועות)
- חתימת עסקאות ושכבת RPC (2-3 שבועות)
- אינטגרציית ERC-4337 (3-4 שבועות)
- אינטגרציית WalletConnect ו-dApp (2-3 שבועות)
- ממשק משתמש (React + TypeScript) (5-7 שבועות)
- חיזוק אבטחה וביקורת (2-3 שבועות)
- תמיכה בריבוי רשתות (3-4 שבועות)
- בדיקות ותיקון באגים (3-4 שבועות)
מדוע בידוד חתימה מממשק המשתמש הוא קריטי?
איומים עיקריים: XSS, התקפות לוח, פישינג, שרשרת אספקה. צעדי הגנה:
- CSP מחמיר + Subresource Integrity
- הצגת התווים הראשונים והאחרונים של הכתובת, שימוש ב-identicons
- PWA עם דומיין מאומת, אימות ENS
- ביטוי גיבוי לעולם לא מועבר ברשת
- מפתח פרטי מאופס בזיכרון לאחר השימוש
מה הופך ארנק לבטוח באמת?
WebAuthn + מפתח חומרה הוא תקן הזהב. המפתח הפרטי אינו נגיש פיזית לדפדפן. השכבה השנייה היא MPC, שבה החתימה מורכבת מחלקים מפוצלים. השלישית היא תוכנית תגמולים לבאגים ב-Immunefi. אנו משתמשים בכל שלוש השכבות לפתרונות ייצור.
לוחות זמנים משוערים
| רכיב | טכנולוגיה | משך |
|---|---|---|
| ניהול מפתחות | Web Crypto API + @scure | 3-4 שבועות |
| ארנק HD (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 שבועות |
| ממשק משתמש (React + TypeScript) | React + Tailwind | 5-7 שבועות |
| חיזוק אבטחה | CSP + בידוד Web Worker | 2-3 שבועות |
| ריבוי רשתות | שכבת הפשטת רשתות | 3-4 שבועות |
| בדיקות + הכנה לביקורת | Vitest + Playwright | 3-4 שבועות |
ארנק MVP (EOA, Ethereum, ממשק בסיסי): 8-10 שבועות. מוכן לייצור עם ERC-4337, ריבוי רשתות, WalletConnect, WebAuthn: 6-9 חודשים. צרו קשר כדי לדון בפרויקט שלכם — נעריך את ההיקף ונציע פתרון אופטימלי. הזמינו פיתוח ארנק דפדפן וקבלו ייעוץ מהנדס.







