@towns-protocol/contracts 0.0.383 → 0.0.385

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,26 +3,50 @@ pragma solidity ^0.8.29;
3
3
 
4
4
  // interfaces
5
5
  import {IAppFactory} from "./IAppFactory.sol";
6
- import {ISimpleApp} from "../../helpers/ISimpleApp.sol";
7
6
  import {ITownsApp} from "../../ITownsApp.sol";
8
7
 
9
8
  // libraries
10
- import {LibClone} from "solady/utils/LibClone.sol";
11
9
  import {CustomRevert} from "../../../utils/libraries/CustomRevert.sol";
10
+ import {AppFactoryStorage} from "./AppFactoryStorage.sol";
12
11
 
13
12
  // contracts
14
13
  import {Facet} from "@towns-protocol/diamond/src/facets/Facet.sol";
15
14
  import {AppRegistryBase} from "../registry/AppRegistryBase.sol";
15
+ import {OwnableBase} from "@towns-protocol/diamond/src/facets/ownable/OwnableBase.sol";
16
16
  import {ReentrancyGuardTransient} from "solady/utils/ReentrancyGuardTransient.sol";
17
+ import {AppFactoryBase} from "./AppFactoryBase.sol";
17
18
 
18
19
  /// @title AppInstallerFacet
19
20
  /// @author Towns Protocol
20
21
  /// @notice Facet for installing apps to spaces
