Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >web3钱包接入之viem

web3钱包接入之viem

作者头像
Maic
发布于 2025-02-12 06:11:37
发布于 2025-02-12 06:11:37
17500
代码可运行
举报
文章被收录于专栏:Web技术学苑Web技术学苑
运行总次数:0
代码可运行

过去一年,接入了很多第三方钱包,有solana,rainbow,web3Modal【现在是reown】了、aptos相关区块链钱包,第三方钱包接入已经相对非常成熟,API通常来讲非常简易,基本不用考虑更底层的API,常用的hook也会更高效,但通常接入钱包过程中,似乎总有种,知其然而不知所以然的感觉,本文是一篇钱包偏底层的接入流程。

本文主要以evm钱包为例子

首先我们要初步了解viem,我们在项目中,你也许会看到大量的wagmi,实际上这是对viem更上层的封装,wagmi是以太坊交互官方封装便捷好用的react库,让你非常快捷方便的与钱包以及以太坊交互,但本质上所有与以太坊交互的hook依赖于viem

在开始本文之前,你的浏览器得提前安装一个metaMask插件钱包

查看区块block

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 
// test.js
import { createPublicClient, http } from "viem";
import { mainnet } from "viem/chains";
const client = createPublicClient({
  chain: mainnet,
  transport: http(),
});
console.log(client);
const getBlock = async () => {
  const blockNumber = await client.getBlockNumber();
  console.log(blockNumber);
};
getBlock();

当我们运行node test.js

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 
{
  account: undefined,
  batch: undefined,
  cacheTime: 4000,
  ccipRead: undefined,
  chain: {
    formatters: undefined,
    fees: undefined,
    serializers: undefined,
    id: 1,
    name: 'Ethereum',
    nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
    rpcUrls: { default: [Object] },
    blockExplorers: { default: [Object] },
    contracts: {
      ensRegistry: [Object],
      ensUniversalResolver: [Object],
      multicall3: [Object]
    }
  },
  key: 'public',
  name: 'Public Client',
  pollingInterval: 4000,
  request: [AsyncFunction (anonymous)],
  transport: {
    key: 'http',
    name: 'HTTP JSON-RPC',
    request: [AsyncFunction: request],
    retryCount: 3,
    retryDelay: 150,
    timeout: 10000,
    type: 'http',
    fetchOptions: undefined,
    url: 'https://cloudflare-eth.com'
  },
  type: 'publicClient',
  uid: '2523e6fdcf6',
  extend: [Function (anonymous)],
  call: [Function: call],
  createBlockFilter: [Function: createBlockFilter],
  createContractEventFilter: [Function: createContractEventFilter],
  createEventFilter: [Function: createEventFilter],
  createPendingTransactionFilter: [Function: createPendingTransactionFilter],
  estimateContractGas: [Function: estimateContractGas],
  estimateGas: [Function: estimateGas],
  getBalance: [Function: getBalance],
  getBlobBaseFee: [Function: getBlobBaseFee],
  getBlock: [Function: getBlock],
  getBlockNumber: [Function: getBlockNumber],
  getBlockTransactionCount: [Function: getBlockTransactionCount],
  getBytecode: [Function: getBytecode],
  getChainId: [Function: getChainId],
  getCode: [Function: getCode],
  getContractEvents: [Function: getContractEvents],
  getEip712Domain: [Function: getEip712Domain],
  getEnsAddress: [Function: getEnsAddress],
  getEnsAvatar: [Function: getEnsAvatar],
  getEnsName: [Function: getEnsName],
  getEnsResolver: [Function: getEnsResolver],
  getEnsText: [Function: getEnsText],
  getFeeHistory: [Function: getFeeHistory],
  estimateFeesPerGas: [Function: estimateFeesPerGas],
  getFilterChanges: [Function: getFilterChanges],
  getFilterLogs: [Function: getFilterLogs],
  getGasPrice: [Function: getGasPrice],
  getLogs: [Function: getLogs],
  getProof: [Function: getProof],
  estimateMaxPriorityFeePerGas: [Function: estimateMaxPriorityFeePerGas],
  getStorageAt: [Function: getStorageAt],
  getTransaction: [Function: getTransaction],
  getTransactionConfirmations: [Function: getTransactionConfirmations],
  getTransactionCount: [Function: getTransactionCount],
  getTransactionReceipt: [Function: getTransactionReceipt],
  multicall: [Function: multicall],
  prepareTransactionRequest: [Function: prepareTransactionRequest],
  readContract: [Function: readContract],
  sendRawTransaction: [Function: sendRawTransaction],
  simulateContract: [Function: simulateContract],
  verifyMessage: [Function: verifyMessage],
  verifySiweMessage: [Function: verifySiweMessage],
  verifyTypedData: [Function: verifyTypedData],
  uninstallFilter: [Function: uninstallFilter],
  waitForTransactionReceipt: [Function: waitForTransactionReceipt],
  watchBlocks: [Function: watchBlocks],
  watchBlockNumber: [Function: watchBlockNumber],
  watchContractEvent: [Function: watchContractEvent],
  watchEvent: [Function: watchEvent],
  watchPendingTransactions: [Function: watchPendingTransactions]
}

