绝大部分开发者学习一门语言的时候,都是从输出一个 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":