Photo by Mikkel Bech / Unsplash

Background

俗話說的好 畢圈一日 人間一年..... 開始學 Smart Contract吧, part 5.

Basic

主要在應用是在怎麼 send ether 到 contract 的錢包並且拿到相對應的資訊. 我們利用 pet-shop當做基底來完成這個範例.

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

Writing Contract

$ vim contracts/Basic.sol

contracts/Basic.sol

pragma solidity ^0.4.24;

contract Basic {

    // Store accounts that have dontate
    mapping(address => uint256) public donors;

    function getSelfValue () public returns (uint256) {
        return donors[msg.sender];
    }

    function donate (bytes32 _id) public payable{
        // require a valid candidate
        require(msg.value > 0);

        if (donors[msg.sender] != 0)
        {
            donors[msg.sender] += msg.value;
        }
        else
        {
            donors[msg.sender] = msg.value;
        }
    }
}

比較值得注意的點, 如果你的function有要send ether進入的話記得加上 payable 這個keyword. 不然是不能 send ether 的喔.

Compile & Deploy

$ vim migrations/2_deploy_contracts.js

2_deploy_contracts.js

var Basic = artifacts.require("./Basic.sol");

module.exports = function(deployer) {
  deployer.deploy(Basic);
};
$ truffle compile
$ truffle migrate 

Front-end

$ vim src/index.html

前端的部份因為我不是很擅長. 所以排版部分就請多多包涵. 然後記得安裝 Metamask 的 chrome套件然後把它切換到 Ganache 的設定前幾篇教學有, 可以參考一下.

src/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Election Results</title>
    <!-- Bootstrap -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
  </head>
  <body>
    <div class="container" style="width: 650px;">
      <div class="row">
        <div class="col-lg-12">
          <h1 class="text-center">Basic Action</h1>
          <hr/>
          <div id="content">
            <button type="submit" id='donate' class="btn btn-primary">Donate</button>
            <hr/>
            <button type="submit" id='show' class="btn btn-primary">Show</button>
            <hr/>
            <p id="accountAddress" class="text-center"></p>
            <p id="accountBalance" class="text-center"></p>
          </div>
        </div>
      </div>
    </div>

    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="js/bootstrap.min.js"></script>
    <script src="js/web3.min.js"></script>
    <script src="js/truffle-contract.js"></script>
    <script src="js/app.js"></script>
  </body>
</html>
$ vim src/js/app.js

src/js/app.js

App = {
  web3Provider: null,
  contracts: {},

  init: function() {
    var basicInstance;

    // Load account data
    web3.eth.getCoinbase(function(err, account) {
      if (err === null) {
        App.account = account;
        $("#accountAddress").html("Your Account: " + account);
      }
    });
    return App.initWeb3();
  },
  
    initWeb3: function() {

    if (typeof web3 !== 'undefined') {
      // If a web3 instance is already provided by Meta Mask.
      App.web3Provider = web3.currentProvider;
      web3 = new Web3(web3.currentProvider);
    } else {
      console.log("localhost")
      // Specify default instance if no web3 instance provided
      App.web3Provider = new Web3.providers.HttpProvider('http://localhost:7545');
      web3 = new Web3(App.web3Provider);
    }
    return App.initContract();
  },

  initContract: function() {

    $.getJSON("Basic.json", function(basic) {
      //console.log(basic)
      // Instantiate a new truffle contract from the artifact
      App.contracts.Basic = TruffleContract(basic);
      // Connect provider to interact with contract
      App.contracts.Basic.setProvider(App.web3Provider);
    });

    return App.bindEvents();
  },
    bindEvents: function() {
    $(document).on('click', '#donate', App.handleDonate);
    $(document).on('click', '#show', App.handleShow);
  },

  handleShow: function(adopters, account) {

    var basicInstance;
    web3.eth.getAccounts(function(error, accounts) {
      if (error) {
        console.log(error);
      }
      var account = accounts[0];
      App.contracts.Basic.deployed().then(function(instance) {
        basicInstance = instance;
        return basicInstance.getSelfValue.call();

      }).then(function(result) {
        console.log(web3.fromWei(result.toNumber(), 'ether'))

        $("#accountBalance").html("Account Balance: " + web3.fromWei(result.toNumber(), 'ether') + " eth");
      }).catch(function(err) {
        console.log(err.message);
      });
    });

  },
  handleDonate: function(event) {
    event.preventDefault();
    var basicInstance;
    web3.eth.getAccounts(function(error, accounts) {
      if (error) {
        console.log(error);
      }
      var account = accounts[0];
      App.contracts.Basic.deployed().then(function(instance) {

        basicInstance = instance;
        return basicInstance.donate("paul", {from: account, value: web3.toWei(0.1, 'ether'), gas: 300000});
      }).catch(function(err) {
        console.log(err.message);
      });
    });
  }
};

$(function() {
  $(window).load(function() {
    App.init();
  });
});

簡單講一下前端介面, 就是有兩個按鈕. 一個負責送錢到合約裡面去. 另一個顯示這個錢包總共丟了多少錢到合約裡面.

Run Service

$ npm run dev

畫面看起來如下:

Screen-Shot-2018-07-26-at-12.14.31-AM

點了Donate看看. (記得Metamask要設成Ganache的配置模式)

Screen-Shot-2018-07-26-at-12.15.18-AM

會秀出Metamask的資訊. 裡面transcation的資訊跟我們在程式中設定的一樣. 接的點選submit. 再點選show這個按鈕看看有什麼反應.

Screen-Shot-2018-07-26-at-12.19.49-AM

那顯示會有點時間差. 因為要等待交易被打包. 以上就是本次文章的示範範例.

P.S 過程中如果有出現 Error: the tx doesn't have the correct nonce. account has nonce of: 0 tx has nonce of: 21 類似這種error的時候記得先 reset 你的 Metamask錢包(Setting -> Reset Account).

Reference