NO。7Web3世界使用Reactethers。js开发简单加密钱包
互联网的技术日新月异,互联网不断深入人们的生活;
web3.0将是彻底改变人们生活的互联网形式;
web3.0使所有网上公民不再受到现有资源积累的限制;
具有更加平等地获得财富和声誉的机会。
web3.0会从哪里开始呢?
本合集文章,授权转载,侵权必究。
Web 3.0世界系列文章
来源 : 代码与野兽
【 Web3 系列文章 】
NO.1 遇见Web3: 在 Web3 的世界中写下第一行 HelloWorld
NO.2 全面系统的 Web3 学习路线,助你成为 Web3 开发专家
NO.3 Web3世界:区块链、比特币、以太坊和智能合约
NO.4 Remix IDE 使用与 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.js VS web3.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,点击后面的 view key,就可以查看 key。
我们把它复制下来,后面会用到。
构造合约
在对合约进行读取或交互之前,我们首先需要构造一个合约对象。
合约对象有三个构造参数,第一个是合约地址,是一个字符串。第二个是合约的 abi,可以是一个 JSON,第三个参数是 provider 对象,它用于管理网络连接状态。
下面的例子中就是使用 alchemy 提供的 JSON RPC 接口作为网络连接。 const rpc = `https://eth-goerli.g.alchemy.com/v2/${process.env.NEXT_PUBLIC_ALCHEMY_API_KEY}`; const provider = new ethers.providers.JsonRpcProvider(rpc); const contract = new ethers.Contract(contractAddress, abi, provider)
除了 JsonRpcProvider 以外,ethers 还有 IpcProvider、InfuraProvider、AlchemyProvider、Web3Provider 等多种 Provider。读取合约信息
abi 中的方法会直接挂载到 contract 对象上,我们可以直接调用。
不过需要注意,所有的操作都是异步的。(async () => { const owner = await contract.owner(); console.log(owner); })();连接钱包
由于和合约交互需要支付 gas 费用,所以必须有一个数字钱包。
钱包有很多种,比如 MetaMask、Rainbow、Coinbase Wallet 等。其中 MetaMask 是最常用的一种数字钱包。
这里主要介绍如何连接到 MetaMask。
MetaMask 有一个浏览器插件,如果用户安装了该插件,在 window 对象下会有一个 ethereum 对象。我们可以调用 ethereum.request 方法发起请求,参数是一个对象,对象的 method 描述该次请求的操作。
ethereum.request 方法是异步的,会返回一个数组,该数组是所有登陆钱包的账户地址字符串,第一个账户就是当前激活的账户。如果返回的数组长度为 0,则意味着没有登陆任何账户。(async ()=> { const accounts = await ethereum.request({ method: "eth_requestAccounts" }) if(accounts.length === 0) { throw Error("未登录任何账户") } const activeAccount = accounts[0] console.log(activeAccount) })()
我们还可以通过 ethereum.request 方法获取当前的网络状态。(async ()=> { const chainId = await ethereum.request({ method: "eth_chainId" }) console.log(chainId) })()
它会返回一个字符串。0x1 表示以太网主网;0x5 表示 Goerli 测试网,更多网络的 chainId 可以在这个网站查看:chainlist.org/。
下面是使用 ethers.js 来连接 MetaMask 的代码。(async ()=> { const provider = new ethers.providers.Web3Provider(window.ethereum) const accounts = await provider.send("eth_requestAccounts", []) const activeAccount = accounts[0] })()
如果使用 MetaMask 作为 provider,那么就不需要再使用 alchemy 了。钱包
在转账交易之前,我们需要创建一个 Wallet 实例,它的作用是对交易和消息进行签名。
创建 Wallet 对象的方法有三种。
通过 Wallet.createRandom 创建随机钱包
这种方式创建的是一个单机钱包,需要连接网络。const wallet = ethers.Wallet.createRandom() wallet.connect(provider)
通过助记词创建const wallet = new ethers.Wallet.fromMnemonic(mnenonic.phrase)
通过私钥和 provider 创建const wallet = new ethers.Wallet(privateKey, provider)钱包信息
我们可以在创建好的钱包上面获取很多有用的信息,比如钱包地址、助记词、私钥、交易次数等。console.log(wallet.address) console.log(await wallet.getAddress()) console.log(wallet.mnemonic) console.log(wallet.privateKey) console.log(wallet.getTransactionCount())转账
一旦又了钱包,我们就可以向其他人发起转账交易。
创建一个 tx 对象,它最少需要两个属性,to 和 value,分别表示接受钱包地址和转账额度。
然后使用 wallet.sendTransaction 方法发送转账,它会返回一个 receipt 对象。这个对象有一个异步的 wait 方法,当交易上链后会返回。const tx = { to: address, value: ethers.utils.parseEther("0.1"), } console.log("开始转账") const receipt = await wallet.sendTransaction(tx) await receipt.wait() console.log("完成转账")通过合约转账交易
交易需要使用 Wallet 对象。再通过 wallet 作为合约的第三个构造参数创建 Contract 对象。
调用合约的 transfer 方法,进行转账交易。该方法需要两个参数,转入的钱包地址字符串和转入的数量。
transfer 会返回一个 tx 对象,该对象有一个异步的 wait 方法,会在交易完成后执行。const wallet = new ethers.Wallet(privateKey, provider) const contract = new ethers.Contract(contractAddress, abi, wallet) (async ()=> { const tx = await contract.transfer(toAddress, ethers.utils.parseEther("1")) await tx.wait() })()通过 signer 进行转账
通常我们无法直接拿到 privateKey,但是可以通过 signer 对象间接使用 privateKey。只需要进行签名就可以进行交易。这也是最常用的交易方式。const signer = walletProvider.getSigner(); const tx = { to, value, }; const receipt = await signer.sendTransaction(tx); await receipt.wait();使用 React 和 ethers.js 开发加密钱包
接下来我们开发一个最简单的加密钱包,具备最基础的转账功能和查询余额功能。创建项目
我们首先创建一个 Next.js 项目。npx create-next-app
需要选择 TypeScript。安装依赖安装 ethers.jsnpm i ethers安装 tailwindcssnpm install -D tailwindcss postcss autoprefixer npx tailwindcss init -p
修改 tailwind.cinfig.js。/** @type {import("tailwindcss").Config} */ module.exports = { content: [ "./pages/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}", ], theme: { extend: {}, }, plugins: [], }
修改 styles/globals.css。@tailwind base; @tailwind components; @tailwind utilities;安装 headless-uinpm install @headlessui/react整体架构设计
由于业务并不复杂,我们可以将它简单划分为几个组件。使用 Context 足够应对这个场景,而不需要额外导入状态管理库来增加复杂性。
Wallet 是根组件,内部维护了很多 state,以 Context 的方式将数据和操作注入到子组件。
Connect 负责连接钱包和断开连接。
Details 负责显示钱包信息,是纯展示型组件。
Transfer 负责向其他账户进行转账。
Loading 负责渲染加载动画,是纯展示型组件。创建上下文type IWalletCtx = { 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; }; const WalletCtx = createContext({} as IWalletCtx);
通过初始化一个对象,然后断言为 IWalletCtx 的方式,可以避免在使用 WalletCtx 时添加是否为 undefined 或 null 的判断。因为我们一定会注入数据。Loading 组件
Loading 作为纯展示型组件,是最简单的组件。SVG 的代码是直接从 tailwindcss 文档中搬运过来的。仅仅是添加了一个 size 属性,用来展示不同大小的尺寸。function Loading({ size = "md" }: { size?: "sm" | "md" | "lg" | "xl" }) { const sizes = { sm: "h-3 w-3", md: "h-5 w-5", lg: "h-7 w-7", xl: "h-9 w-9", }; return ( ); }Wallet 组件
在 Wallet 组件中创建这些 state,并注入到 context 中。export default function Wallet() { const [walletProvider, setWalletProvider] = useState(null); const [msgIsOpen, setMsgIsOpen] = useState(false); const [msg, setMsg] = useState(""); const [account, setAccount] = useState(""); const [networkName, setNetworkName] = useState(""); const [balance, setBalance] = useState(""); const [refresh, setRefresh] = useState(false); useEffect(() => { setWalletProvider(new ethers.providers.Web3Provider(window.ethereum)); }, []); const showMessage = (message: string) => { setMsg(message); setMsgIsOpen(true); setTimeout(() => { setMsg(""); setMsgIsOpen(false); }, 2000); }; return ( ); }
Wallet 组件基本上没有什么逻辑,它的主要作用有三个:向 context 注入数据。创建 ethers.provider。使用 Dialog 组件作为全局消息提示。Connect 组件
在 styles/globals.css 中添加按钮样式。@layer components { .btn { @apply bg-black text-white py-2 px-4 rounded-3xl; } }
在 index.tsx 中编写逻辑。function Connect() { const { walletProvider, account, setAccount, setNetworkName, setBalance, showMessage, refresh, } = useContext(WalletCtx); const refreshBalance = useCallback(async () => { if (!walletProvider || !account) return; const balance = await walletProvider.getBalance(account); setBalance(ethers.utils.formatEther(balance)); }, [setBalance, walletProvider, account]); useEffect(() => { refreshBalance(); }, [refresh, refreshBalance]); const connectToMetamask = async () => { try { await window.ethereum.enable(); const accounts = await walletProvider.send("eth_requestAccounts", []); const network = await walletProvider.getNetwork(); const balance = await walletProvider.getBalance(accounts[0]); setAccount(accounts[0]); setNetworkName(network.name); setBalance(ethers.utils.formatEther(balance)); } catch (error) { console.log(error); showMessage("failed to connect to metamask"); } }; const disconnect = async () => { setAccount(""); }; if (!account) { return ( {walletProvider ? ( ) : ( )} ); } return ( Hello, {account}
); }
我们连接钱包后会获取 3 个重要的信息:钱包账户地址、连接的网络和余额。
分别通过 walletProvider.listAccounts()、walletProvider.getNetwork() 和 walletProvider.getBalance(accounts[0]) 来获取,需要注意它们都是异步操作。Details 组件
Details 作为纯展示型组件没有什么逻辑,主要是一些样式。function Details() { const { account, networkName, balance } = useContext(WalletCtx); if (!account) { return null; } return ( balance network: {networkName} {balance} ETH ); }
现在我们看一下 Connect 和 Details 组件一起使用的效果。
Transfer 组件
Transfer 的功能比较简单,它在 UI 上仅仅包含两个输入框和一个 send 按钮。
两个输入框分别可以输入 to 和 value,表示转账的钱包地址和转账金额。function Transfer() { const { walletProvider, account, showMessage, refresh, setRefresh } = useContext(WalletCtx); const [to, setTo] = useState(""); const [amount, setAmount] = useState(""); const [transferring, setTransferring] = useState(false); const transfer = async () => { try { const value = ethers.utils.parseEther(amount); const signer = walletProvider.getSigner(); const tx = { to, value, }; setTransferring(true); const receipt = await signer.sendTransaction(tx); await receipt.wait(); setTo(""); setAmount(""); showMessage("successfully transferred"); } catch (error) { console.log(error); showMessage("failed to transfer"); } finally { setTransferring(false); setRefresh(!refresh); } }; if (!account) { return null; } return ( Transfer {transferring ? ( transferring... ) : ( setTo(e.target.value)} type="text" placeholder="address" /> setAmount(e.target.value)} type="number" placeholder="amount" /> )} ); }
转账是通过 signer.sendTransaction 方法进行的,它会返回收据对象 receipt。
在转账时使用到了 ethers.utils.parseEther,因为 value 默认的单位是 wei,它非常小,10 的 18 次方才是一个 ehter。在 JS 中需要使用 BigInt 类型表示,并不方便操作,而我们更喜欢用 ether 来描述货币。所以这个 API 可以帮我们转换货币单位。
需要注意,在测试时需要选择 goerli 网或者其他测试网,否则会浪费 gas 费。
你至少要有两个钱包账户,这样可以从一个钱包账户转到另一个钱包账户。
下面我们来测试一下转账。
这时 MetaMask 钱包会弹出来签名界面。
点击确认后,需要等待一段时间上链,大约 1 分钟,或者更久。
转账成功后,当前账户的余额会刷新。
完成
现在一个简单的加密钱包就完成了。通过这个项目的学习,相信你已经学会了 ethers.js 常规 API 的使用。
源码链接:https://github.com/luzhenqian/web3-examples
线上地址:web3-examples.vercel.app/
web3.0 生态系统
Web 3世界非常精彩。
如果你对 Web3 感兴趣,右上角记得加个关注。
我会持续更新更多 Web3 相关的高质量文章。
#头条创作挑战赛# #web3.0#
小米发布无线吸尘器2Slim,吸尘除螨拖地三合一无线吸尘器因为轻便,同时可以更好地清理边角而获得更多消费者的认可,但在很长一段时间,某森一直是很多人的首选,只是高昂的售价,让很多人望而却步,如今小米吸尘器领域也细分出很多品类,比
各国媒体解析日本制胜球未出界!英球迷不该认为淘汰德国是阴谋2022世界杯一些德国媒体与德国球迷质疑日本制胜球之前已经出界了。第50分钟,堂安律射门到后点,日本球员将球捡回来,中路田中碧抢点破门,日本三分钟2球完成逆转。裁判先是示意进球前出
12月待发布新机有哪些?大量新品即将到来近日,各品牌陆续官宣或被曝光了有望在12月份推出的手机产品。这些新机覆盖了不同定价和定位范围,将为接下来的智能手机市场提供更多选择。现在,在其正式到来前,我们也一起来看看这些产品吧
我从来就没有太阳,所以不怕失去我的天空里没有太阳,总是黑夜。但并不暗,因为有东西代替了太阳。虽然没有太阳那么明亮,但对我来说已经足够。凭借着这份光,我便能把黑夜当成白天。我从来就没有太阳,所以不怕失去。东野圭吾
太空家园里的接续奋斗随着神舟十五号顺利升空,中国航天人再次刷新飞天壮举。航天员费俊龙邓清明张陆组成的神舟十五号飞行乘组,接力中国空间站建造阶段载人飞行任务的最后一棒,向着建造空间站的中国式跨越发起最后
2003年,杨利伟在太空听到诡异的敲门声,13年后谜底才被揭开头条创作挑战赛2003年10月15日,北京时间上午9时,在万众瞩目下,杨利伟乘由长征二号F火箭运载的神舟五号飞船首次进入太空。这是我国第一艘载人飞船登陆太空,是全球第三个有能力独自
世界十大未解之谜(上)世界很大,在我们看不到的地方有很多未解之谜,甚至是一些连科学家都无法解释的谜团。世界十大未解之谜困扰着众多科学家。就像百慕大神秘失踪之谜一样,神秘失踪的人连尸体都没有。让我们一起来
王者教学意识和思路讲解(第三期)那接下来就是每一条路需要注意的点对抗路坦边一中期起四分钟左右一方边路一塔告破基本上就可以放线给法刺和野核吃自己去跟团,尽量在每波中线汇合之前站好一边的草丛视野不要舍不得放线坦克的占
whereisjackma?马云行踪终于曝光了前段时间,世界首富马斯克在回答有关如何看待互联网和科技行业的营商环境的问题是,做了一个这样的反问whereisjackma?乍一看,这个反问好像和问题没什么关系,但稍微一想想,就发
魅族20Pro蓄势待发,10倍光变5000mAh100W,4nm坐镇对魅族了解的朋友就知道,当初它的展望是对标苹果三星,但经过几年的发展,魅族如今已被吉利收购。想要对标科技具体,那就要软硬一体化,从底层开始解决问题,形成一整套自己的产品逻辑,而不是
太阳展现出诡异笑脸(NASA)(SDO)1026()。。美国航空航天局(NASA)太阳动力学天文台(SDO)公开了在10月26日拍摄的太阳像在微笑的诡异画面。在炽热的火焰中,眼睛和鼻子的模样格外清晰
极复杂问题背后的简洁数学想象一下,如果有一个微微弯曲的软质曲面,类似一片隐形眼镜,而我们想把它压平,它的表面就会出现一些多出来的材料,进而产生褶皱。但是,这些褶皱会在什么地方出现?它们又会以怎样的方式排列
刚刚!新能源赛道两件大事8月下旬以来,新能源赛道调整明显,龙头企业宁德时代股价创近三个月新低。自8月24日以来的16个交易日里,该公司市值蒸发逾3400亿元,回到万亿元关口附近,年内累计跌幅近30。不过,
中国航天再获突破,登月连续三次成功后,中东土豪找中国合作登月现代航天为尖端的产业,尤其是登月项目更是引人注目,现在中国航天在登月领域取得了相当不错的成就。这让美国人相当不满,登月必须是自己第一,为此美国要给中国一个明确的警告信息!美国将保持
2022女排世锦赛三甲及中国队成绩预测2022年世界女排锦标赛将于本月23日至10月16日进行,本届世锦赛上哪三支队伍会登上领奖台呢?这是大家普遍关注的问题。近日土耳其女排主教练古德蒂接受采访,他认为冠亚军将在塞尔维亚
我眼中的先锋书店先锋汤山矿坑书店(下)先锋汤山矿坑书店地址南京市江宁区汤山街道美泉路11号汤山矿坑公园行走时间2021年6月东西向的拱顶之下是一个大大空间的咖啡区。咖啡区的一侧也是大大的落地窗,窗外是绿意的山坡,草坪和
大理游记这个世界尽管残缺破损笨拙不堪,却也不乏优美宜人富有意义之物。叶芝这次中秋请了个长假,去大理旅游了一番,现在旅程即将结束,谨以此文草草记录这次难忘的旅程。因为没有杭州直飞大理的飞机,
被国台办点名表扬的台湾女孩,她到底经历了什么?李柔,她是一位来自台湾地区的女孩,她第一次来到祖国大陆旅行,正是这次旅行让她感受到了祖国的强大,人民子弟兵的热情。9月14日,在国台办举行的一次新闻发布会上,发言人朱凤莲特意点名表
令人失望的三座城市,游客却不禁感叹,早知道不来了导语如今人们的生活越来越好,追求也会高了许多,旅游自然成为人们下班后放松的方式之一,中国资源丰富,自然有许多旅游景点,一定去过许多著名的旅游景点,但它们真的像人们说的那样好吗?今天
一月排出肾结石,我做了什么?补钙,会加重肾结石?01hr前言很多人一听到身体里长了磨人的小石头肾结石。肯定会认为和钙脱不了干系,心里念叨着,要少吃点含钙丰富的食物,然后将养生心得转发到家庭微信群广而告之,殊不知这样根本防止不了肾
平时做好这5点,胃肠肿瘤发病率降低50小江是一个肉食主义者,每顿饭必定要有肉,没肉都吃不下饭。而且还不爱吃蔬菜,最爱吃红烧肉,连续吃三天不带腻。这也导致小江体型较胖。家人朋友都劝他多吃蔬菜,适当减肥,保证身体健康。但小
最新!贵州这些景区发布闭园公告关于夜郎洞景区停园检修的公告尊敬的游客为进一步提升景区硬件设施服务品质,提升游客游玩体验,夜郎洞景区于2022年9月16日起停园检修,开放时间另行告知。暂停营业期间,给您带来的不便