Skip to content

feat(core): declarative preflight policy gates in deploy() — part 1 of #156 - #170

Open
robercano-ghbot wants to merge 1 commit into
mainfrom
feat/issue-156-core-preflight
Open

feat(core): declarative preflight policy gates in deploy() — part 1 of #156#170
robercano-ghbot wants to merge 1 commit into
mainfrom
feat/issue-156-core-preflight

Conversation

@robercano-ghbot

Copy link
Copy Markdown
Collaborator

What

Part 1 of 2 for #156 — the @redeploy/core half: a declarative preflight phase in deploy() that runs BEFORE any transaction is broadcast, so mainnet mistakes are caught pre-broadcast.

The deploy-server interlock (simulate-first + structured SSE surfacing) lands in a follow-up PR stacked on this branch; merge this one first.

Design

  • New PreflightPolicy { expectedChainId?, minBalanceWei?, maxGasPriceWei? } — declarable on the spec (DeploymentSpec.preflight) and/or passed via DeployOptions.preflight. Effective policy = per-field merge (options override spec per field, via nullish-coalescing so an override can never remove a spec-mandated check).
  • New deploy/preflight.ts runPreflight() performs only the checks whose fields are set: eth_chainId equality, deployer balance ≥ minBalanceWei (via eth_getBalance), gas price ≤ maxGasPriceWei (via eth_gasPrice). Runs after validate/compile but strictly BEFORE ignitionDeploy() — no tx can precede a failure.
  • Failure throws the typed DeployError("PREFLIGHT_FAILED", …) carrying structured preflightFailures: PreflightFailure[] ({ check, expected, actual, message }).
  • Happy path unaffected: when no policy field is set anywhere, preflight short-circuits with ZERO extra RPC calls (regression-guarded by a test whose provider throws on eth_getBalance).
  • Fail-closed: an indeterminate check (provider/RPC error, malformed response) propagates and aborts the deploy — it never proceeds on an unknown result. Schema (preflightPolicySchema) validates spec-declared policies (positive-int chainId, decimal-bigint wei strings) → malformed becomes INVALID_SPEC.

New public API

PreflightPolicy, PreflightFailure, PreflightCheck, runPreflight, preflightPolicySchema; DeployErrorCode gains "PREFLIGHT_FAILED"; DeployError.preflightFailures; DeploymentSpec.preflight / DeployOptions.preflight.

Gates (packages/core)

build / lint / typecheck / test / coverage — all green. 433/433 tests. Core coverage 93.59% stmts (threshold 80); preflight.ts 97.22%, schema.ts 100%.

Review

All four reviewer lenses (correctness, tests, security, performance) approved, high confidence.

Follow-up nit (non-blocking, deferred): harden hexToBigInt to require a ^0x[0-9a-fA-F]+$ prefix so a non-conformant RPC returning a bare decimal/empty string fails closed on the gas-price ceiling. Not a real bypass for any conformant EIP-1193 provider; noted for a later hardening pass.

Part of #156.

🤖 Generated with Claude Code

Adds an opt-in pre-broadcast safety phase: expectedChainId, minBalanceWei,
and maxGasPriceWei checks run against the live RPC/account BEFORE any
transaction is sent, aborting with a typed DeployError("PREFLIGHT_FAILED")
on failure. Effective policy is a per-field merge of DeploymentSpec.preflight
and DeployOptions.preflight; the phase is fully skipped (zero extra RPC
calls) when no policy is configured, keeping the happy path unaffected.

Co-Authored-By: Claude Sonnet 5 <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