Привязки Go имеют тип Log
, который имеет следующие поля:
type Log struct {
// Consensus fields:
// address of the contract that generated the event
Address common.Address `json:"address" gencodec:"required"`
// list of topics provided by the contract.
Topics []common.Hash `json:"topics" gencodec:"required"`
// supplied by the contract, usually ABI-encoded
Data []byte `json:"data" gencodec:"required"`
[...]
}
Допустим, у меня есть такое событиеevent SomethingHappened(uint256 indexed id, address indexed participant1, address indexed participant2, uint256 value1, uint256 value2);
Есть 4 темы и заполняются они следующим образом, они типа common.Hash
:
SHA3("SomethingHappened(uint256,address,address,uint256,uint256)")
uint256
, который можно прочитать с помощью большой библиотеки:new(big.Int).SetBytes(log.Topics[1].Bytes())
common.Hash
имеет длину 32 байта и common.Address
20 байтов, вы можете получить адрес следующим образом: common.BytesToAddress(log.Topics[2].Bytes()[12:32])
.Теперь это не так удобно делать, но все же возможно. Однако когда мы пытаемся получить доступ к последним двум аргументам, я теряюсь. Они как-то закодированы в Data []byte
поле common.Log
структуры. В документации упоминается, что они «закодированы с помощью ABI», но не предлагает способа что-либо сделать с этими данными. abigen также не поддерживает переменные событий, поэтому я действительно не понимаю, как получить доступ к этим значениям.
Поле Data
Тип журнала содержит неиндексированные аргументы журнала событий, поэтому все, что вам нужно сделать, это декодировать их в типы Go.
Так, например, вот простой смарт-контракт, который создает неиндексированные записи журнала:
pragma solidity ^0.4.24;
contract Store {
event ItemSet(bytes32 key, bytes32 value);
mapping (bytes32 => bytes32) public items;
function setItem(bytes32 key, bytes32 value) external {
items[key] = value;
emit ItemSet(key, value);
}
}
Затем в вашем коде, после извлечения журналов событий, вы вызываете Unpack
метод из смарт-контракта Go, передавая ему структуру, содержащую свойства события, имя события журнала смарт-контракта и, наконец, фактические данные журнала.
for _, vLog := range logs {
event := struct {
Key [32]byte
Value [32]byte
}{}
err := contractAbi.Unpack(&event, "ItemSet", vLog.Data)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(event.Key[:])) // foo
fmt.Println(string(event.Value[:])) // bar
}
Вот полный пример запроса и декодирования неиндексированных журналов примера смарт-контракта.
package main
import (
"context"
"fmt"
"log"
"math/big"
"strings"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
store "./contracts" // for demo
)
func main() {
client, err := ethclient.Dial("wss://rinkeby.infura.io/ws")
if err != nil {
log.Fatal(err)
}
contractAddress := common.HexToAddress("0x147B8eb97fD247D06C4006D269c90C1908Fb5D54")
query := ethereum.FilterQuery{
FromBlock: big.NewInt(2394201),
ToBlock: big.NewInt(2394201),
Addresses: []common.Address{
contractAddress,
},
}
logs, err := client.FilterLogs(context.Background(), query)
if err != nil {
log.Fatal(err)
}
contractAbi, err := abi.JSON(strings.NewReader(string(store.StoreABI)))
if err != nil {
log.Fatal(err)
}
for _, vLog := range logs {
event := struct {
Key [32]byte
Value [32]byte
}{}
err := contractAbi.Unpack(&event, "ItemSet", vLog.Data)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(event.Key[:])) // foo
fmt.Println(string(event.Value[:])) // bar
var topics [4]string
for i := range vLog.Topics {
topics[i] = vLog.Topics[i].Hex()
}
fmt.Println(topics[0]) // 0xe79e73da417710ae99aa2088575580a60415d359acfad9cdd3382d59c80281d4
}
eventSignature := []byte("ItemSet(bytes32,bytes32)")
hash := crypto.Keccak256Hash(eventSignature)
fmt.Println(hash.Hex()) // 0xe79e73da417710ae99aa2088575580a60415d359acfad9cdd3382d59c80281d4
}
Для индексированных журналов вы просто используете log.Topics
. Первым разделом всегда является идентификатор метода, который представляет собой хэш сигнатуры функции журнала событий (имя метода и типы аргументов).
Дополнительные примеры см. в руководстве по разработке Ethereum с Go .