Skip to content
Open
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
4 changes: 2 additions & 2 deletions processors/contract/contract_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ func (t *TransformContractDataStruct) TransformContractData(ledgerChange ingest.

ledgerSequence := header.Header.LedgerSeq

outputKey, outputKeyDecoded := SerializeScVal(contractData.Key)
outputVal, outputValDecoded := SerializeScVal(contractData.Val)
outputKey, outputKeyDecoded := contractData.Key.Serialize()
outputVal, outputValDecoded := contractData.Val.Serialize()

outputContractDataXDR, err := xdr.MarshalBase64(contractData)
if err != nil {
Expand Down
77 changes: 10 additions & 67 deletions processors/contract/contract_events.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package contract

import (
"encoding/base64"
"fmt"
"time"

"github.com/stellar/go/ingest"
"github.com/stellar/go/processors/utils"
"github.com/stellar/go/strkey"
"github.com/stellar/go/toid"
"github.com/stellar/go/xdr"
)
Expand Down Expand Up @@ -65,20 +63,17 @@ func TransformContractEvent(transaction ingest.LedgerTransaction, lhe xdr.Ledger
outputType := event.Type
outputTypeString := event.Type.String()

eventTopics := getEventTopics(event.Body)
outputTopics, outputTopicsDecoded := SerializeScValArray(eventTopics)
eventTopics := event.Body.GetTopics()
outputTopics, outputTopicsDecoded := xdr.SerializeScValArray(eventTopics)
outputTopicsJson["topics"] = outputTopics
outputTopicsDecodedJson["topics_decoded"] = outputTopicsDecoded

eventData := getEventData(event.Body)
outputData, outputDataDecoded := SerializeScVal(eventData)
eventData := event.Body.GetData()
outputData, outputDataDecoded := eventData.Serialize()

// Convert the xdrContactId to string
// TODO: https://stellarorg.atlassian.net/browse/HUBBLE-386 this should be a stellar/go/xdr function
// Convert the xdr ContractId to string
if event.ContractId != nil {
contractId := *event.ContractId
contractIdByte, _ := contractId.MarshalBinary()
outputContractId, _ = strkey.Encode(strkey.VersionByteContract, contractIdByte)
outputContractId, _ = event.ContractId.ToContractAddress()
}

outputContractEventXDR, err := xdr.MarshalBase64(contractEvent)
Expand Down Expand Up @@ -112,60 +107,8 @@ func TransformContractEvent(transaction ingest.LedgerTransaction, lhe xdr.Ledger
return transformedContractEvents, nil
}

// TODO this should be a stellar/go/xdr function
func getEventTopics(eventBody xdr.ContractEventBody) []xdr.ScVal {
switch eventBody.V {
case 0:
contractEventV0 := eventBody.MustV0()
return contractEventV0.Topics
default:
panic("unsupported event body version: " + string(eventBody.V))
}
}

// TODO this should be a stellar/go/xdr function
func getEventData(eventBody xdr.ContractEventBody) xdr.ScVal {
switch eventBody.V {
case 0:
contractEventV0 := eventBody.MustV0()
return contractEventV0.Data
default:
panic("unsupported event body version: " + string(eventBody.V))
}
}

// TODO this should also be used in the operations processor
func SerializeScVal(scVal xdr.ScVal) (map[string]string, map[string]string) {
serializedData := map[string]string{}
serializedData["value"] = "n/a"
serializedData["type"] = "n/a"

serializedDataDecoded := map[string]string{}
serializedDataDecoded["value"] = "n/a"
serializedDataDecoded["type"] = "n/a"

if scValTypeName, ok := scVal.ArmForSwitch(int32(scVal.Type)); ok {
serializedData["type"] = scValTypeName
serializedDataDecoded["type"] = scValTypeName
if raw, err := scVal.MarshalBinary(); err == nil {
serializedData["value"] = base64.StdEncoding.EncodeToString(raw)
serializedDataDecoded["value"] = scVal.String()
}
}

return serializedData, serializedDataDecoded
}

// TODO this should also be used in the operations processor
func SerializeScValArray(scVals []xdr.ScVal) ([]map[string]string, []map[string]string) {
data := make([]map[string]string, 0, len(scVals))
dataDecoded := make([]map[string]string, 0, len(scVals))
// getEventTopics and getEventData functions have been moved to xdr/event.go
// as GetTopics() and GetData() methods on ContractEventBody

for _, scVal := range scVals {
serializedData, serializedDataDecoded := SerializeScVal(scVal)
data = append(data, serializedData)
dataDecoded = append(dataDecoded, serializedDataDecoded)
}

return data, dataDecoded
}
// SerializeScVal and SerializeScValArray have been moved to xdr/scval_helpers.go
// as methods on ScVal for better reusability across processors
22 changes: 22 additions & 0 deletions xdr/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,25 @@ func (eb ContractEventBody) String() string {
func (de DiagnosticEvent) String() string {
return fmt.Sprintf("%s, successful call: %t", de.Event, de.InSuccessfulContractCall)
}

// GetTopics extracts the topics from a contract event body.
// This is a helper function to abstract the versioning of ContractEventBody.
func (eb ContractEventBody) GetTopics() []ScVal {
switch eb.V {
case 0:
return eb.MustV0().Topics
default:
panic("unsupported event body version: " + fmt.Sprintf("%d", eb.V))
Copy link
Preview

Copilot AI Aug 9, 2025

Choose a reason for hiding this comment

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

The panic message uses string concatenation with fmt.Sprintf which is inefficient. Consider using fmt.Sprintf directly: panic(fmt.Sprintf("unsupported event body version: %d", eb.V))

Suggested change
panic("unsupported event body version: " + fmt.Sprintf("%d", eb.V))
panic(fmt.Sprintf("unsupported event body version: %d", eb.V))

Copilot uses AI. Check for mistakes.

}
}

// GetData extracts the data from a contract event body.
// This is a helper function to abstract the versioning of ContractEventBody.
func (eb ContractEventBody) GetData() ScVal {
switch eb.V {
case 0:
return eb.MustV0().Data
default:
panic("unsupported event body version: " + fmt.Sprintf("%d", eb.V))
Copy link
Preview

Copilot AI Aug 9, 2025

Choose a reason for hiding this comment

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

The panic message uses string concatenation with fmt.Sprintf which is inefficient. Consider using fmt.Sprintf directly: panic(fmt.Sprintf("unsupported event body version: %d", eb.V))

Suggested change
panic("unsupported event body version: " + fmt.Sprintf("%d", eb.V))
panic(fmt.Sprintf("unsupported event body version: %d", eb.V))

Copilot uses AI. Check for mistakes.

Comment on lines +27 to +43
Copy link
Preview

Copilot AI Aug 9, 2025

Choose a reason for hiding this comment

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

Consider returning an error instead of panicking to allow callers to handle unsupported versions gracefully. Panicking can crash the application and makes error handling more difficult.

Suggested change
func (eb ContractEventBody) GetTopics() []ScVal {
switch eb.V {
case 0:
return eb.MustV0().Topics
default:
panic("unsupported event body version: " + fmt.Sprintf("%d", eb.V))
}
}
// GetData extracts the data from a contract event body.
// This is a helper function to abstract the versioning of ContractEventBody.
func (eb ContractEventBody) GetData() ScVal {
switch eb.V {
case 0:
return eb.MustV0().Data
default:
panic("unsupported event body version: " + fmt.Sprintf("%d", eb.V))
func (eb ContractEventBody) GetTopics() ([]ScVal, error) {
switch eb.V {
case 0:
return eb.MustV0().Topics, nil
default:
return nil, errors.New("unsupported event body version: " + fmt.Sprintf("%d", eb.V))
}
}
// GetData extracts the data from a contract event body.
// This is a helper function to abstract the versioning of ContractEventBody.
func (eb ContractEventBody) GetData() (ScVal, error) {
switch eb.V {
case 0:
return eb.MustV0().Data, nil
default:
return ScVal{}, errors.New("unsupported event body version: " + fmt.Sprintf("%d", eb.V))

Copilot uses AI. Check for mistakes.

Comment on lines +27 to +43
Copy link
Preview

Copilot AI Aug 9, 2025

Choose a reason for hiding this comment

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

Consider returning an error instead of panicking to allow callers to handle unsupported versions gracefully. Panicking can crash the application and makes error handling more difficult.

Suggested change
func (eb ContractEventBody) GetTopics() []ScVal {
switch eb.V {
case 0:
return eb.MustV0().Topics
default:
panic("unsupported event body version: " + fmt.Sprintf("%d", eb.V))
}
}
// GetData extracts the data from a contract event body.
// This is a helper function to abstract the versioning of ContractEventBody.
func (eb ContractEventBody) GetData() ScVal {
switch eb.V {
case 0:
return eb.MustV0().Data
default:
panic("unsupported event body version: " + fmt.Sprintf("%d", eb.V))
func (eb ContractEventBody) GetTopics() ([]ScVal, error) {
switch eb.V {
case 0:
return eb.MustV0().Topics, nil
default:
return nil, fmt.Errorf("unsupported event body version: %d", eb.V)
}
}
// GetData extracts the data from a contract event body.
// This is a helper function to abstract the versioning of ContractEventBody.
func (eb ContractEventBody) GetData() (ScVal, error) {
switch eb.V {
case 0:
return eb.MustV0().Data, nil
default:
return ScVal{}, fmt.Errorf("unsupported event body version: %d", eb.V)

Copilot uses AI. Check for mistakes.

}
}
12 changes: 11 additions & 1 deletion xdr/hash.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package xdr

import "encoding/hex"
import (
"encoding/hex"

"github.com/stellar/go/strkey"
)

func (h Hash) HexString() string {
return hex.EncodeToString(h[:])
Expand All @@ -17,3 +21,9 @@ func (s Hash) Equals(o Hash) bool {
}
return true
}

// ToContractAddress converts a Hash to a Stellar contract address string.
// Returns the contract address in strkey format (C...) and an error if encoding fails.
func (h Hash) ToContractAddress() (string, error) {
return strkey.Encode(strkey.VersionByteContract, h[:])
}
43 changes: 43 additions & 0 deletions xdr/scval_helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package xdr

import "encoding/base64"

// SerializeScVal converts an ScVal to two map representations:
// 1. A map with base64-encoded value and type
// 2. A map with human-readable decoded value and type
// This is useful for data export and analytics purposes.
func (scVal ScVal) Serialize() (map[string]string, map[string]string) {
serializedData := map[string]string{}
serializedData["value"] = "n/a"
serializedData["type"] = "n/a"

serializedDataDecoded := map[string]string{}
serializedDataDecoded["value"] = "n/a"
serializedDataDecoded["type"] = "n/a"

if scValTypeName, ok := scVal.ArmForSwitch(int32(scVal.Type)); ok {
serializedData["type"] = scValTypeName
serializedDataDecoded["type"] = scValTypeName
if raw, err := scVal.MarshalBinary(); err == nil {
serializedData["value"] = base64.StdEncoding.EncodeToString(raw)
serializedDataDecoded["value"] = scVal.String()
}
}

return serializedData, serializedDataDecoded
}

// SerializeScValArray converts an array of ScVal to two arrays of map representations.
// Each ScVal is serialized using the Serialize method.
func SerializeScValArray(scVals []ScVal) ([]map[string]string, []map[string]string) {
data := make([]map[string]string, 0, len(scVals))
dataDecoded := make([]map[string]string, 0, len(scVals))

for _, scVal := range scVals {
serializedData, serializedDataDecoded := scVal.Serialize()
data = append(data, serializedData)
dataDecoded = append(dataDecoded, serializedDataDecoded)
}

return data, dataDecoded
}