用Ethers.js构建一个简单的DApp

这篇文章主要介绍了用Ethers.js构建一个简单的DApp ,文中通过代码以及文档配合进行讲解,很详细,它对在座的每个人的研究和工作具有很经典的参考价值。 如果需要,让我们与区块链资料网一起学习。

https://www.interchains.cc/22246.html

用Ethers.js构建一个简单的DApp是很好的区块链资料,他说明了区块链当中的经典原理,可以给我们提供资料,用Ethers.js构建一个简单的DApp学习起来其实是很简单的,

不多的几个较为抽象的概念也很容易理解,之所以很多人感觉用Ethers.js构建一个简单的DApp比较复杂,一方面是因为大多数的文档没有做到由浅入深地讲解,概念上没有注意先后顺序,给读者的理解带来困难

用Ethers.js构建一个简单的DApp

  • ethers.js
  • ERC20
  • 智能合约

如果你已经在以太坊eth上开发过DApp,那你在前端JavaScript中可能用过 web3.js。Ethers.js可算是一个轻量级的web3.js,本文将介绍如何使用Ether.js构建一个简单的DApp。

与Web3.js相比,Ethers.js有很多优点,如Ethers.js提供的状态和密钥管理就非常的好用。

Web3的设计场景是DApp应该连接到一个本地节点, 由这个节点负责保存密钥、签名交易并与以太坊eth区块链blockchain交互。现实并不是这样的,绝大多数用户不会在本地运行一个geth节点。Metamask在浏览器应用中有效地模拟了这种节点环境,因此绝大多数web3应用需要使用Metamask 来保存密钥、签名交易并完成与以太坊eth的交互。

Ethers.js采取了不同的设计思路,它提供给开发者更大的灵活性。Ethers.js 将“节点”拆分为两个不同的角色:

  • 钱包:负责密钥保存和交易签名
  • 提供器:负责以太坊eth网络的匿名连接、状态检查和交易发送

编译、部署智能合约

本文中我们要与一个ERC20智能合约交互,需要在机器里先安装 nodejs和npm。

创建项目文件夹

首先创建一个文件夹ethers-template,然后在这个文件夹里再创建另一个 contracts文件夹:

~$ mkdir -p ethers-template/contracts

初始化npm配置

然后进入ethers-template目录初始化npm配置:

~$ cd ethers-template ~/ethers-template$ npm init -y

创建项目配置文件

接下来创建一个config.json文件保存你的项目配置:

{   "private_key": "24C4FE6063E62710EAD956611B71825B778B041B18ED53118CE5DA5F02E494BA",   "network": "kovan",   "ERC20": "0x0DEd9F7D82a24099F09AF7831CaB61B31Df10487",   "name": "Kanchan Coin",   "symbol": "SNK",   "total_supply": "1000000000000000000000000",   "decimals": 18 }

说明如下:

  • private_key:账户私钥,将使用这个私钥对应的账户在指定网络上部署智能合约。
  • network:要接入的以太坊eth网络,ethers.js支持以下网络:
    • homestead:主网
    • rinkeby
    • ropsten
    • kovan
    • goerli
  • ERC20:声明要交互的已部署合约,可选
  • name/symbol/decimals:ERC20合约的参数

安装ethers.js

现在可以安装ethers.js了:

~/ethers-template$ npm install --save ethers

安装编译合约需要的npm包

为了编译合约,我们还需要安装solc和fs-extra:

~/ethers-template$ npm install [email protected] [email protected] --save

创建ERC20合约代码

在contracts目录下创建文件erc20.sol:

~/ethers-template$ touch contracts/erc20.sol

并按如下修改:

