Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented event_decoders_test.go #20

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 160 additions & 0 deletions athena_abi/core.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package athena_abi

import (
"errors"
"log"
)

type StarknetABI struct {
ABIName *string
ClassHash []byte
Functions map[string]AbiFunction
Events map[string]AbiEvent
Constructor []AbiParameter
L1Handler *AbiFunction
ImplementedInterfaces map[string]AbiInterface
}

// Declare errors
var (
errParseDefinedTypes = errors.New("unable to parse defined types")
errParseInterfaces = errors.New("unable to parse interfaces")
errParseFunctions = errors.New("unable to parse functions")
errParseEvents = errors.New("unable to parse events")
errParseConstructor = errors.New("unable to parse constructor")
errParseL1Handler = errors.New("unable to parse L1 handler")
errParseImplementedInterfaces = errors.New("unable to parse implemented interfaces")
)

// Parse Starknet ABI from JSON
// @param abiJSON
// @param abiname
// @param classHash
func StarknetAbiFromJSON(abiJson []map[string]interface{}, abiName string, classHash []byte) (*StarknetABI, error) {
groupedAbi := GroupAbiByType(abiJson)

// Parse defined types (structs and enums)
definedTypes, err := ParseEnumsAndStructs(groupedAbi["type_def"])
if err != nil {
sortedDefs, errDef := TopoSortTypeDefs(groupedAbi["type_def"])
if errDef == nil {
defineTypes, errDtypes := ParseEnumsAndStructs(sortedDefs)
definedTypes = defineTypes
errDef = errDtypes
}
if errDef != nil {
return nil, errParseDefinedTypes
}
log.Println("ABI Struct and Enum definitions out of order & required topological sorting")
}

// Parse interfaces
var definedInterfaces []AbiInterface
for _, iface := range groupedAbi["interface"] {
functions := []AbiFunction{}
for _, funcData := range iface["items"].([]interface{}) {
parsedAbi, errWhileParsing := ParseAbiFunction(funcData.(map[string]interface{}), definedTypes)
if errWhileParsing != nil {
return nil, errParseInterfaces
}
functions = append(functions, *parsedAbi)
}
definedInterfaces = append(definedInterfaces, AbiInterface{
name: iface["name"].(string),
functions: functions,
})
}

// Parse functions
functions := make(map[string]AbiFunction)
for _, functionData := range groupedAbi["function"] {
funcName := functionData["name"].(string)
abiFunc, errParsingFunctions := ParseAbiFunction(functionData, definedTypes)
if errParsingFunctions != nil {
return nil, errParseFunctions
}
functions[funcName] = *abiFunc
}

// Add functions from interfaces
for _, iface := range definedInterfaces {
for _, function := range iface.functions {
functions[function.name] = function
}
}

// Parse events
parsedAbiEvents := []AbiEvent{}
for _, eventData := range groupedAbi["event"] {
parsedEvent, errParsingEvent := ParseAbiEvent(eventData, definedTypes)
if errParsingEvent != nil {
return nil, errParseEvents
}
parsedAbiEvents = append(parsedAbiEvents, *parsedEvent)
}

events := make(map[string]AbiEvent)
for _, event := range parsedAbiEvents {
if event.name != "" {
events[event.name] = event
}
}

// Parse constructor
var constructor []AbiParameter
if len(groupedAbi["constructor"]) == 1 {
for _, paramData := range groupedAbi["constructor"][0]["inputs"].([]interface{}) {
param := paramData.(map[string]interface{})
typed, errorParsingType := parseType(param["type"].(string), definedTypes)
if errorParsingType != nil {
return nil, errParseConstructor
}
constructor = append(constructor, AbiParameter{
Name: param["name"].(string),
Type: typed,
})
}
} else {
constructor = nil
}

// Parse L1 handler
var l1Handler *AbiFunction
if len(groupedAbi["l1_handler"]) == 1 {
handler, errorParsingFunction := ParseAbiFunction(groupedAbi["l1_handler"][0], definedTypes)
if errorParsingFunction != nil {
return nil, errParseL1Handler
}
l1Handler = handler
} else {
l1Handler = nil
}

// Parse implemented interfaces
implementedInterfaces := make(map[string]AbiInterface)
implArray, ok := groupedAbi["impl"]
if !ok {
return nil, errParseImplementedInterfaces
}
for _, implData := range implArray {
implMap := implData
if ifaceName, ok := implMap["interface_name"].(string); ok {
for _, iface := range definedInterfaces {
if iface.name == ifaceName {
implementedInterfaces[iface.name] = iface
}
}
}
}

// Return the populated StarknetAbi struct
return &StarknetABI{
ABIName: &abiName,
ClassHash: classHash,
Functions: functions,
Events: events,
Constructor: constructor,
L1Handler: l1Handler,
ImplementedInterfaces: implementedInterfaces,
}, nil
}
96 changes: 96 additions & 0 deletions athena_abi/event_decoders_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package athena_abi

