บันทึกการอบรม Solidity เขียน Smart Contract บน Ethereum Blockchain

เมื่อวันที่ 25–26 สค. 2516 ที่ผ่านมามีโอกาสได้เข้าร่วมงาน Pizza Hackathon โดยในงานจะมีช่วงการสอนเขียน Smart Contract โดยน้อง Sittiphol Phanvilai (นู๋เนย) ซึ่งก็ถ่ายทอดได้ดีมากโดยใช้เวลาเพียง 2 ชั่วโมง และทางทีมงานก็ได้ทำการแจก Slide พร้อม Video การสอนมีครบเลย ดังน ี้:

Slide : https://www.slideshare.net/nuuneoi/smart-contract-programming-101-with-solidity-pizzahackathon

Video : https://www.facebook.com/Katinrun/videos/310205392864249/?fref=gs&dti=246574362665570&hc_location=group

OK จริงๆ เนื้อหาก็ครบหมดแล้วนะ … ขอจบแต่เพียงเท่านี้ ..

แต่เดี๋ยวก่อน! … ขอ note ฉบับย่อนิดนึงสิ .. (โดยจะสรุปตามความเข้าใจของตัวเอง ไม่มีทฤษฎีใดๆ ทั้งสิ้น) โดยแบ่งเป็น 6 ส่วนตามนี้นะ

  1. MetaMask : Plugin บน Chrome ที่เป็น Ethereum Wallet
  2. Kovan : A Test Ethereum Network และการเติม eth เข้า Wallet
  3. Remix : IDE เพื่อเขียน Smart Contract ด้วย Solidity
  4. Deployment : การนำ Solidity deploy เพื่อใช้งานลง Blockchain (Kovan Network)
  5. Web3 : เรียกใช้ Smart Contract ผ่าน Web3.js
  6. Test : ทดสอบการใช้งาน

1. MetaMask

ติดตั้งกระเป๋าเงิน Metamask ผ่าน Chrome Web Store ก่อนเลย มีเงินนับเป็นน้องมีทองนับเป็นพี่

ติดตั้งเสร็จเราจะได้ icon รูปหมาจิ้งจอก ให้กดเพื่อทำการตั้ง password ให้เรียบร้อย จากนั้นก็ Next ๆๆ Accept ๆๆ ให้เรียบร้อย

อ้อ อย่าลิมจด Secret Backup Phrase 12 คำ ไว้ด้วยเอาไว้กู้ Account Wallet กลับมาและเอาไว้ confirm ในหน้าถัดไป

เมื่อผ่านหน้า Confirm 12 words แล้วเราจะได้ เลขที่กระเป๋าเงินเอาไว้ฝากเงิน (ETH) เข้า

หรือกดเพื่อดู Wallet Address ได้ที่

ทำการจด ETH Wallet Address ไว้ ในกรณีนี้คือ

0x231E2ab8cCE8dfDaf1FbeA96A832CB9B33209B14

สรุป ขั้นตอนนี้เราได้กระเป๋าเงิน (ETH Wallet) เรียบร้อย ^_^

2. Kovan

Blockchain Network จะมี main และ Test ในกรณีนี้เราจะใช้ Test network ที่ชื่อว่า Kovan

จากนัันทำการโอนเงินเข้า wallet เนื่องจากจะได้นำไปใช้จ่ายในการเขียน Smart Contract โดยเข้าไปที่ https://faucet.kovan.network

