以太坊dApp入门开发

一、环境搭建

1、安装nodejs,附带npm

2、本地搭建hardhat开发环境

mkdir my-wave-portal
cd my-wave-portal
npm config set registry https://registry.npm.taobao.org
npm init -y
npm install --save-dev hardhat

启动hardhat交互命令行:

npx hardhat
888    888                      888 888               888
888    888                      888 888               888
888    888                      888 888               888
8888888888  8888b.  888d888 .d88888 88888b.   8888b.  888888
888    888     "88b 888P"  d88" 888 888 "88b     "88b 888
888    888 .d888888 888    888  888 888  888 .d888888 888
888    888 888  888 888    Y88b 888 888  888 888  888 Y88b.
888    888 "Y888888 888     "Y88888 888  888 "Y888888  "Y888

Welcome to Hardhat v2.12.2

? What do you want to do? ...
> Create a JavaScript project
  Create a TypeScript project
  Create an empty hardhat.config.js
  Quit

Create a JavaScript project,接下来会问你项目根目录、是否添加.gitignore,向hardhat发送崩溃报告之类的,全选yes就完了。

安装其他依赖:

npm install --save-dev chai @nomiclabs/hardhat-ethers ethers @nomicfoundation/hardhat-toolbox @nomicfoundation/hardhat-chai-matchers

生成本地的几个随机账户:

>npx hardhat node
You have both ethereum-waffle and @nomicfoundation/hardhat-chai-matchers installed. They don't work correctly together, so please make sure you only use one.

We recommend you migrate to @nomicfoundation/hardhat-chai-matchers. Learn how to do it here: https://hardhat.org/migrate-from-waffle
Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/

Accounts
========

WARNING: These accounts, and their private keys, are publicly known.
Any funds sent to them on Mainnet or any other live network WILL BE LOST.

Account #0: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000 ETH)
Private Key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

Account #1: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 (10000 ETH)
Private Key: 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d

......

Account #19: 0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199 (10000 ETH)
Private Key: 0xdf57089febbacf7ba0bc227dafbffa9fc08a93fdc68e1e42411a14efcf23656e

WARNING: These accounts, and their private keys, are publicly known.
Any funds sent to them on Mainnet or any other live network WILL BE LOST.

多贴心,还告诉你不要往这里边的账户转钱:)

测试一下本地环境是否正常:

>npx hardhat compile
You have both ethereum-waffle and @nomicfoundation/hardhat-chai-matchers installed. They don't work correctly together, so please make sure you only use one.

We recommend you migrate to @nomicfoundation/hardhat-chai-matchers. Learn how to do it here: https://hardhat.org/migrate-from-waffle
Downloading compiler 0.8.17
Compiled 1 Solidity file successfully

>npx hardhat test
You have both ethereum-waffle and @nomicfoundation/hardhat-chai-matchers installed. They don't work correctly together, so please make sure you only use one.

We recommend you migrate to @nomicfoundation/hardhat-chai-matchers. Learn how to do it here: https://hardhat.org/migrate-from-waffle


  Lock
    Deployment
      √ Should set the right unlockTime (1651ms)
      √ Should set the right owner
      √ Should receive and store the funds to lock
      √ Should fail if the unlockTime is not in the future (52ms)
    Withdrawals
      Validations
        √ Should revert with the right error if called too soon
        √ Should revert with the right error if called from another account
        √ Shouldn't fail if the unlockTime has arrived and the owner calls it (49ms)
      Events
        √ Should emit an event on withdrawals
      Transfers
        √ Should transfer the funds to the owner

  9 passing (2s)

都没问题,可以把工程导入到vs code了,装好hardhat solidity插件后导入。目录如下图:

脚手架里边的几个文件用不着可以删掉:test/Lock.js, script/deploy.js, contracts/Lock.sol

WavePortal.sol文件放在contracts目录,这是合约的源文件。

scripts目录里边创建run.jsdeploy.js,这两个分别用来本地执行测试以及向链上部署合约,对应的执行命令:

npx hardhat run scripts/run.js
npx hardhat run scripts/deploy.js --network goerli

https://buildspace.so/p/build-solidity-web3-app/lessons/get-local-ethereum-network-running

二、接入以太坊

Alchemy网站、这玩意是个BaaS平台,通过api url和key接入Alchemy、让它来帮我们接入以太坊。

注册好了之后可以得到接入API url和key,分mainnet和goerli testnet,还可以领取goerli的水龙头测试币。

三、dApp架构

