@bananapus/721-hook-v6 0.0.43 → 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.
- package/package.json +1 -1
- package/src/JB721Checkpoints.sol +1 -1
- package/src/JB721TiersHook.sol +62 -52
- package/src/JB721TiersHookDeployer.sol +8 -5
- package/src/JB721TiersHookProjectDeployer.sol +19 -16
- package/src/JB721TiersHookStore.sol +137 -107
- package/src/abstract/JB721Hook.sol +8 -6
- package/src/interfaces/IJB721Checkpoints.sol +1 -1
- package/src/interfaces/IJB721CheckpointsDeployer.sol +1 -1
- package/src/interfaces/IJB721TiersHook.sol +3 -3
- package/src/interfaces/IJB721TiersHookProjectDeployer.sol +2 -2
- package/src/interfaces/IJB721TiersHookStore.sol +11 -11
- package/src/libraries/JB721TiersHookLib.sol +1 -1
- package/src/structs/JB721TiersHookFlags.sol +1 -1
- package/src/structs/JBPayDataHookRulesetMetadata.sol +1 -1
package/package.json
CHANGED
package/src/JB721Checkpoints.sol
CHANGED
|
@@ -78,7 +78,7 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
78
78
|
/// @dev Only callable by the HOOK. Looks up the token's tier voting units from the store.
|
|
79
79
|
/// @param from The previous owner (address(0) on mint).
|
|
80
80
|
/// @param to The new owner (address(0) on burn).
|
|
81
|
-
/// @param tokenId The token ID
|
|
81
|
+
/// @param tokenId The token ID to transfer.
|
|
82
82
|
function onTransfer(address from, address to, uint256 tokenId) external override {
|
|
83
83
|
if (msg.sender != HOOK) revert JB721Checkpoints_Unauthorized();
|
|
84
84
|
|
package/src/JB721TiersHook.sol
CHANGED
|
@@ -151,18 +151,19 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
151
151
|
// ------------------------- external views -------------------------- //
|
|
152
152
|
//*********************************************************************//
|
|
153
153
|
|
|
154
|
-
/// @notice The
|
|
155
|
-
///
|
|
156
|
-
/// @param tokenId The token ID of the NFT
|
|
154
|
+
/// @notice The address that originally received an NFT (typically the payer). Tracked separately from the current
|
|
155
|
+
/// owner so it persists through transfers, useful for provenance and historical voting checkpoints.
|
|
156
|
+
/// @param tokenId The token ID of the NFT.
|
|
157
157
|
/// @return The address of the NFT's first owner.
|
|
158
158
|
function firstOwnerOf(uint256 tokenId) external view override returns (address) {
|
|
159
159
|
address first = _firstOwnerOf[tokenId];
|
|
160
160
|
return first != address(0) ? first : _ownerOf(tokenId);
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
/// @notice
|
|
163
|
+
/// @notice The currency and decimal precision used for this hook's tier prices. For example, if tiers are priced
|
|
164
|
+
/// in ETH with 18 decimals, `currency` would be the ETH currency ID and `decimals` would be 18.
|
|
164
165
|
/// @return currency The currency used for tier prices.
|
|
165
|
-
/// @return decimals The
|
|
166
|
+
/// @return decimals The number of decimals used in tier prices.
|
|
166
167
|
function pricingContext() external view override returns (uint256 currency, uint256 decimals) {
|
|
167
168
|
// Get a reference to the packed pricing context.
|
|
168
169
|
uint256 packed = _packedPricingContext;
|
|
@@ -185,12 +186,12 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
185
186
|
return STORE.balanceOf({hook: address(this), owner: owner});
|
|
186
187
|
}
|
|
187
188
|
|
|
188
|
-
/// @notice
|
|
189
|
-
///
|
|
190
|
-
/// @
|
|
191
|
-
/// @
|
|
192
|
-
/// project (
|
|
193
|
-
/// @return hookSpecifications
|
|
189
|
+
/// @notice Called by the terminal before recording a payment. Calculates how much of the payment should be routed
|
|
190
|
+
/// to tier-based splits vs. kept by the project, and adjusts the minting weight accordingly.
|
|
191
|
+
/// @dev Overrides the base to compute tier split amounts from each tier's `splitPercent`.
|
|
192
|
+
/// @param context The payment context from the terminal.
|
|
193
|
+
/// @return weight The adjusted weight for project token minting (reduced when splits route funds away).
|
|
194
|
+
/// @return hookSpecifications Specifies this hook as the pay hook, with the split amount to forward.
|
|
194
195
|
function beforePayRecordedWith(JBBeforePayRecordedContext calldata context)
|
|
195
196
|
public
|
|
196
197
|
view
|
|
@@ -222,17 +223,20 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
222
223
|
});
|
|
223
224
|
}
|
|
224
225
|
|
|
225
|
-
/// @notice The combined cash
|
|
226
|
-
///
|
|
227
|
-
/// @
|
|
228
|
-
/// @
|
|
229
|
-
/// @return weight The cash out weight of the tokenIds.
|
|
226
|
+
/// @notice The combined cash-out weight of specific NFTs. Divide by `totalCashOutWeight()` to get the fraction of
|
|
227
|
+
/// surplus these NFTs can reclaim. Weight is based on the original tier price, not any discount paid.
|
|
228
|
+
/// @param tokenIds The token IDs of the NFTs to get the combined cash-out weight of.
|
|
229
|
+
/// @return weight The combined cash-out weight.
|
|
230
230
|
function cashOutWeightOf(uint256[] memory tokenIds) public view virtual override returns (uint256) {
|
|
231
231
|
return STORE.cashOutWeightOf({hook: address(this), tokenIds: tokenIds});
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
-
/// @notice
|
|
235
|
-
///
|
|
234
|
+
/// @notice Initialize a cloned copy of the hook. Sets the project association, ERC-721 name/symbol, pricing
|
|
235
|
+
/// context (currency + decimals), metadata URIs, initial tiers, and behavioral flags. Can only be called once
|
|
236
|
+
/// per clone — the implementation contract is pre-initialized in its constructor to prevent misuse.
|
|
237
|
+
/// @dev Called by `JB721TiersHookDeployer` immediately after cloning. Reverts with
|
|
238
|
+
/// `JB721TiersHook_AlreadyInitialized` if called more than once, or `JB721TiersHook_NoProjectId` if projectId is 0.
|
|
239
|
+
/// @param projectId The ID of the project this hook is associated with.
|
|
236
240
|
/// @param name The name of the NFT collection.
|
|
237
241
|
/// @param symbol The symbol representing the NFT collection.
|
|
238
242
|
/// @param baseUri The URI to use as a base for full NFT `tokenUri`s.
|
|
@@ -328,9 +332,9 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
328
332
|
return JB721TiersHookLib.resolveTokenURI(STORE, address(this), baseURI, tokenId);
|
|
329
333
|
}
|
|
330
334
|
|
|
331
|
-
/// @notice The
|
|
332
|
-
///
|
|
333
|
-
/// @return weight The total cash
|
|
335
|
+
/// @notice The total cash-out weight across all outstanding NFTs and pending reserves. This is the denominator
|
|
336
|
+
/// for cash-out calculations — an NFT's share of the surplus is its weight divided by this total.
|
|
337
|
+
/// @return weight The total cash-out weight.
|
|
334
338
|
function totalCashOutWeight() public view virtual override returns (uint256) {
|
|
335
339
|
return STORE.totalCashOutWeight(address(this));
|
|
336
340
|
}
|
|
@@ -339,12 +343,12 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
339
343
|
// ---------------------- external transactions ---------------------- //
|
|
340
344
|
//*********************************************************************//
|
|
341
345
|
|
|
342
|
-
/// @notice Add or
|
|
343
|
-
///
|
|
344
|
-
///
|
|
345
|
-
/// @dev
|
|
346
|
-
/// @param tiersToAdd The tiers to add, as an array of `JB721TierConfig` structs
|
|
347
|
-
/// @param tierIdsToRemove The tiers to remove
|
|
346
|
+
/// @notice Add new NFT tiers or remove existing ones. Added tiers get sequential IDs and must be sorted by
|
|
347
|
+
/// category. Removed tiers stop accepting new mints but existing NFTs remain valid.
|
|
348
|
+
/// @dev Only the collection owner or an operator with `ADJUST_721_TIERS` permission can call this.
|
|
349
|
+
/// @dev Added tiers must respect this hook's flags (e.g. `noNewTiersWithVotes`, `noNewTiersWithReserves`).
|
|
350
|
+
/// @param tiersToAdd The tiers to add, as an array of `JB721TierConfig` structs.
|
|
351
|
+
/// @param tierIdsToRemove The IDs of the tiers to remove.
|
|
348
352
|
function adjustTiers(JB721TierConfig[] calldata tiersToAdd, uint256[] calldata tierIdsToRemove) external override {
|
|
349
353
|
// Enforce permissions.
|
|
350
354
|
_requirePermissionFrom({
|
|
@@ -363,9 +367,11 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
363
367
|
});
|
|
364
368
|
}
|
|
365
369
|
|
|
366
|
-
/// @notice Manually mint NFTs from
|
|
370
|
+
/// @notice Manually mint NFTs from specific tiers to a beneficiary, without requiring payment. Only tiers with
|
|
371
|
+
/// `allowOwnerMint` enabled can be minted this way.
|
|
372
|
+
/// @dev Only the collection owner or an operator with `MINT_721` permission can call this.
|
|
367
373
|
/// @param tierIds The IDs of the tiers to mint from.
|
|
368
|
-
/// @param beneficiary The address to mint to.
|
|
374
|
+
/// @param beneficiary The address to mint the NFTs to.
|
|
369
375
|
/// @return tokenIds The IDs of the newly minted tokens.
|
|
370
376
|
function mintFor(
|
|
371
377
|
uint16[] calldata tierIds,
|
|
@@ -389,9 +395,9 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
389
395
|
_mintTokens({tokenIds: tokenIds, tierIds: tierIds, beneficiary: beneficiary, totalAmountPaid: 0});
|
|
390
396
|
}
|
|
391
397
|
|
|
392
|
-
/// @notice Mint pending reserved NFTs
|
|
393
|
-
///
|
|
394
|
-
/// @param reserveMintConfigs
|
|
398
|
+
/// @notice Mint pending reserved NFTs across multiple tiers in a single call. Reserves accumulate automatically
|
|
399
|
+
/// as NFTs are sold (based on each tier's `reserveFrequency`) and anyone can trigger their minting.
|
|
400
|
+
/// @param reserveMintConfigs The tier IDs and counts specifying how many reserves to mint from each tier.
|
|
395
401
|
function mintPendingReservesFor(JB721TiersMintReservesConfig[] calldata reserveMintConfigs) external override {
|
|
396
402
|
for (uint256 i; i < reserveMintConfigs.length;) {
|
|
397
403
|
// Get a reference to the params being iterated upon.
|
|
@@ -406,12 +412,12 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
406
412
|
}
|
|
407
413
|
}
|
|
408
414
|
|
|
409
|
-
/// @notice
|
|
410
|
-
///
|
|
411
|
-
///
|
|
412
|
-
///
|
|
415
|
+
/// @notice Set a discount on a tier's price. Discounts reduce the price payers must pay, but don't affect the
|
|
416
|
+
/// NFT's cash-out weight (which always uses the original price). The tier must have `cannotIncreaseDiscountPercent`
|
|
417
|
+
/// set appropriately.
|
|
418
|
+
/// @dev Only the collection owner or an operator with `SET_721_DISCOUNT_PERCENT` permission can call this.
|
|
413
419
|
/// @param tierId The ID of the tier to set the discount of.
|
|
414
|
-
/// @param discountPercent The discount percent to set.
|
|
420
|
+
/// @param discountPercent The discount percent to set (0–100).
|
|
415
421
|
function setDiscountPercentOf(uint256 tierId, uint256 discountPercent) external override {
|
|
416
422
|
// Enforce permissions.
|
|
417
423
|
_requirePermissionFrom({
|
|
@@ -420,8 +426,9 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
420
426
|
_setDiscountPercentOf({tierId: tierId, discountPercent: discountPercent});
|
|
421
427
|
}
|
|
422
428
|
|
|
423
|
-
/// @notice
|
|
424
|
-
/// @
|
|
429
|
+
/// @notice Set discount percentages for multiple tiers in a single call.
|
|
430
|
+
/// @dev Only the collection owner or an operator with `SET_721_DISCOUNT_PERCENT` permission can call this.
|
|
431
|
+
/// @param configs An array of tier ID + discount percent pairs to apply.
|
|
425
432
|
function setDiscountPercentsOf(JB721TiersSetDiscountPercentConfig[] calldata configs) external override {
|
|
426
433
|
// Enforce permissions.
|
|
427
434
|
_requirePermissionFrom({
|
|
@@ -440,7 +447,8 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
440
447
|
}
|
|
441
448
|
}
|
|
442
449
|
|
|
443
|
-
/// @notice Update this hook's metadata properties.
|
|
450
|
+
/// @notice Update any combination of this hook's metadata properties in a single call. Pass empty strings or
|
|
451
|
+
/// sentinel values for fields you don't want to change.
|
|
444
452
|
/// @dev Only this contract's owner or an operator with the `SET_721_METADATA` permission can set the metadata.
|
|
445
453
|
/// @param name The new collection name. Send empty to leave unchanged.
|
|
446
454
|
/// @param symbol The new collection symbol. Send empty to leave unchanged.
|
|
@@ -510,8 +518,8 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
510
518
|
// ----------------------- public transactions ----------------------- //
|
|
511
519
|
//*********************************************************************//
|
|
512
520
|
|
|
513
|
-
/// @notice Mint
|
|
514
|
-
///
|
|
521
|
+
/// @notice Mint pending reserved NFTs from a specific tier. Anyone can call this — reserves are minted to the
|
|
522
|
+
/// tier's reserve beneficiary (or the hook's default). Reverts if the ruleset has reserve minting paused.
|
|
515
523
|
/// @param tierId The ID of the tier to mint reserved NFTs from.
|
|
516
524
|
/// @param count The number of reserved NFTs to mint.
|
|
517
525
|
function mintPendingReservesFor(uint256 tierId, uint256 count) public override {
|
|
@@ -591,11 +599,12 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
591
599
|
STORE.recordBurn(tokenIds);
|
|
592
600
|
}
|
|
593
601
|
|
|
594
|
-
/// @notice
|
|
595
|
-
///
|
|
596
|
-
/// @param
|
|
602
|
+
/// @notice Mint a batch of NFTs to the beneficiary and emit a `Mint` event for each. Called after the store has
|
|
603
|
+
/// recorded the mint and generated token IDs.
|
|
604
|
+
/// @param tokenIds The token IDs to mint (generated by the store based on tier and sequence number).
|
|
605
|
+
/// @param tierIds The tier IDs corresponding to each token (same length and order as `tokenIds`).
|
|
597
606
|
/// @param beneficiary The address receiving the NFTs.
|
|
598
|
-
/// @param totalAmountPaid The amount to report in the Mint event.
|
|
607
|
+
/// @param totalAmountPaid The total payment amount (including credits) to report in the Mint event for indexing.
|
|
599
608
|
function _mintTokens(
|
|
600
609
|
uint256[] memory tokenIds,
|
|
601
610
|
uint16[] memory tierIds,
|
|
@@ -735,17 +744,18 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
735
744
|
}
|
|
736
745
|
}
|
|
737
746
|
|
|
738
|
-
/// @notice
|
|
739
|
-
///
|
|
747
|
+
/// @notice Emit the `SetTokenUriResolver` event and persist the new resolver in the store. Pass `address(0)` to
|
|
748
|
+
/// clear the resolver and fall back to the default IPFS-based URI.
|
|
749
|
+
/// @param tokenUriResolver The new token URI resolver (or address(0) to clear).
|
|
740
750
|
function _recordSetTokenUriResolver(IJB721TokenUriResolver tokenUriResolver) internal {
|
|
741
751
|
emit SetTokenUriResolver({resolver: tokenUriResolver, caller: _msgSender()});
|
|
742
752
|
|
|
743
753
|
STORE.recordSetTokenUriResolver(tokenUriResolver);
|
|
744
754
|
}
|
|
745
755
|
|
|
746
|
-
/// @notice
|
|
756
|
+
/// @notice Delegate discount percent storage to the library, which validates and records it in the store.
|
|
747
757
|
/// @param tierId The ID of the tier to set the discount percent for.
|
|
748
|
-
/// @param discountPercent The discount percent to set
|
|
758
|
+
/// @param discountPercent The discount percent to set (0 = no discount, up to DISCOUNT_DENOMINATOR = free).
|
|
749
759
|
function _setDiscountPercentOf(uint256 tierId, uint256 discountPercent) internal {
|
|
750
760
|
// slither-disable-next-line calls-loop
|
|
751
761
|
JB721TiersHookLib.setDiscountPercentOf({
|
|
@@ -754,8 +764,8 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
754
764
|
}
|
|
755
765
|
|
|
756
766
|
/// @notice Before transferring an NFT, register its first owner (if necessary).
|
|
757
|
-
/// @param to The address the NFT
|
|
758
|
-
/// @param tokenId The token ID of the NFT
|
|
767
|
+
/// @param to The address to transfer the NFT to.
|
|
768
|
+
/// @param tokenId The token ID of the NFT to transfer.
|
|
759
769
|
function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address from) {
|
|
760
770
|
// Get only the tier ID and transfersPausable flag (lightweight — avoids full struct construction).
|
|
761
771
|
// slither-disable-next-line calls-loop
|
|
@@ -13,7 +13,9 @@ import {IJB721TiersHookStore} from "./interfaces/IJB721TiersHookStore.sol";
|
|
|
13
13
|
import {JBDeploy721TiersHookConfig} from "./structs/JBDeploy721TiersHookConfig.sol";
|
|
14
14
|
|
|
15
15
|
/// @title JB721TiersHookDeployer
|
|
16
|
-
/// @notice
|
|
16
|
+
/// @notice Factory that deploys EIP-1167 clones of `JB721TiersHook` for existing projects. Each clone is initialized
|
|
17
|
+
/// with its own tiers, metadata, and flags, then ownership is transferred to the caller. The deployed hook is
|
|
18
|
+
/// registered in the `IJBAddressRegistry` for cross-chain address verification.
|
|
17
19
|
contract JB721TiersHookDeployer is ERC2771Context, IJB721TiersHookDeployer {
|
|
18
20
|
//*********************************************************************//
|
|
19
21
|
// --------------- public immutable stored properties ---------------- //
|
|
@@ -22,7 +24,7 @@ contract JB721TiersHookDeployer is ERC2771Context, IJB721TiersHookDeployer {
|
|
|
22
24
|
/// @notice A registry which stores references to contracts and their deployers.
|
|
23
25
|
IJBAddressRegistry public immutable ADDRESS_REGISTRY;
|
|
24
26
|
|
|
25
|
-
/// @notice
|
|
27
|
+
/// @notice The reference 721 tiers hook implementation that gets cloned for each new deployment.
|
|
26
28
|
JB721TiersHook public immutable HOOK;
|
|
27
29
|
|
|
28
30
|
/// @notice The contract that stores and manages data for this contract's NFTs.
|
|
@@ -60,10 +62,11 @@ contract JB721TiersHookDeployer is ERC2771Context, IJB721TiersHookDeployer {
|
|
|
60
62
|
// ---------------------- external transactions ---------------------- //
|
|
61
63
|
//*********************************************************************//
|
|
62
64
|
|
|
63
|
-
/// @notice
|
|
65
|
+
/// @notice Deploy a new 721 tiers hook for a project. Clones the implementation, initializes it with the provided
|
|
66
|
+
/// tiers and flags, transfers ownership to the caller, and registers the hook in the address registry.
|
|
64
67
|
/// @param projectId The ID of the project to deploy the hook for.
|
|
65
|
-
/// @param deployTiersHookConfig The
|
|
66
|
-
/// @param salt A salt
|
|
68
|
+
/// @param deployTiersHookConfig The tiers, metadata, and flags to initialize the hook with.
|
|
69
|
+
/// @param salt A salt for deterministic (CREATE2) deployment. Pass `bytes32(0)` for non-deterministic deployment.
|
|
67
70
|
/// @return newHook The address of the newly deployed hook.
|
|
68
71
|
function deployHookFor(
|
|
69
72
|
uint256 projectId,
|
|
@@ -24,8 +24,9 @@ import {JBPayDataHookRulesetConfig} from "./structs/JBPayDataHookRulesetConfig.s
|
|
|
24
24
|
import {JBQueueRulesetsConfig} from "./structs/JBQueueRulesetsConfig.sol";
|
|
25
25
|
|
|
26
26
|
/// @title JB721TiersHookProjectDeployer
|
|
27
|
-
/// @notice
|
|
28
|
-
///
|
|
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.
|
|
29
30
|
contract JB721TiersHookProjectDeployer is
|
|
30
31
|
ERC2771Context,
|
|
31
32
|
JBPermissioned,
|
|
@@ -39,7 +40,7 @@ contract JB721TiersHookProjectDeployer is
|
|
|
39
40
|
/// @notice The directory of terminals and controllers for projects.
|
|
40
41
|
IJBDirectory public immutable override DIRECTORY;
|
|
41
42
|
|
|
42
|
-
/// @notice The 721 tiers hook
|
|
43
|
+
/// @notice The deployer contract used to create new 721 tiers hook instances via clone.
|
|
43
44
|
IJB721TiersHookDeployer public immutable override HOOK_DEPLOYER;
|
|
44
45
|
|
|
45
46
|
//*********************************************************************//
|
|
@@ -70,9 +71,8 @@ contract JB721TiersHookProjectDeployer is
|
|
|
70
71
|
/// @notice Launches a new project with a 721 tiers hook attached.
|
|
71
72
|
/// @param owner The address to set as the owner of the project. The ERC-721 which confers this project's ownership
|
|
72
73
|
/// will be sent to this address.
|
|
73
|
-
/// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook
|
|
74
|
-
///
|
|
75
|
-
/// @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.
|
|
76
76
|
/// @param controller The controller that the project's rulesets will be queued with.
|
|
77
77
|
/// @param salt A salt to use for the deterministic deployment.
|
|
78
78
|
/// @return projectId The ID of the newly launched project.
|
|
@@ -114,9 +114,8 @@ contract JB721TiersHookProjectDeployer is
|
|
|
114
114
|
/// @notice Launches rulesets for a project with an attached 721 tiers hook.
|
|
115
115
|
/// @dev Only a project's owner or an operator with the `LAUNCH_RULESETS & SET_TERMINALS` permission can launch its
|
|
116
116
|
/// rulesets.
|
|
117
|
-
/// @param projectId The ID of the project
|
|
118
|
-
/// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook
|
|
119
|
-
/// 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.
|
|
120
119
|
/// @param launchRulesetsConfig Configuration which dictates the project's new rulesets.
|
|
121
120
|
/// @param projectUri Metadata URI to associate with the project. Pass an empty string to leave it unchanged.
|
|
122
121
|
/// @param controller The controller that the project's rulesets will be queued with.
|
|
@@ -176,9 +175,8 @@ contract JB721TiersHookProjectDeployer is
|
|
|
176
175
|
|
|
177
176
|
/// @notice Queues rulesets for a project with an attached 721 tiers hook.
|
|
178
177
|
/// @dev Only a project's owner or an operator with the `QUEUE_RULESETS` permission can queue its rulesets.
|
|
179
|
-
/// @param projectId The ID of the project
|
|
180
|
-
/// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook
|
|
181
|
-
/// 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.
|
|
182
180
|
/// @param queueRulesetsConfig Configuration which dictates the project's newly queued rulesets.
|
|
183
181
|
/// @param controller The controller that the project's rulesets will be queued with.
|
|
184
182
|
/// @param salt A salt to use for the deterministic deployment.
|
|
@@ -232,9 +230,11 @@ contract JB721TiersHookProjectDeployer is
|
|
|
232
230
|
// ----------------------- internal helpers -------------------------- //
|
|
233
231
|
//*********************************************************************//
|
|
234
232
|
|
|
235
|
-
/// @notice
|
|
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
236
|
/// @param projectId The ID of the reserved project.
|
|
237
|
-
/// @param launchProjectConfig Configuration which dictates the behavior of the project
|
|
237
|
+
/// @param launchProjectConfig Configuration which dictates the behavior of the project to launch.
|
|
238
238
|
/// @param dataHook The data hook to use for the project.
|
|
239
239
|
/// @param controller The controller that the project's rulesets will be queued with.
|
|
240
240
|
function _launchProjectFor(
|
|
@@ -302,7 +302,8 @@ contract JB721TiersHookProjectDeployer is
|
|
|
302
302
|
});
|
|
303
303
|
}
|
|
304
304
|
|
|
305
|
-
/// @notice
|
|
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.
|
|
306
307
|
/// @param projectId The ID of the project to launch rulesets for.
|
|
307
308
|
/// @param launchRulesetsConfig Configuration which dictates the behavior of the project's rulesets.
|
|
308
309
|
/// @param projectUri Metadata URI to associate with the project. Pass an empty string to leave it unchanged.
|
|
@@ -377,7 +378,9 @@ contract JB721TiersHookProjectDeployer is
|
|
|
377
378
|
return rulesetId;
|
|
378
379
|
}
|
|
379
380
|
|
|
380
|
-
/// @notice
|
|
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.
|
|
381
384
|
/// @param projectId The ID of the project to queue rulesets for.
|
|
382
385
|
/// @param queueRulesetsConfig Configuration which dictates the behavior of the project's rulesets.
|
|
383
386
|
/// @param dataHook The data hook to use for the project.
|
|
@@ -16,7 +16,11 @@ import {JBBitmapWord} from "./structs/JBBitmapWord.sol";
|
|
|
16
16
|
import {JBStored721Tier} from "./structs/JBStored721Tier.sol";
|
|
17
17
|
|
|
18
18
|
/// @title JB721TiersHookStore
|
|
19
|
-
/// @notice
|
|
19
|
+
/// @notice The shared data store for all `JB721TiersHook` instances. Stores tier definitions, mint counts, reserve
|
|
20
|
+
/// tracking, voting units, and removal bitmaps. Each hook registers its own tiers here; the store handles
|
|
21
|
+
/// tier validation, minting logic (including reserve accounting), and cash-out weight calculations.
|
|
22
|
+
/// @dev One store is shared across all hooks. Functions are keyed by hook address so each hook reads/writes
|
|
23
|
+
/// only its own data. Tier IDs are sequential per hook and 1-indexed.
|
|
20
24
|
contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
21
25
|
using JBBitmap for mapping(uint256 => uint256);
|
|
22
26
|
using JBBitmap for JBBitmapWord;
|
|
@@ -155,48 +159,50 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
155
159
|
// ------------------------- external views -------------------------- //
|
|
156
160
|
//*********************************************************************//
|
|
157
161
|
|
|
158
|
-
/// @notice
|
|
159
|
-
///
|
|
160
|
-
/// @param hook The 721 contract
|
|
161
|
-
/// @param tokenId The token ID of the
|
|
162
|
-
/// @return The encoded IPFS URI.
|
|
162
|
+
/// @notice Get the encoded IPFS URI for the tier that a specific NFT belongs to. Used to resolve the NFT's
|
|
163
|
+
/// metadata when no custom `tokenUriResolver` is set.
|
|
164
|
+
/// @param hook The 721 hook contract the NFT belongs to.
|
|
165
|
+
/// @param tokenId The token ID of the NFT.
|
|
166
|
+
/// @return The encoded IPFS URI for the NFT's tier.
|
|
163
167
|
// forge-lint: disable-next-line(mixed-case-function)
|
|
164
168
|
function encodedTierIPFSUriOf(address hook, uint256 tokenId) external view override returns (bytes32) {
|
|
165
169
|
return encodedIPFSUriOf[hook][tierIdOfToken(tokenId)];
|
|
166
170
|
}
|
|
167
171
|
|
|
168
|
-
/// @notice Get the flags
|
|
169
|
-
///
|
|
170
|
-
/// @
|
|
172
|
+
/// @notice Get the behavioral flags for a hook — such as whether transfers are pausable, whether NFT holders can
|
|
173
|
+
/// cash out, and whether token issuance occurs for split-routed payments.
|
|
174
|
+
/// @param hook The 721 hook contract to get the flags of.
|
|
175
|
+
/// @return The hook's flags.
|
|
171
176
|
function flagsOf(address hook) external view override returns (JB721TiersHookFlags memory) {
|
|
172
177
|
return _flagsOf[hook];
|
|
173
178
|
}
|
|
174
179
|
|
|
175
|
-
/// @notice Check
|
|
176
|
-
///
|
|
177
|
-
/// @param
|
|
178
|
-
/// @
|
|
180
|
+
/// @notice Check whether a tier has been removed. Removed tiers can no longer be minted from, but existing NFTs
|
|
181
|
+
/// from that tier remain valid and can still be cashed out.
|
|
182
|
+
/// @param hook The 721 hook contract the tier belongs to.
|
|
183
|
+
/// @param tierId The ID of the tier to check.
|
|
184
|
+
/// @return `true` if the tier has been removed, `false` otherwise.
|
|
179
185
|
function isTierRemoved(address hook, uint256 tierId) external view override returns (bool) {
|
|
180
186
|
JBBitmapWord memory bitmapWord = _removedTiersBitmapWordOf[hook].readId(tierId);
|
|
181
187
|
|
|
182
188
|
return bitmapWord.isTierIdRemoved(tierId);
|
|
183
189
|
}
|
|
184
190
|
|
|
185
|
-
/// @notice
|
|
186
|
-
///
|
|
187
|
-
///
|
|
188
|
-
/// @param
|
|
189
|
-
/// @
|
|
191
|
+
/// @notice How many reserved NFTs are waiting to be minted for a tier. Reserves accumulate automatically as
|
|
192
|
+
/// non-reserve NFTs are minted (based on the tier's `reserveFrequency`). Anyone can mint them via
|
|
193
|
+
/// `mintPendingReservesFor`.
|
|
194
|
+
/// @param hook The 721 hook contract to check.
|
|
195
|
+
/// @param tierId The ID of the tier to check.
|
|
196
|
+
/// @return The number of pending reserved NFTs that can be minted.
|
|
190
197
|
function numberOfPendingReservesFor(address hook, uint256 tierId) external view override returns (uint256) {
|
|
191
198
|
return _numberOfPendingReservesFor({hook: hook, tierId: tierId, storedTier: _storedTierOf[hook][tierId]});
|
|
192
199
|
}
|
|
193
200
|
|
|
194
|
-
/// @notice
|
|
195
|
-
/// @param hook The 721 contract
|
|
196
|
-
/// @param tokenId The token ID of the
|
|
197
|
-
/// @param includeResolvedUri If
|
|
198
|
-
///
|
|
199
|
-
/// @return The tier.
|
|
201
|
+
/// @notice Look up which tier an NFT belongs to and return the full tier details (price, supply, metadata, etc.).
|
|
202
|
+
/// @param hook The 721 hook contract the NFT belongs to.
|
|
203
|
+
/// @param tokenId The token ID of the NFT.
|
|
204
|
+
/// @param includeResolvedUri If `true` and the hook has a `tokenUriResolver`, resolves the URI and includes it.
|
|
205
|
+
/// @return The tier that the NFT belongs to.
|
|
200
206
|
function tierOfTokenId(
|
|
201
207
|
address hook,
|
|
202
208
|
uint256 tokenId,
|
|
@@ -255,14 +261,14 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
255
261
|
transfersPausable = (storedTier.packedBools & 0x2) != 0;
|
|
256
262
|
}
|
|
257
263
|
|
|
258
|
-
/// @notice
|
|
259
|
-
///
|
|
260
|
-
/// @param
|
|
261
|
-
/// @param
|
|
262
|
-
///
|
|
263
|
-
/// @param startingId
|
|
264
|
-
/// @param size The number of tiers to
|
|
265
|
-
/// @return tiers An array of active
|
|
264
|
+
/// @notice Get all active (non-removed) tiers for a hook, with optional filtering by category and pagination.
|
|
265
|
+
/// Tiers are returned sorted by category.
|
|
266
|
+
/// @param hook The 721 hook contract to get tiers from.
|
|
267
|
+
/// @param categories Filter to specific categories. Pass an empty array to include all categories.
|
|
268
|
+
/// @param includeResolvedUri If `true` and the hook has a `tokenUriResolver`, resolves URIs and includes them.
|
|
269
|
+
/// @param startingId Start from this tier ID (for pagination). Pass 0 to start from the beginning.
|
|
270
|
+
/// @param size The maximum number of tiers to return.
|
|
271
|
+
/// @return tiers An array of active tiers.
|
|
266
272
|
function tiersOf(
|
|
267
273
|
address hook,
|
|
268
274
|
uint256[] calldata categories,
|
|
@@ -345,14 +351,12 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
345
351
|
}
|
|
346
352
|
}
|
|
347
353
|
|
|
348
|
-
/// @notice
|
|
349
|
-
///
|
|
350
|
-
/// @
|
|
351
|
-
///
|
|
352
|
-
/// @param
|
|
353
|
-
/// @
|
|
354
|
-
/// @param tierId The ID of the tier to get voting units within.
|
|
355
|
-
/// @return The address' voting units within the tier.
|
|
354
|
+
/// @notice Get an address's voting power from a specific tier. Each NFT in the tier contributes either the tier's
|
|
355
|
+
/// custom `votingUnits` (if configured) or the tier's price. Multiply by the holder's balance in the tier.
|
|
356
|
+
/// @param hook The 721 hook contract that the tier belongs to.
|
|
357
|
+
/// @param account The address to get voting units for.
|
|
358
|
+
/// @param tierId The ID of the tier.
|
|
359
|
+
/// @return The address's total voting units within the tier.
|
|
356
360
|
function tierVotingUnitsOf(
|
|
357
361
|
address hook,
|
|
358
362
|
address account,
|
|
@@ -379,9 +383,9 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
379
383
|
return balance * (useVotingUnits ? _tierVotingUnitsOf[hook][tierId] : storedTier.price);
|
|
380
384
|
}
|
|
381
385
|
|
|
382
|
-
/// @notice
|
|
383
|
-
/// @param hook The 721 contract to get
|
|
384
|
-
/// @return supply The total number of NFTs
|
|
386
|
+
/// @notice The total number of NFTs currently in circulation for a hook (minted minus burned, across all tiers).
|
|
387
|
+
/// @param hook The 721 hook contract to get the total supply of.
|
|
388
|
+
/// @return supply The total number of outstanding NFTs.
|
|
385
389
|
function totalSupplyOf(address hook) external view override returns (uint256 supply) {
|
|
386
390
|
// Keep a reference to the greatest tier ID.
|
|
387
391
|
uint256 maxTierId = maxTierIdOf[hook];
|
|
@@ -402,13 +406,12 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
402
406
|
}
|
|
403
407
|
}
|
|
404
408
|
|
|
405
|
-
/// @notice Get
|
|
406
|
-
///
|
|
407
|
-
/// @dev
|
|
408
|
-
///
|
|
409
|
-
/// @param hook The 721 contract to get the voting units within.
|
|
409
|
+
/// @notice Get an address's total voting power across all tiers of a hook. Sums up the voting units from every
|
|
410
|
+
/// tier where the address holds NFTs.
|
|
411
|
+
/// @dev Each tier contributes: `balance * (customVotingUnits || tierPrice)`.
|
|
412
|
+
/// @param hook The 721 hook contract to get voting units within.
|
|
410
413
|
/// @param account The address to get the voting unit total of.
|
|
411
|
-
/// @return units The total voting units the address
|
|
414
|
+
/// @return units The total voting units the address holds across all tiers.
|
|
412
415
|
function votingUnitsOf(address hook, address account) external view virtual override returns (uint256 units) {
|
|
413
416
|
// Keep a reference to the greatest tier ID.
|
|
414
417
|
uint256 maxTierId = maxTierIdOf[hook];
|
|
@@ -446,11 +449,10 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
446
449
|
// -------------------------- public views --------------------------- //
|
|
447
450
|
//*********************************************************************//
|
|
448
451
|
|
|
449
|
-
/// @notice
|
|
450
|
-
///
|
|
451
|
-
/// @param hook The 721 contract to get the balance within.
|
|
452
|
+
/// @notice How many NFTs an address owns from a hook, totaled across all tiers.
|
|
453
|
+
/// @param hook The 721 hook contract to check.
|
|
452
454
|
/// @param owner The address to check the balance of.
|
|
453
|
-
/// @return balance The number of NFTs the owner
|
|
455
|
+
/// @return balance The total number of NFTs the owner holds.
|
|
454
456
|
function balanceOf(address hook, address owner) public view override returns (uint256 balance) {
|
|
455
457
|
// Keep a reference to the greatest tier ID.
|
|
456
458
|
uint256 maxTierId = maxTierIdOf[hook];
|
|
@@ -466,13 +468,13 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
466
468
|
}
|
|
467
469
|
}
|
|
468
470
|
|
|
469
|
-
/// @notice The combined cash
|
|
470
|
-
///
|
|
471
|
-
/// @dev
|
|
472
|
-
///
|
|
473
|
-
/// @param hook The 721 contract
|
|
474
|
-
/// @param tokenIds The token IDs
|
|
475
|
-
/// @return weight The cash
|
|
471
|
+
/// @notice The combined cash-out weight of specific NFTs. Divide by `totalCashOutWeight` to get the fraction of
|
|
472
|
+
/// the project's surplus that cashing out these NFTs would reclaim.
|
|
473
|
+
/// @dev Weight is based on each NFT's original tier price (not the discounted price paid). Discounts are
|
|
474
|
+
/// transient purchase incentives and don't affect an NFT's share of the cash-out pool.
|
|
475
|
+
/// @param hook The 721 hook contract the NFTs belong to.
|
|
476
|
+
/// @param tokenIds The token IDs to get the combined cash-out weight of.
|
|
477
|
+
/// @return weight The combined cash-out weight.
|
|
476
478
|
function cashOutWeightOf(address hook, uint256[] calldata tokenIds) public view override returns (uint256 weight) {
|
|
477
479
|
// Add each 721's original price (from its tier) to the weight.
|
|
478
480
|
// Uses the full tier price, not the discounted price — by design. Discounts are transient incentives
|
|
@@ -488,10 +490,11 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
488
490
|
}
|
|
489
491
|
}
|
|
490
492
|
|
|
491
|
-
/// @notice The
|
|
492
|
-
///
|
|
493
|
-
/// @param
|
|
494
|
-
/// @
|
|
493
|
+
/// @notice The address that receives reserved NFTs for a tier. Falls back to the hook's default reserve
|
|
494
|
+
/// beneficiary if no tier-specific beneficiary is set.
|
|
495
|
+
/// @param hook The 721 hook contract.
|
|
496
|
+
/// @param tierId The ID of the tier.
|
|
497
|
+
/// @return The reserve beneficiary address for the tier.
|
|
495
498
|
function reserveBeneficiaryOf(address hook, uint256 tierId) public view override returns (address) {
|
|
496
499
|
// Get the stored reserve beneficiary.
|
|
497
500
|
address storedReserveBeneficiaryOfTier = _reserveBeneficiaryOf[hook][tierId];
|
|
@@ -505,19 +508,18 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
505
508
|
return defaultReserveBeneficiaryOf[hook];
|
|
506
509
|
}
|
|
507
510
|
|
|
508
|
-
/// @notice
|
|
509
|
-
///
|
|
510
|
-
/// @param tokenId The token ID of the
|
|
511
|
-
/// @return The ID
|
|
511
|
+
/// @notice Derive which tier an NFT belongs to from its token ID. Token IDs encode the tier: `tokenId / 1e9`
|
|
512
|
+
/// gives the tier ID.
|
|
513
|
+
/// @param tokenId The token ID of the NFT.
|
|
514
|
+
/// @return The tier ID.
|
|
512
515
|
function tierIdOfToken(uint256 tokenId) public pure override returns (uint256) {
|
|
513
516
|
return tokenId / _ONE_BILLION;
|
|
514
517
|
}
|
|
515
518
|
|
|
516
|
-
/// @notice Get the tier
|
|
517
|
-
/// @param hook The 721 contract to get the tier from.
|
|
519
|
+
/// @notice Get the full details of a specific tier by its ID — price, supply, reserve info, metadata, etc.
|
|
520
|
+
/// @param hook The 721 hook contract to get the tier from.
|
|
518
521
|
/// @param id The ID of the tier to get.
|
|
519
|
-
/// @param includeResolvedUri If
|
|
520
|
-
/// resolved and included.
|
|
522
|
+
/// @param includeResolvedUri If `true` and the hook has a `tokenUriResolver`, resolves the URI and includes it.
|
|
521
523
|
/// @return The tier.
|
|
522
524
|
function tierOf(address hook, uint256 id, bool includeResolvedUri) public view override returns (JB721Tier memory) {
|
|
523
525
|
return _getTierFrom({
|
|
@@ -525,9 +527,10 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
525
527
|
});
|
|
526
528
|
}
|
|
527
529
|
|
|
528
|
-
/// @notice The
|
|
529
|
-
///
|
|
530
|
-
/// @
|
|
530
|
+
/// @notice The total cash-out weight across all outstanding NFTs (including pending reserves). This is the
|
|
531
|
+
/// denominator used to determine what fraction of a project's surplus each NFT can reclaim on cash out.
|
|
532
|
+
/// @param hook The 721 hook contract to get the total cash-out weight of.
|
|
533
|
+
/// @return weight The total cash-out weight.
|
|
531
534
|
// Changing defaultReserveBeneficiary retroactively affects totalCashOutWeight. By design —
|
|
532
535
|
// cashOutWeight is calculated dynamically, not snapshotted. The defaultReserveBeneficiary determines which
|
|
533
536
|
// tiers have pending reserves (via _numberOfPendingReservesFor), affecting the denominator. Changing it is
|
|
@@ -752,13 +755,15 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
752
755
|
}
|
|
753
756
|
}
|
|
754
757
|
|
|
755
|
-
/// @notice Pack
|
|
756
|
-
/// @
|
|
757
|
-
///
|
|
758
|
-
/// @param
|
|
759
|
-
/// @param
|
|
760
|
-
/// @param
|
|
761
|
-
/// @param
|
|
758
|
+
/// @notice Pack six tier-level boolean flags into a single uint8 for compact storage in `JBStored721Tier`.
|
|
759
|
+
/// @dev Bit layout: 0=allowOwnerMint, 1=transfersPausable, 2=useVotingUnits, 3=cantBeRemoved,
|
|
760
|
+
/// 4=cantIncreaseDiscountPercent, 5=cantBuyWithCredits.
|
|
761
|
+
/// @param allowOwnerMint Whether the project owner can mint from this tier directly (without paying).
|
|
762
|
+
/// @param transfersPausable Whether transfers of NFTs from this tier can be paused by the ruleset.
|
|
763
|
+
/// @param useVotingUnits Whether this tier uses a custom voting power value instead of defaulting to its price.
|
|
764
|
+
/// @param cantBeRemoved Whether this tier is permanently locked and cannot be removed once added.
|
|
765
|
+
/// @param cantIncreaseDiscountPercent Whether the discount percent can only stay the same or decrease.
|
|
766
|
+
/// @param cantBuyWithCredits Whether this tier cannot be purchased using accumulated pay credits.
|
|
762
767
|
/// @return packed The packed bools.
|
|
763
768
|
function _packBools(
|
|
764
769
|
bool allowOwnerMint,
|
|
@@ -782,14 +787,16 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
782
787
|
}
|
|
783
788
|
}
|
|
784
789
|
|
|
785
|
-
/// @notice Unpack six
|
|
790
|
+
/// @notice Unpack six tier-level boolean flags from a single uint8 stored in `JBStored721Tier.packedBools`.
|
|
791
|
+
/// @dev Inverse of `_packBools`. Same bit layout: 0=allowOwnerMint, 1=transfersPausable, 2=useVotingUnits,
|
|
792
|
+
/// 3=cantBeRemoved, 4=cantIncreaseDiscountPercent, 5=cantBuyWithCredits.
|
|
786
793
|
/// @param packed The packed bools.
|
|
787
|
-
/// @param allowOwnerMint Whether
|
|
788
|
-
/// @param transfersPausable Whether
|
|
789
|
-
/// @param useVotingUnits Whether
|
|
790
|
-
/// @param cantBeRemoved Whether
|
|
791
|
-
/// @param cantIncreaseDiscountPercent Whether
|
|
792
|
-
/// @param cantBuyWithCredits Whether
|
|
794
|
+
/// @param allowOwnerMint Whether the project owner can mint from this tier directly (without paying).
|
|
795
|
+
/// @param transfersPausable Whether transfers of NFTs from this tier can be paused by the ruleset.
|
|
796
|
+
/// @param useVotingUnits Whether this tier uses a custom voting power value instead of defaulting to its price.
|
|
797
|
+
/// @param cantBeRemoved Whether this tier is permanently locked and cannot be removed once added.
|
|
798
|
+
/// @param cantIncreaseDiscountPercent Whether the discount percent can only stay the same or decrease.
|
|
799
|
+
/// @param cantBuyWithCredits Whether this tier cannot be purchased using accumulated pay credits.
|
|
793
800
|
function _unpackBools(uint8 packed)
|
|
794
801
|
internal
|
|
795
802
|
pure
|
|
@@ -816,7 +823,12 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
816
823
|
// ---------------------- external transactions ---------------------- //
|
|
817
824
|
//*********************************************************************//
|
|
818
825
|
|
|
819
|
-
/// @notice
|
|
826
|
+
/// @notice Walk through the tier sorting sequence and skip over any tiers that have been removed. This compacts
|
|
827
|
+
/// the linked list so that `tiersOf` iteration no longer visits removed tiers. Anyone can call this — it's a
|
|
828
|
+
/// maintenance operation with no access control.
|
|
829
|
+
/// @dev Call this after `recordRemoveTierIds` to keep tier iteration efficient. Without cleaning, removed tiers
|
|
830
|
+
/// remain in the linked list (they just can't be minted from). The function also updates
|
|
831
|
+
/// `_startingTierIdOfCategory` if the previous starting tier for a category was removed.
|
|
820
832
|
/// @param hook The 721 contract to clean tiers for.
|
|
821
833
|
function cleanTiers(address hook) external override {
|
|
822
834
|
// Keep a reference to the last tier ID.
|
|
@@ -873,13 +885,16 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
873
885
|
emit CleanTiers({hook: hook, caller: msg.sender});
|
|
874
886
|
}
|
|
875
887
|
|
|
876
|
-
/// @notice
|
|
877
|
-
///
|
|
888
|
+
/// @notice Validate and store new tiers for the calling hook. Each tier is assigned a sequential ID, inserted
|
|
889
|
+
/// into the category-sorted linked list, and has its reserve beneficiary, voting units, IPFS URI, and flags
|
|
890
|
+
/// persisted. Tiers must be provided sorted by category (ascending).
|
|
891
|
+
/// @dev Only callable by hook contracts (msg.sender is treated as the hook address).
|
|
892
|
+
/// WARNING: If any tier in `tiersToAdd` has `useReserveBeneficiaryAsDefault` set to `true`, its
|
|
878
893
|
/// `reserveBeneficiary` will overwrite the hook's global `defaultReserveBeneficiaryOf`. This affects ALL existing
|
|
879
894
|
/// tiers that do not have a tier-specific reserve beneficiary set via `_reserveBeneficiaryOf`. Callers should be
|
|
880
895
|
/// aware of this side effect when using `adjustTiers` to add new tiers.
|
|
881
896
|
/// @param tiersToAdd The tiers to add.
|
|
882
|
-
/// @return tierIds The IDs of the tiers
|
|
897
|
+
/// @return tierIds The IDs of the tiers added.
|
|
883
898
|
function recordAddTiers(JB721TierConfig[] calldata tiersToAdd)
|
|
884
899
|
external
|
|
885
900
|
override
|
|
@@ -1125,7 +1140,8 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
1125
1140
|
maxTierIdOf[msg.sender] = currentMaxTierIdOf + tiersToAdd.length;
|
|
1126
1141
|
}
|
|
1127
1142
|
|
|
1128
|
-
/// @notice
|
|
1143
|
+
/// @notice Increment the burn counter for each token's tier. Does NOT affect `remainingSupply` — burned NFTs
|
|
1144
|
+
/// cannot be re-minted. The burn count is used by `totalSupplyOf` to compute the circulating supply.
|
|
1129
1145
|
/// @dev This function trusts `msg.sender` (the hook contract) to only call it after actually burning the
|
|
1130
1146
|
/// tokens. It does not verify ownership or existence of the token IDs — the hook is responsible for
|
|
1131
1147
|
/// performing those checks before calling this function.
|
|
@@ -1147,16 +1163,22 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
1147
1163
|
}
|
|
1148
1164
|
}
|
|
1149
1165
|
|
|
1150
|
-
/// @notice
|
|
1166
|
+
/// @notice Store the behavioral flags for the calling hook. These flags govern whether new tiers can have voting
|
|
1167
|
+
/// units, reserves, or owner minting enabled.
|
|
1168
|
+
/// @dev Only callable by hook contracts. Overwrites any previously stored flags for the caller.
|
|
1151
1169
|
/// @param flags The flags to set.
|
|
1152
1170
|
function recordFlags(JB721TiersHookFlags calldata flags) external override {
|
|
1153
1171
|
_flagsOf[msg.sender] = flags;
|
|
1154
1172
|
}
|
|
1155
1173
|
|
|
1156
|
-
/// @notice Record
|
|
1157
|
-
///
|
|
1174
|
+
/// @notice Record paid mints: deduct each tier's (discounted) price from `amount`, decrement supply, generate
|
|
1175
|
+
/// token IDs, and enforce that enough supply remains to satisfy pending reserves. Returns the leftover amount
|
|
1176
|
+
/// and the total cost of credit-restricted tiers so the hook can enforce pay-credit rules.
|
|
1177
|
+
/// @dev Reverts if the tier is removed, unrecognized, sold out, or its price exceeds the remaining amount.
|
|
1178
|
+
/// For owner mints, the tier must have `allowOwnerMint` set.
|
|
1179
|
+
/// @param amount The amount to spend on NFTs. The total price must not exceed this amount.
|
|
1158
1180
|
/// @param tierIds The IDs of the tiers to mint from.
|
|
1159
|
-
/// @param isOwnerMint A flag indicating whether
|
|
1181
|
+
/// @param isOwnerMint A flag indicating whether the 721 contract's owner is directly calling this function.
|
|
1160
1182
|
/// @return tokenIds The token IDs of the NFTs which were minted.
|
|
1161
1183
|
/// @return leftoverAmount The `amount` remaining after minting.
|
|
1162
1184
|
/// @return restrictedCost Total cost of tiers with `cantBuyWithCredits` set. The caller can use this to enforce
|
|
@@ -1252,7 +1274,9 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
1252
1274
|
}
|
|
1253
1275
|
}
|
|
1254
1276
|
|
|
1255
|
-
/// @notice
|
|
1277
|
+
/// @notice Generate token IDs for reserve NFT mints and decrement the tier's remaining supply. Reserves
|
|
1278
|
+
/// accumulate as non-reserve NFTs are minted (one reserve per `reserveFrequency` mints). Reverts if `count`
|
|
1279
|
+
/// exceeds the number of pending reserves.
|
|
1256
1280
|
/// @param tierId The ID of the tier to mint reserves from.
|
|
1257
1281
|
/// @param count The number of reserve NFTs to mint.
|
|
1258
1282
|
/// @return tokenIds The token IDs of the reserve NFTs which were minted.
|
|
@@ -1292,10 +1316,12 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
1292
1316
|
}
|
|
1293
1317
|
}
|
|
1294
1318
|
|
|
1295
|
-
/// @notice
|
|
1319
|
+
/// @notice Mark one or more tiers as removed. Removed tiers cannot be minted from, but existing NFTs from those
|
|
1320
|
+
/// tiers remain valid (can still be cashed out and transferred). Pending reserves can still be minted.
|
|
1296
1321
|
/// @dev Removing a tier only marks it in a bitmap — it does not update the sorted tier linked list.
|
|
1297
1322
|
/// Call `cleanTiers()` after removing tiers to update the sorting sequence and prevent stale tier iteration.
|
|
1298
|
-
///
|
|
1323
|
+
/// Reverts if the tier has `cantBeRemoved` set, or if the tier ID is 0 or exceeds `maxTierIdOf`.
|
|
1324
|
+
/// @param tierIds The IDs of the tiers to remove.
|
|
1299
1325
|
function recordRemoveTierIds(uint256[] calldata tierIds) external override {
|
|
1300
1326
|
for (uint256 i; i < tierIds.length;) {
|
|
1301
1327
|
// Set the tier being iterated upon (0-indexed).
|
|
@@ -1325,9 +1351,11 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
1325
1351
|
}
|
|
1326
1352
|
}
|
|
1327
1353
|
|
|
1328
|
-
/// @notice
|
|
1354
|
+
/// @notice Update the discount percentage for a tier. Discounts reduce the price payers pay without affecting the
|
|
1355
|
+
/// NFT's cash-out weight (which always uses the original price). Reverts if the tier is removed, if the percent
|
|
1356
|
+
/// exceeds the denominator, or if the tier has `cantIncreaseDiscountPercent` set and the new value is higher.
|
|
1329
1357
|
/// @param tierId The ID of the tier to record a discount for.
|
|
1330
|
-
/// @param discountPercent The new discount percent
|
|
1358
|
+
/// @param discountPercent The new discount percent to apply.
|
|
1331
1359
|
function recordSetDiscountPercentOf(uint256 tierId, uint256 discountPercent) external override {
|
|
1332
1360
|
// Make sure the tier hasn't been removed.
|
|
1333
1361
|
JBBitmapWord memory bitmapWord = _removedTiersBitmapWordOf[msg.sender].readId(tierId);
|
|
@@ -1370,10 +1398,12 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
1370
1398
|
tokenUriResolverOf[msg.sender] = resolver;
|
|
1371
1399
|
}
|
|
1372
1400
|
|
|
1373
|
-
/// @notice
|
|
1374
|
-
///
|
|
1375
|
-
///
|
|
1376
|
-
/// @param
|
|
1401
|
+
/// @notice Update tier balance accounting when an NFT is transferred. Decrements the sender's balance and
|
|
1402
|
+
/// increments the receiver's balance for the given tier. Handles mints (from == address(0)) and burns
|
|
1403
|
+
/// (to == address(0)) as one-sided updates.
|
|
1404
|
+
/// @param tierId The ID of the tier that the 721 to transfer belongs to.
|
|
1405
|
+
/// @param from The address to transfer the 721 from.
|
|
1406
|
+
/// @param to The address to transfer the 721 to.
|
|
1377
1407
|
function recordTransferForTier(uint256 tierId, address from, address to) external override {
|
|
1378
1408
|
// If this is not a mint,
|
|
1379
1409
|
if (from != address(0)) {
|
|
@@ -20,10 +20,11 @@ import {IJB721Hook} from "../interfaces/IJB721Hook.sol";
|
|
|
20
20
|
import {ERC721} from "./ERC721.sol";
|
|
21
21
|
|
|
22
22
|
/// @title JB721Hook
|
|
23
|
-
/// @notice
|
|
24
|
-
///
|
|
25
|
-
///
|
|
26
|
-
/// the
|
|
23
|
+
/// @notice Abstract base for Juicebox 721 hooks. Implements the pay hook and cash-out hook interfaces: when a project
|
|
24
|
+
/// is paid through its terminal, this hook mints NFTs to the payer; when NFT holders cash out, this hook burns their
|
|
25
|
+
/// NFTs and lets the terminal send them their share of the project's surplus (proportional to the NFT's price).
|
|
26
|
+
/// @dev Subclasses (like `JB721TiersHook`) implement the actual minting logic via `_processPayment` and burn
|
|
27
|
+
/// tracking via `_didBurn`.
|
|
27
28
|
abstract contract JB721Hook is ERC721, IJB721Hook {
|
|
28
29
|
//*********************************************************************//
|
|
29
30
|
// --------------------------- custom errors ------------------------- //
|
|
@@ -270,7 +271,8 @@ abstract contract JB721Hook is ERC721, IJB721Hook {
|
|
|
270
271
|
PROJECT_ID = projectId;
|
|
271
272
|
}
|
|
272
273
|
|
|
273
|
-
/// @notice Process a received payment.
|
|
274
|
-
///
|
|
274
|
+
/// @notice Process a received payment by minting NFTs and/or updating credits. Subclasses implement the
|
|
275
|
+
/// specific minting logic (e.g., tier selection, credit tracking, split distribution).
|
|
276
|
+
/// @param context The payment context passed in by the terminal (includes amount, payer, beneficiary, metadata).
|
|
275
277
|
function _processPayment(JBAfterPayRecordedContext calldata context) internal virtual;
|
|
276
278
|
}
|
|
@@ -36,6 +36,6 @@ interface IJB721Checkpoints is IERC5805 {
|
|
|
36
36
|
/// Auto-self-delegates on first receive so checkpoints work without manual delegation.
|
|
37
37
|
/// @param from The previous owner (address(0) on mint).
|
|
38
38
|
/// @param to The new owner (address(0) on burn).
|
|
39
|
-
/// @param tokenId The token ID
|
|
39
|
+
/// @param tokenId The token ID to transfer (used to look up tier voting units).
|
|
40
40
|
function onTransfer(address from, address to, uint256 tokenId) external;
|
|
41
41
|
}
|
|
@@ -6,7 +6,7 @@ import {IJB721TiersHookStore} from "./IJB721TiersHookStore.sol";
|
|
|
6
6
|
|
|
7
7
|
/// @notice Deploys JB721Checkpoints clones for JB721TiersHook instances.
|
|
8
8
|
interface IJB721CheckpointsDeployer {
|
|
9
|
-
/// @notice Thrown when the caller is not the hook that the checkpoint module is
|
|
9
|
+
/// @notice Thrown when the caller is not the hook that the checkpoint module is deployed for.
|
|
10
10
|
error JB721CheckpointsDeployer_Unauthorized();
|
|
11
11
|
|
|
12
12
|
/// @notice The implementation contract that clones are based on.
|
|
@@ -21,7 +21,7 @@ interface IJB721TiersHook is IJB721Hook {
|
|
|
21
21
|
/// @notice Emitted when an `addToBalanceOf` call reverts during leftover distribution. The funds remain
|
|
22
22
|
/// stranded in the hook contract.
|
|
23
23
|
/// @param projectId The project ID whose terminal reverted.
|
|
24
|
-
/// @param token The token
|
|
24
|
+
/// @param token The token to send.
|
|
25
25
|
/// @param amount The amount that failed to send.
|
|
26
26
|
/// @param reason The revert reason bytes.
|
|
27
27
|
event AddToBalanceReverted(uint256 indexed projectId, address token, uint256 amount, bytes reason);
|
|
@@ -108,7 +108,7 @@ interface IJB721TiersHook is IJB721Hook {
|
|
|
108
108
|
/// project's balance.
|
|
109
109
|
/// @param projectId The project ID the split belongs to.
|
|
110
110
|
/// @param split The split that reverted.
|
|
111
|
-
/// @param amount The amount that was
|
|
111
|
+
/// @param amount The amount that was paid out.
|
|
112
112
|
/// @param reason The revert reason bytes.
|
|
113
113
|
/// @param caller The address that called the function.
|
|
114
114
|
event SplitPayoutReverted(uint256 indexed projectId, JBSplit split, uint256 amount, bytes reason, address caller);
|
|
@@ -142,7 +142,7 @@ interface IJB721TiersHook is IJB721Hook {
|
|
|
142
142
|
|
|
143
143
|
/// @notice Context for the pricing of this hook's tiers.
|
|
144
144
|
/// @return currency The currency used for tier prices.
|
|
145
|
-
/// @return decimals The
|
|
145
|
+
/// @return decimals The number of decimals used in tier prices.
|
|
146
146
|
function pricingContext() external view returns (uint256 currency, uint256 decimals);
|
|
147
147
|
|
|
148
148
|
/// @notice The checkpoint module that manages IVotes-compatible checkpointed voting power for this hook's NFTs.
|
|
@@ -40,7 +40,7 @@ interface IJB721TiersHookProjectDeployer {
|
|
|
40
40
|
returns (uint256 projectId, IJB721TiersHook hook);
|
|
41
41
|
|
|
42
42
|
/// @notice Launches rulesets for a project with an attached 721 tiers hook.
|
|
43
|
-
/// @param projectId The ID of the project
|
|
43
|
+
/// @param projectId The ID of the project to launch rulesets for.
|
|
44
44
|
/// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook.
|
|
45
45
|
/// @param launchRulesetsConfig Configuration which dictates the project's new rulesets.
|
|
46
46
|
/// @param projectUri Metadata URI to associate with the project. Pass an empty string to leave it unchanged.
|
|
@@ -60,7 +60,7 @@ interface IJB721TiersHookProjectDeployer {
|
|
|
60
60
|
returns (uint256 rulesetId, IJB721TiersHook hook);
|
|
61
61
|
|
|
62
62
|
/// @notice Queues rulesets for a project with an attached 721 tiers hook.
|
|
63
|
-
/// @param projectId The ID of the project
|
|
63
|
+
/// @param projectId The ID of the project to queue rulesets for.
|
|
64
64
|
/// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook.
|
|
65
65
|
/// @param queueRulesetsConfig Configuration which dictates the project's newly queued rulesets.
|
|
66
66
|
/// @param controller The controller that the project's rulesets will be queued with.
|
|
@@ -203,19 +203,19 @@ interface IJB721TiersHookStore {
|
|
|
203
203
|
|
|
204
204
|
/// @notice Record newly added tiers.
|
|
205
205
|
/// @param tiersToAdd The tiers to add.
|
|
206
|
-
/// @return tierIds The IDs of the tiers
|
|
206
|
+
/// @return tierIds The IDs of the tiers added.
|
|
207
207
|
function recordAddTiers(JB721TierConfig[] calldata tiersToAdd) external returns (uint256[] memory tierIds);
|
|
208
208
|
|
|
209
|
-
/// @notice
|
|
209
|
+
/// @notice Records the burning of tiered 721 tokens, updating supply tracking for the affected tiers.
|
|
210
210
|
/// @param tokenIds The token IDs of the NFTs to burn.
|
|
211
211
|
function recordBurn(uint256[] calldata tokenIds) external;
|
|
212
212
|
|
|
213
|
-
/// @notice
|
|
213
|
+
/// @notice Stores updated ruleset metadata flags that control minting, transferring, and tier behavior.
|
|
214
214
|
/// @param flags The flags to set.
|
|
215
215
|
function recordFlags(JB721TiersHookFlags calldata flags) external;
|
|
216
216
|
|
|
217
217
|
/// @notice Record 721 mints from the provided tiers.
|
|
218
|
-
/// @param amount The amount
|
|
218
|
+
/// @param amount The amount to spend on NFTs.
|
|
219
219
|
/// @param tierIds The IDs of the tiers to mint from.
|
|
220
220
|
/// @param isOwnerMint Whether this is a direct owner mint.
|
|
221
221
|
/// @return tokenIds The token IDs of the NFTs which were minted.
|
|
@@ -235,13 +235,13 @@ interface IJB721TiersHookStore {
|
|
|
235
235
|
/// @return tokenIds The token IDs of the reserve NFTs which were minted.
|
|
236
236
|
function recordMintReservesFor(uint256 tierId, uint256 count) external returns (uint256[] memory tokenIds);
|
|
237
237
|
|
|
238
|
-
/// @notice Record tiers
|
|
239
|
-
/// @param tierIds The IDs of the tiers
|
|
238
|
+
/// @notice Record tiers to remove.
|
|
239
|
+
/// @param tierIds The IDs of the tiers to remove.
|
|
240
240
|
function recordRemoveTierIds(uint256[] calldata tierIds) external;
|
|
241
241
|
|
|
242
242
|
/// @notice Record the setting of a discount for a tier.
|
|
243
243
|
/// @param tierId The ID of the tier to set the discount of.
|
|
244
|
-
/// @param discountPercent The new discount percent
|
|
244
|
+
/// @param discountPercent The new discount percent to apply.
|
|
245
245
|
function recordSetDiscountPercentOf(uint256 tierId, uint256 discountPercent) external;
|
|
246
246
|
|
|
247
247
|
/// @notice Record a new encoded IPFS URI for a tier.
|
|
@@ -254,9 +254,9 @@ interface IJB721TiersHookStore {
|
|
|
254
254
|
/// @param resolver The resolver to set.
|
|
255
255
|
function recordSetTokenUriResolver(IJB721TokenUriResolver resolver) external;
|
|
256
256
|
|
|
257
|
-
/// @notice
|
|
258
|
-
/// @param tierId The ID of the tier that the 721
|
|
259
|
-
/// @param from The address
|
|
260
|
-
/// @param to The address
|
|
257
|
+
/// @notice Records a 721 token transfer, updating the first-owner tracking for the receiving address.
|
|
258
|
+
/// @param tierId The ID of the tier that the 721 to transfer belongs to.
|
|
259
|
+
/// @param from The address to transfer the 721 from.
|
|
260
|
+
/// @param to The address to transfer the 721 to.
|
|
261
261
|
function recordTransferForTier(uint256 tierId, address from, address to) external;
|
|
262
262
|
}
|
|
@@ -113,7 +113,7 @@ library JB721TiersHookLib {
|
|
|
113
113
|
/// @param splits The splits contract to read tier split groups from.
|
|
114
114
|
/// @param projectId The project ID of the hook.
|
|
115
115
|
/// @param hookAddress The hook address (for computing split group IDs).
|
|
116
|
-
/// @param token The token
|
|
116
|
+
/// @param token The token to distribute.
|
|
117
117
|
/// @param amount The total amount to distribute.
|
|
118
118
|
/// @param decimals The token decimals.
|
|
119
119
|
/// @param encodedSplitData The encoded per-tier breakdown from hookMetadata.
|
|
@@ -8,7 +8,7 @@ pragma solidity ^0.8.0;
|
|
|
8
8
|
/// @custom:member noNewTiersWithOwnerMinting A boolean indicating whether attempts to add new tiers with
|
|
9
9
|
/// `allowOwnerMint` set to true will revert.
|
|
10
10
|
/// @custom:member preventOverspending A boolean indicating whether payments attempting to spend more than the price of
|
|
11
|
-
/// the NFTs
|
|
11
|
+
/// the NFTs to mint will revert.
|
|
12
12
|
/// @custom:member issueTokensForSplits A boolean indicating whether payers receive token credit for the portion of
|
|
13
13
|
/// their payment that is routed to tier splits. When false (default), weight is reduced proportionally.
|
|
14
14
|
struct JB721TiersHookFlags {
|
|
@@ -23,7 +23,7 @@ pragma solidity ^0.8.0;
|
|
|
23
23
|
/// between its tokens.
|
|
24
24
|
/// @custom:member holdFees A flag indicating if fees should be held during this ruleset.
|
|
25
25
|
/// @custom:member useTotalSurplusForCashOut A flag indicating if cash outs should use the project's balance held
|
|
26
|
-
/// in all terminals instead of the project's local terminal balance from which the cash out is
|
|
26
|
+
/// in all terminals instead of the project's local terminal balance from which the cash out is fulfilled.
|
|
27
27
|
/// @custom:member useDataHookForCashOuts A flag indicating if the data hook should be used for cash out transactions
|
|
28
28
|
/// during
|
|
29
29
|
/// this ruleset.
|