Skip to content

Commit

Permalink
fix codegen starknet
Browse files Browse the repository at this point in the history
  • Loading branch information
ArnaudBger committed Oct 21, 2024
1 parent a473e76 commit 89df0a4
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 27 deletions.
66 changes: 59 additions & 7 deletions starknet-events/abi.go
Original file line number Diff line number Diff line change
@@ -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}
}
}
58 changes: 40 additions & 18 deletions starknet-events/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"strings"

"github.com/NethermindEth/juno/core/felt"

starknetRPC "github.com/NethermindEth/starknet.go/rpc"
)

Expand All @@ -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
Expand All @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion starknet-events/convo.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion starknet-events/templates/src/lib.rs.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ fn map_{{ $contract.Identifier }}_events(transactions: Transactions) -> Result<E
transaction_hash: Felt::default(),
};

if let {{ $contract.Identifier}}_event = {{ $contract.IdentifierCapitalize }}Event::try_from(emitted_event) {
if let {{ $contract.Identifier}}_event = {{ $contract.IdentifierCapitalize }}Event::try_from(emitted_event).unwrap() {
let event_json = serde_json::to_string(&{{ $contract.Identifier}}_event).unwrap();
let event = Event {
json_description: event_json
Expand Down
50 changes: 50 additions & 0 deletions starknet-events/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package starknet_events

import (
"strings"
)

func setNonGoldenAliases(potentialsGoldenEvent map[string]*StarknetEvent, goldenName string, aliases []*Alias) []*Alias {
for _, event := range potentialsGoldenEvent {
eventName := event.Name

if eventName == goldenName {
continue
}

_, newName := eventNameInfo(eventName)

alias := NewAlias(event.Name, newName)
aliases = append(aliases, alias)
}

return aliases
}

func eventNameInfo(eventName string) (lastPart, aliasName string) {
splitEventName := strings.Split(eventName, "::")
if len(splitEventName) < 2 {
panic("parsed event name does not contain enough parts to have an alias")
}

lastPart = splitEventName[len(splitEventName)-1]
return lastPart, splitEventName[len(splitEventName)-2] + lastPart
}

func detectGoldenEvent(potentialsGoldenEvent map[string]*StarknetEvent) string {
for _, event := range potentialsGoldenEvent {
seen := make(map[string]struct{})
for _, variant := range event.Variants {
if _, found := potentialsGoldenEvent[variant.Type]; found {
seen[variant.Type] = struct{}{}
}

// Equivalent: Current Event Enum contains all other potentials "golden" events
if len(seen) == (len(potentialsGoldenEvent) - 1) {
return event.Name
}
}
}

return ""
}

0 comments on commit 89df0a4

Please sign in to comment.