@bananapus/721-hook-v6 0.0.72 → 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 +3 -2
- package/package.json +1 -1
- package/references/operations.md +2 -2
- package/src/JB721Checkpoints.sol +74 -13
- package/src/interfaces/IJB721Checkpoints.sol +26 -5
package/README.md
CHANGED
|
@@ -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 owner-tracked voting units (`getPastTierVotingUnits`), global active vote totals (`getPastTotalActiveVotes`),
|
|
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
|
|
|
@@ -65,7 +65,8 @@ If a bug affects supply, reserve minting, or tier lookup, it usually lives in th
|
|
|
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
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 `
|
|
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
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
|
@@ -55,6 +55,13 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
55
55
|
// -------------------- internal stored properties ------------------- //
|
|
56
56
|
//*********************************************************************//
|
|
57
57
|
|
|
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
|
+
|
|
58
65
|
/// @notice Checkpointed token owners for historical reward eligibility.
|
|
59
66
|
/// @dev Mint and transfer hooks write this automatically; `delegate` only backfills missing pre-upgrade history.
|
|
60
67
|
/// @custom:param tokenId The token ID to get historical owner checkpoints for.
|
|
@@ -189,10 +196,20 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
189
196
|
// Move checkpointed voting power from the previous owner to the new owner.
|
|
190
197
|
_transferVotingUnits({from: from, to: to, amount: votingUnits});
|
|
191
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
|
+
|
|
192
209
|
// If both sides are delegated or both sides are undelegated, this tier's active total is unchanged.
|
|
193
210
|
if (decreaseTierActiveVotes != increaseTierActiveVotes) {
|
|
194
211
|
// Otherwise apply the one-sided active-tier delta implied by the receiver's delegated status.
|
|
195
|
-
|
|
212
|
+
_adjustTotalTierActiveVotes({tierId: tierId, amount: votingUnits, increase: increaseTierActiveVotes});
|
|
196
213
|
}
|
|
197
214
|
}
|
|
198
215
|
|
|
@@ -200,12 +217,14 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
200
217
|
// ----------------------- external views ---------------------------- //
|
|
201
218
|
//*********************************************************************//
|
|
202
219
|
|
|
203
|
-
/// @notice The
|
|
204
|
-
/// @dev Counts only tier voting units held by
|
|
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.
|
|
205
223
|
/// @param tierId The tier to get the delegated voting units of.
|
|
206
224
|
/// @param blockNumber The past block number to look up.
|
|
207
|
-
/// @return activeVotes The
|
|
208
|
-
function
|
|
225
|
+
/// @return activeVotes The account's delegated tier voting units at `blockNumber`.
|
|
226
|
+
function getPastAccountTierActiveVotes(
|
|
227
|
+
address account,
|
|
209
228
|
uint256 tierId,
|
|
210
229
|
uint256 blockNumber
|
|
211
230
|
)
|
|
@@ -215,7 +234,8 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
215
234
|
returns (uint256 activeVotes)
|
|
216
235
|
{
|
|
217
236
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
218
|
-
activeVotes =
|
|
237
|
+
activeVotes =
|
|
238
|
+
_accountTierActiveVotesOf[account][tierId].upperLookupRecent(uint96(_validateTimepoint(blockNumber)));
|
|
219
239
|
}
|
|
220
240
|
|
|
221
241
|
/// @notice The total owner-checkpointed voting units of a tier at a past block.
|
|
@@ -243,11 +263,22 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
243
263
|
activeVotes = _activeSupplyCheckpoints.upperLookupRecent(_validateTimepoint(blockNumber));
|
|
244
264
|
}
|
|
245
265
|
|
|
246
|
-
/// @notice The
|
|
247
|
-
/// @
|
|
248
|
-
/// @
|
|
249
|
-
|
|
250
|
-
|
|
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)));
|
|
251
282
|
}
|
|
252
283
|
|
|
253
284
|
/// @notice The current total delegated voting units.
|
|
@@ -257,6 +288,13 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
257
288
|
activeVotes = _activeSupplyCheckpoints.latest();
|
|
258
289
|
}
|
|
259
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
|
+
|
|
260
298
|
/// @notice The owner of an NFT at a past block.
|
|
261
299
|
/// @dev Returns `address(0)` if no ownership checkpoint exists or the query predates the first checkpoint.
|
|
262
300
|
/// @param tokenId The token ID of the NFT to get the historical owner of.
|
|
@@ -354,11 +392,31 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
354
392
|
// ------------------------ private helpers -------------------------- //
|
|
355
393
|
//*********************************************************************//
|
|
356
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
|
+
|
|
357
415
|
/// @notice Add or remove units from a tier's active-voting-units checkpoint at the current block.
|
|
358
416
|
/// @param tierId The tier whose active-voting-units trace to update.
|
|
359
417
|
/// @param amount The voting units to add or remove.
|
|
360
418
|
/// @param increase Whether to add `amount`; if false, `amount` is removed.
|
|
361
|
-
function
|
|
419
|
+
function _adjustTotalTierActiveVotes(uint256 tierId, uint256 amount, bool increase) private {
|
|
362
420
|
// Ignore zero-unit updates because they do not change this tier's active total.
|
|
363
421
|
if (amount == 0) return;
|
|
364
422
|
|
|
@@ -389,7 +447,10 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
389
447
|
|
|
390
448
|
// Only write checkpoints for tiers whose active totals actually change.
|
|
391
449
|
if (tierVotingUnits != 0) {
|
|
392
|
-
|
|
450
|
+
_adjustTotalTierActiveVotes({tierId: tierId, amount: tierVotingUnits, increase: increase});
|
|
451
|
+
_adjustAccountTierActiveVotes({
|
|
452
|
+
account: account, tierId: tierId, amount: tierVotingUnits, increase: increase
|
|
453
|
+
});
|
|
393
454
|
}
|
|
394
455
|
|
|
395
456
|
unchecked {
|
|
@@ -15,12 +15,20 @@ interface IJB721Checkpoints is IERC5805, IJBActiveVotes {
|
|
|
15
15
|
// forge-lint: disable-next-line(mixed-case-function)
|
|
16
16
|
function STORE() external view returns (IJB721TiersHookStore store);
|
|
17
17
|
|
|
18
|
-
/// @notice The
|
|
19
|
-
/// @dev Counts only tier voting units held by
|
|
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.
|
|
20
21
|
/// @param tierId The tier to get the delegated voting units of.
|
|
21
22
|
/// @param blockNumber The past block number to look up.
|
|
22
|
-
/// @return activeVotes The
|
|
23
|
-
function
|
|
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);
|
|
24
32
|
|
|
25
33
|
/// @notice The total owner-checkpointed voting units of a tier at a past block.
|
|
26
34
|
/// @dev Owner-checkpointed voting units are the tier's total owned units, regardless of delegation status.
|
|
@@ -29,10 +37,23 @@ interface IJB721Checkpoints is IERC5805, IJBActiveVotes {
|
|
|
29
37
|
/// @return votingUnits The tier's owner-checkpointed voting units at `blockNumber`.
|
|
30
38
|
function getPastTierVotingUnits(uint256 tierId, uint256 blockNumber) external view returns (uint256 votingUnits);
|
|
31
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
|
+
|
|
32
53
|
/// @notice The current total delegated voting units of a tier.
|
|
33
54
|
/// @param tierId The tier to get the current delegated voting units of.
|
|
34
55
|
/// @return activeVotes The tier's current delegated voting units.
|
|
35
|
-
function
|
|
56
|
+
function getTotalTierActiveVotes(uint256 tierId) external view returns (uint256 activeVotes);
|
|
36
57
|
|
|
37
58
|
/// @notice The hook that this module tracks voting power for.
|
|
38
59
|
/// @return hookAddress The hook address.
|