import (
"math/big"
"testing"

"github.com/stretchr/testify/assert"
)

func TestEventDecode(t *testing.T) {
tests := []struct {
name string
abiEvent *AbiEvent
eventData []*big.Int
eventKeys []*big.Int
expected map[string]interface{}
}{
{
name: "Approval Event",
abiEvent: NewAbiEvent(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How did you get this ? In the original code, I saw that they have loaded an abi for the testing purpose.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, Lets wait for core.go to be implemented. Then you can use from_json function from it.

Copy link
Contributor Author

@x-senpai-x x-senpai-x Sep 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Sh0g0-1758 Do I need to incorporate starknetabifromjson?

"Approval",
[]string{"owner", "spender", "value"},
map[string]StarknetType{"value": U256},
map[string]StarknetType{"owner": U256, "spender": U256},
"erc20_key_events",
),
eventData: []*big.Int{
mustParseBigInt("C95E3D845779376FED50", 16),
big.NewInt(0),
},
eventKeys: []*big.Int{
mustParseBigInt("0134692B230B9E1FFA39098904722134159652B09C5BC41D88D6698779D228FF", 16), // Event ID
mustParseBigInt("060CAFC0B0E66067B3A4978E93552DE54E0CAEEB82A352A202E0DC79A41459B6", 16), // Owner low
big.NewInt(0), // Owner high
mustParseBigInt("04270219D365D6B017231B52E92B3FB5D7C8378B05E9ABC97724537A80E93B0F", 16), // Spender low
big.NewInt(0), // Spender high
},
expected: map[string]interface{}{
"owner": mustParseBigInt("060CAFC0B0E66067B3A4978E93552DE54E0CAEEB82A352A202E0DC79A41459B6", 16),
"spender": mustParseBigInt("04270219D365D6B017231B52E92B3FB5D7C8378B05E9ABC97724537A80E93B0F", 16),
"value": mustParseBigInt("C95E3D845779376FED50", 16), //Doubt here needs review
},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
decodedEvent, err := test.abiEvent.Decode(test.eventData, test.eventKeys)
if err != nil {
t.Fatalf("Error decoding event: %v\nEvent Data: %v\nEvent Keys: %v", err, test.eventData, test.eventKeys)
}

assert.Equal(t, test.abiEvent.abiName, decodedEvent.abiName)
assert.Equal(t, test.abiEvent.name, decodedEvent.name)

for key, expectedValue := range test.expected {
actualValue, exists := decodedEvent.data[key]
assert.True(t, exists, "Key %s not found in decoded event", key)
if !exists {
continue
}

switch expected := expectedValue.(type) {
case string:
actual, ok := actualValue.(*big.Int)
assert.True(t, ok, "Expected *big.Int for key %s, got %T", key, actualValue)
if ok {
assert.Equal(t, expected, bigIntToAddress(actual))
}
case *big.Int:
actual, ok := actualValue.(*big.Int)
assert.True(t, ok, "Expected *big.Int for key %s, got %T", key, actualValue)
if ok {
assert.Equal(t, 0, expected.Cmp(actual), "For key %s, expected %s, got %s", key, expected.String(), actual.String())
}
default:
t.Errorf("Unexpected type for key %s: %T", key, expectedValue)
}
}
})
}
}

// Helper function to parse big integers
func mustParseBigInt(s string, base int) *big.Int {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move these helper functions to uitls.go

n, ok := new(big.Int).SetString(s, base)
if !ok {
panic("Failed to parse big integer: " + s)
}
return n
}

// Helper function to convert big.Int to address string
func bigIntToAddress(n *big.Int) string {
return "0x" + n.Text(16)
}
Loading