使用 在线环境

编写第一个智能合约

在这个合约代码里面,我们定义了一个名为 “Hello” 的合约。在合约初始化时,保存了一个字符串。例如,传入 “hello world”,每次调用 say() 函数时将返回该字符串

1
2
3
4
5
6
7
8
9
10
11
12
pragma solidity ^0.4.20;
contract hello{
string greeting;

function hello(string _greeting) public{
greeting = _greeting;
}

function say() constant public returns (string){
return greeting;
}
}

选择这个版本是 0.4.21+commit,有些是不成功的

image.png
image.png

点击那个 Details,来到 WEB3DEPLOY

image.png
image.png

把第一行的 var_greeting = /* var of type string her e*/;
改成 var_greeting = "hello world";
第 6 行的 from: web3.eth.account[0]
改为 from: web3.eth.account[2] 修改部署账户

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
var _greeting = "hello world";
var helloContract = web3.eth.contract([
{
constant: true,
inputs: [],
name: "say",
outputs: [{ name: "", type: "string" }],
payable: false,
stateMutability: "view",
type: "function",
},
{
inputs: [{ name: "_greeting", type: "string" }],
payable: false,
stateMutability: "nonpayable",
type: "constructor",
},
]);
var hello = helloContract.new(
_greeting,
{
from: web3.eth.accounts[2],
data: "0x6060604052341561000f57600080fd5b6040516102a33803806102a3833981016040528080519091019050600081805161003d929160200190610044565b50506100df565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061008557805160ff19168380011785556100b2565b828001600101855582156100b2579182015b828111156100b2578251825591602001919060010190610097565b506100be9291506100c2565b5090565b6100dc91905b808211156100be57600081556001016100c8565b90565b6101b5806100ee6000396000f3006060604052600436106100405763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663954ab4b28114610045575b600080fd5b341561005057600080fd5b6100586100cf565b60405160208082528190810183818151815260200191508051906020019080838360005b8381101561009457808201518382015260200161007c565b50505050905090810190601f1680156100c15780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100d7610177565b60008054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561016d5780601f106101425761010080835404028352916020019161016d565b820191906000526020600020905b81548152906001019060200180831161015057829003601f168201915b5050505050905090565b602060405190810160405260008152905600a165627a7a7230582016579f8b662064fd3fdbd4215682d7ebefe7998115729b69b443d5ad1572b2a70029",
gas: "4700000",
},
function (e, contract) {
console.log(e, contract);
if (typeof contract.address !== "undefined") {
console.log(
"Contract mined! address: " +
contract.address +
" transactionHash: " +
contract.transactionHash
);
}
}
);

先看一下现在的账户

image.png
image.png

将上面这一段复制到 geth 的控制台(先开始挖矿 ⛏)
如果提示这样,是因为 u2 没有解锁账户

image.png
image.png

解锁
personal.unlockAccount(u2,'123456')

image.png
image.png

部署成功

image.png
image.png

看一下,u2 的账户余额变少了,部署合约用掉了

image.png
image.png

运行合约,调用 hello.say(),可以看到输出了 hello world

image.png
image.png

ERC-20 Token 合约

什么是 Token

以太坊的一个重要的应用就是融资。融资的过程,和股票操作类似,合约的创建者就是上市公司,账户余额就是持有的股份。账户余额表示一个由合约创建者定义的值,Token 就是余额的单位(也可以理解为一种凭证)

Token 合约

在之前,我们使用账户地址进行转账之类的操作,在引入智能合约以后,从本质上看,Token 合约是指包含对账户地址及其余额的映射的智能合约

比如一开始有这样之一张表,他就是账户与余额之间的映射

账户地址(Holder Address) 余额(Balance)
0x0000…0000 0
0x32rd….w3ed 100
0x2ejs….we3c 100
Total Supply 200

当 Token 被从一个账户转移到另一个账户的时候
比如一笔从 0x32rd….w3ed 转 10 个 Token 到 0x2ejs….we3c 的时候,将发生这样的变化

账户地址(Holder Address) 余额(Balance)
0x0000…0000 0
0x32rd….w3ed 90
0x2ejs….we3c 110
Total Supply 200

如果该 Token 允许,我们可以更改 Token 的总供给
可以通过构造新的 Token 来增加,构造 50 个 Token 并放到 0x2ejs….we3c 中,将导致如下变化:

账户地址(Holder Address) 余额(Balance)
0x0000…0000 0
0x32rd….w3ed 90
0x2ejs….we3c 160
Total Supply 250

也可以减少 Token 的总供给,可以通过销毁现有的 Token 来减少,在地址 0x2ejs….we3c 中销毁 10 个 Token 将导致如下变化:

账户地址(Holder Address) 余额(Balance)
0x0000…0000 0
0x32rd….w3ed 90
0x2ejs….we3c 150
Total Supply 240

