Truffle MetaMask Ganache智能合约

Truffle

Truffle Suite - Truffle Suite
快速入门 Truffle | Truffle 中文文档 - DApp 开发框架 | 深入浅出区块链 (learnblockchain.cn)

Truffle is the most popular development framework for Ethereum with a mission to make your life a whole lot easier

只要记住 truffle是只能合约的一个开发框架就好了。

Features

  • 管理智能合约的生命周期
  • 自动化合约测试
  • 可编程,可部署,可发布合约
  • 不用过多的关注网络管理
  • 强大的交互式控制台

Ganache

Ganache下载
快速启动以太坊区块私有链,可以使用它来运行测试、执行命令和检查状态,同时控制链的运行方式。

Dapp Pet-Shop 教程

Truffle tutorial

  • 设置开发环境
  • 通过Truffle Box创建一个Truffle项目
  • 编写智能合约
  • 编译(compile)和迁移(migrate)智能合约
  • 测试合约
  • 创建对外接口
  • 浏览器中与app交互

开发环境

PS C:\Users\simon> node -v
v14.17.1
PS C:\Users\simon> npm -v
6.14.13
PS C:\Users\simon> truffle.cmd version
Truffle v5.4.29 (core: 5.4.29)
Solidity v0.5.16 (solc-js)
Node v14.17.1
Web3.js v1.5.3

npm 安装

Node.js 安装配置

Git 安装

Git (git-scm.com)

truffle 安装

PS C:\Users\simon> npm install -g truffle
PS C:\Users\simon> truffle.cmd version
Truffle v5.4.29 (core: 5.4.29)
Solidity v0.5.16 (solc-js)
Node v14.17.1
Web3.js v1.5.3

下载source

git clone https://github.com/truffle-box/pet-shop-box.git

目录结构:

  • contracts/ : 存放solidity智能合约文件
  • migrations/ : truffle使用migration system 来控制合约的部署。 migration 是一种额外的特殊智能合约,用来合约的维护和更改
  • test/ : 测试文件存放文字(javascript or solidity)
  • truffle-config.js : 配置文件
  • src/: node代码

编写智能合约

  • 在contracts 文件夹下新建 Adoption.sol文件,内容如下
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.8.0;

contract Adoption {
    // 声明一个数组
    address[16] public adopters;

    // 创建收养宠物函数
    function adopt(uint petId) public returns (uint){
        // 校验 petId必须为 0 - 15位
        require(petId >= 0 && petId <= 15);

        // 收养账户为 消息发送者的地址
        adopters[petId] = msg.sender;
        // 返回收养的宠物ID
        return petId;
    }

    // 从内容存中获取 收养账户信息
    // memory 给出变量的来源.
    // 函数中view 关键字,意味着不会改变合约的状态.
    function getAdopters() public view returns (address[16] memory) {
        return adopters;
    }
}

编译合约

  • 打开命令行控制台,进行项目根目录下
PS E:\study\truffle\pet-shop-box> truffle compile

Compiling your contracts...
===========================
> Compiling .\contracts\Adoption.sol
> Compiling .\contracts\Adoption.sol
> Compiling .\contracts\Migrations.sol
> Artifacts written to E:\study\truffle\pet-shop-box\build\contracts
> Compiled successfully using:
   - solc: 0.5.16+commit.9c3226ce.Emscripten.clang

Migration

从上面的compile成功,我们可以将它移动到区块链中

migration 是一个部署脚本,它将修改您的应用合约的状态,使它从一个状态变成另一个。对于第一次migration,你仅仅需要部署新代码即可。随着时间的迁移,后面的迁移可能会改变数据或者使用新的合约替换它。

在 migrations 文件夹下,你会看到1_initial_migration.js 这个合约是部署Migrations.sol的,用于观察后续智能合约迁移,并确保我们将来不会重复迁移未更改的合约。

  • 新增migration 脚本
    在migrations文件夹 下新增 2_deploy_contracts.js
var Adoption = artifacts.require("Adoption");

module.exports = function(deployer) {
  deployer.deploy(Adoption);
};

在运行执行,首先咱们将ganache打开,ganache quickly start 会运行在 默认7545端口

  • 执行migration脚本
PS E:\study\truffle\pet-shop-box> truffle migrate

1_initial_migration.js
======================

   Deploying 'Migrations'
   ----------------------
   > transaction hash:    0x3b558e9cdf1231d8ffb3445cb2f9fb01de9d0363e0b97a17f9517da318c2e5af
   > Blocks: 0            Seconds: 0
   > contract address:    0x5ccb4dc04600cffA8a67197d5b644ae71856aEE4
   > account:             0x8d9606F90B6CA5D856A9f0867a82a645e2DfFf37
   > balance:             99.99430184
   > gas used:            284908
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.00569816 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:          0.00569816 ETH


2_deploy_contracts.js
=====================

   Deploying 'Adoption'
   .............................
   .............................

在ganache中,也可以看到当前区块发生变化,切当前transaciton也多了


image.png

编辑测试

  • 在test文件夹下新增 TestAdoption.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.8.0;

import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Adoption.sol";

