个人简介

[HundredLee]

  • 2013年创业,开始从事数字货币开发工作,区块链开发工程师、iOS&Web开发者。目前担任国内知名数字货币交易平台开发商CTO。四年的技术沉淀,客户已遍布国内外,并有自主研发的JAVA高速撮合引擎。
  • 微博 :http://weibo.com/hundredlee2011
  • 邮箱 :hundred9411#gmail.com

什么是代币?

2017年区块链火爆了整个互联网金融圈,特别是ico的出现,让很多人开始认识了数字货币。我们知道以太坊(Ethereum)是目前最受欢迎的数字货币和区块链系统,而eth每个已经达到了2000+RMB。我记得刚认识以太坊的时候,是三十多一枚。

  • 那么什么是代币呢?在以太坊区块链中,我们称代币为Token,是以太坊区块链中每个人都可以任意发行的数字资产。并且它必须是遵循erc20标准的,至于erc20标准,大家可以参考这篇文章 https://theethereum.wiki/w/index.php/ERC20_Token_Standard
  • 它实际上一段智能合约代码,智能合约代码中必须要有以下的一些function 和 event。
1
2
3
4
5
6
7
8
9
10
contract ERC20 {
function totalSupply() constant returns (uint totalSupply);
function balanceOf(address _owner) constant returns (uint balance);
function transfer(address _to, uint _value) returns (bool success);
function transferFrom(address _from, address _to, uint _value) returns (bool success);
function approve(address _spender, uint _value) returns (bool success);
function allowance(address _owner, address _spender) constant returns (uint remaining);
event Transfer(address indexed _from, address indexed _to, uint _value);
event Approval(address indexed _owner, address indexed _spender, uint _value);
}
  • 智能合约代码是运行在以太坊智能合约虚拟机中的。有兴趣的同学,可以学习一下。附上文档:https://solidity.readthedocs.io/en/latest/installing-solidity.html#building-from-source

  • 我们看到上面那段类似golang中interface的代码,里面分别包含了总量、余额、转账等方法。我们今天重点讲的其实就是用golang来实现transfer、transferFrom方法。

  • 下面进入主题。

连接以太坊RPC节点

  • 目前广泛使用的是go-ethereum,他的客户端名是geth。你可以通过编译、安装等方式把节点搭建在你的电脑或者服务器中,并开启rpc服务。本文省略这一步骤,网上有很文章供你了解。
  • 附上github:https://github.com/ethereum/go-ethereum
  • geth默认的rpc端口是8545,我使用默认端口,后面我们都用http://127.0.0.1:8545作为我们的rpc连接。

首先获取go-ethereum代码

  • go get github.com/ethereum/go-ethereum
  • 然后我们go-ethereum目录,如果你的golang环境没有问题,那么应该是这个路径。
  • cd $GOPATH/src/github.com/ethereum/go-ethereum
  • 当你进入目录,看到代码已经完整拉取下来,那么我们就可以进行下一步了。

连接RPC节点

  • Dont bb..我们直接上代码。
1
2
3
4
5
6
7
8
9
10
11
import (
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/ethclient"
)
rpcDial, err := rpc.Dial("http://127.0.0.1:8545")
if err != nil {
panic(err);
}
client := ethclient.NewClient(rpcDial)
  • 如果没有panic,那么我们已经成功连接了节点。(^__^)

创建测试账户

  • 要进行转账测试,那么我们需要两个以太坊账户。我们用golang来生成,我们知道以太坊的账户私钥是放在keystore文件中的,是一段json,并且创建的时候可以设置密码。跟比特币的wallet.dat文件是一样的意思,不见哪一样,你的资产就永远留在区块链网络中,再也无法找回。
  • 下面我们用代码创建两个以太坊账户。
1
2
3
4
5
6
7
8
9
10
11
12
13
import (
"github.com/ethereum/go-ethereum/accounts/keystore"
)
ks := keystore.NewKeyStore("/", keystore.StandardScryptN, keystore.StandardScryptP)
address, _ := ks.NewAccount("password")
account, err := ks.Export(address, "password", "password")
if err != nil {
panic(err)
}
  • 从上面的代码我们可以看到,我创建了一个以太坊的账户,并且密码设置为password,并导出。最终account变量就是账户的私钥,是一段json文本。我们可以看看它大概长什么样。
  • 通过address变量,我们可以获得账户的地址。比如这样 address.Address.Hex()
