diff --git a/starknet-events/abi.go b/starknet-events/abi.go index 7edc782..b3887a2 100644 --- a/starknet-events/abi.go +++ b/starknet-events/abi.go @@ -1,26 +1,78 @@ package starknet_events import ( - starknetABI "github.com/dipdup-io/starknet-go-api/pkg/abi" + "encoding/json" + "github.com/streamingfast/substreams-codegen/loop" ) type ABI struct { - decodedAbi *starknetABI.Abi - raw string + decodedEvents StarknetEvents + raw string +} + +type StarknetEvents []*StarknetEvent + +type StarknetEvent struct { + CommonAttribute + + Variants []CommonAttribute `json:"variants"` } -type StarknetABI struct { +type OtherItem struct { + CommonAttribute +} +type CommonAttribute struct { + Type string `json:"type"` + Name string `json:"name"` + Kind string `json:"kind"` +} + +const ( + EventType = "event" +) + +func (s *StarknetEvents) ExtractEvents(data []byte) error { + var Attributes []CommonAttribute + if err := json.Unmarshal(data, &Attributes); err != nil { + return err + } + + items := make([]interface{}, 0) + + for _, attribute := range Attributes { + switch attribute.Type { + case EventType: + items = append(items, &StarknetEvent{}) + default: + items = append(items, &OtherItem{}) + } + } + + if err := json.Unmarshal(data, &items); err != nil { + return err + } + + for _, item := range items { + switch i := item.(type) { + case *StarknetEvent: + *s = append(*s, i) + default: + continue + } + } + + return nil } func CmdDecodeABI(contract *Contract) loop.Cmd { return func() loop.Msg { - contractABI := starknetABI.Abi{} - err := contractABI.UnmarshalJSON(contract.RawABI) + events := StarknetEvents{} + err := events.ExtractEvents(contract.RawABI) if err != nil { panic("decoding contract abi") } - return ReturnRunDecodeContractABI{Abi: &ABI{&contractABI, string(contract.RawABI)}, Err: err} + return ReturnRunDecodeContractABI{Abi: &ABI{events, string(contract.RawABI)}, Err: err} } } diff --git a/starknet-events/contract.go b/starknet-events/contract.go index 4dbb38c..61d7c1b 100644 --- a/starknet-events/contract.go +++ b/starknet-events/contract.go @@ -8,7 +8,6 @@ import ( "strings" "github.com/NethermindEth/juno/core/felt" - starknetRPC "github.com/NethermindEth/starknet.go/rpc" ) @@ -17,12 +16,19 @@ type Alias struct { NewName string } +func NewAlias(oldName, newName string) *Alias { + return &Alias{ + OldName: oldName, + NewName: newName, + } +} + type Contract struct { Name string `json:"name,omitempty"` Address string `json:"address"` InitialBlock *uint64 `json:"initialBlock"` - Aliases []Alias `json:"aliases"` + Aliases []*Alias `json:"aliases"` RawABI json.RawMessage `json:"rawAbi,omitempty"` Abi *ABI @@ -43,34 +49,50 @@ func (c *Contract) IdentifierCapitalize() string { return strings.ToUpper(string(c.Name[0])) + c.Name[1:] } func (c *Contract) SetAliases() { - events := c.Abi.decodedAbi.EventsBySelector + events := c.Abi.decodedEvents - aliases := make([]Alias, 0) + aliases := make([]*Alias, 0) seen := make(map[string]struct{}) - for _, eventItem := range events { - eventName := eventItem.Name - - splitEventName := strings.Split(eventName, "::") - - lastPart := splitEventName[len(splitEventName)-1] - - if _, found := seen[lastPart]; found { - if len(splitEventName) < 2 { - panic("parsed event name does not contain enough parts to have an alias") + // Based on Starknet documentation, we assume that in each contract, it exists a Event which is an enum containing all other events... (https://docs.starknet.io/architecture-and-concepts/smart-contracts/contract-abi/) + // Finding this "golden" event is not an easy path, as multiple enum with the same name can exist in the ABI... + // We need to detect the Golden Event to avoid applying Alias on it... + potentialsGoldenEvent := make(map[string]*StarknetEvent) + for _, event := range events { + eventName := event.Name + lastPart, newName := eventNameInfo(eventName) + + if lastPart == "Event" { + // Event which are not enum, we can safely apply alias + if event.Kind != "enum" { + alias := NewAlias(eventName, newName) + aliases = append(aliases, alias) + continue } - alias := Alias{ - OldName: eventName, - NewName: splitEventName[len(splitEventName)-2] + lastPart, - } + potentialsGoldenEvent[event.Name] = event + continue + } + if _, found := seen[lastPart]; found { + alias := NewAlias(eventName, newName) aliases = append(aliases, alias) } seen[lastPart] = struct{}{} } + if len(potentialsGoldenEvent) == 1 { + c.Aliases = aliases + return + } + + goldenName := detectGoldenEvent(potentialsGoldenEvent) + if goldenName == "" { + panic("no golden event found") + } + + aliases = setNonGoldenAliases(potentialsGoldenEvent, goldenName, aliases) c.Aliases = aliases } diff --git a/starknet-events/convo.go b/starknet-events/convo.go index 141feda..a1bbc7d 100644 --- a/starknet-events/convo.go +++ b/starknet-events/convo.go @@ -72,7 +72,7 @@ func (c *Convo) NextStep() loop.Cmd { return cmd(AskContractAddress{}) } - if contract.Abi == nil || contract.Abi.decodedAbi == nil { + if contract.Abi == nil || contract.Abi.decodedEvents == nil { // if the user pasted an empty ABI, we would restart the process or choosing a contract address if contract.emptyABI { contract.Address = "" // reset the address diff --git a/starknet-events/templates/src/lib.rs.gotmpl b/starknet-events/templates/src/lib.rs.gotmpl index ecb06e0..ca4aabf 100644 --- a/starknet-events/templates/src/lib.rs.gotmpl +++ b/starknet-events/templates/src/lib.rs.gotmpl @@ -51,7 +51,7 @@ fn map_{{ $contract.Identifier }}_events(transactions: Transactions) -> Result