@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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@towns-protocol/contracts",
3
- "version": "0.0.383",
3
+ "version": "0.0.385",
4
4
  "packageManager": "yarn@3.8.0",
5
5
  "scripts": {
6
6
  "build-types": "bash scripts/build-contract-types.sh",
@@ -35,7 +35,7 @@
35
35
  "@layerzerolabs/oapp-evm": "^0.3.2",
36
36
  "@openzeppelin/merkle-tree": "^1.0.8",
37
37
  "@prb/test": "^0.6.4",
38
- "@towns-protocol/prettier-config": "^0.0.383",
38
+ "@towns-protocol/prettier-config": "^0.0.385",
39
39
  "@typechain/ethers-v5": "^11.1.2",
40
40
  "@wagmi/cli": "^2.2.0",
41
41
  "forge-std": "github:foundry-rs/forge-std#v1.10.0",
@@ -57,5 +57,5 @@
57
57
  "publishConfig": {
58
58
  "access": "public"
59
59
  },
60
- "gitHead": "7c6bb682702d1d6977ee42ce7d249c2bd4f62c85"
60
+ "gitHead": "2b60b355cb85529d292b3702a695750f5c884ab7"
61
61
  }
@@ -3,6 +3,7 @@ pragma solidity ^0.8.23;
3
3
 
4
4
  // interfaces
5
5
  import {IDiamondInitHelper} from "./IDiamondInitHelper.sol";
6
+ import {IAppFactory, IAppFactoryBase} from "src/apps/facets/factory/IAppFactory.sol";
6
7
 
7
8
  // libraries
8
9
  import {DeployDiamondCut} from "@towns-protocol/diamond/scripts/deployments/facets/DeployDiamondCut.sol";
@@ -12,15 +13,16 @@ import {DeployOwnable} from "@towns-protocol/diamond/scripts/deployments/facets/
12
13
  import {LibString} from "solady/utils/LibString.sol";
13
14
  import {DeployMetadata} from "../facets/DeployMetadata.s.sol";
14
15
  import {DeployAppRegistryFacet} from "../facets/DeployAppRegistryFacet.s.sol";
15
- import {DeployUpgradeableBeacon} from "../facets/DeployUpgradeableBeacon.s.sol";
16
16
  import {DeployAppInstallerFacet} from "../facets/DeployAppInstallerFacet.s.sol";
17
17
  import {DeployAppFactoryFacet} from "../facets/DeployAppFactoryFacet.s.sol";
18
18
  import {DeploySpaceFactory} from "../diamonds/DeploySpaceFactory.s.sol";
19
+ import {DeploySimpleAppBeacon} from "../diamonds/DeploySimpleAppBeacon.s.sol";
19
20
 
20
21
  // contracts
21
22
  import {Diamond} from "@towns-protocol/diamond/src/Diamond.sol";
22
23
  import {MultiInit} from "@towns-protocol/diamond/src/initializers/MultiInit.sol";
23
24
  import {DiamondHelper} from "@towns-protocol/diamond/scripts/common/helpers/DiamondHelper.s.sol";
25
+ import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol";
24
26
 
25
27
  // deployers
26
28
  import {DeployFacet} from "../../common/DeployFacet.s.sol";
