@towns-protocol/contracts 0.0.441 → 0.0.443

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.
Files changed (36) hide show
  1. package/docs/membership_architecture.md +237 -0
  2. package/package.json +3 -3
  3. package/scripts/deployments/diamonds/DeploySpace.s.sol +0 -7
  4. package/scripts/deployments/diamonds/DeploySpaceFactory.s.sol +2 -2
  5. package/scripts/deployments/facets/DeployMembership.s.sol +2 -1
  6. package/scripts/deployments/utils/DeployMockERC20.s.sol +1 -1
  7. package/scripts/deployments/utils/DeployMockUSDC.s.sol +19 -0
  8. package/scripts/interactions/InteractBaseAlpha.s.sol +3 -0
  9. package/scripts/interactions/InteractPostDeploy.s.sol +11 -0
  10. package/src/apps/facets/registry/AppRegistryBase.sol +4 -2
  11. package/src/apps/facets/registry/IAppRegistry.sol +1 -1
  12. package/src/factory/facets/architect/IArchitect.sol +1 -0
  13. package/src/factory/facets/create/CreateSpaceBase.sol +2 -9
  14. package/src/factory/facets/feature/FeatureManagerFacet.sol +32 -29
  15. package/src/factory/facets/feature/FeatureManagerMod.sol +248 -0
  16. package/src/factory/facets/feature/{IFeatureManagerFacet.sol → IFeatureManager.sol} +2 -35
  17. package/src/factory/facets/fee/FeeManagerFacet.sol +1 -1
  18. package/src/factory/facets/fee/FeeTypesLib.sol +8 -1
  19. package/src/spaces/facets/dispatcher/DispatcherBase.sol +13 -5
  20. package/src/spaces/facets/gated/EntitlementGated.sol +9 -5
  21. package/src/spaces/facets/membership/IMembership.sol +11 -17
  22. package/src/spaces/facets/membership/MembershipBase.sol +30 -59
  23. package/src/spaces/facets/membership/MembershipFacet.sol +19 -1
  24. package/src/spaces/facets/membership/MembershipStorage.sol +1 -0
  25. package/src/spaces/facets/membership/join/MembershipJoin.sol +192 -125
  26. package/src/spaces/facets/treasury/ITreasury.sol +2 -1
  27. package/src/spaces/facets/treasury/Treasury.sol +21 -24
  28. package/src/spaces/facets/xchain/SpaceEntitlementGated.sol +3 -4
  29. package/scripts/deployments/facets/DeployPrepayFacet.s.sol +0 -31
  30. package/scripts/interactions/InteractPrepay.s.sol +0 -30
  31. package/src/factory/facets/feature/FeatureManagerBase.sol +0 -152
  32. package/src/factory/facets/feature/FeatureManagerStorage.sol +0 -47
  33. package/src/spaces/facets/prepay/IPrepay.sol +0 -44
  34. package/src/spaces/facets/prepay/PrepayBase.sol +0 -27
  35. package/src/spaces/facets/prepay/PrepayFacet.sol +0 -65
  36. package/src/spaces/facets/prepay/PrepayStorage.sol +0 -26
@@ -2,53 +2,50 @@
2
2
  pragma solidity ^0.8.23;
3
3
 
4
4
  // interfaces
5
-
6
5
  import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
7
- import {IMembershipBase} from "src/spaces/facets/membership/IMembership.sol";
8
- import {ITreasury} from "src/spaces/facets/treasury/ITreasury.sol";
6
+ import {IMembershipBase} from "../membership/IMembership.sol";
7
+ import {ITreasury} from "./ITreasury.sol";
9
8
 
10
9
  // libraries
11
- import {CurrencyTransfer} from "src/utils/libraries/CurrencyTransfer.sol";
10
+ import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
11
+ import {CurrencyTransfer} from "../../../utils/libraries/CurrencyTransfer.sol";
12
+ import {CustomRevert} from "../../../utils/libraries/CustomRevert.sol";
12
13
 
13
14
  // contracts
14
-
15
15
  import {Facet} from "@towns-protocol/diamond/src/facets/Facet.sol";
16
16
  import {TokenOwnableBase} from "@towns-protocol/diamond/src/facets/ownable/token/TokenOwnableBase.sol";
