diff --git a/ethcoder/events.go b/ethcoder/events.go index 515f797f..d72ab272 100644 --- a/ethcoder/events.go +++ b/ethcoder/events.go @@ -8,6 +8,7 @@ import ( "github.com/0xsequence/ethkit/go-ethereum/accounts/abi" "github.com/0xsequence/ethkit/go-ethereum/accounts/abi/bind" "github.com/0xsequence/ethkit/go-ethereum/common" + "github.com/0xsequence/ethkit/go-ethereum/common/hexutil" "github.com/0xsequence/ethkit/go-ethereum/core/types" ) @@ -24,62 +25,13 @@ func EventTopicHash(event string) (ethkit.Hash, string, error) { return topicHash, eventDef.Sig, nil } -type EventDef struct { - TopicHash string `json:"topicHash"` // the event topic hash, ie. 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef - Name string `json:"name"` // the event name, ie. Transfer - Sig string `json:"sig"` // the event sig, ie. Transfer(address,address,uint256) - ArgTypes []string `json:"argTypes"` // the event arg types, ie. [address, address, uint256] - ArgNames []string `json:"argNames"` // the event arg names, ie. [from, to, value] or ["","",""] -} - -func ParseEventDef(event string) (EventDef, error) { - eventDef := EventDef{ - ArgTypes: []string{}, - ArgNames: []string{}, - } - - var errInvalid = fmt.Errorf("event format is invalid, expecting Method(arg1,arg2,..)") - - if !strings.Contains(event, "(") || !strings.Contains(event, ")") { - return eventDef, errInvalid - } - p := strings.Split(event, "(") - if len(p) != 2 { - return eventDef, errInvalid - } - method := strings.TrimSpace(p[0]) - eventDef.Name = method - - args := strings.TrimSuffix(p[1], ")") - if args == "" { - eventDef.Sig = fmt.Sprintf("%s()", method) +func ValidateEventSig(eventSig string) (bool, error) { + _, err := abi.ParseSelector(eventSig) + if err != nil { + return false, err } else { - typs := []string{} - names := []string{} - - p = strings.Split(args, ",") - for _, a := range p { - arg := strings.Split(strings.TrimSpace(a), " ") - - typ := arg[0] - typs = append(typs, typ) - - var name string - if len(arg) > 1 { - name = arg[len(arg)-1] - } - names = append(names, name) - } - - eventDef.ArgTypes = typs - eventDef.ArgNames = names - - eventDef.Sig = fmt.Sprintf("%s(%s)", method, strings.Join(typs, ",")) + return true, nil } - - eventDef.TopicHash = Keccak256Hash([]byte(eventDef.Sig)).String() - - return eventDef, nil } // .. @@ -141,14 +93,26 @@ func DecodeTransactionLogByEventSig(txnLog types.Log, eventSig string, returnHex // Lets build a mini abi on-demand, and decode it abiArgs := abi.Arguments{} numIndexedArgs := len(txnLog.Topics) - 1 + if numIndexedArgs < 0 { + numIndexedArgs = 0 // for anonymous events + } + + // NOTE: we could avoid parsing the selector if we know there are no arrays/tuples + // which means there are no additional components in the abi type + selector, err := abi.ParseSelector(eventDef.Sig) + if err != nil { + return eventDef, nil, false, fmt.Errorf("ParseSelector: %w", err) + } for i, argType := range eventDef.ArgTypes { + selectorArg := selector.Inputs[i] + argName := eventDef.ArgNames[i] if argName == "" { argName = fmt.Sprintf("arg%d", i) } - typ, err := abi.NewType(argType, "", nil) + typ, err := abi.NewType(selectorArg.Type, "", selectorArg.Components) if err != nil { return eventDef, nil, false, fmt.Errorf("invalid abi argument type '%s': %w", argType, err) } @@ -156,40 +120,13 @@ func DecodeTransactionLogByEventSig(txnLog types.Log, eventSig string, returnHex abiArgs = append(abiArgs, abi.Argument{Name: argName, Type: typ, Indexed: i < numIndexedArgs}) } - if !returnHexValues { - - // Decode into native runtime types - abiEvent := abi.NewEvent(eventDef.Name, eventDef.Name, false, abiArgs) - contractABI := abi.ABI{ - Events: map[string]abi.Event{}, - } - contractABI.Events[eventDef.Name] = abiEvent - - args := []string{} - for _, arg := range abiEvent.Inputs { - args = append(args, arg.Name) - } - - bc := bind.NewBoundContract(txnLog.Address, contractABI, nil, nil, nil) - - eventMap := map[string]interface{}{} - err = bc.UnpackLogIntoMap(eventMap, abiEvent.Name, txnLog) - if err != nil { - return eventDef, nil, false, fmt.Errorf("UnpackLogIntoMap: %w", err) - } - - eventValues := []interface{}{} - for _, arg := range args { - eventValues = append(eventValues, eventMap[arg]) - } - - return eventDef, eventValues, true, nil - - } else { + // Fast decode + if returnHexValues && !strings.Contains(eventSig, "[") { // Decode into hex values, which means []interface{} will always return array of strings. // This is useful in cases when you want to return the hex values of the values instead // of decoding to runtime types. + // fast decode eventValues := []interface{}{} dataPos := 0 @@ -210,5 +147,52 @@ func DecodeTransactionLogByEventSig(txnLog types.Log, eventSig string, returnHex } return eventDef, eventValues, true, nil + + } + + // Decode via abi + abiEvent := abi.NewEvent(eventDef.Name, eventDef.Name, false, abiArgs) + contractABI := abi.ABI{ + Events: map[string]abi.Event{}, + } + contractABI.Events[eventDef.Name] = abiEvent + + args := []string{} + for _, arg := range abiEvent.Inputs { + args = append(args, arg.Name) + } + + bc := bind.NewBoundContract(txnLog.Address, contractABI, nil, nil, nil) + + eventMap := map[string]interface{}{} + err = bc.UnpackLogIntoMap(eventMap, abiEvent.Name, txnLog) + if err != nil { + return eventDef, nil, false, fmt.Errorf("UnpackLogIntoMap: %w", err) + } + + eventValues := []interface{}{} + for _, arg := range args { + eventValues = append(eventValues, eventMap[arg]) + } + + // Return native values + if !returnHexValues { + return eventDef, eventValues, true, nil + } + + // Re-encode back to hex values + if len(eventValues) != len(abiArgs) { + return eventDef, nil, false, fmt.Errorf("event values length mismatch: %d != %d", len(eventValues), len(abiArgs)) + } + + out := []interface{}{} + for i, abiArg := range abiArgs { + x := abi.Arguments{abiArg} + data, err := x.Pack(eventValues[i]) + if err != nil { + return eventDef, nil, false, fmt.Errorf("PackValues: %w", err) + } + out = append(out, hexutil.Encode(data)) } + return eventDef, out, true, nil } diff --git a/ethcoder/events_parser.go b/ethcoder/events_parser.go new file mode 100644 index 00000000..bb49190e --- /dev/null +++ b/ethcoder/events_parser.go @@ -0,0 +1,249 @@ +package ethcoder + +import ( + "fmt" + "strings" +) + +type EventDef struct { + TopicHash string `json:"topicHash"` // the event topic hash, ie. 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef + Name string `json:"name"` // the event name, ie. Transfer + Sig string `json:"sig"` // the event sig, ie. Transfer(address,address,uint256) + ArgTypes []string `json:"argTypes"` // the event arg types, ie. [address, address, uint256] + ArgNames []string `json:"argNames"` // the event arg names, ie. [from, to, value] or ["","",""] +} + +func ParseEventDef(event string) (EventDef, error) { + eventDef := EventDef{ + ArgTypes: []string{}, + ArgNames: []string{}, + } + + var errInvalid = fmt.Errorf("event format is invalid, expecting Method(arg1,arg2,..)") + + if !strings.Contains(event, "(") || !strings.Contains(event, ")") { + return eventDef, errInvalid + } + + a := strings.Count(event, "(") + b := strings.Count(event, ")") + if a != b || a < 1 { + return eventDef, errInvalid + } + + a = strings.Index(event, "(") + b = strings.LastIndex(event, ")") + + method := strings.TrimSpace(event[:a]) + eventDef.Name = method + + args := strings.TrimSpace(event[a+1 : b]) + + if args == "" { + // no arguments, we are done + eventDef.Sig = fmt.Sprintf("%s()", method) + } else { + // event parser + tree, err := parseEventArgs(args) + if err != nil { + return eventDef, err + } + + argsSig, typs, err := groupEventSelectorTree(tree, true) + if err != nil { + return eventDef, err + } + eventDef.Sig = fmt.Sprintf("%s(%s)", method, argsSig) + eventDef.ArgTypes = typs + for i := 0; i < len(typs); i++ { + eventDef.ArgNames = append(eventDef.ArgNames, "") + } + } + + eventDef.TopicHash = Keccak256Hash([]byte(eventDef.Sig)).String() + + return eventDef, nil +} + +type eventSelectorTree struct { + left string + tuple []eventSelectorTree + tupleArray string + right []eventSelectorTree +} + +func parseEventArgs(eventArgs string) (eventSelectorTree, error) { + args := strings.TrimSpace(eventArgs) + out := eventSelectorTree{} + if args == "" { + return out, nil + } + + if args[len(args)-1] != ',' { + args += "," + } + + a := strings.Index(args, "(") + + p1 := "" + p2 := "" + p2ar := "" + p3 := "" + + if a < 0 { + p1 = args + } else { + p1 = strings.TrimSpace(args[:a]) + } + + if a >= 0 { + z, err := findParensCloseIndex(args[a:]) + if err != nil { + return out, err + } + z += a + 1 + + x := strings.Index(args[z:], ",") + if x > 0 { + z += x + 1 + } + + p2 = strings.TrimSpace(args[a:z]) + + // remove end params + x1 := strings.LastIndex(p2, "]") + x2 := strings.LastIndex(p2, ")") + if x1 > x2 { + p2 = p2[:x1+1] + } else { + p2 = p2[:x2+1] + } + + // split array from tuple + x1 = strings.LastIndex(p2, "]") + x2 = strings.LastIndex(p2, ")") + if x1 > 0 && x2 < x1 { + p2ar = p2[x2+1 : x1+1] + p2 = p2[:x2+1] + } + p2 = p2[1 : len(p2)-1] + + p3 = strings.TrimSpace(args[z:]) + } + + // p1 + if len(p1) > 0 { + p := strings.Split(p1, ",") + s := "" + for _, a := range p { + arg := strings.Split(strings.TrimSpace(a), " ") + typ := strings.TrimSpace(arg[0]) + if len(typ) > 0 { + s += typ + "," + } + } + if len(s) > 0 { + s = s[:len(s)-1] + } + out.left = s + } + + // p2 + if len(p2) > 0 { + out2, err := parseEventArgs(p2) + if err != nil { + return out, err + } + out.tuple = append(out.tuple, out2) + out.tupleArray = p2ar + } + + // p3 + if len(p3) > 0 { + out3, err := parseEventArgs(p3) + if err != nil { + return out, err + } + out.right = append(out.right, out3) + } + + return out, nil +} + +func groupEventSelectorTree(t eventSelectorTree, include bool) (string, []string, error) { + out := "" + typs := []string{} + + a := "" + b := "" + c := "" + + a = t.left + if t.left != "" { + out += t.left + "," + } + if include { + p := strings.Split(t.left, ",") + for _, v := range p { + if v != "" { + typs = append(typs, v) + } + } + } + + for _, child := range t.tuple { + s, _, err := groupEventSelectorTree(child, false) + if err != nil { + return "", nil, err + } + if s != "" { + b = "(" + strings.TrimRight(s, ",") + ")" + } + } + b += t.tupleArray + if include && b != "" { + typs = append(typs, b) + } + + for _, child := range t.right { + s, rtyps, err := groupEventSelectorTree(child, true) + if err != nil { + return "", nil, err + } + if s != "" { + c = strings.TrimRight(s, ",") + "," + } + for _, v := range rtyps { + if v != "" { + typs = append(typs, v) + } + } + } + + if len(a) > 0 { + out = a + "," + } + if len(b) > 0 { + out += b + "," + } + if len(c) > 0 { + out += c + } + + return strings.TrimRight(out, ","), typs, nil +} + +func findParensCloseIndex(args string) (int, error) { + n := 0 + for i, c := range args { + if c == '(' { + n++ + } else if c == ')' { + n-- + if n == 0 { + return i, nil + } + } + } + return -1, fmt.Errorf("invalid function args, no closing parenthesis found") +} diff --git a/ethcoder/events_parser_test.go b/ethcoder/events_parser_test.go new file mode 100644 index 00000000..393cd022 --- /dev/null +++ b/ethcoder/events_parser_test.go @@ -0,0 +1,79 @@ +package ethcoder + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParseEventArgs(t *testing.T) { + cases := []struct { + in string + expect string + numArgs int + }{ + { + "bytes32 num, address[] from, uint256 num, ( address op, (uint256 val, bytes32 data)) yes, address, (int128 a, int64 b), uint256", + "bytes32,address[],uint256,(address,(uint256,bytes32)),address,(int128,int64),uint256", + 7, + }, + { // its not actually valid selector, but use it for parser testing + "bytes blah, uint256[2][] yes, ( address yo, uint256[2] yes )[2][] okay, address yes", + "bytes,uint256[2][],(address,uint256[2])[2][],address", + 4, + }, + { // its not actually valid selector, but use it for parser testing + "address from, ( uint256 num, address cool, ( address op, uint256 val )[2] hmm)[][] lol, uint256 val", + "address,(uint256,address,(address,uint256)[2])[][],uint256", + 3, + }, + { + "address indexed from, address indexed to, uint256 value", + "address,address,uint256", + 3, + }, + { + "bytes32,address,address,((uint32,uint32,uint32,address,address,bool,bytes,uint256,address,uint256,uint256,uint256,bytes32),address[],bytes[],address,bytes),address,uint256,address", + "bytes32,address,address,((uint32,uint32,uint32,address,address,bool,bytes,uint256,address,uint256,uint256,uint256,bytes32),address[],bytes[],address,bytes),address,uint256,address", + 7, + }, + //.. + { + "(address,uint256,string,string)", + "(address,uint256,string,string)", + 1, + }, + { + "bytes32,address,address,address,(uint8,address,uint256,uint256)[],(uint8,address,uint256,uint256,address)[]", + "bytes32,address,address,address,(uint8,address,uint256,uint256)[],(uint8,address,uint256,uint256,address)[]", + 6, + }, + { + "address,(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,address,uint40,uint40)", + "address,(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,address,uint40,uint40)", + 2, + }, + { + "bytes32,address,address,address,(uint8,address,uint256,uint256)[],(uint8,address,uint256,uint256,address)[]", + "bytes32,address,address,address,(uint8,address,uint256,uint256)[],(uint8,address,uint256,uint256,address)[]", + 6, + }, + } + + for _, c := range cases { + tree, err := parseEventArgs(c.in) + require.NoError(t, err) + // spew.Dump(tree) + + out, typs, err := groupEventSelectorTree(tree, true) + require.NoError(t, err) + require.Equal(t, c.expect, out) + // spew.Dump(typs) + + require.Equal(t, c.numArgs, len(typs)) + + // ok, err := ValidateEventSig(fmt.Sprintf("Test(%s)", out)) + // require.NoError(t, err) + // require.True(t, ok) + } +} diff --git a/ethcoder/events_test.go b/ethcoder/events_test.go index b94d9551..a81925bc 100644 --- a/ethcoder/events_test.go +++ b/ethcoder/events_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestEventTopicHash(t *testing.T) { +func TestEventTopicHash1(t *testing.T) { in := []struct { event string }{ @@ -39,6 +39,58 @@ func TestEventTopicHash(t *testing.T) { } } +func TestEventTopicHash2(t *testing.T) { + in := []struct { + event string + }{ + {"ERC721SellOrderFilled(bytes32,address,address,uint256,address,uint256,(address,uint256)[],address,uint256)"}, + {"ERC721SellOrderFilled(bytes32 indexed ok,address ok,address ok,uint256 ok,address ok,uint256 ok,(address,uint256)[] ok,address ok,uint256 ok)"}, + {"ERC721SellOrderFilled(bytes32 num, address from,address to, uint256 num,address rec,uint256 n, ( address op, uint256 val )[] array, address, uint256 ) "}, + } + + for _, x := range in { + topicHash, eventSig, err := ethcoder.EventTopicHash(x.event) + require.NoError(t, err) + require.Equal(t, "0x9c248aa1a265aa616f707b979d57f4529bb63a4fc34dc7fc61fdddc18410f74e", topicHash.String()) + require.Equal(t, "ERC721SellOrderFilled(bytes32,address,address,uint256,address,uint256,(address,uint256)[],address,uint256)", eventSig) + } + + for _, x := range in { + eventDef, err := ethcoder.ParseEventDef(x.event) + require.NoError(t, err) + require.Equal(t, "0x9c248aa1a265aa616f707b979d57f4529bb63a4fc34dc7fc61fdddc18410f74e", eventDef.TopicHash) + require.Equal(t, "ERC721SellOrderFilled", eventDef.Name) + require.Equal(t, "ERC721SellOrderFilled(bytes32,address,address,uint256,address,uint256,(address,uint256)[],address,uint256)", eventDef.Sig) + require.Equal(t, []string{"bytes32", "address", "address", "uint256", "address", "uint256", "(address,uint256)[]", "address", "uint256"}, eventDef.ArgTypes) + } +} + +func TestEventTopicHash3(t *testing.T) { + in := []struct { + event string + }{ + {"NftItemCreated(uint256,uint32,address,bool,uint32,address,address[],uint32[])"}, + {"NftItemCreated(uint256 num,uint32 val,address from,bool flag, uint32 param, address from, address[] friends, uint32[] nums )"}, + {"NftItemCreated(uint256,uint32,address op,bool,uint32,address,address[],uint32[] nums)"}, + } + + for _, x := range in { + topicHash, eventSig, err := ethcoder.EventTopicHash(x.event) + require.NoError(t, err) + require.Equal(t, "0x041b7d65461f6f51e8fd92623a3848b22ce7077c215e4ea064d790e9efa08b8f", topicHash.String()) + require.Equal(t, "NftItemCreated(uint256,uint32,address,bool,uint32,address,address[],uint32[])", eventSig) + } + + for _, x := range in { + eventDef, err := ethcoder.ParseEventDef(x.event) + require.NoError(t, err) + require.Equal(t, "0x041b7d65461f6f51e8fd92623a3848b22ce7077c215e4ea064d790e9efa08b8f", eventDef.TopicHash) + require.Equal(t, "NftItemCreated", eventDef.Name) + require.Equal(t, "NftItemCreated(uint256,uint32,address,bool,uint32,address,address[],uint32[])", eventDef.Sig) + require.Equal(t, []string{"uint256", "uint32", "address", "bool", "uint32", "address", "address[]", "uint32[]"}, eventDef.ArgTypes) + } +} + func TestDecodeTransactionLogByContractABIJSON(t *testing.T) { logTopics := []string{ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", @@ -198,13 +250,14 @@ func TestDecodeTransactionLogByEventSig4(t *testing.T) { var eventSig = "Swap (address sender, address recipient, int256 amount0, int256 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick)" - eventDef, eventValues, ok, err := ethcoder.DecodeTransactionLogByEventSig(txnLog, eventSig, true) // use generics...? + eventDef, eventValues, ok, err := ethcoder.DecodeTransactionLogByEventSig(txnLog, eventSig, true) require.NoError(t, err) require.True(t, ok) require.Equal(t, "0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67", eventDef.TopicHash) require.Equal(t, "Swap", eventDef.Name) require.Equal(t, "Swap(address,address,int256,int256,uint160,uint128,int24)", eventDef.Sig) - require.Equal(t, []string{"sender", "recipient", "amount0", "amount1", "sqrtPriceX96", "liquidity", "tick"}, eventDef.ArgNames) + require.Equal(t, []string{"address", "address", "int256", "int256", "uint160", "uint128", "int24"}, eventDef.ArgTypes) + require.Equal(t, []string{"", "", "", "", "", "", ""}, eventDef.ArgNames) require.Equal(t, "0xec7be89e9d109e7e3fec59c222cf297125fefda2", eventValues[0]) require.Equal(t, "0xec7be89e9d109e7e3fec59c222cf297125fefda2", eventValues[1]) require.Equal(t, "0x0000000000000000000000000000000000000000000000100f4b6d6675790000", eventValues[2]) @@ -223,3 +276,67 @@ func TestDecodeTransactionLogByEventSig4(t *testing.T) { dataCheck = "0x" + dataCheck require.Equal(t, logData, dataCheck) } + +func TestDecodeTransactionLogByEventSig5(t *testing.T) { + logTopics := []string{ + "0x9c248aa1a265aa616f707b979d57f4529bb63a4fc34dc7fc61fdddc18410f74e", + } + logData := "0x714e9ffe0a4ab971954fe26f6021c8a9bb92e332a93d63b039f16b58be2eb61c0000000000000000000000004efca6d4d5f355ca7955d0024b5b35ae5aadf372000000000000000000000000cee077ea790a32927c49c6294c392404d0d31c0a0000000000000000000000000000000000000000000000000000000000000005000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000000000000000000000000000004fefa17b724000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000005d666f215a85b87cb042d59662a7ecd2c8cc44e6000000000000000000000000000000000000000000000000000000000231ce230000000000000000000000000000000000000000000000000000000000000001000000000000000000000000d207842d66b715df6ea08cf52f025b9e2ed287880000000000000000000000000000000000000000000000000019945ca2620000" + + txnLog := types.Log{} + txnLog.Topics = []common.Hash{} + + for _, topic := range logTopics { + txnLog.Topics = append(txnLog.Topics, common.HexToHash(topic)) + } + txnLog.Data, _ = hexutil.Decode(logData) + + var eventSig = "ERC721SellOrderFilled(bytes32,address,address,uint256,address,uint256,(address,uint256)[],address,uint256)" + + eventDef, eventValues, ok, err := ethcoder.DecodeTransactionLogByEventSig(txnLog, eventSig, true) + require.NoError(t, err) + require.True(t, ok) + require.Equal(t, "0x9c248aa1a265aa616f707b979d57f4529bb63a4fc34dc7fc61fdddc18410f74e", eventDef.TopicHash) + require.Equal(t, "ERC721SellOrderFilled", eventDef.Name) + require.Equal(t, "ERC721SellOrderFilled(bytes32,address,address,uint256,address,uint256,(address,uint256)[],address,uint256)", eventDef.Sig) + require.Equal(t, []string{"", "", "", "", "", "", "", "", ""}, eventDef.ArgNames) + require.Equal(t, 9, len(eventValues)) + + require.Equal(t, "0x714e9ffe0a4ab971954fe26f6021c8a9bb92e332a93d63b039f16b58be2eb61c", eventValues[0]) + require.Equal(t, "0x0000000000000000000000004efca6d4d5f355ca7955d0024b5b35ae5aadf372", eventValues[1]) + require.Equal(t, "0x000000000000000000000000cee077ea790a32927c49c6294c392404d0d31c0a", eventValues[2]) + require.Equal(t, "0x0000000000000000000000000000000000000000000000000000000000000005", eventValues[3]) + require.Equal(t, "0x000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", eventValues[4]) + require.Equal(t, "0x00000000000000000000000000000000000000000000000004fefa17b7240000", eventValues[5]) + require.Equal(t, "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000d207842d66b715df6ea08cf52f025b9e2ed287880000000000000000000000000000000000000000000000000019945ca2620000", eventValues[6]) + require.Equal(t, "0x0000000000000000000000005d666f215a85b87cb042d59662a7ecd2c8cc44e6", eventValues[7]) + require.Equal(t, "0x000000000000000000000000000000000000000000000000000000000231ce23", eventValues[8]) + + // spew.Dump(eventValues) + + // is this correct...? 12 values .. looks weird + // expectedValues := []string{ + // "0x714e9ffe0a4ab971954fe26f6021c8a9bb92e332a93d63b039f16b58be2eb61c", + // "0x0000000000000000000000004efca6d4d5f355ca7955d0024b5b35ae5aadf372", + // "0x000000000000000000000000cee077ea790a32927c49c6294c392404d0d31c0a", + // "0x0000000000000000000000000000000000000000000000000000000000000005", + // "0x000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + // "0x00000000000000000000000000000000000000000000000004fefa17b7240000", + // "0x0000000000000000000000000000000000000000000000000000000000000120", // ??? + // "0x0000000000000000000000005d666f215a85b87cb042d59662a7ecd2c8cc44e6", + // "0x000000000000000000000000000000000000000000000000000000000231ce23", + // "0x0000000000000000000000000000000000000000000000000000000000000001", // .. + // "0x000000000000000000000000d207842d66b715df6ea08cf52f025b9e2ed28788", // .. + // "0x0000000000000000000000000000000000000000000000000019945ca2620000", // .. + // } + + // NOTE: this does not pass, because the order of the values is not the same + // dataCheck := "" + // for i := 0; i < len(eventValues); i++ { + // v := eventValues[i] + // s := v.(string) + // dataCheck += s[2:] + // } + // dataCheck = "0x" + dataCheck + // require.Equal(t, logData, dataCheck) +}