@towns-protocol/contracts 0.0.442 → 0.0.444
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/docs/membership_architecture.md +237 -0
- package/package.json +3 -3
- package/scripts/deployments/diamonds/DeploySpaceFactory.s.sol +2 -2
- package/scripts/deployments/facets/DeployMembership.s.sol +2 -1
- package/scripts/deployments/utils/DeployMockERC20.s.sol +1 -1
- package/scripts/deployments/utils/DeployMockUSDC.s.sol +19 -0
- package/scripts/interactions/InteractPostDeploy.s.sol +11 -0
- package/src/factory/facets/feature/FeatureManagerFacet.sol +32 -29
- package/src/factory/facets/feature/FeatureManagerMod.sol +248 -0
- package/src/factory/facets/feature/{IFeatureManagerFacet.sol → IFeatureManager.sol} +2 -35
- package/src/factory/facets/fee/FeeManagerFacet.sol +1 -1
- package/src/factory/facets/fee/FeeTypesLib.sol +8 -1
- package/src/spaces/facets/dispatcher/DispatcherBase.sol +13 -5
- package/src/spaces/facets/gated/EntitlementGated.sol +9 -5
- package/src/spaces/facets/membership/IMembership.sol +11 -1
- package/src/spaces/facets/membership/MembershipBase.sol +30 -59
- package/src/spaces/facets/membership/MembershipFacet.sol +19 -1
- package/src/spaces/facets/membership/MembershipStorage.sol +1 -0
- package/src/spaces/facets/membership/join/MembershipJoin.sol +186 -110
- package/src/spaces/facets/treasury/ITreasury.sol +2 -1
- package/src/spaces/facets/treasury/Treasury.sol +21 -24
- package/src/spaces/facets/xchain/SpaceEntitlementGated.sol +3 -2
- package/src/factory/facets/feature/FeatureManagerBase.sol +0 -152
- package/src/factory/facets/feature/FeatureManagerStorage.sol +0 -47
|
@@ -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
|
-
}
|