Cover photo

Web3 开发极简教程001:一文快速学会DAPP前端开发

文:nobodyjack (Twitter: @ethcrap)

前言

最近开始了解web3 DAPP开发,了解到一个完整DAPP开发主要包含如下两部分:

  • 智能合约:通常指代运行在 EVM 兼容网络中的 Solidity 或其他合约语言代码,负责与用户交易我们发行的资产并储存 DApp 的链上状态。

  • DAPP前端:负责应用程序界面及与智能合约交互,它们大部分是 Web APP,可以用原生前端语言或流行框架例如 React/Vue 来进行编写。

我没什么编程基础,所以放弃直接从学习智能合约入门,转而先从DAPP前端开始学习,这样入门学习会简单很多,反馈也更快,可以避免自己中途放弃(挑软柿子捏更容易成功,持续反馈是坚持学习的关键)。

我一直非常推崇费曼学习法,简化概念、以教代学,因此后面我会持续更新本系列教程,希望能帮助自己和他人学习。

我也是小白,如有错误,欢迎指教!

基础&准备

1)理解Signer、JSON RPC、Provider与Metamask

为了让大家对DAPP开发有个全貌了解,先祭一张DAPP架构神图

对于DAPP前端开发,图中有几点至关重要的概念需要理解:

  • Provider:节点提供者,这是一个 DApp 架构中特殊的角色,它负责与区块链进行通信,它提供对区块链及其状态的只读访问。

  • Signer:签名者,一个至关重要的角色,它负责签署消息和交易,链上所有交易都需要签名才能执行。

  • Ethereum JSON-RPC:以太坊通信协议接口,所有的节点客户端都需要实现该接口,该接口提供一组统一的方法供DAPP调用,实现DAPP与链进行交互(读取区块链数据或发送交易信息到链上)。

    Metamask作为一个浏览器扩展插件,既是Provider,也是一个Signer。

2)环境与工具

3)技术栈

  • html、css

  • javascript

  • ethers.js:以太坊区块链及其生态系统进行交互的JavaScript库

需求分析

一个简单的DAPP 前端Demo,主要涉及如下几个通用功能:

  • 登录

    • 连接钱包(MetaMask)

    • 签署消息

  • 转账

  • 调用智能合约只读方法

  • 调用智能合约写方法

页面

本教程为了简便,直接用HTML+CSS,并在HTML中引入ethers.js

<script src="https://cdn.ethers.io/lib/ethers-5.2.umd.min.js" type="application/javascript"></script>

做了一个毫无设计的页面,大家可以不用参照,可以自己设计😂

post image

登录:连接钱包、签署消息

登录是最常见的功能之一,不少应用都会要求用户登录才能使用应用或部分功能。与传统应用不同的是,对于一些 DAPP,你只需要一个钱包就可以访问它,它完全没有帐户、用户名、密码、个人数据。

也就是说,对于DAPP来说,登录意味着“连接钱包、访问钱包中的账户”

另外,为了保证安全,DAPP通常也会要求用户在钱包签署一条消息 ,以验证账户控制权限,而不是他人使用用户的钱包/资金进行欺诈行为。

简化流程: 登录 —>  连接钱包 —> 签署消息 —> 使用DAPP

1)连接钱包,获取当前账户

//HTML
<button class="btn btn-primary btn-lg btn-block mb-3" id="connectButton" >  
Connect Wallet</button>
<p class="info-text alert alert-success">Account : 
<span id="showAccount"></span>
</p>

MetaMask将一个全局Provider注入浏览器每个页面的window.ethereum,ethers库提供一个方法可将该Provider包装成一个新的可供 ethers.js 使用的Web3Provider。

“连接”到 MetaMask 实际上意味着“访问用户的以太坊帐户”:

