truffle创建DAPP项目

一、智能合约的创建与测试

1、创建项目

truffle init
生成的目录如下:

2、编写智能合约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);
    adopters[petId] = msg.sender;
    return petId;
}

function getAdopters() public view returns (address[16]) {
    return adopters;
}
}

注:这里没写构造函数,会自动生成。

3、编译合约

在项目根目录下执行:truffle compile,然后会生成一个build文件夹,里面是编译成的Adoption.json文件

4、在migration目录下创建部署文件

var Adoption = artifacts.require("Adoption");  //这里填入的是合约名,而不是文件名

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

5、打开ganache(区块链的运行环境)

6、配置truffle.js文件

module.exports = {
networks: {
development: {
  host: "127.0.0.1",        //ganache网络的地址
  port: 7545,                  //ganache的监听端口
  network_id: "*"    // Match any network id
}
}
};

注意:该配置是为了让truffle连接ganache为我们准备的区块链网络。
注意:查看合约编译生成的文件Adoption.json,里面的"networks":配置为空。

7、部署合约

truffle migrate

执行后查看Adoption.json,这时网络信息已经加上去了:
"networks": {
"5777": {
  "events": {},
  "links": {},
  "address": "0x345ca3e014aaf5dca488057592ee47305d9b3e10",
  "transactionHash": "0x0ec。。。"
}

},

8、这时查看ganache信息的block已经不再是0了。

9、编写测试合约(在test目录下创建TestAdoption.sol)

pragma solidity ^0.4.17;

import 'truffle/Assert.sol';
import 'truffle/DeployedAddresses.sol';  //用于获取部署合约的地址
import '../contracts/Adoption.sol';

contract TestAdoption {
    Adoption adoption = Adoption(DeployedAddresses.Adoption());

function testUserCanAdoptPet() public {
    uint returnedId = adoption.adopt(8);
    uint expected = 8;
    Assert.equal(returnedId, expected, "Aoption of pet Id 8 should be recorded.");
}

function testGetAdopterAddressByPetid() public {
    address expected = this;
    address adopter = adoption.adopters(8);

    Assert.equal(adopter, expected, "Owner of pet ID 8 should be recorded.");
}

function testGetAdopterAddressByPetIdInArray() public {

    address expected = this;

    address[16] memory adopters = adoption.getAdopters();   //返回领养者的地址
    Assert.equal(adopters[8], expected, "Owner of Pet Id 8 should be recoded.");
}
}

10、测试

 在终端运行:truffle test。运行如下图所示,表示三个测试方法都通过了:

这时去查看ganache,发现多了很多交易,也打包了一些区块,说明测试过程也有很多交易产生。

二、初始化web环境

11、将项目转为npm工程

  终端执行npm init

这一步执行完后会生成package.json文件,可用于显示工程的信息,也可以用于管理一些依赖。

12、提供一个web服务器

npm install lite-server
注:我通过这种方式安装失败,然后通过npm install lite-server --save-dev安装成功

13、新建一个src文件夹,用于存储web应用的源文件

14、配置服务器信息

 根目录下创建bs-config.json文件,如下:
{
  "port": 8000,          //这个端口要不指定,则会有个默认端口
"server": {
    "baseDir": ["./src", "./build/contracts"]
     }
}

15、package.json文件中加入红框行

表示:在运行这个脚本命令时运行起server

16、在src目录下创建文件index.html,内容如下:

Hello Pet-shop

17、在终端运行

npm run dev
运行完之后会自动打开一个浏览器,输出 index.html中的内容Hello Pet-shop,如下所示:

18、页面编写

在上面创建的src目录中,完全拷贝从truffle unbox出来的pet-shop项目的src目录(css、fonts、images、js、pets.json等)。

19、编辑index.html,内容如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Pete's Pet Shop</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="css/bootstrap.min.css" rel="stylesheet">
</head>

