摘要:代碼如下設置連接為提供的節點鏈接讀取合約編譯合約代碼獲取合約和字節碼創建合約對象部署合約,并返回部署對象部署合約的外部賬戶地址部署合約的礦工費代碼里我加上了簡單的注釋。
原文發表于:以太坊智能合約開發第四篇:實現Hello World智能合約
絕大部分開發者學習一門語言的時候,都是從輸出一個 Hello World 開始。我們也從實現一個 Hello World 合約為切入點,開始進入智能合約的世界吧。
環境準備安裝好 node 和 npm。這里對node和npm的安裝過程,不做詳細介紹。本篇依賴的環境版本:
Node : v8.9.0
Npm: 5.5.1
在你的代碼目錄里,創建名為 smartcontract 的文件夾,并創建如下兩個文件 package.json 、 Hello.sol 。
smartcontract
├── Hello.sol
└── package.json
在 package.json 文件里,添加如下依賴包配置:
{ "name": "smartcontract", "version": "0.0.1", "dependencies": { "fs": "0.0.1-security", "solc": "^0.4.21", "web3": "^0.20.0" } }
fs模塊用于文件的相關操作
solc模塊是編譯器
web3模塊是以太坊提供的工具包,主要用于與合約的通信
接下來執行 npm install 下載相關的依賴包。
編寫合約代碼環境準備好后,就可以開始編寫合約代碼了。 打開 Hello.sol 文件,編寫代碼如下:
//pragma關鍵字:版本申明。 //用來指示編譯器將代碼編譯成特定版本,以免引起兼容性問題 //此處不支持0.4.0之前的編譯器,也不支持0.5.0之后的編譯器(條件為 ^) pragma solidity ^0.4.0; //contract關鍵字:合約申明 //和Java、PHP中的class類似 //此處是申明一個名為Hello的合約 contract Hello { //public: 函數訪問屬性(后續文章為詳細闡述) //returns (string): 定義返回值類型為string function say(string name) public returns (string) { return name; } }
代碼很簡單。就實現了用戶輸入什么字符串,合約就原樣返回的操作。
接下來,我們需要編寫 合約部署 腳本。
編寫合約部署腳本在 smartcontract 目錄下,新建名為 deploy.js 的文件。代碼如下:
//設置web3連接 var Web3 = require("web3"); //http://localhost:7545 為Ganache提供的節點鏈接 var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:7545")); //讀取合約 var fs = require("fs"); var contractCode = fs.readFileSync("Hello.sol").toString(); //編譯合約代碼 var solc = require("solc"); var compileCode = solc.compile(contractCode); console.log(compileCode); //獲取合約abi和字節碼 var abi = JSON.parse(compileCode.contracts[":Hello"].interface); var byteCode = compileCode.contracts[":Hello"].bytecode; //創建合約對象 var VotingContract = web3.eth.contract(abi); //部署合約,并返回部署對象 var deployedContract = VotingContract.new({ data:byteCode, from:web3.eth.accounts[0], //部署合約的外部賬戶地址 gas:750000 //部署合約的礦工費 }); console.log(deployedContract);
代碼里我加上了簡單的注釋。這里解釋一下 abi 這個概念。
abi全稱是 Application Binary Interface,即應用程序二進制接口。簡單的說,就是合約對外的接口描述
需要注意的是,礦工費gas為750000。以太坊上每筆交易的執行(被礦工打包)都會被收取一定數量的gas。gas的目的是限制執行交易所需的工作量,同時為執行支付費用。當EVM執行交易時,gas將按照特定規則被逐漸消耗,無論執行到什么位置,一旦gas被耗盡,將會觸發一個 out of gas 異常。當前調用幀所做的所有狀態修改都將被回滾。如果執行結束,還有gas剩余,這些gas將會返還給發送賬戶。因此,如果部署時拋出 out of gas 的異常,我們可適當的提高gas值。
部署在當前目錄下,執行 node deploy.js 命令。我們在部署腳本里將 compileCode 變量打印出來了,粗略看看就行:
{ contracts: { ":Hello": { assembly: [Object], bytecode: "6060604052341561000f57600080fd5b61016c8061001e6000396000f300606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063d5c6130114610046575b600080fd5b341561005157600080fd5b6100a1600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190505061011c565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100e15780820151818401526020810190506100c6565b50505050905090810190601f16801561010e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61012461012c565b819050919050565b6020604051908101604052806000815250905600a165627a7a72305820ff14cafd1df21e1edf19eff7598bc82a98940cc0fe045d6107d04bb224014f990029", functionHashes: [Object], gasEstimates: [Object], interface: "[{"constant":false,"inputs":[{"name":"name","type":"string"}],"name":"say","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]", metadata: "{"compiler":{"version":"0.4.21+commit.dfe3193c"},"language":"Solidity","output":{"abi":[{"constant":false,"inputs":[{"name":"name","type":"string"}],"name":"say","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"function"}],"devdoc":{"methods":{}},"userdoc":{"methods":{}}},"settings":{"compilationTarget":{"":"Hello"},"evmVersion":"byzantium","libraries":{},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"":{"keccak256":"0x2e3dd18fbfbd17bb4f866b1bfbb38082172a0bb58d9396b63bab04e67d9d8e08","urls":["bzzr://d1aae746dfab03e712d8a3cb76b7d4b5bf60f48fafbffa04dfa8a2d53ad5d0ca"]}},"version":1}", opcodes: "PUSH1 0x60 PUSH1 0x40 MSTORE CALLVALUE ISZERO PUSH2 0xF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x16C DUP1 PUSH2 0x1E PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x60 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x41 JUMPI PUSH1 0x0 CALLDATALOAD PUSH29 0x100000000000000000000000000000000000000000000000000000000 SWAP1 DIV PUSH4 0xFFFFFFFF AND DUP1 PUSH4 0xD5C61301 EQ PUSH2 0x46 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE ISZERO PUSH2 0x51 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xA1 PUSH1 0x4 DUP1 DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP3 ADD DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP1 DUP1 PUSH1 0x1F ADD PUSH1 0x20 DUP1 SWAP2 DIV MUL PUSH1 0x20 ADD PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 SWAP4 SWAP3 SWAP2 SWAP1 DUP2 DUP2 MSTORE PUSH1 0x20 ADD DUP4 DUP4 DUP1 DUP3 DUP5 CALLDATACOPY DUP3 ADD SWAP2 POP POP POP POP POP POP SWAP2 SWAP1 POP POP PUSH2 0x11C JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE DUP4 DUP2 DUP2 MLOAD DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP1 DUP4 DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xE1 JUMPI DUP1 DUP3 ADD MLOAD DUP2 DUP5 ADD MSTORE PUSH1 0x20 DUP2 ADD SWAP1 POP PUSH2 0xC6 JUMP JUMPDEST POP POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x10E JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH2 0x124 PUSH2 0x12C JUMP JUMPDEST DUP2 SWAP1 POP SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x0 DUP2 MSTORE POP SWAP1 JUMP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 SELFDESTRUCT EQ 0xca REVERT SAR CALLCODE 0x1e 0x1e 0xdf NOT 0xef 0xf7 MSIZE DUP12 0xc8 0x2a SWAP9 SWAP5 0xc 0xc0 INVALID DIV 0x5d PUSH2 0x7D0 0x4b 0xb2 0x24 ADD 0x4f SWAP10 STOP 0x29 ", runtimeBytecode: "606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063d5c6130114610046575b600080fd5b341561005157600080fd5b6100a1600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190505061011c565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100e15780820151818401526020810190506100c6565b50505050905090810190601f16801561010e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61012461012c565b819050919050565b6020604051908101604052806000815250905600a165627a7a72305820ff14cafd1df21e1edf19eff7598bc82a98940cc0fe045d6107d04bb224014f990029", srcmap: "25:102:0:-;;;;;;;;;;;;;;;;;", srcmapRuntime: "25:102:0:-;;;;;;;;;;;;;;;;;;;;;;;;47:78;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;99:1;94:3;90:11;84:18;80:1;75:3;71:11;64:39;52:2;49:1;45:10;40:15;;8:100;;;12:14;47:78:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;89:6;;:::i;:::-;114:4;107:11;;47:78;;;:::o;25:102::-;;;;;;;;;;;;;;;:::o" } }, errors: [ ":5:5: Warning: Function state mutability can be restricted to pure function say(string name) public returns (string) { ^ (Relevant source part starts here and spans across multiple lines). " ], sourceList: [ "" ], sources: { "": { AST: [Object] } } }
打開Ganache的 LOGS 面板,可以看到部署產生的交易(transaction)日志:
箭頭所指就是部署成功后的合約地址:0xbf474d24ba8b19811db5deb51137ddccbe3ff288(每個人部署后的地址可能都不相同)。 我們記錄下來,后面的合約調用代碼里需要用到。同時,也可以打開 ACCOUNTS 面板,觀察賬戶余額的變化。
合約調用合約部署成功之后,我們寫一段代碼來調用合約里的 say() 方法,檢測一下效果。在 smartcontract 目錄下,新建名為 run.js 的文件,代碼如下:
//設置web3連接 var Web3 = require("web3"); //http://localhost:7545 為Ganache提供的節點鏈接 var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:7545")); //讀取合約 var fs = require("fs"); var contractCode = fs.readFileSync("Hello.sol").toString(); //編譯合約代碼 var solc = require("solc"); var compileCode = solc.compile(contractCode); //獲取合約abi和字節碼 var abi = JSON.parse(compileCode.contracts[":Hello"].interface); var byteCode = compileCode.contracts[":Hello"].bytecode; //創建合約對象 var VotingContract = web3.eth.contract(abi); //0xbf474d24ba8b19811db5deb51137ddccbe3ff288為合約部署地址 var contractInstance = VotingContract.at("0xbf474d24ba8b19811db5deb51137ddccbe3ff288"); var result = contractInstance.say.call("Hello world"); console.log(result);
我們來執行 node run.js 命令,可以看到在終端里輸出了 Hello world 。
至此,第一個智能合約的代碼編寫 -> 編譯 -> 部署 -> 調用的過程就完成了。建議大家動手執行一遍,加深理解。
拋磚引玉如果我們事先在合約里定義好 Hello 字符串,如何與 name 變量進行字符串拼接?
敬請期待下一篇的講解。
智能合約開發QQ群:753778670
目前有幾套區塊鏈實踐的視頻課程(視頻+源碼),需要的可加我微信(kuangwenjie)私信我(付費):
『區塊鏈』從零構建以太坊(Ethereum)智能合約到項目實戰
基于Ethereum & IPFS的去中心化Ebay區塊鏈項目開發實戰
HyperLedger(超級賬本)Fabric
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/24020.html
摘要:今天我們來一步一步從搭建以太坊智能合約開發環境開始,講解智能合約的如何編寫。開發環境搭建安裝強烈建議新手使用來進行開發。第行修改部署賬戶為新賬戶索引,即使用新賬戶來部署合約。 本文首發于深入淺出區塊鏈社區原文鏈接:智能合約開發環境搭建及Hello World合約原文已更新,請讀者前往原文閱讀 如果你對于以太坊智能合約開發還沒有概念(本文會假設你已經知道這些概念),建議先閱讀入門篇。就先...
摘要:在新智能合約的構造函數中,將引用我們的合約工廠的地址。以太坊,主要是針對工程師使用進行區塊鏈以太坊開發的詳解。以太坊入門教程,主要介紹智能合約與應用開發,適合入門。這里是原文用工廠模式管理多個智能合約 我們寫了一份小的計算合約作為Hello World。如果我們可以創建一個允許用戶創建自己的計數器的合約怎么辦? showImg(https://segmentfault.com/img/...
摘要:說明最近在做以太坊智能合約的開發,在使用做接口的時候遇到很多問題,記錄下來當做參考。簡單說下拍賣智能合約思路用戶注冊新用戶自動注冊一個賬戶地址發起拍賣最高者存在智能合約里拍賣結束獲取合約中的最高者關閉合約 說明 最近在做以太坊智能合約DAPP的開發,在使用PHP做接口的時候遇到很多問題,記錄下來當做參考。本文的操作環境為Mac,已經安裝好truffle/ganache等開發需要的相關工...
摘要:以太坊是什么以太坊是一個建立在區塊鏈技術之上,去中心化應用平臺。運行環境以太坊虛擬機是以太坊中智能合約的運行環境。是由以太坊客戶端提供的是典型的開發以太坊時使用的客戶端,基于語言開發。 本文首發于深入淺出區塊鏈社區原文鏈接:以太坊是什么 - 以太坊開發入門指南原文已更新,請讀者前往原文閱讀 很多同學已經躍躍欲試投入到區塊鏈開發隊伍當中來,可是又感覺無從下手,本文將基于以太坊平臺,以通俗...
閱讀 2950·2021-11-23 09:51
閱讀 3776·2021-11-22 15:29
閱讀 3226·2021-10-08 10:05
閱讀 1552·2021-09-22 15:20
閱讀 952·2019-08-30 15:56
閱讀 1069·2019-08-30 15:54
閱讀 733·2019-08-26 11:54
閱讀 2636·2019-08-26 11:32