NO。7Web3世界使用Reactethers。js开发简单
互联网的技术日新月异,互联网不断深入人们的生活;
web3。0将是彻底改变人们生活的互联网形式;
web3。0使所有网上公民不再受到现有资源积累的限制;
具有更加平等地获得财富和声誉的机会。
web3。0会从哪里开始呢?
本合集文章,授权转载,侵权必究。
Web3。0世界系列文章
来源:代码与野兽
【Web3系列文章】
NO。1遇见Web3:在Web3的世界中写下第一行HelloWorld
NO。2全面系统的Web3学习路线,助你成为Web3开发专家
NO。3Web3世界:区块链、比特币、以太坊和智能合约
NO。4RemixIDE使用与VSCode搭建Solidity开发环境
NO。5深入聊聊Web3世界中的协议和硬盘:IPFS
NO。6一文聊透Solidity语法:助你成为智能合约专家
最近在开发一个NFT二创平台,其中包含了很多概念和技术。我会更新一个系列的文章来总结和沉淀在这个过程中的一些知识与思考。
本文是对ethers。js进行一个全方位介绍,非常适合web3入门学习。什么是ethers。js?
Web3中的各类DApp,都需要与智能合约进行交互。如果用原生JS来做这些事会很麻烦。这时就需要使用专属的SDK。
目前JS环境中有两个主流的库可以用来和智能合约进行交互,一个是web3。js,另一个是ethers。js。ethers。jsVSweb3。js
web3。js比ethers。js出现的更早。但是目前ethers。js更受欢迎。
主要原因有如下两点:体积:ethers。js体积仅有116。5kb,web3。js有590。6kb。相差5倍左右。设计理念:由于设计理念不同。web3。js认为用户会在本地部署以太坊节点,私钥和网络连接状态由这个节点管理。但实际上大部分人都不会在本地部署以太坊节点。ethers。js充分考虑了这一点,它是用Provider管理网络连接状态,用Wallet管理密钥,更加安全和灵活。而且原生支持ENS。ethers。js基本使用介绍节点即服务
由于在本地部署一个区块链节点的成本并不低,很少会有人真的部署一个节点,而是选择使用节点即服务。
这类服务有很多,比如老牌的Alchemy、Infura、Moralis以及今年估值102亿美金的新秀Tenderly。
在这里我们选择Alchemy,目前它的市场占有率是最高的。
alchemy的网址在这:dashboard。alchemy。com。具体的注册登陆就不讲了。
登陆之后我们创建一个App。
Chain选择Ethereum,Network选择Goerli。
这样就成功创建了一个App,点击后面的viewkey,就可以查看key。
我们把它复制下来,后面会用到。
构造合约
在对合约进行读取或交互之前,我们首先需要构造一个合约对象。
合约对象有三个构造参数,第一个是合约地址,是一个字符串。第二个是合约的abi,可以是一个JSON,第三个参数是provider对象,它用于管理网络连接状态。
下面的例子中就是使用alchemy提供的JSONRPC接口作为网络连接。constrpchttps:ethgoerli。g。alchemy。comv2{process。env。NEXTPUBLICALCHEMYAPIKEY};constprovidernewethers。providers。JsonRpcProvider(rpc);constcontractnewethers。Contract(contractAddress,abi,provider)
除了JsonRpcProvider以外,ethers还有IpcProvider、InfuraProvider、AlchemyProvider、Web3Provider等多种Provider。读取合约信息
abi中的方法会直接挂载到contract对象上,我们可以直接调用。
不过需要注意,所有的操作都是异步的。(async(){constownerawaitcontract。owner();console。log(owner);})();连接钱包
由于和合约交互需要支付gas费用,所以必须有一个数字钱包。
钱包有很多种,比如MetaMask、Rainbow、CoinbaseWallet等。其中MetaMask是最常用的一种数字钱包。
这里主要介绍如何连接到MetaMask。
MetaMask有一个浏览器插件,如果用户安装了该插件,在window对象下会有一个ethereum对象。我们可以调用ethereum。request方法发起请求,参数是一个对象,对象的method描述该次请求的操作。
ethereum。request方法是异步的,会返回一个数组,该数组是所有登陆钱包的账户地址字符串,第一个账户就是当前激活的账户。如果返回的数组长度为0,则意味着没有登陆任何账户。(async(){constaccountsawaitethereum。request({method:ethrequestAccounts})if(accounts。length0){throwError(未登录任何账户)}constactiveAccountaccounts〔0〕console。log(activeAccount)})()
我们还可以通过ethereum。request方法获取当前的网络状态。(async(){constchainIdawaitethereum。request({method:ethchainId})console。log(chainId)})()
它会返回一个字符串。0x1表示以太网主网;0x5表示Goerli测试网,更多网络的chainId可以在这个网站查看:chainlist。org。
下面是使用ethers。js来连接MetaMask的代码。(async(){constprovidernewethers。providers。Web3Provider(window。ethereum)constaccountsawaitprovider。send(ethrequestAccounts,〔〕)constactiveAccountaccounts〔0〕})()
如果使用MetaMask作为provider,那么就不需要再使用alchemy了。钱包
在转账交易之前,我们需要创建一个Wallet实例,它的作用是对交易和消息进行签名。
创建Wallet对象的方法有三种。
通过Wallet。createRandom创建随机钱包
这种方式创建的是一个单机钱包,需要连接网络。constwalletethers。Wallet。createRandom()wallet。connect(provider)
通过助记词创建constwalletnewethers。Wallet。fromMnemonic(mnenonic。phrase)
通过私钥和provider创建constwalletnewethers。Wallet(privateKey,provider)钱包信息
我们可以在创建好的钱包上面获取很多有用的信息,比如钱包地址、助记词、私钥、交易次数等。console。log(wallet。address)console。log(awaitwallet。getAddress())console。log(wallet。mnemonic)console。log(wallet。privateKey)console。log(wallet。getTransactionCount())转账
一旦又了钱包,我们就可以向其他人发起转账交易。
创建一个tx对象,它最少需要两个属性,to和value,分别表示接受钱包地址和转账额度。
然后使用wallet。sendTransaction方法发送转账,它会返回一个receipt对象。这个对象有一个异步的wait方法,当交易上链后会返回。consttx{to:address,value:ethers。utils。parseEther(0。1),}console。log(开始转账)constreceiptawaitwallet。sendTransaction(tx)awaitreceipt。wait()console。log(完成转账)通过合约转账交易
交易需要使用Wallet对象。再通过wallet作为合约的第三个构造参数创建Contract对象。
调用合约的transfer方法,进行转账交易。该方法需要两个参数,转入的钱包地址字符串和转入的数量。
transfer会返回一个tx对象,该对象有一个异步的wait方法,会在交易完成后执行。constwalletnewethers。Wallet(privateKey,provider)constcontractnewethers。Contract(contractAddress,abi,wallet)(async(){consttxawaitcontract。transfer(toAddress,ethers。utils。parseEther(1))awaittx。wait()})()通过signer进行转账
通常我们无法直接拿到privateKey,但是可以通过signer对象间接使用privateKey。只需要进行签名就可以进行交易。这也是最常用的交易方式。constsignerwalletProvider。getSigner();consttx{to,value,};constreceiptawaitsigner。sendTransaction(tx);awaitreceipt。wait();使用React和ethers。js开发加密钱包
接下来我们开发一个最简单的加密钱包,具备最基础的转账功能和查询余额功能。创建项目
我们首先创建一个Next。js项目。npxcreatenextapp
需要选择TypeScript。安装依赖安装ethers。jsnpmiethers安装tailwindcssnpminstallDtailwindcsspostcssautoprefixernpxtailwindcssinitp
修改tailwind。cinfig。js。type{import(tailwindcss)。Config}module。exports{content:〔。pages。{js,ts,jsx,tsx},。components。{js,ts,jsx,tsx},〕,theme:{extend:{},},plugins:〔〕,}
修改stylesglobals。css。tailwindbase;tailwindcomponents;tailwindutilities;安装headlessuinpminstallheadlessuireact整体架构设计
由于业务并不复杂,我们可以将它简单划分为几个组件。使用Context足够应对这个场景,而不需要额外导入状态管理库来增加复杂性。
Wallet是根组件,内部维护了很多state,以Context的方式将数据和操作注入到子组件。
Connect负责连接钱包和断开连接。
Details负责显示钱包信息,是纯展示型组件。
Transfer负责向其他账户进行转账。
Loading负责渲染加载动画,是纯展示型组件。创建上下文typeIWalletCtx{walletProvider:any;setWalletProvider:(walletProvider:any)void;msgIsOpen:boolean;setMsgIsOpen:(msgIsOpen:boolean)void;msg:string;setMsg:(msg:string)void;account:string;setAccount:(account:string)void;networkName:string;setNetworkName:(networkName:string)void;balance:string;setBalance:(balance:string)void;showMessage:(message:string)void;refresh:boolean;setRefresh:(refresh:boolean)void;};constWalletCtxcreateContextIWalletCtx({}asIWalletCtx);
通过初始化一个对象,然后断言为IWalletCtx的方式,可以避免在使用WalletCtx时添加是否为undefined或null的判断。因为我们一定会注入数据。Loading组件
Loading作为纯展示型组件,是最简单的组件。SVG的代码是直接从tailwindcss文档中搬运过来的。仅仅是添加了一个size属性,用来展示不同大小的尺寸。functionLoading({sizemd}:{size?:smmdlgxl}){constsizes{sm:h3w3,md:h5w5,lg:h7w7,xl:h9w9,};return(svgclassName{animatespinml1mr3{sizes〔size〕}textblack}xmlnshttp:www。w3。org2000svgfillnoneviewBox002424circleclassNameopacity25cx12cy12r10strokecurrentColorstrokeWidth4circlepathclassNameopacity75fillcurrentColordM412a8800188V0C5。373005。373012h4zm25。291A7。9627。962001412H0c03。0421。1355。82437。938l32。647zpathsvg);}Wallet组件
在Wallet组件中创建这些state,并注入到context中。exportdefaultfunctionWallet(){const〔walletProvider,setWalletProvider〕useState(null);const〔msgIsOpen,setMsgIsOpen〕useState(false);const〔msg,setMsg〕useState();const〔account,setAccount〕useStatestring();const〔networkName,setNetworkName〕useStatestring();const〔balance,setBalance〕useStatestring();const〔refresh,setRefresh〕useStateboolean(false);useEffect((){setWalletProvider(newethers。providers。Web3Provider(window。ethereum));},〔〕);constshowMessage(message:string){setMsg(message);setMsgIsOpen(true);setTimeout((){setMsg();setMsgIsOpen(false);},2000);};return(WalletCtx。Providervalue{{walletProvider,setWalletProvider,msgIsOpen,setMsgIsOpen,msg,setMsg,account,setAccount,networkName,setNetworkName,balance,setBalance,showMessage,refresh,setRefresh,}}Dialogopen{msgIsOpen}as{p}onClose{()setMsgIsOpen(false)}Dialog。PanelclassNameinlineflexflexcolbggreen400textslate600p4shadowxlrounded3xlDialog。Title{msg}Dialog。TitleDialog。PanelDialogConnectDetailsTransferWalletCtx。Provider);}
Wallet组件基本上没有什么逻辑,它的主要作用有三个:向context注入数据。创建ethers。provider。使用Dialog组件作为全局消息提示。Connect组件
在stylesglobals。css中添加按钮样式。layercomponents{。btn{applybgblacktextwhitepy2px4rounded3xl;}}
在index。tsx中编写逻辑。functionConnect(){const{walletProvider,account,setAccount,setNetworkName,setBalance,showMessage,refresh,}useContext(WalletCtx);constrefreshBalanceuseCallback(async(){if(!walletProvider!account)return;constbalanceawaitwalletProvider。getBalance(account);setBalance(ethers。utils。formatEther(balance));},〔setBalance,walletProvider,account〕);useEffect((){refreshBalance();},〔refresh,refreshBalance〕);constconnectToMetamaskasync(){try{awaitwindow。ethereum。enable();constaccountsawaitwalletProvider。send(ethrequestAccounts,〔〕);constnetworkawaitwalletProvider。getNetwork();constbalanceawaitwalletProvider。getBalance(accounts〔0〕);setAccount(accounts〔0〕);setNetworkName(network。name);setBalance(ethers。utils。formatEther(balance));}catch(error){console。log(error);showMessage(failedtoconnecttometamask);}};constdisconnectasync(){setAccount();};if(!account){return({walletProvider?(buttonclassNamebtnonClick{connectToMetamask}connecttometamaskbutton):(Loading)});}return(h1classNametextendHello,{account}h1buttonclassNamebtnonClick{disconnect}disconnectbutton);}
我们连接钱包后会获取3个重要的信息:钱包账户地址、连接的网络和余额。
分别通过walletProvider。listAccounts()、walletProvider。getNetwork()和walletProvider。getBalance(accounts〔0〕)来获取,需要注意它们都是异步操作。Details组件
Details作为纯展示型组件没有什么逻辑,主要是一些样式。functionDetails(){const{account,networkName,balance}useContext(WalletCtx);if(!account){returnnull;}return(balancenetwork:{networkName}{balance}ETH);}
现在我们看一下Connect和Details组件一起使用的效果。
Transfer组件
Transfer的功能比较简单,它在UI上仅仅包含两个输入框和一个send按钮。
两个输入框分别可以输入to和value,表示转账的钱包地址和转账金额。functionTransfer(){const{walletProvider,account,showMessage,refresh,setRefresh}useContext(WalletCtx);const〔to,setTo〕useStatestring();const〔amount,setAmount〕useStatestring();const〔transferring,setTransferring〕useStateboolean(false);consttransferasync(){try{constvalueethers。utils。parseEther(amount);constsignerwalletProvider。getSigner();consttx{to,value,};setTransferring(true);constreceiptawaitsigner。sendTransaction(tx);awaitreceipt。wait();setTo();setAmount();showMessage(successfullytransferred);}catch(error){console。log(error);showMessage(failedtotransfer);}finally{setTransferring(false);setRefresh(!refresh);}};if(!account){returnnull;}return(Transfer{transferring?(transferring。。。Loadingsizexl):(inputclassNameinputvalue{to}onInput{(e:any)setTo(e。target。value)}typetextplaceholderaddressinputclassNameinputvalue{amount}onInput{(e:any)setAmount(e。target。value)}typenumberplaceholderamountbuttonclassNamebtnonClick{transfer}sendbutton)});}
转账是通过signer。sendTransaction方法进行的,它会返回收据对象receipt。
在转账时使用到了ethers。utils。parseEther,因为value默认的单位是wei,它非常小,10的18次方才是一个ehter。在JS中需要使用BigInt类型表示,并不方便操作,而我们更喜欢用ether来描述货币。所以这个API可以帮我们转换货币单位。
需要注意,在测试时需要选择goerli网或者其他测试网,否则会浪费gas费。
你至少要有两个钱包账户,这样可以从一个钱包账户转到另一个钱包账户。
下面我们来测试一下转账。
这时MetaMask钱包会弹出来签名界面。
点击确认后,需要等待一段时间上链,大约1分钟,或者更久。
转账成功后,当前账户的余额会刷新。
完成
现在一个简单的加密钱包就完成了。通过这个项目的学习,相信你已经学会了ethers。js常规API的使用。
源码链接:https:github。comluzhenqianweb3examples
线上地址:web3examples。vercel。app
web3。0生态系统
Web3世界非常精彩。
如果你对Web3感兴趣,右上角记得加个关注。
我会持续更新更多Web3相关的高质量文章。
头条创作挑战赛web3。0
社区团购的未来趋势线上品类平台化,线下便利店化众多行业人士指出,2021年的社区团购依旧保持高速发展中。互联网巨头对于社区团购的投入只增不减,兴盛优选和十荟团将继续领跑行业融资,食享会和同程生活将慢慢被巨头瓜分市场,其他互……
永辉超市12月全国新开门店34家,全国布局按下加速键12月31日,永辉超市全国5店齐开。至此,永辉业务已覆盖全国29个省份、572个市(区、县),已开业大店数量1017家,实现一至六线城市全覆盖。此次新开的5家门店分别是甘肃兰州……
小象生鲜变七鲜,美团退后是京东岁末知年寒,春望初见暖。进入2021年,新零售五霸(盒马鲜生、超级物种、京东七鲜、苏鲜生、小象生鲜)再次洗牌。美团旗下小象生鲜北京方庄店关闭后,被京东旗下七鲜生活重整开业……
京东旗下社区团购平台京喜拼拼全面上线近日,京东旗下社区团购品牌京喜拼拼正式全面上线,目前已上线八个城市。01进军社区团购京喜拼拼全面上线1月1日消息,京东旗下社区团购平台京喜拼拼已全面上线,正式进军社……
京东七鲜换帅,前沃尔玛高管接任沃尔玛前高管郑峰2021年1月3日by:零售圈新零售超市竞争格局基本形成,处于竞争中位的京东七鲜生鲜美食超市(7FRESH)再次迎来换帅。这个曾经被希冀与盒马……
联华在上海徐汇区推出新形态社区生鲜店1月8日消息,新零售财经了解到联华超市在上海徐汇区推出新形态社区生鲜店。据悉,这是联华推出的全新社区生鲜店形态。这家门店由南丹路上一家具有20多年历史的联华超市改造升级而来,有……
全球七展联动,2021自有品牌有备而来2020年是不平凡的一年,疫情打乱了原本的节奏,却促进了零售行业线上业务的发展,社区团购及直播带货模式借势崛起。进入到2021年,零售市场增加了许多不确定性,然而随着国家新战略……
自有品牌亚洲展进入倒计时,PLF四大主题活动热闹非凡第十一届全球自有品牌产品亚洲展暨第二届零售生鲜食材展将于2021年3月2日4日在上海新国际博览中心举行。PLF作为亚洲零售业界自有品牌的知名年度盛会,现场将聚集商超、零售、生鲜……
祝融号火星车准备越冬环绕器持续开展环绕探测记者从国家航天局探月与航天工程中心了解到,当前,祝融号火星车所在区域已进入冬季,与我们地球相似,进入冬季后,北半球区域太阳光照高度角下降、光照时长缩短。根据测量,火星车当地正午……
2022年世界跆拳道大满贯总决赛中国队夺得两金新华社无锡4月2日电(记者王恒志、陈思)2022年世界跆拳道大满贯冠军系列赛暨奥运直通卡赛2日在江苏无锡经开区太湖国际博览中心展开总决赛较量,中国队选手左菊和宋洁分别获得女子4……
Dota6。83英雄攻略之宙斯游戏新春创作纪大家好久不见,今天我继续来更新一下Dota6。83英雄攻略之宙斯,希望小伙伴们多多点赞啊。〔招财进宝〕宙斯这个英雄我觉得还是比较容易上分的,我用了感觉良好。……
瑞幸咖啡向董事和员工实施股权激励计划自从瑞幸咖啡财务造假了22亿元后,瑞幸咖啡董事会陷入内斗纷争,高层管理人员也经历了大面积的换血,失信的瑞幸咖啡受尽外界排挤,尽管如此,瑞幸咖啡依然没有放弃自救。业内专家对……