@towns-protocol/contracts 0.0.455 → 1.0.2

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 (61) hide show
  1. package/package.json +4 -4
  2. package/scripts/deployments/diamonds/DeployL1Resolver.s.sol +155 -0
  3. package/scripts/deployments/diamonds/DeployL2Registrar.s.sol +171 -0
  4. package/scripts/deployments/diamonds/DeployL2Resolver.s.sol +196 -0
  5. package/scripts/deployments/diamonds/DeploySpaceFactory.s.sol +0 -12
  6. package/scripts/deployments/facets/DeployAddrResolverFacet.s.sol +36 -0
  7. package/scripts/deployments/facets/DeployArchitect.s.sol +1 -3
  8. package/scripts/deployments/facets/DeployContentHashResolverFacet.s.sol +34 -0
  9. package/scripts/deployments/facets/DeployCreateSpace.s.sol +2 -1
  10. package/scripts/deployments/facets/{DeploySpaceFactoryInit.s.sol → DeployExtendedResolverFacet.s.sol} +10 -10
  11. package/scripts/deployments/facets/DeployL1ResolverFacet.s.sol +61 -0
  12. package/scripts/deployments/facets/DeployL2RegistrarFacet.s.sol +56 -0
  13. package/scripts/deployments/facets/DeployL2RegistryFacet.s.sol +71 -0
  14. package/scripts/deployments/facets/DeployNodeRegistry.s.sol +4 -1
  15. package/scripts/deployments/facets/DeployTextResolverFacet.s.sol +34 -0
  16. package/scripts/deployments/utils/DeployDomainFeeHook.s.sol +78 -0
  17. package/scripts/interactions/InteractAlphaPost.s.sol +0 -7
  18. package/scripts/interactions/InteractBaseAlpha.s.sol +1 -14
  19. package/scripts/interactions/InteractDomainFee.s.sol +62 -0
  20. package/scripts/interactions/InteractSetStreamDistributionBalancingAdvantage.s.sol +42 -0
  21. package/scripts/interactions/InteractSetStreamDistributionRequiredOperators.s.sol +32 -0
  22. package/scripts/interactions/helpers/RiverConfigValues.sol +6 -0
  23. package/src/domains/facets/l1/IL1ResolverService.sol +20 -0
  24. package/src/domains/facets/l1/L1ResolverFacet.sol +100 -0
  25. package/src/domains/facets/l1/L1ResolverMod.sol +245 -0
  26. package/src/domains/facets/l2/AddrResolverFacet.sol +59 -0
  27. package/src/domains/facets/l2/ContentHashResolverFacet.sol +41 -0
  28. package/src/domains/facets/l2/ExtendedResolverFacet.sol +38 -0
  29. package/src/domains/facets/l2/IL2Registry.sol +79 -0
  30. package/src/domains/facets/l2/L2RegistryFacet.sol +203 -0
  31. package/src/domains/facets/l2/TextResolverFacet.sol +43 -0
  32. package/src/domains/facets/l2/modules/AddrResolverMod.sol +110 -0
  33. package/src/domains/facets/l2/modules/ContentHashResolverMod.sol +60 -0
  34. package/src/domains/facets/l2/modules/L2RegistryMod.sol +286 -0
  35. package/src/domains/facets/l2/modules/TextResolverMod.sol +62 -0
  36. package/src/domains/facets/l2/modules/VersionRecordMod.sol +42 -0
  37. package/src/domains/facets/registrar/IL2Registrar.sol +57 -0
  38. package/src/domains/facets/registrar/L2RegistrarFacet.sol +127 -0
  39. package/src/domains/facets/registrar/L2RegistrarMod.sol +224 -0
  40. package/src/domains/hooks/DomainFeeHook.sol +212 -0
  41. package/src/factory/facets/architect/Architect.sol +48 -29
  42. package/src/factory/facets/architect/IArchitect.sol +2 -21
  43. package/src/factory/facets/architect/ImplementationStorage.sol +12 -20
  44. package/src/factory/facets/create/CreateSpace.sol +5 -0
  45. package/src/factory/facets/create/CreateSpaceBase.sol +32 -8
  46. package/src/factory/facets/create/ICreateSpace.sol +5 -0
  47. package/src/factory/facets/fee/FeeManagerFacet.sol +10 -0
  48. package/src/factory/facets/fee/FeeTypesLib.sol +3 -0
  49. package/src/river/registry/facets/node/INodeRegistry.sol +25 -0
  50. package/src/river/registry/facets/node/NodeRegistry.sol +53 -2
  51. package/src/river/registry/libraries/RegistryStorage.sol +5 -0
  52. package/src/spaces/facets/ProtocolFeeLib.sol +56 -1
  53. package/src/spaces/facets/proxy/SpaceProxyInitializer.sol +6 -8
  54. package/src/spaces/facets/xchain/SpaceEntitlementGated.sol +9 -0
  55. package/LICENSE.txt +0 -21
  56. package/scripts/bytecode-diff/README.md +0 -182
  57. package/scripts/deployments/utils/DeploySpaceProxyInitializer.s.sol +0 -28
  58. package/scripts/readme.md +0 -289
  59. package/src/diamond/readme.md +0 -50
  60. package/src/factory/SpaceFactoryInit.sol +0 -17
  61. package/src/factory/facets/architect/ArchitectBase.sol +0 -95
