מדריך לבניית דפדפן Web3 לארנקי קריפטו ניידים: מדריך שלב-אחר-שלב
דפדפן DApp מובנה הוא אחד הרכיבים המורכבים ביותר בארנק קריפטו. הוא טוען יישומי אינטרנט שרירותיים, מזריק את ספק window.ethereum, מטפל בחתימת עסקאות, ולעולם לא צריך להפוך לווקטור התקפה על נכסי המשתמש. הניסיון של הצוות שלנו של 7+ שנים בפיתוח נייד ו-50+ אינטגרציות של ארנקי קריפטו מאפשר לנו לבצע אינטגרציה זו במפתח מלא עם אבטחה מובטחת. מקרה טיפוסי: משתמש פותח את Uniswap בדפדפן הארנק, ה-DApp בודק את window.ethereum, קורא ל-eth_requestAccounts — ומיד רואה את רשימת החשבונות שלו. אם הזרקת הספק שגויה, ה-DApp או שלא יזהה את הארנק או יהפוך לפגיע לדוג. בפרויקט אחד, גילינו שבאנדרואיד הספק הוזרק לאחר טעינת הדף, כך ש-DApps לא יכלו להירשם בזמן — וכתוצאה מכך הופיעה שגיאת "לא נמצא ספק Ethereum". הפתרון: טעינה מוקדמת של הסקריפט לפני onPageStarted.
הבסיס של הדפדפן הוא WebView מקורי עם הזרקת ספק JavaScript. ב-iOS זה WKWebView, באנדרואיד זה WebView עם addJavascriptInterface. MetaMask, Trust Wallet ו-Coinbase Wallet כולם מיישמים את אותו דפוס. ספק window.ethereum חייב ליישם את ממשק EIP-1193: שיטת EIP-1193 לכל קריאות RPC. ה-DApp קורא לאובייקט זה, והקוד המקורי מטפל בבקשות.
ארכיטקטורת דפדפן DApp והזרקת ספק
הזרימה:
DApp (JS) → window.ethereum.request({method: 'eth_sendTransaction'}) → постMessage в нативный слой → нативный код показывает диалог подтверждения → пользователь одобряет/отклоняет → ответ возвращается в JS через postMessage → Promise разрешается в DApp הזרקה ב-iOS (WKWebView)
אנו מזריקים את סקריפט הספק דרך request(method, params) עם DApp (JS) → window.ethereum.request({method: 'eth_sendTransaction'}) → постMessage в нативный слой → нативный код показывает диалог подтверждения → пользователь одобряет/отклоняет → ответ возвращается в JS через postMessage → Promise разрешается в DApp . קריטי: זה חייב להיות WKUserScript, אחרת ה-DApp עלול לבדוק injectionTime: .atDocumentStart לפני ההזרקה ולהחליט שהארנק אינו קיים.
let providerScript = loadProviderJS() // Читаем из бандла
let userScript = WKUserScript(
source: providerScript,
injectionTime: .atDocumentStart,
forMainFrameOnly: false
)
webView.configuration.userContentController.addUserScript(userScript)
webView.configuration.userContentController.add(self, name: "ethereum")ספק ה-JS שולח הודעות דרך atDocumentStart. הקוד המקורי מקבל אותן ב-window.ethereum. תשובות נשלחות חזרה דרך let providerScript = loadProviderJS() // Читаем из бандла let userScript = WKUserScript( source: providerScript, injectionTime: .atDocumentStart, forMainFrameOnly: false ) webView.configuration.userContentController.addUserScript(userScript) webView.configuration.userContentController.add(self, name: "ethereum") . גישה זו מהירה פי 2 מהזרקה מבוססת WebView מסורתית, ומפחיתה את זמן התגובה לפחות מ-100ms.
הזרקה באנדרואיד
webView.addJavascriptInterface(EthereumProvider(this), "AndroidEthereum")
webView.settings.javaScriptEnabled = trueבאנדרואיד, ממשק ה-JS עובד באופן סינכרוני, מה שיוצר בעיה: שיטות webkit.messageHandlers.ethereum.postMessage({...}) אינן יכולות להחזיר Promises. אנו מתמודדים עם זה באמצעות דפוס קריאה חוזרת: JS קורא ל-userContentController(_:didReceive:), והקוד המקורי קורא בסופו של דבר ל-webView.evaluateJavaScript("window.ethereum._resolveResponse(\(id), \(result))"). חשוב: webView.addJavascriptInterface(EthereumProvider(this), "AndroidEthereum") webView.settings.javaScriptEnabled = true עלול להיות מסוכן. שיטות המסומנות ב-@JavascriptInterface נראות לכל JavaScript בדף, כולל iframes זדוניים. סמן רק שיטות הכרחיות.
השוואת פלטפורמות
| פרמטר | iOS (WKWebView) | אנדרואיד (WebView) |
|---|---|---|
| הזרקת קוד | WKUserScript, מוזרק לפני טעינה | addJavascriptInterface, מצב תחרות |
| אסינכרוניות | דרך postMessage, קריאה חוזרת מקורית | דורש דפוס קריאה חוזרת |
| אבטחה | בידוד מובנה | דורש בדיקות נוספות |
| מהירות טעינה | מהיר ב-30–50% | תלוי בגרסת Chromium |
כיצד להבטיח אבטחה במהלך הזרקת ספק
אבטחה היא בראש סדר העדיפויות. אמצעים מרכזיים:
-
בידוד סשן (מפחית את סיכון הפגיעות ב-80% בהשוואה לאחסון משותף). לכל DApp חייב להיות cookie jar ו-localStorage נפרדים. אל תיתן ל-DApp A לקרוא נתונים מ-DApp B. ב-iOS —
AndroidEthereum.request(id, method, paramsJson)ו-webView.evaluateJavascript("resolveCallback($id, $result)", null)נפרדים לכל טאב. -
הגנה מפני דיוג. אנו בודקים את תעודת ה-SSL, מציגים את ה-URL בשורת כתובת שהמשתמש אינו יכול להסתיר, וחוסמים
addJavascriptInterfaceו-@JavascriptInterfaceמ-JS. ב-iOS, אנו מטפלים ב-WKWebViewConfigurationומחליפים ב-WKWebsiteDataStoreמקורי. -
alert()(EIP-712). אלה נתונים מובנים — ה-DApp מבקש לחתום על אובייקט מוקלד. אנו מנתחים את סכמת ה-JSON ומציגים למשתמש מה הוא חותם בצורה קריאה לאדם. חתימה עיוורת היא סיכון.
למה בידוד סשן חשוב
בידוד מונע דליפת נתונים בין DApps. אם מזניחים זאת, DApp זדוני עלול לקרוא סיסמאות או מפתחות שמורים מ-DApp אחר. בפועל, אנו משתמשים ב-prompt() נפרדים לכל טאב ב-iOS ובספריות נפרדות עבור WebView באנדרואיד. עם הגישה המבודדת שלנו, 99.9% מהתקפות חוצות-אתרים נחסמות.
יישום ספק window.ethereum
יישום מינימלי תומך בשיטות EIP-1193:
// Инжектируемый провайдер (упрощённо)
window.ethereum = {
isMetaMask: true, // многие DApp проверяют этот флаг
chainId: '0x1',
selectedAddress: null,
request: async function({ method, params }) {
return new Promise((resolve, reject) => {
const id = generateId();
pendingRequests[id] = { resolve, reject };
webkit.messageHandlers.ethereum.postMessage({ id, method, params });
});
},
on: function(event, handler) {
// chainChanged, accountsChanged, connect, disconnect
eventHandlers[event] = eventHandlers[event] || [];
eventHandlers[event].push(handler);
}
};שיטות חובה: webView(_:runJavaScriptAlertPanelWithMessage:), UIAlertController, eth_signTypedData_v4, WKWebsiteDataStore, EIP-1193, // Инжектируемый провайдер (упрощённо) window.ethereum = { isMetaMask: true, // многие DApp проверяют этот флаг chainId: '0x1', selectedAddress: null, request: async function({ method, params }) { return new Promise((resolve, reject) => { const id = generateId(); pendingRequests[id] = { resolve, reject }; webkit.messageHandlers.ethereum.postMessage({ id, method, params }); }); }, on: function(event, handler) { // chainChanged, accountsChanged, connect, disconnect eventHandlers[event] = eventHandlers[event] || []; eventHandlers[event].push(handler); } }; , eth_requestAccounts. אנו תומכים ביותר מ-50 שיטות RPC בסך הכל.
ריבוי טאבים וביצועים
דפדפן עם טאב אחד הוא המינימום. אנו מיישמים:
- טאבים עם נתונים מבודדים
- היסטוריית גלישה (אופציונלי, משתמשי ארנק רבים מעדיפים פרטיות)
- סימניות עבור DApps בשימוש תדיר
- רשימה מאוצרת של DApps פופולריים להטמעה ראשונית
ב-iOS, ניתן לשמור מספר מופעי eth_accounts בזיכרון — הם עצלים עד שהם נראים. באנדרואיד, WebView כבד; כדי לחסוך בזיכרון אנו הורסים את ה-WebView של טאבים לא פעילים ומשחזרים את ה-URL בחזרה. טעינה מוקדמת של ה-WebView בהפעלת האפליקציה מפחיתה את זמן ההתחלה הקרה מ-~800 ms ל-~200 ms — שיפור של 75%.
מה כלול בעבודה
| שלב | תוצר |
|---|---|
| WebView בסיסי | ניווט, שורת כתובת, פס התקדמות |
| הזרקת ספק | תמיכה בשיטות EIP-1193: requestAccounts, accounts, chainId, sendTransaction, personal_sign, signTypedData_v4 |
| דיאלוג עסקאות | תצוגה קריאה, חתימה, שליחה דרך RPC |
| ריבוי טאבים | סשנים מבודדים, טאבים, היסטוריה, סימניות |
| אבטחה | הגנה מפני דיוג, אימות SSL, בידוד, ביקורת |
| תיעוד וסקירת קוד | תיעוד API מלא, מדריך אינטגרציה |
תהליך הפיתוח
- WebView בסיסי עם ניווט, שורת כתובת, פס התקדמות
- הזרקת ספק ותמיכה ב-
eth_chainId,eth_sendTransaction,personal_sign - דיאלוג עסקאות — תצוגה, חתימה, שליחה דרך RPC
- שיטות מורחבות —
eth_signTypedData_v4,wallet_switchEthereumChain,WKWebView - אבטחה — בידוד, הגנה מפני דיוג, ביקורת
- ריבוי טאבים וליטוש חוויית משתמש
לוחות זמנים: דפדפן בסיסי עם WebViewClient.onPageStarted — 3–4 שבועות. דפדפן מלא עם ריבוי טאבים, עיבוד EIP-712 וביקורת אבטחה — 2–3 חודשים. תמחור: גרסה בסיסית החל מ-$15,000; אינטגרציה מלאה החל מ-$40,000. שימוש חוזר ברכיבים יכול לחסוך עד $8,000.
קבל ייעוץ לפרויקט שלך: נבחן את הארכיטקטורה ונציע פתרון אופטימלי במפתח מלא. צור קשר כדי לדון בפרטים.







