这写章节需要了解如何将智能合约的ABI编译成Go的合约文件。如果你还没看, 前先读上一个章节 。
写入智能合约需要我们用私钥来对交易事务进行签名。
privateKey, err := crypto.HexToECDSA("fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19")if err != nil {log.Fatal(err)}publicKey := privateKey.Public()publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)if !ok {log.Fatal("cannot assert type: publicKey is not of type *ecdsa.PublicKey")}fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
我们还需要先查到nonce和燃气价格。
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)}
接下来,我们创建一个新的keyed transactor,它接收私钥。
auth := bind.NewKeyedTransactor(privateKey)
然后我们需要设置keyed transactor的标准交易选项。
auth.Nonce = big.NewInt(int64(nonce))auth.Value = big.NewInt(0) // in weiauth.GasLimit = uint64(300000) // in unitsauth.GasPrice = gasPrice
现在我们加载一个智能合约的实例。如果你还记得上个章节 我们创建一个名为Store的合约,并使用abigen工具生成一个Go文件。 要初始化它,我们只需调用合约包的New方法,并提供智能合约地址和ethclient,它返回我们可以使用的合约实例。
address := common.HexToAddress("0x147B8eb97fD247D06C4006D269c90C1908Fb5D54")instance, err := store.NewStore(address, client)if err != nil {log.Fatal(err)}
我们创建的智能合约有一个名为SetItem的外部方法,它接受solidity“bytes32”格式的两个参数(key,value)。 这意味着Go合约包要求我们传递一个长度为32个字节的字节数组。 调用SetItem方法需要我们传递我们之前创建的auth对象(keyed transactor)。 在幕后,此方法将使用它的参数对此函数调用进行编码,将其设置为事务的data属性,并使用私钥对其进行签名。 结果将是一个已签名的事务对象。
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\n", tx.Hash().Hex()) // tx sent: 0x8d490e535678e9a24360e955d75b27ad307bdfb97a1dca51d0f3035dcee3e870
现在我就可以看到交易已经成功被发送到了以太坊网络了: https://rinkeby.etherscan.io/tx/0x8d490e535678e9a24360e955d75b27ad307bdfb97a1dca51d0f3035dcee3e870
要验证键/值是否已设置,我们可以读取智能合约中的值。
result, err := instance.Items(nil, key)if err != nil {log.Fatal(err)}fmt.Println(string(result[:])) // "bar"
完整代码
Commands
solc --abi Store.solsolc --bin Store.solabigen --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 mainimport ("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("cannot assert type: publicKey is not of type *ecdsa.PublicKey")}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 weiauth.GasLimit = uint64(300000) // in unitsauth.GasPrice = gasPriceaddress := 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: 0x8d490e535678e9a24360e955d75b27ad307bdfb97a1dca51d0f3035dcee3e870result, err := instance.Items(nil, key)if err != nil {log.Fatal(err)}fmt.Println(string(result[:])) // "bar"}
solc version used for these examples
$ solc --version0.4.24+commit.e67f0147.Emscripten.clang
