web3.js 编译 Solidity,发布,调用全部流程(手把手教程) 下面教程是打算在尽量牵涉可能少的以太坊的相关工具,主要使用 web3.js 这个以太坊提供的工具包,来完成合约的编译,发布,合约方法调用的一整个流程。一方面来了解以太坊开发到底需要什么,另一方面来对 web3.js 的 API 有个基本的了解。由于所有其它工具都或多或少的是对 web3.js 的底层函数的包装,所以对 web3.js 使用流程有个认识之后,也能更好的入门,使用相关的工具。
1. 准备工作 1.1 安装 Node.js 由于我们要使用 web3.js1 。这里使用 Node 来集成 web3.js 模块(当然,你还可以使用其它的方式)。你可以通过参考官网文档安装2 。
1.1.1 Ubuntu 如果你使用 ubuntu,可以使用下述命令:
1 2 3 4 sudo apt-get install nodejs sudo apt-get install npm
1.1.2 MAC 如果你使用Homebrew
,可以使用下述命令:
1 2 3 4 brew install node brew install npm
1.1.3 安装检查 安装成功后,可以查看下当前的版本,确认正常安装:
1.2 以太坊的节点 由于整个合约代码的执行需要一个虚拟机环境,所以在开始之前,我们不得不安装一个实现了以太坊虚拟机的节点。
可以选择一个轻量级的节点,比如EtherumJS TestRPC
,它是一个完整的在内存中的区块链仅仅存在于你开发的设备上。它在执行交易时是实时返回,而不等待默认的出块时间,这样你可以快速验证你新写的代码,当出现错误时,也能即时反馈给你。
1 npm install -g ethereumjs-testrpc
安装好后,你就可以通过testrpc
命令来启动了,启动与大多数以太坊节点一样,运行在localhost:8545
。
如果你安装geth
这样的客户端也是可以的。
1.3 Web3 的支持 安装web3
的模块[web3]:
2. 合约编译 2.1 一个简单的合约 我们打算用来测试的合约如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 pragma solidity ^0.4 .0 ; contract Calc{ uint count; function add (uint a, uint b ) returns (uint ) { count++; return a + b; } function getCount ( ) constant returns (uint ) { return count; } }
add()
方法用来返回输入两个数据的和,并会对add()
方法的调用次数进行计数。需要注意的是这个计数是存在区块链上的,对它的调用需要使用transaction
。
getCount()
返回add()
函数的调用次数。由于这个函数不会修改区块链的任何状态,对它的调用使用call
就可以了。
2.2 编译合约 由于合约是使用Solidity
编写,所以我们可以使用web3.eth.compile.solidity
来编译合约3 :
1 2 3 4 let source = "pragma solidity ^0.4.0;contract Calc{ /*区块链存储*/ uint count; /*执行会写入数据,所以需要`transaction`的方式执行。*/ function add(uint a, uint b) returns(uint){ count++; return a + b; } /*执行不会写入数据,所以允许`call`的方式执行。*/ function getCount() returns (uint){ return count; }}" ;let calc = web3.eth.compile.solidity(source);
如果编译成功,结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 { code: '0x606060405234610000575b607e806100176000396000f3606060405260e060020a6000350463771602f781146026578063a87d942c146048575b6000565b3460005760366004356024356064565b60408051918252519081900360200190f35b3460005760366077565b60408051918252519081900360200190f35b6000805460010190558181015b92915050565b6000545b9056' , info: { source: 'pragma solidity ^0.4.0;contract Calc{ /*区块链存储*/ uint count; /*执行会写入数据,所以需要`transaction`的方式执行。*/ function add(uint a, uint b) returns(uint){ count++; return a + b; } /*执行不会写入数据,所以允许`call`的方式执行。*/ function getCount() returns (uint){ return count; }}' , language: 'Solidity' , languageVersion: '0.4.6+commit.2dabbdf0.Emscripten.clang' , compilerVersion: '0.4.6+commit.2dabbdf0.Emscripten.clang' , abiDefinition: [ [ Object ], [ Object ] ], userDoc: { methods: { } }, developerDoc: { methods: { } } } }
3. 发布合约 web3.js
其实也像框架一样对合约的操作进行了封装。发布合约时,可以使用web3.eth.contract
的new
方法4 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let myContractReturned = calcContract.new({ data: deployCode, from : deployeAddr }, function (err, myContract ) { if (!err) { if (!myContract.address) { console .log("contract deploy transaction hash: " + myContract.transactionHash) } else { } });
部署过程中需要主要的是,new
方法的回调会执行两次,第一次是合约的交易创建完成,第二次是在某个地址上完成部署。需要注意的是只有在部署完成后,才能进行方法该用,否则会报错TypeError: myContractReturned.add is not a function
。
4. 调用合约 由于web3.js
封装了合约调用的方法。我们可以使用可以使用web3.eth.contract
的里的sendTransaction
来修改区块链数据。在这里有个坑,有可能会出现Error: invalid address
,原因是没有传from
,交易发起者的地址。在使用web3.js
的 API 都需留意,出现这种找不到地址的,都看看from字段吧。
1 2 3 4 5 6 myContract.add.sendTransaction(1 , 2 , { from : deployeAddr, }); console .log("after contract deploy, call:" + myContract.getCount.call());
需要注意的是,如果要修改区块链上的数据,一定要使用sendTransaction
,这会消耗gas
。如果不修改区块链上的数据,使用call
,这样不会消耗gas
。
5. 使用 web3.js 编译,发布,调用的完整源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 let Web3 = require ("web3" );let web3;if (typeof web3 !== "undefined" ) { web3 = new Web3(web3.currentProvider); } else { web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545" )); } let from = web3.eth.accounts[0 ];let source = "pragma solidity ^0.4.0;contract Calc{ /*区块链存储*/ uint count; /*执行会写入数据,所以需要`transaction`的方式执行。*/ function add(uint a, uint b) returns(uint){ count++; return a + b; } /*执行不会写入数据,所以允许`call`的方式执行。*/ function getCount() constant returns (uint){ return count; }}" ;let calcCompiled = web3.eth.compile.solidity(source);console .log(calcCompiled);console .log("ABI definition:" );console .log(calcCompiled["info" ]["abiDefinition" ]);let abiDefinition = calcCompiled["info" ]["abiDefinition" ];let calcContract = web3.eth.contract(abiDefinition);let deployCode = calcCompiled["code" ];let deployeAddr = web3.eth.accounts[0 ];let myContractReturned = calcContract.new( { data: deployCode, from : deployeAddr, }, function (err, myContract ) { if (!err) { if (!myContract.address) { console .log("contract deploy transaction hash: " + myContract.transactionHash); } else { console .log("contract deploy address: " + myContract.address); myContract.add.sendTransaction(1 , 2 , { from : deployeAddr, }); console .log("after contract deploy, call:" + myContract.getCount.call()); } } } ); console .log("returned deployed didn't have address now: " + myContractReturned.address);
原文