Skip to content

Commit 7997f6a

Browse files
feat: allow p256 keys to be super admins (#378)
* feat: allow p256 keys to be super admins * chore: bump contract versions due to bytecode changes - Contracts updated: IthacaAccount --------- Co-authored-by: GitHub Action <[email protected]>
1 parent 07c9780 commit 7997f6a

File tree

9 files changed

+16
-439
lines changed

9 files changed

+16
-439
lines changed

.gitmodules

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,3 @@
1717
path = lib/solady
1818
url = https://github.com/vectorized/solady
1919
branch = main
20-
[submodule "lib/account_v0_5_5"]
21-
path = lib/account_v0_5_5
22-
url = https://github.com/ithacaxyz/account

lib/account_v0_5_5

Lines changed: 0 additions & 1 deletion
This file was deleted.

snapshots/BenchmarkTest.json

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616
"testERC20Transfer_ERC4337MinimalAccount": "171906",
1717
"testERC20Transfer_ERC4337MinimalAccount_AppSponsor": "168917",
1818
"testERC20Transfer_ERC4337MinimalAccount_ERC20SelfPay": "217488",
19-
"testERC20Transfer_IthacaAccount": "129986",
20-
"testERC20Transfer_IthacaAccountWithSpendLimits": "193499",
21-
"testERC20Transfer_IthacaAccount_AppSponsor": "141385",
22-
"testERC20Transfer_IthacaAccount_AppSponsor_ERC20": "163850",
23-
"testERC20Transfer_IthacaAccount_ERC20SelfPay": "147639",
19+
"testERC20Transfer_IthacaAccount": "129918",
20+
"testERC20Transfer_IthacaAccountWithSpendLimits": "190799",
21+
"testERC20Transfer_IthacaAccount_AppSponsor": "141362",
22+
"testERC20Transfer_IthacaAccount_AppSponsor_ERC20": "163827",
23+
"testERC20Transfer_IthacaAccount_ERC20SelfPay": "147571",
2424
"testERC20Transfer_Safe4337": "197515",
2525
"testERC20Transfer_Safe4337_AppSponsor": "191679",
2626
"testERC20Transfer_Safe4337_ERC20SelfPay": "238658",
@@ -29,25 +29,25 @@
2929
"testERC20Transfer_ZerodevKernel_ERC20SelfPay": "252683",
3030
"testERC20Transfer_batch100_AlchemyModularAccount": "10104466",
3131
"testERC20Transfer_batch100_AlchemyModularAccount_ERC20SelfPay": "11635798",
32-
"testERC20Transfer_batch100_IthacaAccount": "7702476",
33-
"testERC20Transfer_batch100_IthacaAccount_AppSponsor": "8397224",
34-
"testERC20Transfer_batch100_IthacaAccount_AppSponsor_ERC20": "8246556",
35-
"testERC20Transfer_batch100_IthacaAccount_ERC20SelfPay": "7547236",
32+
"testERC20Transfer_batch100_IthacaAccount": "7695676",
33+
"testERC20Transfer_batch100_IthacaAccount_AppSponsor": "8394924",
34+
"testERC20Transfer_batch100_IthacaAccount_AppSponsor_ERC20": "8244256",
35+
"testERC20Transfer_batch100_IthacaAccount_ERC20SelfPay": "7540436",
3636
"testERC20Transfer_batch100_ZerodevKernel": "12626718",
3737
"testERC20Transfer_batch100_ZerodevKernel_ERC20SelfPay": "14176437",
3838
"testNativeTransfer_AlchemyModularAccount": "180829",
3939
"testNativeTransfer_CoinbaseSmartWallet": "178916",
40-
"testNativeTransfer_IthacaAccount": "131388",
41-
"testNativeTransfer_IthacaAccount_AppSponsor": "142818",
42-
"testNativeTransfer_IthacaAccount_ERC20SelfPay": "156341",
40+
"testNativeTransfer_IthacaAccount": "131320",
41+
"testNativeTransfer_IthacaAccount_AppSponsor": "142795",
42+
"testNativeTransfer_IthacaAccount_ERC20SelfPay": "156273",
4343
"testNativeTransfer_Safe4337": "198595",
4444
"testNativeTransfer_ZerodevKernel": "208635",
4545
"testUniswapV2Swap_AlchemyModularAccount": "238767",
4646
"testUniswapV2Swap_CoinbaseSmartWallet": "237571",
4747
"testUniswapV2Swap_ERC4337MinimalAccount": "231242",
48-
"testUniswapV2Swap_IthacaAccount": "189296",
49-
"testUniswapV2Swap_IthacaAccount_AppSponsor": "200671",
50-
"testUniswapV2Swap_IthacaAccount_ERC20SelfPay": "211749",
48+
"testUniswapV2Swap_IthacaAccount": "189228",
49+
"testUniswapV2Swap_IthacaAccount_AppSponsor": "200648",
50+
"testUniswapV2Swap_IthacaAccount_ERC20SelfPay": "211681",
5151
"testUniswapV2Swap_Safe4337": "257453",
5252
"testUniswapV2Swap_ZerodevKernel": "266487"
5353
}