pragma solidity ^0.5.0;  contract ERC20 {      using SafeMath for uint256;     event Approval(address indexed tokenOwner, address indexed spender, uint tokens);     event Transfer(address indexed from, address indexed to, uint tokens);     mapping(address => uint256) balances;     mapping(address => mapping (address => uint256)) allowed;     string public symbol;     uint8 public decimals;     string public  name;     uint256 private _totalSupply;      constructor(uint8 _decimals, string memory _symbol, string memory _name, uint256 _total_supply) public{         decimals = _decimals;         symbol = _symbol;         name = _name;         _totalSupply = _total_supply;         balances[msg.sender] = _totalSupply;     }      function totalSupply() public view returns (uint256) {         return _totalSupply;     }          function balanceOf(address tokenOwner) public view returns (uint) {         return balances[tokenOwner];     }      function transfer(address receiver, uint numTokens) public returns (bool) {         require(numTokens <= balances[msg.sender]);         balances[msg.sender] = balances[msg.sender].sub(numTokens);         balances[receiver] = balances[receiver].add(numTokens);         emit Transfer(msg.sender, receiver, numTokens);         return true;     }      function approve(address delegate, uint numTokens) public returns (bool) {         allowed[msg.sender][delegate] = numTokens;         emit Approval(msg.sender, delegate, numTokens);         return true;     }      function allowance(address owner, address delegate) public view returns (uint) {         return allowed[owner][delegate];     }      function transferFrom(address owner, address buyer, uint numTokens) public returns (bool) {         require(numTokens <= balances[owner]);             require(numTokens <= allowed[owner][msg.sender]);          balances[owner] = balances[owner].sub(numTokens);         allowed[owner][msg.sender] = allowed[owner][msg.sender].sub(numTokens);         balances[buyer] = balances[buyer].add(numTokens);         emit Transfer(owner, buyer, numTokens);         return true;     } }  library SafeMath {      function sub(uint256 a, uint256 b) internal pure returns (uint256) {       assert(b <= a);       return a - b;     }      function add(uint256 a, uint256 b) internal pure returns (uint256) {       uint256 c = a + b;       assert(c >= a);       return c;     } }

编写合约编译脚本

下面的代码使用solc编译合约文件,将其保存为compile.js:

