diff --git a/params/json.libevm.go b/params/json.libevm.go index c9acefe949738..5a517fba98086 100644 --- a/params/json.libevm.go +++ b/params/json.libevm.go @@ -124,3 +124,27 @@ func toJSONRawMessages(v any) (map[string]json.RawMessage, error) { } return msgs, nil } + +// UnmarshalChainConfig JSON decodes the given data into the config and the extra +// pointer arguments. The root JSON keys except the "extra" key are decoded into +// the config argument, and the object at the "extra" key, if present, is decoded +// into the extra argument. Note the extra argument must be a non-nil pointer or +// an error would be returned. +func UnmarshalChainConfig(data []byte, config *ChainConfig, extra any) (err error) { + err = json.Unmarshal(data, config) + if err != nil { + return fmt.Errorf("json decoding root chain config: %w", err) + } + + jsonExtra := struct { + Extra any `json:"extra"` + }{ + Extra: extra, + } + err = json.Unmarshal(data, &jsonExtra) + if err != nil { + return fmt.Errorf("json decoding extra chain config: %w", err) + } + + return nil +} diff --git a/params/json.libevm_test.go b/params/json.libevm_test.go index 70014e0d8d13c..202b0ae57938f 100644 --- a/params/json.libevm_test.go +++ b/params/json.libevm_test.go @@ -21,6 +21,7 @@ import ( "math/big" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/ava-labs/libevm/libevm/pseudo" @@ -144,3 +145,60 @@ func TestChainConfigJSONRoundTrip(t *testing.T) { }) } } + +func Test_UnmarshalChainConfig(t *testing.T) { + t.Parallel() + + type testExtra struct { + Field string `json:"field"` + } + + testCases := map[string]struct { + jsonData string // string for convenience + expectedConfig ChainConfig + expectedExtra any + errMessage string + }{ + "invalid_json": { + expectedExtra: testExtra{}, + errMessage: "json decoding root chain config: unexpected end of JSON input", + }, + "no_extra": { + jsonData: `{"chainId": 1}`, + expectedConfig: ChainConfig{ChainID: big.NewInt(1)}, + expectedExtra: testExtra{}, + }, + "wrong_extra_type": { + jsonData: `{"chainId": 1, "extra": 1}`, + expectedConfig: ChainConfig{ChainID: big.NewInt(1)}, + expectedExtra: testExtra{}, + errMessage: "json decoding extra chain config: " + + "json: cannot unmarshal number into Go struct field " + + ".extra of type params.testExtra", + }, + "extra_success": { + jsonData: `{"chainId": 1, "extra": {"field":"value"}}`, + expectedConfig: ChainConfig{ChainID: big.NewInt(1)}, + expectedExtra: testExtra{Field: "value"}, + }, + } + + for name, testCase := range testCases { + testCase := testCase + t.Run(name, func(t *testing.T) { + t.Parallel() + + data := []byte(testCase.jsonData) + config := ChainConfig{} + var extra testExtra + err := UnmarshalChainConfig(data, &config, &extra) + if testCase.errMessage == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, testCase.errMessage) + } + assert.Equal(t, testCase.expectedConfig, config) + assert.Equal(t, testCase.expectedExtra, extra) + }) + } +}