src/GuardedExecutor.sol

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,6 @@ abstract contract GuardedExecutor is ERC7821 {
120120
/// @dev Emitted when a spend limit is removed.
121121
event SpendLimitRemoved(bytes32 keyHash, address token, SpendPeriod period);
122122

123-
/// @dev Emitted when spend limits are enabled or disabled for a key.
124-
event SpendLimitsEnabledSet(bytes32 keyHash, bool enabled);
125-
126123
////////////////////////////////////////////////////////////////////////
127124
// Constants
128125
////////////////////////////////////////////////////////////////////////
@@ -184,8 +181,6 @@ abstract contract GuardedExecutor is ERC7821 {
184181
SpendStorage spends;
185182
/// @dev Mapping of 3rd-party checkers for determining if an address can execute a function.
186183
EnumerableMapLib.AddressToAddressMap callCheckers;
187-
/// @dev Whether spend limits are disabled for this key. Defaults to false (limits enabled).
188-
bool spendLimitsDisabled;
189184
}
190185

191186
/// @dev Returns the storage pointer.
@@ -234,11 +229,6 @@ abstract contract GuardedExecutor is ERC7821 {
234229
return ERC7821._execute(calls, keyHash);
235230
}
236231

237-
// If spend limits are disabled for this key, execute without spend checks
238-
if (!spendLimitsEnabled(keyHash)) {
239-
return ERC7821._execute(calls, keyHash);
240-
}
241-
242232
SpendStorage storage spends = _getGuardedExecutorKeyStorage(keyHash).spends;
243233
_ExecuteTemps memory t;
244234

@@ -469,21 +459,6 @@ abstract contract GuardedExecutor is ERC7821 {
469459
emit SpendLimitRemoved(keyHash, token, period);
470460
}
471461

472-
/// @dev Sets whether spend limits are enabled or disabled for a specific key.
473-
/// By default, spend limits are enabled for all keys.
474-
function setSpendLimitsEnabled(bytes32 keyHash, bool enabled)
475-
public
476-
virtual
477-
onlyThis
478-
checkKeyHashIsNonZero(keyHash)
479-
{
480-
if (_isSuperAdmin(keyHash)) revert SuperAdminCanSpendAnything();
481-
482-
_getGuardedExecutorKeyStorage(keyHash).spendLimitsDisabled = !enabled;
483-
484-
emit SpendLimitsEnabledSet(keyHash, enabled);
485-
}
486-
487462
////////////////////////////////////////////////////////////////////////
488463
// Public View Functions
489464
////////////////////////////////////////////////////////////////////////
@@ -614,13 +589,6 @@ abstract contract GuardedExecutor is ERC7821 {
614589
}
615590
}
616591

617-
/// @dev Returns whether spend limits are enabled for a specific key.
618-
/// Defaults to true if never set.
619-
function spendLimitsEnabled(bytes32 keyHash) public view virtual returns (bool) {
620-
// disabled defaults to false, so !false = true (enabled by default)
621-
return !_getGuardedExecutorKeyStorage(keyHash).spendLimitsDisabled;
622-
}
623-
624592
/// @dev Rounds the unix timestamp down to the period.
625593
function startOfSpendPeriod(uint256 unixTimestamp, SpendPeriod period)
626594
public

