Skip to content

Commit

Permalink
Fixed substreams init generated code when dealing with Ethereum ABI…
Browse files Browse the repository at this point in the history
… events containing array types.
  • Loading branch information
maoueh committed Jan 30, 2024
1 parent 8e451b2 commit 5c9067d
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 45 deletions.
6 changes: 4 additions & 2 deletions cmd/substreams/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,13 @@ var errInitUnsupportedProtocol = errors.New("unsupported protocol")

var initCmd = &cobra.Command{
Use: "init [<path>]",
Short: "Initialize a new, working Substreams project from scratch.",
Short: "Initialize a new, working Substreams project from scratch",
Long: cli.Dedent(`
Initialize a new, working Substreams project from scratch. The path parameter is optional,
with your current working directory being the default value.
If you have an Etherscan API Key, you can set it to "ETHERSCAN_API_KEY" environment variable, it will be used to fetch the ABIs and contract information.
If you have an Etherscan API Key, you can set it to "ETHERSCAN_API_KEY" environment variable, it will be used to
fetch the ABIs and contract information.
`),
RunE: runSubstreamsInitE,
Args: cobra.RangeArgs(0, 1),
Expand Down
4 changes: 2 additions & 2 deletions codegen/templates/ethereum/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions codegen/templates/ethereum/Makefile.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ endif

.PHONY: run
run: build
substreams run substreams.yaml map_events $(if $(START_BLOCK),-s $(START_BLOCK)) $(if $(STOP_BLOCK),-t $(STOP_BLOCK))
substreams run substreams.yaml $(if $(MODULE),$(MODULE),map_events) $(if $(START_BLOCK),-s $(START_BLOCK)) $(if $(STOP_BLOCK),-t $(STOP_BLOCK))

.PHONY: gui
gui: build
substreams gui substreams.yaml map_events $(if $(START_BLOCK),-s $(START_BLOCK)) $(if $(STOP_BLOCK),-t $(STOP_BLOCK))
substreams gui substreams.yaml $(if $(MODULE),$(MODULE),map_events) $(if $(START_BLOCK),-s $(START_BLOCK)) $(if $(STOP_BLOCK),-t $(STOP_BLOCK))

.PHONY: protogen
protogen:
Expand Down
4 changes: 2 additions & 2 deletions codegen/templates/ethereum/src/lib.rs.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ fn db_out(events: contract::Events) -> Result<DatabaseChanges, substreams::error
{{- $numberOfAttributes := len $rust.ProtoFieldTableChangesMap }}{{ if eq $numberOfAttributes 0 }};{{ end }}
{{- $i := 0 }}
{{- range $protoField, $changesToProtoConversion := $rust.ProtoFieldTableChangesMap }}
{{ $i = add $i 1 }}.set("{{$protoField}}", {{$changesToProtoConversion}}){{ if eq $i $numberOfAttributes }};{{ end }}
{{ $i = add $i 1 }}.{{$changesToProtoConversion.Setter}}("{{$protoField}}", {{$changesToProtoConversion.ValueAccessCode}}){{ if eq $i $numberOfAttributes }};{{ end }}
{{- end }}
});
{{- end }}
Expand All @@ -92,7 +92,7 @@ fn graph_out(events: contract::Events) -> Result<EntityChanges, substreams::erro
{{- $numberOfAttributes := len $rust.ProtoFieldTableChangesMap }}{{ if eq $numberOfAttributes 0 }};{{ end }}
{{- $i := 0 }}
{{- range $protoField, $changesToProtoConversion := $rust.ProtoFieldTableChangesMap }}
{{ $i = add $i 1 }}.set("{{$protoField}}", {{$changesToProtoConversion}}){{ if eq $i $numberOfAttributes }};{{ end }}
{{ $i = add $i 1 }}.set("{{$protoField}}", {{$changesToProtoConversion.ValueAccessCode}}){{ if eq $i $numberOfAttributes }};{{ end }}
{{- end }}
});
{{- end}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ fn db_{{ $contract.GetName }}_out(events: &contract::Events, tables: &mut Databa
.set("evt_block_number", evt.evt_block_number)
{{- $numberOfAttributes := len $rust.ProtoFieldTableChangesMap }}{{ if eq $numberOfAttributes 0 }};{{ end }}
{{- $i := 0 }}
{{- range $protoField, $databaseChangesToProtoConversion := $rust.ProtoFieldTableChangesMap }}
{{ $i = add $i 1 }}.set("{{$protoField}}", {{$databaseChangesToProtoConversion}}){{if eq $i $numberOfAttributes}};{{ end }}
{{- range $protoField, $changesToProtoConversion := $rust.ProtoFieldTableChangesMap }}
{{ $i = add $i 1 }}.{{$changesToProtoConversion.Setter}}("{{$protoField}}", {{$changesToProtoConversion.ValueAccessCode}}){{if eq $i $numberOfAttributes}};{{ end }}
{{- end}}
});
{{- end}}
Expand All @@ -86,8 +86,8 @@ fn graph_{{ $contract.GetName }}_out(events: &contract::Events, tables: &mut Ent
.set("evt_block_number", evt.evt_block_number)
{{- $numberOfAttributes := len $rust.ProtoFieldTableChangesMap }}{{ if eq $numberOfAttributes 0 }};{{ end }}
{{- $i := 0 }}
{{- range $protoField, $databaseChangesToProtoConversion := $rust.ProtoFieldTableChangesMap }}
{{ $i = add $i 1 }}.set("{{$protoField}}", {{$databaseChangesToProtoConversion}}){{if eq $i $numberOfAttributes}};{{ end }}
{{- range $protoField, $changesToProtoConversion := $rust.ProtoFieldTableChangesMap }}
{{ $i = add $i 1 }}.set("{{$protoField}}", {{$changesToProtoConversion.ValueAccessCode}}){{if eq $i $numberOfAttributes}};{{ end }}
{{- end}}
});
{{- end}}
Expand Down
79 changes: 46 additions & 33 deletions codegen/templates/ethereum_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,19 +262,24 @@ type rustEventModel struct {
ProtoOutputModuleFieldName string
TableChangeEntityName string
ProtoFieldABIConversionMap map[string]string
ProtoFieldTableChangesMap map[string]string
ProtoFieldTableChangesMap map[string]tableChangeSetField
ProtoFieldSqlmap map[string]string
ProtoFieldClickhouseMap map[string]string
ProtoFieldGraphQLMap map[string]string
}