17
- import {MembershipStorage} from "src/spaces/facets/membership/MembershipStorage.sol";
18
- import {CustomRevert} from "src/utils/libraries/CustomRevert.sol";
19
17
  import {ReentrancyGuard} from "solady/utils/ReentrancyGuard.sol";
20
18
 
21
19
  contract Treasury is TokenOwnableBase, ReentrancyGuard, Facet, ITreasury {
20
+ using CustomRevert for bytes4;
21
+ using SafeTransferLib for address;
22
+
22
23
  function __Treasury_init() external onlyInitializing {
23
24
  _addInterface(type(IERC1155Receiver).interfaceId);
24
25
  }
25
26
 
26
- ///@inheritdoc ITreasury
27
- function withdraw(address account) external onlyOwner nonReentrant {
28
- if (account == address(0)) {
29
- CustomRevert.revertWith(IMembershipBase.Membership__InvalidAddress.selector);
30
- }
31
-
32
- // get the balance
33
- uint256 balance = address(this).balance;
27
+ /// @inheritdoc ITreasury
28
+ function withdraw(address currency, address account) external onlyOwner nonReentrant {
29
+ if (account == address(0)) IMembershipBase.Membership__InvalidAddress.selector.revertWith();
34
30
 
35
- // verify the balance is not 0
36
- if (balance == 0) {
37
- CustomRevert.revertWith(IMembershipBase.Membership__InsufficientPayment.selector);
38
- }
31
+ // Get balance based on currency type
32
+ uint256 balance = currency == CurrencyTransfer.NATIVE_TOKEN
33
+ ? address(this).balance
34
+ : currency.balanceOf(address(this));
39
35
 
40
- address currency = MembershipStorage.layout().membershipCurrency;
36
+ // Verify the balance is not 0
37
+ if (balance == 0) IMembershipBase.Membership__InsufficientPayment.selector.revertWith();
41
38
 
42
39
  CurrencyTransfer.transferCurrency(currency, address(this), account, balance);
43
40
 
44
- emit IMembershipBase.MembershipWithdrawal(account, balance);
41
+ emit IMembershipBase.MembershipWithdrawal(currency, account, balance);
45
42
  }
46
43
 
47
44
  /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
48
45
  /* Hooks */
49
46
  /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
50
47
 
51
- ///@inheritdoc ITreasury
48
+ /// @inheritdoc ITreasury
52
49
  function onERC721Received(
53
50
  address,
54
51
  address,
@@ -58,7 +55,7 @@ contract Treasury is TokenOwnableBase, ReentrancyGuard, Facet, ITreasury {
58
55
  return this.onERC721Received.selector;
59
56
  }
60
57
 
61
- ///@inheritdoc ITreasury
58
+ /// @inheritdoc ITreasury
62
59
  function onERC1155Received(
63
60
  address,
64
61
  address,
@@ -69,7 +66,7 @@ contract Treasury is TokenOwnableBase, ReentrancyGuard, Facet, ITreasury {
69
66
  return this.onERC1155Received.selector;
70
67
  }
71
68
 
72
- ///@inheritdoc ITreasury
69
+ /// @inheritdoc ITreasury
73
70
  function onERC1155BatchReceived(
74
71
  address,
75
72
  address,
@@ -4,15 +4,12 @@ pragma solidity ^0.8.23;
4
4
  // interfaces
5
5
  import {IMembership} from "../membership/IMembership.sol";
6
6
 
7
- // libraries
8
-
9
7
  // contracts
10
8
  import {EntitlementGated} from "../gated/EntitlementGated.sol";
11
9
  import {MembershipJoin} from "../membership/join/MembershipJoin.sol";
12
10
 
13
11
  /// @title SpaceEntitlementGated
14
12
  /// @notice Handles entitlement-gated access to spaces and membership token issuance
15
- /// @dev Inherits from ISpaceEntitlementGatedBase, MembershipJoin, and EntitlementGated
16
13
  contract SpaceEntitlementGated is MembershipJoin, EntitlementGated {
17
14
  /// @notice Processes the result of an entitlement check
18
15
  /// @dev This function is called when the result of an entitlement check is posted
@@ -34,7 +31,9 @@ contract SpaceEntitlementGated is MembershipJoin, EntitlementGated {
34
31
  if (result == NodeVoteStatus.PASSED) {
35
32
  PricingDetails memory joinDetails = _getPricingDetails();
36
33
 
37
- if (joinDetails.shouldCharge) {
34
+ if (!joinDetails.shouldCharge) {
35
+ _afterChargeForJoinSpace(transactionId, receiver, 0);
36
+ } else {
38
37
  uint256 payment = _getCapturedValue(transactionId);
39
38
 
40
39
  if (payment < joinDetails.amountDue) {
@@ -1,31 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.23;
3
-
4
- //interfaces
5
- import {IDiamond} from "@towns-protocol/diamond/src/Diamond.sol";
6
- import {IPrepay} from "src/spaces/facets/prepay/IPrepay.sol";
7
-
8
- //libraries
9
- import {LibDeploy} from "@towns-protocol/diamond/src/utils/LibDeploy.sol";
10
-
11
- //contracts
12
-
13
- library DeployPrepayFacet {
14
- function selectors() internal pure returns (bytes4[] memory res) {
15
- res = new bytes4[](3);
16
- res[0] = IPrepay.prepayMembership.selector;
17
- res[1] = IPrepay.prepaidMembershipSupply.selector;
18
- res[2] = IPrepay.calculateMembershipPrepayFee.selector;
19
- }
20
-
21
- function makeCut(
22
- address facetAddress,
23
- IDiamond.FacetCutAction action
24
- ) internal pure returns (IDiamond.FacetCut memory) {
25
- return IDiamond.FacetCut(facetAddress, action, selectors());
26
- }
27
-
28
- function deploy() internal returns (address) {
29
- return LibDeploy.deployCode("PrepayFacet.sol", "");
30
- }
31
- }
@@ -1,30 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.23;
3
-
4
- // interfaces
5
-
6
- // libraries
7
-
8
- // contracts
9
- import {Interaction} from "scripts/common/Interaction.s.sol";
10
- import {IPrepay} from "src/spaces/facets/prepay/IPrepay.sol";
11
-
12
- // debuggging
13
- import {console} from "forge-std/console.sol";
14
-
15
- contract InteractPrepay is Interaction {
16
- IPrepay prepay = IPrepay(0x0000000000000000000000000000000000000000);
17
-
18
- function __interact(address deployer) internal override {
19
- uint256 expectedAmount = 1000;
20
- uint256 totalAmount = prepay.calculateMembershipPrepayFee(expectedAmount);
21
-
22
- console.log("paying:", totalAmount);
23
-
24
- vm.startBroadcast(deployer);
25
- IPrepay(prepay).prepayMembership{value: totalAmount}(expectedAmount);
26
- vm.stopBroadcast();
27
-
28
- console.log("prepaidSupply", prepay.prepaidMembershipSupply());
29
- }
30
- }
@@ -1,152 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.23;
3
-
4
- // interfaces
5
- import {IFeatureManagerFacetBase} from "./IFeatureManagerFacet.sol";
6
- import {IVotes} from "@openzeppelin/contracts/governance/utils/IVotes.sol";
7
- import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
8
-
9
- // libraries
10
- import {CustomRevert} from "../../../utils/libraries/CustomRevert.sol";
11
- import {FeatureManagerStorage} from "./FeatureManagerStorage.sol";
12
- import {FeatureCondition} from "./IFeatureManagerFacet.sol";
13
- import {EnumerableSetLib} from "solady/utils/EnumerableSetLib.sol";
14
- import {LibCall} from "solady/utils/LibCall.sol";
15
-
16
- // contracts
17
-
18
- abstract contract FeatureManagerBase is IFeatureManagerFacetBase {
19
- using EnumerableSetLib for EnumerableSetLib.Bytes32Set;
20
- using CustomRevert for bytes4;
21
-
22
- /// @notice Creates or updates a feature condition based on the create flag
23
- /// @dev Validates token interface compliance before storing the condition
24
- /// @param featureId The unique identifier for the feature
25
- /// @param condition The condition struct containing token, threshold, active status, and extra data
26
- /// @param create True to create new feature, false to update existing
27
- function _upsertFeatureCondition(
28
- bytes32 featureId,
29
- FeatureCondition calldata condition,
30
- bool create
31
- ) internal {
32
- _validateToken(condition);
33
-
34
- FeatureManagerStorage.Layout storage $ = FeatureManagerStorage.getLayout();
35
- if (!create) {
36
- if (!$.featureIds.contains(featureId)) FeatureNotActive.selector.revertWith();
37
- } else {
38
- if (!$.featureIds.add(featureId)) FeatureAlreadyExists.selector.revertWith();
39
- }
40
-
41
- $.conditions[featureId] = condition;
42
- }
43
-
44
- /// @notice Disables a feature by setting its active flag to false
45
- /// @dev This does not delete the condition, only deactivates it
46
- /// @param featureId The unique identifier for the feature to disable
47
- function _disableFeatureCondition(bytes32 featureId) internal {
48
- FeatureCondition storage condition = _getFeatureCondition(featureId);
49
- if (!condition.active) FeatureNotActive.selector.revertWith();
50
- condition.active = false;
51
- }
52
-
53
- /// @notice Retrieves the condition for a specific feature
54
- /// @dev Returns the complete condition struct with all parameters
55
- /// @param featureId The unique identifier for the feature
56
- /// @return The complete condition configuration for the feature
57
- function _getFeatureCondition(
58
- bytes32 featureId
59
- ) internal view returns (FeatureCondition storage) {
60
- return FeatureManagerStorage.getLayout().conditions[featureId];
61
- }
62
-
63
- /// @notice Retrieves all feature conditions
64
- /// @dev Returns an array of all feature conditions
65
- /// @return conditions An array of all feature conditions
66
- function _getFeatureConditions() internal view returns (FeatureCondition[] memory conditions) {
67
- FeatureManagerStorage.Layout storage $ = FeatureManagerStorage.getLayout();
68
- // Use values() over at() for full iteration - avoids bounds checking overhead
69
- bytes32[] memory ids = $.featureIds.values();
70
- uint256 featureCount = ids.length;
71
-
72
- conditions = new FeatureCondition[](featureCount);
73
- for (uint256 i; i < featureCount; ++i) {
74
- conditions[i] = $.conditions[ids[i]];
75
- }
76
- }
77
-
78
- /// @notice Retrieves all feature conditions for a specific space
79
- /// @dev Returns an array of all feature conditions that are active for the space
80
- /// @return conditions An array of all feature conditions that are active for the space
81
- function _getFeatureConditionsForSpace(
82
- address space
83
- ) internal view returns (FeatureCondition[] memory conditions) {
84
- FeatureManagerStorage.Layout storage $ = FeatureManagerStorage.getLayout();
85
- // Use values() over at() for full iteration - avoids bounds checking overhead
86
- bytes32[] memory ids = $.featureIds.values();
87
- uint256 featureCount = ids.length;
88
-
89
- // Gas optimization: Allocate full array then resize (memory cheaper than storage reads)
90
- conditions = new FeatureCondition[](featureCount);
91
- uint256 index;
92
-
93
- for (uint256 i; i < featureCount; ++i) {
94
- FeatureCondition storage cond = $.conditions[ids[i]];
95
-
96
- if (_isValidCondition(cond, space)) {
97
- conditions[index++] = cond;
98
- }
99
- }
100
-
101
- // Resize array to actual number of valid conditions
102
- assembly ("memory-safe") {
103
- mstore(conditions, index)
104
- }
105
- }
106
-
107
- function _validateToken(FeatureCondition calldata condition) internal view {
108
- if (condition.token == address(0)) InvalidToken.selector.revertWith();
109
-
110
- // Check if the token implements IVotes.getVotes with proper return data
111
- (bool success, bool exceededMaxCopy, bytes memory data) = LibCall.tryStaticCall(
112
- condition.token,
113
- gasleft(),
114
- 32,
115
- abi.encodeCall(IVotes.getVotes, (address(this)))
116
- );
117
-
118
- if (!success || exceededMaxCopy || data.length != 32)
119
- InvalidInterface.selector.revertWith();
120
-
121
- // Check if the token implements ERC20.totalSupply with proper return data
122
- (success, exceededMaxCopy, data) = LibCall.tryStaticCall(
123
- condition.token,
124
- gasleft(),
125
- 32,
126
- abi.encodeCall(IERC20.totalSupply, ())
127
- );
128
-
129
- if (!success || exceededMaxCopy || data.length != 32)
130
- InvalidInterface.selector.revertWith();
131
-
132
- uint256 totalSupply = abi.decode(data, (uint256));
133
- if (totalSupply == 0) InvalidTotalSupply.selector.revertWith();
134
- if (condition.threshold > totalSupply) InvalidThreshold.selector.revertWith();
135
- }
136
-
137
- /// @notice Checks if a condition should be included for a given space
138
- /// @dev Returns true if the condition is active, has a valid token, and meets the threshold
139
- /// @param condition The condition to check
140
- /// @param space The space address to check against
141
- /// @return True if the condition should be included, false otherwise
142
- function _isValidCondition(
143
- FeatureCondition storage condition,
144
- address space
145
- ) internal view returns (bool) {
146
- if (!condition.active) return false;
147
- address token = condition.token;
148
- if (token == address(0)) return false;
149
- uint256 votes = IVotes(token).getVotes(space);
150
- return votes >= condition.threshold;
151
- }
152
- }
@@ -1,47 +0,0 @@
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
- /// @notice Represents a condition for feature activation
12
- /// @dev Used to determine if a feature should be enabled based on token voting power
13
- /// @param token The address of the token used for voting (must implement IVotes)
14
- /// @param threshold The minimum voting power (votes) required to activate the feature
15
- /// @param active Whether the condition is currently active
16
- /// @param extraData Additional data that might be used for specialized condition logic
17
- struct FeatureCondition {
18
- address token;
19
- bool active;
20
- uint256 threshold;
21
- bytes extraData;
22
- }
23
-
24
- /// @title FeatureManager
25
- library FeatureManagerStorage {
26
- using EnumerableSetLib for EnumerableSetLib.Bytes32Set;
27
-
28
- // keccak256(abi.encode(uint256(keccak256("factory.facets.feature.manager.storage")) - 1)) & ~bytes32(uint256(0xff))
29
- bytes32 constant DEFAULT_STORAGE_SLOT =
30
- 0x20c456a8ea15fcf7965033c954321ffd9dc82a2c65f686a77e2a67da65c29000;
31
-
32
- /// @notice Storage layout for the FeatureManager
33
- /// @dev Maps feature IDs to their activation conditions
34
- /// @custom:storage-location erc7201:towns.storage.FeatureManager
35
- struct Layout {
36
- // Feature IDs
37
- EnumerableSetLib.Bytes32Set featureIds;
38
- // Feature ID => Condition
39
- mapping(bytes32 featureId => FeatureCondition condition) conditions;
40
- }
41
-
42
- function getLayout() internal pure returns (Layout storage $) {
43
- assembly {
44
- $.slot := DEFAULT_STORAGE_SLOT
45
- }
46
- }
47
- }
@@ -1,44 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.23;
3
-
4
- // interfaces
5
-
6
- // libraries
7
-
8
- // contracts
9
- interface IPrepayBase {
10
- // =============================================================
11
- // ERRORS
12
- // =============================================================
13
- error Prepay__InvalidSupplyAmount();
14
- error Prepay__InvalidAmount();
15
- error Prepay__InvalidAddress();
16
- error Prepay__InvalidMembership();
17
- error Prepay__NotAllowed();
18
-
19
- // =============================================================
20
- // EVENTS
21
- // =============================================================
22
- event Prepay__Prepaid(uint256 supply);
23
- }
24
-
25
- interface IPrepay is IPrepayBase {
26
- /**
27
- * @notice Prepay a membership
28
- * @param supply The amount of memberships to prepay
29
- */
30
- function prepayMembership(uint256 supply) external payable;
31
-
32
- /**
33
- * @notice Get the prepaid supply
34
- * @return The remaining prepaid supply
35
- */
36
- function prepaidMembershipSupply() external view returns (uint256);
37
-
38
- /**
39
- * @notice Calculate the prepay fee for a given supply
40
- * @param supply The supply to calculate the fee for
41
- * @return The fee
42
- */
43
- function calculateMembershipPrepayFee(uint256 supply) external view returns (uint256);
44
- }
@@ -1,27 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.23;
3
-
4
- // interfaces
5
- import {IPrepayBase} from "./IPrepay.sol";
6
-
7
- // libraries
8
- import {PrepayStorage} from "./PrepayStorage.sol";
9
-
10
- // contracts
11
-
12
- abstract contract PrepayBase is IPrepayBase {
13
- function _addPrepay(uint256 supply) internal {
14
- PrepayStorage.Layout storage ds = PrepayStorage.layout();
15
- ds.supply += supply;
16
- emit Prepay__Prepaid(supply);
17
- }
18
-
19
- function _reducePrepay(uint256 supply) internal {
20
- PrepayStorage.Layout storage ds = PrepayStorage.layout();
21
- ds.supply -= supply;
22
- }
23
-
24
- function _getPrepaidSupply() internal view returns (uint256) {
25
- return PrepayStorage.layout().supply;
26
- }
27
- }
@@ -1,65 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.23;
3
-
4
- // interfaces
5
-
6
- import {IPlatformRequirements} from "src/factory/facets/platform/requirements/IPlatformRequirements.sol";
7
- import {IPrepay} from "src/spaces/facets/prepay/IPrepay.sol";
8
-
9
- // libraries
10
- import {MembershipStorage} from "src/spaces/facets/membership/MembershipStorage.sol";
11
- import {CurrencyTransfer} from "src/utils/libraries/CurrencyTransfer.sol";
12
-
13
- // contracts
14
- import {PrepayBase} from "./PrepayBase.sol";
15
-
16
- import {Facet} from "@towns-protocol/diamond/src/facets/Facet.sol";
17
- import {Entitled} from "src/spaces/facets/Entitled.sol";
18
- import {ReentrancyGuard} from "solady/utils/ReentrancyGuard.sol";
19
-
20
- contract PrepayFacet is IPrepay, PrepayBase, ReentrancyGuard, Entitled, Facet {
21
- function __PrepayFacet_init() external onlyInitializing {
22
- _addInterface(type(IPrepay).interfaceId);
23
- }
24
-
25
- function prepayMembership(uint256 supply) external payable nonReentrant {
26
- _validatePrepayCaller();
27
- if (supply == 0) revert Prepay__InvalidSupplyAmount();
28
-
29
- MembershipStorage.Layout storage ds = MembershipStorage.layout();
30
- IPlatformRequirements platform = IPlatformRequirements(ds.spaceFactory);
31
-
32
- uint256 cost = supply * platform.getMembershipFee();
33
-
34
- // validate payment covers membership fee
35
- if (msg.value != cost) revert Prepay__InvalidAmount();
36
-
37
- // add prepay
38
- _addPrepay(supply);
39
-
40
- // transfer fee to platform recipient
41
- address currency = ds.membershipCurrency;
42
- address platformRecipient = platform.getFeeRecipient();
43
- CurrencyTransfer.transferCurrency(
44
- currency,
45
- msg.sender, // from
46
- platformRecipient, // to
47
- cost
48
- );
49
- }
50
-
51
- function prepaidMembershipSupply() external view returns (uint256) {
52
- return _getPrepaidSupply();
53
- }
54
-
55
- function calculateMembershipPrepayFee(uint256 supply) external view returns (uint256) {
56
- MembershipStorage.Layout storage ds = MembershipStorage.layout();
57
- IPlatformRequirements platform = IPlatformRequirements(ds.spaceFactory);
58
- return supply * platform.getMembershipFee();
59
- }
60
-
61
- function _validatePrepayCaller() internal view {
62
- address spaceFactory = MembershipStorage.layout().spaceFactory;
63
- if (msg.sender != spaceFactory && msg.sender != _owner()) revert Prepay__NotAllowed();
64
- }
65
- }
@@ -1,26 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.23;
3
-
4
- // interfaces
5
-
6
- // libraries
7
-
8
- // contracts
9
-
10
- library PrepayStorage {
11
- // keccak256(abi.encode(uint256(keccak256("spaces.facets.prepay.storage")) - 1)) &
12
- // ~bytes32(uint256(0xff))
13
- bytes32 constant STORAGE_SLOT =
14
- 0x097b4f25b64e012d0cf55f67e9b34fe5d57f15b11b95baa4ddd136b424967c00;
15
-
16
- struct Layout {
17
- uint256 supply;
18
- }
19
-
20
- function layout() internal pure returns (Layout storage l) {
21
- bytes32 slot = STORAGE_SLOT;
22
- assembly {
23
- l.slot := slot
24
- }
25
- }
26
- }