@@ -99,4 +99,9 @@ contract CreateSpaceFacet is ICreateSpace, PausableBase, ReentrancyGuard, Create
99
99
  SpaceOptions memory spaceOptions = SpaceOptions({to: msg.sender});
100
100
  return _createSpaceWithPrepayFromLegacy(spaceInfo, spaceOptions);
101
101
  }
102
+
103
+ /// @inheritdoc ICreateSpace
104
+ function getProxyInitializer() external view returns (address) {
105
+ return _getProxyInitializer();
106
+ }
102
107
  }
@@ -24,6 +24,7 @@ import {Validator} from "../../../utils/libraries/Validator.sol";
24
24
  import {PricingModulesBase} from "../architect/pricing/PricingModulesBase.sol";
25
25
  import {ArchitectStorage} from "../architect/ArchitectStorage.sol";
26
26
  import {ImplementationStorage} from "../architect/ImplementationStorage.sol";
27
+ import {CustomRevert} from "../../../utils/libraries/CustomRevert.sol";
27
28
 
28
29
  // contracts
29
30
  import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
@@ -33,16 +34,21 @@ import {SpaceProxyInitializer} from "../../../spaces/facets/proxy/SpaceProxyInit
33
34
  abstract contract CreateSpaceBase is IArchitectBase {
34
35
  using StringSet for StringSet.Set;
35
36
  using EnumerableSet for EnumerableSet.AddressSet;
37
+ using CustomRevert for bytes4;
36
38
 
37
39
  address internal constant EVERYONE_ADDRESS = address(1);
38
40
  string internal constant MINTER_ROLE = "Minter";
39
41
  bytes1 internal constant CHANNEL_PREFIX = 0x20;
40
42
 
43
+ bytes32 private constant PROXY_INITIALIZER_INIT_CODE_HASH =
44
+ keccak256(type(SpaceProxyInitializer).creationCode);
45
+ bytes32 private constant PROXY_INITIALIZER_SALT = 0;
46
+
41
47
  function _createSpaceWithPrepay(
42
48
  CreateSpace calldata space,
43
49
  SpaceOptions memory spaceOptions
44
50
  ) internal returns (address spaceAddress) {
45
- if (msg.value > 0) revert Architect__UnexpectedETH();
51
+ if (msg.value > 0) Architect__UnexpectedETH.selector.revertWith();
46
52
  Validator.checkAddress(space.membership.settings.pricingModule);
47
53
  Validator.checkAddress(spaceOptions.to);
48
54
 
@@ -61,7 +67,7 @@ abstract contract CreateSpaceBase is IArchitectBase {
61
67
  CreateSpaceOld calldata space,
62
68
  SpaceOptions memory spaceOptions
63
69
  ) internal returns (address spaceAddress) {
64
- if (msg.value > 0) revert Architect__UnexpectedETH();
70
+ if (msg.value > 0) Architect__UnexpectedETH.selector.revertWith();
65
71
  Validator.checkAddress(space.membership.settings.pricingModule);
66
72
  Validator.checkAddress(spaceOptions.to);
67
73
 
@@ -91,7 +97,7 @@ abstract contract CreateSpaceBase is IArchitectBase {
91
97
  ChannelInfo calldata channel,
92
98
  SpaceOptions memory spaceOptions
93
99
  ) private returns (address spaceAddress) {
94
- ImplementationStorage.Layout storage ims = ImplementationStorage.layout();
100
+ ImplementationStorage.Layout storage ims = ImplementationStorage.getStorage();
95
101
 
96
102
  // get the token id of the next space
97
103
  uint256 spaceTokenId = ims.spaceOwnerToken.nextTokenId();
@@ -309,7 +315,7 @@ abstract contract CreateSpaceBase is IArchitectBase {
309
315
  ds.tokenIdBySpace[spaceAddress] = spaceTokenId;
310
316
  }
311
317
  // mint token to and transfer to Architect
312
- ImplementationStorage.Layout storage ims = ImplementationStorage.layout();
318
+ ImplementationStorage.Layout storage ims = ImplementationStorage.getStorage();
313
319
  ims.spaceOwnerToken.mintSpace(
314
320
  metadata.name,
315
321
  metadata.uri,
@@ -334,23 +340,41 @@ abstract contract CreateSpaceBase is IArchitectBase {
334
340
 
335
341
  function _verifyPricingModule(address pricingModule) internal view {
336
342
  if (pricingModule == address(0) || !PricingModulesBase.isPricingModule(pricingModule)) {
337
- revert Architect__InvalidPricingModule();
343
+ Architect__InvalidPricingModule.selector.revertWith();
338
344
  }
339
345
  }
340
346
 
347
+ /// @dev Returns the deterministic CREATE2 address of SpaceProxyInitializer
348
+ function _getProxyInitializer() internal view returns (address) {
349
+ return
350
+ Factory.calculateDeploymentAddress(
351
+ PROXY_INITIALIZER_INIT_CODE_HASH,
352
+ PROXY_INITIALIZER_SALT
353
+ );
354
+ }
355
+
356
+ /// @dev Deploys SpaceProxyInitializer via CREATE2 if not already deployed, returns its address
357
+ function _getOrDeployProxyInitializer() internal returns (address) {
358
+ address computed = _getProxyInitializer();
359
+
360
+ if (computed.code.length != 0) return computed;
361
+
362
+ return Factory.deploy(type(SpaceProxyInitializer).creationCode, PROXY_INITIALIZER_SALT);
363
+ }
364
+
341
365
  function _getSpaceDeploymentInfo(
342
366
  uint256 spaceTokenId,
343
367
  IMembershipBase.Membership calldata membershipSettings,
344
368
  SpaceOptions memory spaceOptions
345
- ) internal view returns (bytes memory initCode, bytes32 salt) {
369
+ ) internal returns (bytes memory initCode, bytes32 salt) {
346
370
  _verifyPricingModule(membershipSettings.pricingModule);
347
371
 
348
- address spaceOwnerNFT = address(ImplementationStorage.layout().spaceOwnerToken);
372
+ address spaceOwnerNFT = address(ImplementationStorage.getStorage().spaceOwnerToken);
349
373
 
350
374
  // calculate salt
351
375
  salt = keccak256(abi.encode(spaceTokenId, block.timestamp, block.number, spaceOwnerNFT));
352
376
 
353
- address proxyInitializer = address(ImplementationStorage.layout().proxyInitializer);
377
+ address proxyInitializer = _getOrDeployProxyInitializer();
354
378
 
355
379
  // calculate init code
356
380
  initCode = bytes.concat(
@@ -67,4 +67,9 @@ interface ICreateSpace is IArchitectBase, ICreateSpaceBase {
67
67
  function createSpaceWithPrepay(
68
68
  CreateSpaceOld calldata spaceInfo
69
69
  ) external payable returns (address);
70
+
71
+ /// @notice Returns the address of the SpaceProxyInitializer contract
72
+ /// @return The deterministic CREATE2 address of the SpaceProxyInitializer
73
+ /// @dev The initializer is deployed on first space creation if not already deployed
74
+ function getProxyInitializer() external view returns (address);
70
75
  }
@@ -135,5 +135,15 @@ contract FeeManagerFacet is
135
135
  0.0005 ether,
136
136
  true
137
137
  );
138
+
139
+ // domain registration fee
140
+ _setFeeConfig(
141
+ FeeTypesLib.DOMAIN_REGISTRATION,
142
+ protocolRecipient,
143
+ FeeCalculationMethod.FIXED,
144
+ 0,
145
+ 0,
146
+ true
147
+ );
138
148
  }
139
149
  }
@@ -26,6 +26,9 @@ library FeeTypesLib {
26
26
  /// @notice Fee for bot actions
27
27
  bytes32 internal constant BOT_ACTION = keccak256("FEE_TYPE.BOT_ACTION");
28
28
 
29
+ /// @notice Fee for domain registration
30
+ bytes32 internal constant DOMAIN_REGISTRATION = keccak256("FEE_TYPE.DOMAIN_REGISTRATION");
31
+
29
32
  /// @notice Generates fee type for membership based on currency
30
33
  /// @param currency The payment currency address
31
34
  /// @return The fee type identifier for the given currency
@@ -19,6 +19,7 @@ interface INodeRegistryBase {
19
19
  event NodeStatusUpdated(address indexed nodeAddress, NodeStatus status);
20
20
  event NodeUrlUpdated(address indexed nodeAddress, string url);
21
21
  event NodeRemoved(address indexed nodeAddress);
22
+ event NodeCometBftPubKeyUpdated(address indexed nodeAddress, bytes32 cometBftPubKey);
22
23
  }
23
24
 
24
25
  interface INodeRegistry is INodeRegistryBase {
@@ -66,4 +67,28 @@ interface INodeRegistry is INodeRegistryBase {
66
67
  * a block.
67
68
  */
68
69
  function getAllNodes() external view returns (Node[] memory);
70
+
71
+ /**
72
+ * @notice Backfill permanent indices for all existing nodes that don't have one assigned.
73
+ * @dev This should be called once after the contract upgrade. It assigns sequential indices
74
+ * starting from 1 to all nodes in their current array order. Subsequent registrations will
75
+ * continue from the last assigned index.
76
+ * Function can only be called once and reverts with ALREADY_EXISTS on subsequent calls.
77
+ */
78
+ function backfillPermanentIndices() external;
79
+
80
+ /**
81
+ * @notice Set or update the CometBFT public key for a node.
82
+ * @dev Can only be called by the node itself.
83
+ * @param nodeAddress The address of the node to update
84
+ * @param cometBftPubKey The 32-byte CometBFT public key
85
+ */
86
+ function setNodeCometBftPubKey(address nodeAddress, bytes32 cometBftPubKey) external;
87
+
88
+ /**
89
+ * @notice Get the last assigned permanent node index.
90
+ * @dev Returns 0 if backfill has not been called yet.
91
+ * @return The last assigned permanent node index
92
+ */
93
+ function getLastNodeIndex() external view returns (uint32);
69
94
  }
@@ -12,9 +12,10 @@ import {RiverRegistryErrors} from "src/river/registry/libraries/RegistryErrors.s
12
12
  import {CustomRevert} from "src/utils/libraries/CustomRevert.sol";
13
13
 
14
14
  // contracts
15
+ import {OwnableBase} from "@towns-protocol/diamond/src/facets/ownable/OwnableBase.sol";
15
16
  import {RegistryModifiers} from "src/river/registry/libraries/RegistryStorage.sol";
16
17
 
17
- contract NodeRegistry is INodeRegistry, RegistryModifiers {
18
+ contract NodeRegistry is INodeRegistry, RegistryModifiers, OwnableBase {
18
19
  using EnumerableSet for EnumerableSet.AddressSet;
19
20
  using CustomRevert for string;
20
21
 
@@ -32,16 +33,25 @@ contract NodeRegistry is INodeRegistry, RegistryModifiers {
32
33
  RiverRegistryErrors.ALREADY_EXISTS.revertWith();
33
34
  }
34
35
 
36
+ // Assign permanent index if backfill has been called (lastNodeIndex > 0)
37
+ uint32 permanentIndex = 0;
38
+ if (ds.lastNodeIndex > 0) {
39
+ permanentIndex = ++ds.lastNodeIndex;
40
+ }
41
+
35
42
  Node memory newNode = Node({
36
43
  nodeAddress: nodeAddress,
37
44
  url: url,
38
45
  status: status,
39
- operator: msg.sender
46
+ operator: msg.sender,
47
+ permanentIndex: permanentIndex,
48
+ cometBftPubKey: bytes32(0)
40
49
  });
41
50
 
42
51
  ds.nodes.add(nodeAddress); // TODO: remove this line
43
52
  ds.nodeByAddress[nodeAddress] = newNode;
44
53
 
54
+ // TODO: Consider adding permanentIndex to the event
45
55
  emit NodeAdded(nodeAddress, msg.sender, url, status);
46
56
  }
47
57
 
@@ -123,6 +133,47 @@ contract NodeRegistry is INodeRegistry, RegistryModifiers {
123
133
  return nodes;
124
134
  }
125
135
 
136
+ function backfillPermanentIndices() external onlyOwner {
137
+ // Can only be called once - after execution, lastNodeIndex > 0
138
+ if (ds.lastNodeIndex > 0) {
139
+ RiverRegistryErrors.ALREADY_EXISTS.revertWith();
140
+ }
141
+
142
+ address[] memory nodeAddresses = ds.nodes.values();
143
+ uint32 currentIndex;
144
+
145
+ for (uint256 i; i < nodeAddresses.length; ++i) {
146
+ Node storage node = ds.nodeByAddress[nodeAddresses[i]];
147
+
148
+ if (node.permanentIndex != 0) {
149
+ RiverRegistryErrors.BAD_ARG.revertWith();
150
+ }
151
+
152
+ node.permanentIndex = ++currentIndex;
153
+ }
154
+
155
+ ds.lastNodeIndex = currentIndex;
156
+ }
157
+
158
+ function setNodeCometBftPubKey(
159
+ address nodeAddress,
160
+ bytes32 cometBftPubKey
161
+ ) external onlyNode(nodeAddress) {
162
+ // Only the node itself can set its CometBFT public key
163
+ if (msg.sender != nodeAddress) {
164
+ RiverRegistryErrors.BAD_AUTH.revertWith();
165
+ }
166
+
167
+ Node storage node = ds.nodeByAddress[nodeAddress];
168
+ node.cometBftPubKey = cometBftPubKey;
169
+
170
+ emit NodeCometBftPubKeyUpdated(nodeAddress, cometBftPubKey);
171
+ }
172
+
173
+ function getLastNodeIndex() external view returns (uint32) {
174
+ return ds.lastNodeIndex;
175
+ }
176
+
126
177
  function _checkNodeStatusTransionAllowed(NodeStatus from, NodeStatus to) internal pure {
127
178
  if (
128
179
  from == NodeStatus.NotInitialized ||
@@ -49,6 +49,8 @@ struct Node {
49
49
  string url; // dynamically sized, points to a separate location
50
50
  address nodeAddress; // 20 bytes
51
51
  address operator; // 20 bytes
52
+ uint32 permanentIndex; // 4 bytes, permanent index assigned to this node. 0 = not initialized
53
+ bytes32 cometBftPubKey; // 32 bytes, CometBFT public key for consensus
52
54
  }
53
55
 
54
56
  /**
@@ -88,6 +90,9 @@ struct AppStorage {
88
90
  uint256 deprecatedSlot;
89
91
  // Map of node address to its stream ids
90
92
  mapping(address => EnumerableSet.Bytes32Set) streamIdsByNode;
93
+ // Last assigned permanent node index. Used to assign unique indices to new nodes.
94
+ // 0 means backfill has not been called yet; after backfill, this tracks the highest assigned index.
95
+ uint32 lastNodeIndex;
91
96
  }
92
97
 
93
98
  library RiverRegistryStorage {
@@ -23,6 +23,27 @@ library ProtocolFeeLib {
23
23
  address currency,
24
24
  uint256 amount,
25
25
  uint256 expectedFee
26
+ ) internal returns (uint256 protocolFee) {
27
+ return charge(spaceFactory, feeType, user, currency, amount, expectedFee, "");
28
+ }
29
+
30
+ /// @notice Charges protocol fee via FeeManager with ERC20 approval handling and extra data
31
+ /// @param spaceFactory The FeeManager address
32
+ /// @param feeType The type of fee being charged
33
+ /// @param user The user paying the fee
34
+ /// @param currency The payment currency (NATIVE_TOKEN or ERC20)
35
+ /// @param amount The base amount for fee calculation
36
+ /// @param expectedFee The pre-calculated expected fee
37
+ /// @param extraData Additional data for fee calculation (e.g., label length for domain registration)
38
+ /// @return protocolFee The actual fee charged
39
+ function charge(
40
+ address spaceFactory,
41
+ bytes32 feeType,
42
+ address user,
43
+ address currency,
44
+ uint256 amount,
45
+ uint256 expectedFee,
46
+ bytes memory extraData
26
47
  ) internal returns (uint256 protocolFee) {
27
48
  if (expectedFee == 0) return 0;
28
49
 
@@ -35,9 +56,43 @@ library ProtocolFeeLib {
35
56
  amount,
36
57
  currency,
37
58
  expectedFee,
38
- ""
59
+ extraData
39
60
  );
40
61
 
41
62
  if (!isNative) currency.safeApprove(spaceFactory, 0);
42
63
  }
64
+
65
+ /// @notice Charges protocol fee via FeeManager, always calling chargeFee even when expectedFee is 0
66
+ /// @dev Use this when the fee hook needs to track calls regardless of fee amount (e.g., domain registration count)
67
+ /// @param spaceFactory The FeeManager address
68
+ /// @param feeType The type of fee being charged
69
+ /// @param user The user paying the fee
70
+ /// @param currency The payment currency (NATIVE_TOKEN or ERC20)
71
+ /// @param amount The base amount for fee calculation
72
+ /// @param expectedFee The pre-calculated expected fee
73
+ /// @param extraData Additional data for fee calculation
74
+ /// @return protocolFee The actual fee charged
75
+ function chargeAlways(
76
+ address spaceFactory,
77
+ bytes32 feeType,
78
+ address user,
79
+ address currency,
80
+ uint256 amount,
81
+ uint256 expectedFee,
82
+ bytes memory extraData
83
+ ) internal returns (uint256 protocolFee) {
84
+ bool isNative = currency == CurrencyTransfer.NATIVE_TOKEN;
85
+ if (!isNative && expectedFee > 0) currency.safeApproveWithRetry(spaceFactory, expectedFee);
86
+
87
+ protocolFee = IFeeManager(spaceFactory).chargeFee{value: isNative ? expectedFee : 0}(
88
+ feeType,
89
+ user,
90
+ amount,
91
+ currency,
92
+ expectedFee,
93
+ extraData
94
+ );
95
+
96
+ if (!isNative && expectedFee > 0) currency.safeApprove(spaceFactory, 0);
97
+ }
43
98
  }
@@ -2,22 +2,20 @@
2
2
  pragma solidity ^0.8.23;
3
3
 
4
4
  // interfaces
5
-
6
5
  import {IERC173} from "@towns-protocol/diamond/src/facets/ownable/IERC173.sol";
7
- import {IERC5643} from "src/diamond/facets/token/ERC5643/IERC5643.sol";
8
- import {IMembership} from "src/spaces/facets/membership/IMembership.sol";
9
- import {ISpaceProxyInitializer} from "src/spaces/facets/proxy/ISpaceProxyInitializer.sol";
6
+ import {IERC5643} from "../../../diamond/facets/token/ERC5643/IERC5643.sol";
7
+ import {IMembership} from "../membership/IMembership.sol";
8
+ import {ISpaceProxyInitializer} from "./ISpaceProxyInitializer.sol";
10
9
 
11
10
  // libraries
12
11
 
13
12
  // contracts
14
-
15
13
  import {Initializable} from "@towns-protocol/diamond/src/facets/initializable/Initializable.sol";
16
14
  import {IntrospectionBase} from "@towns-protocol/diamond/src/facets/introspection/IntrospectionBase.sol";
17
15
  import {TokenOwnableBase} from "@towns-protocol/diamond/src/facets/ownable/token/TokenOwnableBase.sol";
18
- import {ERC721ABase} from "src/diamond/facets/token/ERC721A/ERC721ABase.sol";
19
- import {EntitlementGatedBase} from "src/spaces/facets/gated/EntitlementGatedBase.sol";
20
- import {MembershipBase} from "src/spaces/facets/membership/MembershipBase.sol";
16
+ import {ERC721ABase} from "../../../diamond/facets/token/ERC721A/ERC721ABase.sol";
17
+ import {EntitlementGatedBase} from "../gated/EntitlementGatedBase.sol";
18
+ import {MembershipBase} from "../membership/MembershipBase.sol";
21
19
 
22
20
  contract SpaceProxyInitializer is
23
21
  ISpaceProxyInitializer,
@@ -29,6 +29,15 @@ contract SpaceEntitlementGated is MembershipJoin, EntitlementGated {
29
29
  );
30
30
 
31
31
  if (result == NodeVoteStatus.PASSED) {
32
+ // Re-validate supply cap before minting to prevent over-minting from
33
+ // concurrent crosschain join requests that were all accepted when under
34
+ // the cap but would exceed it if all finalized
35
+ uint256 membershipSupplyLimit = _getMembershipSupplyLimit();
36
+ if (membershipSupplyLimit != 0 && _totalSupply() >= membershipSupplyLimit) {
37
+ _rejectMembership(transactionId, receiver);
38
+ return;
39
+ }
40
+
32
41
  PricingDetails memory joinDetails = _getPricingDetails();
33
42
 
34
43
  if (!joinDetails.shouldCharge) {
package/LICENSE.txt DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2024 River Association
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1,182 +0,0 @@
1
- # Bytecode-Diff Tool
2
-
3
- Bytecode-Diff is a tool to retrieve and display contract bytecode diff for Base deployed contracts and Solidity source compiled bytecode compiled with forge. It provides functionality to run source code diffs or remote bytecode diffs and create reports that detail changes between two compiled bytecode versions of contracts.
4
-
5
- ## Prerequisites
6
-
7
- - Go 1.22 or later
8
- - Base RPC Provider URL
9
- - River Chain RPC Provider URL
10
- - Basescan API Key
11
-
12
- ## Usage
13
-
14
- ### Local Source Code Diff
15
-
16
- Compile contracts with forge and run source code diff comparing nearest commit report with checked out commit.
17
-
18
- The basic command structure is:
19
-
20
- ```bash
21
- # ensure contracts are compiled with forge
22
- cd ../../packages/contracts
23
- make build
24
- # disable go.work file since bytecode-diff is not a module in parent go workspace
25
- go mod download
26
- # run source diff from checked out commitSha compared nearest commit with a source diff report in SOURCE_DIFF_DIR
27
- GOWORK=off go run main.go -v -s
28
-
29
- # write report with contract addresses, their keccak256 compilaed bytecode hash under two keys, existing and updated.
30
- ➜ bytecode-diff ✗ yq eval '.existing' source-diffs/00adc44f_08292024_5.yaml
31
- Architect: 0xd291e489716f2c9cfc2e2c6047ce777159969943c85d09c51aaf7bbad10f7c13
32
- ArchitectBase: 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
33
- ArchitectStorage: 0x86159d997458669c4df8af2da4b5ce9ca742099a3f854c5eb3e718e16a74e4da
34
- Banning: 0xde1354882fd30088cce4b00ff720a6dbc8c9f25653477c6ee99e20e17edb6068
35
- BanningBase: 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
36
- BanningStorage: 0x86159d997458669c4df8af2da4b5ce9ca742099a3f854c5eb3e718e16a74e4da
37
- ChannelBase: 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
38
- ...
39
- ```
40
-
41
- ### Run pairwise remote bytecode diff on facets deployed to two networks
42
-
43
- Runs bytecode diff from deployed facets for diamonds in alpha, beta, and omega environments as per source coordinates of diamonds for each environment.
44
-
45
- ```bash
46
- # compare omega against beta facets and facet selectors
47
- GOWORK=off go run ./main.go beta omega -v
48
-
49
- # output facet implementation changes by facet or selectors that are missing from omega
50
- ➜ bytecode-diff git:(jt/net-62-contract-differ) ✗ yq eval deployed-diffs/facet_diff_090324_18.yaml
51
- diamonds:
52
- - name: spaceOwner
53
- source: beta
54
- target: omega
55
- facets:
56
- - sourceContractName: ""
57
- sourceFacetAddress: 0xfa98a1648761e494fc7d6efe5a06e357a76bd6fb
58
- selectorsDiff:
59
- - "0x3953801b"
60
- - "0x91de4a83"
61
- sourceBytecodeHash: 0xf86d9dbe53c89e14fa69cde925cca02b6efad519fe172f7b04d9515d7700a59b
62
- sourceVerified: false
63
- targetVerified: false
64
- - sourceContractName: SpaceOwner
65
- sourceFacetAddress: 0x30c912d8ceb9793e4cd240862acfd0e6c4436c52
66
- targetContractAddresses:
67
- - 0x63bC35259Ac32DF43Fba3b890F0F74951451976A
68
- - 0xe7EB1313f0E7076616534225e16E971B72b50C42
69
- selectorsDiff: []
70
- sourceBytecodeHash: 0x461b53ab37fd24283ecd63eb0d4e71bd554a266036c73caf6d2ac39c435e7732
71
- targetBytecodeHashes:
72
- - 0x86d20161a13671a6138b80551e94dd8c1638bc5151807ff2194aa1e50cdb3cac
73
- - 0xff0a94e93a4f4f6ee0ecd0d0e469e55ca40f1ab6c10e6af9da5b2b597f32b178
74
- sourceVerified: true
75
- targetVerified: true
76
- - sourceContractName: ""
77
- sourceFacetAddress: 0xdba2ce6125cc6b7f93c63d181a0780d5b421940b
78
- selectorsDiff:
79
- - "0x0d653654"
80
- - "0x466a18de"
81
- sourceBytecodeHash: 0x583c2852056f90c96ed1cab935489f644b8ef564e0a7f11564925d07cf3bc593
82
- sourceVerified: false
83
- targetVerified: false
84
-
85
- ```
86
-
87
- ### Run keccak256 hash generation on deployed contracts
88
-
89
- ```bash
90
- GOWORK=off go run main.go add-hashes beta deployed-diffs/facet_diff_090624_1.yaml
91
-
92
- # output to new yaml file suffixed with _hashed.yaml including bytecodeHash for each contract in deployments section
93
- ➜ bytecode-diff git:(jt/net-62-upgrade-script-2) ✗ yq e '.deployments' deployed-diffs/facet_diff_090624_1_hashed.yaml
94
- Architect:
95
- address: 0xa18a3df4f63cdcae943d9c76730adf2812388de4
96
- baseScanLink: https://sepolia.basescan.org/tx/0x4280ef1300fe001e7d85e7495eba13fc99be53ee7a7060e753d466f8bebf1622
97
- bytecodeHash: 0x20d0a86e9ea31a39663285aacfe88705983520a4482a7bac5ada891c9adfe090
98
- deploymentDate: 2024-09-06 19:04
99
- transactionHash: 0x4280ef1300fe001e7d85e7495eba13fc99be53ee7a7060e753d466f8bebf1622
100
- Banning:
101
- address: 0x4d88d1fbba6ce6bcdb4381549ee0b7c0d2b56919
102
- baseScanLink: https://sepolia.basescan.org/tx/0x4ccbaf9750bcd0971975e73a24b05f1c51d4703cf72a406356c79eb54de9c33c
103
- bytecodeHash: 0xa2ce3e77ba060ff1d59ed384e1c6c5788f308ad8bbbef612eb3e5de4e1d79de8
104
- deploymentDate: 2024-09-06 19:05
105
- transactionHash: 0x4ccbaf9750bcd0971975e73a24b05f1c51d4703cf72a406356c79eb54de9c33c
106
- ...
107
- ```
108
-
109
- ### Flags
110
-
111
- ```bash
112
- ➜ bytecode-diff ✗ GOWORK=off go run ./main.go --help
113
- A tool to retrieve and display contract bytecode diff for Base
114
-
115
- Usage:
116
- bytecode-diff [source_environment] [target_environment] [flags]
117
-
118
- Flags:
119
- -b, --base-rpc string Base RPC provider URL
120
- --base-sepolia-rpc string Base Sepolia RPC provider URL
121
- --compiled-facets string Path to compiled facets
122
- --deployments string Path to deployments directory (default "../../packages/contracts/deployments")
123
- --facets string Path to facet source files
124
- -h, --help help for bytecode-diff
125
- --log-level string Set the logging level (debug, info, warn, error) (default "info")
126
- --report-out-dir string Path to report output directory (default "deployed-diffs")
127
- --source-diff-log string Path to diff log file (default "source-diffs")
128
- -s, --source-diff-only Run source code diff
129
- -v, --verbose Enable verbose output
130
- ```
131
-
132
- ### Environment Variables
133
-
134
- You can also set the following environment variables instead of using flags:
135
-
136
- - `BASE_RPC_URL`: Base RPC provider URL
137
- - `BASE_SEPOLIA_RPC_URL`: Base Sepolia RPC provider URL
138
- - `FACET_SOURCE_PATH`: Path to facet source files
139
- - `ETHERSCAN_API_KEY`: Your API key for Etherscan.
140
- - `COMPILED_FACETS_PATH`: (Optional) Path to compiled facets
141
- - `DEPLOYMENTS_PATH`: (Optional) Path to deployed contracts
142
- - `REPORT_OUT_DIR`: (Optional) Path to report output directory
143
- - `SOURCE_DIFF_DIR`: (Optional) Path to source diff reports
144
-
145
- ## Examples
146
-
147
- 1. Run source code diff with all parameters specified via flags:
148
-
149
- ```
150
- ./bytecode-diff --source-diff-only \
151
- --source-diff-dir /path/to/source-diff-reports \
152
- --facets /path/to/facet/sources \
153
- --compiled-facets /path/to/compiled/facets \
154
- --report-out-dir /path/to/report/output \
155
- --verbose
156
- ```
157
-
158
- 2. Run source code diff using environment variables:
159
-
160
- ```bash
161
- export SOURCE_DIFF_DIR=/path/to/source_diff.log
162
- export FACET_SOURCE_PATH=/path/to/facet/sources
163
- export COMPILED_FACETS_PATH=/path/to/compiled/facets
164
- export REPORT_OUT_DIR=/path/to/report/output
165
-
166
- ./bytecode-diff -s --verbose
167
- ```
168
-
169
- 3. Run source code diff with r/w to remote s3 bucket:
170
-
171
- ```bash
172
- export AWS_ACCESS_KEY_ID=<your-access-key-id>
173
- export AWS_SECRET_ACCESS_KEY=<your-secret-access-key>
174
- export SOURCE_DIFF_DIR=s3://bucket/path
175
- ./bytecode-diff -s --verbose
176
- ```
177
-
178
- ## Notes
179
-
180
- - If a `.env` file is present in the same directory as the script, it will be loaded automatically.
181
- - When running source code diff, all required paths must be provided either via flags or environment variables.
182
- - Use the `--verbose` flag for more detailed output during execution.
@@ -1,28 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.23;
3
-
4
- // interfaces
5
-
6
- // libraries
7
-
8
- // contracts
9
- import {SpaceProxyInitializer} from "src/spaces/facets/proxy/SpaceProxyInitializer.sol";
10
- import {Deployer} from "scripts/common/Deployer.s.sol";
11
-
12
- contract DeploySpaceProxyInitializer is Deployer {
13
- function versionName() public pure override returns (string memory) {
14
- return "utils/spaceProxyInitializer";
15
- }
16
-
17
- function __deploy(address deployer) internal override returns (address) {
18
- bytes32 salt = bytes32(uint256(1));
19
- bytes32 initCodeHash = hashInitCode(type(SpaceProxyInitializer).creationCode);
20
- address soonToBe = vm.computeCreate2Address(salt, initCodeHash);
21
-
22
- vm.broadcast(deployer);
23
- SpaceProxyInitializer initializer = new SpaceProxyInitializer{salt: salt}();
24
-
25
- require(address(initializer) == soonToBe, "address mismatch");
26
- return address(initializer);
27
- }
28
- }