@bananapus/721-hook-v6 0.0.42 → 0.0.45

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 (86) hide show
  1. package/foundry.lock +1 -7
  2. package/foundry.toml +1 -1
  3. package/package.json +20 -9
  4. package/script/Deploy.s.sol +2 -2
  5. package/src/JB721Checkpoints.sol +61 -19
  6. package/src/JB721CheckpointsDeployer.sol +10 -5
  7. package/src/JB721TiersHook.sol +66 -53
  8. package/src/JB721TiersHookDeployer.sol +8 -5
  9. package/src/JB721TiersHookProjectDeployer.sol +87 -46
  10. package/src/JB721TiersHookStore.sol +137 -107
  11. package/src/abstract/JB721Hook.sol +8 -6
  12. package/src/interfaces/IJB721Checkpoints.sol +21 -14
  13. package/src/interfaces/IJB721CheckpointsDeployer.sol +7 -3
  14. package/src/interfaces/IJB721TiersHook.sol +3 -3
  15. package/src/interfaces/IJB721TiersHookProjectDeployer.sol +4 -2
  16. package/src/interfaces/IJB721TiersHookStore.sol +11 -11
  17. package/src/libraries/JB721TiersHookLib.sol +1 -1
  18. package/src/structs/JB721TiersHookFlags.sol +1 -1
  19. package/src/structs/JBPayDataHookRulesetMetadata.sol +1 -1
  20. package/test/utils/AccessJBLib.sol +49 -0
  21. package/test/utils/ForTest_JB721TiersHook.sol +246 -0
  22. package/test/utils/TestBaseWorkflow.sol +213 -0
  23. package/test/utils/UnitTestSetup.sol +805 -0
  24. package/.gas-snapshot +0 -152
  25. package/ADMINISTRATION.md +0 -87
  26. package/ARCHITECTURE.md +0 -98
  27. package/AUDIT_INSTRUCTIONS.md +0 -77
  28. package/RISKS.md +0 -118
  29. package/SKILLS.md +0 -43
  30. package/STYLE_GUIDE.md +0 -610
  31. package/USER_JOURNEYS.md +0 -121
  32. package/assets/findings/nana-721-hook-v6-pashov-ai-audit-report-20260330-091257.md +0 -83
  33. package/slither-ci.config.json +0 -10
  34. package/test/721HookAttacks.t.sol +0 -408
  35. package/test/E2E/Pay_Mint_Redeem_E2E.t.sol +0 -985
  36. package/test/Fork.t.sol +0 -2346
  37. package/test/TestAuditGaps.sol +0 -1075
  38. package/test/TestCheckpoints.t.sol +0 -341
  39. package/test/TestSafeTransferReentrancy.t.sol +0 -305
  40. package/test/TestVotingUnitsLifecycle.t.sol +0 -313
  41. package/test/audit/AuditRegressions.t.sol +0 -83
  42. package/test/audit/CodexNemesisReserveSellout.t.sol +0 -66
  43. package/test/audit/CrossCurrencySplitNoPrices.t.sol +0 -123
  44. package/test/audit/FreshAudit.t.sol +0 -197
  45. package/test/audit/FutureTierPoC.t.sol +0 -39
  46. package/test/audit/FutureTierRemoval.t.sol +0 -47
  47. package/test/audit/Pass12L18.t.sol +0 -80
  48. package/test/audit/PayCreditsBypassTierSplits.t.sol +0 -200
  49. package/test/audit/ProjectDeployerAuth.t.sol +0 -266
  50. package/test/audit/RepoFindings.t.sol +0 -195
  51. package/test/audit/ReserveActivation.t.sol +0 -87
  52. package/test/audit/ReserveSlotProtection.t.sol +0 -273
  53. package/test/audit/RetroactiveReserveBeneficiaryDilution.t.sol +0 -149
  54. package/test/audit/SameCurrencyDecimalMismatch.t.sol +0 -249
  55. package/test/audit/SplitCreditsMismatch.t.sol +0 -219
  56. package/test/audit/SplitFailureRedistribution.t.sol +0 -143
  57. package/test/audit/USDTVoidReturnCompat.t.sol +0 -301
  58. package/test/fork/ERC20CashOutFork.t.sol +0 -633
  59. package/test/fork/ERC20TierSplitFork.t.sol +0 -596
  60. package/test/fork/IssueTokensForSplitsFork.t.sol +0 -516
  61. package/test/invariants/TierLifecycleInvariant.t.sol +0 -188
  62. package/test/invariants/TieredHookStoreInvariant.t.sol +0 -86
  63. package/test/invariants/handlers/TierLifecycleHandler.sol +0 -300
  64. package/test/invariants/handlers/TierStoreHandler.sol +0 -165
  65. package/test/regression/BrokenTerminalDoesNotDos.t.sol +0 -277
  66. package/test/regression/CacheTierLookup.t.sol +0 -190
  67. package/test/regression/ProjectDeployerRulesets.t.sol +0 -358
  68. package/test/regression/ReserveBeneficiaryOverwrite.t.sol +0 -155
  69. package/test/regression/SplitDistributionBugs.t.sol +0 -751
  70. package/test/regression/SplitNoBeneficiary.t.sol +0 -140
  71. package/test/unit/AuditFixes_Unit.t.sol +0 -624
  72. package/test/unit/JB721CheckpointsDeployer_AccessControl.t.sol +0 -116
  73. package/test/unit/JB721TiersRulesetMetadataResolver.t.sol +0 -144
  74. package/test/unit/JBBitmap.t.sol +0 -170
  75. package/test/unit/JBIpfsDecoder.t.sol +0 -136
  76. package/test/unit/TierSupplyReserveCheck.t.sol +0 -221
  77. package/test/unit/adjustTier_Unit.t.sol +0 -1942
  78. package/test/unit/deployer_Unit.t.sol +0 -114
  79. package/test/unit/getters_constructor_Unit.t.sol +0 -593
  80. package/test/unit/mintFor_mintReservesFor_Unit.t.sol +0 -452
  81. package/test/unit/pay_CrossCurrency_Unit.t.sol +0 -530
  82. package/test/unit/pay_Unit.t.sol +0 -1661
  83. package/test/unit/redeem_Unit.t.sol +0 -473
  84. package/test/unit/relayBeneficiary_Unit.t.sol +0 -182
  85. package/test/unit/splitHookDistribution_Unit.t.sol +0 -604
  86. package/test/unit/tierSplitRouting_Unit.t.sol +0 -757
