@bananapus/core-v6 0.0.83 → 0.0.84
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
CHANGED
|
@@ -72,6 +72,7 @@ The shortest reading path is:
|
|
|
72
72
|
| `JBTerminalStore` | Shared accounting for balances, surplus, fees, and reclaim math. |
|
|
73
73
|
| `JBDirectory` | Registry for controller and terminal routing. |
|
|
74
74
|
| `JBProjects` | ERC-721 project registry and ownership surface. |
|
|
75
|
+
| `JBERC20` | Cloneable project-token ERC-20 with Votes, Permit, ERC-1271, and active-vote total checkpoints. |
|
|
75
76
|
| `JBPermissions` | Packed operator-permission registry. |
|
|
76
77
|
| `JBPrices` | Price-feed routing used by terminals and integrations. |
|
|
77
78
|
|
|
@@ -81,6 +82,8 @@ The shortest reading path is:
|
|
|
81
82
|
- Data hooks and cash-out hooks can change economics and side effects. They are part of the protocol surface.
|
|
82
83
|
- Permission checks are not always against the project owner. Some flows are scoped to the token holder instead.
|
|
83
84
|
- Preview and execution are intentionally close, but callers should still treat them as separate surfaces when hooks or routing can change behavior.
|
|
85
|
+
- `JBERC20.getPastTotalSupply(...)` includes undelegated balances. Use `getPastTotalActiveVotes(...)` when an
|
|
86
|
+
integration must split value only among addresses with checkpointed voting power.
|
|
84
87
|
|
|
85
88
|
## Where state lives
|
|
86
89
|
|
package/package.json
CHANGED
|
@@ -21,7 +21,7 @@ The core Juicebox V6 protocol on EVM: a modular system for launching treasury-ba
|
|
|
21
21
|
| `JBSplits` | Split configurations per project/ruleset/group. Packed storage for gas efficiency. |
|
|
22
22
|
| `JBFundAccessLimits` | Payout limits and surplus allowances per project/ruleset/terminal/token. |
|
|
23
23
|
| `JBPrices` | Append-only price feed registry with project-specific feeds, protocol defaults, inverse lookup, and backup feeds. |
|
|
24
|
-
| `JBERC20` | Cloneable ERC-20 with Votes + Permit + ERC-1271. Controlled by `JBTokens` via `onlyTokens`. Deployed via `Clones.clone()`. |
|
|
24
|
+
| `JBERC20` | Cloneable ERC-20 with Votes + Permit + ERC-1271 and active-vote total checkpoints. Controlled by `JBTokens` via `onlyTokens`. Deployed via `Clones.clone()`. |
|
|
25
25
|
| `JBFeelessAddresses` | Static and hook-driven fee-exemption registry. |
|
|
26
26
|
| `JBChainlinkV3PriceFeed` | Chainlink AggregatorV3 price feed with staleness threshold. Rejects negative/zero prices, incomplete rounds (`updatedAt == 0`), and stale answers carried from previous rounds (`answeredInRound < roundId`). |
|
|
27
27
|
| `JBChainlinkV3SequencerPriceFeed` | L2 sequencer-aware Chainlink feed (Optimism/Arbitrum) with grace period after restart. Treats any non-zero sequencer answer as down (`answer != 0`). |
|
|
@@ -148,6 +148,13 @@ The core Juicebox V6 protocol on EVM: a modular system for launching treasury-ba
|
|
|
148
148
|
| `totalCreditSupplyOf(uint256 projectId)` | Returns the internal credit supply for a project. |
|
|
149
149
|
| `setTokenMetadataFor(uint256 projectId, string name, string symbol)` | Sets the name and symbol of a project's token. Controller-only. |
|
|
150
150
|
|
|
151
|
+
### JBERC20
|
|
152
|
+
|
|
153
|
+
| Function | What it does |
|
|
154
|
+
|----------|--------------|
|
|
155
|
+
| `getPastTotalActiveVotes(uint256 timepoint)` | Returns the total voting units delegated to nonzero delegates at a past block. Unlike `getPastTotalSupply`, this excludes undelegated balances such as AMM-held tokens with no delegate. |
|
|
156
|
+
| `getTotalActiveVotes()` | Returns the current total voting units delegated to nonzero delegates. |
|
|
157
|
+
|
|
151
158
|
### JBSplits
|
|
152
159
|
|
|
153
160
|
| Function | What it does |
|
|
@@ -87,6 +87,8 @@ Use this file when you need deeper protocol reference material after the repo-lo
|
|
|
87
87
|
- Credits are burned before ERC-20 tokens in `JBTokens.burnFrom()`
|
|
88
88
|
- `JBRuleset.weight` is `uint112` with 18 decimals; `JBRuleset.metadata` is packed -- use `JBRulesetMetadataResolver` to unpack
|
|
89
89
|
- `JBERC20` is cloned via `Clones.clone()` -- its constructor sets invalid name/symbol; real values set in `initialize()`
|
|
90
|
+
- `JBERC20.getPastTotalSupply()` includes undelegated balances; `getPastTotalActiveVotes()` only counts balances whose
|
|
91
|
+
owner had a nonzero delegate at the queried block.
|
|
90
92
|
- Fee is 2.5% (`FEE = 25` out of `MAX_FEE = 1000`)
|
|
91
93
|
- Project #1 is the fee beneficiary project (receives all protocol fees)
|
|
92
94
|
- **Fee-free cashout exemption is scoped to fee-free intra-terminal payout amounts.** `feeFreeSurplusOf[projectId][token]` accumulates the value of fee-free payouts. After any outflow (payouts, `useAllowanceOf`, non-zero-tax or feeless cashouts), the counter is capped at the remaining balance — non-fee-free funds leave first, preserving the fee-free counter. During cashout with `cashOutTaxRate=0`, the 2.5% fee applies only up to this surplus, then depletes. Once consumed, subsequent cashouts are fee-free again. Cleared on terminal migration. This prevents a round-trip fee bypass (intra-terminal payout → zero-tax cashout) while scoping fees precisely to the fee-free inflow.
|
package/src/JBERC20.sol
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
|
+
import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol";
|
|
4
5
|
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
|
5
6
|
import {ERC20Permit, Nonces} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
|
|
6
7
|
import {ERC20Votes} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
|
|
7
|
-
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
|
8
|
-
import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol";
|
|
9
8
|
import {Nonces} from "@openzeppelin/contracts/utils/Nonces.sol";
|
|
9
|
+
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
|
10
|
+
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
|
|
11
|
+
import {Checkpoints} from "@openzeppelin/contracts/utils/structs/Checkpoints.sol";
|
|
10
12
|
|
|
11
13
|
import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
|
|
12
14
|
import {JBPermissioned} from "./abstract/JBPermissioned.sol";
|
|
15
|
+
import {IJBActiveVotes} from "./interfaces/IJBActiveVotes.sol";
|
|
13
16
|
import {IJBPermissions} from "./interfaces/IJBPermissions.sol";
|
|
14
17
|
import {IJBProjects} from "./interfaces/IJBProjects.sol";
|
|
15
18
|
import {IJBToken} from "./interfaces/IJBToken.sol";
|
|
@@ -20,7 +23,9 @@ import {IJBTokens} from "./interfaces/IJBTokens.sol";
|
|
|
20
23
|
/// holders can claim their internal credits into this transferable token.
|
|
21
24
|
/// @dev Only `JBTokens` can mint and burn. The project owner (via `SET_TOKEN_METADATA` permission) can rename the
|
|
22
25
|
/// token. Supports ERC-1271 signature validation for smart-contract wallets.
|
|
23
|
-
contract JBERC20 is ERC20Votes, ERC20Permit, JBPermissioned, IERC1271, IJBToken {
|
|
26
|
+
contract JBERC20 is ERC20Votes, ERC20Permit, JBPermissioned, IERC1271, IJBActiveVotes, IJBToken {
|
|
27
|
+
using Checkpoints for Checkpoints.Trace208;
|
|
28
|
+
|
|
24
29
|
//*********************************************************************//
|
|
25
30
|
// --------------------------- custom errors ------------------------- //
|
|
26
31
|
//*********************************************************************//
|
|
@@ -50,6 +55,9 @@ contract JBERC20 is ERC20Votes, ERC20Permit, JBPermissioned, IERC1271, IJBToken
|
|
|
50
55
|
// -------------------- private stored properties -------------------- //
|
|
51
56
|
//*********************************************************************//
|
|
52
57
|
|
|
58
|
+
/// @notice The total voting units currently delegated to nonzero delegates.
|
|
59
|
+
Checkpoints.Trace208 private _activeSupplyCheckpoints;
|
|
60
|
+
|
|
53
61
|
/// @notice The token's name.
|
|
54
62
|
string private _name;
|
|
55
63
|
|
|
@@ -170,6 +178,19 @@ contract JBERC20 is ERC20Votes, ERC20Permit, JBPermissioned, IERC1271, IJBToken
|
|
|
170
178
|
return super.decimals();
|
|
171
179
|
}
|
|
172
180
|
|
|
181
|
+
/// @notice The total delegated voting units at a past block.
|
|
182
|
+
/// @param timepoint The past block number to look up.
|
|
183
|
+
/// @return activeVotes The total voting units delegated to nonzero delegates at `timepoint`.
|
|
184
|
+
function getPastTotalActiveVotes(uint256 timepoint) public view override returns (uint256 activeVotes) {
|
|
185
|
+
activeVotes = _activeSupplyCheckpoints.upperLookupRecent(_validateTimepoint(timepoint));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/// @notice The current total delegated voting units.
|
|
189
|
+
/// @return activeVotes The current total voting units delegated to nonzero delegates.
|
|
190
|
+
function getTotalActiveVotes() public view override returns (uint256 activeVotes) {
|
|
191
|
+
activeVotes = _activeSupplyCheckpoints.latest();
|
|
192
|
+
}
|
|
193
|
+
|
|
173
194
|
/// @notice The token's name, set during initialization.
|
|
174
195
|
function name() public view virtual override returns (string memory) {
|
|
175
196
|
return _name;
|
|
@@ -216,8 +237,45 @@ contract JBERC20 is ERC20Votes, ERC20Permit, JBPermissioned, IERC1271, IJBToken
|
|
|
216
237
|
// ---------------------- internal transactions ---------------------- //
|
|
217
238
|
//*********************************************************************//
|
|
218
239
|
|
|
240
|
+
/// @notice Track active-supply changes when an account moves between undelegated and delegated states.
|
|
241
|
+
function _delegate(address account, address delegatee) internal virtual override {
|
|
242
|
+
address oldDelegate = delegates(account);
|
|
243
|
+
uint256 votingUnits = _getVotingUnits(account);
|
|
244
|
+
|
|
245
|
+
super._delegate({account: account, delegatee: delegatee});
|
|
246
|
+
|
|
247
|
+
if (oldDelegate == address(0) && delegatee != address(0)) {
|
|
248
|
+
_updateActiveVotes({amount: votingUnits, increase: true});
|
|
249
|
+
} else if (oldDelegate != address(0) && delegatee == address(0)) {
|
|
250
|
+
_updateActiveVotes({amount: votingUnits, increase: false});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/// @notice Track active-supply changes when voting units move between delegated and undelegated accounts.
|
|
255
|
+
function _transferVotingUnits(address from, address to, uint256 amount) internal virtual override {
|
|
256
|
+
bool decreaseActiveVotes = from != address(0) && delegates(from) != address(0);
|
|
257
|
+
bool increaseActiveVotes = to != address(0) && delegates(to) != address(0);
|
|
258
|
+
|
|
259
|
+
super._transferVotingUnits({from: from, to: to, amount: amount});
|
|
260
|
+
|
|
261
|
+
if (decreaseActiveVotes == increaseActiveVotes) return;
|
|
262
|
+
_updateActiveVotes({amount: amount, increase: increaseActiveVotes});
|
|
263
|
+
}
|
|
264
|
+
|
|
219
265
|
/// @notice Required override.
|
|
220
266
|
function _update(address from, address to, uint256 value) internal virtual override(ERC20, ERC20Votes) {
|
|
221
267
|
super._update({from: from, to: to, value: value});
|
|
222
268
|
}
|
|
269
|
+
|
|
270
|
+
/// @notice Update the checkpointed total of delegated voting units.
|
|
271
|
+
/// @param amount The amount of voting units to add or remove.
|
|
272
|
+
/// @param increase Whether to add `amount`; if false, `amount` is removed.
|
|
273
|
+
function _updateActiveVotes(uint256 amount, bool increase) internal {
|
|
274
|
+
if (amount == 0) return;
|
|
275
|
+
|
|
276
|
+
uint256 updated =
|
|
277
|
+
increase ? _activeSupplyCheckpoints.latest() + amount : _activeSupplyCheckpoints.latest() - amount;
|
|
278
|
+
|
|
279
|
+
_activeSupplyCheckpoints.push({key: clock(), value: SafeCast.toUint208(updated)});
|
|
280
|
+
}
|
|
223
281
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
/// @notice Provides checkpointed totals for voting units delegated to nonzero delegates.
|
|
5
|
+
interface IJBActiveVotes {
|
|
6
|
+
/// @notice The total delegated voting units at a past timepoint.
|
|
7
|
+
/// @param timepoint The past block number to look up.
|
|
8
|
+
/// @return activeVotes The total voting units delegated to nonzero delegates at `timepoint`.
|
|
9
|
+
function getPastTotalActiveVotes(uint256 timepoint) external view returns (uint256 activeVotes);
|
|
10
|
+
|
|
11
|
+
/// @notice The current total delegated voting units.
|
|
12
|
+
/// @return activeVotes The current total voting units delegated to nonzero delegates.
|
|
13
|
+
function getTotalActiveVotes() external view returns (uint256 activeVotes);
|
|
14
|
+
}
|