ซึ่งจะต้อง Login ด้วย user Github (ถ้ายังไม่มีก็เข้าไปสมัครที่ https://github.com)

จากนั้นก็ใส่ ETH Wallet Address ซึ่งระบบก็จะส่ง ETH ไปให้เรา 1 ETH (ขอได้วันละครั้ง)

ทำการตรวจสอบที่ MetaMask ซึ่งก็จะมียอด 1 ETH

สรุป ขั้นตอนนี้เรามีเงินในกระเป๋า Wallet 1 ETH เรียบร้อย ^_^

3. Remix

การเขียน Ethereum Smart Contract จะใช้ภาษา Solidity ซึ่ง IDE ที่ช่วยเขียนในที่นี้เราจะใช้ Remix ที่เป็น browser-based IDE ซึ

http://remix.ethereum.org

ทำการสร้าง SimpleContract smart contract อย่างง่ายด้วยการเก็บค่า balance และดึงค่า balance ดัง code ดังนี้

pragma solidity ^0.4.18;
contract SimpleContract {
uint balance;

constructor() public { // caleed since it deploy to blockchain
balance = 1000;
}

function setBalance(uint newBalance) public {
balance = newBalance;
}

function getBalance() public view returns (uint) {
return balance;
}

}

ทำการ compile ที่ปุ่มบนขวา ซึ่งเมื่อ Compile สำเร็จไม่ error จะขึ้นแถบเขียวๆ

ทำการทดสอบ SimpleContract ก่อน deploy จริงใน Blockchain ดังแสดงในรูป

  1. Run
  2. Environment = JavaScript VM
  3. กด Deploy

ทำการ test Method โดยใส่ค่า setBalance และกดปุ่ม getBalance

สรุป ขั้นตอนนี้เราได้ Smart Contract พร้อม test เรียบร้อยแต่ยังใช้งานจริงไม่ได้เพราะยังไม่ได้ deploy ลง Ethereum Network

4. Deployment

เราทำการ Deploy ลง Ethereum Kovan Network โดยปลี่ยน Environment เป็น Injected Web3 และกด Deploy

ซึ่งเมื่อ Deploy ลง Network จะต้องมีค่าใช้จ่ายในที่นี้คือ 0.02 ETH โดยจะมีปุ่มจาก MetaMask มา pop up ให้กด confirm

ซึ่งหลังจาก Deploy เรียบร้อยแล้วเราต้องการ 2 ค่าเพื่อนำมาเรียกใช้งานต่อคือ

  1. Contract Address : ตำแหน่งที่ Contract ถูก deploy ลง Network สามารถกด link เข้าไปดูได้ดังรูป

โดย contract address ในที่นี้คือ

0x4bf35042311dda3c1f9a07d9e45c0133174439d5

และค่าที่ 2 คือ Application Binary Interface (ABI) ซึ่งสามารถหาได้จาก

(1) Compile -> (2) Detail

(3) ABI กดเพื่อทำการ Copy ลง Clipboard

ซึ่ง ABI ใน Contract นี้คือ

[  {   "constant": false,   "inputs": [    {     "name": "newBalance",     "type": "uint256"    }   ],   "name": "setBalance",   "outputs": [],   "payable": false,   "stateMutability": "nonpayable",   "type": "function"  },  {   "inputs": [],   "payable": false,   "stateMutability": "nonpayable",   "type": "constructor"  },  {   "constant": true,   "inputs": [],   "name": "getBalance",   "outputs": [    {     "name": "",     "type": "uint256"    }   ],   "payable": false,   "stateMutability": "view",   "type": "function"  } ]

จะเห็นได้ว่า ABI สื่อตรงตามชื่อคือเป็น Interface ของ Smart Contract ที่เรา Deploy ลง Network

สรุป ในขั้นตอนนี้หลังจาก Deploy แล้วเราจะได้ 2 ค่าเพื่อนำไปใช้งานคือ Contract Address และ ABI

5. Web3

เป็น client เพื่อเรียกใช้งาน smart contract ที่เรา Deploy ไป ในที่นี้ ก็เขียน้ html + Java Script และเรียกใช้ web3.js เพื่อติดต่อกับ smart contract ที่เรา deploy ไปเรียบร้อยแล้ว

ซึ่ง code index.html มีดังนี้

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>DApp Tutorial1</title>
<style>
body {
background-color:#F0F0F0;
padding: 2em;
font-family: 'Raleway','Source Sans Pro', 'Arial';
}
.container {
width: 50%;
margin: 0 auto;
}
label {
display:block;
margin-bottom:10px;
}
input {
padding:10px;
width: 50%;
margin-bottom: 1em;
}
button {
margin: 2em 0;
padding: 1em 4em;
display:block;
}
#balance {
padding:1em;
background-color:#fff;
margin: 1em 0;
}
#status {
font-weight:normal;
font-family: monospace;
padding:1em;
background-color:#fff;
margin: 1em 0;
}
</style>
</head>
<body>
<!-- Form -->
<div class="container">
<h1>dApp Basic Example</h1>
<h2 id="balance">Current Balance = <span id="currentBalance"></span></h2>
<button id="button" style="display: block" onclick="javascript:getBalance()">Get New Balance</button>
<hr/>
<br/>
<label for="newBalance" class="col-lg-2 control-label"><strong>New Balance</strong></label>
<input id="newBalance" type="number" value="300" style="display: inline-block">
<button id="button" style="display: inline-block" onclick="javascript:setBalance()">Set New Balance</button>
<br/>
<label><strong>Status</strong></label>
<h4 id="status"></h4>
</div>
</body>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/ethereum/web3.js/dist/web3.min.js"></script>
<script src="Lottoreum.json"></script>
<script>
function addStatusLine(text) {
document.getElementById("status").innerHTML = text + "<br/><br/>" + document.getElementById("status").innerHTML;
}
function getBalance() {
addStatusLine("");
addStatusLine("calling getBalance()");
// TODO: Call getBalance Smart Contract
SimpleContract.getBalance(function(errorGet,resultGet){
addStatusLine("Current balance =" + resultGet);
document.getElementById('currentBalance').innerText=resultGet;
})
}
function setBalance() {
// TODO: Call setBalance Smart Contract
//addStatusLine("called");
newBalance =parseInt(document.getElementById('newBalance').value);
//addStatusLine(newBalance);
SimpleContract.setBalance(newBalance,function(error,result){
if(error){
addStatusLine(error);
return;
}
// addStatusLine("");
addStatusLine("calling setBalance("+newBalance+")");
txHash = result;
addStatusLine("TxHash = <a href='https://kovan.etherscan.io/tx/"+
result + "' target='_blank'>" + result + "</a>");
});
}
// Initializing
if (typeof web3 !== 'undefined') {
web3 = new Web3(web3.currentProvider); // inject from Metamask plugin
}
// Get default address
web3.eth.defaultAccount = web3.eth.accounts[0];
// TODO: Replace your SimpleContract contract address here
var contractAddress = '0x4bf35042311dda3c1f9a07d9e45c0133174439d5';
// TODO: Replace your SimpleContract abi here
var abi = [ { "constant": false, "inputs": [ { "name": "newBalance", "type": "uint256" } ], "name": "setBalance", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "payable": false, "stateMutability": "nonpayable", "type": "constructor" }, { "constant": true, "inputs": [], "name": "getBalance", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" } ];
// Create an interface to SimpleContract on TomoChain
var SimpleContractContract = web3.eth.contract(abi);
var SimpleContract = SimpleContractContract.at(contractAddress);
// Get Balance on the first load
getBalance();
</script>
</html>

โดยใน code ดังกล่าวข้างต้นนี้มีสองส่วนที่ต้องนำข้อมูลมาใส่คือ Contract Address และ ABI ที่เราได้จากข้อ 4

สรุป ในขั้นตอนนี้เราได้ file index.html ที่เป็น client ที่สามารถเรียกใช้ smart contract ได้

6. Test

ทำการติดตั้ง Web Server for Chrome เพื่อ host file index.html ที่ได้จากข้อ 5 หรือถ้ามี http server อยู่แล้วก็สามารถข้ามขั้นตอนไปได้เลยครับ

ทำการเลือก folder ที่เรา save index.html ทีได้จากข้อ 5 ในที่นี้ผม save ไว้ที่ desktop folder

ทำการเปิด browser url

http://127.0.0.1:8887

ค่า balance เริ่มต้นคือ 1000 ให้ทดสอบกดปุ่ม Set New Balance = 300 จะมี pop up จาก MetaMask เพื่อถามเพื่อ จ่าย ETH เนื่องจากมีการเปลี่ยนแปลงข้อมูลบน Network

กด Confirm แล้วรอสักครู่ …. แล้วลองกดดูค่า Get New Balance ใหม่จะได้ 300 ตามที่เรา set ค่าเข้าไป

เรียบร้อยแล้วการอ่านเขียนบน Blockchain แบบเริ่มต้นเสร็จสมบูรณ์

อีกนิดถ้าเราลองกดที่ TxHash เราก็จะเข้าไปหน้ารายละเอียด Transaction ที่ kovan.etherscan.io

TxHash = 0x31c8894a955288e3a2ae421aef324fffab1cfbefd9b499f347e861ab2e65433f

สรุป

ก็เป็น note ที่จดไว้กันลืมสำหรับความรู้เบื้องต้นในการเขียน Smart Contract บน Ethereum ซึ่งถ้าต้องการศึกษาเพิ่มเติมแนะนำที่ https://cryptozombies.io ซึ่งเป็นเกมส์สอนเขียน Ethereum DApps ฟรี และเป็นเกมส์ที่ทาง staff แนะนำให้เรียนมาก่อนเข้า Pizza Hackathon (อย่าถามว่าเรียนมาไหม @_@)

สุดท้ายทีมผมทำอะไรก็ตามอ่านได้ -> ที่นี่ครับ น้องโอ๋ น้องในทีมเขียนเอาไว้

ขอบคุณ Staff ทีมงาน Pizza Hackathon ทุกท่านมากๆ ครับผม ผมน่าจะเป็นคนที่ถามมากที่สุดละ ^_^ สามารถเข้าไปดูผลงานได้ที่ official web https://pizzahackathon.github.io

สุดท้ายจริงๆ ขอแปะรูปไว้เป็นที่ระลึกหน่อยครับ