const path = require('path'); const fs = require('fs-extra'); const solc = require('solc'); const config = require('./config.json');  const sourceFolderPath = path.resolve(__dirname, 'contracts'); const buildFolderPath = path.resolve(__dirname, 'build');  const getContractSource = contractFileName => {   const contractPath = path.resolve(__dirname, 'contracts', contractFileName);   const source = fs.readFileSync(contractPath, 'utf8');   return source; };  let sources = {};  fs.readdirSync(sourceFolderPath).forEach(contractFileName => {   sources = {     ...sources,     [contractFileName]: {       content: getContractSource(contractFileName)     }   } });  const input = {     language: 'Solidity',     sources,     settings: {         outputSelection: {             '*': {                 '*': [ '*' ]             }         }     } }  console.log('nCompiling contracts...'); const output = JSON.parse(solc.compile(JSON.stringify(input))); console.log('Done');  let shouldBuild = true;  if (output.errors) {   console.error(output.errors);   // throw 'nError in compilation please check the contractn';   for(error of output.errors) {     if(error.severity === 'error') {       shouldBuild = false;       throw 'Error found';       break;     }   } }  if(shouldBuild) {   console.log('nBuilding please wait...');    fs.removeSync(buildFolderPath);   fs.ensureDirSync(buildFolderPath);    for (let contractFile in output.contracts) {     for(let key in output.contracts[contractFile]) {       fs.outputJsonSync(         path.resolve(buildFolderPath, `${key}.json`),         {           abi: output.contracts[contractFile][key]["abi"],           bytecode: output.contracts[contractFile][key]["evm"]["bytecode"]["object"]         },         {           spaces:2,           EOL: "n"          }       );     }   }   console.log('Build finished successfully!n'); } else {   console.log('nBuild failedn'); }

上面的代码将读入并编译contracts目录中的所有合约文件, 然后将编译得到的abi和字节码保存为json文件。

编译合约

现在使用compile.js来编译erc20.sol合约:

~/ethers-template$ node compile.js

编译结束后,我们得到如下的目录结构;

+ethers-template     +compile.js     +contracts         -erc20.sol     +build         -ERC.json         -Context.json         -IERC20.sjon         -SafeMath.json    -package.json

编写合约部署脚本

创建文件deploy.js:

~/ethers-template$ touch deploy.js

然后按如下修改:

const startTimestamp = Date.now(); const ethers = require('ethers'); const config = require('./config.json'); const fs = require('fs-extra');  const provider = ethers.getDefaultProvider(config["network"]);  const wallet = new ethers.Wallet(config["private_key"], provider); console.log(`Loaded wallet ${wallet.address}`);  let compiled = require(`./build/${process.argv[2]}.json`);  (async() => {   console.log(`nDeploying ${process.argv[2]} in ${config["network"]}...`);   let contract = new ethers.ContractFactory(     compiled.abi,     compiled.bytecode,     wallet   );    let instance =  await contract.deploy(config["decimals"], config["symbol"], config["name"], config["total_supply"]);   console.log(`deployed at ${instance.address}`)   config[`${process.argv[2]}`] = instance.address   console.log("Waiting for the contract to get mined...")   await instance.deployed()   console.log("Contract deployed")   fs.outputJsonSync(     'config.json',     config,     {       spaces:2,       EOL: "n"      }   );  })();

注意:

  • 上面代码中的默认网络是kovan测试网
  • 在这个测试网中,你的账号需要一些以太币来支付部署交易的手续费
  • 将使用config.json中的private_key来部署合约

部署合约

运行deploy.js脚本时,需要在命令行传入要部署的合约名称ERC20:

~/ethers-template$ node deploy.js ERC20

输出结果:

Loaded wallet 0xC8e1F3B9a0CdFceF9fFd2343B943989A22517b26 Deploying ERC20 in kovan... deployed at 0x77Bb3546f5ee356E4026BaA96b7DDf22141bd77B Waiting for the contract to get mined... Contract deployed

在与合约交互时需要合约部署地址,上面的代码会自动将合约部署 地址保存到config.json文件中。

与智能合约交互

在本文中,我们使用ES6来编写合约交互代码,然后使用webpack 和babel将ES6代码转换为ES5代码。

安装ES6构建工具

首先安装这些依赖项:

~/ethers-template$ npm i webpack webpack-cli @babel/core                            @babel/plugin-proposal-object-rest-spread                            @babel/preset-env babel-loader                            babel-polyfill -D 

编写前端脚本

创建一个文件app.js:

~/ethers-template$ touch app.js

然后按照如下修改:

const ethers = require('ethers'); const config = require('./config.json');  // Import the json file from build to get the abi const erc_json = require('./build/ERC20.json'); //import the json of the contract which you want to interact  // You can use any standard network name //  - "homestead" //  - "rinkeby" //  - "ropsten" //  - "kovan" //  - "goerli" const provider = ethers.getDefaultProvider(config['network']);  // Make a wallet instance using private key and provider const wallet = new ethers.Wallet(config['private_key'] , provider);  const address = config["ERC20"]; const abi = erc_json.abi;  erc20 = new ethers.Contract( address , abi , wallet );  document.getElementById("send").onsubmit = async function(e) {     e.preventDefault();     let address = document.getElementById("address").value;     document.getElementById("status").innerText = "Waiting for transaction to get published...";     let tx = await erc20.functions.transfer(address, "1000000000000000000");     let tx_hash = tx.hash;     let node = document.createElement("LI");     let link = document.createElement("A");     link.target = "_blank";     link.href = `https://${config["network"]}.etherscan.io/tx/` + tx_hash;     let textnode = document.createTextNode(tx_hash);     link.appendChild(textnode);     node.appendChild(link);     document.getElementById("transactions").appendChild(node);     document.getElementById("status").innerText = "Waiting for transaction to be mined...";     await tx.wait();     document.getElementById("status").innerText = "Transaction confirmed";     return false; };

首先需要指定要使用的网络/提供器:

const provider = ethers.getDefaultProvider(config['network']);

为了与合约交互,需要2个东西:

  • 合约部署地址
  • 合约ABI接口

在上面app.js中,我们从配置文件引入了合约地址,从合约编译结果目录中 引入了合约ABI:

//import the json of the contract which you want to interact const erc_json = require('./build/ERC20.json');  const config = require('./config.json'); const address = config["ERC20"]; const abi = erc_json.abi;

创建钱包实例

为了创建合约实例,我们需要先创建一个钱包实例,这样不管什么时候调用 setter方法,都需要一个私钥来签名交易。在ethers.js中,你只需要 创建钱包,所有的setter方法就会由这个钱包签名。

const wallet = new ethers.Wallet(config['private_key'] , provider);

你也可以使用keystore和助记词来创建一个钱包。如果你希望用这个钱包与 智能合约交互,你还需要传入提供器。如果你只是想用私钥签名消息,那么 就不需要提供器了。

erc20 = new ethers.Contract( address , abi , wallet );

上面的代码创建了一个合约实例,然后你就可以像这样调用合约函数:

erc20.functions.function_name_in_smart_contract(parameters);

备注:在ERC20中定义了一个函数transfer,它的参数是转账地址和代币数量。 下面的代码调用了合约的transfer函数,钱包将签名这个交易,然后发布到指定 的网络中:

erc20.functions.transfer(address, "1000000000000000000");

注意:无论任何时候你要创建一个交易,钱包里都需要以太币来支付交易手续费。

修改启动脚本

在package.json中添加:

"deploy": "node compile.js && node deploy.js ERC20",  "build": "webpack — mode production',

修改后的package.json如下:

{   "name": "ethers-template",   "version": "1.0.0",   "description": "",   "main": "webpack.config.js",   "scripts": {     "deploy": "node compile.js && node deploy.js ERC20",     "build": "webpack --mode production",     "test": "echo "Error: no test specified" && exit 1"   },   "keywords": [],   "author": "",   "license": "ISC",   "dependencies": {     "ethers": "^4.0.37",     "fs-extra": "^8.1.0",     "solc": "^0.5.11"   },   "devDependencies": {     "@babel/core": "^7.6.0",     "@babel/plugin-proposal-object-rest-spread": "^7.5.5",     "@babel/preset-env": "^7.6.0",     "babel-loader": "^8.0.6",     "babel-polyfill": "^6.26.0",     "webpack": "^4.40.2",     "webpack-cli": "^3.3.9",     "webpack-dev-server": "^3.8.1"   } }

如果你后续修改了智能合约,就需要重新编译和部署。可以用一个命令来完成合约的编译和部署:

~/ethers-template$ npm run deploy

这个命令会自动修改配置文件和合约构件文件。这样在与合约交互时,你就不需要修改合约地址或者ABI接口了。

如果你修改了app.js,也需要重新构建前端代码:

~/ethers-template$ npm run build

这会生成新的发布版本:dist/bundle.js

创建宿主html文件

创建一个新的文件index.html:

~/ethers-template$ touch index.html

按如下修改:

<!doctype html> <html lang="en">     <head>         <meta charset="utf-8"/>         <title>Ethers Template</title>         <style> body {     padding-top: 75px; }         .search-container {             width: 490px;             display: block;             margin: 0 auto;         }         input#address {             margin: 0 auto;             width: 100%;             height: 45px;             padding: 0 20px;             font-size: 1rem;             border: 1px solid #D0CFCE;             outline: none;         }         .center {             text-align: center;         }         ol {             counter-reset: list;             list-style: none;         }         li {             counter-increment: list;             margin-bottom: 10px;         }         li::before {             content: counter(list, decimal-leading-zero);             background: #2b4353;             font-family: Arial, sans-serif;             color: #fff;             font-size: 13px;             text-align: center;             border-radius: 50%;             width: 2.2em;             height: 2.2em;             line-height: 2.3em;             display: inline-block;             margin-right: 1em;         }         .button {             background-color: #4CAF50;             border: none;             color: white;             padding: 15px 32px;             text-align: center;             text-decoration: none;             display: inline-block;             font-size: 16px;             margin: 19px;             cursor: pointer;         }         </style>     </head>      <body>         <div class="center">             <h1>Ethers Template</h1>             <form class="search-container" id="send">                 <input type="text" id="address" placeholder="Enter you address to get kanchan Coin">                 <button type="submit" class="button">Send</button>             </form>             <h2>Status:</h2>             <p id="status"></p>             <h2>Transactions</h2>             <ol id="transactions">             </ol>         </div>         <script src="dist/bundle.js"></script>     </body> </html>

现在你的文件夹看起来就是这样了:

用Ethers.js构建一个简单的DApp

其中build文件夹将在运行compile.js之后自动创建,而dist 文件夹是在npm run build执行后自动创建。

用浏览器访问看起来是这样:

用Ethers.js构建一个简单的DApp

原文链接:Compiling, Deploying and Interacting with Smart Contract using JavaScript

部分转自网络,侵权联系删除www.interchains.cchttps://www.interchains.cc/22246.html

区块链毕设网(www.interchains.cc)全网最靠谱的原创区块链毕设代做网站 部分资料来自网络,侵权联系删除! 最全最大的区块链源码站 ! QQ3039046426
区块链知识分享网, 以太坊dapp资源网, 区块链教程, fabric教程下载, 区块链书籍下载, 区块链资料下载, 区块链视频教程下载, 区块链基础教程, 区块链入门教程, 区块链资源 » 用Ethers.js构建一个简单的DApp

提供最优质的资源集合

立即查看 了解详情