@towns-protocol/contracts 0.0.354 → 0.0.355

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.354",
3
+ "version": "0.0.355",
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.354",
38
+ "@towns-protocol/prettier-config": "^0.0.355",
39
39
  "@typechain/ethers-v5": "^10.1.1",
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": "a6dc807ec679a6507c2f1b10bb54af6adf78e01e"
60
+ "gitHead": "f0192ebd59ae6a99003a0753d1821713e2407bc5"
61
61
  }
@@ -8,6 +8,7 @@ import {IValidationHookModule} from "@erc6900/reference-implementation/interface
8
8
  import {IValidationModule} from "@erc6900/reference-implementation/interfaces/IValidationModule.sol";
9
9
  import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
10
10
  import {ISubscriptionModule} from "./ISubscriptionModule.sol";
11
+ import {IMembership} from "../../../spaces/facets/membership/IMembership.sol";
11
12
 
12
13
  // libraries
13
14
  import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol";
@@ -22,7 +23,6 @@ import {Subscription, SubscriptionModuleStorage} from "./SubscriptionModuleStora
22
23
  // contracts
23
24
  import {ModuleBase} from "modular-account/src/modules/ModuleBase.sol";
24
25
  import {OwnableBase} from "@towns-protocol/diamond/src/facets/ownable/OwnableBase.sol";
25
- import {MembershipFacet} from "../../../spaces/facets/membership/MembershipFacet.sol";
26
26
  import {Facet} from "@towns-protocol/diamond/src/facets/Facet.sol";
27
27
 
28
28
  /// @title Subscription Module
@@ -75,13 +75,19 @@ contract SubscriptionModuleFacet is
75
75
 
76
76
  Validator.checkAddress(space);
77
77
 
78
+ if (entityId == 0) SubscriptionModule__InvalidEntityId.selector.revertWith();
79
+
78
80
  if (IERC721(space).ownerOf(tokenId) != msg.sender)
79
81
  SubscriptionModule__InvalidTokenOwner.selector.revertWith();
80
82
 
81
- MembershipFacet membershipFacet = MembershipFacet(space);
83
+ IMembership membershipFacet = IMembership(space);
82
84
  uint256 expiresAt = membershipFacet.expiresAt(tokenId);
83
85
 
84
86
  SubscriptionModuleStorage.Layout storage $ = SubscriptionModuleStorage.getLayout();
87
+
88
+ if (!$.entityIds[msg.sender].add(entityId))
89
+ SubscriptionModule__InvalidEntityId.selector.revertWith();
90
+
85
91
  Subscription storage sub = $.subscriptions[msg.sender][entityId];
86
92
  sub.space = space;
87
93
  sub.active = true;
@@ -89,8 +95,6 @@ contract SubscriptionModuleFacet is
89
95
  sub.installTime = uint40(block.timestamp);
90
96
  sub.nextRenewalTime = _calculateNextRenewalTime(expiresAt, sub.installTime);
91
97
 
92
- $.entityIds[msg.sender].add(entityId);
93
-
94
98
  emit SubscriptionConfigured(msg.sender, entityId, space, tokenId, sub.nextRenewalTime);
95
99
  }
96
100
 
@@ -102,6 +106,7 @@ contract SubscriptionModuleFacet is
102
106
 
103
107
  if (!$.entityIds[msg.sender].remove(entityId))
104
108
  SubscriptionModule__InvalidEntityId.selector.revertWith();
109
+
105
110
  delete $.subscriptions[msg.sender][entityId];
106
111
 
107
112
  emit SubscriptionDeactivated(msg.sender, entityId);
@@ -184,6 +189,8 @@ contract SubscriptionModuleFacet is
184
189
  if (!$.operators.contains(msg.sender))
185
190
  SubscriptionModule__InvalidCaller.selector.revertWith();
186
191
 
192
+ IMembership membershipFacet;
193
+
187
194
  for (uint256 i; i < paramsLen; ++i) {
188
195
  Subscription storage sub = $.subscriptions[params[i].account][params[i].entityId];
189
196
 
@@ -213,15 +220,7 @@ contract SubscriptionModuleFacet is
213
220
  continue;
214
221
  }
215
222
 
216
- MembershipFacet membershipFacet = MembershipFacet(sub.space);
217
- uint256 expiresAt = membershipFacet.expiresAt(sub.tokenId);
218
-
219
- // Sync next renewal time from on-chain expiration if user called renewMembership directly
220
- uint40 correctNextRenewalTime = _calculateNextRenewalTime(expiresAt, sub.installTime);
221
- if (sub.nextRenewalTime != correctNextRenewalTime) {
222
- sub.nextRenewalTime = correctNextRenewalTime;
223
- emit SubscriptionSynced(params[i].account, params[i].entityId, sub.nextRenewalTime);
224
- }
223
+ membershipFacet = IMembership(sub.space);
225
224
 