21074983n

我们发现client这个对象返回出了很多与以太坊交互的接口

http()其实会默认选择rpc就是cloudflare-eth.comcontracts也提供了一个默认的address,默认的交易对就是ETH,精度18

连接钱包

我们知道与以太坊交互去中心化,所有的交易账户需要一个账号与链上产生交互,所以需要链接钱包,当我们插件安装了metaMask后,浏览器的window对象就已经绑定了ethereum这个对象,所以基本上所有与钱包交互的接口的前提是你必须在插件商店安装对应的一个钱包才行

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 
   function App() {
        const handleConnetWallet = async () => {
        const [account] = await window.ethereum.request({ method: 'eth_requestAccounts' });
        console.log(account, '==account')
      }
  
   return (<div>
            <h1 onClick={handleConnetWallet}>Content Wallet</h1>
      </div>);
   }

当前的web端唤起小狐狸钱包或者其他钱包,基本就是依靠这个window.ethereum.request这个接口

我们注意到一个钱包有很多个账号,而且我们默认的就是ETH主网,因为我们的钱包会默认选择一个ETH主网,当你选择确认对应钱包后,此时的account就是我们选择的钱包地址

SignMessage

通常我们在连接钱包后,我们需要做一个签名,这个签名一般由客户端主动发起,当签名成功后,就会获取一个hash,然后这个hash一般会是通过一个后端登录的接口进行验证这个账户

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 
import  {memo, useRef} from "react";
...
function App() {
     const accountRef = useRef(null);
    const handleSignMessage = async () => {
    const walletClient = createWalletClient({
        account: accountRef.current,
        chain: mainnet,
        transport:custom(window.ethereum),
    });
    const hash = await walletClient.signMessage({
        message: "hello world"
    });
    console.log(hash);
  }
  const handleConnetWallet = async () => {
    const [account] = await window.ethereum.request({ method: 'eth_requestAccounts' });
    accountRef.current = account;
  }
  return (<div>
     <h1 onClick={handleConnetWallet}>Content Wallet</h1>
     <h1 onClick={handleSignMessage}>SignMessage</h1>
  </div>)
}

注意签名的前提必须是你已经链接钱包

返回的hash是这这样的

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 
0xaeedca0a7745ee7dbbabdf69226dd7c27aa0f17c8bdcf58616ced8f1b9fe7bc60da3fcdb850af23d1379c2a0b57349862db2010ceb50f8a33322aed68119b14b1c

我们会发现签名值的变化与message这个传入的字符串有关,如果这个字符串发生变化,那么签名后的hash就不回一样,反之当message不发生变化时,产生的hash是不会变化的

isAddress

主要是判断这个钱包地址是否是eth地址

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 
import {isAddress} from "viem";
...
function App() {
       const [address, setIsAddress] = useState("");
      const [isEthAddress, setIsEthAddress] = useState(false);
     const handleVerifyAddress = () => {
            const isEthAddress = isAddress(address);
            setIsEthAddress(isEthAddress);
      }
    return <>
        <div>
            <input type="text" onChange={(e) => setIsAddress(e.target.value)} />
            <button onClick={handleVerifyAddress}>Verify address</button>
            <p>{address} {isEthAddress ? 'is': 'is not'} eth address</p>
        </div>
    <>
}

sendTransaction

可以理解成一个钱包地址向另外一个地址转账,在操作前,必须有SignMessage操作

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 
  ...
  const handleSendTransaction = async () => {
    const res = await wallect.current.sendTransaction({
        to: "0x5A39756bAE97685917a292B33ddFb2B54DF1e806",
        value: parseEther("0.001"), // 0.001ETH
    });
    console.log(res);
  }
  return (<>
      <h1 onClick={handleSendTransaction}>sendTransaction</h1>
  </>)

你会发现,我发送2.66$,所需要的gas费用就是0.67$,所以实际上你在钱包中发起的一笔转账,大概总计就需要3.33$

