美文网首页
Wallet端实现简易的Dapp浏览器

Wallet端实现简易的Dapp浏览器

作者: 说叁两事 | 来源:发表于2023-06-03 01:08 被阅读0次

    react-native@0.71.7环境为例,开发Android应用。

    MetaMask一键登录

    示例Demo

    import {WebView} from 'react-native-webview';
    import {useRef} from 'react';
    const address = '<personal address>';
    const injectedJavaScript = `
        window.ethereum = {};
        window.ethereum.isMetaMask = true;
        window.ethereum.isConnected = function() {
          console.log('-----------连接成功后调用---------')
          return true
        };
        window.ethereum.wallet = {};
        window.ethereum.wallet.address = '${address}';
        window.ethereum.selectedAddress = '${address}';
        window.ethereum.request = function(args = {}) {
          console.log('---------Dapp交互触发该事件-----------', args)
          const { method, params } = args
          return window.ethereum.send(method, params)
        };
        window.ethereum.send = function(method, params) {
          console.log('---------send-----------', method, params)
          return new Promise(function(resolve, reject) {
            window.ReactNativeWebView.postMessage(JSON.stringify({
                type: 'bsc',
                payload: {
                    method: method,
                    params: params,
                }
            }));
            document.addEventListener("message", function(event) {
              /**
              * wallet端主动调用postMessage触发该事件
              * 将webviewRef.current.postMessage回调的event.data作为Promise的值返回
              */
              const data = JSON.parse(event.data) || {}
              if (data.type === 'ethereum' && data.payload.id === method) {
                  if (data.payload.error) {
                      reject(data.payload.error);
                  } else {
                      resolve(data.payload.result);
                  }
              }
            }, { once: true });
          });
        };
    `;
    
    const BrowserTab = function () {
      const webviewRef = useRef(null);
      const handleWebViewMessage = async function (event: any) {
        /**
        * Dapp端交互调用window.ethereum.request时触发
        * 根据window.ReactNativeWebView.postMessage传递的不同参数,返回对应的结果
        */
        const {data} = event.nativeEvent;
        const {type, payload = {}} = JSON.parse(data) || {};
        const {method} = payload;
        console.log(webviewRef.current);
        if (webviewRef.current) {
          method === 'eth_requestAccounts' &&
            webviewRef.current.postMessage(
              JSON.stringify({
                type: 'ethereum',
                payload: {
                  id: 'eth_requestAccounts',
                  result: [address],
                },
              }),
              '*',
            );
          method === 'eth_chainId' &&
            webviewRef.current.postMessage(
              JSON.stringify({
                type: 'ethereum',
                payload: {
                  id: 'eth_chainId',
                  result: 5,
                },
              }),
              '*',
            );
        }
      };
      return (
        <WebView
          ref={webviewRef}
          source={{uri: 'https://app.uniswap.org/'}}
          style={{flex: 1}}
          javaScriptEnabled={true}
          onMessage={handleWebViewMessage}
          injectedJavaScriptBeforeContentLoaded={injectedJavaScript}
        />
      );
    };
    
    export default BrowserTab;
    

    流程分析:

    1. 借助react-native-webview加载Dapp Web;
    2. 绑定webviewRef实例,便于后续通信;
    3. injectedJavaScriptBeforeContentLoaded内容加载之前注入JS脚本;
    • Dapp实现会判断isMetaMask环境展示标识
    • 注入业务相关的符合eip-1102等规范的事件实现,如eth_requestAccountssignTransaction —— 在Dapp中进行操作,会调用对应的注入方法;
    1. <WebView>绑定onMessage={handleWebViewMessage},监听Dapp通过window.ReactNativeWebView.postMessage上报的消息;
    2. 格式化event.nativeEvent.data数据,根据methodparams进行对应的业务代码,通过 webviewRef.current.postMessage将结果返回;
    • webviewRef.current.postMessage会触发注入脚本window.ethereum.send方法中的document.addEventListener("message", handler)回调逻辑;
    • Dapp端会通过Promise接收返回结果,执行后续逻辑

    Notes:

    Metamask相关代码

      const onMessage = ({ nativeEvent }) => {
        let data = nativeEvent.data;
        try {
          data = typeof data === 'string' ? JSON.parse(data) : data;
          if (!data || (!data.type && !data.name)) {
            return;
          }
          if (data.name) {
            const origin = new URL(nativeEvent.url).origin;
            backgroundBridges.current.forEach((bridge) => {
              const bridgeOrigin = new URL(bridge.url).origin;
              bridgeOrigin === origin && bridge.onMessage(data);
            });
            return;
          }
        } catch (e) {
          Logger.error(e, `Browser::onMessage on ${url.current}`);
        }
      };
    

    backgroundBridges又是什么呢?

      const initializeBackgroundBridge = (urlBridge, isMainFrame) => {
        const newBridge = new BackgroundBridge({
          webview: webviewRef,  // webview实例,可以通过postMessage向内部渲染的Dapp通信
          url: urlBridge,
          getRpcMethodMiddleware: ({ hostname, getProviderState }) =>
            getRpcMethodMiddleware({
              hostname,
              getProviderState,
              navigation: props.navigation,
              // Website info
              url,
              title,
              icon,
              // Bookmarks
              isHomepage,
              // Show autocomplete
              fromHomepage,
              toggleUrlModal,
              // Wizard
              wizardScrollAdjusted,
              tabId: props.id,
              injectHomePageScripts,
            }),
          isMainFrame,
        });
        backgroundBridges.current.push(newBridge);
      };
    

    getRpcMethodMiddleware的定义

    RpcMethod
    其中,rpcMethods定义了一系列符合以太坊协议的方法
    RpcMethod

    参考文档

    https://stackoverflow.com/questions/75633905/how-to-inject-a-wallet-into-the-browser-from-mobile-react-native

    相关文章

      网友评论

          本文标题:Wallet端实现简易的Dapp浏览器

          本文链接:https://www.haomeiwen.com/subject/pnwmedtx.html