feat: SCOPE_POLICY, SCOPE_NONCE, admin=scope-0 (match EIP #9)#28
Merged
Conversation
Align AccountConfiguration with the EIP-8130 draft: gated initiation is SCOPE_POLICY, policy slots are written from that bit, scope combos are stored verbatim for protocol use-time checks, and nonceLane carries the unbound / lane-or-nonceless / nonceless-only restriction.
Drop nonceLane and SCOPE_CONFIG to match EIP PR #9: ActorConfig is authenticator/scope/expiry only, SCOPE_NONCE is stored verbatim for protocol use, and actor-change / upgrade authority requires scope == 0.
…-nonce-lane # Conflicts: # src/AccountConfiguration.sol # src/accounts/UpgradeableAccount.sol # test/unit/AccountConfiguration/applyAccountChange.t.sol # test/unit/AccountConfiguration/applyKeyChange.t.sol # test/unit/AccountConfiguration/authenticate.t.sol # test/unit/AccountConfiguration/policyAccessors.t.sol
…ning admin-only (match EIP #9)
…min-only (match EIP #9)
…ifications Restores the conditional policy-manager SLOAD in both auth paths so admin/non-policy authentications avoid an unused cold read. Adds NatSpec/README notes on the shared local sequence (lock vs actor changes) and zero-commitment policy actors, and corrects the README SENDER+POLICY guidance to SHOULD NOT (POLICY gates regardless).
…and PolicyManager
…cope Initial actors carry their declared scope and policyData (external policy manager expressible at create); only expiry is forced to 0. The prior "always unrestricted owners (scope 0x00, no policy)" wording contradicted the code and the merged EIP-8130 spec.
The EIP makes the packed account-state slot normative — nodes read it directly for mempool rate-limit tiering. The prior implementation encoded lock state via unlocksAt sentinels (type(uint40).max = hard-locked) plus a separate unlockDelay field, which a spec-conformant node reading the raw slot would misinterpret. Align storage to the spec: a flags byte carrying LOCKED (bit 1) and UNLOCK_INITIATED (bit 2) alongside DEFAULT_EOA_REVOKED (bit 0), and a single uint40 lock_union reinterpreted by UNLOCK_INITIATED (unlock delay vs unlocks_at). Field order/widths now match the spec table, with 3 reserved zero bytes. Public ABI (getLockStatus, events, lock digest) is unchanged; this is a storage-layout + internal-logic change only. Adds a test covering the pending-unlock freeze on the config-change path.
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
Brings the reference
AccountConfiguration(and the example accounts/authenticators) in line with the current EIP-8130 draft: chunter-cb/EIPs#9Authorization model
scope == 0x00; config changes (applySignedActorChanges), account lock, delegation, and upgrades all requirescope == 0— there is no separate CONFIG grant.SENDER(0x01),POLICY(0x02),NONCE(0x04),SELF_PAYER(0x08),SPONSOR_PAYER(0x10);0x20/0x40/0x80spare. Stored verbatim —getActorConfig/authenticateActornever normalize or mask.POLICYis gated initiation (gates the actor to itsmanagerregardless ofSENDER); it composes withSELF_PAYER,SPONSOR_PAYER, andNONCE.PAYERis split:SELF_PAYERauthorizes self-pay (payer == sender);SPONSOR_PAYERauthorizes sponsoring a different sender (payer != sender).verifySignature()requires operational authority — admin, orSENDERwithoutPOLICY— not a dedicated grant (signing is an encoding of authority aSENDERkey already holds via calls;approve ≡ permit). APOLICYactor is never operational and cannot sign.Actor policies
policy_manager/policy_commitment) are written iffPOLICYis set.policyDataismanager(20) ‖ commitment(32)(52 bytes whenPOLICYis set, empty otherwise); neither field need be nonzero.authenticateActorreturns(uint8 scope, address policyTarget). Granular getters:getPolicy,getPolicyManager,getPolicyCommitment.Account creation & import
InitialActorcarriesactorId,authenticator,scope, andpolicyData, all committed to the derived address and hashed into the import digest. Initial actors are non-expiring (expiry = 0);manager = accountis expressible at import but not at create.Account lock
applySignedLockChanges(account, op, unlockDelay, auth)(op1= lock,2= unlock) replaces the caller-basedlock/initiateUnlock. Local channel only: the digest bindsblock.chainidand the currentlocalSequence(sign-current-then-increment, shared with local config changes), authenticated as admin (scope == 0).Delegate authenticator
scope == 0), decoupled from the now-operationalverifySignature, preserving non-escalation: aSENDERkey on account B can produce a valid ERC-1271 signature for B but cannot vouch as a delegate on an account that delegates to B.ABI
ActorAuthorized/DelegationAppliedevents,uint256 chainIdon import / apply (chainId bound in the import digest).Test plan
forge test— 341 passingforge fmt --check