contract TestAdoption {

    Adoption adoption = Adoption(DeployedAddresses.Adoption());

    uint expectedPetId = 8;

    address expectedAdopter = address(this);

    function testUserCanAdoptPet() public {
        uint returnedId = adoption.adopt(expectedPetId);

        Assert.equal(returnedId, expectedPetId, "Adoption of the expected pet should match what is returned.");
    }

    function testGetAdopterAddressByPetId() public {
        address adopter = adoption.adopters(expectedPetId);

        Assert.equal(adopter, expectedAdopter, "Owner of teh expected pet should be this contract");
    }

    function testGetAdopterAddressByPetIdInArray() public {
        address[16] memory adopters = adoption.getAdopters();

        Assert.equal(adopters[expectedPetId], expectedAdopter, "Owner of the expected pet should be this contract");
    }
}

输出内容

PS E:\study\truffle\pet-shop-box> truffle test
Using network 'development'.


Compiling your contracts...
===========================
> Compiling .\contracts\Adoption.sol
> Compiling .\contracts\Migrations.sol
> Compiling .\test\TestAdoption.sol
> Compiling truffle\Assert.sol
> Compiling truffle\AssertAddress.sol
> Compiling truffle\AssertAddressArray.sol
> Compiling truffle\AssertBalance.sol
> Compiling truffle\AssertBool.sol
> Compiling truffle\AssertBytes32.sol
> Compiling truffle\AssertBytes32Array.sol
> Compiling truffle\AssertGeneral.sol
> Compiling truffle\AssertInt.sol
> Compiling truffle\AssertIntArray.sol
> Compiling truffle\AssertString.sol
> Compiling truffle\AssertUint.sol
> Compiling truffle\AssertUintArray.sol
> Compiling truffle\DeployedAddresses.sol
> Artifacts written to C:\Users\simon\AppData\Local\Temp\test--27844-JGhn0zeX68hk
> Compiled successfully using:
   - solc: 0.5.16+commit.9c3226ce.Emscripten.clang

  TestAdoption
    √ testUserCanAdoptPet (596ms)
    √ testGetAdopterAddressByPetId (818ms)
    √ testGetAdopterAddressByPetIdInArray (966ms)

  3 passing (20s)

编写用户接口与合约交互

打开项目下 src/app.js

  • initWeb3 内容如下
initWeb3: async function () {
    if (window.ethereum) {
      // 判断是否为dapp浏览器或者由新版本的metaMask已安装到浏览器插件中
      App.web3Provider = window.ethereum;
      console.log("ethereum window")
      try {
        await window.ethereum.request({ method: "eth_requestAccounts" });
      } catch (error) {
        console.log("User denied account accesss")
      }
    } else if (window.web3) {
      // web是否已经安装被安装
      console.log("web3 window")
      App.web3Provider = window.web3.currentProvider;
    } else {
      // 本地开发环境提供
      console.log("local window")
      App.web3Provider = new Web3.providers.HttpProvider("http://localhost:9545");
    }
    web3 = new Web3(App.web3Provider);

    return App.initContract();
  }
  • 实例化合约
initContract: function () {
    $.getJSON("Adoption.json", function (data) {
      //获取必要的合约文件,并且 实例化它
      var AdoptionArtifact = data;
      App.contracts.Adoption = TruffleContract(AdoptionArtifact);

      // 为合约提供provider
      App.contracts.Adoption.setProvider(App.web3Provider);
      return App.markAdopted();
    })

    return App.bindEvents();
  }
  • 更新前端UI
markAdopted: function () {
    console.log("markAdopted ...")
    var adoptionInstance;

App.contracts.Adoption.deployed().then(function (instance) {
      adoptionInstance = instance;
      console.log("markAdopted ...", adoptionInstance);
      return adoptionInstance.getAdopters.call();
    }).then(function (adopters) {
      console.log("markAdopted ... adopters ", adopters);
      for (i = 0; i < adopters.length; i++) {
        if (adopters[i] !== "0x0000000000000000000000000000000000000000") {
          $('.panel-pet').eq(i).find('button').text('Success').attr('disabled', true);
        }
      }
    }).catch(function (err) {
      console.error("markAdopted ...", err.message);
    });
  }
  • Handle Function
handleAdopt: function (event) {
    event.preventDefault();

    var petId = parseInt($(event.target).data('id'));

    var adoptionInstance;
    web3.eth.getAccounts(function(error, accounts) {
      if(error) {
        console.log(error);
      }
      var account = accounts[0];

      App.contracts.Adoption.deployed().then(function(instance) {
        adoptionInstance = instance;
        return adoptionInstance.adopt(petId, { from: account});
      }).then(function(result) {
        console.log(result);
        return App.markAdopted();
      }).catch(function(err) {
        console.log(err.message);
      });
    });
  }

运行

npm install
npm run dev

打开浏览器,服务会调用metamask插件,选择一个账户即可

1642663948(1).png

点击 按钮 adopt 会触发 handleAdopt函数。web3 会调用合约中adopt,发起transaction,然后调用 markAdopted函数标记宠物已被收养。


1642664730(1).png

打完收工。

你可能感兴趣的:(Truffle MetaMask Ganache智能合约)