那麼讓我們開始,在Web應用程式的中間建立一個帶有2個按鈕的框。建立一個名為dice的資料夾和一個名為index.html的檔案。這是程式碼:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Dice ethereum game</title>
</head>
<body>
<div class="main-content">
<button>Start new game</button>
<button>Join existing game</button>
</div>
</body>
</html>
在這段程式碼中,我剛建立了一個基本的HTML結構,其中包含一個包含按鈕和標題的div。請注意,div有一個名為main-content的類,我們稍後會使用它。
讓我們用一些css修飾一下介面。使用以下程式碼建立一個名為index.css的檔案:
body {
font-family: sans-serif;
}
.main-content {
margin: auto;
max-width: 500px;
background-color: whitesmoke;
padding: 50px;
border-radius: 10px;
display: grid;
grid-template-rows: 1fr 1fr;
grid-template-columns: 1fr 1fr;
grid-column-gap: 10px;
}
.main-content h2 {
grid-column: 1 / span 2;
}
.main-content button {
border: none;
color: white;
background-color: #007dff;
padding: 20px;
border-radius: 5px;
cursor: pointer;
}
.main-content button:hover {
opacity: 0.8;
}
.main-content button:active {
opacity: 0.6;
}
我為HTML新增了一個h2標題以使其看起來更好,請確保透過向CSS新增連結來更新HTML:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="index.css">
<title>Dice ethereum game</title>
</head>
<body>
<div class="main-content">
<h2>Ethereum Dice</h2>
<button>Start new game</button>
<button>Join existing game</button>
</div>
</body>
</html>
我決定顯示使用者所需的下一個操作的最佳方法是在javascript中顯示包含所需資訊的div。所以當他點選“開始新遊戲”時,他會看到一個盒子,詢問他想要為遊戲設定多少託管。
他點選“加入現有遊戲”,他將被要求提供現有遊戲的託管和合同地址。
以下是按鈕操作的響應方式:
為了實現這一點,我用一些JavaScript邏輯建立了一個index.js檔案。
function start() {
document.querySelector('#new-game').addEventListener('click', () => {
const classNewGameBox = document.querySelector('.new-game-setup').className
// Toggle hidden box to display it or hide it
if(classNewGameBox === 'new-game-setup') {
// To hide the box
document.querySelector('.new-game-setup').className = 'hidden new-game-setup'
document.querySelector('#button-continue').className = 'hidden'
document.querySelector('#join-game').disabled = false
} else {
// To show the box
document.querySelector('.new-game-setup').className = 'new-game-setup'
document.querySelector('#button-continue').className = ''
document.querySelector('#join-game').disabled = true
}
})
document.querySelector('#join-game').addEventListener('click', () => {
const classJoinGameBox = document.querySelector('.join-game-setup').className
// Toggle hidden box to display it or hide it
if(classJoinGameBox === 'join-game-setup') {
document.querySelector('.new-game-setup').className = 'hidden new-game-setup'
document.querySelector('.join-game-setup').className = 'hidden join-game-setup'
document.querySelector('#button-continue').className = 'hidden'
document.querySelector('#new-game').disabled = false
} else {
document.querySelector('.new-game-setup').className = 'new-game-setup'
document.querySelector('.join-game-setup').className = 'join-game-setup'
document.querySelector('#button-continue').className = ''
document.querySelector('#new-game').disabled = true
}
})
}
start()
解釋一下我做了什麼:
1. 首先,我建立了一個名為start()的函式,該函式將會立即執行並打包內容,以便它包含在一個大函式中。
2. 然後我建立了2個事件監聽器,每當我單擊html檔案中的啟動或連線按鈕時,它們就會被啟用。一個用於#new-game按鈕,另一個用於#joall-game按鈕。使用document.querySelector(),這是在js程式碼中選擇任何內容的最有效方法之一。
3. 在這些監聽器中,我顯示或隱藏每個相應元素的div框。基本上選擇帶有querySelector的盒子並刪除或新增隱藏的類,它在css中設定為display:none;。
然後我們可以將js檔案與我們的modifie index.html連線:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="index.css">
<title>Dice ethereum game</title>
</head>
<body>
<div class="main-content">
<h2>Ethereum Dice</h2>
<button id="new-game">Start new game</button>
<button id="join-game">Join existing game</button>
<div class="hidden new-game-setup">
<h3>How much escrow will you use in ETH?</h3>
<input type="number" placeholder="2...">
</div>
<div class="hidden join-game-setup">
<h3>What's the smart contract address of the existing game?</h3>
<input type="text" placeholder="0x38dfj39...">
</div>
<button id="button-continue" class="hidden">Continue</button>
</div>
<script src="index.js"></script>
</body>
</html>
我把新增的新程式碼塊加粗。以下是更新後的CSS,用於設定新資訊的樣式:
body {
font-family: sans-serif;
}
.hidden {
display: none;
}
.main-content {
margin: auto;
max-width: 500px;
background-color: whitesmoke;
padding: 50px;
border-radius: 10px;
display: grid;
grid-template-rows: 1fr 80px auto;
grid-template-columns: 1fr 1fr;
grid-column-gap: 10px;
}
.main-content h2 {
grid-column: 1 / span 2;
}
.main-content button {
border: none;
color: white;
background-color: #007dff;
padding: 20px;
border-radius: 5px;
cursor: pointer;
}
.main-content button:hover {
opacity: 0.8;
}
.main-content button:active {
opacity: 0.6;
}
.main-content button:disabled {
opacity: 0.5;
background-color: grey;
cursor: auto;
}
.main-content input {
width: 100%;
border-radius: 10px;
padding: 10px;
border: 1px solid lightgrey;
}
.main-content div.new-game-setup, .main-content div.join-game-setup {
grid-column: 1 / span 2;
}
#button-continue {
grid-column: 1 / span 2;
margin-top: 20px;
}
“Continue”按鈕現在不起任何作用,所以讓我們建立一個功能來部署新的智慧合約,並在使用者希望在下一節中建立新遊戲時開啟狀態通道。
2.建立並連線初始智慧合約
現在是建立智慧合約的並使用web3.js將其與JavaScript連線的時候了。現在我們只需要建構函式和一些基本資訊,並將這段程式碼自己寫在一個名為Dice.sol的新檔案中:
pragma solidity 0.4.25;
contract Dice {
address public player1;
address public player2;
uint256 public player1Escrow;
uint256 public player2Escrow;
constructor() public payable {
require(msg.value > 0);
player1 = msg.sender;
player1Escrow = msg.value;
}
function setupPlayer2() public payable {
require(msg.value > 0);
player2 = msg.sender;
player2Escrow = msg.value;
}
}
有2個函式,建構函式用於設定第一個播放器的地址和託管,setupPlayer2()函式用於設定第二個播放器的資訊。
我們希望在使用者單擊“continue”按鈕時部署智慧合約並使用指定的msg.value執行建構函式。為此,我們必須在智慧合約中實施web3.js。因為它是與瀏覽器上的區塊鏈進行通訊的主要方式。
請點選連線進行下載,單擊raw檢視完整程式碼並複製程式碼以將其貼上到專案資料夾中名為web3.js的新檔案中:https://github.com/ethereum/web3.js/blob/develop/dist/web3.js
如果您使用metamask,則不必執行此操作,因為metamask為您注入了web3.js的版本,但如果metamask不可用,則需要專案中的web3庫與區塊鏈進行互動。
我們使用metamask與區塊鏈互動。但是當您在瀏覽器上開啟index.html檔案時,會打不開檔案,因為metamask不支援file://副檔名。
我們需要執行一個本地伺服器,將檔案提交給http:// localhost:8080 url,因為當您直接開啟index.html檔案時,metamask不起作用。為此,請開啟終端並安裝:
npm i -g http-server
然後,在專案資料夾中執行http-server以啟動index.html的本地伺服器:
http-server
這將為localhost:8080上的檔案提供服務,這樣您就可以訪問它們並從metamask注入Web3。
在這種情況下,讓我們集中精力部署我們剛從我們的Web應用程式建立的合同,就在使用者單擊“continue”時。
要部署新合同,我們需要ABI,建構函式引數和位元組碼。這些是web3.js的要求。
要生成ABI,請轉到remix.ethereum.org,將程式碼貼上到主部分,然後單擊ABI:
這將複製ABI程式碼。轉到專案資料夾並建立一個名為contractData.js的檔案,將程式碼貼上到一個名為abi的變數,如下所示:
const abi = [
{
"constant": true,
"inputs": [],
"name": "player2Escrow",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "player1Escrow",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "player2",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [],
"name": "setupPlayer2",
"outputs": [],
"payable": true,
"stateMutability": "payable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "player1",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"payable": true,
"stateMutability": "payable",
"type": "constructor"
}
]
2.現在我們需要智慧合約的bytecode。bytecode是將被部署到區塊鏈的已編譯的智慧合約,我們需要該資訊才能部署它。要使bytecode再次重新混合並單擊此按鈕:
並在contractData.js中建立另一個變數,稱為betycode,其資訊如下:
const abi = [
{
"constant": true,
"inputs": [],
"name": "player2Escrow",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "player1Escrow",
"outputs": [
{
"name": "",