Estoy obteniendo los registros usando client.SubscribeFilterLogs
. Los parámetros del evento están codificados en types.Log.Data
. ¿Cómo puedo decodificarlo en go
?
El paquete go-ethereum abi en octubre de 2017 recibió la actualización para desempaquetar la salida del evento. Inicialmente, solo podía desempaquetar la salida del método.
Toda la funcionalidad se entrega a través del abi.ABI
objeto. Para usarlo, debe tener Event ABI (cadena JSON). Luego use func (abi *ABI) UnmarshalJSON para construir el ABI
objeto. Desde allí, puede usar el Unpack
método usando Data
desde su objeto de registro.
Tenga en cuenta que los atributos indexados van al registro en Topic
lugar de Data
.
Para obtener más detalles de uso, puede seguir estos ejemplos:
NOTA: En la implementación actual (2017-11-29) hay un error con los atributos indexados. Envié un PR para eso y todavía estoy esperando la aprobación final.
Aquí hay un ejemplo de código completo para cualquiera que todavía esté confundido (gracias a la respuesta de @Robert Zaremba)
package main
import (
"context"
"log"
"math/big"
"strings"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/myorg/myrepo/go-contracts/token"
"github.com/ethereum/go-ethereum/core/types"
)
func main() {
contractAddress := common.HexToAddress("0x0d8775f648430679a709e98d2b0cb6250d2887ef")
query := ethereum.FilterQuery{
Addresses: []common.Address{contractAddress},
}
var ch = make(chan types.Log)
ctx := context.Background()
sub, err := Client.SubscribeFilterLogs(ctx, query, ch)
if err != nil {
log.Fatal(err)
}
tokenAbi, err := abi.JSON(strings.NewReader(string(token.TokenABI)))
if err != nil {
log.Fatal(err)
}
for {
select {
case err := <-sub.Err():
log.Fatal(err)
case eventLog := <-ch:
var transferEvent struct {
From common.Address
To common.Address
Value *big.Int
}
err = tokenAbi.Unpack(&transferEvent, "Transfer", eventLog.Data)
if err != nil {
log.Println("Failed to unpack")
continue
}
transferEvent.From = common.BytesToAddress(eventLog.Topics[1].Bytes())
transferEvent.To = common.BytesToAddress(eventLog.Topics[2].Bytes())
log.Println("From", transferEvent.From.Hex())
log.Println("To", transferEvent.To.Hex())
log.Println("Value", transferEvent.Value)
}
}
}
burn
o mint
, entonces, ¿cómo sabe de antemano qué tipo de evento tiene que desempacar?keccak256(eventName(arg1,arg2))
por lo que si se trata de un evento de transferencia, topic[0] será keccak256(Transfer(address,address,uint256))
=> ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
. En resumen: conoce el tipo de evento filtrando por tema [0], que es el hash de la firma del evento. Consulte esta guía Ethereum Development with Go para ver ejemplos goethereumbook.orgkeccak256(Transfer(address,address,uint256))
=>ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
keccak256(Approval(address,address,uint256))
=>8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925
geth
versión se usó con este código?Version: 1.8.10-stable
Así es como lo hice usando el ABI:
path, _ := filepath.Abs("./resources/etherdelta.abi")
file, err := ioutil.ReadFile(path)
if err != nil {
fmt.Println("Failed to read file:", err)
}
edabi, err := abi.JSON(strings.NewReader(string(file)))
if err != nil {
fmt.Println("Invalid abi:", err)
}
var orderStruct struct {
TokenGet common.Address
AmountGet *big.Int
TokenGive common.Address
AmountGive *big.Int
Expires *big.Int
Nonce *big.Int
User common.Address
}
err = edabi.Unpack(&orderStruct, "Order", log.Data)
if err != nil {
fmt.Println("Failed to unpack:", err)
}
fmt.Println("TokenGet:", orderStruct.TokenGet.Hex())
fmt.Println("AmountGet:", orderStruct.AmountGet.Hex())
data := common.TrimLeftZeroes(log.Data)
hex := common.Bytes2Hex(data)
hex = TrimLeftZeroes(hex)
if hex != "" {
erc20Amount, err := hexutil.DecodeBig("0x" + hex)
}
func TrimLeftZeroes(hex string) string {
idx := 0
for ; idx < len(hex); idx++ {
if hex[idx] != '0' {
break
}
}
return hex[idx:]
}
Roberto Zaremba