@bananapus/721-hook-v6 0.0.67 → 0.0.69
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/README.md +21 -17
- package/package.json +2 -2
- package/references/operations.md +4 -4
- package/references/runtime.md +4 -4
- package/src/JB721Checkpoints.sol +9 -1
- package/src/JB721CheckpointsDeployer.sol +1 -0
- package/src/JB721TiersHook.sol +13 -1
- package/src/JB721TiersHookStore.sol +47 -5
- package/src/abstract/JB721Hook.sol +9 -0
- package/src/libraries/JB721TiersHookLib.sol +34 -0
package/README.md
CHANGED
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
`@bananapus/721-hook-v6` is the tiered NFT issuance layer for Juicebox V6. It lets a project mint ERC-721s on payment, attach tier-specific pricing and supply rules, mint reserves, and integrate custom token URI resolvers.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
- [ARCHITECTURE.md](./ARCHITECTURE.md) — module layout, data flow, and the hook-store accounting model.
|
|
6
|
+
- [INVARIANTS.md](./INVARIANTS.md) — per-contract operation inventory and the guarantees the hook makes to projects and integrators.
|
|
7
|
+
- [ADMINISTRATION.md](./ADMINISTRATION.md) — admin surfaces, role matrix, immutable decisions, and recovery posture.
|
|
8
|
+
- [RISKS.md](./RISKS.md) — threat model, accepted risks, and invariants to verify.
|
|
9
|
+
- [USER_JOURNEYS.md](./USER_JOURNEYS.md) — example end-to-end flows for launching, paying, minting, and cashing out tiers.
|
|
10
|
+
- [AUDIT_INSTRUCTIONS.md](./AUDIT_INSTRUCTIONS.md) — what to focus on for a security audit and how to start.
|
|
11
|
+
- [SKILLS.md](./SKILLS.md) — implementation nuances, gotchas, and reading order for working on this codebase.
|
|
12
|
+
- [STYLE_GUIDE.md](./STYLE_GUIDE.md) — Solidity conventions and repo layout used across the V6 ecosystem.
|
|
13
|
+
- [CHANGELOG.md](./CHANGELOG.md) — v5 → v6 ABI and behavior deltas.
|
|
14
|
+
- [references/runtime.md](./references/runtime.md) — contract roles, the runtime pay and cash-out path, and high-risk areas.
|
|
15
|
+
- [references/operations.md](./references/operations.md) — deployment surface, change checklist, and common failure modes.
|
|
12
16
|
|
|
13
17
|
## Overview
|
|
14
18
|
|
|
@@ -25,7 +29,7 @@ Use this repo when a project's NFT logic should be part of its payment and cash-
|
|
|
25
29
|
|
|
26
30
|
This repo does more than "mint NFTs on pay." It changes how payment value, tier state, reserves, and cash-out behavior interact.
|
|
27
31
|
|
|
28
|
-
## Key
|
|
32
|
+
## Key contracts
|
|
29
33
|
|
|
30
34
|
| Contract | Role |
|
|
31
35
|
| --- | --- |
|
|
@@ -36,7 +40,7 @@ This repo does more than "mint NFTs on pay." It changes how payment value, tier
|
|
|
36
40
|
| `JB721Hook` | Abstract base for 721 pay and cash-out hook behavior. |
|
|
37
41
|
| `JB721Checkpoints` | Per-hook IVotes checkpoint module. Tracks historical owner checkpoints plus per-tier eligible voting units (`getPastTierVotingUnits`) for tier-scoped reward distribution. |
|
|
38
42
|
|
|
39
|
-
## Mental
|
|
43
|
+
## Mental model
|
|
40
44
|
|
|
41
45
|
Think about the repo in three pieces:
|
|
42
46
|
|
|
@@ -46,7 +50,7 @@ Think about the repo in three pieces:
|
|
|
46
50
|
|
|
47
51
|
If a bug affects supply, reserve minting, or tier lookup, it usually lives in the hook-store interaction. If it affects project wiring, it usually lives in the deployer path or in how the hook is attached to rulesets.
|
|
48
52
|
|
|
49
|
-
## Read
|
|
53
|
+
## Read these files first
|
|
50
54
|
|
|
51
55
|
1. `src/JB721TiersHook.sol`
|
|
52
56
|
2. `src/JB721TiersHookStore.sol`
|
|
@@ -54,7 +58,7 @@ If a bug affects supply, reserve minting, or tier lookup, it usually lives in th
|
|
|
54
58
|
4. `src/JB721TiersHookDeployer.sol` or `src/JB721TiersHookProjectDeployer.sol`
|
|
55
59
|
5. the resolver contract in the downstream repo, if present
|
|
56
60
|
|
|
57
|
-
## Integration
|
|
61
|
+
## Integration traps
|
|
58
62
|
|
|
59
63
|
- this hook participates in treasury-facing execution, not only metadata
|
|
60
64
|
- custom token URI resolvers should be treated as part of the trusted surface
|
|
@@ -62,7 +66,7 @@ If a bug affects supply, reserve minting, or tier lookup, it usually lives in th
|
|
|
62
66
|
- projects should be explicit about whether the hook affects pay, cash out, or only metadata-facing paths
|
|
63
67
|
- per-tier eligible voting units are queryable via `getPastTierVotingUnits(tierId, blockNumber)` for tier-scoped reward denominators, but minting alone does not enroll a token: a token only counts toward that total once it is enrolled (`delegate(address, uint256[])`) or transferred for the first time, and stops counting when burned
|
|
64
68
|
|
|
65
|
-
## Where
|
|
69
|
+
## Where state lives
|
|
66
70
|
|
|
67
71
|
- tier definitions and accounting: `JB721TiersHookStore`
|
|
68
72
|
- project-facing execution and permission checks: `JB721TiersHook`
|
|
@@ -70,7 +74,7 @@ If a bug affects supply, reserve minting, or tier lookup, it usually lives in th
|
|
|
70
74
|
|
|
71
75
|
That split is why UI bugs, economic bugs, and deployment bugs often land in different repos even when users describe them all as "721 hook issues."
|
|
72
76
|
|
|
73
|
-
## High-
|
|
77
|
+
## High-signal tests
|
|
74
78
|
|
|
75
79
|
1. `test/E2E/Pay_Mint_Redeem_E2E.t.sol`
|
|
76
80
|
2. `test/invariants/TierLifecycleInvariant.t.sol`
|
|
@@ -97,11 +101,11 @@ Useful scripts:
|
|
|
97
101
|
- `npm run deploy:mainnets`
|
|
98
102
|
- `npm run deploy:testnets`
|
|
99
103
|
|
|
100
|
-
## Deployment
|
|
104
|
+
## Deployment notes
|
|
101
105
|
|
|
102
106
|
Hooks are deployed as clones and typically registered in the address registry. The package is designed to compose with Omnichain, Croptop, Defifa, Banny, and other packages that rely on tier-aware NFT issuance.
|
|
103
107
|
|
|
104
|
-
## Repository
|
|
108
|
+
## Repository layout
|
|
105
109
|
|
|
106
110
|
```text
|
|
107
111
|
src/
|
|
@@ -120,7 +124,7 @@ script/
|
|
|
120
124
|
helpers/
|
|
121
125
|
```
|
|
122
126
|
|
|
123
|
-
## Risks
|
|
127
|
+
## Risks and notes
|
|
124
128
|
|
|
125
129
|
- tier accounting is sensitive to reserve minting, split routing, and cross-currency normalization
|
|
126
130
|
- tiny split allocations can round down to zero recipient amounts
|
|
@@ -130,7 +134,7 @@ script/
|
|
|
130
134
|
|
|
131
135
|
When people say "the 721 hook," they often mean three different things: the hook contract, the store, and the metadata resolver plugged into it.
|
|
132
136
|
|
|
133
|
-
## For AI
|
|
137
|
+
## For AI agents
|
|
134
138
|
|
|
135
139
|
- Separate hook behavior, store behavior, and resolver behavior in your explanation.
|
|
136
140
|
- Read the store invariants and end-to-end pay/mint/redeem tests before summarizing lifecycle guarantees.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bananapus/721-hook-v6",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.69",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@bananapus/address-registry-v6": "^0.0.33",
|
|
29
|
-
"@bananapus/core-v6": "^0.0.
|
|
29
|
+
"@bananapus/core-v6": "^0.0.81",
|
|
30
30
|
"@bananapus/ownable-v6": "^0.0.36",
|
|
31
31
|
"@bananapus/permission-ids-v6": "^0.0.29",
|
|
32
32
|
"@openzeppelin/contracts": "5.6.1",
|
package/references/operations.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# 721 Hook Operations
|
|
2
2
|
|
|
3
|
-
## Deployment
|
|
3
|
+
## Deployment surface
|
|
4
4
|
|
|
5
5
|
- [`src/JB721TiersHookDeployer.sol`](../src/JB721TiersHookDeployer.sol) clones and initializes hooks for existing projects.
|
|
6
6
|
- [`src/JB721TiersHookProjectDeployer.sol`](../src/JB721TiersHookProjectDeployer.sol) combines hook deployment with project launch or ruleset setup.
|
|
7
7
|
- [`script/Deploy.s.sol`](../script/Deploy.s.sol) is the deployment entry point when you need current deployment wiring rather than abstract runtime behavior.
|
|
8
8
|
|
|
9
|
-
## Change
|
|
9
|
+
## Change checklist
|
|
10
10
|
|
|
11
11
|
- If you edit hook initialization, verify deployer config structs and project-launch helpers still encode the same assumptions.
|
|
12
12
|
- If you edit tier config or metadata behavior, inspect the corresponding structs and interfaces in `src/structs/` and `src/interfaces/`.
|
|
@@ -16,14 +16,14 @@
|
|
|
16
16
|
- If you touch permissions, verify the caller path and permission constants still line up with the downstream ecosystem package that defines them.
|
|
17
17
|
- If you touch URI behavior, confirm whether the issue belongs in this repo or in a downstream resolver contract that the hook calls.
|
|
18
18
|
|
|
19
|
-
## Common
|
|
19
|
+
## Common failure modes
|
|
20
20
|
|
|
21
21
|
- Payment metadata decodes to tier IDs that no longer match the intended mint path.
|
|
22
22
|
- Reserve or owner-mint changes accidentally violate the store's supply guarantees.
|
|
23
23
|
- Hook-side assumptions drift from deployer-side assumptions, especially around initialization flags and pricing context.
|
|
24
24
|
- A change looks metadata-only but actually changes treasury behavior because split, credit, or cash-out logic moved with it.
|
|
25
25
|
|
|
26
|
-
## Useful
|
|
26
|
+
## Useful proof points
|
|
27
27
|
|
|
28
28
|
- [`test/Fork.t.sol`](../test/Fork.t.sol) for live-integration assumptions.
|
|
29
29
|
- [`test/TestRegressionGaps.sol`](../test/TestRegressionGaps.sol) for known edge cases the repo authors considered worth pinning down.
|
package/references/runtime.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# 721 Hook Runtime
|
|
2
2
|
|
|
3
|
-
## Contract
|
|
3
|
+
## Contract roles
|
|
4
4
|
|
|
5
5
|
- [`src/abstract/JB721Hook.sol`](../src/abstract/JB721Hook.sol) is the shared pay and cash-out hook surface. It validates the calling terminal, decodes metadata, and delegates runtime behavior to the concrete hook.
|
|
6
6
|
- [`src/JB721TiersHook.sol`](../src/JB721TiersHook.sol) is the main project-facing contract. It handles tier-aware minting, split forwarding, discount updates, metadata changes, reserve minting, and cash-out weight calculations.
|
|
7
7
|
- [`src/JB721TiersHookStore.sol`](../src/JB721TiersHookStore.sol) is the shared storage and accounting backend. It owns tier definitions, supply counters, burn counts, reserve availability, and voting-unit state.
|
|
8
8
|
- [`src/libraries/JB721TiersHookLib.sol`](../src/libraries/JB721TiersHookLib.sol) holds size-sensitive helper logic such as tier adjustment, split calculation/distribution, pricing normalization, and token-URI resolution.
|
|
9
9
|
|
|
10
|
-
## Runtime
|
|
10
|
+
## Runtime path
|
|
11
11
|
|
|
12
12
|
1. Terminal calls [`src/abstract/JB721Hook.sol`](../src/abstract/JB721Hook.sol) through the pay or cash-out hook interface.
|
|
13
13
|
2. The abstract hook validates the terminal and decodes metadata.
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
4. [`src/JB721TiersHookStore.sol`](../src/JB721TiersHookStore.sol) records the mint, reserve, burn, and tier-state effects.
|
|
16
16
|
5. If the flow forwards split funds or resolves token metadata, the hook delegates into [`src/libraries/JB721TiersHookLib.sol`](../src/libraries/JB721TiersHookLib.sol).
|
|
17
17
|
|
|
18
|
-
## High-
|
|
18
|
+
## High-risk areas
|
|
19
19
|
|
|
20
20
|
- Reserve accounting: edits around `reserveFrequency`, pending reserves, or owner minting must preserve the store's supply protections.
|
|
21
21
|
- Tier splits: split forwarding changes affect both payer economics and project treasury accounting. Check both `beforePayRecordedWith` and the distribution path.
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
- Tier removal and cleanup: removing tiers is not the same as cleaning the sorted tier list. Storage cleanup behavior matters.
|
|
25
25
|
- Default reserve beneficiary changes: they affect which tiers count pending reserves unless a tier-specific beneficiary overrides it. That is an economic change, not just an admin update.
|
|
26
26
|
|
|
27
|
-
## Tests
|
|
27
|
+
## Tests to trust first
|
|
28
28
|
|
|
29
29
|
- [`test/Fork.t.sol`](../test/Fork.t.sol) for launch and live integration flows.
|
|
30
30
|
- [`test/TestVotingUnitsLifecycle.t.sol`](../test/TestVotingUnitsLifecycle.t.sol) for voting-unit lifecycle behavior.
|
package/src/JB721Checkpoints.sol
CHANGED
|
@@ -24,8 +24,13 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
24
24
|
// --------------------------- custom errors ------------------------- //
|
|
25
25
|
//*********************************************************************//
|
|
26
26
|
|
|
27
|
+
/// @notice Thrown when `initialize` is called on a module whose hook has already been set.
|
|
27
28
|
error JB721Checkpoints_AlreadyInitialized(address hook);
|
|
29
|
+
|
|
30
|
+
/// @notice Thrown when the caller tries to enroll a token they do not currently own.
|
|
28
31
|
error JB721Checkpoints_NotOwner(uint256 tokenId, address caller);
|
|
32
|
+
|
|
33
|
+
/// @notice Thrown when a hook-only function is called by an address other than the module's hook.
|
|
29
34
|
error JB721Checkpoints_Unauthorized(address caller, address hook);
|
|
30
35
|
|
|
31
36
|
//*********************************************************************//
|
|
@@ -158,7 +163,10 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
158
163
|
// ----------------------- external views ---------------------------- //
|
|
159
164
|
//*********************************************************************//
|
|
160
165
|
|
|
161
|
-
/// @
|
|
166
|
+
/// @notice The total eligible voting units of a tier at a past block.
|
|
167
|
+
/// @param tierId The tier to get the eligible voting units of.
|
|
168
|
+
/// @param blockNumber The block number to look up (must be strictly in the past).
|
|
169
|
+
/// @return The tier's eligible voting units at `blockNumber`.
|
|
162
170
|
function getPastTierVotingUnits(uint256 tierId, uint256 blockNumber) external view override returns (uint256) {
|
|
163
171
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
164
172
|
return _tierEligibleUnitsOf[tierId].upperLookupRecent(uint96(_validateTimepoint(blockNumber)));
|
|
@@ -17,6 +17,7 @@ contract JB721CheckpointsDeployer is IJB721CheckpointsDeployer {
|
|
|
17
17
|
// --------------------------- custom errors ------------------------- //
|
|
18
18
|
//*********************************************************************//
|
|
19
19
|
|
|
20
|
+
/// @notice Thrown when the caller of `deploy` is not the hook the module will serve.
|
|
20
21
|
error JB721CheckpointsDeployer_Unauthorized(address caller, address hook);
|
|
21
22
|
|
|
22
23
|
//*********************************************************************//
|
package/src/JB721TiersHook.sol
CHANGED
|
@@ -41,11 +41,22 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
41
41
|
// --------------------------- custom errors ------------------------- //
|
|
42
42
|
//*********************************************************************//
|
|
43
43
|
|
|
44
|
+
/// @notice Thrown when `initialize` is called on a hook that has already been initialized.
|
|
44
45
|
error JB721TiersHook_AlreadyInitialized(uint256 projectId);
|
|
46
|
+
|
|
47
|
+
/// @notice Thrown when the configured pricing decimals exceed 18.
|
|
45
48
|
error JB721TiersHook_InvalidPricingDecimals(uint256 decimals);
|
|
49
|
+
|
|
50
|
+
/// @notice Thrown when minting reserved NFTs while the ruleset has reserve minting paused.
|
|
46
51
|
error JB721TiersHook_MintReserveNftsPaused(uint256 projectId, uint256 tierId);
|
|
52
|
+
|
|
53
|
+
/// @notice Thrown when forwarded funds must be distributed to tier splits but no split metadata was provided.
|
|
47
54
|
error JB721TiersHook_MissingSplitMetadata();
|
|
55
|
+
|
|
56
|
+
/// @notice Thrown when the hook is initialized with a project ID of zero.
|
|
48
57
|
error JB721TiersHook_NoProjectId(uint256 projectId);
|
|
58
|
+
|
|
59
|
+
/// @notice Thrown when an NFT is transferred (to a non-zero address) while the ruleset has transfers paused.
|
|
49
60
|
error JB721TiersHook_TierTransfersPaused(uint256 projectId, uint256 tokenId, address from, address to);
|
|
50
61
|
|
|
51
62
|
//*********************************************************************//
|
|
@@ -97,7 +108,7 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
97
108
|
//*********************************************************************//
|
|
98
109
|
|
|
99
110
|
/// @notice The first owner of each token ID, stored on first transfer out.
|
|
100
|
-
/// @custom:param The token ID of the NFT to get the stored first owner of.
|
|
111
|
+
/// @custom:param tokenId The token ID of the NFT to get the stored first owner of.
|
|
101
112
|
mapping(uint256 tokenId => address) internal _firstOwnerOf;
|
|
102
113
|
|
|
103
114
|
/// @notice Packed context for the pricing of this contract's tiers.
|
|
@@ -755,6 +766,7 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
755
766
|
/// @notice Before transferring an NFT, register its first owner (if necessary).
|
|
756
767
|
/// @param to The address to transfer the NFT to.
|
|
757
768
|
/// @param tokenId The token ID of the NFT to transfer.
|
|
769
|
+
/// @return from The address the NFT is being transferred from.
|
|
758
770
|
function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address from) {
|
|
759
771
|
// Get only the tier ID and transfersPausable flag (lightweight — avoids full struct construction).
|
|
760
772
|
(uint256 tierId, bool transfersPausable) =
|
|
@@ -29,24 +29,62 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
29
29
|
// --------------------------- custom errors ------------------------- //
|
|
30
30
|
//*********************************************************************//
|
|
31
31
|
|
|
32
|
+
/// @notice Thrown when an owner mint is attempted on a tier that does not allow owner minting.
|
|
32
33
|
error JB721TiersHookStore_CantMintManually(uint256 tierId);
|
|
34
|
+
|
|
35
|
+
/// @notice Thrown when removing a tier that is flagged as non-removable.
|
|
33
36
|
error JB721TiersHookStore_CantRemoveTier(uint256 tierId);
|
|
37
|
+
|
|
38
|
+
/// @notice Thrown when adding a tier with an initial supply of one and a non-zero reserve frequency, which would
|
|
39
|
+
/// deadlock reserve minting.
|
|
34
40
|
error JB721TiersHookStore_DeadlockedReserve(uint256 tierId, uint256 initialSupply, uint256 reserveFrequency);
|
|
41
|
+
|
|
42
|
+
/// @notice Thrown when a tier's discount percent exceeds the discount denominator.
|
|
35
43
|
error JB721TiersHookStore_DiscountPercentExceedsBounds(uint256 percent, uint256 limit);
|
|
44
|
+
|
|
45
|
+
/// @notice Thrown when increasing a tier's discount percent that is flagged as not allowing discount increases.
|
|
36
46
|
error JB721TiersHookStore_DiscountPercentIncreaseNotAllowed(uint256 percent, uint256 storedPercent);
|
|
47
|
+
|
|
48
|
+
/// @notice Thrown when minting more reserved NFTs than the number currently pending for the tier.
|
|
37
49
|
error JB721TiersHookStore_InsufficientPendingReserves(uint256 count, uint256 numberOfPendingReserves);
|
|
50
|
+
|
|
51
|
+
/// @notice Thrown when minting from a tier that has no remaining supply available for the mint.
|
|
38
52
|
error JB721TiersHookStore_InsufficientSupplyRemaining(uint256 tierId);
|
|
53
|
+
|
|
54
|
+
/// @notice Thrown when tiers to add are not sorted by category in ascending order.
|
|
39
55
|
error JB721TiersHookStore_InvalidCategorySortOrder(uint256 tierCategory, uint256 previousTierCategory);
|
|
56
|
+
|
|
57
|
+
/// @notice Thrown when a tier's initial supply exceeds the maximum allowed quantity.
|
|
40
58
|
error JB721TiersHookStore_InvalidQuantity(uint256 quantity, uint256 limit);
|
|
59
|
+
|
|
60
|
+
/// @notice Thrown when adding a tier with owner minting enabled while the contract's flags disallow it.
|
|
41
61
|
error JB721TiersHookStore_ManualMintingNotAllowed(uint256 tierId);
|
|
62
|
+
|
|
63
|
+
/// @notice Thrown when adding tiers would exceed the maximum number of tiers the contract can hold.
|
|
42
64
|
error JB721TiersHookStore_MaxTiersExceeded(uint256 numberOfTiers, uint256 limit);
|
|
65
|
+
|
|
66
|
+
/// @notice Thrown when adding a tier with reserves but neither a tier-specific nor default reserve beneficiary set.
|
|
43
67
|
error JB721TiersHookStore_MissingReserveBeneficiary(uint256 tierId);
|
|
68
|
+
|
|
69
|
+
/// @notice Thrown when a tier's price exceeds the amount left over to spend on it.
|
|
44
70
|
error JB721TiersHookStore_PriceExceedsAmount(uint256 price, uint256 leftoverAmount);
|
|
71
|
+
|
|
72
|
+
/// @notice Thrown when adding a tier with a reserve frequency while the contract's flags disallow it.
|
|
45
73
|
error JB721TiersHookStore_ReserveFrequencyNotAllowed(uint256 tierId);
|
|
74
|
+
|
|
75
|
+
/// @notice Thrown when a tier's split percent exceeds the total splits percent.
|
|
46
76
|
error JB721TiersHookStore_SplitPercentExceedsBounds(uint256 percent, uint256 limit);
|
|
77
|
+
|
|
78
|
+
/// @notice Thrown when operating on a tier that has been removed.
|
|
47
79
|
error JB721TiersHookStore_TierRemoved(uint256 tierId);
|
|
80
|
+
|
|
81
|
+
/// @notice Thrown when referencing a tier that does not exist.
|
|
48
82
|
error JB721TiersHookStore_UnrecognizedTier(uint256 tierId);
|
|
83
|
+
|
|
84
|
+
/// @notice Thrown when adding a tier with voting units while the contract's flags disallow it.
|
|
49
85
|
error JB721TiersHookStore_VotingUnitsNotAllowed(uint256 tierId);
|
|
86
|
+
|
|
87
|
+
/// @notice Thrown when adding a tier with an initial supply of zero.
|
|
50
88
|
error JB721TiersHookStore_ZeroInitialSupply(uint256 tierId);
|
|
51
89
|
|
|
52
90
|
//*********************************************************************//
|
|
@@ -544,13 +582,11 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
544
582
|
});
|
|
545
583
|
}
|
|
546
584
|
|
|
547
|
-
/// @notice The total cash-out weight across all outstanding NFTs (including pending reserves). This is the
|
|
548
|
-
/// denominator used to determine what fraction of a project's surplus each NFT can reclaim on cash out.
|
|
549
585
|
//*********************************************************************//
|
|
550
586
|
// -------------------------- internal views ------------------------- //
|
|
551
587
|
//*********************************************************************//
|
|
552
588
|
|
|
553
|
-
/// @notice Get the first tier ID from
|
|
589
|
+
/// @notice Get the first tier ID from a 721 contract (when sorted by category) within a provided category.
|
|
554
590
|
/// @param hook The 721 contract to get the first sorted tier ID of.
|
|
555
591
|
/// @param category The category to get the first sorted tier ID within. Send 0 for the first ID across all tiers,
|
|
556
592
|
/// which may belong to any category.
|
|
@@ -561,7 +597,7 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
561
597
|
if (id == 0) id = 1;
|
|
562
598
|
}
|
|
563
599
|
|
|
564
|
-
/// @notice Generate a token ID for
|
|
600
|
+
/// @notice Generate a token ID for a 721 given a tier ID and a token number within that tier.
|
|
565
601
|
/// @param tierId The ID of the tier to generate a token ID for.
|
|
566
602
|
/// @param tokenNumber The token number of the 721 within the tier.
|
|
567
603
|
/// @return The token ID of the 721.
|
|
@@ -644,7 +680,7 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
644
680
|
return bitmapWord.isTierIdRemoved(tierId);
|
|
645
681
|
}
|
|
646
682
|
|
|
647
|
-
/// @notice The last sorted tier ID from
|
|
683
|
+
/// @notice The last sorted tier ID from a 721 contract (when sorted by category).
|
|
648
684
|
/// @param hook The 721 contract to get the last sorted tier ID of.
|
|
649
685
|
/// @return id The last sorted tier ID.
|
|
650
686
|
function _lastSortedTierIdOf(address hook) internal view returns (uint256 id) {
|
|
@@ -771,6 +807,12 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
771
807
|
/// @param cantBeRemoved Whether this tier is permanently locked and cannot be removed once added.
|
|
772
808
|
/// @param cantIncreaseDiscountPercent Whether the discount percent can only stay the same or decrease.
|
|
773
809
|
/// @param cantBuyWithCredits Whether this tier cannot be purchased using accumulated pay credits.
|
|
810
|
+
/// @return allowOwnerMint Whether the project owner can mint from this tier directly (without paying).
|
|
811
|
+
/// @return transfersPausable Whether transfers of NFTs from this tier can be paused by the ruleset.
|
|
812
|
+
/// @return useVotingUnits Whether this tier uses a custom voting power value instead of defaulting to its price.
|
|
813
|
+
/// @return cantBeRemoved Whether this tier is permanently locked and cannot be removed once added.
|
|
814
|
+
/// @return cantIncreaseDiscountPercent Whether the discount percent can only stay the same or decrease.
|
|
815
|
+
/// @return cantBuyWithCredits Whether this tier cannot be purchased using accumulated pay credits.
|
|
774
816
|
function _unpackBools(uint8 packed)
|
|
775
817
|
internal
|
|
776
818
|
pure
|
|
@@ -31,10 +31,19 @@ abstract contract JB721Hook is ERC721, IJB721Hook {
|
|
|
31
31
|
// --------------------------- custom errors ------------------------- //
|
|
32
32
|
//*********************************************************************//
|
|
33
33
|
|
|
34
|
+
/// @notice Thrown when a cash out callback carries value, comes from a non-terminal, or targets the wrong project.
|
|
34
35
|
error JB721Hook_InvalidCashOut(address caller, uint256 contextProjectId, uint256 projectId, uint256 msgValue);
|
|
36
|
+
|
|
37
|
+
/// @notice Thrown when a pay callback comes from a non-terminal or targets the wrong project.
|
|
35
38
|
error JB721Hook_InvalidPay(address caller, uint256 contextProjectId, uint256 projectId);
|
|
39
|
+
|
|
40
|
+
/// @notice Thrown when the ETH attached to a pay callback does not match the terminal-forwarded amount.
|
|
36
41
|
error JB721Hook_InvalidPayValue(address token, uint256 msgValue, uint256 forwardedValue);
|
|
42
|
+
|
|
43
|
+
/// @notice Thrown when cashing out an NFT whose owner is not the cash-out holder.
|
|
37
44
|
error JB721Hook_UnauthorizedToken(uint256 tokenId, address holder);
|
|
45
|
+
|
|
46
|
+
/// @notice Thrown when fungible project tokens are cashed out alongside the NFTs (non-zero cash-out count).
|
|
38
47
|
error JB721Hook_UnexpectedTokenCashedOut(uint256 cashOutCount);
|
|
39
48
|
|
|
40
49
|
//*********************************************************************//
|
|
@@ -31,21 +31,55 @@ library JB721TiersHookLib {
|
|
|
31
31
|
// --------------------------- custom errors ------------------------- //
|
|
32
32
|
//*********************************************************************//
|
|
33
33
|
|
|
34
|
+
/// @notice Thrown when the fresh payment value does not cover the cost of credit-restricted tiers being minted.
|
|
34
35
|
error JB721TiersHook_CantBuyWithCredits(uint256 restrictedCost, uint256 freshValue);
|
|
36
|
+
|
|
37
|
+
/// @notice Thrown when funds are left over after minting but overspending is not allowed.
|
|
35
38
|
error JB721TiersHook_Overspending(uint256 leftoverAmount);
|
|
39
|
+
|
|
40
|
+
/// @notice Thrown when leftover funds must be routed to the project but it has no primary terminal for the token.
|
|
36
41
|
error JB721TiersHookLib_NoTerminalForLeftover(uint256 projectId, address token, uint256 leftoverAmount);
|
|
42
|
+
|
|
43
|
+
/// @notice Thrown when the per-tier split amounts do not sum to the total amount forwarded by the terminal.
|
|
37
44
|
error JB721TiersHookLib_SplitAmountMismatch(uint256 expectedAmount, uint256 actualAmount);
|
|
45
|
+
|
|
46
|
+
/// @notice Thrown when routing leftover funds to the project's terminal reverts or under-consumes the allowance.
|
|
38
47
|
error JB721TiersHookLib_SplitFallbackFailed(uint256 projectId, address token, uint256 amount, bytes reason);
|
|
48
|
+
|
|
49
|
+
/// @notice Thrown when the decoded split metadata's tier ID and amount arrays have different lengths.
|
|
39
50
|
error JB721TiersHookLib_SplitMetadataLengthMismatch(uint256 tierIdCount, uint256 amountCount);
|
|
51
|
+
|
|
52
|
+
/// @notice Thrown when the ERC-20 amount actually received does not match the expected transfer amount.
|
|
40
53
|
error JB721TiersHookLib_TokenTransferAmountMismatch(uint256 expectedAmount, uint256 receivedAmount);
|
|
41
54
|
|
|
42
55
|
//*********************************************************************//
|
|
43
56
|
// ------------------------------- events ---------------------------- //
|
|
44
57
|
//*********************************************************************//
|
|
45
58
|
|
|
59
|
+
/// @notice Emitted when a new tier is added.
|
|
60
|
+
/// @param tierId The ID of the tier that was added.
|
|
61
|
+
/// @param tier The configuration of the tier that was added.
|
|
62
|
+
/// @param caller The address that called the function.
|
|
46
63
|
event AddTier(uint256 indexed tierId, JB721TierConfig tier, address caller);
|
|
64
|
+
|
|
65
|
+
/// @notice Emitted when a tier is removed.
|
|
66
|
+
/// @param tierId The ID of the tier that was removed.
|
|
67
|
+
/// @param caller The address that called the function.
|
|
47
68
|
event RemoveTier(uint256 indexed tierId, address caller);
|
|
69
|
+
|
|
70
|
+
/// @notice Emitted when a tier's discount percent is set.
|
|
71
|
+
/// @param tierId The ID of the tier whose discount percent was set.
|
|
72
|
+
/// @param discountPercent The new discount percent.
|
|
73
|
+
/// @param caller The address that called the function.
|
|
48
74
|
event SetDiscountPercent(uint256 indexed tierId, uint256 discountPercent, address caller);
|
|
75
|
+
|
|
76
|
+
/// @notice Emitted when a split payout reverts during distribution. The failed split's funds route to the
|
|
77
|
+
/// project's balance.
|
|
78
|
+
/// @param projectId The project ID the split belongs to.
|
|
79
|
+
/// @param split The split that reverted.
|
|
80
|
+
/// @param amount The amount that was paid out.
|
|
81
|
+
/// @param reason The revert reason bytes.
|
|
82
|
+
/// @param caller The address that called the function.
|
|
49
83
|
event SplitPayoutReverted(uint256 indexed projectId, JBSplit split, uint256 amount, bytes reason, address caller);
|
|
50
84
|
|
|
51
85
|
//*********************************************************************//
|