如果你把实际转账的参数改成这样

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 
  const handleSendTransaction = async () => {
    const res = await wallect.current.sendTransaction({
        to: "0x5A39756bAE97685917a292B33ddFb2B54DF1e806",
        value: parseEther("1"), // 1 ETH
    });
    console.log(res);
  }
  return (<>
       <h1 onClick={handleSendTransaction}>sendTransaction</h1>
  </>)

你会发现gas费用是动态变化的,但是跟你当下交易的笔数的大小无关,gas费每次都会变化,如果多笔转账合并,那么会减少不少的gas费用

获取公共操作

主要是查询用户账户余额、检索当前区块号,或者检索当前的交易信息,查询这些并不需要签名等权限

  • getBalance
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 
import {createPublicClient, http, createWalletClient, custom, isAddress, parseEther, formatUnits } from "viem"

import { mainnet, bsc } from "viem/chains";

const client = createPublicClient({
    chain: bsc, // bsc链
    transport: http(),
 })
 
 function App() {
      const handleGetBlance = async () => {
          const res = await client.getBalance({
              address: "0x6c85e349A70791e95fD6D9D5e066C6Ec136E0a92"
          });
           console.log(`余额:${formatUnits(res, 18)} BNB`);
      }
     return <div>
         <div onClick={handleGetBlance}>get Blance</div>
     </div>
 }

