Skip to content

refactor: make simulator chain-agnostic with pluggable chain families#373

Draft
Fletch153 wants to merge 14 commits intomainfrom
plex-2751-modularise-chain-families
Draft

refactor: make simulator chain-agnostic with pluggable chain families#373
Fletch153 wants to merge 14 commits intomainfrom
plex-2751-modularise-chain-families

Conversation

@Fletch153
Copy link
Copy Markdown

@Fletch153 Fletch153 commented Apr 15, 2026

Summary

  • Introduces chain.ChainType interface and thread-safe registry enabling pluggable chain support (Aptos, Solana future)
  • Extracts all EVM-specific simulation code into cmd/workflow/simulate/chain/evm/ with init() self-registration
  • Refactors simulate.go to iterate registered chain types instead of hardcoding EVM logic
  • No user-facing CLI changes — pure internal refactor
  • Adds evm.EVMLimits sub-interface so EVM-specific gas limits do not pollute the chain-agnostic chain.Limits
  • Adds chain-agnostic chain.StartAll/chain.CloseAll lifecycle helpers with rewind-on-partial-failure semantics
  • Adds chain.ResolvedChains so chain-type-private state (e.g. EVM experimental-selector set) flows through return values instead of hidden struct fields
  • Fixes pre-existing cleanup gaps: every fatal-init os.Exit(1) path now closes services that had already started

Architecture

chain/
  registry.go      — ChainType interface + Register/Build/Get/All/Names + Close() hook
  types.go         — ChainClient, ChainConfig, CapabilityConfig, Limits, ResolvedChains
  lifecycle.go     — StartAll/CloseAll helpers (rewind + errors.Join)
  utils.go         — RedactURL
  evm/
    chaintype.go   — EVMChainType implementing chain.ChainType
    capabilities.go — EVM chain capability servers + Start/Close delegating to chain.StartAll/CloseAll
    health.go      — RPC health check
    trigger.go     — Trigger parsing + EVM log fetching
    supported_chains.go — Chain selector list
    limited_capabilities.go — EVMLimits (extends chain.Limits) + LimitedEVMChain

Non-refactor behavior changes in this PR

  • supported_chains.go expands the EVM chain-selector list (adds multiple new selectors, un-comments PHAROS_ATLANTIC_TESTNET). Zero selectors were removed — this is additive. Called out here so the "pure internal refactor" summary is honest.

Known follow-ups (NOT blockers)

  • Trigger dispatch iterates chain.All() (a map) and breaks on the first match. Deterministic today since EVM is the only registered chain type; must be scoped per-family and iteration-ordered before a second chain type lands.
  • Experimental-chain labelling edge case: when an experimental config overrides a supported chain's forwarder (same selector), health-check errors label the chain by its supported name rather than "experimental chain N". Cosmetic.
  • LimitedEVMChain delegates 12 methods explicitly. Could be reduced to an embedded evmserver.ClientCapability with overrides on WriteReport only.
  • CLIFlagType covers only String/Int. Extend when a family needs Uint64/Bool/StringSlice.

Test plan

  • All existing tests pass (go test ./cmd/workflow/simulate/...)
  • Registry tests: register/get, duplicate panics, All returns copy, Names sorted
  • Lifecycle helper tests: rewind-on-failure, aggregation-of-close-errors, empty-slice
  • Health check tests: no clients, nil client, all OK, RPC error, zero chain ID, aggregation, invalid client type, experimental-selector labelling
  • Limited EVM tests: oversized report, oversized gas, boundary values
  • Trigger parsing tests: mainnet, case insensitive, overflow, edge cases
  • Cleanup helper test: closes every service even when earlier Close errored

@github-actions
Copy link
Copy Markdown

⚠️ Abigen Fork Check - Update Available

The forked abigen package is outdated and may be missing important updates.

Version Value
Current Fork v1.17.0
Latest Upstream null

Action Required

  1. Review abigen changes in upstream (only the accounts/abi/bind directory matters)
  2. Compare with our fork in cmd/generate-bindings/bindings/abigen/
  3. If relevant changes exist, sync them and update FORK_METADATA.md
  4. If no abigen changes, just update the version in FORK_METADATA.md to null

Files to Review

  • cmd/generate-bindings/bindings/abigen/bind.go
  • cmd/generate-bindings/bindings/abigen/bindv2.go
  • cmd/generate-bindings/bindings/abigen/template.go

⚠️ Note to PR author: This is not something you need to fix. The Platform Expansion team is responsible for maintaining the abigen fork.

cc @smartcontractkit/bix-framework

…rchitecture

Introduce ChainFamily interface and package-level registry enabling
future Aptos/Solana chain support. Extract all EVM-specific simulation
code into cmd/workflow/simulate/chain/evm/ package with self-registration
via init().

- Add ChainFamily interface with 7 methods (Name, ResolveClients,
  RegisterCapabilities, ExecuteTrigger, ParseTriggerChainSelector,
  RunHealthCheck, SupportedChains)
- Add thread-safe chain family registry with Register/Get/All/Names
- Move EVM health checks, trigger parsing, supported chains list,
  and limited capabilities into chain/evm package
- Refactor simulate.go to iterate registered families instead of
  hardcoding EVM logic
