Los enlaces Go tienen un tipo llamado Log
que tiene los siguientes campos
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"`
[...]
}
Digamos que tengo un evento como esteevent SomethingHappened(uint256 indexed id, address indexed participant1, address indexed participant2, uint256 value1, uint256 value2);
Hay 4 temas y se llenan de la siguiente manera, son del tipo common.Hash
:
SHA3("SomethingHappened(uint256,address,address,uint256,uint256)")
uint256
que se puede leer usando la gran biblioteca:new(big.Int).SetBytes(log.Topics[1].Bytes())
common.Hash
que tiene una longitud de 32 bytes y common.Address
20 bytes, puede obtener la dirección de esta manera: common.BytesToAddress(log.Topics[2].Bytes()[12:32])
.Ahora, eso no es tan conveniente de hacer, pero aún es posible. Sin embargo, cuando intentamos acceder a los dos últimos argumentos, me pierdo. Están codificados de alguna manera en el Data []byte
campo de la common.Log
estructura. La documentación menciona que están "codificados con ABI", pero no ofrece ninguna forma de hacer nada con esos datos. abigen tampoco tiene soporte para variables de eventos, por lo que realmente no sé cómo acceder a esos valores.
El Data
campo Tipo de registro contiene los argumentos de registro de eventos no indexados , por lo que todo lo que tiene que hacer es decodificarlos en tipos Go.
Entonces, por ejemplo, aquí hay un contrato inteligente simple que emite entradas de registro no indexadas:
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);
}
}
Luego, en su código, después de obtener los registros de eventos, llama al Unpack
método del contrato inteligente Go vinculante pasándole la estructura que contiene las propiedades del evento, el nombre del evento de registro del contrato inteligente y, por último, los datos de registro reales.
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
}
Este es el ejemplo completo de consulta y decodificación de los registros no indexados del contrato inteligente de ejemplo.
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
}
Para registros indexados, simplemente use log.Topics
. El primer tema es siempre el ID del método, que es un hash de la firma de la función de registro de eventos (nombre del método y tipos de argumentos).
Consulte la guía Ethereum Development with Go para obtener más ejemplos.