@@ -31,9 +33,11 @@ contract DeployAppRegistry is IDiamondInitHelper, DiamondHelper, Deployer {
31
33
 
32
34
  DeployFacet private facetHelper = new DeployFacet();
33
35
  DeploySpaceFactory private deploySpaceFactory = new DeploySpaceFactory();
36
+ DeploySimpleAppBeacon private deploySimpleAppBeacon = new DeploySimpleAppBeacon();
34
37
 
35
38
  string internal constant APP_REGISTRY_SCHEMA = "address app, address client";
36
39
  address internal spaceFactory;
40
+ address internal simpleAppBeacon;
37
41
 
38
42
  function versionName() public pure override returns (string memory) {
39
43
  return "appRegistry";
@@ -41,6 +45,7 @@ contract DeployAppRegistry is IDiamondInitHelper, DiamondHelper, Deployer {
41
45
 
42
46
  function addImmutableCuts(address deployer) internal {
43
47
  spaceFactory = deploySpaceFactory.deploy(deployer);
48
+ simpleAppBeacon = deploySimpleAppBeacon.deploy(deployer);
44
49
 
45
50
  // Queue up all core facets for batch deployment
46
51
  facetHelper.add("DiamondCutFacet");
@@ -81,7 +86,7 @@ contract DeployAppRegistry is IDiamondInitHelper, DiamondHelper, Deployer {
81
86
  DeployOwnable.makeInitData(deployer)
82
87
  );
83
88
 
84
- facet = facetHelper.getDeployedAddress("MetadataFacet");
89
+ facet = facetHelper.predictAddress("MetadataFacet");
85
90
  addFacet(
86
91
  makeCut(facet, FacetCutAction.Add, DeployMetadata.selectors()),
87
92
  facet,
@@ -96,20 +101,10 @@ contract DeployAppRegistry is IDiamondInitHelper, DiamondHelper, Deployer {
96
101
  facetHelper.add("AppRegistryFacet");
97
102
  facetHelper.add("AppInstallerFacet");
98
103
  facetHelper.add("AppFactoryFacet");
99
- facetHelper.add("SimpleApp");
100
104
 
101
105
  facetHelper.deployBatch(deployer);
102
106
 
103
- address simpleApp = facetHelper.getDeployedAddress("SimpleApp");
104
- address facet = facetHelper.getDeployedAddress("UpgradeableBeaconFacet");
105
-
106
- addFacet(
107
- makeCut(facet, FacetCutAction.Add, DeployUpgradeableBeacon.selectors()),
108
- facet,
109
- DeployUpgradeableBeacon.makeInitData(simpleApp)
110
- );
111
-
112
- facet = facetHelper.getDeployedAddress("AppRegistryFacet");
107
+ address facet = facetHelper.getDeployedAddress("AppRegistryFacet");
113
108
  addFacet(
114
109
  makeCut(facet, FacetCutAction.Add, DeployAppRegistryFacet.selectors()),
115
110
  facet,
@@ -123,11 +118,17 @@ contract DeployAppRegistry is IDiamondInitHelper, DiamondHelper, Deployer {
123
118
  DeployAppInstallerFacet.makeInitData()
124
119
  );
125
120
 
121
+ IAppFactoryBase.Beacon[] memory beacons = new IAppFactoryBase.Beacon[](1);
122
+ beacons[0] = IAppFactoryBase.Beacon({
123
+ beaconId: deploySimpleAppBeacon.SIMPLE_APP_BEACON_ID(),
124
+ beacon: simpleAppBeacon
125
+ });
126
+
126
127
  facet = facetHelper.getDeployedAddress("AppFactoryFacet");
127
128
  addFacet(
128
129
  makeCut(facet, FacetCutAction.Add, DeployAppFactoryFacet.selectors()),
129
130
  facet,
130
- DeployAppFactoryFacet.makeInitData()
131
+ DeployAppFactoryFacet.makeInitData(beacons, _getEntryPoint())
131
132
  );
132
133
 
133
134
  address multiInit = facetHelper.getDeployedAddress("MultiInit");
@@ -175,9 +176,15 @@ contract DeployAppRegistry is IDiamondInitHelper, DiamondHelper, Deployer {
175
176
 
176
177
  Diamond.InitParams memory initDiamondCut = diamondInitParams(deployer);
177
178
 
178
- vm.broadcast(deployer);
179
+ vm.startBroadcast(deployer);
179
180
  Diamond diamond = new Diamond(initDiamondCut);
181
+ vm.stopBroadcast();
180
182
 
181
183
  return address(diamond);
182
184
  }
185
+
186
+ function _getEntryPoint() internal returns (address) {
187
+ if (isTesting()) return address(new EntryPoint());
188
+ return 0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108;
189
+ }
183
190
  }
@@ -0,0 +1,149 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.23;
3
+
4
+ // interfaces
5
+ import {IDiamondInitHelper} from "./IDiamondInitHelper.sol";
6
+
7
+ // libraries
8
+ import {DeployDiamondCut} from "@towns-protocol/diamond/scripts/deployments/facets/DeployDiamondCut.sol";
9
+ import {DeployDiamondLoupe} from "@towns-protocol/diamond/scripts/deployments/facets/DeployDiamondLoupe.sol";
10
+ import {DeployIntrospection} from "@towns-protocol/diamond/scripts/deployments/facets/DeployIntrospection.sol";
11
+ import {DeployOwnable} from "@towns-protocol/diamond/scripts/deployments/facets/DeployOwnable.sol";
12
+ import {DeployMetadata} from "../facets/DeployMetadata.s.sol";
13
+ import {DeployUpgradeableBeacon} from "../facets/DeployUpgradeableBeacon.s.sol";
14
+ import {LibString} from "solady/utils/LibString.sol";
15
+
16
+ // contracts
17
+ import {Diamond} from "@towns-protocol/diamond/src/Diamond.sol";
18
+ import {MultiInit} from "@towns-protocol/diamond/src/initializers/MultiInit.sol";
19
+ import {DiamondHelper} from "@towns-protocol/diamond/scripts/common/helpers/DiamondHelper.s.sol";
20
+
21
+ // deployers
22
+ import {DeployFacet} from "../../common/DeployFacet.s.sol";
23
+ import {Deployer} from "../../common/Deployer.s.sol";
24
+
25
+ contract DeploySimpleAppBeacon is DiamondHelper, Deployer, IDiamondInitHelper {
26
+ using LibString for string;
27
+
28
+ DeployFacet private facetHelper = new DeployFacet();
29
+
30
+ // keccak256("SimpleAppBeacon")
31
+ bytes32 public constant SIMPLE_APP_BEACON_ID =
32
+ 0xff51c4406dd10a0ec471147fd1e468ff84c7fe0c8a9bc8165c6ac339490f6027;
33
+
34
+ function versionName() public pure override returns (string memory) {
35
+ return "simpleAppBeacon";
36
+ }
37
+
38
+ function diamondInitHelper(
39
+ address deployer,
40
+ string[] memory facetNames
41
+ ) external override returns (FacetCut[] memory) {
42
+ // Queue up all requested facets for batch deployment
43
+ for (uint256 i; i < facetNames.length; ++i) {
44
+ facetHelper.add(facetNames[i]);
45
+ }
46
+
47
+ // Deploy all requested facets in a single batch transaction
48
+ facetHelper.deployBatch(deployer);
49
+
50
+ for (uint256 i; i < facetNames.length; ++i) {
51
+ string memory facetName = facetNames[i];
52
+ address facet = facetHelper.getDeployedAddress(facetName);
53
+
54
+ if (facetName.eq("UpgradeableBeaconFacet")) {
55
+ addCut(makeCut(facet, FacetCutAction.Add, DeployUpgradeableBeacon.selectors()));
56
+ }
57
+ }
58
+
59
+ return baseFacets();
60
+ }
61
+
62
+ function diamondInitParams(address deployer) public returns (Diamond.InitParams memory) {
63
+ // Queue up feature facets for batch deployment
64
+ facetHelper.add("MultiInit");
65
+ facetHelper.add("UpgradeableBeaconFacet");
66
+ facetHelper.add("SimpleAppFacet");
67
+
68
+ // Deploy all facets in a batch
69
+ facetHelper.deployBatch(deployer);
70
+
71
+ // Add feature facets
72
+ address simpleApp = facetHelper.getDeployedAddress("SimpleAppFacet");
73
+ address facet = facetHelper.getDeployedAddress("UpgradeableBeaconFacet");
74
+ addFacet(
75
+ makeCut(facet, FacetCutAction.Add, DeployUpgradeableBeacon.selectors()),
76
+ facet,
77
+ DeployUpgradeableBeacon.makeInitData(simpleApp)
78
+ );
79
+
80
+ address multiInit = facetHelper.getDeployedAddress("MultiInit");
81
+
82
+ return
83
+ Diamond.InitParams({
84
+ baseFacets: baseFacets(),
85
+ init: multiInit,
86
+ initData: abi.encodeCall(MultiInit.multiInit, (_initAddresses, _initDatas))
87
+ });
88
+ }
89
+
90
+ /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
91
+ /* Internal */
92
+ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
93
+
94
+ function _coreFacets(address deployer) private {
95
+ // Queue up all core facets for batch deployment
96
+ facetHelper.add("DiamondCutFacet");
97
+ facetHelper.add("DiamondLoupeFacet");
98
+ facetHelper.add("IntrospectionFacet");
99
+ facetHelper.add("OwnableFacet");
100
+ facetHelper.add("MetadataFacet");
101
+
102
+ // Get predicted addresses
103
+ address facet = facetHelper.predictAddress("DiamondCutFacet");
104
+ addFacet(
105
+ makeCut(facet, FacetCutAction.Add, DeployDiamondCut.selectors()),
106
+ facet,
107
+ DeployDiamondCut.makeInitData()
108
+ );
109
+
110
+ facet = facetHelper.predictAddress("DiamondLoupeFacet");
111
+ addFacet(
112
+ makeCut(facet, FacetCutAction.Add, DeployDiamondLoupe.selectors()),
113
+ facet,
114
+ DeployDiamondLoupe.makeInitData()
115
+ );
116
+
117
+ facet = facetHelper.predictAddress("IntrospectionFacet");
118
+ addFacet(
119
+ makeCut(facet, FacetCutAction.Add, DeployIntrospection.selectors()),
120
+ facet,
121
+ DeployIntrospection.makeInitData()
122
+ );
123
+
124
+ facet = facetHelper.predictAddress("MetadataFacet");
125
+ addFacet(
126
+ makeCut(facet, FacetCutAction.Add, DeployMetadata.selectors()),
127
+ facet,
128
+ DeployMetadata.makeInitData(SIMPLE_APP_BEACON_ID, "")
129
+ );
130
+
131
+ facet = facetHelper.predictAddress("OwnableFacet");
132
+ addFacet(
133
+ makeCut(facet, FacetCutAction.Add, DeployOwnable.selectors()),
134
+ facet,
135
+ DeployOwnable.makeInitData(deployer)
136
+ );
137
+ }
138
+
139
+ function __deploy(address deployer) internal override returns (address) {
140
+ _coreFacets(deployer);
141
+
142
+ Diamond.InitParams memory initDiamondCut = diamondInitParams(deployer);
143
+
144
+ vm.broadcast(deployer);
145
+ Diamond diamond = new Diamond(initDiamondCut);
146
+
147
+ return address(diamond);
148
+ }
149
+ }
@@ -12,7 +12,6 @@ import {DeployIntrospection} from "@towns-protocol/diamond/scripts/deployments/f
12
12
  import {DeployOwnable} from "@towns-protocol/diamond/scripts/deployments/facets/DeployOwnable.sol";
13
13
  import {DeployPausable} from "@towns-protocol/diamond/scripts/deployments/facets/DeployPausable.sol";
14
14
  import {DeployProxyManager} from "@towns-protocol/diamond/scripts/deployments/utils/DeployProxyManager.sol";
15
- import {LibDeploy} from "@towns-protocol/diamond/src/utils/LibDeploy.sol";
16
15
  import {DeployFeatureManager} from "../facets/DeployFeatureManager.s.sol";
17
16
  import {DeployMetadata} from "../facets/DeployMetadata.s.sol";
18
17
  import {DeployPricingModules} from "../facets/DeployPricingModules.s.sol";
@@ -10,13 +10,21 @@ import {IDiamond} from "@towns-protocol/diamond/src/IDiamond.sol";
10
10
  import {LibDeploy} from "@towns-protocol/diamond/src/utils/LibDeploy.sol";
11
11
  import {AppFactoryFacet} from "src/apps/facets/factory/AppFactoryFacet.sol";
12
12
  import {DynamicArrayLib} from "solady/utils/DynamicArrayLib.sol";
13
+ import {IAppFactoryBase} from "src/apps/facets/factory/IAppFactory.sol";
13
14
 
14
15
  library DeployAppFactoryFacet {
15
16
  using DynamicArrayLib for DynamicArrayLib.DynamicArray;
16
17
 
17
18
  function selectors() internal pure returns (bytes4[] memory res) {
18
- DynamicArrayLib.DynamicArray memory arr = DynamicArrayLib.p().reserve(1);
19
+ DynamicArrayLib.DynamicArray memory arr = DynamicArrayLib.p().reserve(8);
20
+ arr.p(AppFactoryFacet.addBeacons.selector);
21
+ arr.p(AppFactoryFacet.removeBeacons.selector);
22
+ arr.p(AppFactoryFacet.getBeacon.selector);
23
+ arr.p(AppFactoryFacet.getBeacons.selector);
24
+ arr.p(AppFactoryFacet.setEntryPoint.selector);
25
+ arr.p(AppFactoryFacet.getEntryPoint.selector);
19
26
  arr.p(AppFactoryFacet.createApp.selector);
27
+ arr.p(AppFactoryFacet.createAppByBeacon.selector);
20
28
  bytes32[] memory selectors_ = arr.asBytes32Array();
21
29
  assembly ("memory-safe") {
22
30
  res := selectors_
@@ -35,8 +43,11 @@ library DeployAppFactoryFacet {
35
43
  });
36
44
  }
37
45
 
38
- function makeInitData() internal pure returns (bytes memory) {
39
- return abi.encodeCall(AppFactoryFacet.__AppFactory_init, ());
46
+ function makeInitData(
47
+ IAppFactoryBase.Beacon[] memory beacons,
48
+ address entryPoint
49
+ ) internal pure returns (bytes memory) {
50
+ return abi.encodeCall(AppFactoryFacet.__AppFactory_init, (beacons, entryPoint));
40
51
  }
41
52
 
42
53
  function deploy() internal returns (address) {
@@ -6,7 +6,6 @@ import {IAppFactory} from "src/apps/facets/factory/IAppFactory.sol";
6
6
 
7
7
  //contracts
8
8
  import {Interaction} from "../common/Interaction.s.sol";
9
- import {SimpleApp} from "src/apps/helpers/SimpleApp.sol";
10
9
  import {IAppFactoryBase} from "src/apps/facets/factory/IAppFactory.sol";
11
10
 
12
11
  contract InteractRegisterApp is Interaction, IAppFactoryBase {
@@ -0,0 +1,75 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.23;
3
+
4
+ // interfaces
5
+ import {IAppFactory, IAppFactoryBase} from "src/apps/facets/factory/IAppFactory.sol";
6
+
7
+ // libraries
8
+ import {console} from "forge-std/console.sol";
9
+
10
+ // contracts
11
+ import {Interaction} from "../../common/Interaction.s.sol";
12
+ import {AlphaHelper} from "../helpers/AlphaHelper.sol";
13
+ import {DeployAppRegistry} from "../../deployments/diamonds/DeployAppRegistry.s.sol";
14
+ import {DeploySimpleAppBeacon} from "../../deployments/diamonds/DeploySimpleAppBeacon.s.sol";
15
+
16
+ // facet deployers
17
+ import {DeployAppRegistryFacet} from "../../deployments/facets/DeployAppRegistryFacet.s.sol";
18
+ import {DeployAppInstallerFacet} from "../../deployments/facets/DeployAppInstallerFacet.s.sol";
19
+ import {DeployAppFactoryFacet} from "../../deployments/facets/DeployAppFactoryFacet.s.sol";
20
+
21
+ contract InteractAppRegistry is Interaction, AlphaHelper {
22
+ DeployAppRegistry private deployHelper = new DeployAppRegistry();
23
+ DeploySimpleAppBeacon private beaconHelper = new DeploySimpleAppBeacon();
24
+
25
+ function __interact(address deployer) internal override {
26
+ // Get the deployed AppRegistry diamond address
27
+ address appRegistry = getDeployment("appRegistry");
28
+ address spaceFactory = getDeployment("spaceFactory");
29
+ address simpleAppBeacon = getDeployment("simpleAppBeacon");
30
+ address entryPoint = getDeployment("entryPoint");
31
+
32
+ console.log("AppRegistry Diamond:", appRegistry);
33
+ console.log("Space Factory:", spaceFactory);
34
+ console.log("Simple App Beacon:", simpleAppBeacon);
35
+
36
+ // Deploy new facet implementations
37
+ console.log("\n=== Deploying New Facets ===");
38
+ vm.setEnv("OVERRIDE_DEPLOYMENTS", "1");
39
+
40
+ address appRegistryFacet = DeployAppRegistryFacet.deploy();
41
+ console.log("AppRegistryFacet deployed at:", appRegistryFacet);
42
+
43
+ address appInstallerFacet = DeployAppInstallerFacet.deploy();
44
+ console.log("AppInstallerFacet deployed at:", appInstallerFacet);
45
+
46
+ address appFactoryFacet = DeployAppFactoryFacet.deploy();
47
+ console.log("AppFactoryFacet deployed at:", appFactoryFacet);
48
+
49
+ // Add the cuts for the new facet implementations
50
+ addCut(DeployAppRegistryFacet.makeCut(appRegistryFacet, FacetCutAction.Replace));
51
+ addCut(DeployAppInstallerFacet.makeCut(appInstallerFacet, FacetCutAction.Replace));
52
+ addCut(DeployAppFactoryFacet.makeCut(appFactoryFacet, FacetCutAction.Replace));
53
+
54
+ // Prepare initialization data for AppFactoryFacet with the beacon configuration
55
+ IAppFactoryBase.Beacon[] memory beacons = new IAppFactoryBase.Beacon[](1);
56
+ beacons[0] = IAppFactoryBase.Beacon({
57
+ beaconId: beaconHelper.SIMPLE_APP_BEACON_ID(),
58
+ beacon: simpleAppBeacon
59
+ });
60
+ bytes memory initData = DeployAppFactoryFacet.makeInitData(beacons, entryPoint);
61
+
62
+ // Generate and execute smart cuts with initialization
63
+ console.log("\n=== Executing Diamond Cut with Initialization ===");
64
+ executeDiamondCutsWithLogging(
65
+ deployer,
66
+ appRegistry,
67
+ "AppRegistry",
68
+ deployHelper,
69
+ appFactoryFacet,
70
+ initData
71
+ );
72
+
73
+ console.log("\n=== Diamond Cut Complete ===");
74
+ }
75
+ }
@@ -12,18 +12,21 @@ import {ITownsApp} from "./ITownsApp.sol";
12
12
  /// @dev Inheriting contracts should override _onInstall and _onUninstall as needed
13
13
  /// @dev Implements IModule, IExecutionModule, and ITownsApp interfaces
14
14
  abstract contract BaseApp is ITownsApp {
15
- receive() external payable {
15
+ receive() external payable virtual {
16
16
  _onPayment(msg.sender, msg.value);
17
17
  }
18
18
 
19
- // External functions
20
- function supportsInterface(bytes4 interfaceId) external pure returns (bool) {
19
+ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
21
20
  return
21
+ interfaceId == type(ITownsApp).interfaceId ||
22
22
  interfaceId == type(IExecutionModule).interfaceId ||
23
- interfaceId == type(IModule).interfaceId ||
24
- interfaceId == type(ITownsApp).interfaceId;
23
+ interfaceId == type(IModule).interfaceId;
25
24
  }
26
25
 
26
+ /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
27
+ /* Base App Functions */
28
+ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
29
+
27
30
  /// @notice Required by IModule - called when module is installed
28
31
  function onInstall(bytes calldata postInstallData) external {
29
32
  _onInstall(postInstallData);
@@ -46,7 +49,7 @@ abstract contract BaseApp is ITownsApp {
46
49
  return _accessDuration();
47
50
  }
48
51
 
49
- // Internal functions
52
+ // Hooks
50
53
  function _onInstall(bytes calldata postInstallData) internal virtual {}
51
54
 
52
55
  function _onUninstall(bytes calldata postUninstallData) internal virtual {}
@@ -10,6 +10,10 @@ import {IModule} from "@erc6900/reference-implementation/interfaces/IModule.sol"
10
10
  /// @dev Combines IModule (module lifecycle), and IExecutionModule (execution)
11
11
  /// @dev Apps must implement required permissions and support these interfaces
12
12
  interface ITownsApp is IModule, IExecutionModule {
13
+ /// @notice Initializes the app
14
+ /// @param data The data to initialize the app
15
+ function initialize(bytes calldata data) external;
16
+
13
17
  /// @notice Returns the required permissions for the app
14
18
  /// @return permissions The required permissions for the app
15
19
  function requiredPermissions() external view returns (bytes32[] memory);
@@ -0,0 +1,108 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.29;
3
+
4
+ // interfaces
5
+ import {IAppFactoryBase} from "./IAppFactory.sol";
6
+ import {ITownsApp} from "../../ITownsApp.sol";
7
+
8
+ // libraries
9
+ import {LibClone} from "solady/utils/LibClone.sol";
10
+ import {CustomRevert} from "../../../utils/libraries/CustomRevert.sol";
11
+ import {AppFactoryStorage} from "./AppFactoryStorage.sol";
12
+ import {EnumerableSetLib} from "solady/utils/EnumerableSetLib.sol";
13
+ import {LibAppRegistry} from "../registry/LibAppRegistry.sol";
14
+
15
+ // contracts
16
+ import {Facet} from "@towns-protocol/diamond/src/facets/Facet.sol";
17
+
18
+ /// @title AppInstallerFacet
19
+ /// @author Towns Protocol
20
+ /// @notice Facet for installing apps to spaces
21
+ abstract contract AppFactoryBase is IAppFactoryBase {
22
+ using CustomRevert for bytes4;
23
+ using EnumerableSetLib for EnumerableSetLib.Bytes32Set;
24
+
25
+ function _addBeacons(Beacon[] calldata beacons) internal {
26
+ AppFactoryStorage.Layout storage $ = AppFactoryStorage.getLayout();
27
+ uint256 length = beacons.length;
28
+ for (uint256 i; i < length; ++i) {
29
+ Beacon calldata beacon = beacons[i];
30
+ if (beacon.beacon == address(0)) AppFactory__InvalidAddressInput.selector.revertWith();
31
+ if (beacon.beaconId == bytes32(0)) AppFactory__InvalidBeaconId.selector.revertWith();
32
+ if ($.beaconIds.contains(beacon.beaconId))
33
+ AppFactory__BeaconAlreadyExists.selector.revertWith();
34
+ $.beacons[beacon.beaconId] = beacon.beacon;
35
+ $.beaconIds.add(beacon.beaconId);
36
+ emit BeaconAdded(beacon.beaconId, beacon.beacon);
37
+ }
38
+ }
39
+
40
+ function _removeBeacons(bytes32[] calldata beaconIds) internal {
41
+ AppFactoryStorage.Layout storage $ = AppFactoryStorage.getLayout();
42
+ uint256 length = beaconIds.length;
43
+ for (uint256 i; i < length; ++i) {
44
+ bytes32 beaconId = beaconIds[i];
45
+ address beacon = $.beacons[beaconId];
46
+
47
+ if (beaconId == bytes32(0)) AppFactory__InvalidBeaconId.selector.revertWith();
48
+ if (beacon == address(0)) AppFactory__BeaconNotFound.selector.revertWith();
49
+
50
+ delete $.beacons[beaconId];
51
+ $.beaconIds.remove(beaconId);
52
+ emit BeaconRemoved(beaconId, beacon);
53
+ }
54
+ }
55
+
56
+ function _setEntryPoint(address entryPoint) internal {
57
+ AppFactoryStorage.Layout storage $ = AppFactoryStorage.getLayout();
58
+ address oldEntryPoint = $.entryPoint;
59
+ $.entryPoint = entryPoint;
60
+ emit EntryPointSet(oldEntryPoint, entryPoint);
61
+ }
62
+
63
+ function _getBeacon(bytes32 beaconId) internal view returns (address beacon) {
64
+ return AppFactoryStorage.getLayout().beacons[beaconId];
65
+ }
66
+
67
+ function _getBeacons() internal view returns (bytes32[] memory beaconIds) {
68
+ return AppFactoryStorage.getLayout().beaconIds.values();
69
+ }
70
+
71
+ function _getDefaultBeaconId() internal view returns (bytes32 beaconId) {
72
+ return AppFactoryStorage.getLayout().beaconIds.at(0);
73
+ }
74
+
75
+ /// @notice Create an upgradeable simple app contract
76
+ /// @param params The parameters of the app
77
+ function _createApp(
78
+ bytes32 beaconId,
79
+ AppParams calldata params
80
+ ) internal returns (address app) {
81
+ AppFactoryStorage.Layout storage $ = AppFactoryStorage.getLayout();
82
+
83
+ address beacon = $.beacons[beaconId];
84
+ if (beacon == address(0)) AppFactory__BeaconNotFound.selector.revertWith();
85
+
86
+ uint48 accessDuration = LibAppRegistry.validateDuration(params.accessDuration);
87
+
88
+ app = LibClone.deployERC1967BeaconProxy(beacon);
89
+ ITownsApp(app).initialize(
90
+ abi.encode(
91
+ msg.sender,
92
+ params.name,
93
+ params.permissions,
94
+ params.installPrice,
95
+ accessDuration,
96
+ params.client,
97
+ $.entryPoint,
98
+ address(this)
99
+ )
100
+ );
101
+ }
102
+
103
+ function _validateParams(AppParams calldata params) internal pure {
104
+ if (bytes(params.name).length == 0) AppFactory__InvalidAppName.selector.revertWith();
105
+ if (params.permissions.length == 0) AppFactory__InvalidArrayInput.selector.revertWith();
106
+ if (params.client == address(0)) AppFactory__InvalidAddressInput.selector.revertWith();
107
+ }
108
+ }