用Golang實現以太坊代幣轉賬

HundredLee發表於2017-11-05

個人簡介

[HundredLee]

  • 2013年創業,開始從事數字貨幣開發工作,區塊鏈開發工程師、iOS&Web開發者。目前擔任國內知名數字貨幣交易平臺開發商CTO。四年的技術沉澱,客戶已遍佈國內外,並有自主研發的JAVA高速撮合引擎。
  • 微博 :weibo.com/hundredlee2…
  • 郵箱 :hundred9411#gmail.com
  • Blog : blog.sodroid.com

什麼是代幣?

2017年區塊鏈火爆了整個網際網路金融圈,特別是ico的出現,讓很多人開始認識了數字貨幣。我們知道以太坊(Ethereum)是目前最受歡迎的數字貨幣和區塊鏈系統,而eth每個已經達到了2000+RMB。我記得剛認識以太坊的時候,是三十多一枚。

  • 那麼什麼是代幣呢?在以太坊區塊鏈中,我們稱代幣為Token,是以太坊區塊鏈中每個人都可以任意發行的數字資產。並且它必須是遵循erc20標準的,至於erc20標準,大家可以參考這篇文章 theethereum.wiki/w/index.php…
  • 它實際上一段智慧合約程式碼,智慧合約程式碼中必須要有以下的一些function 和 event。
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);
 }複製程式碼
  • 智慧合約程式碼是執行在以太坊智慧合約虛擬機器中的。有興趣的同學,可以學習一下。附上文件:solidity.readthedocs.io/en/latest/i…

  • 我們看到上面那段類似golang中interface的程式碼,裡面分別包含了總量、餘額、轉賬等方法。我們今天重點講的其實就是用golang來實現transfer、transferFrom方法。

  • 下面進入主題。

連線以太坊RPC節點

  • 目前廣泛使用的是go-ethereum,他的客戶端名是geth。你可以通過編譯、安裝等方式把節點搭建在你的電腦或者伺服器中,並開啟rpc服務。本文省略這一步驟,網上有很文章供你瞭解。
  • 附上github:github.com/ethereum/go…
  • 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..我們直接上程式碼。

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檔案是一樣的意思,不見哪一樣,你的資產就永遠留在區塊鏈網路中,再也無法找回。
  • 下面我們用程式碼建立兩個以太坊賬戶。

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()

{"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,並放在當前目錄下。


[{"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拖入專案中。
  • 接下來的敘述,我寫在程式碼的註釋裡
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
-w265

相關文章