以太坊开发之搭建宠物商店

truffle的宠物商店是一个了解以太坊开发的一个很不错的例子. 跟着官方提供的demo与教程,可以完整的感受一下整个流程的开发.

Ganache

为了环境需要,并且快速的在本地搭建一个私有链环境. truffle推出了一个可视化私有链客户端: Ganache下载地址 下载之后运行,你会看到这样一个界面:

初始化你的项目

首先我们新建一个目录,并且初始化一下项目

$ mkdir pet-shop
$ cd pet-shop
$ truffle unbox pet-shop

项目目录结构

这里只列出了重要的目录与文件

├── bs-config.json
├── contracts //合约目录
│   └── Migrations.sol //合约文件
├── migrations // 部署脚本
│   └── 1_initial_migration.js
├── package-lock.json
├── package.json
├── src // 前端代码目录
├── test // 测试代码目录
└── truffle.js // truffle配置文件

编写智能合约

在contracts/目录中,创建一个Adoption.sol文件 文件内容:

pragma solidity ^0.4.17;

contract Adoption {

  address[16] public adopters;  // 声明一个地址变量,用于保存领养者地址

    // 领养宠物
  function adopt(uint petId) public returns (uint) {
    require(petId >= 0 && petId <= 15);  // 确保宠物id正确,为0到15之间,
                                         // 如果不符合条件就会回滚    
    //msg.sender 为调用这个函数的人的地址
    adopters[petId] = msg.sender;        // 保存调用这地址 
    return petId;
  }

  // 返回领养者
  function getAdopters() public view returns (address[16]) {
    return adopters;
  }

}

编译你的智能合约

$ truffle compile

//输出
Compiling ./contracts/Adoption.sol...
Writing artifacts to ./build/contracts

这样你就会发现你的项目多了一个build文件夹, 里面存放这里刚刚写好的智能合约编译完成的json文件

部署你的智能合约

你的智能合约写好了,你可以暂且理解为你的后台代码写好了.要部署起来便于前端调用.

在你的migrations/目录新建一个部署脚本:2_deploy_contracts.js

var Adoption = artifacts.require("Adoption");

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

打开刚刚下载的Ganache, Ganache会启动一个私有链. 我们的智能合约就是要部署在这个私有链上.

确认项目录根目录的truffle.js

module.exports = {
  networks: {
    development: {
      host: "127.0.0.1",
      port: 7545, //确保端口地址,是否与私有链地址一致,如果不一致请保持一致
      network_id: "*" // Match any network id
    }
  }
};

执行以下命令,部署合约:

truffle  migrate

# 输出
Using network 'develop'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0x3076b7dac65afc44ec51508bf6f2b6894f833f0f9560ecad2d6d41ed98a4679f
  Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0
Saving successful migration to network...
  ... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Deploying Adoption...
  ... 0x2c6ab4471c225b5473f2079ee42ca1356007e51d5bb57eb80bfeb406acc35cd4
  Adoption: 0x345ca3e014aaf5dca488057592ee47305d9b3e10
Saving successful migration to network...
  ... 0xf36163615f41ef7ed8f4a8f192149a0bf633fe1a2398ce001bf44c43dc7bdda0
Saving artifacts...

重新打开Ganache,你会发现区块链的状态发生了变化,多了4个区块. OK,部署完毕

前端与智能合约的交互

好,智能合约我们已经部署好了.接下来我们就要开始我们的javascript部分了 打开 src/js/app.js

初始化web3

web3是一个前端与以太坊通信的库,所有调用智能合约的操作,我们基于web3来实现.

我们在app.js里找到initWeb3方法,为其添加以下代码:

initWeb3: function() {
  // 判断是否有全局web3对象,一般装了MetaMask这种钱包就会有全局的web3对象
  if (typeof web3 !== 'undefined') {
    // 如果有,就直接使用
    App.web3Provider = web3.currentProvider;
  } else {
    // 如果没有全局的web3对象,就直接在本地初始化一个
    App.web3Provider = new Web3.providers.HttpProvider('http://localhost:7545');
  }
  web3 = new Web3(App.web3Provider);

  return App.initContract();
}

实例化智能合约

找到initContract方法部分,添加以下代码:

initContract: function() {
  //加载我们的之前写好的智能合约
  $.getJSON('Adoption.json', function(data) {
    // 用Adoption.json数据创建一个可交互的TruffleContract合约实例。
    var AdoptionArtifact = data;
    //TruffleContract 为该项目的全局方法,使用的是 truffle-contract这个库
    //如果在其他项目,你可以直接npm i truffle-contract --save 来安装这个库
    //用于实例化合约
    App.contracts.Adoption = TruffleContract(AdoptionArtifact);

    // Set the provider for our contract
    App.contracts.Adoption.setProvider(App.web3Provider);

    // Use our contract to retrieve and mark the adopted pets
    return App.markAdopted();
  });
  return App.bindEvents();
}

领养部分

handleAdopt方法部分,添加以下代码:

handleAdopt: function(event) {
  event.preventDefault();
  //dom里获取到宠物的id
  var petId = parseInt($(event.target).data('id'));

  var adoptionInstance;

  // 获取用户账号
  web3.eth.getAccounts(function(error, accounts) {
    if (error) {
      console.log(error);
    }
    //获取用户,如果装了钱包只会返回长度只有1的数组,如果没有装钱包,会返回所有用户的地址
    var account = accounts[0];
    //调用智能合约
    App.contracts.Adoption.deployed().then(function(instance) {
      adoptionInstance = instance;

      // 发送交易领养宠物
      return adoptionInstance.adopt(petId, {from: account});
    }).then(function(result) {
      return App.markAdopted();
    }).catch(function(err) {
      console.log(err.message);
    });
  });
}

找到 markAdopted方法,添加以下代码:

markAdopted: function(adopters, account) {
  var adoptionInstance;
  //调用合约的方法
  App.contracts.Adoption.deployed().then(function(instance) {
    adoptionInstance = instance;

    // 调用合约的getAdopters(), 用call读取信息不用消耗gas
    return adoptionInstance.getAdopters.call();
  }).then(function(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.log(err.message);
  });
}

启动服务

好了,所有代码我们都已经填充完毕,启动我们的项目,开始领养你喜欢的宠物吧

$ npm run dev

本文链接:

https://alili.tech/archive/b75b18ec/