21
- contract AppFactoryFacet is IAppFactory, AppRegistryBase, ReentrancyGuardTransient, Facet {
22
+ contract AppFactoryFacet is
23
+ IAppFactory,
24
+ AppRegistryBase,
25
+ OwnableBase,
26
+ ReentrancyGuardTransient,
27
+ Facet,
28
+ AppFactoryBase
29
+ {
22
30
  using CustomRevert for bytes4;
23
31
 
24
- function __AppFactory_init() external onlyInitializing {
32
+ function __AppFactory_init(
33
+ Beacon[] calldata beacons,
34
+ address entryPoint
35
+ ) external onlyInitializing {
36
+ _addBeacons(beacons);
25
37
  _addInterface(type(IAppFactory).interfaceId);
38
+ _setEntryPoint(entryPoint);
39
+ }
40
+
41
+ function createAppByBeacon(
42
+ bytes32 beaconId,
43
+ AppParams calldata params
44
+ ) external payable nonReentrant returns (address app, bytes32 appId) {
45
+ _validateParams(params);
46
+
47
+ app = _createApp(beaconId, params);
48
+ appId = _registerApp(ITownsApp(app), params.client);
49
+ emit AppCreated(app, appId);
26
50
  }
27
51
 
28
52
  /// @notice Create an upgradeable simple app contract
@@ -30,23 +54,35 @@ contract AppFactoryFacet is IAppFactory, AppRegistryBase, ReentrancyGuardTransie
30
54
  function createApp(
31
55
  AppParams calldata params
32
56
  ) external payable nonReentrant returns (address app, bytes32 appId) {
33
- if (bytes(params.name).length == 0) AppFactory__InvalidAppName.selector.revertWith();
34
- if (params.permissions.length == 0) AppFactory__InvalidArrayInput.selector.revertWith();
35
- if (params.client == address(0)) AppFactory__InvalidAddressInput.selector.revertWith();
36
-
37
- uint48 duration = _validateDuration(params.accessDuration);
38
-
39
- app = LibClone.deployERC1967BeaconProxy(address(this));
40
- ISimpleApp(app).initialize(
41
- msg.sender,
42
- params.name,
43
- params.permissions,
44
- params.installPrice,
45
- duration,
46
- params.client
47
- );
57
+ _validateParams(params);
48
58
 
59
+ bytes32 beaconId = _getDefaultBeaconId();
60
+ app = _createApp(beaconId, params);
49
61
  appId = _registerApp(ITownsApp(app), params.client);
50
62
  emit AppCreated(app, appId);
51
63
  }
64
+
65
+ function addBeacons(Beacon[] calldata beacons) external onlyOwner {
66
+ _addBeacons(beacons);
67
+ }
68
+
69
+ function removeBeacons(bytes32[] calldata beaconIds) external onlyOwner {
70
+ _removeBeacons(beaconIds);
71
+ }
72
+
73
+ function setEntryPoint(address entryPoint) external onlyOwner {
74
+ _setEntryPoint(entryPoint);
75
+ }
76
+
77
+ function getBeacon(bytes32 beaconId) external view returns (address beacon) {
78
+ return _getBeacon(beaconId);
79
+ }
80
+
81
+ function getBeacons() external view returns (bytes32[] memory beaconIds) {
82
+ return _getBeacons();
83
+ }
84
+
85
+ function getEntryPoint() external view returns (address entryPoint) {
86
+ entryPoint = AppFactoryStorage.getLayout().entryPoint;
87
+ }
52
88
  }
@@ -0,0 +1,29 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.23;
3
+
4
+ // interfaces
5
+
6
+ // libraries
7
+ import {EnumerableSetLib} from "solady/utils/EnumerableSetLib.sol";
8
+
9
+ // contracts
10
+
11
+ library AppFactoryStorage {
12
+ using EnumerableSetLib for EnumerableSetLib.Bytes32Set;
13
+
14
+ // keccak256(abi.encode(uint256(keccak256("app.factory.storage")) - 1)) & ~bytes32(uint256(0xff))
15
+ bytes32 internal constant STORAGE_SLOT =
16
+ 0xca64e2f4a307cd87bb6e650a2b64b61bb8e05eb19e155e37ac786fdc5c4f0500;
17
+
18
+ struct Layout {
19
+ address entryPoint;
20
+ EnumerableSetLib.Bytes32Set beaconIds;
21
+ mapping(bytes32 beaconId => address beacon) beacons;
22
+ }
23
+
24
+ function getLayout() internal pure returns (Layout storage $) {
25
+ assembly {
26
+ $.slot := STORAGE_SLOT
27
+ }
28
+ }
29
+ }
@@ -16,12 +16,20 @@ interface IAppFactoryBase {
16
16
  uint48 accessDuration;
17
17
  }
18
18
 
19
+ struct Beacon {
20
+ bytes32 beaconId;
21
+ address beacon;
22
+ }
23
+
19
24
  /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
20
25
  /* ERRORS */
21
26
  /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
22
27
  error AppFactory__InvalidAppName();
23
28
  error AppFactory__InvalidArrayInput();
24
29
  error AppFactory__InvalidAddressInput();
30
+ error AppFactory__BeaconNotFound();
31
+ error AppFactory__InvalidBeaconId();
32
+ error AppFactory__BeaconAlreadyExists();
25
33
 
26
34
  /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
27
35
 
@@ -29,6 +37,9 @@ interface IAppFactoryBase {
29
37
  /* EVENTS */
30
38
  /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
31
39
  event AppCreated(address indexed app, bytes32 indexed uid);
40
+ event BeaconAdded(bytes32 indexed beaconId, address indexed beacon);
41
+ event BeaconRemoved(bytes32 indexed beaconId, address indexed beacon);
42
+ event EntryPointSet(address indexed oldEntryPoint, address indexed newEntryPoint);
32
43
  }
33
44
 
34
45
  interface IAppFactory is IAppFactoryBase {
@@ -39,4 +50,39 @@ interface IAppFactory is IAppFactoryBase {
39
50
  function createApp(
40
51
  AppParams calldata params
41
52
  ) external payable returns (address app, bytes32 appId);
53
+
54
+ /// @notice Create an app by beacon ID
55
+ /// @param beaconId The ID of the beacon to use for app deployment
56
+ /// @param params The parameters of the app
57
+ /// @return app The app address
58
+ /// @return appId The attestation UID of the registered app
59
+ function createAppByBeacon(
60
+ bytes32 beaconId,
61
+ AppParams calldata params
62
+ ) external payable returns (address app, bytes32 appId);
63
+
64
+ /// @notice Add new beacon contracts for app deployment
65
+ /// @param beacons Array of beacon contracts to add with their IDs
66
+ function addBeacons(Beacon[] calldata beacons) external;
67
+
68
+ /// @notice Remove existing beacon contracts
69
+ /// @param beaconIds Array of beacon IDs to remove
70
+ function removeBeacons(bytes32[] calldata beaconIds) external;
71
+
72
+ /// @notice Get the beacon contract address for a given beacon ID
73
+ /// @param beaconId The ID of the beacon to look up
74
+ /// @return beacon The address of the beacon contract
75
+ function getBeacon(bytes32 beaconId) external view returns (address beacon);
76
+
77
+ /// @notice Get all registered beacon IDs
78
+ /// @return beaconIds Array of all registered beacon IDs
79
+ function getBeacons() external view returns (bytes32[] memory beaconIds);
80
+
81
+ /// @notice Set the entry point contract address for account abstraction
82
+ /// @param entryPoint The address of the entry point contract
83
+ function setEntryPoint(address entryPoint) external;
84
+
85
+ /// @notice Get the current entry point contract address
86
+ /// @return entryPoint The address of the entry point contract
87
+ function getEntryPoint() external view returns (address entryPoint);
42
88
  }
@@ -8,7 +8,6 @@ import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";
8
8
  import {ITownsApp} from "../../ITownsApp.sol";
9
9
  import {IAppRegistryBase} from "./IAppRegistry.sol";
10
10
  import {ISchemaResolver} from "@ethereum-attestation-service/eas-contracts/resolver/ISchemaResolver.sol";
11
- import {ISimpleApp} from "../../helpers/ISimpleApp.sol";
12
11
  import {IPlatformRequirements} from "../../../factory/facets/platform/requirements/IPlatformRequirements.sol";
13
12
  import {IERC173} from "@towns-protocol/diamond/src/facets/ownable/IERC173.sol";
14
13
  import {IAppAccount} from "../../../spaces/facets/account/IAppAccount.sol";
@@ -20,6 +19,7 @@ import {AppRegistryStorage, ClientInfo, AppInfo} from "./AppRegistryStorage.sol"
20
19
  import {LibClone} from "solady/utils/LibClone.sol";
21
20
  import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
22
21
  import {CurrencyTransfer} from "../../../utils/libraries/CurrencyTransfer.sol";
22
+ import {LibAppRegistry} from "./LibAppRegistry.sol";
23
23
 
24
24
  // types
25
25
  import {ExecutionManifest} from "@erc6900/reference-implementation/interfaces/IExecutionModule.sol";
@@ -33,8 +33,6 @@ import {AttestationBase} from "../attest/AttestationBase.sol";
33
33
  abstract contract AppRegistryBase is IAppRegistryBase, SchemaBase, AttestationBase {
34
34
  using CustomRevert for bytes4;
35
35
 
36
- uint48 private constant MAX_DURATION = 365 days;
37
-
38
36
  modifier onlyAllowed(IAppAccount account) {
39
37
  if (IERC173(address(account)).owner() != msg.sender) NotAllowed.selector.revertWith();
40
38
  _;
@@ -99,10 +97,11 @@ abstract contract AppRegistryBase is IAppRegistryBase, SchemaBase, AttestationBa
99
97
 
100
98
  function _getAppDuration(address app) internal view returns (uint48 duration) {
101
99
  try ITownsApp(app).accessDuration() returns (uint48 accessDuration) {
102
- return _validateDuration(accessDuration);
100
+ duration = LibAppRegistry.validateDuration(accessDuration);
103
101
  } catch {
104
- return MAX_DURATION;
102
+ duration = LibAppRegistry.validateDuration(0);
105
103
  }
104
+ return duration;
106
105
  }
107
106
 
108
107
  /// @notice Retrieves detailed information about an app by its ID
@@ -389,18 +388,6 @@ abstract contract AppRegistryBase is IAppRegistryBase, SchemaBase, AttestationBa
389
388
  newVersionId = _attest(msg.sender, msg.value, request).uid;
390
389
  }
391
390
 
392
- function _validatePricing(uint256 price) internal view returns (uint256) {
393
- uint256 minPlatformFee = _getPlatformRequirements().getMembershipFee();
394
- if (price > 0 && price < minPlatformFee) InvalidPrice.selector.revertWith();
395
- return price;
396
- }
397
-
398
- function _validateDuration(uint48 duration) internal pure returns (uint48) {
399
- if (duration > MAX_DURATION) InvalidDuration.selector.revertWith();
400
- if (duration == 0) duration = MAX_DURATION;
401
- return duration;
402
- }
403
-
404
391
  /// @notice Validates inputs for adding a new app
405
392
  /// @param appContract The app contract to verify
406
393
  /// @param client The client address to verify
@@ -434,8 +421,8 @@ abstract contract AppRegistryBase is IAppRegistryBase, SchemaBase, AttestationBa
434
421
  if (permissions.length == 0) InvalidArrayInput.selector.revertWith();
435
422
  if (owner == address(0)) InvalidAddressInput.selector.revertWith();
436
423
 
437
- _validatePricing(installPrice);
438
- uint48 duration = _validateDuration(accessDuration);
424
+ installPrice = LibAppRegistry.validatePricing(_getPlatformRequirements(), installPrice);
425
+ accessDuration = LibAppRegistry.validateDuration(accessDuration);
439
426
 
440
427
  if (
441
428
  !IERC165(appAddress).supportsInterface(type(IModule).interfaceId) ||
@@ -445,7 +432,7 @@ abstract contract AppRegistryBase is IAppRegistryBase, SchemaBase, AttestationBa
445
432
  AppDoesNotImplementInterface.selector.revertWith();
446
433
  }
447
434
 
448
- return (owner, permissions, manifest, duration);
435
+ return (owner, permissions, manifest, accessDuration);
449
436
  }
450
437
 
451
438
  function _getPlatformRequirements() internal view returns (IPlatformRequirements) {
@@ -35,13 +35,14 @@ interface IAppRegistryBase {
35
35
  error InvalidArrayInput();
36
36
  error BannedApp();
37
37
  error InvalidAppId();
38
- error InvalidPrice();
39
- error InvalidDuration();
40
38
  error InsufficientPayment();
41
39
  error NotAllowed();
42
40
  error ClientAlreadyRegistered();
43
41
  error ClientNotRegistered();
44
42
 
43
+ error AppRegistry__InvalidDuration();
44
+ error AppRegistry__InvalidPrice();
45
+
45
46
  /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
46
47
  /* EVENTS */
47
48
  /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
@@ -0,0 +1,33 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.29;
3
+
4
+ // interfaces
5
+ import {IPlatformRequirements} from "../../../factory/facets/platform/requirements/IPlatformRequirements.sol";
6
+ import {IAppRegistryBase} from "./IAppRegistry.sol";
7
+
8
+ // libraries
9
+ import {CustomRevert} from "../../../utils/libraries/CustomRevert.sol";
10
+
11
+ // contracts
12
+
13
+ library LibAppRegistry {
14
+ using CustomRevert for bytes4;
15
+
16
+ uint48 internal constant MAX_DURATION = 365 days;
17
+
18
+ function validateDuration(uint48 accessDuration) internal pure returns (uint48 duration) {
19
+ if (accessDuration > MAX_DURATION)
20
+ IAppRegistryBase.AppRegistry__InvalidDuration.selector.revertWith();
21
+ duration = accessDuration == 0 ? MAX_DURATION : accessDuration;
22
+ }
23
+
24
+ function validatePricing(
25
+ IPlatformRequirements platformRequirements,
26
+ uint256 price
27
+ ) internal view returns (uint256) {
28
+ uint256 minPlatformFee = platformRequirements.getMembershipFee();
29
+ if (price > 0 && price < minPlatformFee)
30
+ IAppRegistryBase.AppRegistry__InvalidPrice.selector.revertWith();
31
+ return price;
32
+ }
33
+ }
@@ -0,0 +1,40 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.23;
3
+
4
+ // interfaces
5
+ import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol";
6
+
7
+ // libraries
8
+
9
+ // contracts
10
+
11
+ interface ISimpleAccountBase {
12
+ error SimpleAccount__NotFromTrustedCaller();
13
+ error SimpleAccount__OpDataNotSupported();
14
+
15
+ /// @notice Emitted when the entry point is updated
16
+ /// @param oldEntryPoint The old entry point
17
+ /// @param newEntryPoint The new entry point
18
+ event EntryPointUpdated(address indexed oldEntryPoint, address indexed newEntryPoint);
19
+
20
+ /// @notice Emitted when the coordinator is updated
21
+ /// @param oldCoordinator The old coordinator
22
+ /// @param newCoordinator The new coordinator
23
+ event CoordinatorUpdated(address indexed oldCoordinator, address indexed newCoordinator);
24
+ }
25
+
26
+ interface ISimpleAccount is ISimpleAccountBase {
27
+ /// @notice Return the account nonce.
28
+ /// @dev This method returns the next sequential nonce.
29
+ /// @dev For a nonce of a specific key, use `entrypoint.getNonce(account, key)`
30
+ /// @return The next sequential nonce.
31
+ function getNonce() external view returns (uint256);
32
+
33
+ /// @notice Updates the entry point of the account
34
+ /// @param newEntryPoint The new entry point
35
+ function updateEntryPoint(address newEntryPoint) external;
36
+
37
+ /// @notice Updates the coordinator of the account
38
+ /// @param newCoordinator The new coordinator
39
+ function updateCoordinator(address newCoordinator) external;
40
+ }
@@ -0,0 +1,58 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.29;
3
+
4
+ // interfaces
5
+ import {IAccount} from "@eth-infinitism/account-abstraction/interfaces/IAccount.sol";
6
+ import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol";
7
+ import {ISimpleAccountBase} from "./ISimpleAccount.sol";
8
+
9
+ // libraries
10
+ import {UserOperationLib, PackedUserOperation} from "@eth-infinitism/account-abstraction/core/UserOperationLib.sol";
11
+ import {CustomRevert} from "../../../utils/libraries/CustomRevert.sol";
12
+ import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
13
+
14
+ /// @title SimpleAccountBase
15
+ /// @notice Base contract for simple accounts implementing core ERC-6900 account functionality
16
+ abstract contract SimpleAccountBase is IAccount, ISimpleAccountBase {
17
+ using UserOperationLib for PackedUserOperation;
18
+ using CustomRevert for bytes4;
19
+
20
+ /// @notice Return the entryPoint used by this account.
21
+ /// @dev Subclass should return the current entryPoint used by this account.
22
+ function entryPoint() public view virtual returns (IEntryPoint);
23
+
24
+ /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
25
+ /* Hooks */
26
+ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
27
+
28
+ /// @dev Internal function to require the sender to be the entry point.
29
+ function _requireFromEntryPoint() internal view virtual {
30
+ if (msg.sender != address(entryPoint()))
31
+ SimpleAccount__NotFromTrustedCaller.selector.revertWith();
32
+ }
33
+
34
+ /// @dev Internal function to require the sender to be the entry point.
35
+ function _requireForExecute() internal view virtual {
36
+ _requireFromEntryPoint();
37
+ }
38
+
39
+ /// @dev Internal function to validate the signature of the user operation.
40
+ /// @param userOp The user operation to validate.
41
+ /// @param userOpHash The hash of the user operation.
42
+ /// @return validationData The validation data.
43
+ function _validateSignature(
44
+ PackedUserOperation calldata userOp,
45
+ bytes32 userOpHash
46
+ ) internal virtual returns (uint256 validationData);
47
+
48
+ /// @dev Internal function to validate the nonce of the user operation.
49
+ /// @param nonce The nonce of the user operation.
50
+ function _validateNonce(uint256 nonce) internal view virtual {}
51
+
52
+ /// @dev Internal function to pay the prefund.
53
+ /// @param missingAccountFunds The missing account funds.
54
+ function _payPrefund(uint256 missingAccountFunds) internal virtual {
55
+ if (missingAccountFunds == 0) return;
56
+ SafeTransferLib.safeTransferETH(msg.sender, missingAccountFunds);
57
+ }
58
+ }
@@ -0,0 +1,151 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.29;
3
+
4
+ // interfaces
5
+ import {IEntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol";
6
+ import {IAccount} from "@eth-infinitism/account-abstraction/interfaces/IAccount.sol";
7
+ import {ISimpleAccount} from "./ISimpleAccount.sol";
8
+ import {IERC7821} from "@openzeppelin/contracts/interfaces/draft-IERC7821.sol";
9
+
10
+ // contracts
11
+ import {SimpleAccountBase} from "./SimpleAccountBase.sol";
12
+ import {Facet} from "@towns-protocol/diamond/src/facets/Facet.sol";
13
+ import {OwnableBase} from "@towns-protocol/diamond/src/facets/ownable/OwnableBase.sol";
14
+ import {ERC7821} from "solady/accounts/ERC7821.sol";
15
+
16
+ // libraries
17
+ import {CustomRevert} from "../../../utils/libraries/CustomRevert.sol";
18
+ import {UserOperationLib, PackedUserOperation} from "@eth-infinitism/account-abstraction/core/UserOperationLib.sol";
19
+ import {ECDSA} from "solady/utils/ECDSA.sol";
20
+ import {LibBit} from "solady/utils/LibBit.sol";
21
+ import {SIG_VALIDATION_FAILED, SIG_VALIDATION_SUCCESS} from "@eth-infinitism/account-abstraction/core/Helpers.sol";
22
+ import {SimpleAppStorage} from "../app/SimpleAppStorage.sol";
23
+ import {SimpleAccountStorage} from "./SimpleAccountStorage.sol";
24
+ import {Validator} from "../../../utils/libraries/Validator.sol";
25
+
26
+ contract SimpleAccountFacet is ISimpleAccount, SimpleAccountBase, OwnableBase, ERC7821, Facet {
27
+ using CustomRevert for bytes4;
28
+ using UserOperationLib for PackedUserOperation;
29
+
30
+ function __SimpleAccountFacet_init(
31
+ address entrypoint,
32
+ address coordinator
33
+ ) external onlyInitializing {
34
+ Validator.checkAddress(entrypoint);
35
+ Validator.checkAddress(coordinator);
36
+ __SimpleAccountFacet_init_unchained(entrypoint, coordinator);
37
+ }
38
+
39
+ function __SimpleAccountFacet_init_unchained(address entrypoint, address coordinator) internal {
40
+ SimpleAccountStorage.Layout storage $ = SimpleAccountStorage.getLayout();
41
+ $.entryPoint = entrypoint;
42
+ $.coordinator = coordinator;
43
+ _addInterface(type(IAccount).interfaceId);
44
+ _addInterface(type(ISimpleAccount).interfaceId);
45
+ _addInterface(type(IERC7821).interfaceId);
46
+ }
47
+
48
+ /// @inheritdoc ISimpleAccount
49
+ function updateEntryPoint(address newEntryPoint) external onlyOwner {
50
+ Validator.checkAddress(newEntryPoint);
51
+ SimpleAccountStorage.Layout storage $ = SimpleAccountStorage.getLayout();
52
+ address oldEntryPoint = $.entryPoint;
53
+ $.entryPoint = newEntryPoint;
54
+ emit EntryPointUpdated(oldEntryPoint, newEntryPoint);
55
+ }
56
+
57
+ /// @inheritdoc ISimpleAccount
58
+ function updateCoordinator(address newCoordinator) external onlyOwner {
59
+ Validator.checkAddress(newCoordinator);
60
+ SimpleAccountStorage.Layout storage $ = SimpleAccountStorage.getLayout();
61
+ address oldCoordinator = $.coordinator;
62
+ $.coordinator = newCoordinator;
63
+ emit CoordinatorUpdated(oldCoordinator, newCoordinator);
64
+ }
65
+
66
+ /// @notice Return the account nonce.
67
+ /// @dev This method returns the next sequential nonce.
68
+ /// @dev For a nonce of a specific key, use `entrypoint.getNonce(account, key)`
69
+ /// @return The next sequential nonce.
70
+ function getNonce() public view virtual returns (uint256) {
71
+ return entryPoint().getNonce(address(this), 0);
72
+ }
73
+
74
+ /// @notice Return the entryPoint used by this account.
75
+ function entryPoint() public view override(SimpleAccountBase) returns (IEntryPoint) {
76
+ return IEntryPoint(SimpleAccountStorage.getLayout().entryPoint);
77
+ }
78
+
79
+ /// @inheritdoc IAccount
80
+ function validateUserOp(
81
+ PackedUserOperation calldata userOp,
82
+ bytes32 userOpHash,
83
+ uint256 missingAccountFunds
84
+ ) external virtual override returns (uint256 validationData) {
85
+ _requireFromEntryPoint();
86
+ validationData = _validateSignature(userOp, userOpHash);
87
+ _validateNonce(userOp.nonce);
88
+ _payPrefund(missingAccountFunds);
89
+ }
90
+
91
+ /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
92
+ /* INTERNAL FUNCTIONS */
93
+ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
94
+ function _getCoordinator() internal view returns (address) {
95
+ return SimpleAccountStorage.getLayout().coordinator;
96
+ }
97
+
98
+ /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
99
+ /* Overrides */
100
+ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
101
+ /// @dev Override the 4-parameter execution to add custom authorization before the standard checks
102
+ function _execute(
103
+ bytes32,
104
+ bytes calldata,
105
+ Call[] calldata calls,
106
+ bytes calldata opData
107
+ ) internal virtual override {
108
+ if (opData.length == 0) {
109
+ _requireForExecute();
110
+ return _execute(calls, bytes32(0));
111
+ }
112
+
113
+ SimpleAccount__OpDataNotSupported.selector.revertWith();
114
+ }
115
+
116
+ /// @notice Override the _requireForExecute function to allow the owner, client, coordinator, entry point, and self-calls.
117
+ function _requireForExecute() internal view override {
118
+ // Early return for owner
119
+ if (msg.sender == _owner()) return;
120
+
121
+ SimpleAppStorage.Layout storage $ = SimpleAppStorage.getLayout();
122
+ SimpleAccountStorage.Layout storage $$ = SimpleAccountStorage.getLayout();
123
+
124
+ // Use LibBit.or for efficient multi-condition check
125
+ if (
126
+ LibBit.or(
127
+ LibBit.or(msg.sender == $.client, msg.sender == $$.coordinator),
128
+ LibBit.or(msg.sender == $$.entryPoint, msg.sender == address(this))
129
+ )
130
+ ) return;
131
+
132
+ SimpleAccount__NotFromTrustedCaller.selector.revertWith();
133
+ }
134
+
135
+ function _validateSignature(
136
+ PackedUserOperation calldata userOp,
137
+ bytes32 userOpHash
138
+ ) internal virtual override returns (uint256 validationData) {
139
+ address signer = ECDSA.recoverCalldata(userOpHash, userOp.signature);
140
+ address owner = _owner();
141
+
142
+ // Early return for owner
143
+ if (signer == owner) return SIG_VALIDATION_SUCCESS;
144
+
145
+ // Check client using LibBit for consistency
146
+ SimpleAppStorage.Layout storage $ = SimpleAppStorage.getLayout();
147
+ if (signer == $.client) return SIG_VALIDATION_SUCCESS;
148
+
149
+ return SIG_VALIDATION_FAILED;
150
+ }
151
+ }
@@ -0,0 +1,25 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.29;
3
+
4
+ // interfaces
5
+
6
+ // libraries
7
+
8
+ // contracts
9
+
10
+ library SimpleAccountStorage {
11
+ // keccak256(abi.encode(uint256(keccak256("simple.account.storage")) - 1)) & ~bytes32(uint256(0xff))
12
+ bytes32 internal constant STORAGE_SLOT =
13
+ 0x50b1c084b04d93141a198030532d2108cf8654a43d025af0de681aa06dac5f00;
14
+
15
+ struct Layout {
16
+ address entryPoint;
17
+ address coordinator;
18
+ }
19
+
20
+ function getLayout() internal pure returns (Layout storage $) {
21
+ assembly ("memory-safe") {
22
+ $.slot := STORAGE_SLOT
23
+ }
24
+ }
25
+ }