另一种销毁的方式是往一个未创建私钥的地址发送 Token,通常是 0 地址,但这种方法不会减少 Token 的总数

账户地址(Holder Address) 余额(Balance)
0x0000…0000 40
0x32rd….w3ed 90
0x2ejs….we3c 110
Total Supply 240

ETC-20 Token 合约

可以简单的理解为以太坊上的一个代币协议,所有基于以太坊开发的代币都要遵循此协议。遵守协议的代币是标准化的代币,可以得到各种以太坊钱包的支持,用在不同的平台和项目中

ERC-20 标准接口

name():返回 ERC-20 代币的名字(string 类型)例如:”my_Token”
function name() constant returns (string name)

symbol():返回代币的简称,例如:”TNT”,简称通常三四个字母
function symbol() constant returns (string symbol)

decimals():返回 Token 使用的小数位数,例如设置为 3,表示支持三位小数
function decimals() constant returns (uint8 decimals)

补充:确定要取的小数位数时,应遵循如下原则:

  • Token 合约表示的是一个不可切分的物体嘛?如果是,就将小数设置为 0
  • Token 合约表示的是一个有特定小数位数的物体吗?如果是就将小数设置为那个数字
  • 如果上述两者都不是,将小数位数设置为 18

被创建的 Token 的数量,应该等于全部 Token 的数量(要求是 10 的倍数)

totalSupply():返回 Token 的总供给量
function totalSupply() constant returns (uint totalSupply)

balanceOf():返回某个账户地址余额,给个地址就可以查到余额,所有数据在区块链上是公开的
function balanceOf(address _owner) constant returns (uint balance)

transfer():从代币合约的调用者地址上转移 _value 数量的 Token 到地址 _to,且必须触发 transfer 事件
function transfer(address _to, uint _value) returns (bool success)
当使用 Token 对智能合约中的一个函数进行支付的时候他就不起作用了。因为智能合约运行的时候,他没有办法获得“从那个地址转账到哪里”的细节,也就无法保证合约用户已经支付了启动合约的资金

想象一下,有一个 hello 合约被部署在网络上,该合约有一个函数 sayHello(),该函数要 10 个 Token 才能运行起来,账户 A 已经有 20 个 Token,他希望调用这个函数,他该怎么把足够的 Token 支付给 Hello 合约呐?

这时就需要下面这里俩了:
transferFrom():从地址 _from 发送数量为 _value 的 Token 到地址 _to,且必须触发 transfer 事件。该方法用于允许合约代理某人转移 Token,条件是 from 账户必须是经过 approve()
function transferFrom(address _from,address _to,uint _value) returns (bool success)

approve():允许 _spender 多次取回账户余额,最高金额设置为 value,如果此次调用此函数,将以 _value 覆盖当前金额
function approve(address _spender,uint _value) returns (bool success)

当持有者向一个地址(常常是智能合约)转移最大特定数量的 Token,也就是所谓的 “配额”时,Token 持有者使用 approve() 来提供这些信息

下面这个表括号里的内容是我加上的方便称呼

账户地址(Holder Address) 配额地址(Allowence Address) 配额(Allowence)
0x1234….(A) 0xabcd….(X) 25
0x1234….(A) 0xdcba….(Y) 15
0x4321….(B) 0xabcd….(X) 200
0x4321….(B) 0xdcba….(Y) 5

A 可以向 Y 合约转移 15 个 Token,即配额为 15 个 Token
B 可以向 Y 合约转移 5 个 Token,即配额为 5 个 Token

一旦一个许可被创建,合约就可以在一个账户的配额中占用许可数量的 Token,作为合约的一部分运行,账户 A 可以调用 sayHello(),而 sayHello() 可以使用 transferFrom() 从账户 A 的余额中获得 10 个 Token 并开始他的工作,如果余额不足 10 或配额不足 10,sayHello() 就会停止运行。所以 B 就不能调用,以为 B 配额为 5

allowance():返回 _spender 仍然被允许从 _owner 提取的金额,函数用于指定从一个给定地址转移到另一个给定地址的 Token 数量
function allowance(address _owner,address _spender) constant returns (uint remaining)
配额可以超过一个地址的余额,所以使用 allowance() 函数的时候要考虑用户余额问题

transfer() 和 approval 属于事件(Event)是为了方便获取日志提供的
前者在代币被转移时触发,用于描述 Token 从一个地址转移到另一个地址的细节
后者在调用 approve() 方法时触发,用于描述 Token 被允许从一个地址转移到另一个地址的细节
他们都可以用于跟踪地址余额和配额的变化,无需查询区块链

在 Token 构造时,会发出一个带有 0 地址的 transfer 事件作为源
被销毁时没有事件发出,ERC-20 Token 合约通过 transfer() 将 Token 发送到 0 地址来代替真正的“销毁”Token