@@ -262,12 +262,92 @@ contract ApplyConfigChangeOwnerTest is AccountConfigurationTest {
262262 accountConfiguration.applySignedOwnerChanges (account, uint64 (block .chainid ), changes, badAuth);
263263 }
264264
265- // ── EOA self-ownerId (default key) revoke/add ──
265+ // ── Implicit EOA (registered by default) ──
266+ //
267+ // Every account has an implicit self-ownerId bytes32(bytes20(account))
268+ // that is authorized with unrestricted scopes when the config slot
269+ // is empty. No createAccount/importAccount needed.
270+
271+ function test_implicitEOA_canSignOwnerChanges () public {
272+ uint256 eoaPk = 500 ;
273+ address eoa = vm.addr (eoaPk);
274+ bytes32 newOwnerId = bytes32 (bytes20 (vm.addr (501 )));
275+
276+ IAccountConfiguration.OwnerChange[] memory changes = new IAccountConfiguration.OwnerChange [](1 );
277+ changes[0 ] = IAccountConfiguration.OwnerChange ({
278+ ownerId: newOwnerId,
279+ changeType: 0x01 ,
280+ configData: abi.encode (IAccountConfiguration.OwnerConfig ({verifier: address (k1Verifier), scopes: 0x00 }))
281+ });
282+
283+ uint64 seq = accountConfiguration.getChangeSequences (eoa).local;
284+ bytes32 digest = _computeOwnerChangeBatchDigest (eoa, uint64 (block .chainid ), seq, changes);
285+ bytes memory auth = _buildImplicitEOAAuth (eoaPk, digest);
286+
287+ accountConfiguration.applySignedOwnerChanges (eoa, uint64 (block .chainid ), changes, auth);
288+ assertTrue (accountConfiguration.isOwner (eoa, newOwnerId));
289+ }
290+
291+ function test_implicitEOA_canRevokeItselfViaSentinel () public {
292+ uint256 eoaPk = 500 ;
293+ address eoa = vm.addr (eoaPk);
294+ bytes32 selfOwnerId = bytes32 (bytes20 (eoa));
295+
296+ assertTrue (accountConfiguration.isOwner (eoa, selfOwnerId));
297+
298+ // Add a second key first using implicit EOA auth
299+ bytes32 newOwnerId = bytes32 (bytes20 (vm.addr (501 )));
300+ _implicitAuthorizeOwner (eoa, eoaPk, newOwnerId, address (k1Verifier));
301+
302+ // Revoke self-ownerId using the new explicit key
303+ _revokeOwner (eoa, 501 , selfOwnerId);
304+
305+ assertFalse (accountConfiguration.isOwner (eoa, selfOwnerId));
306+ assertTrue (accountConfiguration.isOwner (eoa, newOwnerId));
307+
308+ IAccountConfiguration.OwnerConfig memory cfg = accountConfiguration.getOwnerConfig (eoa, selfOwnerId);
309+ assertEq (cfg.verifier, accountConfiguration.REVOKED_VERIFIER ());
310+ assertEq (cfg.scopes, 0 );
311+ }
312+
313+ function test_implicitEOA_canBeExplicitlyRegistered () public {
314+ uint256 eoaPk = 500 ;
315+ address eoa = vm.addr (eoaPk);
316+ bytes32 selfOwnerId = bytes32 (bytes20 (eoa));
317+
318+ _implicitAuthorizeOwnerWithScope (eoa, eoaPk, selfOwnerId, address (k1Verifier), 0x01 );
319+
320+ IAccountConfiguration.OwnerConfig memory cfg = accountConfiguration.getOwnerConfig (eoa, selfOwnerId);
321+ assertEq (cfg.verifier, address (k1Verifier));
322+ assertEq (cfg.scopes, 0x01 );
323+ }
324+
325+ function test_implicitEOA_crossChainOwnerChange () public {
326+ uint256 eoaPk = 500 ;
327+ address eoa = vm.addr (eoaPk);
328+ bytes32 newOwnerId = bytes32 (bytes20 (vm.addr (501 )));
329+
330+ IAccountConfiguration.OwnerChange[] memory changes = new IAccountConfiguration.OwnerChange [](1 );
331+ changes[0 ] = IAccountConfiguration.OwnerChange ({
332+ ownerId: newOwnerId,
333+ changeType: 0x01 ,
334+ configData: abi.encode (IAccountConfiguration.OwnerConfig ({verifier: address (k1Verifier), scopes: 0x00 }))
335+ });
336+
337+ // chainId=0 for multichain
338+ uint64 seq = accountConfiguration.getChangeSequences (eoa).multichain;
339+ bytes32 digest = _computeOwnerChangeBatchDigest (eoa, 0 , seq, changes);
340+ bytes memory auth = _buildImplicitEOAAuth (eoaPk, digest);
341+
342+ accountConfiguration.applySignedOwnerChanges (eoa, 0 , changes, auth);
343+ assertTrue (accountConfiguration.isOwner (eoa, newOwnerId));
344+ }
345+
346+ // ── EOA self-ownerId revoke/add with explicit registration ──
266347 //
267348 // The self-ownerId for an account is bytes32(bytes20(account)).
268349 // Revoking this ownerId sets a sentinel (verifier=0, scopes=0xff)
269- // instead of deleting, to prevent the 8130 protocol's implicit
270- // re-authorization on an empty slot.
350+ // instead of deleting, to block the implicit authorization.
271351
272352 function test_selfOwnerId_addKey () public {
273353 (address account ,) = _createK1Account (OWNER_PK);
@@ -289,8 +369,8 @@ contract ApplyConfigChangeOwnerTest is AccountConfigurationTest {
289369 assertFalse (accountConfiguration.isOwner (account, selfOwnerId));
290370
291371 IAccountConfiguration.OwnerConfig memory cfg = accountConfiguration.getOwnerConfig (account, selfOwnerId);
292- assertEq (cfg.verifier, address ( 0 ));
293- assertEq (cfg.scopes, type ( uint8 ).max );
372+ assertEq (cfg.verifier, accountConfiguration. REVOKED_VERIFIER ( ));
373+ assertEq (cfg.scopes, 0 );
294374 }
295375
296376 function test_selfOwnerId_canReauthorizeAfterSentinel () public {
@@ -334,8 +414,8 @@ contract ApplyConfigChangeOwnerTest is AccountConfigurationTest {
334414
335415 // Self-ownerId has sentinel, not zeroed
336416 IAccountConfiguration.OwnerConfig memory cfg = accountConfiguration.getOwnerConfig (account, selfOwnerId);
337- assertEq (cfg.verifier, address ( 0 ));
338- assertEq (cfg.scopes, type ( uint8 ).max );
417+ assertEq (cfg.verifier, accountConfiguration. REVOKED_VERIFIER ( ));
418+ assertEq (cfg.scopes, 0 );
339419 }
340420
341421 function test_selfOwnerId_revokedCannotSignOwnerChanges () public {
@@ -442,6 +522,31 @@ contract ApplyConfigChangeOwnerTest is AccountConfigurationTest {
442522 accountConfiguration.applySignedOwnerChanges (account, uint64 (block .chainid ), changes, auth);
443523 }
444524
525+ function _implicitAuthorizeOwner (address account , uint256 pk , bytes32 newOwnerId , address verifier ) internal {
526+ _implicitAuthorizeOwnerWithScope (account, pk, newOwnerId, verifier, 0x00 );
527+ }
528+
529+ function _implicitAuthorizeOwnerWithScope (
530+ address account ,
531+ uint256 pk ,
532+ bytes32 newOwnerId ,
533+ address verifier ,
534+ uint8 scope
535+ ) internal {
536+ IAccountConfiguration.OwnerChange[] memory changes = new IAccountConfiguration.OwnerChange [](1 );
537+ changes[0 ] = IAccountConfiguration.OwnerChange ({
538+ ownerId: newOwnerId,
539+ changeType: 0x01 ,
540+ configData: abi.encode (IAccountConfiguration.OwnerConfig ({verifier: verifier, scopes: scope}))
541+ });
542+
543+ uint64 seq = accountConfiguration.getChangeSequences (account).local;
544+ bytes32 digest = _computeOwnerChangeBatchDigest (account, uint64 (block .chainid ), seq, changes);
545+ bytes memory auth = _buildImplicitEOAAuth (pk, digest);
546+
547+ accountConfiguration.applySignedOwnerChanges (account, uint64 (block .chainid ), changes, auth);
548+ }
549+
445550 function _lockAccount (address account ) internal {
446551 vm.prank (account);
447552 accountConfiguration.lock (1 hours);
0 commit comments