@bananapus/721-hook-v6 0.0.70 → 0.0.73
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 +5 -4
- package/package.json +6 -6
- package/references/operations.md +2 -2
- package/src/JB721Checkpoints.sol +229 -49
- package/src/JB721TiersHook.sol +3 -5
- package/src/JB721TiersHookProjectDeployer.sol +2 -4
- package/src/JB721TiersHookStore.sol +2 -4
- package/src/abstract/JB721Hook.sol +3 -6
- package/src/interfaces/IJB721Checkpoints.sol +50 -20
- package/src/interfaces/IJB721TiersHook.sol +3 -5
- package/src/libraries/JB721TiersHookLib.sol +1 -2
- package/src/structs/JB721TiersRulesetMetadata.sol +1 -2
- package/src/structs/JBLaunchProjectConfig.sol +1 -2
- package/src/structs/JBPayDataHookRulesetConfig.sol +7 -10
- package/src/structs/JBPayDataHookRulesetMetadata.sol +2 -4
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
- [AUDIT_INSTRUCTIONS.md](./AUDIT_INSTRUCTIONS.md) — what to focus on for a security audit and how to start.
|
|
11
11
|
- [SKILLS.md](./SKILLS.md) — implementation nuances, gotchas, and reading order for working on this codebase.
|
|
12
12
|
- [STYLE_GUIDE.md](./STYLE_GUIDE.md) — Solidity conventions and repo layout used across the V6 ecosystem.
|
|
13
|
-
- [CHANGELOG.md](./CHANGELOG.md)
|
|
13
|
+
- [CHANGELOG.md](./CHANGELOG.md) - V5 to V6 ABI and behavior deltas.
|
|
14
14
|
- [references/runtime.md](./references/runtime.md) — contract roles, the runtime pay and cash-out path, and high-risk areas.
|
|
15
15
|
- [references/operations.md](./references/operations.md) — deployment surface, change checklist, and common failure modes.
|
|
16
16
|
|
|
@@ -38,7 +38,7 @@ This repo does more than "mint NFTs on pay." It changes how payment value, tier
|
|
|
38
38
|
| `JB721TiersHookDeployer` | Clone factory for deploying a hook for an existing project. |
|
|
39
39
|
| `JB721TiersHookProjectDeployer` | Convenience deployer for launching a project with a hook already wired in. |
|
|
40
40
|
| `JB721Hook` | Abstract base for 721 pay and cash-out hook behavior. |
|
|
41
|
-
| `JB721Checkpoints` | Per-hook IVotes checkpoint module. Tracks historical owner checkpoints, per-tier
|
|
41
|
+
| `JB721Checkpoints` | Per-hook IVotes checkpoint module. Tracks historical owner checkpoints, per-tier owner-tracked voting units (`getPastTierVotingUnits`), global active vote totals (`getPastTotalActiveVotes`), per-tier active vote totals (`getPastTotalTierActiveVotes`), and per-account active tier voting units (`getPastAccountTierActiveVotes`). |
|
|
42
42
|
|
|
43
43
|
## Mental model
|
|
44
44
|
|
|
@@ -64,8 +64,9 @@ If a bug affects supply, reserve minting, or tier lookup, it usually lives in th
|
|
|
64
64
|
- custom token URI resolvers should be treated as part of the trusted surface
|
|
65
65
|
- adding a 721 hook through a deployer is easy; carrying the right ruleset behavior forward is where mistakes happen
|
|
66
66
|
- projects should be explicit about whether the hook affects pay, cash out, or only metadata-facing paths
|
|
67
|
-
- per-tier
|
|
68
|
-
- active delegated vote totals are queryable via `getPastTotalActiveVotes(blockNumber)`
|
|
67
|
+
- per-tier owner-tracked voting units are queryable via `getPastTierVotingUnits(tierId, blockNumber)`: mints, transfers, and burns write ownership history, so the trace follows owned units regardless of delegation
|
|
68
|
+
- active delegated vote totals are queryable globally via `getPastTotalActiveVotes(blockNumber)` / `getTotalActiveVotes()` and per tier via `getPastTotalTierActiveVotes(tierId, blockNumber)` / `getTotalTierActiveVotes(tierId)`. These totals include only voting units held by accounts with a nonzero delegate, so a token in undelegated custody does not count and returned tokens become active again if the holder's delegation is still set.
|
|
69
|
+
- per-account active tier voting units are queryable via `getPastAccountTierActiveVotes(account, tierId, blockNumber)`. This follows the account holding the tier units, not the delegate receiving voting power, so reward distributors can cap tier-scoped claims against the holder's active units even when votes are delegated to another address.
|
|
69
70
|
|
|
70
71
|
## Where state lives
|
|
71
72
|
|
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.73",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -25,12 +25,12 @@
|
|
|
25
25
|
"artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'nana-721-hook-v6'"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@bananapus/address-registry-v6": "^0.0.
|
|
29
|
-
"@bananapus/core-v6": "^0.0.
|
|
30
|
-
"@bananapus/ownable-v6": "^0.0.
|
|
31
|
-
"@bananapus/permission-ids-v6": "^0.0.
|
|
28
|
+
"@bananapus/address-registry-v6": "^0.0.34",
|
|
29
|
+
"@bananapus/core-v6": "^0.0.86",
|
|
30
|
+
"@bananapus/ownable-v6": "^0.0.37",
|
|
31
|
+
"@bananapus/permission-ids-v6": "^0.0.30",
|
|
32
32
|
"@openzeppelin/contracts": "5.6.1",
|
|
33
|
-
"@prb/math": "4.1.
|
|
33
|
+
"@prb/math": "4.1.2",
|
|
34
34
|
"solady": "0.1.26"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
package/references/operations.md
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
- If you edit tier config or metadata behavior, inspect the corresponding structs and interfaces in `src/structs/` and `src/interfaces/`.
|
|
13
13
|
- If you edit reserve behavior, verify pending reserve counts, default reserve beneficiary semantics, and cash-out denominator effects together.
|
|
14
14
|
- If you edit discount behavior, verify mint price and cash-out weight separately. They are intentionally not the same quantity.
|
|
15
|
-
- If you touch checkpoint, `onTransfer`, or `delegate` behavior, verify the per-tier eligible-voting-units trace (`_tierEligibleUnitsOf`, read via `getPastTierVotingUnits`) still moves only on eligibility changes: increment on
|
|
16
|
-
- Also verify the active delegated vote
|
|
15
|
+
- If you touch checkpoint, `onTransfer`, or `delegate` behavior, verify the per-tier eligible-voting-units trace (`_tierEligibleUnitsOf`, read via `getPastTierVotingUnits`) still moves only on eligibility changes: increment on mint or first checkpoint backfill, decrement on burn, and stay unchanged on ordinary transfers. Keep it in lockstep with `ownerOfAt` eligibility.
|
|
16
|
+
- Also verify the active delegated vote traces (`_activeSupplyCheckpoints`, `_tierActiveSupplyCheckpointsOf`, and `_accountTierActiveVotesOf`) move only when voting units enter or leave nonzero delegation. They are separate from owner-based tier reward eligibility.
|
|
17
17
|
- If you touch permissions, verify the caller path and permission constants still line up with the downstream ecosystem package that defines them.
|
|
18
18
|
- If you touch URI behavior, confirm whether the issue belongs in this repo or in a downstream resolver contract that the hook calls.
|
|
19
19
|
|
package/src/JB721Checkpoints.sol
CHANGED
|
@@ -29,7 +29,7 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
29
29
|
/// @notice Thrown when `initialize` is called on a module whose hook has already been set.
|
|
30
30
|
error JB721Checkpoints_AlreadyInitialized(address hook);
|
|
31
31
|
|
|
32
|
-
/// @notice Thrown when the caller tries to
|
|
32
|
+
/// @notice Thrown when the caller tries to backfill a token they do not currently own.
|
|
33
33
|
error JB721Checkpoints_NotOwner(uint256 tokenId, address caller);
|
|
34
34
|
|
|
35
35
|
/// @notice Thrown when a hook-only function is called by an address other than the module's hook.
|
|
@@ -40,6 +40,7 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
40
40
|
//*********************************************************************//
|
|
41
41
|
|
|
42
42
|
/// @notice The store that holds tier and voting data for the hook's NFTs.
|
|
43
|
+
/// @dev All tier lookups are scoped by `hook`; this immutable only identifies the shared store contract.
|
|
43
44
|
IJB721TiersHookStore public immutable override STORE;
|
|
44
45
|
|
|
45
46
|
//*********************************************************************//
|
|
@@ -47,33 +48,50 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
47
48
|
//*********************************************************************//
|
|
48
49
|
|
|
49
50
|
/// @notice The hook that this module tracks voting power for.
|
|
51
|
+
/// @dev Clones set this once in `initialize`; the implementation uses `address(1)` as a locked sentinel.
|
|
50
52
|
address public override hook;
|
|
51
53
|
|
|
52
54
|
//*********************************************************************//
|
|
53
55
|
// -------------------- internal stored properties ------------------- //
|
|
54
56
|
//*********************************************************************//
|
|
55
57
|
|
|
56
|
-
/// @notice Checkpointed
|
|
58
|
+
/// @notice Checkpointed active voting units per account and tier.
|
|
59
|
+
/// @dev Maintained only for units held by accounts with nonzero delegates. Undelegated custody is not checkpointed
|
|
60
|
+
/// in this trace because those units are inactive for active-vote reward accounting.
|
|
61
|
+
/// @custom:param account The account to get historical active tier voting units for.
|
|
62
|
+
/// @custom:param tierId The tier to get historical active voting units for.
|
|
63
|
+
mapping(address account => mapping(uint256 tierId => Checkpoints.Trace160)) internal _accountTierActiveVotesOf;
|
|
64
|
+
|
|
65
|
+
/// @notice Checkpointed token owners for historical reward eligibility.
|
|
66
|
+
/// @dev Mint and transfer hooks write this automatically; `delegate` only backfills missing pre-upgrade history.
|
|
57
67
|
/// @custom:param tokenId The token ID to get historical owner checkpoints for.
|
|
58
68
|
mapping(uint256 tokenId => Checkpoints.Trace160) internal _ownerCheckpointsOf;
|
|
59
69
|
|
|
60
|
-
/// @notice Checkpointed total
|
|
61
|
-
///
|
|
62
|
-
///
|
|
63
|
-
/// @custom:param tierId The tier to get the historical
|
|
70
|
+
/// @notice Checkpointed total active voting units per tier.
|
|
71
|
+
/// @dev Maintained when delegation changes activate/deactivate all of an account's tier units, and when transfers
|
|
72
|
+
/// move one token's tier units between delegated and undelegated custody.
|
|
73
|
+
/// @custom:param tierId The tier to get the historical delegated voting units for.
|
|
74
|
+
mapping(uint256 tierId => Checkpoints.Trace160) internal _tierActiveSupplyCheckpointsOf;
|
|
75
|
+
|
|
76
|
+
/// @notice Checkpointed total owner-tracked voting units per tier.
|
|
77
|
+
/// @dev Increased on mint or backfill and decreased on burn. Transfers keep the total unchanged because the token
|
|
78
|
+
/// still has a nonzero owner.
|
|
79
|
+
/// @custom:param tierId The tier to get the historical owner-tracked voting units for.
|
|
64
80
|
mapping(uint256 tierId => Checkpoints.Trace160) internal _tierEligibleUnitsOf;
|
|
65
81
|
|
|
66
82
|
//*********************************************************************//
|
|
67
83
|
// -------------------- private stored properties -------------------- //
|
|
68
84
|
//*********************************************************************//
|
|
69
85
|
|
|
70
|
-
/// @notice
|
|
86
|
+
/// @notice Checkpointed total voting units held by accounts with nonzero delegates.
|
|
87
|
+
/// @dev Maintained by `_delegate` and `_transferVotingUnits` using the same clock as OZ `Votes`.
|
|
71
88
|
Checkpoints.Trace208 private _activeSupplyCheckpoints;
|
|
72
89
|
|
|
73
90
|
//*********************************************************************//
|
|
74
91
|
// -------------------------- constructor ---------------------------- //
|
|
75
92
|
//*********************************************************************//
|
|
76
93
|
|
|
94
|
+
/// @notice Initializes the checkpoint implementation and locks it against direct use.
|
|
77
95
|
/// @dev The implementation contract is initialized in the constructor to prevent direct use. Clones are initialized
|
|
78
96
|
/// via `initialize()`.
|
|
79
97
|
/// @param store The store that holds tier data for each hook's NFTs.
|
|
@@ -86,26 +104,25 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
86
104
|
// ---------------------- external transactions ---------------------- //
|
|
87
105
|
//*********************************************************************//
|
|
88
106
|
|
|
89
|
-
/// @notice Delegates voting power and
|
|
90
|
-
/// @dev
|
|
91
|
-
///
|
|
92
|
-
/// distribution. The existing `delegate(address)` from OZ Votes still works for pure delegation without enrollment.
|
|
107
|
+
/// @notice Delegates voting power and backfills ownership history for listed tokens if needed.
|
|
108
|
+
/// @dev Mint and transfer hooks normally write owner checkpoints automatically. The token ID list keeps
|
|
109
|
+
/// pre-upgrade or otherwise uncheckpointed tokens recoverable while preserving the owner-only authorization check.
|
|
93
110
|
/// @param delegatee The address to delegate voting power to. Use your own address for self-delegation.
|
|
94
|
-
/// @param tokenIds The token IDs
|
|
111
|
+
/// @param tokenIds The token IDs whose owner checkpoints should be backfilled if missing.
|
|
95
112
|
function delegate(address delegatee, uint256[] calldata tokenIds) external override {
|
|
96
113
|
// Delegate voting power (reuses OZ Votes internals).
|
|
97
114
|
_delegate({account: msg.sender, delegatee: delegatee});
|
|
98
115
|
|
|
99
|
-
// Write per-token owner checkpoints for
|
|
116
|
+
// Write any missing per-token owner checkpoints for historical reward eligibility.
|
|
100
117
|
for (uint256 i; i < tokenIds.length;) {
|
|
101
118
|
uint256 tokenId = tokenIds[i];
|
|
102
119
|
|
|
103
|
-
// Only the current owner can
|
|
120
|
+
// Only the current owner can backfill their token's ownership checkpoint.
|
|
104
121
|
if (IERC721(hook).ownerOf(tokenId) != msg.sender) {
|
|
105
122
|
revert JB721Checkpoints_NotOwner({tokenId: tokenId, caller: msg.sender});
|
|
106
123
|
}
|
|
107
124
|
|
|
108
|
-
// Write an owner checkpoint if the token has none yet, and
|
|
125
|
+
// Write an owner checkpoint if the token has none yet, and track its tier voting units.
|
|
109
126
|
if (_ownerCheckpointsOf[tokenId].length() == 0) {
|
|
110
127
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
111
128
|
_ownerCheckpointsOf[tokenId].push({key: uint96(block.number), value: uint160(msg.sender)});
|
|
@@ -139,54 +156,129 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
139
156
|
function onTransfer(address from, address to, uint256 tokenId) external override {
|
|
140
157
|
if (msg.sender != hook) revert JB721Checkpoints_Unauthorized({caller: msg.sender, hook: hook});
|
|
141
158
|
|
|
159
|
+
// Look up this token's tier ID and voting units once; both the owner and active traces need them.
|
|
160
|
+
uint256 tierId = STORE.tierIdOfToken(tokenId);
|
|
161
|
+
|
|
142
162
|
// Look up this token's tier voting units (lightweight getter — avoids full tier struct construction).
|
|
143
163
|
uint256 votingUnits = STORE.tierVotingUnitsOfTokenId({hook: hook, tokenId: tokenId});
|
|
144
164
|
|
|
145
|
-
//
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
165
|
+
// Keep a reference to the token's historical owner trace.
|
|
166
|
+
Checkpoints.Trace160 storage ownerTrace = _ownerCheckpointsOf[tokenId];
|
|
167
|
+
|
|
168
|
+
// Existing deployments may have tokens that predate mint checkpointing; detect the first written owner.
|
|
169
|
+
bool wasEligible = ownerTrace.length() != 0;
|
|
150
170
|
|
|
171
|
+
// Mints, transfers, and burns all write ownership history so reward claims can prove snapshot ownership.
|
|
172
|
+
if (to != address(0) || from != address(0)) {
|
|
151
173
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
152
174
|
ownerTrace.push({key: uint96(block.number), value: uint160(to)});
|
|
175
|
+
}
|
|
153
176
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
} else if (!wasEligible) {
|
|
162
|
-
// First transfer of a never-enrolled token makes it eligible: add the tier's units.
|
|
163
|
-
_updateTierEligibleUnits({tierId: STORE.tierIdOfToken(tokenId), amount: votingUnits, increase: true});
|
|
177
|
+
if (from == address(0) && to != address(0)) {
|
|
178
|
+
// Mint: add the token's tier units to the owner-tracked tier supply.
|
|
179
|
+
_updateTierEligibleUnits({tierId: tierId, amount: votingUnits, increase: true});
|
|
180
|
+
} else if (to == address(0)) {
|
|
181
|
+
// Burn: remove the tier's units only if the token was already part of the owner-tracked supply.
|
|
182
|
+
if (wasEligible) {
|
|
183
|
+
_updateTierEligibleUnits({tierId: tierId, amount: votingUnits, increase: false});
|
|
164
184
|
}
|
|
185
|
+
} else if (!wasEligible) {
|
|
186
|
+
// First transfer of a pre-upgrade uncheckpointed token makes its ownership history trackable.
|
|
187
|
+
_updateTierEligibleUnits({tierId: tierId, amount: votingUnits, increase: true});
|
|
165
188
|
}
|
|
166
189
|
|
|
190
|
+
// The tier active total decreases if units leave an account that already has a nonzero delegate.
|
|
191
|
+
bool decreaseTierActiveVotes = from != address(0) && delegates(from) != address(0);
|
|
192
|
+
|
|
193
|
+
// The tier active total increases if units arrive at an account that already has a nonzero delegate.
|
|
194
|
+
bool increaseTierActiveVotes = to != address(0) && delegates(to) != address(0);
|
|
195
|
+
|
|
167
196
|
// Move checkpointed voting power from the previous owner to the new owner.
|
|
168
197
|
_transferVotingUnits({from: from, to: to, amount: votingUnits});
|
|
198
|
+
|
|
199
|
+
// If the sender was delegated, remove this token's tier units from the sender's active tier balance.
|
|
200
|
+
if (decreaseTierActiveVotes) {
|
|
201
|
+
_adjustAccountTierActiveVotes({account: from, tierId: tierId, amount: votingUnits, increase: false});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// If the receiver is delegated, add this token's tier units to the receiver's active tier balance.
|
|
205
|
+
if (increaseTierActiveVotes) {
|
|
206
|
+
_adjustAccountTierActiveVotes({account: to, tierId: tierId, amount: votingUnits, increase: true});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// If both sides are delegated or both sides are undelegated, this tier's active total is unchanged.
|
|
210
|
+
if (decreaseTierActiveVotes != increaseTierActiveVotes) {
|
|
211
|
+
// Otherwise apply the one-sided active-tier delta implied by the receiver's delegated status.
|
|
212
|
+
_adjustTotalTierActiveVotes({tierId: tierId, amount: votingUnits, increase: increaseTierActiveVotes});
|
|
213
|
+
}
|
|
169
214
|
}
|
|
170
215
|
|
|
171
216
|
//*********************************************************************//
|
|
172
217
|
// ----------------------- external views ---------------------------- //
|
|
173
218
|
//*********************************************************************//
|
|
174
219
|
|
|
175
|
-
/// @notice The
|
|
176
|
-
/// @
|
|
220
|
+
/// @notice The delegated voting units held by an account in a tier at a past block.
|
|
221
|
+
/// @dev Counts only tier voting units held by `account` while `account` had a nonzero delegate.
|
|
222
|
+
/// @param account The account to get the delegated tier voting units of.
|
|
223
|
+
/// @param tierId The tier to get the delegated voting units of.
|
|
224
|
+
/// @param blockNumber The past block number to look up.
|
|
225
|
+
/// @return activeVotes The account's delegated tier voting units at `blockNumber`.
|
|
226
|
+
function getPastAccountTierActiveVotes(
|
|
227
|
+
address account,
|
|
228
|
+
uint256 tierId,
|
|
229
|
+
uint256 blockNumber
|
|
230
|
+
)
|
|
231
|
+
external
|
|
232
|
+
view
|
|
233
|
+
override
|
|
234
|
+
returns (uint256 activeVotes)
|
|
235
|
+
{
|
|
236
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
237
|
+
activeVotes =
|
|
238
|
+
_accountTierActiveVotesOf[account][tierId].upperLookupRecent(uint96(_validateTimepoint(blockNumber)));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/// @notice The total owner-checkpointed voting units of a tier at a past block.
|
|
242
|
+
/// @param tierId The tier to get the owner-checkpointed voting units of.
|
|
177
243
|
/// @param blockNumber The block number to look up (must be strictly in the past).
|
|
178
|
-
/// @return The tier's
|
|
179
|
-
function getPastTierVotingUnits(
|
|
244
|
+
/// @return votingUnits The tier's owner-checkpointed voting units at `blockNumber`.
|
|
245
|
+
function getPastTierVotingUnits(
|
|
246
|
+
uint256 tierId,
|
|
247
|
+
uint256 blockNumber
|
|
248
|
+
)
|
|
249
|
+
external
|
|
250
|
+
view
|
|
251
|
+
override
|
|
252
|
+
returns (uint256 votingUnits)
|
|
253
|
+
{
|
|
180
254
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
181
|
-
|
|
255
|
+
votingUnits = _tierEligibleUnitsOf[tierId].upperLookupRecent(uint96(_validateTimepoint(blockNumber)));
|
|
182
256
|
}
|
|
183
257
|
|
|
184
258
|
/// @notice The total delegated voting units at a past block.
|
|
185
259
|
/// @dev This tracks delegated vote participation and is separate from tier reward eligibility.
|
|
186
|
-
/// @param
|
|
187
|
-
/// @return activeVotes The total voting units delegated to nonzero delegates at `
|
|
188
|
-
function getPastTotalActiveVotes(uint256
|
|
189
|
-
activeVotes = _activeSupplyCheckpoints.upperLookupRecent(_validateTimepoint(
|
|
260
|
+
/// @param blockNumber The past block number to look up.
|
|
261
|
+
/// @return activeVotes The total voting units delegated to nonzero delegates at `blockNumber`.
|
|
262
|
+
function getPastTotalActiveVotes(uint256 blockNumber) external view override returns (uint256 activeVotes) {
|
|
263
|
+
activeVotes = _activeSupplyCheckpoints.upperLookupRecent(_validateTimepoint(blockNumber));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/// @notice The total delegated voting units of a tier at a past block.
|
|
267
|
+
/// @dev Counts only tier voting units held by accounts with a nonzero delegate.
|
|
268
|
+
/// @param tierId The tier to get the delegated voting units of.
|
|
269
|
+
/// @param blockNumber The past block number to look up.
|
|
270
|
+
/// @return activeVotes The tier's delegated voting units at `blockNumber`.
|
|
271
|
+
function getPastTotalTierActiveVotes(
|
|
272
|
+
uint256 tierId,
|
|
273
|
+
uint256 blockNumber
|
|
274
|
+
)
|
|
275
|
+
external
|
|
276
|
+
view
|
|
277
|
+
override
|
|
278
|
+
returns (uint256 activeVotes)
|
|
279
|
+
{
|
|
280
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
281
|
+
activeVotes = _tierActiveSupplyCheckpointsOf[tierId].upperLookupRecent(uint96(_validateTimepoint(blockNumber)));
|
|
190
282
|
}
|
|
191
283
|
|
|
192
284
|
/// @notice The current total delegated voting units.
|
|
@@ -196,23 +288,29 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
196
288
|
activeVotes = _activeSupplyCheckpoints.latest();
|
|
197
289
|
}
|
|
198
290
|
|
|
291
|
+
/// @notice The current total delegated voting units of a tier.
|
|
292
|
+
/// @param tierId The tier to get the current delegated voting units of.
|
|
293
|
+
/// @return activeVotes The tier's current delegated voting units.
|
|
294
|
+
function getTotalTierActiveVotes(uint256 tierId) external view override returns (uint256 activeVotes) {
|
|
295
|
+
activeVotes = _tierActiveSupplyCheckpointsOf[tierId].latest();
|
|
296
|
+
}
|
|
297
|
+
|
|
199
298
|
/// @notice The owner of an NFT at a past block.
|
|
200
|
-
/// @dev Returns `address(0)`
|
|
201
|
-
/// transferred. Unenrolled tokens are ineligible for snapshot-based distribution.
|
|
299
|
+
/// @dev Returns `address(0)` if no ownership checkpoint exists or the query predates the first checkpoint.
|
|
202
300
|
/// @param tokenId The token ID of the NFT to get the historical owner of.
|
|
203
301
|
/// @param blockNumber The block number to look up.
|
|
204
|
-
/// @return The owner of the token at `blockNumber`, or zero if
|
|
205
|
-
function ownerOfAt(uint256 tokenId, uint256 blockNumber) external view override returns (address) {
|
|
302
|
+
/// @return owner The owner of the token at `blockNumber`, or zero if no owner is proven at that block.
|
|
303
|
+
function ownerOfAt(uint256 tokenId, uint256 blockNumber) external view override returns (address owner) {
|
|
206
304
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
207
305
|
uint96 blockNumber96 = uint96(blockNumber);
|
|
208
306
|
|
|
209
307
|
Checkpoints.Trace160 storage checkpoints = _ownerCheckpointsOf[tokenId];
|
|
210
308
|
uint256 checkpointCount = checkpoints.length();
|
|
211
309
|
|
|
212
|
-
// No checkpoints
|
|
310
|
+
// No checkpoints means no historical owner can be proven for this token.
|
|
213
311
|
if (checkpointCount == 0) return address(0);
|
|
214
312
|
|
|
215
|
-
// Query is before the first checkpoint
|
|
313
|
+
// Query is before the first checkpoint, so this token had no proven owner at that block.
|
|
216
314
|
if (checkpoints.at(0)._key > blockNumber96) return address(0);
|
|
217
315
|
|
|
218
316
|
return address(uint160(checkpoints.upperLookupRecent(blockNumber96)));
|
|
@@ -242,9 +340,15 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
242
340
|
if (oldDelegate == address(0) && delegatee != address(0)) {
|
|
243
341
|
// Add the account's voting units to the checkpointed active total.
|
|
244
342
|
_updateActiveVotes({amount: votingUnits, increase: true});
|
|
343
|
+
|
|
344
|
+
// Add the account's voting units to each tier-level active total it currently contributes to.
|
|
345
|
+
_applyAccountDelegationToTierActiveVotes({account: account, increase: true});
|
|
245
346
|
} else if (oldDelegate != address(0) && delegatee == address(0)) {
|
|
246
347
|
// If the account had a delegate and now has none, its voting units just became inactive.
|
|
247
348
|
_updateActiveVotes({amount: votingUnits, increase: false});
|
|
349
|
+
|
|
350
|
+
// Remove the account's voting units from each tier-level active total it currently contributes to.
|
|
351
|
+
_applyAccountDelegationToTierActiveVotes({account: account, increase: false});
|
|
248
352
|
}
|
|
249
353
|
}
|
|
250
354
|
|
|
@@ -279,8 +383,8 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
279
383
|
/// @notice Returns the total voting units held by an account (across all tiers).
|
|
280
384
|
/// @dev Called by OZ Votes when re-delegating to compute the account's total voting units.
|
|
281
385
|
/// @param account The address to get the voting units of.
|
|
282
|
-
/// @return The total voting units the account holds.
|
|
283
|
-
function _getVotingUnits(address account) internal view override returns (uint256) {
|
|
386
|
+
/// @return votingUnits The total voting units the account holds.
|
|
387
|
+
function _getVotingUnits(address account) internal view override returns (uint256 votingUnits) {
|
|
284
388
|
return STORE.votingUnitsOf({hook: hook, account: account});
|
|
285
389
|
}
|
|
286
390
|
|
|
@@ -288,6 +392,74 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
288
392
|
// ------------------------ private helpers -------------------------- //
|
|
289
393
|
//*********************************************************************//
|
|
290
394
|
|
|
395
|
+
/// @notice Add or remove units from an account's active-voting-units checkpoint for a tier at the current block.
|
|
396
|
+
/// @param account The account whose active-voting-units trace should be updated.
|
|
397
|
+
/// @param tierId The tier whose active-voting-units trace should be updated.
|
|
398
|
+
/// @param amount The voting units to add or remove.
|
|
399
|
+
/// @param increase Whether to add `amount`; if false, `amount` is removed.
|
|
400
|
+
function _adjustAccountTierActiveVotes(address account, uint256 tierId, uint256 amount, bool increase) private {
|
|
401
|
+
// Ignore zero-unit updates because they do not change the account's active tier total.
|
|
402
|
+
if (amount == 0) return;
|
|
403
|
+
|
|
404
|
+
// Keep a reference to the account's active-voting-units trace for this tier.
|
|
405
|
+
Checkpoints.Trace160 storage trace = _accountTierActiveVotesOf[account][tierId];
|
|
406
|
+
|
|
407
|
+
// Calculate the next account-tier active total from its latest checkpointed value.
|
|
408
|
+
uint256 updated = increase ? trace.latest() + amount : trace.latest() - amount;
|
|
409
|
+
|
|
410
|
+
// Write the new account-tier active total at the current block.
|
|
411
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
412
|
+
trace.push({key: uint96(block.number), value: uint160(updated)});
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/// @notice Add or remove units from a tier's active-voting-units checkpoint at the current block.
|
|
416
|
+
/// @param tierId The tier whose active-voting-units trace to update.
|
|
417
|
+
/// @param amount The voting units to add or remove.
|
|
418
|
+
/// @param increase Whether to add `amount`; if false, `amount` is removed.
|
|
419
|
+
function _adjustTotalTierActiveVotes(uint256 tierId, uint256 amount, bool increase) private {
|
|
420
|
+
// Ignore zero-unit updates because they do not change this tier's active total.
|
|
421
|
+
if (amount == 0) return;
|
|
422
|
+
|
|
423
|
+
// Keep a reference to the tier's active-voting-units trace.
|
|
424
|
+
Checkpoints.Trace160 storage trace = _tierActiveSupplyCheckpointsOf[tierId];
|
|
425
|
+
|
|
426
|
+
// Calculate the next tier active total by adding or subtracting from the latest checkpointed value.
|
|
427
|
+
uint256 updated = increase ? trace.latest() + amount : trace.latest() - amount;
|
|
428
|
+
|
|
429
|
+
// Write the new tier active total at the current block.
|
|
430
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
431
|
+
trace.push({key: uint96(block.number), value: uint160(updated)});
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/// @notice Apply an account delegation change to every tier-level active total the account contributes to.
|
|
435
|
+
/// @dev Delegation is account-wide in OZ Votes, so changing an account's delegate activates or deactivates every
|
|
436
|
+
/// tier balance currently held by the account. Transfer hooks handle one-token tier deltas separately.
|
|
437
|
+
/// @param account The account whose tier voting units should be added or removed.
|
|
438
|
+
/// @param increase Whether to add `account`'s tier voting units; if false, they are removed.
|
|
439
|
+
function _applyAccountDelegationToTierActiveVotes(address account, bool increase) private {
|
|
440
|
+
// Read the largest tier ID once; tier IDs are sequential and 1-indexed for each hook.
|
|
441
|
+
uint256 tierId = STORE.maxTierIdOf(hook);
|
|
442
|
+
|
|
443
|
+
// Walk each tier from max to 1 so empty hooks skip cleanly when maxTierId is zero.
|
|
444
|
+
while (tierId != 0) {
|
|
445
|
+
// Read only this account's units for the tier; empty tiers return zero and are ignored below.
|
|
446
|
+
uint256 tierVotingUnits = STORE.tierVotingUnitsOf({hook: hook, account: account, tierId: tierId});
|
|
447
|
+
|
|
448
|
+
// Only write checkpoints for tiers whose active totals actually change.
|
|
449
|
+
if (tierVotingUnits != 0) {
|
|
450
|
+
_adjustTotalTierActiveVotes({tierId: tierId, amount: tierVotingUnits, increase: increase});
|
|
451
|
+
_adjustAccountTierActiveVotes({
|
|
452
|
+
account: account, tierId: tierId, amount: tierVotingUnits, increase: increase
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
unchecked {
|
|
457
|
+
// The loop condition proves tierId is nonzero, so the decrement cannot underflow.
|
|
458
|
+
--tierId;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
291
463
|
/// @notice Update the checkpointed total of delegated voting units.
|
|
292
464
|
/// @dev Writes at most one active-total checkpoint at the current OZ clock. A zero amount is ignored so zero-value
|
|
293
465
|
/// delegation or transfer hooks do not create empty checkpoints.
|
|
@@ -305,13 +477,21 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
305
477
|
_activeSupplyCheckpoints.push({key: clock(), value: SafeCast.toUint208(updated)});
|
|
306
478
|
}
|
|
307
479
|
|
|
308
|
-
/// @notice Add or remove units from a tier's
|
|
309
|
-
/// @param tierId The tier whose
|
|
480
|
+
/// @notice Add or remove units from a tier's owner-tracked voting-units checkpoint at the current block.
|
|
481
|
+
/// @param tierId The tier whose owner-tracked voting-units trace to update.
|
|
310
482
|
/// @param amount The voting units to add or remove.
|
|
311
483
|
/// @param increase Whether to add `amount`; if false, `amount` is removed.
|
|
312
484
|
function _updateTierEligibleUnits(uint256 tierId, uint256 amount, bool increase) private {
|
|
485
|
+
// Ignore zero-unit updates because they do not change this tier's owner-tracked total.
|
|
486
|
+
if (amount == 0) return;
|
|
487
|
+
|
|
488
|
+
// Keep a reference to the tier's owner-tracked voting-units trace.
|
|
313
489
|
Checkpoints.Trace160 storage trace = _tierEligibleUnitsOf[tierId];
|
|
490
|
+
|
|
491
|
+
// Calculate the next owner-tracked total by adding or subtracting from the latest checkpointed value.
|
|
314
492
|
uint256 updated = increase ? trace.latest() + amount : trace.latest() - amount;
|
|
493
|
+
|
|
494
|
+
// Write the new owner-tracked total at the current block.
|
|
315
495
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
316
496
|
trace.push({key: uint96(block.number), value: uint160(updated)});
|
|
317
497
|
}
|
package/src/JB721TiersHook.sol
CHANGED
|
@@ -244,8 +244,7 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
244
244
|
/// @notice Initialize a cloned copy of the hook. Sets the project association, ERC-721 name/symbol, pricing
|
|
245
245
|
/// context (currency + decimals), metadata URIs, initial tiers, and behavioral flags. Can only be called once
|
|
246
246
|
/// per clone — the implementation contract is pre-initialized in its constructor to prevent misuse.
|
|
247
|
-
/// @dev Called by `JB721TiersHookDeployer`
|
|
248
|
-
/// project ID is zero.
|
|
247
|
+
/// @dev Called by `JB721TiersHookDeployer` after cloning. Reverts if called twice or if the project ID is zero.
|
|
249
248
|
/// @param initialProjectId The ID of the project this hook is associated with.
|
|
250
249
|
/// @param name The name of the NFT collection.
|
|
251
250
|
/// @param symbol The symbol representing the NFT collection.
|
|
@@ -461,9 +460,8 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
461
460
|
/// @param symbol The new collection symbol. Send empty to leave unchanged.
|
|
462
461
|
/// @param baseUri The new base URI. Send empty to leave unchanged.
|
|
463
462
|
/// @param contractUri The new contract URI. Send empty to leave unchanged.
|
|
464
|
-
/// @param tokenUriResolver The new URI resolver. Pass `IJB721TokenUriResolver(address(this))`
|
|
465
|
-
///
|
|
466
|
-
/// clears the resolver.
|
|
463
|
+
/// @param tokenUriResolver The new URI resolver. Pass `IJB721TokenUriResolver(address(this))` to leave it
|
|
464
|
+
/// unchanged; `address(0)` clears the resolver.
|
|
467
465
|
/// @param encodedIpfsUriTierId The ID of the tier to set the encoded IPFS URI of.
|
|
468
466
|
/// @param encodedIpfsUri The encoded IPFS URI to set.
|
|
469
467
|
function setMetadata(
|
|
@@ -114,8 +114,7 @@ contract JB721TiersHookProjectDeployer is
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
/// @notice Launches rulesets for a project with an attached 721 tiers hook.
|
|
117
|
-
/// @dev Only a project's owner or an operator with
|
|
118
|
-
/// rulesets.
|
|
117
|
+
/// @dev Only a project's owner or an operator with `LAUNCH_RULESETS & SET_TERMINALS` can launch its rulesets.
|
|
119
118
|
/// @param projectId The ID of the project to launch rulesets for.
|
|
120
119
|
/// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook to deploy.
|
|
121
120
|
/// @param launchRulesetsConfig Configuration which dictates the project's new rulesets.
|
|
@@ -233,8 +232,7 @@ contract JB721TiersHookProjectDeployer is
|
|
|
233
232
|
//*********************************************************************//
|
|
234
233
|
|
|
235
234
|
/// @notice Configure and launch rulesets for a newly created project. Converts `JBPayDataHookRulesetConfig` entries
|
|
236
|
-
/// into standard `JBRulesetConfig` entries
|
|
237
|
-
/// the data hook.
|
|
235
|
+
/// into standard `JBRulesetConfig` entries that use the deployed hook as their data hook.
|
|
238
236
|
/// @param projectId The ID of the reserved project.
|
|
239
237
|
/// @param launchProjectConfig Configuration which dictates the behavior of the project to launch.
|
|
240
238
|
/// @param dataHook The data hook to use for the project.
|
|
@@ -148,8 +148,7 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
148
148
|
/// @custom:param hook The 721 contract to get the custom token URI resolver of.
|
|
149
149
|
mapping(address hook => IJB721TokenUriResolver) public override tokenUriResolverOf;
|
|
150
150
|
|
|
151
|
-
/// @notice The combined cash-out weight of all
|
|
152
|
-
/// O(1) instead of O(maxTierId).
|
|
151
|
+
/// @notice The combined cash-out weight of all hook NFTs, tracked so cash-out pricing is O(1).
|
|
153
152
|
/// @dev Maintained incrementally in `recordMint` (+ the tier's full price for the new outstanding NFT plus any
|
|
154
153
|
/// newly-accrued pending reserve) and `recordBurn` (- the tier's full price). It is invariant under everything
|
|
155
154
|
/// else: reserve mints are weight-neutral (a pending reserve becomes an outstanding NFT), removed tiers keep
|
|
@@ -610,8 +609,7 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
610
609
|
/// @param hook The 721 contract to get the tier from.
|
|
611
610
|
/// @param tierId The ID of the tier to get.
|
|
612
611
|
/// @param storedTier The stored tier to get the corresponding tier for.
|
|
613
|
-
/// @param includeResolvedUri If
|
|
614
|
-
/// resolved and included.
|
|
612
|
+
/// @param includeResolvedUri If true and the contract has a token URI resolver, resolve and include its content.
|
|
615
613
|
/// @return tier The tier as a `JB721Tier` struct.
|
|
616
614
|
function _getTierFrom(
|
|
617
615
|
address hook,
|
|
@@ -135,8 +135,7 @@ abstract contract JB721Hook is ERC721, IJB721Hook {
|
|
|
135
135
|
cashOutTaxRate = context.cashOutTaxRate;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
/// @notice The data calculated before a payment is recorded in the terminal store
|
|
139
|
-
/// terminal's `pay(...)` transaction.
|
|
138
|
+
/// @notice The data calculated before a payment is recorded in the terminal store for `pay(...)`.
|
|
140
139
|
/// @dev Sets this contract as the pay hook. Part of `IJBRulesetDataHook`.
|
|
141
140
|
/// @param context The payment context passed to this contract by the `pay(...)` function.
|
|
142
141
|
/// @return weight The new `weight` to use, overriding the ruleset's `weight`.
|
|
@@ -164,8 +163,7 @@ abstract contract JB721Hook is ERC721, IJB721Hook {
|
|
|
164
163
|
// -------------------------- public views --------------------------- //
|
|
165
164
|
//*********************************************************************//
|
|
166
165
|
|
|
167
|
-
/// @notice Returns the cumulative cash out weight of the specified token IDs relative to
|
|
168
|
-
/// `totalCashOutWeight`.
|
|
166
|
+
/// @notice Returns the cumulative cash out weight of the specified token IDs relative to `totalCashOutWeight`.
|
|
169
167
|
/// @param tokenIds The NFT token IDs to calculate the cumulative cash out weight of.
|
|
170
168
|
/// @return The cumulative cash out weight of the specified token IDs.
|
|
171
169
|
function cashOutWeightOf(uint256[] memory tokenIds) public view virtual returns (uint256) {
|
|
@@ -248,8 +246,7 @@ abstract contract JB721Hook is ERC721, IJB721Hook {
|
|
|
248
246
|
_didBurn(decodedTokenIds);
|
|
249
247
|
}
|
|
250
248
|
|
|
251
|
-
/// @notice Mints
|
|
252
|
-
/// `IJBPayHook`.
|
|
249
|
+
/// @notice Mints NFTs to `context.beneficiary` upon payment if conditions are met. Part of `IJBPayHook`.
|
|
253
250
|
/// @dev Reverts if the calling contract is not one of the project's terminals.
|
|
254
251
|
/// @param context The payment context passed in by the terminal.
|
|
255
252
|
function afterPayRecordedWith(JBAfterPayRecordedContext calldata context) external payable virtual override {
|
|
@@ -3,6 +3,7 @@ pragma solidity ^0.8.0;
|
|
|
3
3
|
|
|
4
4
|
import {IJBActiveVotes} from "@bananapus/core-v6/src/interfaces/IJBActiveVotes.sol";
|
|
5
5
|
import {IERC5805} from "@openzeppelin/contracts/interfaces/IERC5805.sol";
|
|
6
|
+
|
|
6
7
|
import {IJB721TiersHookStore} from "./IJB721TiersHookStore.sol";
|
|
7
8
|
|
|
8
9
|
/// @notice A checkpoint module that provides IVotes-compatible checkpointed voting power for a JB721TiersHook.
|
|
@@ -10,38 +11,67 @@ import {IJB721TiersHookStore} from "./IJB721TiersHookStore.sol";
|
|
|
10
11
|
/// Pass this address to JBTokenDistributor as the IVotes token.
|
|
11
12
|
interface IJB721Checkpoints is IERC5805, IJBActiveVotes {
|
|
12
13
|
/// @notice The store that holds tier and voting data for the hook's NFTs.
|
|
13
|
-
/// @return The store contract.
|
|
14
|
+
/// @return store The store contract.
|
|
14
15
|
// forge-lint: disable-next-line(mixed-case-function)
|
|
15
|
-
function STORE() external view returns (IJB721TiersHookStore);
|
|
16
|
+
function STORE() external view returns (IJB721TiersHookStore store);
|
|
17
|
+
|
|
18
|
+
/// @notice The delegated voting units held by an account in a tier at a past block.
|
|
19
|
+
/// @dev Counts only tier voting units held by `account` while `account` had a nonzero delegate.
|
|
20
|
+
/// @param account The account to get the delegated tier voting units of.
|
|
21
|
+
/// @param tierId The tier to get the delegated voting units of.
|
|
22
|
+
/// @param blockNumber The past block number to look up.
|
|
23
|
+
/// @return activeVotes The account's delegated tier voting units at `blockNumber`.
|
|
24
|
+
function getPastAccountTierActiveVotes(
|
|
25
|
+
address account,
|
|
26
|
+
uint256 tierId,
|
|
27
|
+
uint256 blockNumber
|
|
28
|
+
)
|
|
29
|
+
external
|
|
30
|
+
view
|
|
31
|
+
returns (uint256 activeVotes);
|
|
16
32
|
|
|
17
|
-
/// @notice The total
|
|
18
|
-
/// @dev
|
|
19
|
-
///
|
|
20
|
-
/// mints never write to this trace. Distributors use this as the denominator for tier-scoped reward pots.
|
|
21
|
-
/// @param tierId The tier to get the eligible voting units of.
|
|
33
|
+
/// @notice The total owner-checkpointed voting units of a tier at a past block.
|
|
34
|
+
/// @dev Owner-checkpointed voting units are the tier's total owned units, regardless of delegation status.
|
|
35
|
+
/// @param tierId The tier to get the owner-checkpointed voting units of.
|
|
22
36
|
/// @param blockNumber The block number to look up (must be strictly in the past).
|
|
23
|
-
/// @return The tier's
|
|
24
|
-
function getPastTierVotingUnits(uint256 tierId, uint256 blockNumber) external view returns (uint256);
|
|
37
|
+
/// @return votingUnits The tier's owner-checkpointed voting units at `blockNumber`.
|
|
38
|
+
function getPastTierVotingUnits(uint256 tierId, uint256 blockNumber) external view returns (uint256 votingUnits);
|
|
39
|
+
|
|
40
|
+
/// @notice The total delegated voting units of a tier at a past block.
|
|
41
|
+
/// @dev Counts only tier voting units held by accounts with a nonzero delegate.
|
|
42
|
+
/// @param tierId The tier to get the delegated voting units of.
|
|
43
|
+
/// @param blockNumber The past block number to look up.
|
|
44
|
+
/// @return activeVotes The tier's delegated voting units at `blockNumber`.
|
|
45
|
+
function getPastTotalTierActiveVotes(
|
|
46
|
+
uint256 tierId,
|
|
47
|
+
uint256 blockNumber
|
|
48
|
+
)
|
|
49
|
+
external
|
|
50
|
+
view
|
|
51
|
+
returns (uint256 activeVotes);
|
|
52
|
+
|
|
53
|
+
/// @notice The current total delegated voting units of a tier.
|
|
54
|
+
/// @param tierId The tier to get the current delegated voting units of.
|
|
55
|
+
/// @return activeVotes The tier's current delegated voting units.
|
|
56
|
+
function getTotalTierActiveVotes(uint256 tierId) external view returns (uint256 activeVotes);
|
|
25
57
|
|
|
26
58
|
/// @notice The hook that this module tracks voting power for.
|
|
27
|
-
/// @return The hook address.
|
|
59
|
+
/// @return hookAddress The hook address.
|
|
28
60
|
// forge-lint: disable-next-line(mixed-case-function)
|
|
29
|
-
function hook() external view returns (address);
|
|
61
|
+
function hook() external view returns (address hookAddress);
|
|
30
62
|
|
|
31
63
|
/// @notice The owner of an NFT at a past block.
|
|
32
|
-
/// @dev Returns `address(0)`
|
|
33
|
-
/// transferred. Unenrolled tokens are ineligible for snapshot-based distribution.
|
|
64
|
+
/// @dev Returns `address(0)` if no ownership checkpoint exists or the query predates the first checkpoint.
|
|
34
65
|
/// @param tokenId The token ID of the NFT to get the historical owner of.
|
|
35
66
|
/// @param blockNumber The block number to look up.
|
|
36
|
-
/// @return The owner of the token at `blockNumber`, or zero if
|
|
37
|
-
function ownerOfAt(uint256 tokenId, uint256 blockNumber) external view returns (address);
|
|
67
|
+
/// @return owner The owner of the token at `blockNumber`, or zero if no owner is proven at that block.
|
|
68
|
+
function ownerOfAt(uint256 tokenId, uint256 blockNumber) external view returns (address owner);
|
|
38
69
|
|
|
39
|
-
/// @notice Delegates voting power and
|
|
40
|
-
/// @dev
|
|
41
|
-
///
|
|
42
|
-
/// distribution.
|
|
70
|
+
/// @notice Delegates voting power and backfills ownership history for listed tokens if needed.
|
|
71
|
+
/// @dev Mint and transfer hooks normally write owner checkpoints automatically. The token ID list keeps
|
|
72
|
+
/// pre-upgrade or otherwise uncheckpointed tokens recoverable while preserving the owner-only authorization check.
|
|
43
73
|
/// @param delegatee The address to delegate voting power to. Use your own address for self-delegation.
|
|
44
|
-
/// @param tokenIds The token IDs
|
|
74
|
+
/// @param tokenIds The token IDs whose owner checkpoints should be backfilled if missing.
|
|
45
75
|
function delegate(address delegatee, uint256[] calldata tokenIds) external;
|
|
46
76
|
|
|
47
77
|
/// @notice Initializes a cloned module with its hook reference.
|
|
@@ -96,8 +96,7 @@ interface IJB721TiersHook is IJB721Hook {
|
|
|
96
96
|
/// @param caller The address that called the function.
|
|
97
97
|
event SetTokenUriResolver(IJB721TokenUriResolver indexed resolver, address caller);
|
|
98
98
|
|
|
99
|
-
/// @notice Emitted when a split payout reverts during distribution
|
|
100
|
-
/// project's balance.
|
|
99
|
+
/// @notice Emitted when a split payout reverts during distribution, routing funds to the project's balance.
|
|
101
100
|
/// @param projectId The project ID the split belongs to.
|
|
102
101
|
/// @param split The split that reverted.
|
|
103
102
|
/// @param amount The amount that was paid out.
|
|
@@ -213,9 +212,8 @@ interface IJB721TiersHook is IJB721Hook {
|
|
|
213
212
|
/// @param symbol The new collection symbol. Send empty to leave unchanged.
|
|
214
213
|
/// @param baseUri The new base URI. Send empty to leave unchanged.
|
|
215
214
|
/// @param contractUri The new contract URI. Send empty to leave unchanged.
|
|
216
|
-
/// @param tokenUriResolver The new URI resolver. Pass `IJB721TokenUriResolver(address(this))`
|
|
217
|
-
///
|
|
218
|
-
/// clears the resolver.
|
|
215
|
+
/// @param tokenUriResolver The new URI resolver. Pass `IJB721TokenUriResolver(address(this))` to leave it
|
|
216
|
+
/// unchanged; `address(0)` clears the resolver.
|
|
219
217
|
/// @param encodedIpfsUriTierId The ID of the tier to set the encoded IPFS URI of.
|
|
220
218
|
/// @param encodedIpfsUri The encoded IPFS URI to set.
|
|
221
219
|
function setMetadata(
|
|
@@ -73,8 +73,7 @@ library JB721TiersHookLib {
|
|
|
73
73
|
/// @param caller The address that called the function.
|
|
74
74
|
event SetDiscountPercent(uint256 indexed tierId, uint256 discountPercent, address caller);
|
|
75
75
|
|
|
76
|
-
/// @notice Emitted when a split payout reverts during distribution
|
|
77
|
-
/// project's balance.
|
|
76
|
+
/// @notice Emitted when a split payout reverts during distribution, routing funds to the project's balance.
|
|
78
77
|
/// @param projectId The project ID the split belongs to.
|
|
79
78
|
/// @param split The split that reverted.
|
|
80
79
|
/// @param amount The amount that was paid out.
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity ^0.8.0;
|
|
3
3
|
|
|
4
|
-
/// @notice `JB721TiersHook` options
|
|
5
|
-
/// per-ruleset basis.
|
|
4
|
+
/// @notice `JB721TiersHook` options packed into each ruleset's `JBRulesetMetadata.metadata`.
|
|
6
5
|
/// @custom:member pauseTransfers A boolean indicating whether NFT transfers are paused during this ruleset.
|
|
7
6
|
/// @custom:member pauseMintPendingReserves A boolean indicating whether pending/outstanding NFT reserves can be minted
|
|
8
7
|
/// during this ruleset.
|
|
@@ -5,8 +5,7 @@ import {JBTerminalConfig} from "@bananapus/core-v6/src/structs/JBTerminalConfig.
|
|
|
5
5
|
|
|
6
6
|
import {JBPayDataHookRulesetConfig} from "./JBPayDataHookRulesetConfig.sol";
|
|
7
7
|
|
|
8
|
-
/// @custom:member projectUri Metadata URI to associate with the project
|
|
9
|
-
/// the project.
|
|
8
|
+
/// @custom:member projectUri Metadata URI to associate with the project, updatable by the project owner.
|
|
10
9
|
/// @custom:member rulesetConfigurations The ruleset configurations to queue.
|
|
11
10
|
/// @custom:member terminalConfigurations The terminal configurations to add for the project.
|
|
12
11
|
/// @custom:member memo A memo to pass along to the emitted event.
|
|
@@ -14,17 +14,14 @@ import {JBPayDataHookRulesetMetadata} from "./JBPayDataHookRulesetMetadata.sol";
|
|
|
14
14
|
/// a project owner cannot make changes to a ruleset's parameters while it is active – any proposed changes will apply
|
|
15
15
|
/// to the subsequent ruleset. If no changes are proposed, a ruleset rolls over to another one with the same properties
|
|
16
16
|
/// but new `start` timestamp and a decayed `weight`.
|
|
17
|
-
/// @custom:member weight A fixed point number with 18 decimals that contracts can use
|
|
18
|
-
///
|
|
19
|
-
///
|
|
20
|
-
///
|
|
21
|
-
/// the
|
|
22
|
-
/// project owner hasn't queued the subsequent ruleset with an explicit `weight`. If it's 0, each ruleset will have
|
|
23
|
-
/// equal weight. If the number is 90%, the next ruleset will have a 10% smaller weight. This weight is out of
|
|
17
|
+
/// @custom:member weight A fixed point number with 18 decimals that contracts can use for calculations. For example,
|
|
18
|
+
/// payment terminals can use this to determine how many tokens should be minted when a payment is received.
|
|
19
|
+
/// @custom:member weightCutPercent A percent by how much the `weight` of the subsequent ruleset should be reduced if
|
|
20
|
+
/// the project owner hasn't queued the subsequent ruleset with an explicit `weight`. If it's 0, each ruleset will
|
|
21
|
+
/// have equal weight. If the number is 90%, the next ruleset will have a 10% smaller weight. This weight is out of
|
|
24
22
|
/// `JBConstants.MAX_WEIGHT_CUT_PERCENT`.
|
|
25
|
-
/// @custom:member approvalHook
|
|
26
|
-
///
|
|
27
|
-
/// can be used to create rules around how a project owner can change ruleset parameters over time.
|
|
23
|
+
/// @custom:member approvalHook A contract that says whether a proposed ruleset should be accepted or rejected. It can
|
|
24
|
+
/// create rules around how a project owner changes ruleset parameters over time.
|
|
28
25
|
/// @custom:member metadata Metadata specifying the controller-specific parameters that a ruleset can have. These
|
|
29
26
|
/// properties cannot change until the next ruleset starts.
|
|
30
27
|
/// @custom:member splitGroups An array of splits to use for any number of groups while the ruleset is active.
|
|
@@ -13,12 +13,10 @@ pragma solidity ^0.8.0;
|
|
|
13
13
|
/// permission from the owner should be allowed to mint project tokens on demand during this ruleset.
|
|
14
14
|
/// @custom:member allowSetCustomToken A flag indicating if the project owner can set the project's token to a custom
|
|
15
15
|
/// ERC-20.
|
|
16
|
-
/// @custom:member allowTerminalMigration A flag indicating if
|
|
17
|
-
/// ruleset.
|
|
16
|
+
/// @custom:member allowTerminalMigration A flag indicating if terminal migration is allowed during this ruleset.
|
|
18
17
|
/// @custom:member allowSetTerminals A flag indicating if a project's terminals can be added or removed.
|
|
19
18
|
/// @custom:member allowSetController A flag indicating if a project's controller can be changed.
|
|
20
|
-
/// @custom:member allowAddAccountingContext A flag indicating if a project can add
|
|
21
|
-
/// terminals to use.
|
|
19
|
+
/// @custom:member allowAddAccountingContext A flag indicating if a project can add accounting contexts to terminals.
|
|
22
20
|
/// @custom:member allowAddPriceFeed A flag indicating if a project can add new price feeds to calculate exchange rates
|
|
23
21
|
/// between its tokens.
|
|
24
22
|
/// @custom:member ownerMustSendPayouts A flag indicating if the owner must manually trigger payout distributions.
|