วันศุกร์, สิงหาคม 23

มาพัฒนา DApp ง่าย ๆ ด้วย Truffle กันเถอะ ตอนที่ 3: Migration

หัวข้อ: มาพัฒนา DApp ง่าย ๆ ด้วย Truffle กันเถอะ ตอนที่ 3: Migration

สวัสดีค่ะ คุณผู้อ่านทุกท่าน กลับมาพบกันอีกครั้งกับบทความตอนที่ 3 ของ “มาพัฒนา DApp ง่าย ๆ ด้วย Truffle กันเถอะ” 🙂

หลังจากที่เราสร้าง Smart Contract เรียบร้อย เราจะต้อง Deploy ส่งไปที่ Ethereum เพื่อใช้งานต่อไปค่ะ

โดยปกติแล้ว การ Deploy Contract ขึ้นไปนั้น เราจะต้องทำผ่าน web3 ที่เป็น Javascript Library ที่ช่วยติดต่อ Ethereum ให้เรา ด้วยความเป็น Library นี่แหละค่ะ เมื่อจะเรียกใช้มันนั้น เราจะต้องเขียนเป็น Script ขึ้นมา โดยนำ ABI และ Bytecode ของทุก Smart Contract ทั้งหมดที่เราต้องการ Deploy เข้ามาใน Script ด้วยตัวเอง จากนั้นก็นำสองสิ่งของแต่ละ Smart Contract มาเข้าฟังก์ชันหนึ่งของ web3 เมื่อเสร็จแล้ว เราต้องสั่งให้ Script ทำงาน

หาก Script ของเราจะ Deploy Smart Contract โปรเจค Simple Ecommerce ที่ได้สร้างไปแล้วในตอนที่ 2 และใช้ web3 เวอร์ชัน 1.0.0 Script ที่ได้จะเป็นดังนี้

const Web3 = require('web3');
const web3 = new Web3('<http://localhost:7545>');

