@towns-protocol/contracts 0.0.452 → 0.0.455
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 +23 -6
- package/package.json +6 -6
- package/scripts/common/Interaction.s.sol +3 -3
- package/scripts/deployments/diamonds/DeployBaseRegistry.s.sol +1 -4
- package/scripts/deployments/diamonds/DeploySpace.s.sol +1 -9
- package/scripts/deployments/diamonds/DeploySpaceFactory.s.sol +1 -5
- package/scripts/deployments/facets/DeployAppFactoryFacet.s.sol +3 -3
- package/scripts/deployments/facets/DeployAppInstallerFacet.s.sol +3 -3
- package/scripts/deployments/facets/DeployAppRegistryFacet.s.sol +3 -3
- package/scripts/deployments/facets/DeployArchitect.s.sol +3 -3
- package/scripts/deployments/facets/DeployAttestationRegistry.s.sol +3 -3
- package/scripts/deployments/facets/DeployERC721ANonTransferable.s.sol +3 -3
- package/scripts/deployments/facets/DeployFeatureManager.s.sol +3 -3
- package/scripts/deployments/facets/DeployMainnetDelegation.s.sol +3 -3
- package/scripts/deployments/facets/DeployMembershipMetadata.s.sol +3 -3
- package/scripts/deployments/facets/DeployMerkleAirdrop.s.sol +3 -3
- package/scripts/deployments/facets/DeployMetadata.s.sol +3 -3
- package/scripts/deployments/facets/DeployMockLegacyArchitect.s.sol +3 -3
- package/scripts/deployments/facets/DeployNodeOperator.s.sol +3 -3
- package/scripts/deployments/facets/DeployPartnerRegistry.s.sol +3 -3
- package/scripts/deployments/facets/DeployPricingModules.s.sol +3 -3
- package/scripts/deployments/facets/DeploySchemaRegistry.s.sol +3 -3
- package/scripts/deployments/facets/DeploySpaceFactoryInit.s.sol +3 -3
- package/scripts/deployments/facets/DeploySpaceOwnerFacet.s.sol +3 -3
- package/scripts/deployments/facets/DeployTokenMigration.s.sol +3 -3
- package/scripts/deployments/facets/DeployXChain.s.sol +3 -3
- package/scripts/deployments/utils/DeployAccountFactory.s.sol +3 -3
- package/scripts/deployments/utils/DeployEntitlementGatedExample.s.sol +3 -3
- package/scripts/deployments/utils/DeployEntrypoint.s.sol +3 -3
- package/scripts/deployments/utils/DeployMember.s.sol +3 -3
- package/scripts/deployments/utils/DeployMockLegacyMembership.s.sol +3 -3
- package/scripts/deployments/utils/DeployMockMessenger.s.sol +3 -3
- package/scripts/deployments/utils/DeploySpaceProxyInitializer.s.sol +3 -3
- package/scripts/deployments/utils/DeployTieredLogPricingV2.s.sol +3 -3
- package/scripts/deployments/utils/DeployTieredLogPricingV3.s.sol +3 -3
- package/scripts/deployments/utils/DeployTownsBase.s.sol +3 -3
- package/scripts/deployments/utils/DeployTownsMainnet.s.sol +3 -3
- package/scripts/deployments/utils/pricing/TieredLogPricing.s.sol +3 -3
- package/scripts/interactions/InteractAirdrop.s.sol +3 -3
- package/scripts/interactions/InteractBaseBridge.s.sol +3 -3
- package/scripts/interactions/InteractRegisterApp.s.sol +2 -2
- package/scripts/interactions/InteractRiverRegistrySetTrimByStreamId.s.sol +44 -0
- package/scripts/interactions/helpers/RiverConfigValues.sol +1 -2
- package/src/account/facets/app/AppManagerFacet.sol +43 -14
- package/src/account/facets/app/AppManagerMod.sol +311 -278
- package/src/account/facets/hub/AccountHubFacet.sol +12 -11
- package/src/account/facets/hub/AccountHubMod.sol +117 -116
- package/src/account/facets/tipping/AccountTippingFacet.sol +10 -9
- package/src/account/facets/tipping/AccountTippingMod.sol +133 -124
- package/src/apps/modules/subscription/ISubscriptionModule.sol +40 -43
- package/src/apps/modules/subscription/SubscriptionModuleBase.sol +41 -16
- package/src/apps/modules/subscription/SubscriptionModuleFacet.sol +113 -105
- package/src/apps/modules/subscription/SubscriptionModuleStorage.sol +2 -1
- package/src/base/registry/facets/checker/EntitlementChecker.sol +43 -17
- package/src/river/registry/facets/stream/StreamRegistry.sol +4 -1
- package/src/spaces/facets/ProtocolFeeLib.sol +43 -0
- package/src/spaces/facets/membership/IMembership.sol +6 -1
- package/src/spaces/facets/membership/MembershipFacet.sol +16 -7
- package/src/spaces/facets/membership/join/MembershipJoin.sol +34 -23
- package/src/spaces/facets/tipping/TippingBase.sol +10 -26
- package/src/tokens/Member.sol +3 -4
- package/src/utils/libraries/CurrencyTransfer.sol +6 -2
- package/scripts/interactions/InteractRiverRegistrySetFreq.s.sol +0 -27
|
@@ -6,19 +6,19 @@ import {IModule} from "@erc6900/reference-implementation/interfaces/IModule.sol"
|
|
|
6
6
|
import {IValidationHookModule} from "@erc6900/reference-implementation/interfaces/IValidationHookModule.sol";
|
|
7
7
|
import {IValidationModule} from "@erc6900/reference-implementation/interfaces/IValidationModule.sol";
|
|
8
8
|
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
|
|
9
|
-
import {ISubscriptionModule} from "./ISubscriptionModule.sol";
|
|
10
9
|
import {IMembership} from "../../../spaces/facets/membership/IMembership.sol";
|
|
11
10
|
import {IBanning} from "../../../spaces/facets/banning/IBanning.sol";
|
|
11
|
+
import {ISubscriptionModule} from "./ISubscriptionModule.sol";
|
|
12
12
|
|
|
13
13
|
// libraries
|
|
14
14
|
import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol";
|
|
15
15
|
import {EnumerableSetLib} from "solady/utils/EnumerableSetLib.sol";
|
|
16
16
|
import {ReentrancyGuardTransient} from "solady/utils/ReentrancyGuardTransient.sol";
|
|
17
|
+
import {SafeCastLib} from "solady/utils/SafeCastLib.sol";
|
|
17
18
|
import {CustomRevert} from "../../../utils/libraries/CustomRevert.sol";
|
|
18
19
|
import {Validator} from "../../../utils/libraries/Validator.sol";
|
|
19
20
|
import {IArchitect} from "../../../factory/facets/architect/IArchitect.sol";
|
|
20
21
|
import {Subscription, SubscriptionModuleStorage} from "./SubscriptionModuleStorage.sol";
|
|
21
|
-
import {SafeCastLib} from "solady/utils/SafeCastLib.sol";
|
|
22
22
|
|
|
23
23
|
// contracts
|
|
24
24
|
import {ModuleBase} from "modular-account/src/modules/ModuleBase.sol";
|
|
@@ -38,10 +38,10 @@ contract SubscriptionModuleFacet is
|
|
|
38
38
|
SubscriptionModuleBase,
|
|
39
39
|
Facet
|
|
40
40
|
{
|
|
41
|
-
using EnumerableSetLib for EnumerableSetLib.Uint256Set;
|
|
42
41
|
using EnumerableSetLib for EnumerableSetLib.AddressSet;
|
|
43
|
-
using
|
|
42
|
+
using EnumerableSetLib for EnumerableSetLib.Uint256Set;
|
|
44
43
|
using CustomRevert for bytes4;
|
|
44
|
+
using SafeCastLib for uint256;
|
|
45
45
|
|
|
46
46
|
uint256 internal constant _SIG_VALIDATION_FAILED = 1;
|
|
47
47
|
|
|
@@ -54,14 +54,34 @@ contract SubscriptionModuleFacet is
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
|
|
57
|
-
/*
|
|
57
|
+
/* ADMIN FUNCTIONS */
|
|
58
58
|
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
|
|
59
59
|
|
|
60
|
-
/// @inheritdoc
|
|
61
|
-
function
|
|
62
|
-
|
|
60
|
+
/// @inheritdoc ISubscriptionModule
|
|
61
|
+
function setSpaceFactory(address spaceFactory) external onlyOwner {
|
|
62
|
+
Validator.checkAddress(spaceFactory);
|
|
63
|
+
SubscriptionModuleStorage.getLayout().spaceFactory = spaceFactory;
|
|
64
|
+
emit SpaceFactoryChanged(spaceFactory);
|
|
63
65
|
}
|
|
64
66
|
|
|
67
|
+
/// @inheritdoc ISubscriptionModule
|
|
68
|
+
function grantOperator(address operator) external onlyOwner {
|
|
69
|
+
Validator.checkAddress(operator);
|
|
70
|
+
SubscriptionModuleStorage.getLayout().operators.add(operator);
|
|
71
|
+
emit OperatorGranted(operator);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// @inheritdoc ISubscriptionModule
|
|
75
|
+
function revokeOperator(address operator) external onlyOwner {
|
|
76
|
+
Validator.checkAddress(operator);
|
|
77
|
+
SubscriptionModuleStorage.getLayout().operators.remove(operator);
|
|
78
|
+
emit OperatorRevoked(operator);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
|
|
82
|
+
/* MODULE LIFECYCLE */
|
|
83
|
+
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
|
|
84
|
+
|
|
65
85
|
/// @inheritdoc IModule
|
|
66
86
|
function onInstall(bytes calldata data) external override nonReentrant {
|
|
67
87
|
(uint32 entityId, address space, uint256 tokenId) = abi.decode(
|
|
@@ -135,69 +155,9 @@ contract SubscriptionModuleFacet is
|
|
|
135
155
|
emit SubscriptionDeactivated(msg.sender, entityId);
|
|
136
156
|
}
|
|
137
157
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
PackedUserOperation calldata,
|
|
142
|
-
bytes32
|
|
143
|
-
) external pure override returns (uint256) {
|
|
144
|
-
return _SIG_VALIDATION_FAILED;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/// @inheritdoc IValidationModule
|
|
148
|
-
function validateSignature(
|
|
149
|
-
address,
|
|
150
|
-
uint32,
|
|
151
|
-
address,
|
|
152
|
-
bytes32,
|
|
153
|
-
bytes calldata
|
|
154
|
-
) external pure override returns (bytes4) {
|
|
155
|
-
return 0xffffffff;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/// @inheritdoc IValidationModule
|
|
159
|
-
function validateRuntime(
|
|
160
|
-
address account,
|
|
161
|
-
uint32 entityId,
|
|
162
|
-
address sender,
|
|
163
|
-
uint256,
|
|
164
|
-
bytes calldata,
|
|
165
|
-
bytes calldata
|
|
166
|
-
) external view override {
|
|
167
|
-
if (sender != address(this)) SubscriptionModule__InvalidSender.selector.revertWith();
|
|
168
|
-
bool active = SubscriptionModuleStorage.getLayout().subscriptions[account][entityId].active;
|
|
169
|
-
if (!active) SubscriptionModule__InactiveSubscription.selector.revertWith();
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/// @inheritdoc IValidationHookModule
|
|
173
|
-
function preUserOpValidationHook(
|
|
174
|
-
uint32 /* entityId */,
|
|
175
|
-
PackedUserOperation calldata /* userOp */,
|
|
176
|
-
bytes32 /* userOpHash */
|
|
177
|
-
) external pure override returns (uint256) {
|
|
178
|
-
return _SIG_VALIDATION_FAILED;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/// @inheritdoc IValidationHookModule
|
|
182
|
-
function preRuntimeValidationHook(
|
|
183
|
-
uint32 /* entityId */,
|
|
184
|
-
address /* sender */,
|
|
185
|
-
uint256 /* value */,
|
|
186
|
-
bytes calldata /* data */,
|
|
187
|
-
bytes calldata /* authorization */
|
|
188
|
-
) external pure override {
|
|
189
|
-
SubscriptionModule__NotSupported.selector.revertWith();
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/// @inheritdoc IValidationHookModule
|
|
193
|
-
function preSignatureValidationHook(
|
|
194
|
-
uint32 /* entityId */,
|
|
195
|
-
address /* sender */,
|
|
196
|
-
bytes32 /* hash */,
|
|
197
|
-
bytes calldata /* signature */
|
|
198
|
-
) external pure override {
|
|
199
|
-
SubscriptionModule__NotSupported.selector.revertWith();
|
|
200
|
-
}
|
|
158
|
+
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
|
|
159
|
+
/* STATE-CHANGING FUNCTIONS */
|
|
160
|
+
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
|
|
201
161
|
|
|
202
162
|
/// @inheritdoc ISubscriptionModule
|
|
203
163
|
function batchProcessRenewals(RenewalParams[] calldata params) external nonReentrant {
|
|
@@ -257,19 +217,6 @@ contract SubscriptionModuleFacet is
|
|
|
257
217
|
}
|
|
258
218
|
}
|
|
259
219
|
|
|
260
|
-
/// @inheritdoc ISubscriptionModule
|
|
261
|
-
function getSubscription(
|
|
262
|
-
address account,
|
|
263
|
-
uint32 entityId
|
|
264
|
-
) external view returns (Subscription memory) {
|
|
265
|
-
return SubscriptionModuleStorage.getLayout().subscriptions[account][entityId];
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/// @inheritdoc ISubscriptionModule
|
|
269
|
-
function getRenewalBuffer(uint256 duration) external pure returns (uint256) {
|
|
270
|
-
return _getRenewalBuffer(duration);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
220
|
/// @inheritdoc ISubscriptionModule
|
|
274
221
|
function activateSubscription(uint32 entityId) external nonReentrant {
|
|
275
222
|
SubscriptionModuleStorage.Layout storage $ = SubscriptionModuleStorage.getLayout();
|
|
@@ -304,16 +251,26 @@ contract SubscriptionModuleFacet is
|
|
|
304
251
|
_pauseSubscription(sub, msg.sender, entityId);
|
|
305
252
|
}
|
|
306
253
|
|
|
254
|
+
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
|
|
255
|
+
/* GETTERS */
|
|
256
|
+
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
|
|
257
|
+
|
|
307
258
|
/// @inheritdoc ISubscriptionModule
|
|
308
|
-
function
|
|
309
|
-
return SubscriptionModuleStorage.getLayout().
|
|
259
|
+
function getSpaceFactory() external view returns (address) {
|
|
260
|
+
return SubscriptionModuleStorage.getLayout().spaceFactory;
|
|
310
261
|
}
|
|
311
262
|
|
|
312
263
|
/// @inheritdoc ISubscriptionModule
|
|
313
|
-
function
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
264
|
+
function getSubscription(
|
|
265
|
+
address account,
|
|
266
|
+
uint32 entityId
|
|
267
|
+
) external view returns (Subscription memory) {
|
|
268
|
+
return SubscriptionModuleStorage.getLayout().subscriptions[account][entityId];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/// @inheritdoc ISubscriptionModule
|
|
272
|
+
function getEntityIds(address account) external view returns (uint256[] memory) {
|
|
273
|
+
return SubscriptionModuleStorage.getLayout().entityIds[account].values();
|
|
317
274
|
}
|
|
318
275
|
|
|
319
276
|
/// @inheritdoc ISubscriptionModule
|
|
@@ -321,26 +278,77 @@ contract SubscriptionModuleFacet is
|
|
|
321
278
|
return SubscriptionModuleStorage.getLayout().operators.contains(operator);
|
|
322
279
|
}
|
|
323
280
|
|
|
324
|
-
/// @inheritdoc
|
|
325
|
-
function
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
281
|
+
/// @inheritdoc IValidationModule
|
|
282
|
+
function validateRuntime(
|
|
283
|
+
address account,
|
|
284
|
+
uint32 entityId,
|
|
285
|
+
address sender,
|
|
286
|
+
uint256,
|
|
287
|
+
bytes calldata,
|
|
288
|
+
bytes calldata
|
|
289
|
+
) external view override {
|
|
290
|
+
if (sender != address(this)) SubscriptionModule__InvalidSender.selector.revertWith();
|
|
291
|
+
bool active = SubscriptionModuleStorage.getLayout().subscriptions[account][entityId].active;
|
|
292
|
+
if (!active) SubscriptionModule__InactiveSubscription.selector.revertWith();
|
|
329
293
|
}
|
|
330
294
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
295
|
+
/// @inheritdoc IModule
|
|
296
|
+
function moduleId() external pure returns (string memory) {
|
|
297
|
+
return "towns.subscription-module.1.0.0";
|
|
298
|
+
}
|
|
334
299
|
|
|
335
300
|
/// @inheritdoc ISubscriptionModule
|
|
336
|
-
function
|
|
337
|
-
|
|
338
|
-
SubscriptionModuleStorage.getLayout().spaceFactory = spaceFactory;
|
|
339
|
-
emit SpaceFactoryChanged(spaceFactory);
|
|
301
|
+
function getRenewalBuffer(uint256 duration) external pure returns (uint256) {
|
|
302
|
+
return _getRenewalBuffer(duration);
|
|
340
303
|
}
|
|
341
304
|
|
|
342
|
-
/// @inheritdoc
|
|
343
|
-
function
|
|
344
|
-
|
|
305
|
+
/// @inheritdoc IValidationModule
|
|
306
|
+
function validateUserOp(
|
|
307
|
+
uint32,
|
|
308
|
+
PackedUserOperation calldata,
|
|
309
|
+
bytes32
|
|
310
|
+
) external pure override returns (uint256) {
|
|
311
|
+
return _SIG_VALIDATION_FAILED;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/// @inheritdoc IValidationModule
|
|
315
|
+
function validateSignature(
|
|
316
|
+
address,
|
|
317
|
+
uint32,
|
|
318
|
+
address,
|
|
319
|
+
bytes32,
|
|
320
|
+
bytes calldata
|
|
321
|
+
) external pure override returns (bytes4) {
|
|
322
|
+
return 0xffffffff;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/// @inheritdoc IValidationHookModule
|
|
326
|
+
function preUserOpValidationHook(
|
|
327
|
+
uint32 /* entityId */,
|
|
328
|
+
PackedUserOperation calldata /* userOp */,
|
|
329
|
+
bytes32 /* userOpHash */
|
|
330
|
+
) external pure override returns (uint256) {
|
|
331
|
+
return _SIG_VALIDATION_FAILED;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/// @inheritdoc IValidationHookModule
|
|
335
|
+
function preRuntimeValidationHook(
|
|
336
|
+
uint32 /* entityId */,
|
|
337
|
+
address /* sender */,
|
|
338
|
+
uint256 /* value */,
|
|
339
|
+
bytes calldata /* data */,
|
|
340
|
+
bytes calldata /* authorization */
|
|
341
|
+
) external pure override {
|
|
342
|
+
SubscriptionModule__NotSupported.selector.revertWith();
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/// @inheritdoc IValidationHookModule
|
|
346
|
+
function preSignatureValidationHook(
|
|
347
|
+
uint32 /* entityId */,
|
|
348
|
+
address /* sender */,
|
|
349
|
+
bytes32 /* hash */,
|
|
350
|
+
bytes calldata /* signature */
|
|
351
|
+
) external pure override {
|
|
352
|
+
SubscriptionModule__NotSupported.selector.revertWith();
|
|
345
353
|
}
|
|
346
354
|
}
|
|
@@ -14,6 +14,7 @@ struct Subscription {
|
|
|
14
14
|
uint64 duration; // 8 bytes
|
|
15
15
|
uint256 lastKnownRenewalPrice; // 32 bytes
|
|
16
16
|
uint256 lastKnownExpiresAt; // 32 bytes
|
|
17
|
+
address lastKnownCurrency; // 20 bytes - currency at install/sync time
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
struct OperatorConfig {
|
|
@@ -39,7 +40,7 @@ library SubscriptionModuleStorage {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
// keccak256(abi.encode(uint256(keccak256("towns.subscription.validation.module.storage")) - 1)) & ~bytes32(uint256(0xff))
|
|
42
|
-
bytes32
|
|
43
|
+
bytes32 internal constant STORAGE_SLOT =
|
|
43
44
|
0xd241b3ceee256b40f80fe7a66fe789234ac389ed1408c472c4ee1cbb1deb8600;
|
|
44
45
|
|
|
45
46
|
function getLayout() internal pure returns (Layout storage $) {
|
|
@@ -121,17 +121,36 @@ contract EntitlementChecker is IEntitlementChecker, Facet {
|
|
|
121
121
|
function requestEntitlementCheck(CheckType checkType, bytes calldata data) external payable {
|
|
122
122
|
if (checkType == CheckType.V1) {
|
|
123
123
|
if (msg.value != 0) EntitlementChecker_InvalidValue.selector.revertWith();
|
|
124
|
-
(address
|
|
125
|
-
|
|
124
|
+
// equivalent: abi.decode(data, (address, bytes32, uint256, address[]))
|
|
125
|
+
address receiver;
|
|
126
|
+
bytes32 transactionId;
|
|
127
|
+
uint256 roleId;
|
|
128
|
+
address[] calldata nodes;
|
|
129
|
+
assembly {
|
|
130
|
+
receiver := shr(96, shl(96, calldataload(data.offset)))
|
|
131
|
+
transactionId := calldataload(add(data.offset, 0x20))
|
|
132
|
+
roleId := calldataload(add(data.offset, 0x40))
|
|
133
|
+
// nodes is dynamic: offset at 0x60, array starts at data.offset + offset
|
|
134
|
+
let nodesPtr := add(data.offset, calldataload(add(data.offset, 0x60)))
|
|
135
|
+
nodes.length := calldataload(nodesPtr)
|
|
136
|
+
nodes.offset := add(nodesPtr, 0x20)
|
|
137
|
+
}
|
|
126
138
|
emit EntitlementCheckRequested(receiver, msg.sender, transactionId, roleId, nodes);
|
|
127
139
|
} else if (checkType == CheckType.V2) {
|
|
128
|
-
(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
140
|
+
// equivalent: abi.decode(data, (address, bytes32, uint256, bytes))
|
|
141
|
+
// extraData contains: (address sender)
|
|
142
|
+
address receiver;
|
|
143
|
+
bytes32 transactionId;
|
|
144
|
+
uint256 requestId;
|
|
145
|
+
address sender;
|
|
146
|
+
assembly {
|
|
147
|
+
receiver := shr(96, shl(96, calldataload(data.offset)))
|
|
148
|
+
transactionId := calldataload(add(data.offset, 0x20))
|
|
149
|
+
requestId := calldataload(add(data.offset, 0x40))
|
|
150
|
+
// extraData offset at 0x60, sender is first word after length
|
|
151
|
+
let extraDataPtr := add(data.offset, calldataload(add(data.offset, 0x60)))
|
|
152
|
+
sender := shr(96, shl(96, calldataload(add(extraDataPtr, 0x20))))
|
|
153
|
+
}
|
|
135
154
|
_requestEntitlementCheck(
|
|
136
155
|
receiver,
|
|
137
156
|
transactionId,
|
|
@@ -141,14 +160,21 @@ contract EntitlementChecker is IEntitlementChecker, Facet {
|
|
|
141
160
|
sender
|
|
142
161
|
);
|
|
143
162
|
} else if (checkType == CheckType.V3) {
|
|
144
|
-
(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
163
|
+
// equivalent: abi.decode(data, (address, bytes32, uint256, address, uint256, address))
|
|
164
|
+
address receiver;
|
|
165
|
+
bytes32 transactionId;
|
|
166
|
+
uint256 requestId;
|
|
167
|
+
address currency;
|
|
168
|
+
uint256 amount;
|
|
169
|
+
address sender;
|
|
170
|
+
assembly {
|
|
171
|
+
receiver := shr(96, shl(96, calldataload(data.offset)))
|
|
172
|
+
transactionId := calldataload(add(data.offset, 0x20))
|
|
173
|
+
requestId := calldataload(add(data.offset, 0x40))
|
|
174
|
+
currency := shr(96, shl(96, calldataload(add(data.offset, 0x60))))
|
|
175
|
+
amount := calldataload(add(data.offset, 0x80))
|
|
176
|
+
sender := shr(96, shl(96, calldataload(add(data.offset, 0xa0))))
|
|
177
|
+
}
|
|
152
178
|
_requestEntitlementCheck(receiver, transactionId, requestId, currency, amount, sender);
|
|
153
179
|
} else {
|
|
154
180
|
EntitlementChecker_InvalidCheckType.selector.revertWith();
|
|
@@ -110,7 +110,10 @@ contract StreamRegistry is IStreamRegistry, RegistryModifiers {
|
|
|
110
110
|
|
|
111
111
|
// Check if the lastMiniblockNum is the next expected miniblock and
|
|
112
112
|
// the prevMiniblockHash is correct
|
|
113
|
-
if (
|
|
113
|
+
if (
|
|
114
|
+
stream.lastMiniblockNum + 1 != miniblock.lastMiniblockNum ||
|
|
115
|
+
stream.lastMiniblockHash != miniblock.prevMiniBlockHash
|
|
116
|
+
) {
|
|
114
117
|
emit StreamLastMiniblockUpdateFailed(
|
|
115
118
|
miniblock.streamId,
|
|
116
119
|
miniblock.lastMiniblockHash,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.23;
|
|
3
|
+
|
|
4
|
+
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
|
|
5
|
+
import {IFeeManager} from "../../factory/facets/fee/IFeeManager.sol";
|
|
6
|
+
import {CurrencyTransfer} from "../../utils/libraries/CurrencyTransfer.sol";
|
|
7
|
+
|
|
8
|
+
library ProtocolFeeLib {
|
|
9
|
+
using SafeTransferLib for address;
|
|
10
|
+
|
|
11
|
+
/// @notice Charges protocol fee via FeeManager with ERC20 approval handling
|
|
12
|
+
/// @param spaceFactory The FeeManager address
|
|
13
|
+
/// @param feeType The type of fee being charged
|
|
14
|
+
/// @param user The user paying the fee
|
|
15
|
+
/// @param currency The payment currency (NATIVE_TOKEN or ERC20)
|
|
16
|
+
/// @param amount The base amount for fee calculation
|
|
17
|
+
/// @param expectedFee The pre-calculated expected fee
|
|
18
|
+
/// @return protocolFee The actual fee charged
|
|
19
|
+
function charge(
|
|
20
|
+
address spaceFactory,
|
|
21
|
+
bytes32 feeType,
|
|
22
|
+
address user,
|
|
23
|
+
address currency,
|
|
24
|
+
uint256 amount,
|
|
25
|
+
uint256 expectedFee
|
|
26
|
+
) internal returns (uint256 protocolFee) {
|
|
27
|
+
if (expectedFee == 0) return 0;
|
|
28
|
+
|
|
29
|
+
bool isNative = currency == CurrencyTransfer.NATIVE_TOKEN;
|
|
30
|
+
if (!isNative) currency.safeApproveWithRetry(spaceFactory, expectedFee);
|
|
31
|
+
|
|
32
|
+
protocolFee = IFeeManager(spaceFactory).chargeFee{value: isNative ? expectedFee : 0}(
|
|
33
|
+
feeType,
|
|
34
|
+
user,
|
|
35
|
+
amount,
|
|
36
|
+
currency,
|
|
37
|
+
expectedFee,
|
|
38
|
+
""
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
if (!isNative) currency.safeApprove(spaceFactory, 0);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -75,6 +75,11 @@ interface IMembershipBase {
|
|
|
75
75
|
event MembershipFreeAllocationUpdated(uint256 indexed allocation);
|
|
76
76
|
event MembershipWithdrawal(address indexed currency, address indexed recipient, uint256 amount);
|
|
77
77
|
event MembershipTokenIssued(address indexed recipient, uint256 indexed tokenId);
|
|
78
|
+
/// @notice Emitted when a membership payment is processed (new membership or renewal)
|
|
79
|
+
/// @param currency The currency used for payment (0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE for ETH)
|
|
80
|
+
/// @param price The base membership price paid
|
|
81
|
+
/// @param protocolFee The protocol fee paid
|
|
82
|
+
event MembershipPaid(address indexed currency, uint256 price, uint256 protocolFee);
|
|
78
83
|
event MembershipTokenRejected(address indexed recipient);
|
|
79
84
|
}
|
|
80
85
|
|
|
@@ -97,7 +102,7 @@ interface IMembership is IMembershipBase {
|
|
|
97
102
|
/// @param referral The referral data
|
|
98
103
|
function joinSpaceWithReferral(
|
|
99
104
|
address receiver,
|
|
100
|
-
ReferralTypes
|
|
105
|
+
ReferralTypes calldata referral
|
|
101
106
|
) external payable;
|
|
102
107
|
|
|
103
108
|
/// @notice Renew a space membership
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
pragma solidity ^0.8.23;
|
|
3
3
|
|
|
4
4
|
// interfaces
|
|
5
|
-
import {IMembership} from "./IMembership.sol";
|
|
6
5
|
import {IMembershipPricing} from "./pricing/IMembershipPricing.sol";
|
|
6
|
+
import {IMembership} from "./IMembership.sol";
|
|
7
7
|
|
|
8
8
|
// libraries
|
|
9
9
|
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
|
|
@@ -26,13 +26,22 @@ contract MembershipFacet is IMembership, MembershipJoin, ReentrancyGuard, Facet
|
|
|
26
26
|
/// @inheritdoc IMembership
|
|
27
27
|
function joinSpace(JoinType action, bytes calldata data) external payable nonReentrant {
|
|
28
28
|
if (action == JoinType.Basic) {
|
|
29
|
-
|
|
29
|
+
// equivalent: abi.decode(data, (address))
|
|
30
|
+
address receiver;
|
|
31
|
+
assembly {
|
|
32
|
+
receiver := shr(96, shl(96, calldataload(data.offset)))
|
|
33
|
+
}
|
|
30
34
|
_joinSpace(receiver);
|
|
31
35
|
} else if (action == JoinType.WithReferral) {
|
|
32
|
-
(address
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
// equivalent: abi.decode(data, (address, ReferralTypes))
|
|
37
|
+
address receiver;
|
|
38
|
+
ReferralTypes calldata referral;
|
|
39
|
+
assembly {
|
|
40
|
+
receiver := shr(96, shl(96, calldataload(data.offset)))
|
|
41
|
+
// this is a variable length struct, so (data.offset + 0x20) contains
|
|
42
|
+
// the offset from data.offset at which the struct begins
|
|
43
|
+
referral := add(data.offset, calldataload(add(data.offset, 0x20)))
|
|
44
|
+
}
|
|
36
45
|
_joinSpaceWithReferral(receiver, referral);
|
|
37
46
|
} else {
|
|
38
47
|
Membership__InvalidAction.selector.revertWith();
|
|
@@ -47,7 +56,7 @@ contract MembershipFacet is IMembership, MembershipJoin, ReentrancyGuard, Facet
|
|
|
47
56
|
/// @inheritdoc IMembership
|
|
48
57
|
function joinSpaceWithReferral(
|
|
49
58
|
address receiver,
|
|
50
|
-
ReferralTypes
|
|
59
|
+
ReferralTypes calldata referral
|
|
51
60
|
) external payable nonReentrant {
|
|
52
61
|
_joinSpaceWithReferral(receiver, referral);
|
|
53
62
|
}
|
|
@@ -16,6 +16,7 @@ import {CurrencyTransfer} from "../../../../utils/libraries/CurrencyTransfer.sol
|
|
|
16
16
|
import {CustomRevert} from "../../../../utils/libraries/CustomRevert.sol";
|
|
17
17
|
import {MembershipStorage} from "../MembershipStorage.sol";
|
|
18
18
|
import {Permissions} from "../../Permissions.sol";
|
|
19
|
+
import {ProtocolFeeLib} from "../../ProtocolFeeLib.sol";
|
|
19
20
|
|
|
20
21
|
// contracts
|
|
21
22
|
import {ERC5643Base} from "../../../../diamond/facets/token/ERC5643/ERC5643Base.sol";
|
|
@@ -67,6 +68,7 @@ abstract contract MembershipJoin is
|
|
|
67
68
|
|
|
68
69
|
struct PricingDetails {
|
|
69
70
|
uint256 basePrice;
|
|
71
|
+
uint256 protocolFee;
|
|
70
72
|
uint256 amountDue;
|
|
71
73
|
bool shouldCharge;
|
|
72
74
|
}
|
|
@@ -100,8 +102,12 @@ abstract contract MembershipJoin is
|
|
|
100
102
|
return joinDetails;
|
|
101
103
|
}
|
|
102
104
|
|
|
103
|
-
(uint256 totalRequired, ) = _getTotalMembershipPayment(membershipPrice);
|
|
104
|
-
(joinDetails.amountDue, joinDetails.shouldCharge) = (
|
|
105
|
+
(uint256 totalRequired, uint256 protocolFee) = _getTotalMembershipPayment(membershipPrice);
|
|
106
|
+
(joinDetails.protocolFee, joinDetails.amountDue, joinDetails.shouldCharge) = (
|
|
107
|
+
protocolFee,
|
|
108
|
+
totalRequired,
|
|
109
|
+
true
|
|
110
|
+
);
|
|
105
111
|
}
|
|
106
112
|
|
|
107
113
|
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
|
|
@@ -150,7 +156,7 @@ abstract contract MembershipJoin is
|
|
|
150
156
|
/// @notice Handles the process of joining a space with a referral
|
|
151
157
|
/// @param receiver The address that will receive the membership token
|
|
152
158
|
/// @param referral The referral information
|
|
153
|
-
function _joinSpaceWithReferral(address receiver, ReferralTypes
|
|
159
|
+
function _joinSpaceWithReferral(address receiver, ReferralTypes calldata referral) internal {
|
|
154
160
|
_validateJoinSpace(receiver);
|
|
155
161
|
|
|
156
162
|
PricingDetails memory joinDetails = _getPricingDetails();
|
|
@@ -229,7 +235,10 @@ abstract contract MembershipJoin is
|
|
|
229
235
|
return amountRequired;
|
|
230
236
|
}
|
|
231
237
|
|
|
232
|
-
function _validateUserReferral(
|
|
238
|
+
function _validateUserReferral(
|
|
239
|
+
address receiver,
|
|
240
|
+
ReferralTypes calldata referral
|
|
241
|
+
) internal view {
|
|
233
242
|
if (referral.userReferral == receiver || referral.userReferral == msg.sender) {
|
|
234
243
|
Membership__InvalidAddress.selector.revertWith();
|
|
235
244
|
}
|
|
@@ -341,7 +350,10 @@ abstract contract MembershipJoin is
|
|
|
341
350
|
Membership__InvalidTransactionType.selector.revertWith();
|
|
342
351
|
}
|
|
343
352
|
|
|
344
|
-
|
|
353
|
+
address currency = _getMembershipCurrency();
|
|
354
|
+
_payProtocolFee(currency, joinDetails.basePrice, joinDetails.protocolFee);
|
|
355
|
+
|
|
356
|
+
emit MembershipPaid(currency, joinDetails.basePrice, joinDetails.protocolFee);
|
|
345
357
|
|
|
346
358
|
_afterChargeForJoinSpace(transactionId, receiver, joinDetails.amountDue);
|
|
347
359
|
}
|
|
@@ -365,7 +377,7 @@ abstract contract MembershipJoin is
|
|
|
365
377
|
ReferralTypes memory referral = abi.decode(referralData, (ReferralTypes));
|
|
366
378
|
|
|
367
379
|
address currency = _getMembershipCurrency();
|
|
368
|
-
_payProtocolFee(currency, joinDetails.basePrice);
|
|
380
|
+
_payProtocolFee(currency, joinDetails.basePrice, joinDetails.protocolFee);
|
|
369
381
|
_payPartnerFee(currency, referral.partner, joinDetails.basePrice);
|
|
370
382
|
_payReferralFee(
|
|
371
383
|
currency,
|
|
@@ -375,6 +387,8 @@ abstract contract MembershipJoin is
|
|
|
375
387
|
joinDetails.basePrice
|
|
376
388
|
);
|
|
377
389
|
|
|
390
|
+
emit MembershipPaid(currency, joinDetails.basePrice, joinDetails.protocolFee);
|
|
391
|
+
|
|
378
392
|
_afterChargeForJoinSpace(transactionId, receiver, joinDetails.amountDue);
|
|
379
393
|
}
|
|
380
394
|
|
|
@@ -410,7 +424,6 @@ abstract contract MembershipJoin is
|
|
|
410
424
|
// set expiration of membership
|
|
411
425
|
_renewSubscription(tokenId, _getMembershipDuration());
|
|
412
426
|
|
|
413
|
-
// emit event
|
|
414
427
|
emit MembershipTokenIssued(receiver, tokenId);
|
|
415
428
|
}
|
|
416
429
|
|
|
@@ -447,21 +460,18 @@ abstract contract MembershipJoin is
|
|
|
447
460
|
/* FEE DISTRIBUTION */
|
|
448
461
|
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
|
|
449
462
|
|
|
450
|
-
/// @notice Pays the protocol fee
|
|
463
|
+
/// @notice Pays the protocol fee via FeeManager
|
|
451
464
|
/// @param currency The currency to pay in
|
|
452
|
-
/// @param
|
|
453
|
-
/// @
|
|
454
|
-
function _payProtocolFee(
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
CurrencyTransfer.transferCurrency(
|
|
465
|
+
/// @param basePrice The base price of the membership (for fee calculation)
|
|
466
|
+
/// @param expectedFee The pre-calculated protocol fee
|
|
467
|
+
function _payProtocolFee(address currency, uint256 basePrice, uint256 expectedFee) internal {
|
|
468
|
+
ProtocolFeeLib.charge(
|
|
469
|
+
_getSpaceFactory(),
|
|
470
|
+
_getMembershipFeeType(currency),
|
|
471
|
+
msg.sender,
|
|
461
472
|
currency,
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
protocolFee
|
|
473
|
+
basePrice,
|
|
474
|
+
expectedFee
|
|
465
475
|
);
|
|
466
476
|
}
|
|
467
477
|
|
|
@@ -542,13 +552,13 @@ abstract contract MembershipJoin is
|
|
|
542
552
|
return;
|
|
543
553
|
}
|
|
544
554
|
|
|
545
|
-
(uint256 totalRequired, ) = _getTotalMembershipPayment(basePrice);
|
|
555
|
+
(uint256 totalRequired, uint256 protocolFee) = _getTotalMembershipPayment(basePrice);
|
|
546
556
|
|
|
547
557
|
if (currency == CurrencyTransfer.NATIVE_TOKEN) {
|
|
548
558
|
// ETH payment: validate msg.value
|
|
549
559
|
if (totalRequired > msg.value) Membership__InvalidPayment.selector.revertWith();
|
|
550
560
|
|
|
551
|
-
_payProtocolFee(currency, basePrice);
|
|
561
|
+
_payProtocolFee(currency, basePrice, protocolFee);
|
|
552
562
|
|
|
553
563
|
// Handle excess payment
|
|
554
564
|
uint256 excess = msg.value - totalRequired;
|
|
@@ -562,9 +572,10 @@ abstract contract MembershipJoin is
|
|
|
562
572
|
// Transfer ERC20 from payer to contract
|
|
563
573
|
_transferIn(currency, payer, totalRequired);
|
|
564
574
|
|
|
565
|
-
_payProtocolFee(currency, basePrice);
|
|
575
|
+
_payProtocolFee(currency, basePrice, protocolFee);
|
|
566
576
|
}
|
|
567
577
|
|
|
578
|
+
emit MembershipPaid(currency, basePrice, protocolFee);
|
|
568
579
|
_mintMembershipPoints(receiver, totalRequired);
|
|
569
580
|
_renewSubscription(tokenId, uint64(duration));
|
|
570
581
|
}
|