我们发现对于获取后的余额实际上精度是18位的,最终我们使用viem中的formatUnits帮我们处理成了与钱包余额一样的余额数值

  • getTransactionReceipt 主要是根本交易hash能查询当前的交易状态,比如
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 
 ...
 const handleGetTranstion = async () => {
    const res = await client.getTransactionReceipt({
        hash: "0xf6914af778b18ca1be98adead61af6351862fe26faad7e64d631df30fe925cd4"
    });
 console.log(res);
 function App() {
     return <>
             <h1 onClick={handleGetTranstion}>get transition hash</h1>
     </>
 }
  

我们会查询这个这交易hash的在区块链的实际交易状态,实际上你在https://bscscan.com/ 中根据交易hash也可查询看到对应的状态

至此一个从一个连接钱包登录,到签名,再到sendTransition的基础功能就基本结束,viem也提供了如何与以太坊合约交互的接口,这些都会有一个ABI的数据相关联,后续也会写个测试网合约进行调试,更多的详细的与以太坊交互的参考viem 文档

总结

  • 了解学习到使用更低层的viem官方接口唤起钱包,进行钱包连接签名sendTranstion操作
  • viem开放出来的公用区块查询,比如钱包余额,区块,以及交易状态
  • code example
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-10-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Web技术学苑 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
探索 JQuery EasyUI:构建简单易用的前端页面
当我们站在网页开发的浩瀚世界中,眼花缭乱的选择让我们难以抉择。而就在这纷繁复杂的技术海洋中,JQuery EasyUI 如一位指路明灯,为我们提供了一条清晰的航线。
繁依Fanyi
2024/04/25
8040
jQuery EasyUI一个基于 jQuery 的框架(创建网页所需的一切)
本文主要介绍了EasyUI中的一些组件和模块,包括表格、日历、表单、对话框、颜色选择器等。通过这些组件和模块,可以快速搭建出各种类型的应用程序。同时,还介绍了一些EasyUI中的实用工具,包括easyui-lang-zh、easyui-validate、easyui-component-extend等。这些工具可以帮助开发者更加灵活地开发和管理系统。
别先生
2018/01/02
4.4K1
jQuery EasyUI一个基于 jQuery 的框架(创建网页所需的一切)
EasyUI 基础组件
本案例主要针对EasyUI常用的panel,window,dialog做了下简要的讲解,没有把window的图上传,敬请大家自己敲下代码实现,比较简单,主要内容针对基金系统作了下应用。
张哥编程
2024/12/17
830
EasyUI 基础组件
EasyUI之Tabs基本用法 原
(adsbygoogle = window.adsbygoogle || []).push({});
tianyawhl
2019/04/04
2.2K0
JQuery EasyUI 案例加使用
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/ht
用户5927264
2019/07/31
6930
初试JqueryEasyUI(附Demo)[通俗易懂]
  关于easyui不多说,对于我们这样没有美术功底的程序员来说,简直是大大的福利,园里面也有不少人在用,自己在整理一个个站,后台管理要用,正好可以学习下。网上找了相关的教程什么的,但是发现还是官网上的demo讲的狠详细,还有就是下载后的demo示例,但都是某个控件示例,很遗憾,没有整个系统的demo。
全栈程序员站长
2022/09/07
2K0
初试JqueryEasyUI(附Demo)[通俗易懂]
【Java框架型项目从入门到装逼】第八节 - 用EasyUI绘制主界面
1.引入资源包 在上一节中,我们把基本的框架都搭好了,用了Spring,SPringMVC。这一节,我们先来画页面,前端框架采用EasyUI来实现。 easyui是一种基于jQuery的用户界面插件
剽悍一小兔
2018/05/17
9680
easyui+nodejs+sqlserver增删改查实现
用到的模块或者技术: Express: http://www.expressjs.com.cn/4x/api.html#express Easyui: http://www.jeasyui.com
用户1141560
2017/12/26
3.1K0
easyui+nodejs+sqlserver增删改查实现
【ssm个人博客项目实战02】easy UI搭建后台管理界面基于easy UI搭建后台界面
前面一节我们已经成功搭建ssm项目的环境,接下来我们需要做的就是搭建我们后台管理界面的框架。 这搭建完之后的效果图
yukong
2018/08/21
1.6K0
【ssm个人博客项目实战02】easy UI搭建后台管理界面基于easy UI搭建后台界面
MVC5+EasyUI+EF6增删改查以及登录登出的演示
创建StudentController、 及Index视图, 在Index上按F5运行
明志德道
2023/10/21
2790
MVC5+EasyUI+EF6增删改查以及登录登出的演示
最新jquery+easyui_api培训文档
1 Accordion(可折叠标签) 1.1 实例 1.1.1 代码 <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>jQuery EasyUI</title> <link rel="stylesheet" type="text/css" href="../themes/default/easyui.css"> <link rel="stylesheet" type="text
用户1112962
2018/07/03
3.3K0
Spring Boot 之整合 EasyUI 打造 Web 应用
EasyUI 下载地址:http://www.jeasyui.cn/download.html
静默虚空
2022/03/23
8130
ASP.NET MVC5+EF6+EasyUI 后台管理系统(82)-Easyui Datagrid批量操作(编辑,删除,添加)
前言 有时候我们的后台系统表单比较复杂,做过进销存或者一些销售订单的都应该有过感觉 虽然Easyui Datagrid提供了行内编辑,但是不够灵活,但是我们稍微修改一下来达到批量编辑,批量删除,批
用户1149182
2018/01/12
1.8K0
ASP.NET MVC5+EF6+EasyUI 后台管理系统(82)-Easyui Datagrid批量操作(编辑,删除,添加)
【Jqurey EasyUI+Asp.net】—DataGrid增加、删、更改、搜
在前面写了两,但不知道如何完成,对比刚刚开始学这个,他们摸着石头过河,一步步。在最后两天DataGridCRUD融合在一起。因此份额。我希望像我这样谁是刚刚开始学习一些帮助。
全栈程序员站长
2022/07/06
1.4K0
Web-第十六天 EasyUI【悟空教程】
使用easyui你不需要写很多代码,你只需要通过编写一些简单HTML标记,就可以定义用户界面。
Java帮帮
2018/07/27
1.3K0
Web-第十六天 EasyUI【悟空教程】
基于asp.net + easyui框架,一步步学习easyui-datagrid——界面(一)
从这篇博客,我会一步步的为大家讲解,easyui框架中最常用的一个控件datagrid。在使用easyui框架时,datagrid是使用最多的控件,它不仅好用,关键是实用。
程序猿小亮
2021/01/29
1.1K0
easyui常用组件
表单 日期选择(html初始化) <input name="tadminModel.birthday" class="easyui-validatebox Wdate" style="width: 370px;" onclick="WdatePicker({dateFmt:'yyyy-MM-dd',position:{right:0,top:0}})"/> 按钮(html初始化) <a type="button" href="javascript:;" class="easyui-linkbutton" i
码客说
2019/10/22
2.4K0
SpringMVC+easyUI CRUD 添加数据C
接一篇文章,今天上午实现了添加数据。以下是Jsp。里面主要是看newUser()和saveUser().注意这函数里的url,newUser()里面去掉url属性。还要注意的一个问题
全栈程序员站长
2022/07/08
2460
jquery easyui datagrid 保存/加载自定义配置每列属性
直接附上源代码 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Format DataGrid Columns - jQuery EasyUI Demo</title> <link rel="stylesheet" type="text/css" href="css/material/easyui.css"> <script type="text/javascript" src="js/jquery.mi
阿新
2019/10/06
1.3K0
golang学习之beego框架配合easyui实现增删改查及图片上传
demo目录: upload文件夹主要放置上传的头像文件,main是主文件,所有效果如下: 主页面: 具体代码: <!DOCTYPE html> <html lang="en"> <head>
用户1141560
2017/12/26
1.9K0
golang学习之beego框架配合easyui实现增删改查及图片上传
推荐阅读
相关推荐
探索 JQuery EasyUI:构建简单易用的前端页面
更多 >
领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档