Skip to content

feat(deploy): add state oracle v2 flow - #48

Open
fredo wants to merge 1 commit into
frederik/eng-4229-fixcontracts-scope-executor-event-guard-by-keyfrom
frederik/eng-4099-featdeploy-add-state-oracle-v2-flow
Open

feat(deploy): add state oracle v2 flow#48
fredo wants to merge 1 commit into
frederik/eng-4229-fixcontracts-scope-executor-event-guard-by-keyfrom
frederik/eng-4099-featdeploy-add-state-oracle-v2-flow

Conversation

@fredo

@fredo fredo commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Addresses ENG-4099.

Adds the production-only V2 deployment script, confirmation-depth validation, optimized size profile, focused deployment tests, and ABI-generation inputs.

@linear-code

linear-code Bot commented Jul 31, 2026

Copy link
Copy Markdown

ENG-4099

@fredo

fredo commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

Known inherited CI blocker: the shared workflow builds StateOracleV2 with the default unoptimized profile and fails EIP-170 size enforcement. The dedicated optimized V2 profile passes; CI/profile integration is tracked in ENG-4250. No fix is included here so the reviewed implementation remains unchanged.

Comment thread script/DeployCoreV2.s.sol

function run() public override broadcast {
_fundPersistentAccounts();
address[] memory adminVerifierAddresses = _deployAdminVerifiers();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Both verifier flags can be false here, which lets a production deployment initialize with no admin verifier. That proxy cannot register any adopter until a follow up governance transaction, so it misses ENG 4099's ready for normal operations requirement. Can we require at least one production admin verifier before broadcasting?

Comment thread script/DeployCoreV2.s.sol
TriggerManifestValidatorV1 validator = new TriggerManifestValidatorV1(admin);
StateOracleV2 implementation = new StateOracleV2(assertionTimelockBlocks);

IAdminVerifier[] memory adminVfrs = new IAdminVerifier[](adminVerifierAddresses.length);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This script silently uses whichever Foundry profile the caller has selected. With the default profile StateOracleV2 is 25,556 bytes and cannot be deployed under EIP 170, while only the v2 profile makes it deployable. Can the script reject a nonoptimized build or provide a deployment entrypoint that always selects the v2 profile?

@fredo
fredo force-pushed the frederik/eng-4099-featdeploy-add-state-oracle-v2-flow branch from 475444f to 340c304 Compare August 12, 2026 11:05
@fredo
fredo force-pushed the frederik/eng-4229-fixcontracts-scope-executor-event-guard-by-key branch from 78862b5 to 079196e Compare August 12, 2026 11:05
@fredo
fredo marked this pull request as ready for review August 12, 2026 11:14
penumbra23 added a commit that referenced this pull request Aug 13, 2026
- check_storage_layout.sh compares the metadata of every previous type, not
  only the ones with members. A user defined value type keeps its normalized
  key when its underlying type changes, so its storage entry still matched and
  the change was reported as benign. Widths may grow for a type with members -
  appending to a struct behind a mapping leaves existing records in place - and
  any other change to width, encoding, key, value or base is a collision.
- A missing .storage-layout no longer bootstraps itself. Writing one into the
  CI runner and exiting 0 meant a pull request could delete the snapshot and
  every later run would re-baseline against itself and pass.
- check_abi.sh reads its scope out of create_artifacts.sh instead of repeating
  it. The two lists had already drifted: #48 publishes StateOracleV2,
  TriggerManifestValidatorV1 and ITriggerManifestValidator, none of which this
  check would have fingerprinted.
- Functions and errors are keyed by a 4-byte selector, so a shared key does not
  mean a shared signature. Differing signatures under one selector are now a
  break; previously the entry looked present on both sides and old calldata
  would decode under a different input layout.

Also drops the HEAD^ fallback when resolving a push base, for the same reason
it was dropped from the proto gate: after a force-push HEAD^ belongs to the
rewritten history, not the published ABI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
penumbra23 added a commit that referenced this pull request Aug 14, 2026
#50)

* ci(contracts): gate the published ABI and run the storage-layout check

Adds an ABI compatibility gate and wires both contract-surface checks into
CI, where neither ran before.

The ABI fingerprint compares selector and topic identity rather than
document text, so reordering or reformatting by the toolchain cannot
produce a false failure. Events additionally carry an indexed bitmask,
because topic0 is keccak of the canonical signature and excludes
indexedness: flipping a field between topic and data leaves topic0
untouched while silently breaking every historical log decoder.

The storage-layout check was already present, complete with a committed
snapshot and make targets, but no workflow ever invoked it -- and it could
not have passed if one had. Its snapshot embeds solc AST node ids, which
shift whenever any storage-neutral declaration is added, so a clean
rebuild of unmodified main reported a fake collision. It is therefore both
wired up and repaired: AST ids are normalized away while array lengths and
integer widths are preserved, entries are sorted by slot, struct members
behind mappings are compared, and tool failure is distinguished from a
real layout change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* refactor(ci): take the ABI baseline from the base branch, not a committed snapshot

The published ABI is a release artifact: shell/create_artifacts.sh generates
it into a gitignored artifacts/, and release.yml publishes it to npm and a
GitHub release on every tag. Committing a second, differently shaped ABI
fingerprint under .abi/ duplicated that artifact inside a repository that
deliberately excludes generated ABI.

The gate now computes both sides instead. It checks the base revision out
into a temporary worktree, builds it, fingerprints it, and compares that
against the working tree, so nothing is stored in the repository. The base
revision is resolved the same way as the proto gate in credible-sdk: the
pull request base sha, or the commit before a push, never origin/main on a
push -- on main that is the commit under test, and the comparison would be
vacuous. An unresolvable base, or one that leaves nothing to compare,
refuses to report a pass rather than passing silently.

The fingerprint itself is unchanged: selectors, event topic0s, the indexed
bitmask, return types, error selectors, and the classification rules all
behave exactly as before, as does the exit-code contract.

The storage-layout half is untouched. It guards proxy upgrade safety
against already-deployed state rather than a published package, so its
committed snapshot is the right baseline.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* ci(contracts): close four gaps found in review

- check_storage_layout.sh compares the metadata of every previous type, not
  only the ones with members. A user defined value type keeps its normalized
  key when its underlying type changes, so its storage entry still matched and
  the change was reported as benign. Widths may grow for a type with members -
  appending to a struct behind a mapping leaves existing records in place - and
  any other change to width, encoding, key, value or base is a collision.
- A missing .storage-layout no longer bootstraps itself. Writing one into the
  CI runner and exiting 0 meant a pull request could delete the snapshot and
  every later run would re-baseline against itself and pass.
- check_abi.sh reads its scope out of create_artifacts.sh instead of repeating
  it. The two lists had already drifted: #48 publishes StateOracleV2,
  TriggerManifestValidatorV1 and ITriggerManifestValidator, none of which this
  check would have fingerprinted.
- Functions and errors are keyed by a 4-byte selector, so a shared key does not
  mean a shared signature. Differing signatures under one selector are now a
  break; previously the entry looked present on both sides and old calldata
  would decode under a different input layout.

Also drops the HEAD^ fallback when resolving a push base, for the same reason
it was dropped from the proto gate: after a force-push HEAD^ belongs to the
rewritten history, not the published ABI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

2 participants