type tableChangeSetField struct {
Setter string
ValueAccessCode string
}

func (e *rustEventModel) populateFields(log *eth.LogEventDef, multipleContracts bool) error {
if len(log.Parameters) == 0 {
return nil
}

e.ProtoFieldABIConversionMap = map[string]string{}
e.ProtoFieldTableChangesMap = map[string]string{}
e.ProtoFieldTableChangesMap = map[string]tableChangeSetField{}
e.ProtoFieldSqlmap = map[string]string{}
e.ProtoFieldClickhouseMap = map[string]string{}
e.ProtoFieldGraphQLMap = map[string]string{}
Expand All @@ -296,11 +301,11 @@ func (e *rustEventModel) populateFields(log *eth.LogEventDef, multipleContracts
return fmt.Errorf("transform - field type %q on parameter with name %q is not supported right now", parameter.TypeName, parameter.Name)
}

toDatabaseChangeCode := generateFieldTableChangeCode(parameter.Type, "evt."+name, multipleContracts)
toDatabaseChangeSetter, toDatabaseChangeCode := generateFieldTableChangeCode(parameter.Type, "evt."+name, multipleContracts)
if toDatabaseChangeCode == SKIP_FIELD {
continue
}
if toDatabaseChangeCode == "" {
if toDatabaseChangeSetter == "" {
return fmt.Errorf("table change - field type %q on parameter with name %q is not supported right now", parameter.TypeName, parameter.Name)
}

Expand Down Expand Up @@ -328,7 +333,7 @@ func (e *rustEventModel) populateFields(log *eth.LogEventDef, multipleContracts
columnName := sanitizeTableChangesColumnNames(name)

e.ProtoFieldABIConversionMap[name] = toProtoCode
e.ProtoFieldTableChangesMap[name] = toDatabaseChangeCode
e.ProtoFieldTableChangesMap[name] = tableChangeSetField{Setter: toDatabaseChangeSetter, ValueAccessCode: toDatabaseChangeCode}
e.ProtoFieldSqlmap[columnName] = toSqlCode
e.ProtoFieldClickhouseMap[columnName] = toClickhouseCode
e.ProtoFieldGraphQLMap[name] = toGraphQLCode
Expand Down Expand Up @@ -424,12 +429,13 @@ func generateFieldClickhouseTypes(fieldType eth.SolidityType) string {
case eth.StructType:
return SKIP_FIELD

//case eth.ArrayType:
// elemType := generateFieldClickhouseTypes(v.ElementType)
// if elemType == "" || elemType == SKIP_FIELD {
// return elemType
// }
// return fmt.Sprintf("Array(%s)", elemType)
case eth.ArrayType:
elemType := generateFieldClickhouseTypes(v.ElementType)
if elemType == "" || elemType == SKIP_FIELD {
return SKIP_FIELD
}

return fmt.Sprintf("Array(%s)", elemType)

default:
return ""
Expand Down Expand Up @@ -465,55 +471,62 @@ func generateFieldSqlTypes(fieldType eth.SolidityType) string {
case eth.StructType:
return SKIP_FIELD

//case eth.ArrayType:
// elemType := generateFieldClickhouseTypes(v.ElementType)
// if elemType == "" || elemType == SKIP_FIELD {
// return elemType
// }
// return fmt.Sprintf("%s ARRAY", elemType)
case eth.ArrayType:
elemType := generateFieldSqlTypes(v.ElementType)
if elemType == "" || elemType == SKIP_FIELD {
return SKIP_FIELD
}

return elemType + "[]"

default:
return ""
}
}

func generateFieldTableChangeCode(fieldType eth.SolidityType, fieldAccess string, multipleContract bool) string {
func generateFieldTableChangeCode(fieldType eth.SolidityType, fieldAccess string, multipleContract bool) (setter string, valueAccessCode string) {
switch v := fieldType.(type) {
case eth.AddressType, eth.BytesType, eth.FixedSizeBytesType:
return fmt.Sprintf("Hex(&%s).to_string()", fieldAccess)
return "set", fmt.Sprintf("Hex(&%s).to_string()", fieldAccess)

case eth.BooleanType:
return fieldAccess
return "set", fieldAccess

case eth.StringType:
if multipleContract {
return fmt.Sprintf("&%s", fieldAccess)
return "set", fmt.Sprintf("&%s", fieldAccess)
}
return fieldAccess
return "set", fieldAccess

case eth.SignedIntegerType:
if v.ByteSize <= 8 {
return fieldAccess
return "set", fieldAccess
}
return fmt.Sprintf("BigDecimal::from_str(&%s).unwrap()", fieldAccess)
return "set", fmt.Sprintf("BigDecimal::from_str(&%s).unwrap()", fieldAccess)

case eth.UnsignedIntegerType:
if v.ByteSize <= 8 {
return fieldAccess
return "set", fieldAccess
}
return fmt.Sprintf("BigDecimal::from_str(&%s).unwrap()", fieldAccess)
return "set", fmt.Sprintf("BigDecimal::from_str(&%s).unwrap()", fieldAccess)

case eth.SignedFixedPointType, eth.UnsignedFixedPointType:
return fmt.Sprintf("BigDecimal::from_str(&%s).unwrap()", fieldAccess)
return "set", fmt.Sprintf("BigDecimal::from_str(&%s).unwrap()", fieldAccess)

case eth.ArrayType:
return SKIP_FIELD
// FIXME: Implement multiple contract support, check what is the actual semantics there
_, inner := generateFieldTableChangeCode(v.ElementType, "x", false)
if inner == SKIP_FIELD {
return SKIP_FIELD, SKIP_FIELD
}

return "set_psql_array", fmt.Sprintf("%s.into_iter().map(|x| %s).collect::<Vec<_>>()", fieldAccess, inner)

case eth.StructType:
return SKIP_FIELD
return SKIP_FIELD, SKIP_FIELD

default:
return ""
return "", ""
}
}

Expand Down Expand Up @@ -587,10 +600,10 @@ func generateFieldGraphQLTypes(fieldType eth.SolidityType) string {
case eth.SignedFixedPointType, eth.UnsignedFixedPointType:
return "BigDecimal!"

case eth.StructType:
return SKIP_FIELD

case eth.ArrayType:
return "[" + generateFieldGraphQLTypes(v.ElementType) + "]!"

case eth.StructType:
return SKIP_FIELD

default:
Expand Down
7 changes: 7 additions & 0 deletions docs/release-notes/change-log.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## v1.3.3

* Fixed `substreams init` generated code when dealing with Ethereum ABI events containing array types.

> [!NOTE]
> For now, the generated code only works with Postgres, an upcoming revision is going to lift that constraint.
## v1.3.2

* Fixed `store.has_at` Wazero signature which was defined as `has_at(storeIdx: i32, ord: i32, key_ptr: i32, key_len: i32)` but should have been `has_at(storeIdx: i32, ord: i64, key_ptr: i32, key_len: i32)`.
Expand Down

0 comments on commit 5c9067d

Please sign in to comment.