新闻开发人员企业区块链解释事件和会议新闻时事通讯
订阅我们的新闻.
电子邮件地址
我们尊重您的隐私
主页博客区块链开发
如何使用React和SWR从以太坊获取和更新数据
这是配置dapp前端的方法,以便在用户的以太坊钱包中更新代币余额和资金转账.Lorenzo Sicilia 2020年6月18日发布于2020年6月18日
以太坊允许我们构建去中心化应用程序(dapps)。典型应用程序和dapp之间的主要区别在于,您无需部署后端-至少只要您利用以太坊主网上部署的其他智能合约即可.
因此,前端起着重要的作用。它负责对智能合约中的数据进行封送和封送处理,处理与钱包(硬件或软件)的交互,并照常管理UX。不仅如此,根据设计,dapp还使用JSON-RPC调用,并且可以打开套接字连接以接收更新。.
如您所见,有一些事情需要协调,但不用担心,在过去的几个月中,生态系统已经相当成熟.
先决条件
在本教程中,我将假定您已经具备以下条件:
连接到Geth节点的钱包
最简单的方法是 安装MetaMask 这样你就可以使用 Infura 开箱即用的基础架构.
您帐户中的一些以太币
当您使用以太坊进行开发时,强烈建议您切换到测试网并使用测试以太坊。如果您需要资金进行测试,则可以使用水龙头,例如. https://faucet.rinkeby.io/
对React的基本了解
我将逐步指导您,但我假设您知道React的工作原理(包括钩子)。如果有什么不熟悉的地方,请咨询 反应文档.
一个正在运转的React游乐场
我使用Typescript编写了本教程,但是只键入了几样内容,因此只需进行最小的更改,就可以像使用Javascript一样使用它。我用了 Parcel.js 但随时使用 创建React应用 还是其他Web应用程序捆绑软件.
连接以太坊主网
一旦您准备好MetaMask,我们将使用 Web3-反应 处理与网络的交互。它将为您提供一个方便的钩子useWeb3React,其中包含许多用于以太坊的有用实用程序.
纱线添加@ web3-react / core @ web3-react / injected-connector代码语言:CSS(css)
然后,您需要提供者。提供商抽象出与以太坊区块链的连接,用于发出查询并发送已签名的状态改变交易.
我们将使用来自 Ether.js.
它似乎已经有了一些库,但是当与以太坊进行交互时,您需要将Javascript数据类型转换为Solidity数据类型。并且,当您要执行操作时,还要求您签署交易。 Ether.js优雅地提供了这些功能.
毛线添加@ ethersproject / providers代码语言:CSS(css)
注意:上面的Ether.js软件包是当前的v5 贝塔.
之后,我们准备记下最小的hello世界,以检查我们是否拥有所需的一切:
从’react’导入React从’@ web3-react / core’导入{Web3ReactProvider}从’@ ethersproject / providers’导入{Web3Provider}从’@ web3-react / core’导入{useWeb3React}从’@导入{InjectedConnector} web3-react / injected-connector’export const injectionConnector = new InjectedConnector({supportedChainIds:[1,// Mainet 3,// Ropsten 4,// Rinkeby 5,// Goerli 42,// Kovan],})函数getLibrary (提供者:任意):Web3Provider {const库=新的Web3Provider(provider)library.pollingInterval = 12000返回库} export const Wallet =()=> {const {chainId,帐户,激活,活动中} = useWeb3React()const onClick =()=> {activate(injectedConnector)} return( <股利> <股利>ChainId:{chainId} div> <股利>帐户:{account} div> {积极的 ? ( <股利>✅div> ):( <按钮类型="按钮" onClick = {onClick}> 连接按钮> )} div> }}导出const App =()=> { 返回 ( <Web3ReactProvider getLibrary = {getLibrary}> <钱包 /> Web3ReactProvider> )}代码语言:JavaScript(javascript)
如果您完成家庭作业,则应该有以下内容:
这是我们到目前为止所做的: GIT –步骤1
如何从主网上获取数据
我会用 驻波比 管理数据提取.
这就是我要实现的.
const {data:balance} = useSWR(["getBalance", 帐户, "最新的"])代码语言:JavaScript(javascript)
很酷 &#128578;
让我们揭开这个窍门! SWR表示Stale-While-Revalidate,这是一种HTTP缓存无效化策略,由 RFC 5861.
SWR首先从高速缓存中返回数据(陈旧),然后发送获取请求(重新验证),最后再次提供最新数据.
SWR接受了一把钥匙,幕后将设法解决
为此,SWR允许传递能够通过返回承诺来解决密钥的访存程序。 SWR的问候世界基于REST API请求以及基于fetch API或Axios的访存程序.
SWR的杰出之处在于,创建提取程序的唯一要求是必须返回承诺.
所以这是我对以太坊的访存器的第一个实现:
const fetcher =(库)=> (… args)=> {const [method,… params] = args console.log(method,params)返回库[method](… params)}代码语言:JavaScript(javascript)
如您所见,它是部分应用的功能。这样,我可以在配置提取程序时注入库(Web3Provider)。之后,每次更改键,都可以通过返回所需的Promise来解决该功能。.
现在我可以创建我的组件
export const余额=()=> {const {帐户,库} = useWeb3React()const {数据:余额} = useSWR([‘getBalance’,account,’latest’],{fetcher:fetcher(library),})if(!balance){return <股利>…股利> } 返回 <股利>余额:{balance.toString()} div> }代码语言:JavaScript(javascript)
返回的余额对象是一个BigNumber.
如您所见,该数字未格式化且非常大。这是因为Solidity最多使用256位整数.
为了以人类可读的格式显示数字,该解决方案使用了Ether.js实用工具中的上述实用工具之一:formatEther(balance)
毛线安装@ ethersproject / units代码语言:CSS(css)
现在我可以重做我的组件,以人类可读的形式处理和格式化BitInt:
export const余额=()=> {const {帐户,库} = useWeb3React()const {数据:余额} = useSWR([‘getBalance’,account,’latest’],{fetcher:fetcher(library),})if(!balance){return <股利>…股利> } 返回 <股利>Ξ{parseFloat(formatEther(balance))。toPrecision(4)} div> }代码语言:JavaScript(javascript)
这是我们到目前为止所做的: GIT步骤2
如何实时更新数据
SWR公开了一个mutate功能来更新其内部缓存.
const {data:balance,mutate} = useSWR([‘getBalance’,account,’latest’],{fetcher:fetcher(library),})const onClick =()=> {mutate(new BigNumber(10),false)}代码语言:JavaScript(javascript)
mutate函数自动绑定到生成密钥的键(例如[‘getBalance’,account,’latest’]。它接受两个参数。新数据以及是否应触发验证。如果是,则SWR会自动使用提取程序来更新缓存 &#128165;
正如预期的那样,Solidity事件在EVM的日志记录功能之上提供了一个很小的抽象。应用程序可以通过以太坊客户端的RPC接口订阅和侦听这些事件.
Ether.js有一个简单的API可以订阅事件:
const {account,library} = useWeb3React()library.on("blockNumber", (blockNumber)=> {console.log({blockNumber})})代码语言:JavaScript(javascript)
现在让我们在新组件中结合这两种方法
export const余额=()=> {const {帐户,库} = useWeb3React()const {数据:余额,变异} = useSWR([‘getBalance’,account,’latest’],{fetcher:fetcher(library),})useEffect(()=> {//在以太坊地址控制台上监听更改。log(`listing for blocks …`)library.on(’block’,()=> {console.log(’update balance …’)mutate(undefined,true)})//卸载组件时删除侦听器return()=> {library.removeAllListeners(’block’)} //仅在组件安装时触发效果},[])if(!balance){return <股利>…股利> } 返回 <股利>Ξ{parseFloat(formatEther(balance))。toPrecision(4)} div> }代码语言:JavaScript(javascript)
最初,SWR将提取帐户余额,然后每次收到冻结事件时,它将使用mutate触发重新提取.
注意:我们使用了mutate(undefined,true),因为我们无法从当前事件中检索到实际余额,我们只是触发了余额的重新提取.
以下是两个正在交换一些ETH的以太坊钱包的快速演示.
这是我们到目前为止所做的: GIT步骤3
如何与智能合约进行交互
到目前为止,我们已经说明了使用SWR的基础知识以及如何通过Web3Provider进行基本调用。现在,让我们发现如何与智能合约进行交互.
Ether.js使用由Solidity Compiler生成的Contract Application Binary Interface(ABI)ABI处理智能合约交互.
合约应用程序二进制接口(ABI)是在以太坊生态系统中与合约进行交互的标准方法,既可以从区块链外部进行,也可以用于合约间的交互.
例如,给出以下简单的智能合约:
语用强度^ 0.5.0;合同测试{构造函数()公共{b =十六进制"12345678901234567890123456789012"; }事件Event(uint索引a,bytes32 b);事件Event2(uint索引a,bytes32 b);函数foo(uint a)public {发出Event(a,b); } bytes32 b; }代码语言:JavaScript(javascript)
这是ABI生成的
[{ "类型": "事件", "输入":[{ "姓名": "一种", "类型": "uint256", "索引": 真的 }, { "姓名": "b", "类型": "字节32", "索引": 错误的 } ], "姓名": "事件" },{ "类型": "事件", "输入":[{ "姓名": "一种", "类型": "uint256", "索引": 真的 }, { "姓名": "b", "类型": "字节32", "索引": 错误的 } ], "姓名": "赛事2" },{ "类型": "功能", "输入":[{ "姓名": "一种", "类型": "uint256" }], "姓名": "富", "输出":[]}]代码语言:JSON /带有注释的JSON(json)
要使用ABI,我们可以直接将它们直接复制到您的代码中,然后将其导入到需要的地方。在本演示中,我们将使用标准 ERC20 ABI,因为我们要检索两个代币的余额:DAI和MKR.
下一步是创建组件
export const TokenBalance =({符号,地址,小数点})=> {const {account,library} = useWeb3React()const {data:balance,mutate} = useSWR([address,’balanceOf’,account],{fetcher:fetcher(library,ERC20ABI),})useEffect(()=> {//在以太坊地址控制台上监听更改。log(`监听转移…`)const contract = new Contract(address,ERC20ABI,library.getSigner())const fromMe = contract.filters.Transfer(account, null)library.on(fromMe,(from,to,amount,event)=> {console.log(’Transfer | sent’,{from,to,amount,event})mutate(undefined,true)})const toMe = contract.filters.Transfer(null,account)library.on(toMe,(from ,至,金额,事件)=> {console.log(’Transfer | received’,{从,到,金额,事件})mutate(undefined,true)})//卸载组件时删除侦听器return()=> {library.removeAllListeners(toMe)library.removeAllListeners(fromMe)} //仅在组件安装时触发效果},[])if(!balance){return <div>…股利> } 返回 ( <div> {parseFloat(formatUnits(balance,decimals))。toPrecision(4)} {symbol} div> )}代码语言:JavaScript(javascript)
让我们放大。有两个主要区别:
按键定义
useSWR([address,’balanceOf’,account]))使用的密钥需要以太坊地址开头,而不是方法开头。因此,提取程序可以识别我们要实现的目标并使用ABI.
让我们相应地重构提取程序:
const fetcher =(库:Web3Provider,abi ?:任何)=> (… args)=> {const [arg1,arg2,… params] = args //如果是(isAddress(arg1)){const address = arg1 const method = arg2 const contract = new Contract(address,abi,library.getSigner() )return contract [method](… params)} //这是一个eth调用const method = arg1 return library [method](arg2,… params)}代码语言:JavaScript(javascript)
现在我们有一个通用的访存器,能够与以太坊的JSON-RPC调用进行交互. &#128588;
日志过滤器
另一个方面是如何监听ERC20事件。 Ether.js提供了一种方便的方法来根据事件的主题和名称来配置过滤器。有关主题是什么的更多信息,请参见 实体文档.
const contract = new Contract(address,ERC20ABI,library.getSigner())const fromMe = contract.filters.Transfer(account,null)代码语言:JavaScript(javascript)
使用ABI构建合同实例后,就可以将过滤器传递给库实例.
警告:
您可能会尝试直接使用ERC20事件中的金额来增加或减少余额.
注意龙。设置提取程序时,您将Clojure作为回调传递给on函数,其中包含当时的余额.
可以使用useRef进行修复,但是为了简单起见,让我们重新验证缓存以确保余额是新鲜的:mutate(undefined,true)
现在,我们拥有了所需的所有部件。最后一点是胶水.
我配置了一些常量,以便有一种很好的方式将TokenBalance组件映射到令牌列表,具体取决于我们工作的网络:
export const Networks = {MainNet:1,Rinkeby:4,Ropsten:3,Kovan:42}导出接口IERC20 {符号:字符串地址:字符串小数:数字名称:字符串} export const TOKENS_BY_NETWORK:{[key:数字]: IERC20 []} = {[Networks.Rinkeby]:[{地址: "0x5592EC0cfb4dbc12D3aB100b257153436a1f0FEa", 象征: "戴", 姓名: "戴", 小数:18,},{地址: "0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85", 象征: "MKR", 姓名: "制作者", 小数点:18,},],}代码语言:JavaScript(javascript)
一旦有了常量,就可以轻松地将配置的令牌映射到我的组件:
export const TokenList =({chainId})=> { 返回 ( <> {TOKENS_BY_NETWORK [chainId] .map((token)=> ( <TokenBalance键= {token.address} {… token} /> ))})}代码语言:JavaScript(javascript)
可以了,好了!现在我们有一个以太坊钱包,可以加载以太币和代币余额。并且,如果用户发送或接收资金,则钱包UI会更新.
到目前为止,这是我们所做的: GIT步骤4
重构
让我们将每个组件移动到一个单独的文件中,并使用SWRConfig提供程序使获取程序在全局范围内可用.
<SWRConfig值= {{fetcher:fetcher(library,ERC20ABI)}}> <EthBalance /> <令牌列表chainId = {chainId} /> <SWRConfig />代码语言:HTML,XML(xml)
使用SWRConfig,我们可以配置一些始终可用的选项,以便我们可以更方便地使用SWR.
const {data:balance,mutate} = useSWR([address,’balanceOf’,account])代码语言:JavaScript(javascript)
重构后的一切都在这里: GIT步骤5
包起来
驻波比 和 Ether.js 如果您想使用以太坊dapp简化数据获取策略,可以使用两个不错的库.
关键优势
- 声明式方法
- 数据始终通过Web套接字或SWR选项进行更新
- 避免使用自定义React上下文重塑状态管理的轮子
如果您在dapp中使用了多个智能合约,并且喜欢本教程,那么我将web3提取程序概括为一个小工具: 斯沃思 (明星表示赞赏 &#128123;)
最后,这是完整的GIT回购:(https://github.com/aboutlo/swr-eth-tutorial).
直接获取更多以太坊教程
订阅我们的时事通讯以获取最新的以太坊开发人员课程,工具,专业提示等. 订阅 订阅我们的时事通讯以获取最新的以太坊新闻,企业解决方案,开发人员资源等信息。网络研讨会
如何构建成功的区块链产品
网络研讨会
如何设置和运行以太坊节点
网络研讨会
如何构建自己的以太坊API
网络研讨会
如何创建社交令牌
网络研讨会
在智能合约开发中使用安全工具
网络研讨会