add methods to abi that allow for the generic unpacking of event logs into map[string]interface{}#18440
Conversation
9bc835a to
ca1c41c
Compare
…il [PR](ethereum/go-ethereum#18440) is accepted (if it is accepted)
ca1c41c to
7ecf541
Compare
|
Any interest in this PR? I can make the tests more thorough if that would help. |
…ps (allows for agnostic unpacking of logs)
gballet
left a comment
There was a problem hiding this comment.
Thank you for your contribution. My comments are mostly style and requesting a more unit tests.
There was a problem hiding this comment.
Thanks for those tests. I think there should be a couple more:
- test the "receive" function for inputs, check nothing ends up in the map
- add a function with only outputs, check its values end up in the map
- add one with both inputs and outputs, check only outputs values end up in the map
- add a test with a naming conflict
Could you also please break this function into several, each testing a different aspect? This way, when it fails we know exactly where the issue is.
There was a problem hiding this comment.
is the String() conversion necessary?
There was a problem hiding this comment.
Yeah, as a pointer they aren't pointing to the same big.Int, so even though they have the same value they won't be equivalent.
|
Thanks for the review @gballet!! I will make the requested changes shortly. |
…cking event with indexed string
9d2039a to
d883113
Compare
|
@i-norden I've made a partial review of your changes. There are some further issues with the lint build: Nothing terminal, just some coding style changes to satisfy the Most Enlightened Golang Linter:tm: |
|
Quick question/feedback: Why don't we fit this into the if reflect.ValueOf(v).Kind() == reflect.Map {
return arguments.unpackMap(v, marshalledValues)
}in Also; just making sure - this would work when unpacking arguments to / from function calls as well? EDIT: forgot the reflect.ValueOf call in the example |
There was a problem hiding this comment.
To fix the linter error, you need to replace this with fmt.Errorf("unsupported...
There was a problem hiding this comment.
to fix the linter issue, you need to write it as !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) instead
|
@i-norden could you please fix the linter issues? I would do it myself but you have not allowed me to update your branch; |
|
@gballet sorry for the delay, I've been distracted! On it now. I am also trying to tease out a bug while using this on live data with https://github.com/vulcanize/account_transformers. I'm not yet sure if it is an issue specific to this new method but I am coming across @mewwts This method can only be used to unpack method outputs, similar to the We could embed this within func (abi ABI) Unpack(v interface{}, name string, data []byte) (err error) {
if len(data) == 0 {
return fmt.Errorf("abi: unmarshalling empty output")
}
vMap, ok := v.(map[string]interface{})
if ok {
if method, ok := abi.Methods[name]; ok {
if len(data)%32 != 0 {
return fmt.Errorf("abi: improperly formatted output")
}
return method.Outputs.UnpackIntoMap(vMap, data)
}
if event, ok := abi.Events[name]; ok {
return event.Inputs.UnpackIntoMap(vMap, data)
}
}
// since there can't be naming collisions with contracts and events,
// we need to decide whether we're calling a method or an event
if method, ok := abi.Methods[name]; ok {
if len(data)%32 != 0 {
return fmt.Errorf("abi: improperly formatted output: %s - Bytes: [%+v]", string(data), data)
}
return method.Outputs.Unpack(v, data)
}
if event, ok := abi.Events[name]; ok {
return event.Inputs.Unpack(v, data)
}
return fmt.Errorf("abi: could not locate named method or event")
}but I'm not sure it is preferable to having the separate method, the behavior is different enough that I think it makes sense to keep them separate. And it's super minor but there is a cost to the type assertion. @gballet do you have a preference here? Thanks! |
|
The error I was running into was caused by the event diverging from the standard and having the wrong indexed arguments, nothing wrong here. Made the linting changes and left as separate method for now. |
|
Sorry for the inaccuracy in my previous comment; my thinking was to expose this functionality in arguments by checking It could look something like func (arguments Arguments) Unpack(v interface{}, data []byte) error {
// make sure the passed value is arguments pointer
if reflect.Ptr != reflect.ValueOf(v).Kind() {
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
}
marshalledValues, err := arguments.UnpackValues(data)
if err != nil {
return err
}
if arguments.isTuple() {
return arguments.unpackTuple(v, marshalledValues)
}
if reflect.ValueOf(v).Kind() != reflect.Map {
return arguments.unpackMap(v, marshalledValues)
}
return arguments.unpackAtomic(v, marshalledValues[0])
} |
…il [PR](ethereum/go-ethereum#18440) is accepted (if it is accepted)
…ce{} (ethereum#18440)
Add methods that allow for the unpacking of event logs into maps (allows for agnostic unpacking of logs)
This PR introduces an
abi.UnpackIntoMapmethod which can be used to unpack an event without having had to first define astructwith matching field names to unpack it into. This allows for dynamic unpacking of event logs using a contract's ABI. Additionally, since the purpose of this method is to trade some type safety for flexibility, this method uses abind.parseTopicsIntoMapfunction which allows for unpacking of event logs which have an indexed string.While it makes sense to me that we want to exclude strings and other types of unbound size from
topics, there is nothing stopping this from being implemented on the contract side of things (and it is) so I think it would be good to have a method to handle this case. Again, since this PR creates a new method for unpacking, we can introduce this capability without weakening the safety of theabi.Unpackmethod.