Add SETDELEGATE factory example#17
Draft
chunter-cb wants to merge 2 commits into
Draft
Conversation
Rebased onto main, which already removed the importAccount 0xef0100 delegation-indicator reject and the _isDelegated helper. The remaining spec-side change is small: importAccount now writes localSequence = 1 *after* the ERC-1271 STATICCALL instead of before. A view callback cannot write state, so there is no reentrancy risk in deferring the write, and deferring it keeps getChangeSequences(this).local == 0 observable during the callback — which a bootstrap-aware delegate branches on. Adds an example external factory under src/examples/factory/ that combines EIP-7819 SETDELEGATE with EIP-8130 importAccount to deploy accounts as 23-byte delegation indicators instead of full runtime bytecode, with factory-mediated upgradeability and no AccountConfiguration coupling: - SetDelegateFactory — atomic SETDELEGATE -> bootstrap -> importAccount - BootstrapAccount — bootstrap-aware ERC-1271 reference implementation - IBootstrap — minimal priming interface - README.md — flow, squatting defenses, caveats, future options EIP-7819 is Draft and SETDELEGATE (opcode 0xf6) is not yet executable, so SetDelegateFactory._setDelegate is virtual and reverts; the test subclass simulates it via vm.etch. Squatting/front-running is prevented by the SETDELEGATE address derivation (binds to msg.sender), the actor-set commitment in the salt, and the atomic single-tx-frame guarantee. Adds a codeless-account import test (squat prevention) to importAccount.t.sol and full factory-flow tests. forge test: 179/179 pass.
30c37e7 to
12f4c60
Compare
Track the one-time bootstrap window in the account's own transient (EIP-1153) latch instead of reading AccountConfiguration's local sequence. The account now drives importAccount on itself in `bootstrap`: set latch -> import -> clear latch, all in one frame. This reverts the importAccount localSequence deferral so AccountConfiguration is used exactly as it ships on main. - AccountConfiguration.sol: revert deferral (matches main; no core change). - BootstrapAccount: transient latch + self-driven import; ERC-1271 bootstrap branch gated by the latch, else defers to authenticateActor. - SetDelegateFactory: deploy = SETDELEGATE + account.bootstrap; drops the now-unused AccountConfiguration reference. - IBootstrap: bootstrap(actorsHash, initialActors). - README + tests updated.
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.
Summary
Reference, unaudited example of an external account factory that combines EIP-7819 (
SETDELEGATE) with EIP-8130 (importAccount) to deploy lightweight, upgradeable delegate accounts at deterministic addresses.The headline property: this needs no changes to
AccountConfiguration. A keyless factory account has no signature to present at import time, so it must answer "am I in my one-time bootstrap window?" during its ownimportAccountERC-1271 callback. It answers that from its own transient (EIP-1153) latch, not by reading AccountConfiguration'slocalSequence.BootstrapAccount.bootstrapdoes it all in one frame: set latch → callimportAccounton itself → clear latch. While the latch is set (only across that nested import),isValidSignaturevalidates the import digest against the primedactorsHash; otherwise it defers toauthenticateActor. The latch is transient, so it cannot leak past the tx, andimportAccount's one-time guard prevents any second import — the bootstrap branch is reachable exactly once.Changes
AccountConfiguration.sol: no change vsmain(an earlier revision of this PR deferred thelocalSequencewrite; that's now reverted — the pattern no longer needs it).src/examples/factory/BootstrapAccount.sol: transient bootstrap latch + self-driven import; bootstrap-aware ERC-1271.src/examples/factory/SetDelegateFactory.sol:deploy=SETDELEGATE+account.bootstrap(...); drops the now-unusedAccountConfigurationreference.src/examples/factory/IBootstrap.sol:bootstrap(actorsHash, initialActors).src/examples/factory/README.md: rewritten for the self-contained design.Squatting / front-running defenses
SETDELEGATEderivation includes the factory (msg.sender) and a salt that commits to the actor set, so address ↔ actor-set binding is collision-hard.SETDELEGATE → bootstrapis atomic in one tx; no interposition window.Spec prerequisite (already on
main)importAccountdoes not reject0xef0100…delegated code. That rule never closed an attack surface (a compromised k1 key already drains a delegated EOA via standard 7702/1559 txs) but did block this pattern. The ERC-1271 callback is the sole account ↔ actor-set binding.Caveats
SETDELEGATEisn't executable on most chains. Tests simulate the opcode viavm.etch(TestableSetDelegateFactory).BootstrapAccountis intentionally minimal (no execution/auth plumbing beyond SoladyReceiver).Test plan
forge build(evm_version osaka)forge test— 179 passed, 0 failedforge test --match-path 'test/unit/examples/factory/*'— 7 passedforge test --match-path '.../importAccount.t.sol'— 10 passedforge fmt --checkclean