整个dApp项目分成一个前端react js页面应用,以及部署在以太坊goerli testnet上的WavePortal合约组成。

  • 前端页面使用ethers.js来完成与MetaMask钱包通信,以及和以太坊通信(准确说是和Alchemy平台通信,由Alchemy来连接以太坊)。
  • 合约里边提供了几个public方法供外部应用调用;有emit event事件供外部应用订阅,外部应用可以跟进事件触发来执行回调函数;合约内部有一些状态变量,比如数组、map等等可以存储数据。智能合约有方法有存储,就像个“有状态的对象实例”。
1、Solidity智能合约

前面我们用hardhat搭好了solidity智能合约的脚手架工程,接下来往里边填内容,需要关注的有以下几个文件:

  • contracts/WavePortal.sol:solidity合约源文件
  • script/run.js, script/deploy.js :运行测试和部署脚本
  • artifacts/contracts/WavePortal.sol/WavePortal.json :合约的ABI文件

WavePortal.sol:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

import "hardhat/console.sol";

contract WavePortal {
    uint256 totalWaves;  //wave次数计数器

    uint256 private seed; //随机种子
    
   //新的wave事件,调用wave()方法时emit
    event NewWave(address indexed from, uint256 timestamp, string message);
    
    //Wave结构体
    struct Wave {
        address waver;
        string message;
        uint256 timestamp;
    }

    Wave[] waves;  //在合约里,存放所有wave数据的数组
    
    //存放每个wave的人最后一次执行合约交易的时间,每个人15分钟只能wave一次
    mapping (address => uint256) public lastWavedAt;
    
    //合约的构造函数,payable表示合约可以支付ETH给其他地址
    constructor() payable {
        console.log("Hello, this a smart contract!");
        seed = (block.timestamp + block.difficulty) % 100; //seed初始化
    }

    //核心方法
    function wave(string memory _message) public {
        //require前面的表达式必须为真,否则抛后面异常信息;这里是如果该地址距上次交易不到15分钟则不允许再次发起交易
        require(lastWavedAt[msg.sender] + 15 minutes < block.timestamp , "Error: Wait 15 Minutes please");

        lastWavedAt[msg.sender] = block.timestamp; //更新mapping

        totalWaves += 1;
        console.log("%s has waved!", msg.sender);

        waves.push( Wave(msg.sender, _message, block.timestamp) ); //数组里新push一个Wave

        seed = (block.timestamp + block.difficulty + seed) % 100; //计算seed
        console.log("Random # generated: d%", seed);

        if(seed <= 50){ //本次seed<=50则向发起wave交易的地址发放奖励0.0001 ETH
            console.log("%s won!", msg.sender);
            uint256 prizeAmount = 0.0001 ether;
            require(prizeAmount <= address(this).balance, "Error: Trying to withdraw more money than the contract has."); //判断合约是否余额不足
            (bool success, ) = (msg.sender).call{value: prizeAmount}(""); //给msg.sender对应的地址转账prizeAmount
            require(success, "Failed to withdraw money from contract.");
        }

        emit NewWave(msg.sender, block.timestamp, _message); //发射NewWave事件,前端可以订阅并捕捉到

        
    }
    
    //返回当前所有wave的数组
    function getAllWaves() public view returns (Wave[] memory) {
        return waves;
    }
    //返回wave计数
    function getTotalWaves() public view returns (uint256){
        console.log("We have %d total waves!", totalWaves);
        return totalWaves;
    }
}

run.js:

const main = async () => {
    const [owner, randomPerson] = await hre.ethers.getSigners();

    const waveContractFactory = await hre.ethers.getContractFactory("WavePortal");
    const waveContract = await waveContractFactory.deploy({
        value: hre.ethers.utils.parseEther('0.01'),   //部署合约的时候充值0.01 ETH
    });

    await waveContract.deployed();
    console.log("合约部署到如下地址:", waveContract.address);
    console.log("合约部署人:", owner.address);

    let contractBalance = await hre.ethers.provider.getBalance(waveContract.address);
    console.log('当前合约余额:', hre.ethers.utils.formatEther(contractBalance));

    let waveCount;
    waveCount = await waveContract.getTotalWaves();

    let waveTxn;
  
    waveTxn = await waveContract.wave('hello, 豆畜子');
    await waveTxn.wait();
    
    waveCount = await waveContract.getTotalWaves();

    contractBalance = await hre.ethers.provider.getBalance(waveContract.address);
    console.log('当前合约余额:', hre.ethers.utils.formatEther(contractBalance));

    waveTxn = await waveContract.connect(randomPerson).wave('hello, 肥兔子');
    //waveTxn = await waveContract.wave('hello, 肥兔子');
    await waveTxn.wait();

    waveCount = await waveContract.getTotalWaves();

    contractBalance = await hre.ethers.provider.getBalance(waveContract.address);
    console.log('当前合约余额:', hre.ethers.utils.formatEther(contractBalance));

    let allWaves = await waveContract.getAllWaves();
    console.log(allWaves);

};