//javacriptconst 
connectButton = document.querySelector('#connectButton');const showAccount = document.querySelector('#showAccount');
var currentAccount ='';
connectButton.addEventListener('click', () => {    getAccount();});
async function getAccount({   
 // const accounts = await ethereum.request({ method: 'eth_requestAccounts' });    
if (window.ethereum) {
        const provider = new ethers.providers.Web3Provider(window.ethereum);
        const accounts = await provider.send("eth_requestAccounts", []);        currentAccount = accounts[0];        showAccount.innerHTML = currentAccount;
    } else {
        alert('Please install MetaMask ');
    }
}
  • 签署消息

    要签署消息,必须先获得一个signer实例,然后通过signMessage方法签署消息。

//HTML
<button class="btn btn-primary btn-lg btn-block mb-3" id="signButton">Sign Message</button>
<p class="info-text alert alert-warning">Result :   <span id="signResult"></span>
</p>
//javacriptconst 
signButton = document.querySelector('#signButton')
const signResult = document.querySelector('#signResult')
signButton.addEventListener('click', () => {    signMessage();});
async function signMessage() {    
  if (currentAccount !== '') {        
    console.log(typeof account);        
    const provider = new    ethers.providers.Web3Provider(window.ethereum);        
    const signer = provider.getSigner();        
    const result = await signer.signMessage("Hello World");        
    signResult.innerHTML = result;    
   } else {        
      alert('Please connect to MetaMask. ');    
      }
   }

转账:发送ETH

signer提供了一个sendTransaction方法用于发送交易:

//HTML
<div class="card full-width">    
  <div class="card-body">        
    <h4 class="card-title"> Send ETH </h4>
        <div class="form-group">
            <label>To</label>
            <input class="form-control" type="text" id="toInput" value="0x83BFFEe57cE413f846d54bb8d7A55b4F6d475F8E" />
        </div>
        <div class="form-group">
            <label>Amount</label>
            <input class="form-control" type="text" id="amountInput" value="0" />
        </div>        
<div class="form-group">
            <label>Data</label>
            <input class="form-control" type="text" id="dataInput" />        </div>
        <button class="btn btn-primary btn-lg btn-block mb-3" id="submitForm">Submit</button>
    </div>
</div>
//javacript
const toInput = document.querySelector('#toInput');
const amountInput = document.querySelector('#amountInput');
const dataInput = document.querySelector('#dataInput');
const submitForm = document.querySelector('#submitForm');
submitForm.addEventListener('click', () => {
    sendETH();
});
async function sendETH() {
    if (window.ethereum) {
        const amount = amountInput.value.trim();
        const to = toInput.value.trim();
        let data = dataInput.value.trim();
        if (to !== '' && amount !== '') {
            const provider = new ethers.providers.Web3Provider(window.ethereum);
            const signer = provider.getSigner();
            const tx = {};
            tx.to = to;
            tx.value = ethers.utils.parseEther(amount);            if (data != '') {
                tx.data = ethers.utils.isHexString(data) ? data : '0x' + data;
            }
            await signer.sendTransaction(tx).catch(err =>{                alert(err.message);
            });
        } else {
            alert('to or amount can not blank');
        }
    } else {
        alert('Please connect to MetaMask. ');
    }}

调用合约的只读方法

调用只读方法(由Solidity 表示pureview在 Solidity 中表示),可以由节点返回结果,因此它是免费的,但不能更改区块链状态。

调用合约的方法,需要合约的ABI和地址,我们直接在etherscan.io找个Ropsten测试网的开源合约,可以查到合约的源代码、ABI及合约,这里以NFT Doodles 的合约为例:

https://ropsten.etherscan.io/address/0xa86ea6e910ed86ad53807f533e1550b569b47ad4#code

调用合约的balanceof方法,查询地址持有该NFT的mint数量:

//HTML
<div class="card full-width">
    <div class="card-body">
        <h4 class="card-title">
            Mint
        </h4>
        <div class="form-group">
            <label>Num</label>
            <input class="form-control" type="text" id="numInput"                value="0x83BFFEe57cE413f846d54bb8d7A55b4F6d475F8E" />
        </div>
        <button class="btn btn-primary btn-lg btn-block mb-3" id="mintButton">
            Mint
        </button>
        <p class="info-text alert alert-warning">
            Hash Result: <span id="mintResult"></span>
        </p>
    </div>
</div>
//javacript
queryButton.addEventListener('click', () => {
    const owner = ownerInput.value.trim();
    queryAvailableNum(owner);});
async function queryAvailableNum(owner) {
    const provider = new   ethers.providers.Web3Provider(window.ethereum);
    const url = 'abi.json';
    const abi = await getABI(url);
    console.log(abi);
    const contract_adress = '0xA86eA6e910ED86aD53807F533e1550B569B47AD4';
    const contract = new ethers.Contract(contract_adress, abi, provider);
    const num = await contract.balanceOf(owner) 
    console.log(num);
    nftNum.innerHTML = num;}
async function getABI(url) {
    const res = await fetch(url);
    return await res.json();}

调用合约的写方法

调用写方法签署交易,并需要支付给矿工费用,该交易将由整个网络上的每个节点以及矿工验证,矿工将在针对当前状态执行区块链后计算区块链的新状态。

//HTML
<div class="card full-width">
    <div class="card-body">
        <h4 class="card-title">
            Query Doodle Num
        </h4>
        <div class="form-group">
            <label>owner</label>
            <input class="form-control" type="text" id="ownerInput"                value="0x83BFFEe57cE413f846d54bb8d7A55b4F6d475F8E" />
        </div>
        <button class="btn btn-primary btn-lg btn-block mb-3" id="queryButton">
            Query
        </button>
        <p class="info-text alert alert-warning">
            Doodle Num: <span id="nftNum"></span>
        </p>
    </div>
</div>
//javacript
<div class="card full-width">
    <div class="card-body"> 
       <h4 class="card-title">
            Mint
        </h4>
        <div class="form-group">
            <label>Num</label>
            <input class="form-control" type="text" id="numInput"                value="0x83BFFEe57cE413f846d54bb8d7A55b4F6d475F8E" />
        </div>
        <button class="btn btn-primary btn-lg btn-block mb-3" id="mintButton">
            Mint
        </button>
        <p class="info-text alert alert-warning">
            Hash Result: <span id="mintResult"></span>
        </p>
    </div>
</div>

完整实现

把以上的功能组合起来就可以实现一个简单完整的DAPP前端Demo,这里我只是抛砖引玉,剩下的工作就交给你们了。

如果你觉得对你有用,请关注我

Mirror: 

https://mirror.xyz/0x83BFFEe57cE413f846d54bb8d7A55b4F6d475F8E

Twitter:

https://twitter.com/ethcrap

Medium:

https://medium.com/@nobodyjack2050