智能合約
在這個(gè)章節(jié)中我們會(huì)介紹如何用Go來編譯慎框,部署路狮,寫入和讀取智能合約莱衩。
智能合約的編譯與ABI
與智能合約交互悴晰,我們要先生成相應(yīng)智能合約的應(yīng)用二進(jìn)制接口ABI(application binary interface),并把ABI編譯成我們可以在Go應(yīng)用中調(diào)用的格式节榜。
第一步是安裝 Solidity編譯器 (solc
).
Solc 在Ubuntu上有snapcraft包羡玛。
sudo snap install solc --edge
Solc在macOS上有Homebrew的包别智。
brew update
brew tap ethereum/ethereum
brew install solidity
其他的平臺(tái)或者從源碼編譯的教程請(qǐng)查閱官方solidity文檔install guide.
我們還得安裝一個(gè)叫abigen
的工具宗苍,來從solidity智能合約生成ABI。
假設(shè)您已經(jīng)在計(jì)算機(jī)上設(shè)置了Go薄榛,只需運(yùn)行以下命令即可安裝abigen
工具讳窟。
go get -u github.com/ethereum/go-ethereum
cd $GOPATH/src/github.com/ethereum/go-ethereum/
make
make devtools
我們將創(chuàng)建一個(gè)簡(jiǎn)單的智能合約來測(cè)試。 學(xué)習(xí)更復(fù)雜的智能合約敞恋,或者智能合約的開發(fā)的內(nèi)容則超出了本書的范圍丽啡。 我強(qiáng)烈建議您查看truffle framework 來學(xué)習(xí)開發(fā)和測(cè)試智能合約。
這里只是一個(gè)簡(jiǎn)單的合約硬猫,就是一個(gè)鍵/值存儲(chǔ)补箍,只有一個(gè)外部方法來設(shè)置任何人的鍵/值對(duì)改执。 我們還在設(shè)置值后添加了要發(fā)出的事件。
pragma solidity ^0.4.24;
contract Store {
event ItemSet(bytes32 key, bytes32 value);
string public version;
mapping (bytes32 => bytes32) public items;
constructor(string _version) public {
version = _version;
}
function setItem(bytes32 key, bytes32 value) external {
items[key] = value;
emit ItemSet(key, value);
}
}
雖然這個(gè)智能合約很簡(jiǎn)單坑雅,但它將適用于這個(gè)例子辈挂。
現(xiàn)在我們可以從一個(gè)solidity文件生成ABI。
solc --abi Store.sol
它會(huì)將其寫入名為“Store_sol_Store.abi”的文件中
現(xiàn)在讓我們用abigen
將ABI轉(zhuǎn)換為我們可以導(dǎo)入的Go文件裹粤。 這個(gè)新文件將包含我們可以用來與Go應(yīng)用程序中的智能合約進(jìn)行交互的所有可用方法终蒂。
abigen --abi=Store_sol_Store.abi --pkg=store --out=Store.go
為了從Go部署智能合約,我們還需要將solidity智能合約編譯為EVM字節(jié)碼遥诉。 EVM字節(jié)碼將在事務(wù)的數(shù)據(jù)字段中發(fā)送拇泣。 在Go文件上生成部署方法需要bin文件。
solc --bin Store.sol
現(xiàn)在我們編譯Go合約文件矮锈,其中包括deploy方法霉翔,因?yàn)槲覀儼薭in文件。
abigen --bin=Store_sol_Store.bin --abi=Store_sol_Store.abi --pkg=store --out=Store.go
在接下來的課程中苞笨,我們將學(xué)習(xí)如何部署智能合約早龟,然后與之交互。
完整代碼
Commands
go get -u github.com/ethereum/go-ethereum
cd $GOPATH/src/github.com/ethereum/go-ethereum/
make
make devtools
solc --abi Store.sol
solc --bin Store.sol
abigen --bin=Store_sol_Store.bin --abi=Store_sol_Store.abi --pkg=store --out=Store.go
pragma solidity ^0.4.24;
contract Store {
event ItemSet(bytes32 key, bytes32 value);
string public version;
mapping (bytes32 => bytes32) public items;
constructor(string _version) public {
version = _version;
}
function setItem(bytes32 key, bytes32 value) external {
items[key] = value;
emit ItemSet(key, value);
}
}
solc version used for these examples
$ solc --version
0.4.24+commit.e67f0147.Emscripten.clang
部署智能合約
如果你還沒看之前的章節(jié)猫缭,請(qǐng)先學(xué)習(xí)編譯智能合約的章節(jié)因?yàn)檫@節(jié)內(nèi)容葱弟,需要先了解如何將智能合約編譯為Go文件。
假設(shè)你已經(jīng)導(dǎo)入從abigen
生成的新創(chuàng)建的Go包文件猜丹,并設(shè)置ethclient芝加,加載您的私鑰,下一步是創(chuàng)建一個(gè)有配置密匙的交易發(fā)送器(tansactor)射窒。 首先從go-ethereum導(dǎo)入accounts/abi/bind
包藏杖,然后調(diào)用傳入私鑰的NewKeyedTransactor
。 然后設(shè)置通常的屬性脉顿,如nonce蝌麸,燃?xì)鈨r(jià)格,燃?xì)馍暇€限制和ETH值艾疟。
auth := bind.NewKeyedTransactor(privateKey)
auth.Nonce = big.NewInt(int64(nonce))
auth.Value = big.NewInt(0) // in wei
auth.GasLimit = uint64(300000) // in units
auth.GasPrice = gasPrice
如果你還記得上個(gè)章節(jié)的內(nèi)容, 我們創(chuàng)建了一個(gè)非常簡(jiǎn)單的“Store”合約来吩,用于設(shè)置和存儲(chǔ)鍵/值對(duì)。 生成的Go合約文件提供了部署方法蔽莱。 部署方法名稱始終以單詞Deploy開頭弟疆,后跟合約名稱,在本例中為Store盗冷。
deploy函數(shù)接受有密匙的事務(wù)處理器怠苔,ethclient,以及智能合約構(gòu)造函數(shù)可能接受的任何輸入?yún)?shù)仪糖。我們測(cè)試的智能合約接受一個(gè)版本號(hào)的字符串參數(shù)柑司。 此函數(shù)將返回新部署的合約地址迫肖,事務(wù)對(duì)象,我們可以交互的合約實(shí)例攒驰,還有錯(cuò)誤(如果有)咒程。
input := "1.0"
address, tx, instance, err := store.DeployStore(auth, client, input)
if err != nil {
log.Fatal(err)
}
fmt.Println(address.Hex()) // 0x147B8eb97fD247D06C4006D269c90C1908Fb5D54
fmt.Println(tx.Hash().Hex()) // 0xdae8ba5444eefdc99f4d45cd0c4f24056cba6a02cefbf78066ef9f4188ff7dc0
_ = instance // will be using the instance in the 下個(gè)章節(jié)
就這么簡(jiǎn)單:)你可以用事務(wù)哈希來在Etherscan上查詢合約的部署狀態(tài): https://rinkeby.etherscan.io/tx/0xdae8ba5444eefdc99f4d45cd0c4f24056cba6a02cefbf78066ef9f4188ff7dc0
完整代碼
Commands
solc --abi Store.sol
solc --bin Store.sol
abigen --bin=Store_sol_Store.bin --abi=Store_sol_Store.abi --pkg=store --out=Store.go
pragma solidity ^0.4.24;
contract Store {
event ItemSet(bytes32 key, bytes32 value);
string public version;
mapping (bytes32 => bytes32) public items;
constructor(string _version) public {
version = _version;
}
function setItem(bytes32 key, bytes32 value) external {
items[key] = value;
emit ItemSet(key, value);
}
}
package main
import (
"context"
"crypto/ecdsa"
"fmt"
"log"
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
store "./contracts" // for demo
)
func main() {
client, err := ethclient.Dial("https://rinkeby.infura.io")
if err != nil {
log.Fatal(err)
}
privateKey, err := crypto.HexToECDSA("fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19")
if err != nil {
log.Fatal(err)
}
publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
log.Fatal("error casting public key to ECDSA")
}
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
if err != nil {
log.Fatal(err)
}
gasPrice, err := client.SuggestGasPrice(context.Background())
if err != nil {
log.Fatal(err)
}
auth := bind.NewKeyedTransactor(privateKey)
auth.Nonce = big.NewInt(int64(nonce))
auth.Value = big.NewInt(0) // in wei
auth.GasLimit = uint64(300000) // in units
auth.GasPrice = gasPrice
input := "1.0"
address, tx, instance, err := store.DeployStore(auth, client, input)
if err != nil {
log.Fatal(err)
}
fmt.Println(address.Hex()) // 0x147B8eb97fD247D06C4006D269c90C1908Fb5D54
fmt.Println(tx.Hash().Hex()) // 0xdae8ba5444eefdc99f4d45cd0c4f24056cba6a02cefbf78066ef9f4188ff7dc0
_ = instance
}
solc version used for these examples
$ solc --version
0.4.24+commit.e67f0147.Emscripten.clang
加載智能合約
這寫章節(jié)需要了解如何將智能合約的ABI編譯成Go的合約文件。如果你還沒看讼育, 前先讀上一個(gè)章節(jié) 帐姻。
一旦使用abigen
工具將智能合約的ABI編譯為Go包,下一步就是調(diào)用“New”方法奶段,其格式為“New<contractname style="box-sizing: border-box; font-size: 16px; -ms-text-size-adjust: auto; -webkit-tap-highlight-color: transparent;">”饥瓷,所以在我們的例子中如果你 回想一下它將是NewStore。 此初始化方法接收智能合約的地址痹籍,并返回可以開始與之交互的合約實(shí)例呢铆。</contractname>
address := common.HexToAddress("0x147B8eb97fD247D06C4006D269c90C1908Fb5D54")
instance, err := store.NewStore(address, client)
if err != nil {
log.Fatal(err)
}
_ = instance // we'll be using this in the 下個(gè)章節(jié)
完整代碼
Commands
solc --abi Store.sol
solc --bin Store.sol
abigen --bin=Store_sol_Store.bin --abi=Store_sol_Store.abi --pkg=store --out=Store.go
pragma solidity ^0.4.24;
contract Store {
event ItemSet(bytes32 key, bytes32 value);
string public version;
mapping (bytes32 => bytes32) public items;
constructor(string _version) public {
version = _version;
}
function setItem(bytes32 key, bytes32 value) external {
items[key] = value;
emit ItemSet(key, value);
}
}
package main
import (
"fmt"
"log"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
store "./contracts" // for demo
)
func main() {
client, err := ethclient.Dial("https://rinkeby.infura.io")
if err != nil {
log.Fatal(err)
}
address := common.HexToAddress("0x147B8eb97fD247D06C4006D269c90C1908Fb5D54")
instance, err := store.NewStore(address, client)
if err != nil {
log.Fatal(err)
}
fmt.Println("contract is loaded")
_ = instance
}
solc version used for these examples
$ solc --version
0.4.24+commit.e67f0147.Emscripten.clang
查詢智能合約
Querying a Smart Contract
這寫章節(jié)需要了解如何將智能合約的ABI編譯成Go的合約文件。如果你還沒看蹲缠, 前先讀上一個(gè)章節(jié) 棺克。
在上個(gè)章節(jié)我們學(xué)習(xí)了如何在Go應(yīng)用程序中初始化合約實(shí)例。 現(xiàn)在我們將使用新合約實(shí)例提供的方法來閱讀智能合約线定。 如果你還記得我們?cè)诓渴疬^程中設(shè)置的合約中有一個(gè)名為version
的全局變量娜谊。 因?yàn)樗枪_的,這意味著它們將成為我們自動(dòng)創(chuàng)建的getter函數(shù)斤讥。 常量和view函數(shù)也接受bind.CallOpts
作為第一個(gè)參數(shù)纱皆。了解可用的具體選項(xiàng)要看相應(yīng)類的文檔 一般情況下我們可以用 nil
。
version, err := instance.Version(nil)
if err != nil {
log.Fatal(err)
}
fmt.Println(version) // "1.0"
完整代碼
Commands
solc --abi Store.sol
solc --bin Store.sol
abigen --bin=Store_sol_Store.bin --abi=Store_sol_Store.abi --pkg=store --out=Store.go
pragma solidity ^0.4.24;
contract Store {
event ItemSet(bytes32 key, bytes32 value);
string public version;
mapping (bytes32 => bytes32) public items;
constructor(string _version) public {
version = _version;
}
function setItem(bytes32 key, bytes32 value) external {
items[key] = value;
emit ItemSet(key, value);
}
}
package main
import (
"fmt"
"log"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
store "./contracts" // for demo
)
func main() {
client, err := ethclient.Dial("https://rinkeby.infura.io")
if err != nil {
log.Fatal(err)
}
address := common.HexToAddress("0x147B8eb97fD247D06C4006D269c90C1908Fb5D54")
instance, err := store.NewStore(address, client)
if err != nil {
log.Fatal(err)
}
version, err := instance.Version(nil)
if err != nil {
log.Fatal(err)
}
fmt.Println(version) // "1.0"
}
solc version used for these examples
$ solc --version
0.4.24+commit.e67f0147.Emscripten.clang
寫入智能合約
這寫章節(jié)需要了解如何將智能合約的ABI編譯成Go的合約文件芭商。如果你還沒看派草, 前先讀上一個(gè)章節(jié) 。
寫入智能合約需要我們用私鑰來對(duì)交易事務(wù)進(jìn)行簽名铛楣。
privateKey, err := crypto.HexToECDSA("fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19")
if err != nil {
log.Fatal(err)
}
publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
log.Fatal("error casting public key to ECDSA")
}
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
我們還需要先查到nonce和燃?xì)鈨r(jià)格近迁。
nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
if err != nil {
log.Fatal(err)
}
gasPrice, err := client.SuggestGasPrice(context.Background())
if err != nil {
log.Fatal(err)
}
接下來,我們創(chuàng)建一個(gè)新的keyed transactor簸州,它接收私鑰鉴竭。
auth := bind.NewKeyedTransactor(privateKey)
然后我們需要設(shè)置keyed transactor的標(biāo)準(zhǔn)交易選項(xiàng)。
auth.Nonce = big.NewInt(int64(nonce))
auth.Value = big.NewInt(0) // in wei
auth.GasLimit = uint64(300000) // in units
auth.GasPrice = gasPrice
現(xiàn)在我們加載一個(gè)智能合約的實(shí)例勿侯。如果你還記得上個(gè)章節(jié) 我們創(chuàng)建一個(gè)名為Store的合約拓瞪,并使用abigen
工具生成一個(gè)Go文件缴罗。 要初始化它助琐,我們只需調(diào)用合約包的New方法,并提供智能合約地址和ethclient面氓,它返回我們可以使用的合約實(shí)例兵钮。
address := common.HexToAddress("0x147B8eb97fD247D06C4006D269c90C1908Fb5D54")
instance, err := store.NewStore(address, client)
if err != nil {
log.Fatal(err)
}
我們創(chuàng)建的智能合約有一個(gè)名為SetItem的外部方法蛆橡,它接受solidity“bytes32”格式的兩個(gè)參數(shù)(key,value)掘譬。 這意味著Go合約包要求我們傳遞一個(gè)長(zhǎng)度為32個(gè)字節(jié)的字節(jié)數(shù)組泰演。 調(diào)用SetItem方法需要我們傳遞我們之前創(chuàng)建的auth
對(duì)象(keyed transactor)。 在幕后葱轩,此方法將使用它的參數(shù)對(duì)此函數(shù)調(diào)用進(jìn)行編碼睦焕,將其設(shè)置為事務(wù)的data
屬性,并使用私鑰對(duì)其進(jìn)行簽名靴拱。 結(jié)果將是一個(gè)已簽名的事務(wù)對(duì)象垃喊。
key := [32]byte{}
value := [32]byte{}
copy(key[:], []byte("foo"))
copy(value[:], []byte("bar"))
tx, err := instance.SetItem(auth, key, value)
if err != nil {
log.Fatal(err)
}
fmt.Printf("tx sent: %s", tx.Hash().Hex()) // tx sent: 0x8d490e535678e9a24360e955d75b27ad307bdfb97a1dca51d0f3035dcee3e870
現(xiàn)在我就可以看到交易已經(jīng)成功被發(fā)送到了以太坊網(wǎng)絡(luò)了: https://rinkeby.etherscan.io/tx/0x8d490e535678e9a24360e955d75b27ad307bdfb97a1dca51d0f3035dcee3e870
要驗(yàn)證鍵/值是否已設(shè)置,我們可以讀取智能合約中的值袜炕。
result, err := instance.Items(nil, key)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(result[:])) // "bar"
搞定本谜!
完整代碼
Commands
solc --abi Store.sol
solc --bin Store.sol
abigen --bin=Store_sol_Store.bin --abi=Store_sol_Store.abi --pkg=store --out=Store.go
pragma solidity ^0.4.24;
contract Store {
event ItemSet(bytes32 key, bytes32 value);
string public version;
mapping (bytes32 => bytes32) public items;
constructor(string _version) public {
version = _version;
}
function setItem(bytes32 key, bytes32 value) external {
items[key] = value;
emit ItemSet(key, value);
}
}
package main
import (
"fmt"
"log"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
store "./contracts" // for demo
)
func main() {
client, err := ethclient.Dial("https://rinkeby.infura.io")
if err != nil {
log.Fatal(err)
}
privateKey, err := crypto.HexToECDSA("fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19")
if err != nil {
log.Fatal(err)
}
publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
log.Fatal("error casting public key to ECDSA")
}
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
if err != nil {
log.Fatal(err)
}
gasPrice, err := client.SuggestGasPrice(context.Background())
if err != nil {
log.Fatal(err)
}
auth := bind.NewKeyedTransactor(privateKey)
auth.Nonce = big.NewInt(int64(nonce))
auth.Value = big.NewInt(0) // in wei
auth.GasLimit = uint64(300000) // in units
auth.GasPrice = gasPrice
address := common.HexToAddress("0x147B8eb97fD247D06C4006D269c90C1908Fb5D54")
instance, err := store.NewStore(address, client)
if err != nil {
log.Fatal(err)
}
key := [32]byte{}
value := [32]byte{}
copy(key[:], []byte("foo"))
copy(value[:], []byte("bar"))
tx, err := instance.SetItem(auth, key, value)
if err != nil {
log.Fatal(err)
}
fmt.Printf("tx sent: %s", tx.Hash().Hex()) // tx sent: 0x8d490e535678e9a24360e955d75b27ad307bdfb97a1dca51d0f3035dcee3e870
result, err := instance.Items(nil, key)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(result[:])) // "bar"
}
solc version used for these examples
$ solc --version
0.4.24+commit.e67f0147.Emscripten.clang
讀取智能合約的字節(jié)碼
有時(shí)您需要讀取已部署的智能合約的字節(jié)碼。 由于所有智能合約字節(jié)碼都存在于區(qū)塊鏈中偎窘,因此我們可以輕松獲取它乌助。
首先設(shè)置客戶端和要讀取的字節(jié)碼的智能合約地址。
client, err := ethclient.Dial("https://rinkeby.infura.io")
if err != nil {
log.Fatal(err)
}
contractAddress := common.HexToAddress("0x147B8eb97fD247D06C4006D269c90C1908Fb5D54")
現(xiàn)在你需要調(diào)用客戶端的codeAt
方法陌知。 codeAt
方法接受智能合約地址和可選的塊編號(hào)他托,并以字節(jié)格式返回字節(jié)碼。
bytecode, err := client.CodeAt(context.Background(), contractAddress, nil) // nil is latest block
if err != nil {
log.Fatal(err)
}
fmt.Println(hex.EncodeToString(bytecode)) // 60806...10029
你也可以在etherscan上查詢16進(jìn)制格式的字節(jié)碼 https://rinkeby.etherscan.io/address/0x147b8eb97fd247d06c4006d269c90c1908fb5d54#code
完整代碼
package main
import (
"context"
"encoding/hex"
"fmt"
"log"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
)
func main() {
client, err := ethclient.Dial("https://rinkeby.infura.io")
if err != nil {
log.Fatal(err)
}
contractAddress := common.HexToAddress("0x147B8eb97fD247D06C4006D269c90C1908Fb5D54")
bytecode, err := client.CodeAt(context.Background(), contractAddress, nil) // nil is latest block
if err != nil {
log.Fatal(err)
}
fmt.Println(hex.EncodeToString(bytecode)) // 60806...10029
}
查詢ERC20代幣智能合約
首先創(chuàng)建一個(gè)ERC20智能合約interface仆葡。 這只是與您可以調(diào)用的函數(shù)的函數(shù)定義的契約上祈。
pragma solidity ^0.4.24;
contract ERC20 {
string public constant name = "";
string public constant symbol = "";
uint8 public constant decimals = 0;
function totalSupply() public constant returns (uint);
function balanceOf(address tokenOwner) public constant returns (uint balance);
function allowance(address tokenOwner, address spender) public constant returns (uint remaining);
function transfer(address to, uint tokens) public returns (bool success);
function approve(address spender, uint tokens) public returns (bool success);
function transferFrom(address from, address to, uint tokens) public returns (bool success);
event Transfer(address indexed from, address indexed to, uint tokens);
event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}
然后將interface智能合約編譯為JSON ABI,并使用abigen
從ABI創(chuàng)建Go包浙芙。
solc --abi erc20.sol
abigen --abi=erc20_sol_ERC20.abi --pkg=token --out=erc20.go
假設(shè)我們已經(jīng)像往常一樣設(shè)置了以太坊客戶端登刺,我們現(xiàn)在可以將新的token包導(dǎo)入我們的應(yīng)用程序并實(shí)例化它。這個(gè)例子里我們用Golem 代幣的地址.
tokenAddress := common.HexToAddress("0xa74476443119A942dE498590Fe1f2454d7D4aC0d")
instance, err := token.NewToken(tokenAddress, client)
if err != nil {
log.Fatal(err)
}
我們現(xiàn)在可以調(diào)用任何ERC20的方法嗡呼。 例如纸俭,我們可以查詢用戶的代幣余額。
address := common.HexToAddress("0x0536806df512d6cdde913cf95c9886f65b1d3462")
bal, err := instance.BalanceOf(&bind.CallOpts{}, address)
if err != nil {
log.Fatal(err)
}
fmt.Printf("wei: %s\n", bal) // "wei: 74605500647408739782407023"
我們還可以讀ERC20智能合約的公共變量南窗。
name, err := instance.Name(&bind.CallOpts{})
if err != nil {
log.Fatal(err)
}
symbol, err := instance.Symbol(&bind.CallOpts{})
if err != nil {
log.Fatal(err)
}
decimals, err := instance.Decimals(&bind.CallOpts{})
if err != nil {
log.Fatal(err)
}
fmt.Printf("name: %s\n", name) // "name: Golem Network"
fmt.Printf("symbol: %s\n", symbol) // "symbol: GNT"
fmt.Printf("decimals: %v\n", decimals) // "decimals: 18"
我們可以做一些簡(jiǎn)單的數(shù)學(xué)運(yùn)算將余額轉(zhuǎn)換為可讀的十進(jìn)制格式揍很。
fbal := new(big.Float)
fbal.SetString(bal.String())
value := new(big.Float).Quo(fbal, big.NewFloat(math.Pow10(int(decimals))))
fmt.Printf("balance: %f", value) // "balance: 74605500.647409"
同樣的信息也可以在etherscan上查詢: https://etherscan.io/token/0xa74476443119a942de498590fe1f2454d7d4ac0d?a=0x0536806df512d6cdde913cf95c9886f65b1d3462
完整代碼
Commands
solc --abi erc20.sol
abigen --abi=erc20_sol_ERC20.abi --pkg=token --out=erc20.go
pragma solidity ^0.4.24;
contract ERC20 {
string public constant name = "";
string public constant symbol = "";
uint8 public constant decimals = 0;
function totalSupply() public constant returns (uint);
function balanceOf(address tokenOwner) public constant returns (uint balance);
function allowance(address tokenOwner, address spender) public constant returns (uint remaining);
function transfer(address to, uint tokens) public returns (bool success);
function approve(address spender, uint tokens) public returns (bool success);
function transferFrom(address from, address to, uint tokens) public returns (bool success);
event Transfer(address indexed from, address indexed to, uint tokens);
event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}
package main
import (
"fmt"
"log"
"math"
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
token "./contracts_erc20" // for demo
)
func main() {
client, err := ethclient.Dial("https://mainnet.infura.io")
if err != nil {
log.Fatal(err)
}
// Golem (GNT) Address
tokenAddress := common.HexToAddress("0xa74476443119A942dE498590Fe1f2454d7D4aC0d")
instance, err := token.NewToken(tokenAddress, client)
if err != nil {
log.Fatal(err)
}
address := common.HexToAddress("0x0536806df512d6cdde913cf95c9886f65b1d3462")
bal, err := instance.BalanceOf(&bind.CallOpts{}, address)
if err != nil {
log.Fatal(err)
}
name, err := instance.Name(&bind.CallOpts{})
if err != nil {
log.Fatal(err)
}
symbol, err := instance.Symbol(&bind.CallOpts{})
if err != nil {
log.Fatal(err)
}
decimals, err := instance.Decimals(&bind.CallOpts{})
if err != nil {
log.Fatal(err)
}
fmt.Printf("name: %s\n", name) // "name: Golem Network"
fmt.Printf("symbol: %s\n", symbol) // "symbol: GNT"
fmt.Printf("decimals: %v\n", decimals) // "decimals: 18"
fmt.Printf("wei: %s\n", bal) // "wei: 74605500647408739782407023"
fbal := new(big.Float)
fbal.SetString(bal.String())
value := new(big.Float).Quo(fbal, big.NewFloat(math.Pow10(int(decimals))))
fmt.Printf("balance: %f", value) // "balance: 74605500.647409"
}
solc version used for these examples
$ solc --version
0.4.24+commit.e67f0147.Emscripten.clang