<body>
    <div class="container">
        <h1 class="text-center">Pete's Pet Shop</h1>
        <div id="petsRow" class="row"></div>
    </div>

    <div id="petTemplate" style="display: none">
        <div class="col-sm-6 col-md-4 col-lg-3">
          <div class="panel panel-default panel-pet">
            <div class="panel-heading">
              <h3 class="panel-title">Scrappy</h3>
            </div>
            <div class="panel-body">
              <img alt="140x140" data-src="holder.js/140x140"
               class="img-rounded img-center" style="width: 100%;" src="" data-holder-rendered="true">
              <br/><br/>
              <strong>Breed</strong>: <span class="pet-breed">Golden Retriever</span><br/>
              <strong>Age</strong>: <span class="pet-age">3</span><br/>
              <strong>Location</strong>: <span class="pet-location">Warren, MI</span><br/><br/>
              <button class="btn btn-default btn-adopt" type="button" data-id="0">Adopt</button>
            </div>
          </div>
        </div>
      </div>

      <!-- 官方的代码中引入谷歌的库,但是谷歌被墙了 -->
      <script src="http://libs.baidu.com/jquery/1.9.1/jquery.min.js"></script>
      <script src="js/bootstrap.min.js"></script>
      <!-- 下面的两个库是要和智能合约进行交互的,truffle-contract是web3的包装,有高级一点的js的用法 -->
      <script src="js/web3.min.js"></script>
      <script src="js/truffle-contract.js"></script>

      <script src="js/app.js"></script>
</body>
</html>

20、编辑pets.json文件,内容如下:

[
  {
    "id": 0,
    "name": "Frieda",
    "picture": "images/scottish-terrier.jpeg",
    "age": 3,
    "breed": "Scottish Terrier",
    "location": "Lisco, Alabama"
  },
  {
    "id": 1,
    "name": "Gina",
    "picture": "images/scottish-terrier.jpeg",
    "age": 3,
    "breed": "Scottish Terrier",
    "location": "Tooleville, West Virginia"
  },
  {
    "id": 2,
    "name": "Collins",
    "picture": "images/french-bulldog.jpeg",
    "age": 2,
    "breed": "French Bulldog",
    "location": "Freeburn, Idaho"
  },
  {
    "id": 3,
    "name": "Melissa",
    "picture": "images/boxer.jpeg",
    "age": 2,
    "breed": "Boxer",
    "location": "Camas, Pennsylvania"
  },
  {
    "id": 4,
    "name": "Jeanine",
    "picture": "images/french-bulldog.jpeg",
    "age": 2,
    "breed": "French Bulldog",
    "location": "Gerber, South Dakota"
  },
  {
    "id": 5,
    "name": "Elvia",
    "picture": "images/french-bulldog.jpeg",
    "age": 3,
    "breed": "French Bulldog",
    "location": "Innsbrook, Illinois"
  },
  {
    "id": 6,
    "name": "Latisha",
    "picture": "images/golden-retriever.jpeg",
    "age": 3,
    "breed": "Golden Retriever",
    "location": "Soudan, Louisiana"
  },
  {
    "id": 7,
    "name": "Coleman",
    "picture": "images/golden-retriever.jpeg",
    "age": 3,
    "breed": "Golden Retriever",
    "location": "Jacksonwald, Palau"
  },
  {
    "id": 8,
    "name": "Nichole",
    "picture": "images/french-bulldog.jpeg",
    "age": 2,
    "breed": "French Bulldog",
    "location": "Honolulu, Hawaii"
  },
  {
    "id": 9,
    "name": "Fran",
    "picture": "images/boxer.jpeg",
    "age": 3,
    "breed": "Boxer",
    "location": "Matheny, Utah"
  },
  {
    "id": 10,
    "name": "Leonor",
    "picture": "images/boxer.jpeg",
    "age": 2,
    "breed": "Boxer",
    "location": "Tyhee, Indiana"
  },
  {
    "id": 11,
    "name": "Dean",
    "picture": "images/scottish-terrier.jpeg",
    "age": 3,
    "breed": "Scottish Terrier",
    "location": "Windsor, Montana"
  },
  {
    "id": 12,
    "name": "Stevenson",
    "picture": "images/french-bulldog.jpeg",
    "age": 3,
    "breed": "French Bulldog",
    "location": "Kingstowne, Nevada"
  },
  {
    "id": 13,
    "name": "Kristina",
    "picture": "images/golden-retriever.jpeg",
    "age": 4,
    "breed": "Golden Retriever",
    "location": "Sultana, Massachusetts"
  },
  {
    "id": 14,
    "name": "Ethel",
    "picture": "images/golden-retriever.jpeg",
    "age": 2,
    "breed": "Golden Retriever",
    "location": "Broadlands, Oregon"
  },
  {
    "id": 15,
    "name": "Terry",
    "picture": "images/golden-retriever.jpeg",
    "age": 2,
    "breed": "Golden Retriever",
    "location": "Dawn, Wisconsin"
  }
]

21、这时刷新浏览器,就可以看到UI界面了。

22、完成UI和合约的交互

    在上一步骤完成时,有个很漂亮的UI,但是还没有交互效果,因为并没有调用到智能合约。这