const runMain = async () => {
    try{
        await main();
        process.exit(0);
    }catch(error){
        console.log(error);
        process.exit(1);
    }
};

runMain();

deploy.js:

const main = async () => {
    const [deployer] = await hre.ethers.getSigners();
    const accountBalance = await deployer.getBalance();

    console.log("使用如下账户部署合约:", deployer.address);
    console.log("账户余额:", accountBalance.toString());

    const factory = await hre.ethers.getContractFactory("WavePortal");
    const contract = await factory.deploy({
        value: hre.ethers.utils.parseEther('0.01'), //部署合约的时候充值0.01 ETH
    });
    await contract.deployed();

    let contractBalance = await hre.ethers.provider.getBalance(contract.address);
    console.log('当前合约余额:', hre.ethers.utils.formatEther(contractBalance));

    console.log("WavePortal address: ", contract.address);

};

const runMain = async () => {
    try{
        await main();
        process.exit(0);
    }catch(error){
        console.log(error);
        process.exit(1);
    }
};

runMain();

WavePortal.json ABI文件:

{
  "_format": "hh-sol-artifact-1",
  "contractName": "WavePortal",
  "sourceName": "contracts/WavePortal.sol",
  "abi": [
    {
      "inputs": [],
      "stateMutability": "payable",
      "type": "constructor"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": true,
          "internalType": "address",
          "name": "from",
          "type": "address"
        },
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "timestamp",
          "type": "uint256"
        },
        {
          "indexed": false,
          "internalType": "string",
          "name": "message",
          "type": "string"
        }
      ],
      "name": "NewWave",
      "type": "event"
    },
    {
      "inputs": [],
      "name": "getAllWaves",
      "outputs": [
        {
          "components": [
            {
              "internalType": "address",
              "name": "waver",
              "type": "address"
            },
            {
              "internalType": "string",
              "name": "message",
              "type": "string"
            },
            {
              "internalType": "uint256",
              "name": "timestamp",
              "type": "uint256"
            }
          ],
          "internalType": "struct WavePortal.Wave[]",
          "name": "",
          "type": "tuple[]"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getTotalWaves",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "",
          "type": "address"
        }
      ],
      "name": "lastWavedAt",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "string",
          "name": "_message",
          "type": "string"
        }
      ],
      "name": "wave",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }
  ],
  "bytecode": "0x60806040526200004f6040518060400160405280601d81526020017f48656c6c6f2c2074686973206120736d61727420636f6e7472616374210000008152506200007760201b620006811760201c565b606444426200005f91906200017c565b6200006b9190620001e6565b600181905550620002dc565b62000117816040516024016200008e9190620002b8565b6040516020818303038152906040527f41304fac000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506200011a60201b60201c565b50565b60008151905060006a636f6e736f6c652e6c6f679050602083016000808483855afa5050505050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000620001898262000143565b9150620001968362000143565b9250828201905080821115620001b157620001b06200014d565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000620001f38262000143565b9150620002008362000143565b925082620002135762000212620001b7565b5b828206905092915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156200025a5780820151818401526020810190506200023d565b60008484015250505050565b6000601f19601f8301169050919050565b600062000284826200021e565b62000290818562000229565b9350620002a28185602086016200023a565b620002ad8162000266565b840191505092915050565b60006020820190508181036000830152620002d4818462000277565b905092915050565b61136680620002ec6000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c8063449d46c0146100515780639a2cdc081461006d578063a42a46631461008b578063bd43a908146100bb575b600080fd5b61006b600480360381019061006691906109d5565b6100d9565b005b6100756104cd565b6040516100829190610a37565b60405180910390f35b6100a560048036038101906100a09190610ab0565b610518565b6040516100b29190610a37565b60405180910390f35b6100c3610530565b6040516100d09190610c8c565b60405180910390f35b42610384600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546101279190610cdd565b10610167576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161015e90610d6e565b60405180910390fd5b42600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555060016000808282546101bd9190610cdd565b925050819055506102036040518060400160405280600d81526020017f25732068617320776176656421000000000000000000000000000000000000008152503361071a565b600260405180606001604052803373ffffffffffffffffffffffffffffffffffffffff16815260200183815260200142815250908060018154018082558091505060019003906000526020600020906003020160009091909190915060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010190816102bb9190610f9a565b50604082015181600201555050606460015444426102d99190610cdd565b6102e39190610cdd565b6102ed919061109b565b6001819055506103346040518060400160405280601681526020017f52616e646f6d20232067656e6572617465643a206425000000000000000000008152506001546107b6565b60326001541161047a5761037d6040518060400160405280600781526020017f257320776f6e21000000000000000000000000000000000000000000000000008152503361071a565b6000655af3107a40009050478111156103cb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103c29061113e565b60405180910390fd5b60003373ffffffffffffffffffffffffffffffffffffffff16826040516103f19061118f565b60006040518083038185875af1925050503d806000811461042e576040519150601f19603f3d011682016040523d82523d6000602084013e610433565b606091505b5050905080610477576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161046e90611216565b60405180910390fd5b50505b3373ffffffffffffffffffffffffffffffffffffffff167f5f7e16dc676677766a70e9c5628aa6c54ddb8b6e5188e2ae1e1f17f1ffbea71642836040516104c292919061126f565b60405180910390a250565b60006105106040518060400160405280601781526020017f5765206861766520256420746f74616c207761766573210000000000000000008152506000546107b6565b600054905090565b60036020528060005260406000206000915090505481565b60606002805480602002602001604051908101604052809291908181526020016000905b8282101561067857838290600052602060002090600302016040518060600160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820180546105dd90610dbd565b80601f016020809104026020016040519081016040528092919081815260200182805461060990610dbd565b80156106565780601f1061062b57610100808354040283529160200191610656565b820191906000526020600020905b81548152906001019060200180831161063957829003601f168201915b5050505050815260200160028201548152505081526020019060010190610554565b50505050905090565b61071781604051602401610695919061129f565b6040516020818303038152906040527f41304fac000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050610852565b50565b6107b282826040516024016107309291906112d0565b6040516020818303038152906040527f319af333000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050610852565b5050565b61084e82826040516024016107cc929190611300565b6040516020818303038152906040527fb60e72cc000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050610852565b5050565b60008151905060006a636f6e736f6c652e6c6f679050602083016000808483855afa5050505050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6108e282610899565b810181811067ffffffffffffffff82111715610901576109006108aa565b5b80604052505050565b600061091461087b565b905061092082826108d9565b919050565b600067ffffffffffffffff8211156109405761093f6108aa565b5b61094982610899565b9050602081019050919050565b82818337600083830152505050565b600061097861097384610925565b61090a565b90508281526020810184848401111561099457610993610894565b5b61099f848285610956565b509392505050565b600082601f8301126109bc576109bb61088f565b5b81356109cc848260208601610965565b91505092915050565b6000602082840312156109eb576109ea610885565b5b600082013567ffffffffffffffff811115610a0957610a0861088a565b5b610a15848285016109a7565b91505092915050565b6000819050919050565b610a3181610a1e565b82525050565b6000602082019050610a4c6000830184610a28565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610a7d82610a52565b9050919050565b610a8d81610a72565b8114610a9857600080fd5b50565b600081359050610aaa81610a84565b92915050565b600060208284031215610ac657610ac5610885565b5b6000610ad484828501610a9b565b91505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b610b1281610a72565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610b52578082015181840152602081019050610b37565b60008484015250505050565b6000610b6982610b18565b610b738185610b23565b9350610b83818560208601610b34565b610b8c81610899565b840191505092915050565b610ba081610a1e565b82525050565b6000606083016000830151610bbe6000860182610b09565b5060208301518482036020860152610bd68282610b5e565b9150506040830151610beb6040860182610b97565b508091505092915050565b6000610c028383610ba6565b905092915050565b6000602082019050919050565b6000610c2282610add565b610c2c8185610ae8565b935083602082028501610c3e85610af9565b8060005b85811015610c7a5784840389528151610c5b8582610bf6565b9450610c6683610c0a565b925060208a01995050600181019050610c42565b50829750879550505050505092915050565b60006020820190508181036000830152610ca68184610c17565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610ce882610a1e565b9150610cf383610a1e565b9250828201905080821115610d0b57610d0a610cae565b5b92915050565b600082825260208201905092915050565b7f4572726f723a2057616974203135204d696e7574657320706c65617365000000600082015250565b6000610d58601d83610d11565b9150610d6382610d22565b602082019050919050565b60006020820190508181036000830152610d8781610d4b565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610dd557607f821691505b602082108103610de857610de7610d8e565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302610e507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82610e13565b610e5a8683610e13565b95508019841693508086168417925050509392505050565b6000819050919050565b6000610e97610e92610e8d84610a1e565b610e72565b610a1e565b9050919050565b6000819050919050565b610eb183610e7c565b610ec5610ebd82610e9e565b848454610e20565b825550505050565b600090565b610eda610ecd565b610ee5818484610ea8565b505050565b5b81811015610f0957610efe600082610ed2565b600181019050610eeb565b5050565b601f821115610f4e57610f1f81610dee565b610f2884610e03565b81016020851015610f37578190505b610f4b610f4385610e03565b830182610eea565b50505b505050565b600082821c905092915050565b6000610f7160001984600802610f53565b1980831691505092915050565b6000610f8a8383610f60565b9150826002028217905092915050565b610fa382610b18565b67ffffffffffffffff811115610fbc57610fbb6108aa565b5b610fc68254610dbd565b610fd1828285610f0d565b600060209050601f8311600181146110045760008415610ff2578287015190505b610ffc8582610f7e565b865550611064565b601f19841661101286610dee565b60005b8281101561103a57848901518255600182019150602085019450602081019050611015565b868310156110575784890151611053601f891682610f60565b8355505b6001600288020188555050505b505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006110a682610a1e565b91506110b183610a1e565b9250826110c1576110c061106c565b5b828206905092915050565b7f4572726f723a20547279696e6720746f207769746864726177206d6f7265206d60008201527f6f6e6579207468616e2074686520636f6e7472616374206861732e0000000000602082015250565b6000611128603b83610d11565b9150611133826110cc565b604082019050919050565b600060208201905081810360008301526111578161111b565b9050919050565b600081905092915050565b50565b600061117960008361115e565b915061118482611169565b600082019050919050565b600061119a8261116c565b9150819050919050565b7f4661696c656420746f207769746864726177206d6f6e65792066726f6d20636f60008201527f6e74726163742e00000000000000000000000000000000000000000000000000602082015250565b6000611200602783610d11565b915061120b826111a4565b604082019050919050565b6000602082019050818103600083015261122f816111f3565b9050919050565b600061124182610b18565b61124b8185610d11565b935061125b818560208601610b34565b61126481610899565b840191505092915050565b60006040820190506112846000830185610a28565b81810360208301526112968184611236565b90509392505050565b600060208201905081810360008301526112b98184611236565b905092915050565b6112ca81610a72565b82525050565b600060408201905081810360008301526112ea8185611236565b90506112f960208301846112c1565b9392505050565b6000604082019050818103600083015261131a8185611236565b90506113296020830184610a28565b939250505056fea2646970667358221220634855d1b0911e130596a92fa9f800c86f809688ef3c921cb7305897abf5de7c64736f6c63430008110033",
  "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c8063449d46c0146100515780639a2cdc081461006d578063a42a46631461008b578063bd43a908146100bb575b600080fd5b61006b600480360381019061006691906109d5565b6100d9565b005b6100756104cd565b6040516100829190610a37565b60405180910390f35b6100a560048036038101906100a09190610ab0565b610518565b6040516100b29190610a37565b60405180910390f35b6100c3610530565b6040516100d09190610c8c565b60405180910390f35b42610384600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546101279190610cdd565b10610167576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161015e90610d6e565b60405180910390fd5b42600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555060016000808282546101bd9190610cdd565b925050819055506102036040518060400160405280600d81526020017f25732068617320776176656421000000000000000000000000000000000000008152503361071a565b600260405180606001604052803373ffffffffffffffffffffffffffffffffffffffff16815260200183815260200142815250908060018154018082558091505060019003906000526020600020906003020160009091909190915060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010190816102bb9190610f9a565b50604082015181600201555050606460015444426102d99190610cdd565b6102e39190610cdd565b6102ed919061109b565b6001819055506103346040518060400160405280601681526020017f52616e646f6d20232067656e6572617465643a206425000000000000000000008152506001546107b6565b60326001541161047a5761037d6040518060400160405280600781526020017f257320776f6e21000000000000000000000000000000000000000000000000008152503361071a565b6000655af3107a40009050478111156103cb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103c29061113e565b60405180910390fd5b60003373ffffffffffffffffffffffffffffffffffffffff16826040516103f19061118f565b60006040518083038185875af1925050503d806000811461042e576040519150601f19603f3d011682016040523d82523d6000602084013e610433565b606091505b5050905080610477576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161046e90611216565b60405180910390fd5b50505b3373ffffffffffffffffffffffffffffffffffffffff167f5f7e16dc676677766a70e9c5628aa6c54ddb8b6e5188e2ae1e1f17f1ffbea71642836040516104c292919061126f565b60405180910390a250565b60006105106040518060400160405280601781526020017f5765206861766520256420746f74616c207761766573210000000000000000008152506000546107b6565b600054905090565b60036020528060005260406000206000915090505481565b60606002805480602002602001604051908101604052809291908181526020016000905b8282101561067857838290600052602060002090600302016040518060600160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820180546105dd90610dbd565b80601f016020809104026020016040519081016040528092919081815260200182805461060990610dbd565b80156106565780601f1061062b57610100808354040283529160200191610656565b820191906000526020600020905b81548152906001019060200180831161063957829003601f168201915b5050505050815260200160028201548152505081526020019060010190610554565b50505050905090565b61071781604051602401610695919061129f565b6040516020818303038152906040527f41304fac000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050610852565b50565b6107b282826040516024016107309291906112d0565b6040516020818303038152906040527f319af333000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050610852565b5050565b61084e82826040516024016107cc929190611300565b6040516020818303038152906040527fb60e72cc000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050610852565b5050565b60008151905060006a636f6e736f6c652e6c6f679050602083016000808483855afa5050505050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6108e282610899565b810181811067ffffffffffffffff82111715610901576109006108aa565b5b80604052505050565b600061091461087b565b905061092082826108d9565b919050565b600067ffffffffffffffff8211156109405761093f6108aa565b5b61094982610899565b9050602081019050919050565b82818337600083830152505050565b600061097861097384610925565b61090a565b90508281526020810184848401111561099457610993610894565b5b61099f848285610956565b509392505050565b600082601f8301126109bc576109bb61088f565b5b81356109cc848260208601610965565b91505092915050565b6000602082840312156109eb576109ea610885565b5b600082013567ffffffffffffffff811115610a0957610a0861088a565b5b610a15848285016109a7565b91505092915050565b6000819050919050565b610a3181610a1e565b82525050565b6000602082019050610a4c6000830184610a28565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610a7d82610a52565b9050919050565b610a8d81610a72565b8114610a9857600080fd5b50565b600081359050610aaa81610a84565b92915050565b600060208284031215610ac657610ac5610885565b5b6000610ad484828501610a9b565b91505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b610b1281610a72565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610b52578082015181840152602081019050610b37565b60008484015250505050565b6000610b6982610b18565b610b738185610b23565b9350610b83818560208601610b34565b610b8c81610899565b840191505092915050565b610ba081610a1e565b82525050565b6000606083016000830151610bbe6000860182610b09565b5060208301518482036020860152610bd68282610b5e565b9150506040830151610beb6040860182610b97565b508091505092915050565b6000610c028383610ba6565b905092915050565b6000602082019050919050565b6000610c2282610add565b610c2c8185610ae8565b935083602082028501610c3e85610af9565b8060005b85811015610c7a5784840389528151610c5b8582610bf6565b9450610c6683610c0a565b925060208a01995050600181019050610c42565b50829750879550505050505092915050565b60006020820190508181036000830152610ca68184610c17565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610ce882610a1e565b9150610cf383610a1e565b9250828201905080821115610d0b57610d0a610cae565b5b92915050565b600082825260208201905092915050565b7f4572726f723a2057616974203135204d696e7574657320706c65617365000000600082015250565b6000610d58601d83610d11565b9150610d6382610d22565b602082019050919050565b60006020820190508181036000830152610d8781610d4b565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610dd557607f821691505b602082108103610de857610de7610d8e565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302610e507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82610e13565b610e5a8683610e13565b95508019841693508086168417925050509392505050565b6000819050919050565b6000610e97610e92610e8d84610a1e565b610e72565b610a1e565b9050919050565b6000819050919050565b610eb183610e7c565b610ec5610ebd82610e9e565b848454610e20565b825550505050565b600090565b610eda610ecd565b610ee5818484610ea8565b505050565b5b81811015610f0957610efe600082610ed2565b600181019050610eeb565b5050565b601f821115610f4e57610f1f81610dee565b610f2884610e03565b81016020851015610f37578190505b610f4b610f4385610e03565b830182610eea565b50505b505050565b600082821c905092915050565b6000610f7160001984600802610f53565b1980831691505092915050565b6000610f8a8383610f60565b9150826002028217905092915050565b610fa382610b18565b67ffffffffffffffff811115610fbc57610fbb6108aa565b5b610fc68254610dbd565b610fd1828285610f0d565b600060209050601f8311600181146110045760008415610ff2578287015190505b610ffc8582610f7e565b865550611064565b601f19841661101286610dee565b60005b8281101561103a57848901518255600182019150602085019450602081019050611015565b868310156110575784890151611053601f891682610f60565b8355505b6001600288020188555050505b505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006110a682610a1e565b91506110b183610a1e565b9250826110c1576110c061106c565b5b828206905092915050565b7f4572726f723a20547279696e6720746f207769746864726177206d6f7265206d60008201527f6f6e6579207468616e2074686520636f6e7472616374206861732e0000000000602082015250565b6000611128603b83610d11565b9150611133826110cc565b604082019050919050565b600060208201905081810360008301526111578161111b565b9050919050565b600081905092915050565b50565b600061117960008361115e565b915061118482611169565b600082019050919050565b600061119a8261116c565b9150819050919050565b7f4661696c656420746f207769746864726177206d6f6e65792066726f6d20636f60008201527f6e74726163742e00000000000000000000000000000000000000000000000000602082015250565b6000611200602783610d11565b915061120b826111a4565b604082019050919050565b6000602082019050818103600083015261122f816111f3565b9050919050565b600061124182610b18565b61124b8185610d11565b935061125b818560208601610b34565b61126481610899565b840191505092915050565b60006040820190506112846000830185610a28565b81810360208301526112968184611236565b90509392505050565b600060208201905081810360008301526112b98184611236565b905092915050565b6112ca81610a72565b82525050565b600060408201905081810360008301526112ea8185611236565b90506112f960208301846112c1565b9392505050565b6000604082019050818103600083015261131a8185611236565b90506113296020830184610a28565b939250505056fea2646970667358221220634855d1b0911e130596a92fa9f800c86f809688ef3c921cb7305897abf5de7c64736f6c63430008110033",
  "linkReferences": {},
  "deployedLinkReferences": {}
}

