以太坊智能合约开发 - 环境搭建

在区块链的世界中,比特币被称为区块链1.0,以太坊进化为区块链2.0,这一跨越的实现缘于以太坊对智能合约的实现。智能合约在以太坊网络中扮演着至关重要的角色,目前ERC20代币的发行、多重签名乃至DApp的开发等都需要通过智能合约来进行实现等,因此学习智能合约的开发是很重要的。这篇文章会详细阐述该如何进行以太坊合约开发环境的搭建,以及编写第一个以太坊上的智能合约。

环境准备

进行以太坊智能合约的开发需要我们有一个以太坊网络的环境,我建议使用自己搭建的私链进行测试,在自己的私链上,以太币的获取比较容易,对于以太坊私链的的搭建可以参考我的另一篇博文《【Ethereum基础实践】:以太坊测试私链的搭建》

Remix的使用

Remix是以太坊官方推荐使用的IDE,在Remix上我们可以进行合约的编写,并且还可以对以太坊环境进行集成,进行智能合约的部署与调试。

Remix有Web版本和Electron APP两个版本:

你可以选择Web版或者Electron版本,它们的功能没有任何差别。

安装Remixd服务

由于Remix提供的是一个在线的Web环境,因此在本地不会存储你所编写的智能合约源文件,如果你希望文件能一直保存在本地,你可以安装Remixd服务。

1
npm install -g remixd

启动remixd:

1
2
# 使用绝对路径
remixd -s /Users/username/path_you_store_contracts

点击Remix IDE左上方按钮连接至remixd服务:

这样就可以在本地编写智能合约了:

连接到以太坊节点

在Remix IDE中,我们可以连接我们自己的以太坊节点进行测试,在控制面板的Run页面中可以配置自定义的以太坊节点,在Environment中选择Web3 Provider:

输入节点对应的IP地址和RPC端口即可进行连接:

在连接节点之后,我们可以看到私链中的各个账户的余额等信息:

这样我们就可以使用自己的以太坊私链进行智能合约的部署与测试了。

一个简单的智能合约

接下来我们会编写和部署一个简单的智能合约来演示一下整个流程。

先来看一下合约的源文件代码:

1
2
3
4
5
6
7
8
9
10
11
contract Demo {
uint256 public number = 0;

function Demo(uint8 _initial) {
number = _initial;
}

function add() {
number = number +1;
}
}

这个合约的功能是在合约中存储了一个数字number,并且提供了一个对该数字进行+1的方法add().

我们先对源代码进行编译:

之后在Run面板点击Create进行部署:

由于该合约的构造方法中需要一个参数,来对number进行初始化,这里我填的是10,另外,在Account中需要选择部署该合约的账户(需要消耗gas),并且输入对应的密码对账户进行解锁:

待创建合约的交易广播至矿工节点并且被打包至区块中之后,我们就可以在面板中看到相关的合约信息了:

点击number可以查看当前的值:

点击add可以触发对应方法(该操作是一个transaction,需要消耗gas),number的值会进行+1:

我们在控制台中可以看到该交易的详细信息:

至此,我们就完成了一个简单的智能合约的部署。

部署一个ERC20的合约

在这部分,我们会以以太坊上的EOS代币合约为例,进行ERC20代币的合约部署与测试。

首先,我们要获取EOS智能合约的源代码,可以在etherscan上获取对应源码:

https://etherscan.io/address/0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0#code

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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
contract DSNote {
event LogNote(
bytes4 indexed sig,
address indexed guy,
bytes32 indexed foo,
bytes32 indexed bar,
uint wad,
bytes fax
) anonymous;

modifier note {
bytes32 foo;
bytes32 bar;

assembly {
foo := calldataload(4)
bar := calldataload(36)
}

LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data);

_;
}
}

contract DSAuthority {
function canCall(
address src, address dst, bytes4 sig
) constant returns (bool);
}

contract DSAuthEvents {
event LogSetAuthority (address indexed authority);
event LogSetOwner (address indexed owner);
}