src/IthacaAccount.sol

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -575,9 +575,6 @@ contract IthacaAccount is IIthacaAccount, EIP712, GuardedExecutor {
575575

576576
/// @dev Adds the key. If the key already exist, its expiry will be updated.
577577
function _addKey(Key memory key) internal virtual returns (bytes32 keyHash) {
578-
if (key.isSuperAdmin) {
579-
if (!_keyTypeCanBeSuperAdmin(key.keyType)) revert KeyTypeCannotBeSuperAdmin();
580-
}
581578
// `keccak256(abi.encode(key.keyType, keccak256(key.publicKey)))`.
582579
keyHash = hash(key);
583580
AccountStorage storage $ = _getAccountStorage();
@@ -587,11 +584,6 @@ contract IthacaAccount is IIthacaAccount, EIP712, GuardedExecutor {
587584
$.keyHashes.add(keyHash);
588585
}
589586

590-
/// @dev Returns if the `keyType` can be a super admin.
591-
function _keyTypeCanBeSuperAdmin(KeyType keyType) internal view virtual returns (bool) {
592-
return keyType != KeyType.P256;
593-
}
594-
595587
/// @dev Removes the key corresponding to the `keyHash`. Reverts if the key does not exist.
596588
function _removeKey(bytes32 keyHash) internal virtual {
597589
AccountStorage storage $ = _getAccountStorage();
@@ -763,6 +755,6 @@ contract IthacaAccount is IIthacaAccount, EIP712, GuardedExecutor {
763755
returns (string memory name, string memory version)
764756
{
765757
name = "IthacaAccount";
766-
version = "0.5.9";
758+
version = "0.5.10";
767759
}
768760
}

test/Account.t.sol

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -260,29 +260,6 @@ contract AccountTest is BaseTest {
260260
assert(keys[1].expiry == 5);
261261
}
262262

263-
function testAddDisallowedSuperAdminKeyTypeReverts() public {
264-
address orchestrator = address(new Orchestrator());
265-
address accountImplementation = address(new IthacaAccount(address(orchestrator)));
266-
address accountProxy = address(LibEIP7702.deployProxy(accountImplementation, address(0)));
267-
account = MockAccount(payable(accountProxy));
268-
269-
DelegatedEOA memory d = _randomEIP7702DelegatedEOA();
270-
271-
PassKey memory k = _randomSecp256k1PassKey();
272-
k.k.isSuperAdmin = true;
273-
274-
vm.startPrank(d.eoa);
275-
276-
d.d.authorize(k.k);
277-
278-
k = _randomSecp256r1PassKey();
279-
k.k.isSuperAdmin = true;
280-
vm.expectRevert(bytes4(keccak256("KeyTypeCannotBeSuperAdmin()")));
281-
d.d.authorize(k.k);
282-
283-
vm.stopPrank();
284-
}
285-
286263
function testCrossChainKeyPreCallsAuthorization() public {
287264
// Setup Keys
288265
PassKey memory adminKey = _randomSecp256k1PassKey();

test/GuardedExecutor.t.sol

Lines changed: 0 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -654,127 +654,6 @@ contract GuardedExecutorTest is BaseTest {
654654
}
655655
}
656656

657-
function testSetSpendLimitsEnabled() public {
658-
Orchestrator.Intent memory u;
659-
DelegatedEOA memory d = _randomEIP7702DelegatedEOA();
660-
661-
u.eoa = d.eoa;
662-
u.combinedGas = 1000000;
663-
u.nonce = d.d.getNonce(0);
664-
665-
PassKey memory k = _randomSecp256k1PassKey();
666-
667-
address token = LibClone.clone(address(paymentToken));
668-
_mint(token, u.eoa, type(uint192).max);
669-
670-
// Test that spend limits are enabled by default for the key
671-
assertTrue(d.d.spendLimitsEnabled(k.keyHash), "Spend limits should be enabled by default");
672-
673-
// Authorize the key and set up spend limit
674-
{
675-
ERC7821.Call[] memory calls = new ERC7821.Call[](3);
676-
calls[0].data = abi.encodeWithSelector(IthacaAccount.authorize.selector, k.k);
677-
// Only allow the key to execute on the token, not on the account itself
678-
calls[1].data = abi.encodeWithSelector(
679-
GuardedExecutor.setCanExecute.selector, k.keyHash, token, _ANY_FN_SEL, true
680-
);
681-
calls[2] = _setSpendLimitCall(k, token, GuardedExecutor.SpendPeriod.Day, 1 ether);
682-
683-
u.executionData = abi.encode(calls);
684-
u.nonce = 0xc1d0 << 240;
685-
u.signature = _eoaSig(d.privateKey, u);
686-
687-
assertEq(oc.execute(abi.encode(u)), 0);
688-
}
689-
690-
// Test that non-EOA cannot call setSpendLimitsEnabled
691-
{
692-
ERC7821.Call[] memory calls = new ERC7821.Call[](1);
693-
calls[0].to = address(0);
694-
calls[0].data = abi.encodeWithSelector(
695-
GuardedExecutor.setSpendLimitsEnabled.selector, k.keyHash, false
696-
);
697-
698-
u.nonce = d.d.getNonce(0);
699-
u.executionData = abi.encode(calls);
700-
u.signature = _sig(k, u);
701-
702-
// This should fail because setSpendLimitsEnabled requires msg.sender == address(this)
703-
// and the key is not authorized to self-execute
704-
assertEq(
705-
oc.execute(abi.encode(u)),
706-
bytes4(keccak256("UnauthorizedCall(bytes32,address,bytes)"))
707-
);
708-
}
709-
710-
// Test that EOA can disable spend limits with event
711-
{
712-
ERC7821.Call[] memory calls = new ERC7821.Call[](1);
713-
calls[0].to = address(0);
714-
calls[0].data = abi.encodeWithSelector(
715-
GuardedExecutor.setSpendLimitsEnabled.selector, k.keyHash, false
716-
);
717-
718-
u.nonce = d.d.getNonce(0);
719-
u.executionData = abi.encode(calls);
720-
u.signature = _eoaSig(d.privateKey, u);
721-
722-
vm.expectEmit(true, true, true, true);
723-
emit GuardedExecutor.SpendLimitsEnabledSet(k.keyHash, false);
724-
725-
assertEq(oc.execute(abi.encode(u)), 0);
726-
assertFalse(d.d.spendLimitsEnabled(k.keyHash), "Spend limits should be disabled");
727-
}
728-
729-
// Test that when disabled, spend limits are not enforced (can spend beyond limit)
730-
{
731-
ERC7821.Call[] memory calls = new ERC7821.Call[](1);
732-
// Try to transfer 2 ether (exceeds the 1 ether daily limit)
733-
calls[0] = _transferCall2(token, address(0xb0b), 2 ether);
734-
735-
u.nonce = d.d.getNonce(0);
736-
u.executionData = abi.encode(calls);
737-
u.signature = _sig(k, u);
738-
739-
// Should succeed even though it exceeds the limit
740-
assertEq(oc.execute(abi.encode(u)), 0);
741-
assertEq(_balanceOf(token, address(0xb0b)), 2 ether);
742-
}
743-
744-
// Test that EOA can re-enable spend limits with event
745-
{
746-
ERC7821.Call[] memory calls = new ERC7821.Call[](1);
747-
calls[0].to = address(0);
748-
calls[0].data = abi.encodeWithSelector(
749-
GuardedExecutor.setSpendLimitsEnabled.selector, k.keyHash, true
750-
);
751-
752-
u.nonce = d.d.getNonce(0);
753-
u.executionData = abi.encode(calls);
754-
u.signature = _eoaSig(d.privateKey, u);
755-
756-
vm.expectEmit(true, true, true, true);
757-
emit GuardedExecutor.SpendLimitsEnabledSet(k.keyHash, true);
758-
759-
assertEq(oc.execute(abi.encode(u)), 0);
760-
assertTrue(d.d.spendLimitsEnabled(k.keyHash), "Spend limits should be enabled again");
761-
}
762-
763-
// Test that when re-enabled, spend limits are enforced again
764-
{
765-
ERC7821.Call[] memory calls = new ERC7821.Call[](1);
766-
// Try to transfer 2 ether again (exceeds the 1 ether daily limit)
767-
calls[0] = _transferCall2(token, address(0xb0b), 2 ether);
768-
769-
u.nonce = d.d.getNonce(0);
770-
u.executionData = abi.encode(calls);
771-
u.signature = _sig(k, u);
772-
773-
// Should fail now that limits are enforced
774-
assertEq(oc.execute(abi.encode(u)), bytes4(keccak256("ExceededSpendLimit(address)")));
775-
}
776-
}
777-
778657
function testSpends(bytes32) public {
779658
Orchestrator.Intent memory u;
780659
DelegatedEOA memory d = _randomEIP7702DelegatedEOA();

0 commit comments

Comments
 (0)