@@ -11,6 +11,7 @@ import {JBRulesetMetadata} from "@bananapus/core-v6/src/structs/JBRulesetMetadat
11
11
  import {JBOwnable} from "@bananapus/ownable-v6/src/JBOwnable.sol";
12
12
  import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
13
13
  import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
14
+ import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
14
15
  import {Context} from "@openzeppelin/contracts/utils/Context.sol";
15
16
 
16
17
  import {IJB721TiersHook} from "./interfaces/IJB721TiersHook.sol";
@@ -23,9 +24,15 @@ import {JBPayDataHookRulesetConfig} from "./structs/JBPayDataHookRulesetConfig.s
23
24
  import {JBQueueRulesetsConfig} from "./structs/JBQueueRulesetsConfig.sol";
24
25
 
25
26
  /// @title JB721TiersHookProjectDeployer
26
- /// @notice Deploys a project and a 721 tiers hook for it. Can be used to queue rulesets for the project if given
27
- /// `JBPermissionIds.QUEUE_RULESETS` or `JBPermissionIds.LAUNCH_RULESETS`.
28
- contract JB721TiersHookProjectDeployer is ERC2771Context, JBPermissioned, IJB721TiersHookProjectDeployer {
27
+ /// @notice All-in-one deployer that creates a Juicebox project, deploys a 721 tiers hook, and configures its
28
+ /// rulesets in a single transaction. Can also attach a hook to an existing project by launching or queuing rulesets
29
+ /// with `LAUNCH_RULESETS` or `QUEUE_RULESETS` permission.
30
+ contract JB721TiersHookProjectDeployer is
31
+ ERC2771Context,
32
+ JBPermissioned,
33
+ IERC721Receiver,
34
+ IJB721TiersHookProjectDeployer
35
+ {
29
36
  //*********************************************************************//
30
37
  // --------------- public immutable stored properties ---------------- //
31
38
  //*********************************************************************//
@@ -33,7 +40,7 @@ contract JB721TiersHookProjectDeployer is ERC2771Context, JBPermissioned, IJB721
33
40
  /// @notice The directory of terminals and controllers for projects.
34
41
  IJBDirectory public immutable override DIRECTORY;
35
42
 
36
- /// @notice The 721 tiers hook deployer.
43
+ /// @notice The deployer contract used to create new 721 tiers hook instances via clone.
37
44
  IJB721TiersHookDeployer public immutable override HOOK_DEPLOYER;
38
45
 
39
46
  //*********************************************************************//
@@ -64,9 +71,8 @@ contract JB721TiersHookProjectDeployer is ERC2771Context, JBPermissioned, IJB721
64
71
  /// @notice Launches a new project with a 721 tiers hook attached.
65
72
  /// @param owner The address to set as the owner of the project. The ERC-721 which confers this project's ownership
66
73
  /// will be sent to this address.
67
- /// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook which is being
68
- /// deployed.
69
- /// @param launchProjectConfig Configuration which dictates the behavior of the project which is being launched.
74
+ /// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook to deploy.
75
+ /// @param launchProjectConfig Configuration which dictates the behavior of the project to launch.
70
76
  /// @param controller The controller that the project's rulesets will be queued with.
71
77
  /// @param salt A salt to use for the deterministic deployment.
72
78
  /// @return projectId The ID of the newly launched project.
@@ -82,8 +88,9 @@ contract JB721TiersHookProjectDeployer is ERC2771Context, JBPermissioned, IJB721
82
88
  override
83
89
  returns (uint256 projectId, IJB721TiersHook hook)
84
90
  {
85
- // Get the project's ID, optimistically knowing it will be one greater than the current number of projects.
86
- projectId = DIRECTORY.PROJECTS().count() + 1;
91
+ // Reserve the project ID up front so permissionless project creations cannot invalidate hook deployment.
92
+ IJBProjects PROJECTS = DIRECTORY.PROJECTS();
93
+ projectId = PROJECTS.createFor(address(this));
87
94
 
88
95
  // Deploy the hook.
89
96
  hook = HOOK_DEPLOYER.deployHookFor({
@@ -92,22 +99,25 @@ contract JB721TiersHookProjectDeployer is ERC2771Context, JBPermissioned, IJB721
92
99
  salt: salt == bytes32(0) ? bytes32(0) : keccak256(abi.encode(_msgSender(), salt))
93
100
  });
94
101
 
95
- // Launch the project.
102
+ // Launch the rulesets for the reserved project.
96
103
  _launchProjectFor({
97
- owner: owner, launchProjectConfig: launchProjectConfig, dataHook: hook, controller: controller
104
+ projectId: projectId, launchProjectConfig: launchProjectConfig, dataHook: hook, controller: controller
98
105
  });
99
106
 
100
107
  // Transfer the hook's ownership to the project.
101
108
  JBOwnable(address(hook)).transferOwnershipToProject(projectId);
109
+
110
+ // Transfer the project NFT to its intended owner.
111
+ PROJECTS.safeTransferFrom({from: address(this), to: owner, tokenId: projectId});
102
112
  }
103
113
 
104
114
  /// @notice Launches rulesets for a project with an attached 721 tiers hook.
105
115
  /// @dev Only a project's owner or an operator with the `LAUNCH_RULESETS & SET_TERMINALS` permission can launch its
106
116
  /// rulesets.
107
- /// @param projectId The ID of the project that rulesets are being launched for.
108
- /// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook which is being
109
- /// deployed.
117
+ /// @param projectId The ID of the project to launch rulesets for.
118
+ /// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook to deploy.
110
119
  /// @param launchRulesetsConfig Configuration which dictates the project's new rulesets.
120
+ /// @param projectUri Metadata URI to associate with the project. Pass an empty string to leave it unchanged.
111
121
  /// @param controller The controller that the project's rulesets will be queued with.
112
122
  /// @param salt A salt to use for the deterministic deployment.
113
123
  /// @return rulesetId The ID of the successfully created ruleset.
@@ -116,6 +126,7 @@ contract JB721TiersHookProjectDeployer is ERC2771Context, JBPermissioned, IJB721
116
126
  uint256 projectId,
117
127
  JBDeploy721TiersHookConfig calldata deployTiersHookConfig,
118
128
  JBLaunchRulesetsConfig calldata launchRulesetsConfig,
129
+ string calldata projectUri,
119
130
  IJBController controller,
120
131
  bytes32 salt
121
132
  )
@@ -125,16 +136,23 @@ contract JB721TiersHookProjectDeployer is ERC2771Context, JBPermissioned, IJB721
125
136
  {
126
137
  // Get the project's projects contract.
127
138
  IJBProjects PROJECTS = DIRECTORY.PROJECTS();
139
+ address projectOwner = PROJECTS.ownerOf(projectId);
128
140
 
129
141
  // Enforce permissions.
130
142
  _requirePermissionFrom({
131
- account: PROJECTS.ownerOf(projectId), projectId: projectId, permissionId: JBPermissionIds.LAUNCH_RULESETS
143
+ account: projectOwner, projectId: projectId, permissionId: JBPermissionIds.LAUNCH_RULESETS
132
144
  });
133
145
 
134
146
  _requirePermissionFrom({
135
- account: PROJECTS.ownerOf(projectId), projectId: projectId, permissionId: JBPermissionIds.SET_TERMINALS
147
+ account: projectOwner, projectId: projectId, permissionId: JBPermissionIds.SET_TERMINALS
136
148
  });
137
149
 
150
+ if (bytes(projectUri).length != 0) {
151
+ _requirePermissionFrom({
152
+ account: projectOwner, projectId: projectId, permissionId: JBPermissionIds.SET_PROJECT_URI
153
+ });
154
+ }
155
+
138
156
  // Deploy the hook.
139
157
  hook = HOOK_DEPLOYER.deployHookFor({
140
158
  projectId: projectId,
@@ -147,15 +165,18 @@ contract JB721TiersHookProjectDeployer is ERC2771Context, JBPermissioned, IJB721
147
165
 
148
166
  // Launch the rulesets.
149
167
  rulesetId = _launchRulesetsFor({
150
- projectId: projectId, launchRulesetsConfig: launchRulesetsConfig, dataHook: hook, controller: controller
168
+ projectId: projectId,
169
+ launchRulesetsConfig: launchRulesetsConfig,
170
+ projectUri: projectUri,
171
+ dataHook: hook,
172
+ controller: controller
151
173
  });
152
174
  }
153
175
 
154
176
  /// @notice Queues rulesets for a project with an attached 721 tiers hook.
155
177
  /// @dev Only a project's owner or an operator with the `QUEUE_RULESETS` permission can queue its rulesets.
156
- /// @param projectId The ID of the project that rulesets are being queued for.
157
- /// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook which is being
158
- /// deployed.
178
+ /// @param projectId The ID of the project to queue rulesets for.
179
+ /// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook to deploy.
159
180
  /// @param queueRulesetsConfig Configuration which dictates the project's newly queued rulesets.
160
181
  /// @param controller The controller that the project's rulesets will be queued with.
161
182
  /// @param salt A salt to use for the deterministic deployment.
@@ -196,37 +217,28 @@ contract JB721TiersHookProjectDeployer is ERC2771Context, JBPermissioned, IJB721
196
217
  }
197
218
 
198
219
  //*********************************************************************//
199
- // -------------------------- internal views ------------------------- //
220
+ // ----------------------- external views ---------------------------- //
200
221
  //*********************************************************************//
201
222
 
202
- /// @dev ERC-2771 specifies the context as being a single address (20 bytes).
203
- function _contextSuffixLength() internal view virtual override(ERC2771Context, Context) returns (uint256) {
204
- return ERC2771Context._contextSuffixLength();
205
- }
206
-
207
- /// @notice The calldata. Preferred to use over `msg.data`.
208
- /// @return calldata The `msg.data` of this call.
209
- function _msgData() internal view override(ERC2771Context, Context) returns (bytes calldata) {
210
- return ERC2771Context._msgData();
211
- }
212
-
213
- /// @notice The message's sender. Preferred to use over `msg.sender`.
214
- /// @return sender The address which sent this call.
215
- function _msgSender() internal view override(ERC2771Context, Context) returns (address sender) {
216
- return ERC2771Context._msgSender();
223
+ /// @notice Accepts project NFT reservations minted by `JBProjects.createFor`.
224
+ function onERC721Received(address, address from, uint256, bytes calldata) external view returns (bytes4) {
225
+ if (msg.sender != address(DIRECTORY.PROJECTS()) || from != address(0)) revert();
226
+ return IERC721Receiver.onERC721Received.selector;
217
227
  }
218
228
 
219
229
  //*********************************************************************//
220
230
  // ----------------------- internal helpers -------------------------- //
221
231
  //*********************************************************************//
222
232
 
223
- /// @notice Launches a project.
224
- /// @param owner The address that will own the project.
225
- /// @param launchProjectConfig Configuration which dictates the behavior of the project which is being launched.
233
+ /// @notice Configure and launch rulesets for a newly created project. Converts `JBPayDataHookRulesetConfig` entries
234
+ /// into standard `JBRulesetConfig` entries with `useDataHookForPay` forced to `true` and the deployed hook set as
235
+ /// the data hook.
236
+ /// @param projectId The ID of the reserved project.
237
+ /// @param launchProjectConfig Configuration which dictates the behavior of the project to launch.
226
238
  /// @param dataHook The data hook to use for the project.
227
239
  /// @param controller The controller that the project's rulesets will be queued with.
228
240
  function _launchProjectFor(
229
- address owner,
241
+ uint256 projectId,
230
242
  JBLaunchProjectConfig memory launchProjectConfig,
231
243
  IJB721TiersHook dataHook,
232
244
  IJBController controller
@@ -279,10 +291,10 @@ contract JB721TiersHookProjectDeployer is ERC2771Context, JBPermissioned, IJB721
279
291
  }
280
292
  }
281
293
 
282
- // Launch the project.
294
+ // Launch the rulesets for the reserved project.
283
295
  // slither-disable-next-line unused-return
284
- controller.launchProjectFor({
285
- owner: owner,
296
+ controller.launchRulesetsFor({
297
+ projectId: projectId,
286
298
  projectUri: launchProjectConfig.projectUri,
287
299
  rulesetConfigurations: rulesetConfigurations,
288
300
  terminalConfigurations: launchProjectConfig.terminalConfigurations,
@@ -290,15 +302,18 @@ contract JB721TiersHookProjectDeployer is ERC2771Context, JBPermissioned, IJB721
290
302
  });
291
303
  }
292
304
 
293
- /// @notice Launches rulesets for a project.
305
+ /// @notice Launch rulesets for an existing project. Same conversion logic as `_launchProjectFor` — each
306
+ /// `JBPayDataHookRulesetConfig` is transformed into a `JBRulesetConfig` with the hook wired as the data hook.
294
307
  /// @param projectId The ID of the project to launch rulesets for.
295
308
  /// @param launchRulesetsConfig Configuration which dictates the behavior of the project's rulesets.
309
+ /// @param projectUri Metadata URI to associate with the project. Pass an empty string to leave it unchanged.
296
310
  /// @param dataHook The data hook to use for the project.
297
311
  /// @param controller The controller that the project's rulesets will be queued with.
298
312
  /// @return rulesetId The ID of the successfully created ruleset.
299
313
  function _launchRulesetsFor(
300
314
  uint256 projectId,
301
315
  JBLaunchRulesetsConfig memory launchRulesetsConfig,
316
+ string memory projectUri,
302
317
  IJB721TiersHook dataHook,
303
318
  IJBController controller
304
319
  )
@@ -352,15 +367,20 @@ contract JB721TiersHookProjectDeployer is ERC2771Context, JBPermissioned, IJB721
352
367
  }
353
368
 
354
369
  // Launch the rulesets.
355
- return controller.launchRulesetsFor({
370
+ uint256 rulesetId = controller.launchRulesetsFor({
356
371
  projectId: projectId,
372
+ projectUri: projectUri,
357
373
  rulesetConfigurations: rulesetConfigurations,
358
374
  terminalConfigurations: launchRulesetsConfig.terminalConfigurations,
359
375
  memo: launchRulesetsConfig.memo
360
376
  });
377
+
378
+ return rulesetId;
361
379
  }
362
380
 
363
- /// @notice Queues rulesets for a project.
381
+ /// @notice Queue future rulesets for an existing project. Same conversion logic as the launch functions — each
382
+ /// `JBPayDataHookRulesetConfig` is transformed into a `JBRulesetConfig` with the hook wired as the data hook.
383
+ /// Queued rulesets take effect after the current ruleset expires.
364
384
  /// @param projectId The ID of the project to queue rulesets for.
365
385
  /// @param queueRulesetsConfig Configuration which dictates the behavior of the project's rulesets.
366
386
  /// @param dataHook The data hook to use for the project.
@@ -426,4 +446,25 @@ contract JB721TiersHookProjectDeployer is ERC2771Context, JBPermissioned, IJB721
426
446
  projectId: projectId, rulesetConfigurations: rulesetConfigurations, memo: queueRulesetsConfig.memo
427
447
  });
428
448
  }
449
+
450
+ //*********************************************************************//
451
+ // -------------------------- internal views ------------------------- //
452
+ //*********************************************************************//
453
+
454
+ /// @dev ERC-2771 specifies the context as being a single address (20 bytes).
455
+ function _contextSuffixLength() internal view virtual override(ERC2771Context, Context) returns (uint256) {
456
+ return ERC2771Context._contextSuffixLength();
457
+ }
458
+
459
+ /// @notice The calldata. Preferred to use over `msg.data`.
460
+ /// @return calldata The `msg.data` of this call.
461
+ function _msgData() internal view override(ERC2771Context, Context) returns (bytes calldata) {
462
+ return ERC2771Context._msgData();
463
+ }
464
+
465
+ /// @notice The message's sender. Preferred to use over `msg.sender`.
466
+ /// @return sender The address which sent this call.
467
+ function _msgSender() internal view override(ERC2771Context, Context) returns (address sender) {
468
+ return ERC2771Context._msgSender();
469
+ }
429
470
  }