חיבור ארנק קריפטו הוא נקודת הכניסה הראשונה לכל יישום Web3. המשתמש לוחץ על "Connect Wallet", הדפדפן פותח את MetaMask או קוד QR של WalletConnect, והיישום מקבל כתובת וחתימה. זה נשמע פשוט, אבל מתחת לפני השטח יש שלושה פרוטוקולים שונים, תריסר ספקי ארנקים, ומספר בעיות הקשורות למצב (state) שצריך לפתור נכון מההתחלה. לדוגמה, טעות אופיינית היא קריאה ל-eth_requestAccounts בכל הרכבה (mount) של רכיב, מה שמוביל לבקשות חלונות קופצים תמידיות ופוגע בחוויית המשתמש. אנחנו עובדים עם זה כבר שנים: יש לנו 30+ פרויקטים עם אינטגרציית ארנקים ב-React, Next.js ו-Vue תחת החגורה.
אילו ארנקים אנחנו תומכים?
אנחנו מיישמים חיבורים עבור MetaMask (הזרקת EIP-1193), WalletConnect v2 (קוד QR/WebSocket לארנקים ניידים), Coinbase Wallet (שתי השיטות), Rabby ו-Brave Wallet. הטבלה שלהלן מציגה את הפרטים:
| ארנק | סוג חיבור | מאפיינים מיוחדים |
|---|---|---|
| MetaMask | הזרקת EIP-1193 | פופולרי, תוסף דפדפן, רשת בדיקות |
| WalletConnect | קוד QR / WebSocket | כל ארנק נייד (Trust, Rainbow, OKX) |
| Coinbase Wallet | הזרקה + WalletConnect | אינטגרציה עם Coinbase, תמיכה בדפדפן dapp |
| Rabby | הזרקת EIP-1193 | רב-רשתי, ממשק ידידותי, קוד פתוח |
לאחידות אנחנו משתמשים בספריות מתאמות (adapter): @web3-onboard או wagmi עם viem. בהשוואה ליישום ידני דרך ethers.js, פתרונות אלה מפחיתים את נפח הקוד ב-40% ומנמיכים את הסבירות לשגיאות בעת מעבר בין ארנקים. איזה מחסנית (stack) לבחור תלוי בדרישות לגודל ה-bundle וגמישות. אנחנו נעריך את הפרויקט שלך ונציע את הפתרון האופטימלי.
יישום מינימלי באמצעות ethers.js
להלן יישום חיבור בסיסי באמצעות ethers.js (v6). קוד זה עובד עבור ממשקים מותאמים אישית כאשר נדרשת שליטה מלאה.
// lib/wallet.ts
import { BrowserProvider, JsonRpcSigner } from 'ethers';
export interface WalletState {
address: string | null;
chainId: number | null;
provider: BrowserProvider | null;
signer: JsonRpcSigner | null;
}
export async function connectWallet(): Promise<WalletState> {
if (!window.ethereum) {
throw new Error('No injected wallet found. Install MetaMask.');
}
const provider = new BrowserProvider(window.ethereum);
const accounts = await provider.send('eth_requestAccounts', []);
const network = await provider.getNetwork();
const signer = await provider.getSigner();
return {
address: accounts[0],
chainId: Number(network.chainId),
provider,
signer,
};
}
export async function switchChain(chainId: number): Promise<void> {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: `0x${chainId.toString(16)}` }],
});
}
כיצד לטפל באירועי ארנק ללא דליפות זיכרון?
הארנק יכול לשנות חשבון או רשת ללא הודעה ליישום – צריך להירשם לאירועים. הרשמה נכונה עם ניקוי בעת ביטול הרכבה (unmount) היא המפתח ליציבות.
// hooks/useWalletEvents.ts
import { useEffect } from 'react';
import { useWalletStore } from '@/store/wallet';
export function useWalletEvents() {
const { disconnect, setAddress, setChainId } = useWalletStore();
useEffect(() => {
if (!window.ethereum) return;
const handleAccountsChanged = (accounts: string[]) => {
if (accounts.length === 0) {
disconnect();
} else {
setAddress(accounts[0]);
}
};
const handleChainChanged = (chainIdHex: string) => {
setChainId(parseInt(chainIdHex, 16));
// Страницу не перезагружаем — обновляем состояние
};
window.ethereum.on('accountsChanged', handleAccountsChanged);
window.ethereum.on('chainChanged', handleChainChanged);
window.ethereum.on('disconnect', disconnect);
return () => {
window.ethereum.removeListener('accountsChanged', handleAccountsChanged);
window.ethereum.removeListener('chainChanged', handleChainChanged);
window.ethereum.removeListener('disconnect', disconnect);
};
}, [disconnect, setAddress, setChainId]);
} מדוע SIWE הוא תקן האימות ללא סיסמה?
כתובת ארנק אינה מזהה משתמש – אפשר לזייף אותה בקלות בבקשת HTTP. SIWE (Sign-In with Ethereum) פותר זאת: המשתמש חותם על הודעה, השרת מאמת את החתימה דרך // lib/wallet.ts import { BrowserProvider, JsonRpcSigner } from 'ethers'; export interface WalletState { address: string | null; chainId: number | null; provider: BrowserProvider | null; signer: JsonRpcSigner | null; } export async function connectWallet(): Promise<WalletState> { if (!window.ethereum) { throw new Error('No injected wallet found. Install MetaMask.'); } const provider = new BrowserProvider(window.ethereum); const accounts = await provider.send('eth_requestAccounts', []); const network = await provider.getNetwork(); const signer = await provider.getSigner(); return { address: accounts[0], chainId: Number(network.chainId), provider, signer, }; } export async function switchChain(chainId: number): Promise<void> { await window.ethereum.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: `0x${chainId.toString(16)}` }], }); } . Nonce עם TTL של 5 דקות מגן מפני התקפות replay. גישה זו מספקת סשן מאובטח ללא אחסון סיסמאות.
// lib/siwe.ts
import { SiweMessage } from 'siwe';
export async function signInWithEthereum(
address: string,
chainId: number,
signer: JsonRpcSigner,
): Promise<{ message: string; signature: string }> {
const nonce = await fetch('/api/auth/nonce').then(r => r.text());
const message = new SiweMessage({
domain: window.location.host,
address,
statement: 'Sign in to MyApp',
uri: window.location.origin,
version: '1',
chainId,
nonce,
});
const messageStr = message.prepareMessage();
const signature = await signer.signMessage(messageStr);
return { message: messageStr, signature };
}השרת מאמת את החתימה דרך חבילת // hooks/useWalletEvents.ts import { useEffect } from 'react'; import { useWalletStore } from '@/store/wallet'; export function useWalletEvents() { const { disconnect, setAddress, setChainId } = useWalletStore(); useEffect(() => { if (!window.ethereum) return; const handleAccountsChanged = (accounts: string[]) => { if (accounts.length === 0) { disconnect(); } else { setAddress(accounts[0]); } }; const handleChainChanged = (chainIdHex: string) => { setChainId(parseInt(chainIdHex, 16)); // Страницу не перезагружаем — обновляем состояние }; window.ethereum.on('accountsChanged', handleAccountsChanged); window.ethereum.on('chainChanged', handleChainChanged); window.ethereum.on('disconnect', disconnect); return () => { window.ethereum.removeListener('accountsChanged', handleAccountsChanged); window.ethereum.removeListener('chainChanged', handleChainChanged); window.ethereum.removeListener('disconnect', disconnect); }; }, [disconnect, setAddress, setChainId]); } (Node.js) או כל יישום ecrecover. ניתן לקרוא עוד על התקן ב-EIP-4361 או ב-ויקיפדיה.
כיצד לאחד חיבור של מספר ארנקים?
כדי לתמוך במספר ספקים ללא לוגיקה מותאמת אישית, אנחנו משתמשים בספריות מתאמות. יישום ידני דרך // lib/siwe.ts import { SiweMessage } from 'siwe'; export async function signInWithEthereum( address: string, chainId: number, signer: JsonRpcSigner, ): Promise<{ message: string; signature: string }> { const nonce = await fetch('/api/auth/nonce').then(r => r.text()); const message = new SiweMessage({ domain: window.location.host, address, statement: 'Sign in to MyApp', uri: window.location.origin, version: '1', chainId, nonce, }); const messageStr = message.prepareMessage(); const signature = await signer.signMessage(messageStr); return { message: messageStr, signature }; } נותן שליטה מלאה אך דורש כתיבת הפשטה (abstraction) לכל ארנק. siwe עם ecrecover מציע hooks של React ו-API בטוח מבחינת טיפוסים, ומפחית קוד ב-30%, אך אינו מתאים ל-Vue. ethers.js הוא האוניברסלי ביותר: ממשק משתמש מובנה ותמיכה ב-20+ ארנקים, אך מוסיף כ-50 KB ל-bundle. הבחירה תלויה בעדיפויות: קלילות מול מהירות פיתוח.
דוגמה לתצורה עם wagmi:
import Onboard from '@web3-onboard/core';
import injectedModule from '@web3-onboard/injected-wallets';
import walletConnectModule from '@web3-onboard/walletconnect';
const injected = injectedModule();
const walletConnect = walletConnectModule({
projectId: process.env.NEXT_PUBLIC_WC_PROJECT_ID!,
requiredChains: [1, 137],
});
export const onboard = Onboard({
wallets: [injected, walletConnect],
chains: [
{
id: '0x1',
token: 'ETH',
label: 'Ethereum Mainnet',
rpcUrl: process.env.ETH_RPC_URL!,
},
{
id: '0x89',
token: 'MATIC',
label: 'Polygon',
rpcUrl: process.env.POLYGON_RPC_URL!,
},
],
appMetadata: {
name: 'MyApp',
icon: '/logo.svg',
description: 'DeFi platform',
},
});
שחזור סשן לאחר רענון
ללא התמדה (persistence), המשתמש יצטרך לעבור את כל התהליך בכל פעם. אנחנו משתמשים ב-viem – זה מחזיר כתובות ללא חלון קופץ אם הארנק כבר מורשה.
// Проверка при инициализации
async function restoreConnection(): Promise<void> {
if (!window.ethereum) return;
const accounts: string[] = await window.ethereum.request({
method: 'eth_accounts',
});
if (accounts.length > 0) {
const provider = new BrowserProvider(window.ethereum);
const network = await provider.getNetwork();
walletStore.set({
address: accounts[0],
chainId: Number(network.chainId)
});
}
} טעויות אופייניות וכיצד להימנע מהן
- שימוש ב-
@web3-onboardבכל הרכבה – גורם לחלון קופץ גם אם הארנק כבר מחובר. פתרון: בדוק את@web3-onboardקודם. - חוסר טיפול ב-
import Onboard from '@web3-onboard/core'; import injectedModule from '@web3-onboard/injected-wallets'; import walletConnectModule from '@web3-onboard/walletconnect'; const injected = injectedModule(); const walletConnect = walletConnectModule({ projectId: process.env.NEXT_PUBLIC_WC_PROJECT_ID!, requiredChains: [1, 137], }); export const onboard = Onboard({ wallets: [injected, walletConnect], chains: [ { id: '0x1', token: 'ETH', label: 'Ethereum Mainnet', rpcUrl: process.env.ETH_RPC_URL! }, { id: '0x89', token: 'MATIC', label: 'Polygon', rpcUrl: process.env.POLYGON_RPC_URL! }, ], appMetadata: { name: 'MyApp', icon: '/logo.svg', description: 'DeFi platform', }, });– יכול להוביל לדליפות זיכרון. תמיד הירשם ונקה מאזינים. - חישוב nonce שגוי ב-SIWE – אם ה-nonce לא נשמר בשרת, לא ניתן לאמת את החתימה. השתמש ב-nonce ייחודי עם TTL.
מה כלול בעבודה
אנחנו מספקים חבילה מלאה:
- אינטגרציה של 3-4 ארנקים (MetaMask, WalletConnect, Coinbase, Rabby) דרך מתאם
- אימות SIWE עם חלק שרת (Node.js/Python)
- טיפול באירועים (accountsChanged, chainChanged, disconnect)
- שחזור סשן וטיפול בשגיאות (דחייה, אין ארנק)
- החלפת רשת ובדיקת יתרה
- תיעוד אינטגרציה ו-Readme
- ייעוץ לבחירת ספרייה עבור המחסנית שלך
צור קשר להערכה חינמית של הפרויקט שלך – נגיב תוך יום. הזמן אינטגרציית ארנק עכשיו וקבל קוד נקי עם כיסוי מלא של מקרי קצה.
לוחות זמנים ועלות
אינטגרציה בסיסית של ארנק אחד עם SIWE – בין 2 ל-3 ימים. פתרון מלא (turnkey) עם תמיכה ב-4-5 ארנקים, טיפול בכל המצבים ותיעוד – בין 5 ל-7 ימים. העלות מחושבת באופן אישי בהתאם למורכבות השרת ומספר הארנקים. קבל ייעוץ – נגיב תוך יום.