contract DSAuth is DSAuthEvents {
DSAuthority public authority;
address public owner;

function DSAuth() {
owner = msg.sender;
LogSetOwner(msg.sender);
}

function setOwner(address owner_)
auth
{
owner = owner_;
LogSetOwner(owner);
}

function setAuthority(DSAuthority authority_)
auth
{
authority = authority_;
LogSetAuthority(authority);
}

modifier auth {
assert(isAuthorized(msg.sender, msg.sig));
_;
}

modifier authorized(bytes4 sig) {
assert(isAuthorized(msg.sender, sig));
_;
}

function isAuthorized(address src, bytes4 sig) internal returns (bool) {
if (src == address(this)) {
return true;
} else if (src == owner) {
return true;
} else if (authority == DSAuthority(0)) {
return false;
} else {
return authority.canCall(src, this, sig);
}
}

function assert(bool x) internal {
if (!x) throw;
}
}

contract DSStop is DSAuth, DSNote {

bool public stopped;

modifier stoppable {
assert (!stopped);
_;
}
function stop() auth note {
stopped = true;
}
function start() auth note {
stopped = false;
}

}

contract DSMath {

/*
standard uint256 functions
*/

function add(uint256 x, uint256 y) constant internal returns (uint256 z) {
assert((z = x + y) >= x);
}

function sub(uint256 x, uint256 y) constant internal returns (uint256 z) {
assert((z = x - y) <= x);
}

function mul(uint256 x, uint256 y) constant internal returns (uint256 z) {
assert((z = x * y) >= x);
}

function div(uint256 x, uint256 y) constant internal returns (uint256 z) {
z = x / y;
}

function min(uint256 x, uint256 y) constant internal returns (uint256 z) {
return x <= y ? x : y;
}
function max(uint256 x, uint256 y) constant internal returns (uint256 z) {
return x >= y ? x : y;
}

/*
uint128 functions (h is for half)
*/


function hadd(uint128 x, uint128 y) constant internal returns (uint128 z) {
assert((z = x + y) >= x);
}

function hsub(uint128 x, uint128 y) constant internal returns (uint128 z) {
assert((z = x - y) <= x);
}

function hmul(uint128 x, uint128 y) constant internal returns (uint128 z) {
assert((z = x * y) >= x);
}

function hdiv(uint128 x, uint128 y) constant internal returns (uint128 z) {
z = x / y;
}

function hmin(uint128 x, uint128 y) constant internal returns (uint128 z) {
return x <= y ? x : y;
}
function hmax(uint128 x, uint128 y) constant internal returns (uint128 z) {
return x >= y ? x : y;
}


/*
int256 functions
*/

function imin(int256 x, int256 y) constant internal returns (int256 z) {
return x <= y ? x : y;
}
function imax(int256 x, int256 y) constant internal returns (int256 z) {
return x >= y ? x : y;
}

/*
WAD math
*/

uint128 constant WAD = 10 ** 18;

function wadd(uint128 x, uint128 y) constant internal returns (uint128) {
return hadd(x, y);
}

function wsub(uint128 x, uint128 y) constant internal returns (uint128) {
return hsub(x, y);
}

function wmul(uint128 x, uint128 y) constant internal returns (uint128 z) {
z = cast((uint256(x) * y + WAD / 2) / WAD);
}

function wdiv(uint128 x, uint128 y) constant internal returns (uint128 z) {
z = cast((uint256(x) * WAD + y / 2) / y);
}

function wmin(uint128 x, uint128 y) constant internal returns (uint128) {
return hmin(x, y);
}
function wmax(uint128 x, uint128 y) constant internal returns (uint128) {
return hmax(x, y);
}

/*
RAY math
*/

uint128 constant RAY = 10 ** 27;

function radd(uint128 x, uint128 y) constant internal returns (uint128) {
return hadd(x, y);
}

function rsub(uint128 x, uint128 y) constant internal returns (uint128) {
return hsub(x, y);
}

function rmul(uint128 x, uint128 y) constant internal returns (uint128 z) {
z = cast((uint256(x) * y + RAY / 2) / RAY);
}

function rdiv(uint128 x, uint128 y) constant internal returns (uint128 z) {
z = cast((uint256(x) * RAY + y / 2) / y);
}

function rpow(uint128 x, uint64 n) constant internal returns (uint128 z) {
// This famous algorithm is called "exponentiation by squaring"
// and calculates x^n with x as fixed-point and n as regular unsigned.
//
// It's O(log n), instead of O(n) for naive repeated multiplication.
//
// These facts are why it works:
//
// If n is even, then x^n = (x^2)^(n/2).
// If n is odd, then x^n = x * x^(n-1),
// and applying the equation for even x gives
// x^n = x * (x^2)^((n-1) / 2).
//
// Also, EVM division is flooring and
// floor[(n-1) / 2] = floor[n / 2].

z = n % 2 != 0 ? x : RAY;

for (n /= 2; n != 0; n /= 2) {
x = rmul(x, x);

if (n % 2 != 0) {
z = rmul(z, x);
}
}
}

function rmin(uint128 x, uint128 y) constant internal returns (uint128) {
return hmin(x, y);
}
function rmax(uint128 x, uint128 y) constant internal returns (uint128) {
return hmax(x, y);
}

function cast(uint256 x) constant internal returns (uint128 z) {
assert((z = uint128(x)) == x);
}

}