部分逻辑在./src/app.js中完成,如下:
App = {
  web3Provider: null,
  contracts: {},

  init: function() {
    // Load pets.
    $.getJSON('../pets.json', function(data) {
      var petsRow = $('#petsRow');
      var petTemplate = $('#petTemplate');

      //把一个个pet对象赋值到模板对应的标签上去
      for (i = 0; i < data.length; i ++) {
        petTemplate.find('.panel-title').text(data[i].name);
        petTemplate.find('img').attr('src', data[i].picture);
        petTemplate.find('.pet-breed').text(data[i].breed);
        petTemplate.find('.pet-age').text(data[i].age);
        petTemplate.find('.pet-location').text(data[i].location);
        petTemplate.find('.btn-adopt').attr('data-id', data[i].id);

        petsRow.append(petTemplate.html());
      }
    });

    return App.initWeb3();
  },

  initWeb3: function() {            //初始化web3
    //web3初始化过程参考:https://github.com/ethereum/web3.js
       //在初始化web3时,要先判断当前环境中是否已经有web3,没有再构建web3。因为在使用metamask时,会自带web3.
    if (typeof web3 !== 'undefined') {
      App.web3Provider = web3.currentProvider;
    } else {
      App.web3Provider = new Web3.prviders.HttpProvider("http://127.0.0.1:7545");   //ganache的地址
    }
    web3 = new Web3(App.web3Provider);

    return App.initContract();
  },

  initContract: function() {        //初始化合约
    $.getJSON('Adoption.json', function(data) {   //为何可直接加载Adoption.json文件呢,这是因为./build/contracts目录亿配置到服务器的环境中
      var AdoptionArtifact = data;

      App.contracts.Adoption = TruffleContract(AdoptionArtifact);
      App.contracts.Adoption.setProvider(App.web3Provider);

      return App.markAdopted();       //标记当前宠物是否被领养
    });

    return App.bindEvents();
  },

  bindEvents: function() {          //绑定事件
    $(document).on('click', '.btn-adopt', App.handleAdopt);     //btn-adopt是index.html,当这个按钮被点击时调用App的handleAdopt方法
  },

  markAdopted: function(adopters, account) {        //标记当前的宠物是否被领养

    var apotionInstance;
    //App.contracts.Adoption.deployed()可以拿到合约的实例,传递给then(function(instance)
    //中的instance。.then(function(instance)是promise的写法,
    App.contracts.Adoption.deployed().then(function(instance) {
      apotionInstance = instance;
      return apotionInstance.getAdopters.call();
    }).then(function(adopters) {            //上一步return apotionInstance.getAdopters.call()的返回值传递给了function(adopters)

      for(i =0; i< adopters.length; i++) {
        console.log(adopters[i]);
        //地址元素默认为0x0000000000000000000000000000000000000000
        if(adopters[i] !== '0x0000000000000000000000000000000000000000') {
          //button的文字写成Success,同时不能被点击
          $('.panel-pet').eq(i).find('button').text('Success').attr('disabled', true);
        }
      }
    }).catch(function(err) {          //捕获异常
      console.log(err.message);
    })
  },

  handleAdopt: function(event) {
    event.preventDefault();
    var apotionInstance;
    var petId = parseInt($(event.target).data('id'));

    web3.eth.getAccounts(function(error, accounts){       //拿到当前环境下的账号
      var account = accounts[0];      //取第一个账号

      App.contracts.Adoption.deployed().then(function(instance){
        apotionInstance = instance;

        return apotionInstance.adopt(petId, {from: account});
      }).then(function(result) {
        return App.markAdopted();       //标记状态
      } ).catch(function(err) {
        console.log(err.message);
      });
    });
  }
};

$(function() {
  $(window).load(function() {       //当window被加载时,会调用App.init
    App.init();
  });
});

22、运行(调试1)

点了adopt没有反应,打开F12调试。(360浏览器)


22、运行(调试2)。(谷歌浏览器)


原因是要在谷歌浏览器的metamask中设置本地以太坊网络。

23、metamask设置ganache网络

我们的ganache中的地址默认为127.0.0.1:7545,如果metamask中有这个地址的网络则直接选择就好,如果没有则需要选择Custom RPC进行配置。



这样还是不能领养宠物,因为网络中的地址和ganache中的10个地址不一样,要将ganache中的10个地址,导入到metamask网络中去

24、将ganache中的一个地址私钥导入到metamask网络

1)复制ganache中的第一个地址的私钥


2)打开metemask对应网络,选择Import Account:

3)输入刚才拷贝的私钥,导入即可看到我们选择的那个地址

25、测试,这时完全可以正常使用了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容