hardhat工程的配置文件hardhat.config.js:

require("@nomicfoundation/hardhat-toolbox");

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
  solidity: "0.8.17",
  networks: {
    goerli: {
      url: 'https://eth-goerli.g.alchemy.com/v2/alchemy领的API_KEY',
      accounts: ['账户私钥,因为跟mainnet一样,所以注意保密!!!'],
    },
  },
};

本地测试合约和部署合约:

npx hardhat run script/run.js

npx hardhat run script/deploy.js --network goerli
2、前端React应用

https://replit.com/ 在线开发环境,构建react应用。

App.jsx代码如下:

import React, { useEffect ,useState } from "react";
import { ethers } from "ethers";
import './App.css';
import abi from './utils/WaveContract.json';

export default function App() {


  /*
  * Just a state variable we use to store our user's public wallet.
  */
  const [currentAccount, setCurrentAccount] = useState("");
  //状态变量,存储前端发来的数据
  const [allWaves, setAllWaves] = useState([]);
  
  //合约地址
  const contractAddress = "0x662c74BDFAee0d24C75d9626AB770072b881CCc0";
  //ABI文件,相当于合约的接口声明
  const contractABI = abi.abi;

  /*
   * 查询合约里的wave数据
   */
  const getAllWaves = async () => {
    try {
      const { ethereum } = window;	//如果安装了metamask插件,那么会向window注入ethereum对象
        
      if(ethereum){
        /*
          ethers.js用法
          provider是连接链的抽象,signer是钱包账户的抽象
        */
        const provider = new ethers.providers.Web3Provider(ethereum);
        const signer = provider.getSigner();
        //前端合约对象
        const wavePortalContract = new ethers.Contract(contractAddress, contractABI, signer);

        const waves = await wavePortalContract.getAllWaves(); //调用智能合约的getAllWaves()方法

        let wavesCleaned = [];
        waves.forEach(
            wave => {
              wavesCleaned.push({
                address: wave.waver,
                timestamp: new Date(wave.timestamp*1000),
                message: wave.message
              });
            }
        );
        
        /*
         * 把查询回来的数据存储到前端的allWaves对象里
         */
        setAllWaves(wavesCleaned);
        
      }else{
        console.log("没有ethereum对象!");
      }
    } catch (error) {
      console.log(error);
    }
  }
  
  /**
  * 
  */
  const checkIfWalletIsConnected = async () => {

    try{
      /*
      * 是否有接入window.ethereum;是否安装了钱包
      */
      const { ethereum } = window;

      if (!ethereum) {
        console.log("Make sure you have metamask!");
        return;
      } else {
        console.log("We have the ethereum object", ethereum);
      }
        
      /*
      * 用户是否授权
        尝试去获取本地钱包账户列表
      */
      const accounts = await ethereum.request({ method: 'eth_accounts' });
      
      if (accounts.length !== 0) {
        const account = accounts[0]; //取本地钱包第一个账户
        console.log("Found an authorized account:", account);
        setCurrentAccount(account);  //把当前账户存到前端currentAccount对象
        getAllWaves();
      } else {
        console.log("No authorized account found")
      }
    }catch(error){
      console.log(error);
    }
    
  }


  /**
  * 连接钱包
  */
  const connectWallet = async () => {
    try {
      const { ethereum } = window;

      if (!ethereum) {
        alert("Get MetaMask!");
        return;
      }
	  //请求获取本地钱包地址
      const accounts = await ethereum.request({ method: "eth_requestAccounts" });

      console.log("Connected", accounts[0]);
      setCurrentAccount(accounts[0]); //钱包地址存放到前端对象
    } catch (error) {
      console.log(error)
    }
  }

  /**
  * 向智能合约发送wave数据
  */
  const wave = async () => {
    try {
      const { ethereum } = window;

      if (ethereum) {
         
        //ethers.js三连~
        const provider = new ethers.providers.Web3Provider(ethereum);
        const signer = provider.getSigner();
        const wavePortalContract = new ethers.Contract(contractAddress, contractABI, signer);

        /*
        * 调用合约wave(message)方法,发送数据
        */
        let waveMessage = document.getElementById("waveMsg");
        const waveTxn = await wavePortalContract.wave(waveMessage.value, {gasLimit:300000});
        console.log("Mining...", waveTxn.hash);

        await waveTxn.wait();  //等待调用wave()方法这个交易执行完毕
        console.log("Mined -- ", waveTxn.hash);

      } else {
        console.log("Ethereum object doesn't exist!");
      }
    } catch (error) {
      console.log(error)
    }
}
  
  /**
  * 调用合约getTotalWaves()方法,前端计数展示合约里多少个Wave
  */
  const totalWaves = async () => {
    const { ethereum } = window;

    if (ethereum) {
      const provider = new ethers.providers.Web3Provider(ethereum);
      const signer = provider.getSigner();
      const wavePortalContract = new ethers.Contract(contractAddress, contractABI, signer);

      let count = await wavePortalContract.getTotalWaves();
      console.log("Retrieved total wave count...", count.toNumber());
      let total = document.getElementById('totalWaves');
      total.innerHTML = "Total Waves : " + count;
    }
  }
  
  /*
  * 页面载入时执行
  */
  useEffect(() => {
    checkIfWalletIsConnected(); //检查钱包是否连接
    totalWaves(); //展示计数wave数

    /**
      emit NewWave事件处理回调函数
    */
    let wavePortalContract;
    //收到事件以后重新渲染 
    const onNewWave = (from, timestamp, message) => {
      console.log("NewWave", from, timestamp, message);
      setAllWaves(prevState => [
        ...prevState,
        {
          address: from,
          timestamp: new Date(timestamp * 1000),
          message: message,
        },
      ]);  //更新前端allWaves对象后,div自动渲染了

      //totalWaves();
    };

    if(window.ethereum){
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const signer = provider.getSigner();
      wavePortalContract = new ethers.Contract(contractAddress, contractABI, signer);
      wavePortalContract.on("NewWave", onNewWave); //绑定合约的NewWave事件
    }

    return () => {
      if(wavePortalContract){
        wavePortalContract.off("NewWave", onNewWave); //解除事件绑定
      }
    };
    
  }, [])
  
  //返回渲染的页面
  return (
    <div className="mainContainer">

      <div className="dataContainer">
        <div className="header">
        👋 Hi there!
        </div>

        <div className="bio">
        I am Mr.L and I live with my wife in a small town near by HangZhou City. 
          <br></br>
          Connect your Ethereum wallet and wave at me!
        </div>
        <input id="waveMsg" type="text" ></input>
        <button className="waveButton" onClick={wave}>
          Wave at me
        </button>

        {/*
        * If there is no currentAccount render this button
        */}
        {!currentAccount && (
          <button className="waveButton" onClick={connectWallet}>
            <img src="src/metamask.ico" height="25" width="25"></img>
            Connect Wallet
          </button>
        )}
        <button id="totalWaves" className="waveButton" >

          {allWaves.map((wave, index) => {
          return (
            <div key={index} style={{ backgroundColor: "OldLace", marginTop: "16px", padding: "8px" }}>
              <div>Address: {wave.address}</div>
              <div>Time: {wave.timestamp.toString()}</div>
              <div>Message: {wave.message}</div>
            </div>)
        })}
          
        </button>
      </div>
    </div>
  );
}
DEMO演示

https://waveportal-starter-project.yanglin7.repl.co/

posted on 2022-11-15 16:05  肥兔子爱豆畜子  阅读(807)  评论(2编辑  收藏  举报

导航