diff --git a/config.go b/config.go index a100318..76a7bf9 100644 --- a/config.go +++ b/config.go @@ -391,6 +391,8 @@ type EVMConfig struct { DenyList []string `toml:"deny_list"` + EnabledLegacySeiApis []string `toml:"enabled_legacy_sei_apis"` + MaxLogNoBlock int64 `toml:"max_log_no_block"` MaxBlocksForLog int64 `toml:"max_blocks_for_log"` MaxSubscriptionsNewHead uint64 `toml:"max_subscriptions_new_head"` diff --git a/config_test.go b/config_test.go index b499879..1efa196 100644 --- a/config_test.go +++ b/config_test.go @@ -227,6 +227,7 @@ func TestWriteReadRoundTrip(t *testing.T) { // so it does not round-trip through the legacy two-file format. original.Chain.Moniker = "test-node" original.EVM.HTTPPort = 9545 + original.EVM.EnabledLegacySeiApis = []string{"sei_getLogs", "sei_getBlockByNumber"} original.Storage.StateStore.KeepRecent = 50000 if err := WriteConfigToDir(original, dir); err != nil { @@ -253,6 +254,10 @@ func TestWriteReadRoundTrip(t *testing.T) { if loaded.EVM.HTTPPort != 9545 { t.Errorf("evm.http_port: got %d, want 9545", loaded.EVM.HTTPPort) } + if got := loaded.EVM.EnabledLegacySeiApis; len(got) != 2 || + got[0] != "sei_getLogs" || got[1] != "sei_getBlockByNumber" { + t.Errorf("evm.enabled_legacy_sei_apis: got %v, want [sei_getLogs sei_getBlockByNumber]", got) + } if loaded.Storage.StateStore.KeepRecent != 50000 { t.Errorf("state_store.keep_recent: got %d, want 50000", loaded.Storage.StateStore.KeepRecent) } @@ -530,6 +535,43 @@ func TestApplyOverrides_StringSliceOverwritesDefault(t *testing.T) { } } +// TestStringSliceMissingKeyDecodesAsNil locks the asymmetry between +// "field absent in app.toml" and "field present as empty list": +// +// nil -> key omitted from written TOML -> reads back as nil +// []string{} -> "field = []" in written TOML -> reads back as []string{} +// +// Downstream consumers branching on nil vs non-nil-empty (e.g. "was this +// configured at all?") rely on this. BurntSushi/toml v1.5.0 honors the +// distinction; a future encoder change that normalized nil and empty +// would silently break that branch. +func TestStringSliceMissingKeyDecodesAsNil(t *testing.T) { + cfg := DefaultForMode(ModeFull) + cfg.EVM.EnabledLegacySeiApis = nil + + dir := t.TempDir() + if err := WriteConfigToDir(cfg, dir); err != nil { + t.Fatalf("WriteConfigToDir: %v", err) + } + + appToml, err := os.ReadFile(filepath.Join(dir, "config", "app.toml")) + if err != nil { + t.Fatalf("read app.toml: %v", err) + } + if strings.Contains(string(appToml), "enabled_legacy_sei_apis") { + t.Errorf("nil slice should be omitted from app.toml; got it written") + } + + loaded, err := ReadConfigFromDir(dir) + if err != nil { + t.Fatalf("ReadConfigFromDir: %v", err) + } + if loaded.EVM.EnabledLegacySeiApis != nil { + t.Errorf("missing key should decode as nil; got %v (len %d)", + loaded.EVM.EnabledLegacySeiApis, len(loaded.EVM.EnabledLegacySeiApis)) + } +} + func TestApplyOverrides_StringSliceRoundTripTOML(t *testing.T) { dir := t.TempDir() @@ -604,6 +646,17 @@ func TestResolveEnv_LegacyPrefix(t *testing.T) { } } +func TestResolveEnv_StringSlice(t *testing.T) { + cfg := Default() + t.Setenv("SEI_EVM_ENABLED_LEGACY_SEI_APIS", "sei_getLogs,sei_getBlockByNumber") + + ResolveEnv(cfg) + got := cfg.EVM.EnabledLegacySeiApis + if len(got) != 2 || got[0] != "sei_getLogs" || got[1] != "sei_getBlockByNumber" { + t.Errorf("after ResolveEnv: got %v, want [sei_getLogs sei_getBlockByNumber]", got) + } +} + func TestResolveEnv_SEIPrecedence(t *testing.T) { cfg := Default() t.Setenv("SEI_CHAIN_MIN_GAS_PRICES", "0.5usei") diff --git a/defaults.go b/defaults.go index 2a7a256..da8f80a 100644 --- a/defaults.go +++ b/defaults.go @@ -161,6 +161,7 @@ func baseDefaults() *SeiConfig { CheckTxTimeout: Dur(5 * time.Second), MaxTxPoolTxs: 1000, DenyList: []string{}, + EnabledLegacySeiApis: []string{}, MaxLogNoBlock: 10_000, MaxBlocksForLog: 2000, MaxSubscriptionsNewHead: 10_000, diff --git a/enrichments.go b/enrichments.go index 7ddebc9..5ba6ed2 100644 --- a/enrichments.go +++ b/enrichments.go @@ -295,6 +295,9 @@ func DefaultEnrichments() map[string][]FieldOption { WithDescription("RPC methods that should immediately fail (e.g. debug_traceBlockByNumber)."), WithHotReload(), }, + "evm.enabled_legacy_sei_apis": { + WithDescription("Deprecated sei_*/sei2_* JSON-RPC methods served on the EVM HTTP endpoint."), + }, "evm.max_log_no_block": { WithDescription("Maximum logs returned when block range is open-ended."), }, diff --git a/legacy.go b/legacy.go index b8c248c..5753923 100644 --- a/legacy.go +++ b/legacy.go @@ -311,6 +311,7 @@ type legacyEVM struct { MaxTxPoolTxs uint64 `toml:"max_tx_pool_txs"` Slow bool `toml:"slow"` DenyList []string `toml:"deny_list"` + EnabledLegacySeiApis []string `toml:"enabled_legacy_sei_apis"` MaxLogNoBlock int64 `toml:"max_log_no_block"` MaxBlocksForLog int64 `toml:"max_blocks_for_log"` MaxSubscriptionsNewHead uint64 `toml:"max_subscriptions_new_head"` @@ -628,6 +629,7 @@ func (cfg *SeiConfig) toLegacyApp() legacyAppConfig { MaxTxPoolTxs: cfg.EVM.MaxTxPoolTxs, Slow: cfg.EVM.Slow, DenyList: cfg.EVM.DenyList, + EnabledLegacySeiApis: cfg.EVM.EnabledLegacySeiApis, MaxLogNoBlock: cfg.EVM.MaxLogNoBlock, MaxBlocksForLog: cfg.EVM.MaxBlocksForLog, MaxSubscriptionsNewHead: cfg.EVM.MaxSubscriptionsNewHead, @@ -895,6 +897,7 @@ func fromLegacy(tm legacyTendermintConfig, app legacyAppConfig) *SeiConfig { MaxTxPoolTxs: app.EVM.MaxTxPoolTxs, Slow: app.EVM.Slow, DenyList: app.EVM.DenyList, + EnabledLegacySeiApis: app.EVM.EnabledLegacySeiApis, MaxLogNoBlock: app.EVM.MaxLogNoBlock, MaxBlocksForLog: app.EVM.MaxBlocksForLog, MaxSubscriptionsNewHead: app.EVM.MaxSubscriptionsNewHead, diff --git a/registry_test.go b/registry_test.go index 0b9416b..589ab92 100644 --- a/registry_test.go +++ b/registry_test.go @@ -86,6 +86,7 @@ func TestBuildRegistry_FieldTypes(t *testing.T) { {"mempool.drop_priority_threshold", FieldTypeFloat}, {"network.rpc.timeout_broadcast_tx_commit", FieldTypeDuration}, {"tx_index.indexer", FieldTypeStringSlice}, + {"evm.enabled_legacy_sei_apis", FieldTypeStringSlice}, } for _, tt := range tests {