226
225
  uint256 actualRenewalPrice = membershipFacet.getMembershipRenewalPrice(sub.tokenId);
227
226
 
@@ -235,6 +234,14 @@ contract SubscriptionModuleFacet is
235
234
  continue;
236
235
  }
237
236
 
237
+ uint256 expiresAt = membershipFacet.expiresAt(sub.tokenId);
238
+ uint40 correctNextRenewalTime = _calculateNextRenewalTime(expiresAt, sub.installTime);
239
+
240
+ if (sub.nextRenewalTime != correctNextRenewalTime) {
241
+ sub.nextRenewalTime = correctNextRenewalTime;
242
+ emit SubscriptionSynced(params[i].account, params[i].entityId, sub.nextRenewalTime);
243
+ }
244
+
238
245
  _processRenewal(sub, params[i], membershipFacet, actualRenewalPrice);
239
246
  }
240
247
  }
@@ -254,17 +261,29 @@ contract SubscriptionModuleFacet is
254
261
 
255
262
  /// @inheritdoc ISubscriptionModule
256
263
  function activateSubscription(uint32 entityId) external nonReentrant {
257
- Subscription storage sub = SubscriptionModuleStorage.getLayout().subscriptions[msg.sender][
258
- entityId
259
- ];
264
+ SubscriptionModuleStorage.Layout storage $ = SubscriptionModuleStorage.getLayout();
260
265
 
261
- if (sub.active) SubscriptionModule__ActiveSubscription.selector.revertWith();
266
+ if (!_hasEntityId($, msg.sender, entityId))
267
+ SubscriptionModule__InvalidEntityId.selector.revertWith();
262
268
 
263
- sub.active = true;
269
+ Subscription storage sub = $.subscriptions[msg.sender][entityId];
270
+
271
+ if (sub.active) SubscriptionModule__ActiveSubscription.selector.revertWith();
264
272
 
265
273
  address owner = IERC721(sub.space).ownerOf(sub.tokenId);
266
274
  if (msg.sender != owner) SubscriptionModule__InvalidCaller.selector.revertWith();
267
275
 
276
+ IMembership membershipFacet = IMembership(sub.space);
277
+ uint256 expiresAt = membershipFacet.expiresAt(sub.tokenId);
278
+
279
+ // 6. Always sync renewal time to current membership state
280
+ uint40 correctNextRenewalTime = _calculateNextRenewalTime(expiresAt, sub.installTime);
281
+ if (sub.nextRenewalTime != correctNextRenewalTime) {
282
+ sub.nextRenewalTime = correctNextRenewalTime;
283
+ emit SubscriptionSynced(msg.sender, entityId, sub.nextRenewalTime);
284
+ }
285
+
286
+ sub.active = true;
268
287
  emit SubscriptionActivated(msg.sender, entityId);
269
288
  }
270
289
 
@@ -276,10 +295,10 @@ contract SubscriptionModuleFacet is
276
295
 
277
296
  if (!sub.active) SubscriptionModule__InactiveSubscription.selector.revertWith();
278
297
 
279
- _pauseSubscription(sub, msg.sender, entityId);
280
-
281
298
  address owner = IERC721(sub.space).ownerOf(sub.tokenId);
282
299
  if (msg.sender != owner) SubscriptionModule__InvalidCaller.selector.revertWith();
300
+
301
+ _pauseSubscription(sub, msg.sender, entityId);
283
302
  }
284
303
 
285
304
  /// @inheritdoc ISubscriptionModule
@@ -310,17 +329,25 @@ contract SubscriptionModuleFacet is
310
329
  /* Internal */
311
330
  /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
312
331
 
332
+ function _hasEntityId(
333
+ SubscriptionModuleStorage.Layout storage $,
334
+ address account,
335
+ uint32 entityId
336
+ ) internal view returns (bool) {
337
+ return $.entityIds[account].contains(entityId);
338
+ }
339
+
313
340
  /// @dev Processes a single subscription renewal
314
341
  /// @param sub The subscription to renew
315
342
  /// @param params The parameters for the renewal
316
343
  function _processRenewal(
317
344
  Subscription storage sub,
318
345
  RenewalParams calldata params,
319
- MembershipFacet membershipFacet,
346
+ IMembership membershipFacet,
320
347
  uint256 actualRenewalPrice
321
348
  ) internal {
322
349
  // Construct the renewal call to space contract
323
- bytes memory renewalCall = abi.encodeCall(MembershipFacet.renewMembership, (sub.tokenId));
350
+ bytes memory renewalCall = abi.encodeCall(IMembership.renewMembership, (sub.tokenId));
324
351
 
325
352
  // Create the data parameter for executeWithRuntimeValidation
326
353
  // This should be an execute() call to the space contract