core: Safe/multisig + external-signer deploy path (no raw private key required) - #174
Open
robercano-ghbot wants to merge 1 commit into
Open
core: Safe/multisig + external-signer deploy path (no raw private key required)#174robercano-ghbot wants to merge 1 commit into
robercano-ghbot wants to merge 1 commit into
Conversation
…154) Generalizes jsonRpcProvider's hard-wired private-key signing into a pluggable Signer interface (provider/signer.ts) - jsonRpcProvider() keeps its exact original signature/behavior, delegating to the new signerProvider() for external signers (hardware wallet, remote KMS, etc). Adds proposeDeploy() (propose/propose.ts): runs the same validate/resolve/ compile pipeline as deploy(), but collects the ordered transaction batch Ignition would send via an in-memory collectingProvider instead of broadcasting. Never touches the real, resumable deploymentDir/journal - proven by test/propose.test.ts's journal-invariant suite (byte-for-byte comparison before/after a propose() call against a partially-deployed spec). buildSafeBatch() (propose/safeBatch.ts) emits a Safe Transaction Builder-compatible batch JSON for config/call-only batches; raw contract-creation steps (to: null) are documented as an out-of-scope seam (Safe cannot originate raw CREATE) rather than silently mis-emitted. Documents the propose -> Safe execution -> resume operator flow in packages/core/README.md.
Collaborator
Author
|
core: Safe/multisig + external-signer deploy path (no raw private key required) (not yet reviewed) |
Collaborator
Author
|
core: Safe/multisig + external-signer deploy path (no raw private key required) (not yet reviewed) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #154
Summary
Adds a pluggable signer seam and a "propose" execution mode to
@redeploy/core, so deploy/config transactions can be signed by an external signer or collected into a Safe-compatible batch instead of being broadcast directly with a rawDEPLOYER_PRIVATE_KEY.1. Signer seam (
packages/core/src/provider/signer.ts)jsonRpc.ts's signing logic is generalized behind a narrowSignerinterface (address+signTransaction/signMessage/signTypedData, structurally a subset of viem'sLocalAccount).jsonRpcProvider({ rpcUrl, privateKey })is unchanged — same signature, same behavior — it now just derives aSignerviaprivateKeySigner()and delegates to the newsignerProvider({ rpcUrl, signer }). External signers (hardware wallet, remote KMS, ...) implementSignerand callsignerProvider()directly. No new fields were added toDeployOptions— this follows the existing injection pattern (provideris already the seam).2 & 4. Propose mode + the journal-safety invariant (
packages/core/src/propose/)proposeDeploy()runs the exact same validate → resolve-crossRef → resolve-resolver → compile pipeline asdeploy(), then runs Ignition's realdeploy()engine against an in-memorycollectingProviderthat interceptseth_sendTransactionand never broadcasts — so batch ordering, constructor-arg encoding, proxy expansion, and CREATE address prediction are byte-for-byte what a realdeploy()would produce, with zero reinvented logic.The load-bearing invariant:
proposeDeploy()never passes the caller's realdeploymentDirto Ignition.deploymentDirgiven → Ignition runs against a throwaway temp dir, deleted on return. No journal is ever created anywhere visible to the caller.deploymentDirgiven (resume case) → itsjournal.jsonlis copied (read-only on the original) into the same throwaway dir; Ignition sees the resume state and only collects transactions for NOT-yet-complete futures. The realdeploymentDiris opened at most once, for a read, never for a write.Proven in
test/propose.test.ts's journal-invariant suite via a byte-for-byte comparison of a realjournal.jsonlbefore/after aproposeDeploy()call against a partially-deployed spec (plus a full subsequentdeploy()resume showing resumability was untouched).3. Safe batch output (
packages/core/src/propose/safeBatch.ts)buildSafeBatch()convertsProposeResult.transactionsinto the Safe Transaction Builder batch JSON schema (version/chainId/createdAt/meta/transactions[]). Raw contract-creation steps (to: null) have no Safe representation (Safe can't originate a CREATE) —buildSafeBatch()throwsSafeBatchError("UNSUPPORTED_CREATION")rather than silently emitting an unusable batch. Config/call-only batches are fully supported.5. Docs
packages/core/README.mdgets a new "Signers and propose mode" section covering the pluggable signer seam and the full propose → Safe execution → resume operator flow.Deferred (documented seams, not implemented)
buildSafeBatch()'s output is the seam for a follow-up HTTP client.safeBatch.ts's module doc.deploy()resume also works when the Safe itself, not an EOA the journal already tracks, was the sender) is not implemented; the confirm-then-resume flow documents this boundary explicitly.Test plan
pnpm -F @redeploy/core buildpnpm -F @redeploy/core lintpnpm -F @redeploy/core typecheckpnpm -F @redeploy/core test(513 tests, incl. newsigner.test.ts,propose.test.ts,safeBatch.test.ts)pnpm -F @redeploy/core coverage(packages/core: 94.41% stmts, threshold 80).claude/scripts/gate.sh {build,lint,typecheck,test,coverage}from repo root — all green