边学边用--使用React下的Material UI框架开发一个简单的仿MetaMask的网页版以太坊钱包(六)

在上一次开发中,我们完成了钱包的ETH转账功能,这次开发我们计划完成账号导出功能。

一、本次开发计划

       因为我们的钱包为网页版,账号私钥加密后保存在本地存储中,当用户清理浏览器本地存储时便会丢失账号。所以需要一个导出账号功能,将用户的私钥或者助记词导出,用户可以复制后保存在别处,防止账号丢失。计划开发中的页面如下:

       用户登录后,点击右上角的账号选项,会弹出一个用选择菜单:
在这里插入图片描述
       点击在EtherScan上查看便会打开一个新浏览器窗口并转到EtherScan显示账号详情。如果我们点击账户详情,则显示如下页面:
在这里插入图片描述
       微信扫描那个二维码会显示账号地址,这个页面同样也同样提供了在EtherScan上查看的功能,最下方是导出助记词和导出私钥按钮。

       提示:

       只有在使用助记词导入账号时才会显示导出助记词按钮,使用私钥导入账号是获取不了助记词的。因为我们的钱包只保存了私钥,所以在登录时是使用私钥登录的,获取不了助记词,也就无法导出助记词。

       点击导出私钥,会出现如下界面:
在这里插入图片描述
       输入你的密码后点击确定,就会在文本框中显示你的私钥。点击私钥可以复制到粘贴板,可以将它保存在别处。
在这里插入图片描述
       点击完成返回到钱包主界面,点击左上角返回按钮退到账号详情界面。

二、用户地址Icon的实现

       我们在MetaMask中可以看到,每个账号都有一个不同的圆球形的icon来表示,比如下图中的主网账号、Kovan账号1和2等。
在这里插入图片描述
       这个功能我们使用jazzicon库来实现,这是它的文档地址: => https://www.npmjs.com/package/jazzicon。它返回一个HTML节点对象(一个包含了svgdiv)。通常使用父元素(一般是一个div)的appendChild方法来添加icon。

       在React中,定位一个元素通常使用如下的方式:

const ref = useRef()
......
<div ref={ref} />

       然后再添加子节点,使用ref.current.appendChild(node)。只不过在我们的钱包中,账号详情界面是使用一个Dialog组件来实现的。由于未知原因,在Dialog组件里子元素的ref属性无法绑定(Dialog组件本身也不行),所以也就无法使用上面的方法来显示账号icon。我们采取了一个绕行的办法:先将节点对象转换成字符串,然后再设置父对象的innerHTML属性。下面列出相关的代码:

  1. 产生icon的代码
import {useMemo} from 'react';
import Jazzicon from 'jazzicon'

export function useAddressIcon(address,size) {
  return useMemo(() => {
      return Jazzicon(size, parseInt(address.slice(2, 8), 100))
  }, [address,size])
}

       从代码中可以看出,给定一个地址和icon大小,它都会返回一个节点对象。因为使用了useMemo,所以如果作为参数的地址和大小不变,它直接返回上次的返回值,并不重新计算。

  1. 节点对象转字符串,这个参考了别人的实现
//将一个html节点对象转换成字符串
function nodeToString(node) {
    let tmpNode = document.createElement( "div" );
    tmpNode.appendChild( node.cloneNode( true ) );
    let str = tmpNode.innerHTML;
    tmpNode = node = null; // prevent memory leaks in IE
    return str;
}
  1. 将Icon字符串在div中显示
<div dangerouslySetInnerHTML={{__html: nodeToString(icon)}}  />

       这里对比 MetaMask还有一个小小的不同,这是MetaMask的账号详情界面:
在这里插入图片描述
       可以看到账号icon有一半是显示在详情底层paper的上方。因为我们这里直接使用了Dialog组件,将icon移出paper的边框后就无法再显示,所以该icon并未上移。还希望各位读者指出问题的原因或者给出解决的办法。

三、二维码的实现

       二维码的实现很简单,使用qrcode-react这个库即可。下面是相关代码:

import QRCode from 'qrcode-react';
......
<QRCode size={150} value={ETHEREUM_PREv + address}/>

       直接设置size属性和显示的value就OK了。

四、自定义Material UI组件的外观

       从最开始的计划图中我们可以看到,点击按钮后菜单背景颜色为黑色透明,而菜单默认背景是白色的。这需要对Material UI组件的一些属性进行修改,这里是官方文档的自定义组件详细指南:https://material-ui.com/zh/customization/components/

       我们这里采取文档中提到的用类覆盖样式这种方法。应用到本次开发中就是更改菜单的背景颜色。先打开菜单组件的API指南:https://material-ui.com/zh/api/menu/。在页面的最下方,列出了可以重写的CSS样式表规则:
在这里插入图片描述
       可以看到,paper规则是应用于Paper元素的(包括了菜单背景),所以我们需要设置它,相关代码为:

const useStyles = makeStyles(theme => ({
    menuPaper:{
        border: '1px solid #d3d4d5',
        backgroundColor:"#000000BB",
        color:'white'
    }
}));
......
const classes = useStyles();
......
<Menu
    id="customized-menu-account"
    anchorEl={anchorRefAccount.current}
    keepMounted
    open={state.accountOpen}
    onClose={handleCloseAccount}
    onKeyDown={handleListKeyDownAccount}
    classes={{
        paper:classes.menuPaper
    }}
    elevation={0}
    getContentAnchorEl={null}
    anchorOrigin={{
      vertical: 'bottom',
      horizontal: 'right',
    }}
    transformOrigin={{
      vertical: 'top',
      horizontal: 'right',
    }}
>

       这里菜单组件设置了很多属性,大家可以在菜单API里看各属性的具体含义,链接上面已经给出。现在让我们只关注classes属性,这里将paper规则设置成为我们预设的名为menuPaper样式。

五、其它细节

  1. 因为导出账号时需要验证密码,为了简化,我们将账号登录(包括创建账号、导入账号)时的密码保存在全局变量中,也就是GlobalProvider.js中,使用时直接比较。
  2. 我们新建了一个hooks\index.js用来存放用户自定义Hook,比如生成地址Icon的Hook等。它和工具类utils\index.js是不同的,需要分开。
  3. 因为我们的钱包在桌面端只占web页面中间一小部分,此时消息条显示在左下角有些远。所以桌面端我们的消息条改成显示在下方中间的位置。
  4. 账号导出就是直接把钱包的私钥或者助记词显示出来。当然也可以进一步拓展,选择导出为一个加密json钱包,这里并未列入开发计划。

六、总结

       本次开发我们实现了账号导出的功能,并且更改了弹出菜单的背景颜色。应用的知识点主要是地址Icon的生成和添加、二维码的生成和用类覆盖样式来自定义组件外观等。

       在开发中也遇到了两个问题:一是Dialog组件子元素里(也包括它本身)无法绑定ref属性。二是Dialog组件里子元素超过了底层Paper的边框就无法显示。由于时间问题,没有进一步深入研究,留到以后有空了慢慢来看。也希望有经验的读者能够指出根源所在或者给出解决办法,不胜感激。

       下一次开发我们计划实现用户添加ERC20代币的功能。

       本钱包码云(gitee)仓库地址为: => https://gitee.com/TianCaoJiangLin/khwallet

       欢迎读者留言指出错误或者提出改进意见。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AiMateZero

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值