1
2
3
4
5
6
7
8
9
10
11
12
13
{"address":"41e2f6a4eb0e61f627207ec4a3f7098388174368","crypto":{"cipher":"aes-128-ctr","ciphertext"
:"c0b10f9a3ca83837de83d38ca95bef200170d97e818f15bbc35642b6076c4a16",
"cipherparams":{"iv":"e46c3f2c76b35eedd7b065677faf6927"},
"kdf":"scrypt",
"kdfparams":
{"dklen":32,"n":262144,"p":1,"r":8,"salt"
:"05c8493a6a8518451c18ac05785e6c60507d906b130ee859e99804f0df90b63d"},
"mac"
:"62f52d9c4a078765b496cf76ed50634199c509e2d6e91106e24276d51124b971"}
,"id"
:"6c00339c-773b-4862-bb61-3a51a6f671ee",
"version":3}
  • 反正就是这么一段json,一定要妥善保存。

生成代币文件

  • 打开 cd $GOPATH/src/github.com/ethereum/go-ethereum/cmd/abigen 你能看到main.go文件
  • 执行 go build main.go,会在目录下生成一个main的二进制文件。

  • 将以下的json 保存为token.abi,并放在当前目录下。

1
2
[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"stop","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint128"}],"name":"push","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"name_","type":"bytes32"}],"name":"setName","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint128"}],"name":"mint","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"src","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"stopped","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"authority_","type":"address"}],"name":"setAuthority","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"wad","type":"uint128"}],"name":"pull","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint128"}],"name":"burn","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"start","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"authority","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"src","type":"address"},{"name":"guy","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"symbol_","type":"bytes32"}],"payable":false,"type":"constructor"},{"anonymous":true,"inputs":[{"indexed":true,"name":"sig","type":"bytes4"},{"indexed":true,"name":"guy","type":"address"},{"indexed":true,"name":"foo","type":"bytes32"},{"indexed":true,"name":"bar","type":"bytes32"},{"indexed":false,"name":"wad","type":"uint256"},{"indexed":false,"name":"fax","type":"bytes"}],"name":"LogNote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"authority","type":"address"}],"name":"LogSetAuthority","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"}],"name":"LogSetOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"}]
  • 执行命令 ./main --abi token.abi --pkg main --type Token --out token.go

  • 我们可以看到目录下生成了一个token.go文件。大功告成。

开始转账

  • 首先就把上一步生成的token.go拖入项目中。
  • 接下来的叙述,我写在代码的注释里
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
import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
)
//首先导入上面生成的账户密钥(json)和密码
auth, err := bind.NewTransactor(strings.NewReader("json"), "password")
//这句用的是生成的token.go里面的方法
//client变量是我们第一步连接以太坊rpc节点的时候创建的
//contractAddress 是代币地址,比如eos 的地址是0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0
//那么我们转账针对的就是账户里的eos代币
//具体看这里 https://etherscan.io/token/0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0
token, err := NewToken(common.HexToAddress("0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0"), client)
if err != nil {
panic(err)
}
//每个代币都会有相应的位数,例如eos是18位,那么我们转账的时候,需要在金额后面加18个0
decimal, err := token.Decimals(nil)
if err != nil {
panic(err)
}
//这是处理位数的代码段
tenDecimal := big.NewFloat(math.Pow(10, float64(decimal)))
convertAmount, _ := new(big.Float).Mul(tenDecimal, amount).Int(&big.Int{})
//然后就可以转账到你需要接受的账户上了
//toAddress 是接受eos的账户地址
txs, err := token.Transfer(auth, common.HexToAddress(toAddress), convertAmount)
  • 由此,转账就大功告成了。另外,token.go里面还有很多其他的方法,有兴趣的同学,可以自行研究。

本文结束

  • 我的以太坊地址 0x67f883a42031215622e0b84c96d0E4DcA7A3ce81
  • 哈哈哈,支持eos、omg、tnt等代币的捐赠,请我喝杯星巴克。
  • 当然,eth也是不会拒接的。或者你可以扫码,下面是我的以太坊收款二维码。谢谢哦。

-w265