const tokenAbi = [{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x70a08231"},{"inputs":[{"name":"initialSupply","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xbeabacc8"}]
const shopAbi = [{"inputs":[{"name":"_tokenAddress","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"pid","type":"uint256"},{"indexed":false,"name":"seller","type":"address"},{"indexed":false,"name":"timestamp","type":"uint256"}],"name":"AddedProduct","type":"event","signature":"0x283a98e56d9d477bc9675301834a627bc96a173f644ce87ab9bbeee35e192965"},{"anonymous":false,"inputs":[{"indexed":false,"name":"pid","type":"uint256"},{"indexed":false,"name":"buyer","type":"address"},{"indexed":false,"name":"timestamp","type":"uint256"}],"name":"BuyProduct","type":"event","signature":"0x6e963996302b87595be0bd0f2d400e2f43c1cfce4378ba7e9b34305fe2f2746b"},{"constant":false,"inputs":[{"name":"_pid","type":"uint256"},{"name":"_name","type":"string"},{"name":"_price","type":"uint256"},{"name":"_quantity","type":"uint256"},{"name":"_imgPath","type":"string"},{"name":"timestamp","type":"uint256"}],"name":"addProduct","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xdd31ee57"},{"constant":true,"inputs":[{"name":"_pid","type":"uint256"}],"name":"getProduct","outputs":[{"name":"name","type":"string"},{"name":"price","type":"uint256"},{"name":"quantity","type":"uint256"},{"name":"imgPath","type":"string"},{"name":"seller","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xb9db15b4"},{"constant":false,"inputs":[{"name":"_pid","type":"uint256"},{"name":"_timestamp","type":"uint256"}],"name":"buyProduct","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x53b62866"}]
const tokenBytecode = 0x608060405234801561001057600080fd5b506040516020806103a08339810180604052602081101561003057600080fd5b8101908080519060200190929190505050806000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505061030c806100946000396000f3fe60806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806370a0823114610051578063beabacc8146100b6575b600080fd5b34801561005d57600080fd5b506100a06004803603602081101561007457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610149565b6040518082815260200191505060405180910390f35b3480156100c257600080fd5b5061012f600480360360608110156100d957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610161565b604051808215151515815260200191505060405180910390f35b60006020528060005260406000206000915090505481565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101515156101b057600080fd5b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054826000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054011015151561023d57600080fd5b816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555060019050939250505056fea165627a7a72305820237bede4b5ceb2d9241cf6932aeb02c1d0c27feba97a95184c62e7d3462ef27c0029
const shopBytecode = 0x608060405234801561001057600080fd5b50604051602080610baf8339810180604052602081101561003057600080fd5b810190808051906020019092919050505080600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050610b1d806100926000396000f3fe608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806353b628661461005c578063b9db15b4146100a1578063dd31ee5714610202575b600080fd5b34801561006857600080fd5b5061009f6004803603604081101561007f57600080fd5b810190808035906020019092919080359060200190929190505050610389565b005b3480156100ad57600080fd5b506100da600480360360208110156100c457600080fd5b8101908080359060200190929190505050610691565b6040518080602001868152602001858152602001806020018473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001838103835288818151815260200191508051906020019080838360005b8381101561015c578082015181840152602081019050610141565b50505050905090810190601f1680156101895780820380516001836020036101000a031916815260200191505b50838103825285818151815260200191508051906020019080838360005b838110156101c25780820151818401526020810190506101a7565b50505050905090810190601f1680156101ef5780820380516001836020036101000a031916815260200191505b5097505050505050505060405180910390f35b34801561020e57600080fd5b50610387600480360360c081101561022557600080fd5b81019080803590602001909291908035906020019064010000000081111561024c57600080fd5b82018360208201111561025e57600080fd5b8035906020019184600183028401116401000000008311171561028057600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019092919080359060200190929190803590602001906401000000008111156102f757600080fd5b82018360208201111561030957600080fd5b8035906020019184600183028401116401000000008311171561032b57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803590602001909291905050506108a3565b005b600080600084815260200190815260200160002060030154111515610416576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f50726f6475637420697320736f6c64206f75740000000000000000000000000081525060200191505060405180910390fd5b600080600084815260200190815260200160002090506000339050600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663beabacc8828460040160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1685600201546040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019350505050602060405180830381600087803b15801561055257600080fd5b505af1158015610566573d6000803e3d6000fd5b505050506040513d602081101561057c57600080fd5b81019080805190602001909291905050505060018260030160008282540392505081905550600160008581526020019081526020016000208190806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550507f6e963996302b87595be0bd0f2d400e2f43c1cfce4378ba7e9b34305fe2f2746b848285604051808481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001935050505060405180910390a150505050565b6060600080606060006106a2610a06565b60008088815260200190815260200160002060a06040519081016040529081600082018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156107595780601f1061072e57610100808354040283529160200191610759565b820191906000526020600020905b81548152906001019060200180831161073c57829003601f168201915b50505050508152602001600182018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156107fb5780601f106107d0576101008083540402835291602001916107fb565b820191906000526020600020905b8154815290600101906020018083116107de57829003601f168201915b5050505050815260200160028201548152602001600382015481526020016004820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681525050905080600001518160400151826060015183602001518460800151849450819150955095509550955095505091939590929450565b60a0604051908101604052808681526020018381526020018581526020018481526020013373ffffffffffffffffffffffffffffffffffffffff16815250600080888152602001908152602001600020600082015181600001908051906020019061090f929190610a4c565b50602082015181600101908051906020019061092c929190610a4c565b50604082015181600201556060820151816003015560808201518160040160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509050507f283a98e56d9d477bc9675301834a627bc96a173f644ce87ab9bbeee35e192965863383604051808481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001935050505060405180910390a1505050505050565b60a06040519081016040528060608152602001606081526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610a8d57805160ff1916838001178555610abb565b82800160010185558215610abb579182015b82811115610aba578251825591602001919060010190610a9f565b5b509050610ac89190610acc565b5090565b610aee91905b80821115610aea576000816000905550600101610ad2565b5090565b9056fea165627a7a72305820698f03b8ca8d1f1572f4c44e12148998d2ae07d6c17af72dd937e82a1c7c3f070029
let tokenContractAddress;
let shopContractAddress;

const token = new web3.eth.Contract(tokenAbi);
token.deploy({
	data: tokenBytecode,
  arguments: [1000000000]
}).send({
  from: '0x....',
  gas: '5000000'
}).then((contractInstance) => {
	tokenContractAddress = contractInstance.options.address;
});

const shop = new web3.eth.Contract(shopAbi);
shop.deploy({
  data: shopBytecode,
  arguments: [tokenContractAddress],
}).send({
  from: '0x....',
  gas: '5000000'
}).then((contractInstance) => {
	shopContractAddress = contractInstance.options.address;
});

จะเห็นว่า การ Deploy ผ่าน web3 นั้นค่อนข้างยุ่งยาก ไม่สะดวกเอามาก ๆ เมื่อเราต้องแก้ไขและ Deploy Smart Contract จำนวนมาก

…ความยุ่งยากนี้จะหมดไป หากใช้ Truffle

วิธีการทำจะเป็นอย่างไรนั้น เชิญชมได้เลยค่ะ

…แต่ก่อนที่จะไปต่อ หากคุณผู้อ่านยังไม่สร้างโปรเจค ผู้เขียนขอแนะนำให้สร้างโปรเจคตามบทความ “มาพัฒนา DApp ง่าย ๆ ด้วย Truffle กันเถอะ ตอนที่ 2: Make New Project” ก่อนนะคะ

ถ้าสร้างเรียบร้อยแล้ว มาเริ่มกันเลย!

Create Migration Scripts

ก่อนที่จะ Deploy ในครั้งแรกนั้น เราต้องเขียน Migration ก่อนค่ะ

Migration คืออะไร?

Migration Script ใน Truffle คือไฟล์ JavaScript ที่ช่วยทำงานที่เกี่ยวข้องกับการ Deploy Smart Contract ของเรา เมื่อ Truffle รัน Migration ตัวหนึ่งแล้วมันจะบันทึกหมายเลขของ Migration ตัวนี้ใน Contract พิเศษของ Truffle ที่มีชื่อว่า Migrations ค่ะ แล้วหากเราสั่งให้ Truffle ช่วย Deploy ครั้งต่อไป Truffle จะไม่รัน Migration ตัวเดิมอีก

จากบทความตอนที่แล้ว หากเราสังเกตที่โฟลเดอร์ contracts จะเห็นว่ามี Smart Contact ที่ชื่อ Migrations …ใช่แล้วค่ะ มันคือ Smart Contract ที่จะช่วยเก็บหมายเลขของ Migration ให้เรา มันถูกสร้างขึ้นหลังจากเราสร้างโปรเจคเสร็จแล้ว อยู่ที่ contracts/Migrations.sol หน้าตาของมันเป็นแบบในรูปนี้ค่ะ

pragma solidity >=0.4.21 <0.6.0;

contract Migrations {
  address public owner;
  uint public last_completed_migration;

  constructor() public {
    owner = msg.sender;
  }

  modifier restricted() {
    if (msg.sender == owner) _;
  }

  function setCompleted(uint completed) public restricted {
    last_completed_migration = completed;
  }

  function upgrade(address new_address) public restricted {
    Migrations upgraded = Migrations(new_address);
    upgraded.setCompleted(last_completed_migration);
  }
}

และในโปรเจคของเราจะมีโฟลเดอร์ชื่อว่า migrations เป็นโฟลเดอร์ที่ช่วยรวบรวม Migration Script ภายในจะมีไฟล์ JavaScript อยู่ไฟล์หนึ่ง ชื่อว่า 1_initial_migration.js เป็น Migration Script ที่ Truffle สร้างมาให้เพื่อใช้ Deploy Contract Migration หลังจาก Script นี้เป็นต้นไป เราก็สามารถเขียน Migration Script ของตัวเองได้เลยค่ะ โดใช้ชื่อที่มีหมายเลขนำหน้าตามด้วยชื่อที่เราต้องการ เช่น 2_deploy_contracts.js

const Migrations = artifacts.require("Migrations");

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

จาก 1_initial_migration.js จะเห็นได้ว่าวิธีการ Deploy ใน Truffle ง่ายมากเลยคือ โหลด Artifact ของ Truffle ที่ได้จากการ Compile มาทำการ Deploy โดยใช้คำสั่ง deployer.deploy(...) เมื่อเทียบกับ Script ที่เราใช้ web3 ในตอนแรกแล้ว จะเห็นว่า Script ที่เราจะเขียนตอนนี้ สั้นกว่ามาก

สำหรับการ Deploy Smart Contract ที่เราเขียนขึ้น สามารถเขียน Migration ได้ดังนี้ค่ะ

const Token = artifacts.require('Token');
const Shop = artifacts.require('Shop');

module.exports = function(deployer) {
  deployer
    .deploy(Token, 1000000)
    .then(async () => {
      const tokenContract = await Token.deployed();
      return deployer.deploy(Shop, tokenContract.address);
    })
};

Smart Contract ทั้ง Token และ Shop มี Constructor ดังที่แสดงไว้ข้างล่าง ดังนั้นเราจึงต้องใส่ Argument ลงในคำสั่ง deployer.deploy(...) หลังจากชื่อ Contract ด้วย

pragma solidity >=0.4.25 <0.6.0;

contract Token {
	  ...
    constructor(uint256 initialSupply) public {
        balanceOf[msg.sender] = initialSupply;
    }
		...
}

pragma solidity >=0.4.25 <0.6.0;


contract Shop {

    ...
    constructor (address _tokenAddress) public {
        token = Token(_tokenAddress);
    }
    ...
}

จากโค้ดเป็นการบอกว่า ให้ Truffle ช่วย Deploy Token โดยกำหนดให้ระบบของเรามีเงินหมุนเวียนในระบบ 1,000,000 Token ค่ะ และหลังจากนั้น ให้ Deploy Shop ด้วย โดยให้ Shop ใช้ Token ที่เพิ่ง Deploy ไปในการเรียกฟังก์ชัน transfer ภายในตัวมันเอง

Migration Command

ใน Truffle เราสามารถรัน Migration ได้ในคำสั่งเดียว คือ

truffle migrate

ที่นี้ เมื่อเราลองรันคำสั่งนี้ใน Command Line ผลลัพธ์ที่ได้จะเป็นดังนี้ค่ะ

Compiling your contracts...
===========================
> Compiling ./contracts/Migrations.sol
> Compiling ./contracts/Shop.sol
> Compiling ./contracts/Token.sol
> Artifacts written to /mnt/data/truffle-example/simple_ecommerce_contract/build/contracts
> Compiled successfully using:
   - solc: 0.5.0+commit.1d4f565a.Emscripten.clang


Could not connect to your Ethereum client with the following parameters:
    - host       > 127.0.0.1
    - port       > 7545
    - network_id > *
Please check that your Ethereum client:
    - is running
    - is accepting RPC connections (i.e., "--rpc" option is used in geth)
    - is accessible over the network
    - is properly configured in your Truffle configuration file (truffle-config.js)

Truffle v5.0.13 (core: 5.0.13)
Node v10.15.3

จากผลลัพธ์ จะเห็นได้ว่า หากเรายังไม่ Compile Smart Contract ให้ครบ Truffle จะ Compile ให้โดยอัตโนมัติ และพยายามเชื่อมต่อกับ Ethereum เพื่อทำการ Deploy ต่อไปค่ะ

แต่เนื่องจาก Truffle ค้นหา Ethereum ของเราไม่เจอ Truffle จึงแจ้งมาพร้อมให้คำแนะนำกับเราค่ะ

ทำไมถึงหาไม่เจอ? นั่นก็เป็นเพราะว่า เรายังไม่กำหนดรายละเอียดของ Blockchain ที่เราใช้งานให้ Truffle รู้ค่ะ วิธีการกำหนดสามารถดูในหัวข้อถัดไปได้เลยค่ะ

Connect Ethereum Networks

ก่อนที่จะ Deploy เราต้องตรวจสอบให้แน่ใจว่า เครื่องของเราได้เชื่อมต่อกับ Ethereum เรียบร้อยแล้ว ในบทความนี้จะเชื่อมต่อกับ Ethereum ที่เป็น Private Network (หมายถึง Blockchain ที่ใช้ได้เฉพาะเครื่องของเรา หรือเครื่องที่เรากำหนดเท่านั้น) หากคุณผู้อ่านยังไม่มี Private Network เป็นของตัวเอง ด้าน Truffle เองก็ยังมีโปรแกรมช่วยสร้างอีกโปรแกรมหนึ่ง ชื่อว่า Ganache ค่ะ ความพิเศษของมันคือ เมื่อเปิดขึ้นมาแล้ว คุณจะได้ Blockchain พร้อมใช้งานทันที โดยไม่ต้องนั่งเขียนคำสั่งเองแม้แต่คำสั่งเดียว!

คุณผู้อ่านสามารถดาวน์โหลดตัวติดตั้งได้ที่ https://truffleframework.com/ganache เมื่อติดตั้งเรียบร้อย ก็เปิดโปรแกรมขึ้นมา และคลิกที่ Quickstart หากทำตามแล้วได้ดังภาพด้านบน …ยินดีด้วย! คุณผู้อ่านได้สร้าง Blockchain เรียบร้อยแล้วค่ะ! 🙂 …แต่อย่าเผลอปิดไปในระหว่างที่ Deploy นะคะ

Config Ethereum Networks

หลังจากเชื่อมต่อกับ Ethereum เรียบร้อยแล้ว เราต้องบอกให้ Truffle รู้ว่าต้องเชื่อมต่อกับ Network ใดบ้าง โดยการแก้ไขที่ไฟล์ truffle-config.js ในหัวข้อ networks เราสามารถกำหนดตามตัวอย่างที่ Truffle ยกมาในไฟล์ได้เลย แต่ในที่นี้ให้สร้าง Network ชื่อ development ตามนี้ค่ะ

networks: {
    development: {
     host: "127.0.0.1",     // Localhost (default: none)
     port: 8545,            // Standard Ethereum port (default: none)
     network_id: "*",       // Any network (default: none)
    },
  },

development จะเป็น Network ตัวแรกที่ Truffle มองหาอัตโนมัติเมื่อเราทำคำสั่ง truffle migrate เพราะฉะนั้นเมื่อเราทำคำสั่ง truffle migrate Truffle จะพยายามเชื่อมต่อ Network development เสมอ

ที่นี้ เมื่อพร้อมแล้ว ก็ทำคำสั่ง truffle migrate ได้เลยค่ะ

Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.


Starting migrations...
======================
> Network name:    'development'
> Network id:      5777
> Block gas limit: 0x6691b7


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

   Deploying 'Migrations'
   ----------------------
   > transaction hash:    0xb825048000bd455e8f7371c9b4ec44f9552003b204fa6b41e209599eb23df745
   > Blocks: 0            Seconds: 0
   > contract address:    0x78cEDc7d6DB8a3C13067AD4BD633F6645422Bafb
   > block number:        1
   > block timestamp:     1557647867
   > account:             0x49D995cb90891917d5fBb429EF6Ca5F1c42c6056
   > 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 'Token'
   -----------------
   > transaction hash:    0x7fb67f6e6da7f8592d71e94e9d6c3dc59028e8acea6f932702a7da51c8cd14b9
   > Blocks: 0            Seconds: 0
   > contract address:    0xA9A19BE1d9705f31aAD05FDd7ffc7B369AEe88EE
   > block number:        3
   > block timestamp:     1557647868
   > account:             0x49D995cb90891917d5fBb429EF6Ca5F1c42c6056
   > balance:             99.98769522
   > gas used:            288297
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.00576594 ETH


   Deploying 'Shop'
   ----------------
   > transaction hash:    0xe4cbe7c70003a8c2f5a610e248cf28b32020b3207d8fe38b802940e0821f06bd
   > Blocks: 0            Seconds: 0
   > contract address:    0x88a5BDa662A2dD31dE6Eb9EAb2ababb2C9bC9BD5
   > block number:        4
   > block timestamp:     1557647868
   > account:             0x49D995cb90891917d5fBb429EF6Ca5F1c42c6056
   > balance:             99.97100724
   > gas used:            834399
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.01668798 ETH


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


Summary
=======
> Total deployments:   3
> Final cost:          0.02815208 ETH

หลังจาก Deploy เสร็จเรียบร้อย Truffle ก็จะทำการบันทึก Address ที่ได้ไว้ใน Artifact ของ Smart Contract แต่ละตัวด้วย เราจะไม่ต้องจำ Address ของ Smart Contract ด้วยตัวเองอีกต่อไป

ตัวอย่างการบันทึก Address ใน build/contracts/Shop.json

"networks": {
    "5777": {
      "events": {},
      "links": {},
      "address": "0x1444ac14a1E725E1B30C4eA2b20be3F29100BB98",
      "transactionHash": "0xb3f3866eb2847e10ce3ceadb6bd3a334a76d738023f74ae2ef02a637912f520a"
    }
  }

Truffle จะบันทึกที่ท้าย ๆ ไฟล์ (หัวข้อ networks) ในตัวอย่างนี้ 5777 คือ Network ID ของ Ganache Blockchain ที่เราได้สร้างในหัวข้อที่แล้วค่ะ (Network ID คือค่าระบุตัวตนของ Blockchain นั่นเอง) ภายในนี้ จะเก็บ Address ของ Contract ที่เราต้องนำไปใช้งานใน DApp รวมทั้ง Hash ของ Transaction (transactionHash) ที่แสดงว่าเราได้ปล่อย Smart Contract เข้ามาใน Blockchain แล้วด้วย

นอกจาก development แล้ว หากคุณผู้อ่านต้องการให้ Truffle ทำการ Deploy ไปยัง Network อื่น ๆ บ้างก็สามารถเพิ่ม Network ของตัวเอง ในรูปแบบตามตัวอย่างด้านล่างนี้

networks: {
    development: {
     host: "127.0.0.1",     // Localhost (default: none)
     port: 8545,            // Standard Ethereum port (default: none)
     network_id: "*",       // Any network (default: none)
    },
    mainnet: {
     host: "127.0.0.1",
     port:  8545,
     network_id: 1,
    },
    testnet: {
     host: "127.0.0.1",
     port:  8545,
     network_id: 3,
    },
  },

จากตัวอย่าง เป็นการเพิ่ม Network อีก 2 ตัวเข้าไป โดยตัวแรกที่เพิ่มเป็น Mainnet ของ Ethereum (Ethereum Network ตัวจริง) และตัวที่สองเป็น Testnet ของ Ethereum (Network ที่ Ethereum จัดไว้ให้เราทดสอบในสภาพเหมือน Mainnet แต่ไม่ต้องเสียค่าใช้จ่ายจริงเหมือน Mainnet) ทั้งนี้หากต้องการใช้ Network ตามตัวอย่าง เครื่องของคุณผู้อ่านจะต้องมี Blockchain Node ที่ Sync ข้อมูลของ Mainnet และ Testnet เรียบร้อยแล้วนะคะ

เมื่อเพิ่มข้อมูลเรียบร้อย หากเราจะ Deploy ไปที่ Network ตามที่เราต้องการ เราต้องกำหนดชื่อ Network ในคำสั่งด้วย ยกตัวอย่าง หากเราต้องการ Deploy Smart Contract ไปยัง testnet เราสามารถทำได้โดยคำสั่ง

truffle migrate --network testnet

เพียงเท่านี้ Smart Contract ของเราก็จะเข้าไปอยู่ใน Ethereum ที่เราเลือกแล้วค่ะ

ปรับแต่งอีกนิด

นอกจากการ Deploy แล้ว Migration ยังสามารถทำอย่างอื่นได้อีกไม่ต่างจากแอปพลิเคชัน JavaScript ทั่วไปเลย เราสามารถเรียกใช้งานฟังก์ชันต่าง ๆ ที่อยู่ใน web3 แม้กระทั่งใน Smart Contract ของเราเองโดยไม่ต้องติดตั้งและประกาศอะไรเพิ่มเลยค่ะ

ใน 2_deploy_contracts.js ของเรา จะมีการแก้ไขโดยให้ประกาศตัวแปรเพิ่มต่อจาก deployer ดังนี้ค่ะ

module.exports = function(deployer, network, accounts) {
      ...
}

การประกาศ network เข้าไปจะทำให้เราสามารถใช้ชื่อ Network ที่เรา Deploy ใน Script ได้ตามต้องการ ส่วนมากมักใช้เมื่อเราต้องการตรวจสอบว่า Network ที่เราจะ Deploy ไปนั้นคืออะไรก่อนที่จะทำคำสั่งต่อค่ะ

module.exports = function(deployer, network) {
  if (network == "mainnet") {
    // ถ้า Network เป็น "mainnet" ให้ทำตามนี้
    // statement 1
    // statement 2
    // ...
  } else {
    // ถ้า Network ไม่ใช่ "mainnet" ให้ทำตามนี้
    // statement 1
    // statement 2
    // ...
  }
}

ส่วน accounts จะเก็บรายการ Address ที่มีอยู่ใน Network นั้น เราสามารถใช้ในคำสั่งส่ง Transaction (ส่งจากใครไปหาใคร) และเป็น Parameter ของฟังก์ชันต่าง ๆ ได้เลยค่ะ

ในโปรเจคของเรา หลังจาก Deploy Smart Contract ทั้งสองเรียบร้อยแล้ว ผู้เขียนต้องการให้มีการส่งเงินจาก Account แรกไปยัง Account ที่ 2 เนื่องจากตอนที่ Deploy Token เงินทั้งหมดที่เรากำหนดจะไปกองรวมกันที่ Account แรกทั้งหมด (เราเรียก Account ที่เก็บเงินทั้งหมดว่า coinbase) ดังนั้นเราต้องออกเหรียญ (Issue Token) ให้ Account อื่นจับจ่ายซื้อของในระบบเรา

const Token = artifacts.require('Token');
const Shop = artifacts.require('Shop');

module.exports = function(deployer, network, accounts) {
  deployer
    .deploy(Token, 1000000)
    .then(async () => {
      const tokenContract = await Token.deployed();
      return deployer.deploy(Shop, tokenContract.address);
    })
    .then(async () => {
      const token = await Token.deployed();
      const coinbase = accounts[0];
      const value = 50000;
      await token.transfer(coinbase, accounts[1], value);
    });
};

เมื่อปรับแต่ง Script เสร็จแล้ว เราต้องทำการ Deploy ใหม่อีกรอบโดยการทำคำสั่งด้านล่างนี้เพื่อให้ Truffle รัน Migration ตั้งแต่ต้น

truffle migrate --reset

หรือหากต้องการ Deploy ไปที่ Network อื่นที่ไม่ใช่ development สามารถพิมพ์คำสั่งดังด้านล่างนี้ค่ะ

truffle migrate --reset --network testnet

สำหรับบทความตอนนี้ ผู้เขียนก็ขอจบตอนเพียงเท่านี้นะคะ ในบทความตอนสุดท้าย เราจะเตรียมทำแอปพลิเคชัน Ecommerce ที่มี Smart Contract ทั้งสองทำงานอยู่ข้างหลังกัน

ขอขอบคุณคุณผู้อ่านที่อ่านบทความมาถึงตรงนี้ด้วยนะคะ แล้วพบกันใหม่ สวัสดีค่ะ ?