以太坊智能合约ganache+truffle集成开发实验

397 阅读9分钟

1、安装前的npm、node环境准备(此处忽略、建议可使用nvm进行npm版本管理)

2、安装ganache、图形版本或cli版本二选一、即可

ganache是以太坊的单机测试环境、安装运行后也可以通过geth命令连接当前ganache

geth attach http://localhost:8545

2.1、安装ganache图形版 (参考官网github.com/trufflesuit…

打开连接 github.com/trufflesuit… File**,等待下载 windown可以运行安装[Ganache-2.7.1-win-x64-setup.exe]

安装完成后运行ganache点击右下角CONTINUE

在启动界面选择QUICKSTART即可运行ganache测试网络。

自动生成10个账户的私有链,每个账户有100个以太币

2.1、安装ganache-cli版本

安装ganache(-cli)

安装命令:

sudo npm install -g ganache-cli

运行gananche-cli

ganache-cli

自动生成10个账户的私有链,每个账户有100个以太币

Ganache CLI v6.12.2 (ganache-core: 2.13.2)


Available Accounts
==================
(0) 0x119a74123f55cf802d2d51c55A13bCefdB12397f (100 ETH)
(1) 0x76CCa6c98176f9D7263D9281053eE371A2A3D46B (100 ETH)
(2) 0x562D5CeC46312E8A7BEA5e72C9fF3378A2DaB6c8 (100 ETH)
(3) 0x82e496bF038d94b45FD1173443B1c0531251897f (100 ETH)
(4) 0x2bF178Cd9205DB4A870a48E1962C46524A374685 (100 ETH)
(5) 0x0d172591b8B470369bc31af88e2c94E239E550c9 (100 ETH)
(6) 0x82Ad2600452A11564bd018Ba236652c9225F2174 (100 ETH)
(7) 0x281BCADE69190AeeBf9030987663E2566Aea2FdA (100 ETH)
(8) 0x0c443Ea77c071D0B1F711dd716caCCd065E9d0f8 (100 ETH)
(9) 0x16E5Df6eEdA4D37dF5792cE8EFCA6465B572CDa3 (100 ETH)

Private Keys
==================
(0) 0x00ff1ebb8069f3acba6c0018879ac79adb3306ea02e8450af0b275effdd7f371
(1) 0xe6ba2d36bd2221220cbef8eca1fd60fcd1919c9a913e283959428c13df2feb8d
(2) 0x25c22bdc288c505f8b6d80b3b4559fa1c18c3f965aae32c6ac76a4f06b4c11c1
(3) 0xcaa06e47ef2fc32bb329cb9123cba3fab4f370863fc61f5bfd4ec15d5f1efa24
(4) 0xa9216cd254baa6b0b04a8a245541672a1fa46add76b9ca52cbe1f8fab047cd8d
(5) 0x1e56408c03b21fa46d3357c7928c345823bcafa0e3043eb35ac314c613e45a7e
(6) 0xb459f565a25cc3e019c4231c535a0fbebc1d7f94bb52d1f1aa0e94d542d2846e
(7) 0xd83ac928007da106a0053f449c16513692b9c69bf0b769a5fb10a4bbba57ed56
(8) 0xa320d1635251f5d164cfcd74c179b33b3491e79e8b6dc5284185872f61b24d2d
(9) 0x3d962128fec69e19630e7e4f7cd902075f1d495006115630683864abbf073d93

HD Wallet
==================
Mnemonic:      check indicate common program success invite bargain street legal uphold tomato found
Base HD Path:  m/44'/60'/0'/0/{account_index}

Gas Price
==================
20000000000

Gas Limit
==================
6721975

Call Gas Limit
==================
9007199254740991

Listening on 127.0.0.1:8545

3、安装truffle

安装命令:

sudo npm install –g truffle

  看版本:

truffle version

 看一下帮助:

truffle --help

运行truffle的时候一定要先运行ganache-cli或者ganache图形界面

如果运行ganache-cli,打开终端窗口

 如果运行ganache图形界,点击右上角设置图标

设置PORT NUMBER 为8545,点击右上角SAVE AND RESTART

4、创建一个项目

新开一个目录:

sudo mkdir mytruffle

进入目录:

cd mytruffle

初始化生成一个开发项目,用编辑器工具可以查看生成的相关工程文件

sudo truffle init

下载默认沙箱项目(可不执行truffle init): 在早期版本的truffle中,刚刚创建的工程中还会包含metacoin的示例代码。新版本truffle引入了box的概念,所有的示例代码都以box的形式提供。因此我们不需要用truffle init命令,用下面的命令就可以直接下载metacoin的示例代码:

truffle unbox metacoin

 查看:

sudo vim truffle-config.js

修改配置eth网络信息。

Networks:{

test: {

      host: "127.0.0.1",     // Localhost (default: none)

      port: 8545,            // Standard Ethereum port (default: none)

      network_id: "*",       // Any network (default: none)

     }

}

 编译项目部署测试的合约

编译:

sudo truffle compile

部署:

sudo truffle migrate

5、测试合约

metacoin的示例代码里已经把测试代码写好了,直接用下面的命令运行就可以了:

truffle test

从界面ganache界面看结果,可以看到ganache中多了几个区块

如果运行的是ganache-cli,可以看到多了一些内容

6、完成合约发布

1、编写合约 \demo1\contracts\MyContract.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MyContract {
    string public name;  
    constructor() {
        name = "xiaohe"; 
    }
    function setName(string memory _newName) public {
        name = _newName;  
    }
    function getName() public view returns (string memory) {
        return name; 
    }
}

2、编译合约

sudo truffle compile

3、提取abi代码 打开\demo1\build\contracts\MyContract.json 可以看到类似以下的代码、其中"abi": [....]就是我们要的内容

{
  "contractName": "MyContract",
  "abi": [....]
  ...
  .....
}

提取abi内容: abi= [....],我这边提取到的是

abi = [    {      "inputs": [],      "stateMutability": "nonpayable",      "type": "constructor"    },    {      "inputs": [],      "name": "name",      "outputs": [        {          "internalType": "string",          "name": "",          "type": "string"        }      ],      "stateMutability": "view",      "type": "function"    },    {      "inputs": [        {          "internalType": "string",          "name": "_newName",          "type": "string"        }      ],      "name": "setName",      "outputs": [],      "stateMutability": "nonpayable",      "type": "function"    },    {      "inputs": [],      "name": "getName",      "outputs": [        {          "internalType": "string",          "name": "",          "type": "string"        }      ],      "stateMutability": "view",      "type": "function"    }  ]  

3、部署合约

sudo truffle migrate

部署结果:transaction hash:为交易地址,contract address:为合约地址

   Deploying 'MyContract'
   ----------------------
   > transaction hash:    0xcb5c2e8d66a7e944a312a8ea19c2bb86f98889c1e08808adbaf2e73c2680ec1d
   > Blocks: 0            Seconds: 0
   > contract address:    0x85726b97fb414Fd0A9aF3E05caC84E6Ae501Cb61
   > block number:        3
   > block timestamp:     1705461720
   > account:             0x0c35E8cA2A421c7FaC801c0921706bf3894a51bE
   > balance:             99.996906815516427136
   > gas used:            376235 (0x5bdab)
   > gas price:           3.186350418 gwei
   > value sent:          0 ETH
   > total cost:          0.00119881654951623 ETH

4、geth验证

D:\code\geth-windows-amd64-1.10.15-8be800ff\geth-windows-amd64-1.10.15\geth attach http://localhost:8545

执行以下脚本、运行结果为:"xietao1"即为合约调用成功

abi = [    {      "inputs": [],      "stateMutability": "nonpayable",      "type": "constructor"    },    {      "inputs": [],      "name": "name",      "outputs": [        {          "internalType": "string",          "name": "",          "type": "string"        }      ],      "stateMutability": "view",      "type": "function"    },    {      "inputs": [        {          "internalType": "string",          "name": "_newName",          "type": "string"        }      ],      "name": "setName",      "outputs": [],      "stateMutability": "nonpayable",      "type": "function"    },    {      "inputs": [],      "name": "getName",      "outputs": [        {          "internalType": "string",          "name": "",          "type": "string"        }      ],      "stateMutability": "view",      "type": "function"    }  ]  
contractAddress = "0x85726b97fb414Fd0A9aF3E05caC84E6Ae501Cb61";
MyContract = web3.eth.contract(abi);
myContractInstance = MyContract.at(contractAddress);
myContractInstance.getName.call();
"xiaohe"

调用合约设置方法:需要指定执行账号,这里没有设置gas

myContractInstance.setName("xietao1", {from:eth.accounts[0]});
"xietao1"

调用合约设置方法:需要指定执行账号,设置gas

myContractInstance.setName("xietao1", {from:eth.accounts[0],gas: 300000});

6、vue下使用web3、访问合约

<template>
  <div id="app">
    <!-- 如果未连接到智能合约时显示连接中 -->
    <h1 v-if="!connected">连接...</h1>
    <!-- 如果未初始化智能合约时显示初始化中 -->
    <h1 v-else-if="!contractInstance">初始化智能合约...</h1>
    <!-- 当已连接并初始化智能合约后,显示以下内容 -->
    <div v-if="connected && contractInstance">
      <!-- 输入新名称的文本框 -->
      <input type="text" v-model="newName" placeholder="输入新名称">
      <!-- 点击按钮调用setNewName方法 -->
      <button @click="setNewName">设置名称</button>
      <!-- 显示智能合约返回的结果 -->
      <p>智能合约返回: {{ contractResult }}</p>
      <!-- 点击按钮调用getNameFromContract方法 -->
      <button @click="getNameFromContract">获取名称</button>
    </div>
  </div>
</template>

<script>
import Web3 from 'web3'
export default {
  // 组件名称为App
  name: 'App',
  // 数据对象初始化方法
  data() {
    // 返回数据对象
    return {
      // 连接状态,默认为false
      connected: false,
      // 合约实例,默认为null
      contractInstance: null,
      // 合约地址,默认值示例,根据实际合约地址填写
      contractAddress: '0x3c2b01fE9c2ea1366ADC357529A82b51093f0da3',
      // ABI(合约接口描述),默认值示例
      ABI: JSON.parse('[    {      "inputs": [],      "stateMutability": "nonpayable",      "type": "constructor"    },    {      "inputs": [],      "name": "name",      "outputs": [        {          "internalType": "string",          "name": "",          "type": "string"        }      ],      "stateMutability": "view",      "type": "function"    },    {      "inputs": [        {          "internalType": "string",          "name": "_newName",          "type": "string"        }      ],      "name": "setName",      "outputs": [],      "stateMutability": "nonpayable",      "type": "function"    },    {      "inputs": [],      "name": "getName",      "outputs": [        {          "internalType": "string",          "name": "",          "type": "string"        }      ],      "stateMutability": "view",      "type": "function"    }  ]'),
      // 新名称,默认为空字符串
      newName: '',
      // 合约结果,默认为空字符串
      contractResult: '',
    }
  }, // 返回数据对象
  // 生命周期钩子函数,组件被创建时执行
  async created() {
    // 创建Web3实例并连接到本地节点
    this.web3 = new Web3(new Web3.providers.HttpProvider('http://127.0.0.1:8545'));
    // 获取当前网络ID
    const networkId = await this.web3.eth.net.getId();
    console.log(networkId); // 打印网络ID
    // 将Web3实例赋值给变量web3
    const web3 = this.web3;
    // 使用ABI和合约地址创建合约实例
    this.contractInstance = new web3.eth.Contract(this.ABI, this.contractAddress);
    // 设置连接状态为已连接
    this.connected = true;
  },

  // 方法对象
  methods: {
    // 异步方法,设置新名称
    async setNewName() {
      if (!this.newName) return; // 如果新名称为空,则返回
      try {
        // 获取以太坊账号列表
        const accounts = await this.web3.eth.getAccounts();
        // 发送设置名称的交易
        const tx = await this.contractInstance.methods.setName(this.newName).send({from: accounts[0], gas: 300000});
        // 清空新名称输入框
        this.newName = '';
        console.log('Transaction successful:', tx.transactionHash); // 打印交易成功信息
        // 调用获取合约名称的方法
        this.getNameFromContract();
      } catch (error) {
        console.error("Failed to call setName function on the contract", error); // 打印错误信息
      }
    },

    // 异步方法,从合约获取名称
    async getNameFromContract() {
      try {
        // 调用合约的获取名称方法,并获取结果
        const result = await this.contractInstance.methods.getName().call();
        // 将获取的合约名称结果保存到组件数据中
        this.contractResult = result;
      } catch (error) {
        console.error("Failed to call getName function on the contract", error); // 打印错误信息
      }
    },
  },
}
</script>