You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将自定义钱包接入React Native内嵌浏览器?

解决思路与实现步骤

首先明确:expo-web-browser是系统浏览器的封装,无法直接注入自定义JavaScript代码,所以要实现钱包提供商注入,必须替换为可控制的WebView组件(比如react-native-webview),因为它允许我们操作网页的上下文环境,实现双向通信。

以下是具体实现方案:

1. 替换为react-native-webview

先安装依赖(Expo项目直接用expo安装):

expo install react-native-webview

2. 注入EIP-1193标准的钱包Provider

Web3网站都是基于EIP-1193标准和钱包交互的,我们需要在WebView加载网页时,向网页注入一个符合该标准的window.ethereum对象,同时实现双向通信逻辑:

核心代码示例

import React, { useRef } from 'react';
import { WebView } from 'react-native-webview';
import { ethers } from 'ethers';
import * as SecureStore from 'expo-secure-store';

// 从安全存储获取私钥(示例)
const getWallet = async () => {
  const privateKey = await SecureStore.getItemAsync('wallet_private_key');
  return privateKey ? new ethers.Wallet(privateKey) : null;
};

const Web3Browser = () => {
  const webviewRef = useRef(null);
  const [wallet, setWallet] = React.useState(null);

  React.useEffect(() => {
    (async () => {
      const loadedWallet = await getWallet();
      setWallet(loadedWallet);
    })();
  }, []);

  // 注入到网页的EIP-1193 Provider代码
  const injectProviderScript = `
    window.ethereum = {
      _requestPromises: new Map(),
      _nextId: 1,
      request: async (args) => {
        return new Promise((resolve, reject) => {
          const id = this._nextId++;
          this._requestPromises.set(id, { resolve, reject });
          window.ReactNativeWebView.postMessage(JSON.stringify({
            id,
            method: args.method,
            params: args.params || []
          }));
        });
      },
      sendAsync: (payload, callback) => {
        this.request(payload)
          .then(result => callback(null, { id: payload.id, result }))
          .catch(error => callback(error, null));
      },
      on: (eventName, listener) => {
        // 后续可扩展实现账户/链变更事件监听
      }
    };

    window.addEventListener('message', (event) => {
      const data = JSON.parse(event.data);
      const promise = window.ethereum._requestPromises.get(data.id);
      if (!promise) return;
      window.ethereum._requestPromises.delete(data.id);
      if (data.error) {
        promise.reject(new Error(data.error));
      } else {
        promise.resolve(data.result);
      }
    });

    window.dispatchEvent(new Event('ethereum#initialized'));
  `;

  // 处理WebView发来的请求
  const handleMessage = async (event) => {
    if (!wallet) {
      webviewRef.current.postMessage(JSON.stringify({
        id: JSON.parse(event.nativeEvent.data).id,
        error: 'Wallet not loaded'
      }));
      return;
    }

    const { id, method, params } = JSON.parse(event.nativeEvent.data);
    let result;
    let error = null;

    switch (method) {
      case 'eth_requestAccounts':
        result = [wallet.address];
        break;
      case 'eth_sign':
        try {
          const signature = await wallet.signMessage(params[1]);
          webviewRef.current.postMessage(JSON.stringify({ id, result: signature }));
        } catch (err) {
          webviewRef.current.postMessage(JSON.stringify({ id, error: err.message }));
        }
        return;
      case 'eth_sendTransaction':
        try {
          const tx = await wallet.sendTransaction(params[0]);
          webviewRef.current.postMessage(JSON.stringify({ id, result: tx.hash }));
        } catch (err) {
          webviewRef.current.postMessage(JSON.stringify({ id, error: err.message }));
        }
        return;
      case 'eth_chainId':
        result = '0x1'; // 以太坊主网,按需修改为目标链ID
        break;
      case 'eth_getBalance':
        const balance = await wallet.getBalance();
        result = balance.toHexString();
        break;
      default:
        error = `Unsupported method: ${method}`;
    }

    webviewRef.current.postMessage(JSON.stringify({ id, result, error }));
  };

  return (
    <WebView
      ref={webviewRef}
      source={{ uri: 'https://sparklesnft.com/' }}
      injectedJavaScriptBeforeContentLoaded={injectProviderScript}
      onMessage={handleMessage}
      style={{ flex: 1 }}
      // 可添加原expo-web-browser的类似配置,比如工具栏颜色等
    />
  );
};

export default Web3Browser;

3. 关键注意事项

  • 安全存储:私钥绝对不能硬编码,必须用expo-secure-store这类安全存储方案保存,避免泄露。
  • EIP-1193兼容性:尽量实现标准中的核心方法和事件(比如accountsChanged、chainChanged),确保Web3网站能正常识别并交互。
  • 异步处理:签名、发送交易等操作是异步的,需在RN端处理完成后再将结果返回网页。
  • 错误捕获:要捕获ethers.js的调用错误,将清晰的错误信息返回给网页,避免前端出现未处理异常。

内容的提问来源于stack exchange,提问作者xfscrypt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 22:30:33