contract ERC20 {
function totalSupply() constant returns (uint supply);
function balanceOf( address who ) constant returns (uint value);
function allowance( address owner, address spender ) constant returns (uint _allowance);

function transfer( address to, uint value) returns (bool ok);
function transferFrom( address from, address to, uint value) returns (bool ok);
function approve( address spender, uint value ) returns (bool ok);

event Transfer( address indexed from, address indexed to, uint value);
event Approval( address indexed owner, address indexed spender, uint value);
}

contract DSTokenBase is ERC20, DSMath {
uint256 _supply;
mapping (address => uint256) _balances;
mapping (address => mapping (address => uint256)) _approvals;

function DSTokenBase(uint256 supply) {
_balances[msg.sender] = supply;
_supply = supply;
}

function totalSupply() constant returns (uint256) {
return _supply;
}
function balanceOf(address src) constant returns (uint256) {
return _balances[src];
}
function allowance(address src, address guy) constant returns (uint256) {
return _approvals[src][guy];
}

function transfer(address dst, uint wad) returns (bool) {
assert(_balances[msg.sender] >= wad);

_balances[msg.sender] = sub(_balances[msg.sender], wad);
_balances[dst] = add(_balances[dst], wad);

Transfer(msg.sender, dst, wad);

return true;
}

function transferFrom(address src, address dst, uint wad) returns (bool) {
assert(_balances[src] >= wad);
assert(_approvals[src][msg.sender] >= wad);

_approvals[src][msg.sender] = sub(_approvals[src][msg.sender], wad);
_balances[src] = sub(_balances[src], wad);
_balances[dst] = add(_balances[dst], wad);

Transfer(src, dst, wad);

return true;
}

function approve(address guy, uint256 wad) returns (bool) {
_approvals[msg.sender][guy] = wad;

Approval(msg.sender, guy, wad);

return true;
}

}

contract DSToken is DSTokenBase(0), DSStop {

bytes32 public symbol;
uint256 public decimals = 18; // standard token precision. override to customize

function DSToken(bytes32 symbol_) {
symbol = symbol_;
}

function transfer(address dst, uint wad) stoppable note returns (bool) {
return super.transfer(dst, wad);
}
function transferFrom(
address src, address dst, uint wad
) stoppable note returns (bool) {
return super.transferFrom(src, dst, wad);
}
function approve(address guy, uint wad) stoppable note returns (bool) {
return super.approve(guy, wad);
}

function push(address dst, uint128 wad) returns (bool) {
return transfer(dst, wad);
}
function pull(address src, uint128 wad) returns (bool) {
return transferFrom(src, msg.sender, wad);
}

function mint(uint128 wad) auth stoppable note {
_balances[msg.sender] = add(_balances[msg.sender], wad);
_supply = add(_supply, wad);
}
function burn(uint128 wad) auth stoppable note {
_balances[msg.sender] = sub(_balances[msg.sender], wad);
_supply = sub(_supply, wad);
}

// Optional token name

bytes32 public name = "";

function setName(bytes32 name_) auth {
name = name_;
}

}

使用上述相同的部署方法,我们就可以看到EOS合约中提供的相关方法,并且开始测试了:


本文的版权归作者 罗远航 所有,采用 Attribution-NonCommercial 3.0 License。任何人可以进行转载、分享,但不可在未经允许的情况下用于商业用途;转载请注明出处。感谢配合!