- Fix EVM chains not being Start()ed after registration
- Remove redundant blank import (named import triggers init())
- Rename test stubs for clarity
@Fletch153 Fletch153 force-pushed the plex-2751-modularise-chain-families branch from b13b64c to 1d3000b Compare April 15, 2026 11:44
- Restore missing debug/error log statements in EVM ResolveClients
- Restore ExecuteTrigger nil guards (f.evmChains, EVMChains[selector])
- Restore ui.Success for EVM log discovery in fetchAndConvertLog
- Restore comments: Use Opaque, runRPCHealthCheck semantics, regex example
- Track experimentalSelectors on EVMFamily, pass to RunHealthCheck
- Factory-pattern family registration with logger injection (chain.Build)
- Extract ResolveKey + ResolveTriggerData to EVM family
- Extract simulator_utils.go contents; WorkflowExecutionTimeout to simulate.go
- RegisterCapabilities returns []services.Service for lifecycle parity
- getTriggerDataForFamily doc comment chain-agnostic
- PROMPT.md + SMOKE_TESTS.md audit artifacts (65 tests)
Covers ResolveKey private-key matrix (valid/invalid/sentinel/empty
across broadcast and non-broadcast), LimitedEVMChain delegation for
every method, trigger hash validation and log-fetch mock scenarios,
supported-chains invariants (unique selectors, valid 20-byte
forwarders), and extended health-check labelling for experimental,
known, and unknown selectors.

100+ new test cases, zero production code changes.
@Fletch153 Fletch153 force-pushed the plex-2751-modularise-chain-families branch from dab3e25 to cbf498d Compare April 15, 2026 21:33
Replace EVMTxHash/EVMEventIndex on TriggerParams with a generic
FamilyInputs string map so each chain family owns its input keys.
Unify EVMChainLimits as a type alias for chain.Limits, removing the
redundant interface and the type assertion in RegisterCapabilities.
Guard against the typed-nil interface trap when boxing SimulationLimits.
…gistry

Chain families now own their CLI flag definitions and input collection,
removing all EVM-specific knowledge from simulate.go. Fixes a potential
deadlock in registry.Get() by extracting namesLocked() helper, and
guards ResolveKey to skip families without configured clients.
@Fletch153 Fletch153 force-pushed the plex-2751-modularise-chain-families branch from 88045cc to b9a4f58 Compare April 16, 2026 09:41
Drop EVM prefix from the chain.Limits interface method to better
reflect the generic abstraction layer.
fetchAndConvertLog now takes a verbose flag. Interactive mode emits
ui.Success messages as before; non-interactive mode stays silent,
matching the original behavior.
Rename the chain abstraction from "family" to "type" for clearer
domain language. EVM is a chain type, not a chain family.

- ChainFamily interface → ChainType
- EVMFamily struct → EVMChainType
- family.go → chaintype.go (file rename)
- FamilyClients/Forwarders/Keys/Inputs → ChainTypeClients/etc
- getTriggerDataForFamily → getTriggerDataForChainType
- Import alias chaintype → corekeys (conflict avoidance)
- All comments, error strings, test names updated
- Remove redundant copyloopvar 'tt := tt' (Go 1.22+)
- Remove unused evmCapabilityBaseStub embed in fullStubCapability
- Convert 'switch { case x == a }' to 'switch x { case a }' (staticcheck QF1002)
- Fix goimports formatting
@github-actions
Copy link
Copy Markdown

⚠️ Abigen Fork Check - Update Available

The forked abigen package is outdated and may be missing important updates.

Version Value
Current Fork v1.17.0
Latest Upstream null

Action Required

  1. Review abigen changes in upstream (only the accounts/abi/bind directory matters)
  2. Compare with our fork in cmd/generate-bindings/bindings/abigen/
  3. If relevant changes exist, sync them and update FORK_METADATA.md
  4. If no abigen changes, just update the version in FORK_METADATA.md to null

Files to Review

  • cmd/generate-bindings/bindings/abigen/bind.go
  • cmd/generate-bindings/bindings/abigen/bindv2.go
  • cmd/generate-bindings/bindings/abigen/template.go

⚠️ Note to PR author: This is not something you need to fix. The Platform Expansion team is responsible for maintaining the abigen fork.

cc @smartcontractkit/bix-framework

Consolidated review-feedback fixes on top of the chain-family refactor.

Interface cleanup (makes the abstraction live up to its chain-agnostic
name):

- chain.Limits: narrow to ChainWriteReportSizeLimit only; move
  ChainWriteGasLimit onto a new evm.EVMChainLimits sub-interface so
  EVM-specific accessors do not leak into the generic contract.
- chain.ResolvedChains: new struct returned from ResolveClients, carrying
  clients, forwarders, and experimental-selector flags. Replaces the
  hidden cross-method state the EVM implementation was keeping on its
  struct for RunHealthCheck to read.
- ChainType.ResolveClients and ChainType.RunHealthCheck adopt the new
  return/argument shape.

User-visible parity with main:

- Per-family error prefix restored when trigger-data resolution fails,
  using the family name (e.g. 'Failed to get evm trigger data: ...').
- 'No {name} chain initialized for selector %d' uses the registry-key
  name rather than a manually-uppercased variant.

Small cleanups touched on the way:

- Hoist the 'RPC health check failed:' wrap from evm.RunRPCHealthCheck
  to the simulate.go caller so a future second chain family cannot
  produce doubled headers.
- Remove dead ManualTriggers.Close (no callers before or after this PR).
- Rename the EVMChainLimits test stub to stubEVMLimits to stop it
  colliding by one letter with the production EVMChainLimits alias.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant