@bananapus/core-v6 0.0.58 → 0.0.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -5
- package/script/Deploy.s.sol +121 -0
- package/script/DeployPeriphery.s.sol +346 -0
- package/src/JBMultiTerminal.sol +126 -95
- package/src/JBTerminalStore.sol +111 -0
- package/src/interfaces/IJBCashOutTerminal.sol +4 -1
- package/src/interfaces/IJBMultiTerminal.sol +13 -0
- package/src/interfaces/IJBPayoutTerminal.sol +8 -2
- package/src/interfaces/IJBTerminalStore.sol +55 -0
- package/src/libraries/JBConstants.sol +18 -8
- package/src/libraries/JBHeldFees.sol +170 -0
- package/src/structs/JBFee.sol +13 -2
- package/test/mock/MockMaliciousBeneficiary.sol +4 -2
- package/CHANGELOG.md +0 -73
- package/foundry.lock +0 -5
- package/sphinx.lock +0 -507
|
@@ -15,6 +15,24 @@ import {JBTokenAmount} from "../structs/JBTokenAmount.sol";
|
|
|
15
15
|
/// allowances, calculates token issuance per payment, and determines cash-out reclaim amounts via the bonding curve.
|
|
16
16
|
/// Terminals delegate all state-changing accounting to this contract.
|
|
17
17
|
interface IJBTerminalStore {
|
|
18
|
+
/// @notice Emitted when a referrer is credited with a fee payment amount.
|
|
19
|
+
/// @dev `referralChainId` and `referralProjectId` are emitted as separate indexed topics so off-chain consumers
|
|
20
|
+
/// can filter directly on either dimension. The `feeVolumeByReferralOf` storage key is the packed
|
|
21
|
+
/// `(referralChainId << 48) | referralProjectId` form — indexers re-pack the two fields when looking up the
|
|
22
|
+
/// cumulative balance for a referrer.
|
|
23
|
+
/// @param terminal The terminal that originated the fee-paying call (`msg.sender` on `recordFeeReferralCreditOf`).
|
|
24
|
+
/// @param referralChainId The EIP-155 chain ID of the referrer's home chain.
|
|
25
|
+
/// @param referralProjectId The referrer's bare project ID on `referralChainId` (no chain bits).
|
|
26
|
+
/// @param amount The fee amount credited, in the terminal's accounting-context units.
|
|
27
|
+
/// @param newTotal The new value of `totalFeeVolumeOf[terminal]` after this credit.
|
|
28
|
+
event ReferralCredit(
|
|
29
|
+
address indexed terminal,
|
|
30
|
+
uint256 indexed referralChainId,
|
|
31
|
+
uint256 indexed referralProjectId,
|
|
32
|
+
uint256 amount,
|
|
33
|
+
uint256 newTotal
|
|
34
|
+
);
|
|
35
|
+
|
|
18
36
|
/// @notice The directory of terminals and controllers for projects.
|
|
19
37
|
function DIRECTORY() external view returns (IJBDirectory);
|
|
20
38
|
|
|
@@ -142,6 +160,27 @@ interface IJBTerminalStore {
|
|
|
142
160
|
view
|
|
143
161
|
returns (uint256);
|
|
144
162
|
|
|
163
|
+
/// @notice The cumulative fee payment amount credited to a referrer (chainId, projectId) pair as a result of
|
|
164
|
+
/// fee-paying calls that originated through a given terminal.
|
|
165
|
+
/// @dev Written by terminals via `recordFeeReferralCreditOf` — `msg.sender` is recorded as the writing terminal,
|
|
166
|
+
/// so a malicious caller can only pollute their own slot. Off-chain consumers should filter on known terminal
|
|
167
|
+
/// addresses.
|
|
168
|
+
/// @dev Nested by chain ID then project ID so consumers can read the credit for a specific
|
|
169
|
+
/// `(chainId, projectId)` directly without re-packing the encoded form. The encoded `(chainId << 48) | projectId`
|
|
170
|
+
/// form is used only inside the transient slot and the entry-point parameter; storage exposes the unpacked pair.
|
|
171
|
+
/// @param terminal The terminal that originated the fee-paying call.
|
|
172
|
+
/// @param referralChainId The EIP-155 chain ID of the referrer's home chain.
|
|
173
|
+
/// @param referralProjectId The referrer's bare project ID on `referralChainId`.
|
|
174
|
+
/// @return The cumulative fee amount credited.
|
|
175
|
+
function feeVolumeByReferralOf(
|
|
176
|
+
address terminal,
|
|
177
|
+
uint256 referralChainId,
|
|
178
|
+
uint256 referralProjectId
|
|
179
|
+
)
|
|
180
|
+
external
|
|
181
|
+
view
|
|
182
|
+
returns (uint256);
|
|
183
|
+
|
|
145
184
|
/// @notice Simulates a cash out without modifying state.
|
|
146
185
|
/// @param terminal The terminal to simulate the cash out from.
|
|
147
186
|
/// @param holder The address cashing out.
|
|
@@ -194,6 +233,13 @@ interface IJBTerminalStore {
|
|
|
194
233
|
view
|
|
195
234
|
returns (JBRuleset memory ruleset, uint256 tokenCount, JBPayHookSpecification[] memory hookSpecifications);
|
|
196
235
|
|
|
236
|
+
/// @notice The cumulative fee payment amount credited across all referral projects for a given terminal.
|
|
237
|
+
/// @dev Incremented in lockstep with `feeVolumeByReferralOf` so consumers can compute pro-rata shares in a
|
|
238
|
+
/// single SLOAD pair without enumerating referrers.
|
|
239
|
+
/// @param terminal The terminal that originated the fee-paying calls.
|
|
240
|
+
/// @return The cumulative total fee amount credited across all referrers for this terminal.
|
|
241
|
+
function totalFeeVolumeOf(address terminal) external view returns (uint256);
|
|
242
|
+
|
|
197
243
|
/// @notice Returns the amount of payout limit used by a terminal for a project in a given cycle.
|
|
198
244
|
/// @param terminal The terminal to get the used payout limit of.
|
|
199
245
|
/// @param projectId The ID of the project.
|
|
@@ -269,6 +315,15 @@ interface IJBTerminalStore {
|
|
|
269
315
|
JBCashOutHookSpecification[] memory hookSpecifications
|
|
270
316
|
);
|
|
271
317
|
|
|
318
|
+
/// @notice Credit a referral project with a fee payment amount routed through `msg.sender` (the calling terminal).
|
|
319
|
+
/// @dev Permissionless: the write is scoped to `msg.sender`'s slot, so an arbitrary caller can only pollute
|
|
320
|
+
/// their own bucket. The amount is normalized to `JBConstants.NATIVE_TOKEN` units (18 decimals) using the fee
|
|
321
|
+
/// project's price feeds so credits across different fee tokens (ETH, USDC, USDT, …) are summable. No-op when
|
|
322
|
+
/// `referralProjectId == 0`, when `amount.value == 0`, or when no price feed exists for the pair.
|
|
323
|
+
/// @param referralProjectId The referral project to credit.
|
|
324
|
+
/// @param amount The fee amount paid by this fee-take call (raw token amount, decimals, and currency).
|
|
325
|
+
function recordFeeReferralCreditOf(uint256 referralProjectId, JBTokenAmount calldata amount) external;
|
|
326
|
+
|
|
272
327
|
/// @notice Records a payment to a project.
|
|
273
328
|
/// @param payer The address of the payer.
|
|
274
329
|
/// @param amount The amount to pay.
|
|
@@ -3,24 +3,34 @@ pragma solidity 0.8.28;
|
|
|
3
3
|
|
|
4
4
|
/// @notice Protocol-wide constants. These define the boundaries for economic parameters throughout Juicebox.
|
|
5
5
|
library JBConstants {
|
|
6
|
-
/// @notice The
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
/// @notice The maximum reserved token percentage (basis points). 10,000 = 100% of minted tokens go to reserves.
|
|
10
|
-
uint16 public constant MAX_RESERVED_PERCENT = 10_000;
|
|
6
|
+
/// @notice The project ID that receives protocol fees. Project #1 is the protocol's own project; every
|
|
7
|
+
/// terminal forwards 2.5% of qualifying outflows to its primary fee terminal for this project.
|
|
8
|
+
uint256 public constant FEE_BENEFICIARY_PROJECT_ID = 1;
|
|
11
9
|
|
|
12
10
|
/// @notice The maximum cash-out tax rate (basis points). 10,000 = 100% tax, meaning token holders reclaim nothing.
|
|
13
11
|
uint16 public constant MAX_CASH_OUT_TAX_RATE = 10_000;
|
|
14
12
|
|
|
13
|
+
/// @notice The fee denominator. The protocol fee is `STANDARD_FEE / MAX_FEE`.
|
|
14
|
+
uint16 public constant MAX_FEE = 1000;
|
|
15
|
+
|
|
16
|
+
/// @notice The maximum reserved token percentage (basis points). 10,000 = 100% of minted tokens go to reserves.
|
|
17
|
+
uint16 public constant MAX_RESERVED_PERCENT = 10_000;
|
|
18
|
+
|
|
15
19
|
/// @notice The maximum weight cut percent (9-decimal precision). 1,000,000,000 = 100% cut per cycle (no issuance).
|
|
16
20
|
uint32 public constant MAX_WEIGHT_CUT_PERCENT = 1_000_000_000;
|
|
17
21
|
|
|
22
|
+
/// @notice The sentinel address used to represent each chain's native token (ETH on mainnet, etc.).
|
|
23
|
+
address public constant NATIVE_TOKEN = address(0x000000000000000000000000000000000000EEEe);
|
|
24
|
+
|
|
25
|
+
/// @notice The accounting-context currency identifier for `NATIVE_TOKEN`. Derived from `NATIVE_TOKEN`'s address
|
|
26
|
+
/// through the same cast (`uint32(uint160(token))`) that `JBAccountingContext.currency` uses, so a comparison
|
|
27
|
+
/// against this constant identifies the native-token currency without recomputing the cast at each call site.
|
|
28
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
29
|
+
uint32 public constant NATIVE_TOKEN_CURRENCY = uint32(uint160(NATIVE_TOKEN));
|
|
30
|
+
|
|
18
31
|
/// @notice The denominator for split percentages (9-decimal precision). A split of 1,000,000,000 = 100%.
|
|
19
32
|
uint32 public constant SPLITS_TOTAL_PERCENT = 1_000_000_000;
|
|
20
33
|
|
|
21
|
-
/// @notice The fee denominator. The protocol fee is `STANDARD_FEE / MAX_FEE`.
|
|
22
|
-
uint16 public constant MAX_FEE = 1000;
|
|
23
|
-
|
|
24
34
|
/// @notice The standard protocol fee numerator. The protocol fee is `STANDARD_FEE / MAX_FEE` = 2.5%.
|
|
25
35
|
uint16 public constant STANDARD_FEE = 25;
|
|
26
36
|
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
|
+
|
|
4
|
+
import {IJBFeeTerminal} from "../interfaces/IJBFeeTerminal.sol";
|
|
5
|
+
import {JBFee} from "../structs/JBFee.sol";
|
|
6
|
+
import {JBFees} from "./JBFees.sol";
|
|
7
|
+
|
|
8
|
+
/// @notice External-library implementation of held-fee storage operations for `JBMultiTerminal`.
|
|
9
|
+
/// @dev Functions are `external` so they live in this library's deployed bytecode and are reached from the caller
|
|
10
|
+
/// terminal via `DELEGATECALL`, keeping terminal bytecode below the EIP-170 limit. Storage refs are passed as
|
|
11
|
+
/// parameters so the library reads/writes the caller's mappings.
|
|
12
|
+
library JBHeldFees {
|
|
13
|
+
/// @notice Returns held fees back to a project's balance based on the specified incoming amount.
|
|
14
|
+
/// @dev Walks unprocessed held fees in storage order. For each fee, checks if the incoming `amount` covers
|
|
15
|
+
/// the original net payout (gross minus fee). If yes, the fee is fully credited back and the entry is
|
|
16
|
+
/// tombstoned by advancing the index. If only part of the fee can be refunded, the entry's stored gross
|
|
17
|
+
/// amount is shrunk in place using the back-calculated fee so a future top-up can still settle the remainder.
|
|
18
|
+
/// @dev Partial refunds use `standardFeeAmountResultingIn` so repaying a dust amount cannot both credit the
|
|
19
|
+
/// payer project and leave the fee project owed the 1-unit minimum fee. Reads `caller` from a parameter
|
|
20
|
+
/// instead of `msg.sender` because the library runs under `DELEGATECALL` from the terminal — `msg.sender`
|
|
21
|
+
/// here is the original external caller of the terminal, not the meta-tx-aware caller the terminal would
|
|
22
|
+
/// emit. The terminal passes `_msgSender()` so the event carries the meta-tx sender as intended.
|
|
23
|
+
/// @param heldFeesOf The terminal's held-fee storage mapping.
|
|
24
|
+
/// @param nextHeldFeeIndexOf The terminal's per-project/token next-index storage mapping.
|
|
25
|
+
/// @param projectId The project to return held fees to.
|
|
26
|
+
/// @param token The token that the held fees are in.
|
|
27
|
+
/// @param amount The incoming amount available to match against held fees.
|
|
28
|
+
/// @param caller The address that triggered the return, forwarded into the `ReturnHeldFees` event.
|
|
29
|
+
/// @return returnedFees The total fee amount returned to the project (sum of fully and partially refunded fees).
|
|
30
|
+
function returnHeldFees(
|
|
31
|
+
mapping(uint256 => mapping(address => JBFee[])) storage heldFeesOf,
|
|
32
|
+
mapping(uint256 => mapping(address => uint256)) storage nextHeldFeeIndexOf,
|
|
33
|
+
uint256 projectId,
|
|
34
|
+
address token,
|
|
35
|
+
uint256 amount,
|
|
36
|
+
address caller
|
|
37
|
+
)
|
|
38
|
+
external
|
|
39
|
+
returns (uint256 returnedFees)
|
|
40
|
+
{
|
|
41
|
+
// The first slot not yet returned, processed, or forgiven. Earlier slots are tombstones and skipped.
|
|
42
|
+
uint256 startIndex = nextHeldFeeIndexOf[projectId][token];
|
|
43
|
+
|
|
44
|
+
// Upper bound for the loop. Returning held fees never appends new entries, so the live length is fixed
|
|
45
|
+
// for the duration of this call.
|
|
46
|
+
uint256 numberOfHeldFees = heldFeesOf[projectId][token].length;
|
|
47
|
+
|
|
48
|
+
// No live entries — nothing to refund. Early return keeps the gas cost predictable for projects that
|
|
49
|
+
// never held a fee.
|
|
50
|
+
if (startIndex >= numberOfHeldFees) return 0;
|
|
51
|
+
|
|
52
|
+
// Tracks how much of the incoming `amount` is still available to match against further held fees as the
|
|
53
|
+
// loop consumes them. Initialized to the full incoming amount.
|
|
54
|
+
uint256 leftoverAmount = amount;
|
|
55
|
+
|
|
56
|
+
// Tracks how far the live-entry window has advanced. Only persisted if it actually moves, to avoid an
|
|
57
|
+
// unnecessary SSTORE when the loop ends without fully refunding any fee.
|
|
58
|
+
uint256 newStartIndex = startIndex;
|
|
59
|
+
|
|
60
|
+
for (uint256 i = startIndex; i < numberOfHeldFees;) {
|
|
61
|
+
// Stop early once the incoming amount has been fully consumed. Remaining held fees stay live and
|
|
62
|
+
// can be refunded by a future top-up.
|
|
63
|
+
if (leftoverAmount == 0) break;
|
|
64
|
+
|
|
65
|
+
// Snapshot the held fee into memory so subsequent reads (`.amount`) don't re-fetch from storage.
|
|
66
|
+
JBFee memory heldFee = heldFeesOf[projectId][token][i];
|
|
67
|
+
|
|
68
|
+
// Recompute the standard fee that was originally withheld from this entry's gross amount.
|
|
69
|
+
uint256 feeAmount = JBFees.standardFeeAmountFrom(heldFee.amount);
|
|
70
|
+
|
|
71
|
+
// The net amount that originally left the project after the fee was withheld — the deposit threshold
|
|
72
|
+
// this incoming amount must clear to fully release the fee.
|
|
73
|
+
uint256 amountPaidOut = heldFee.amount - feeAmount;
|
|
74
|
+
|
|
75
|
+
if (leftoverAmount >= amountPaidOut) {
|
|
76
|
+
// Full refund: the incoming amount covers everything the project paid out for this entry, so
|
|
77
|
+
// the whole fee comes back. Consume `amountPaidOut` from `leftoverAmount` and advance the
|
|
78
|
+
// tombstone window past this slot.
|
|
79
|
+
unchecked {
|
|
80
|
+
leftoverAmount -= amountPaidOut;
|
|
81
|
+
returnedFees += feeAmount;
|
|
82
|
+
}
|
|
83
|
+
newStartIndex = i + 1;
|
|
84
|
+
} else {
|
|
85
|
+
// Partial refund: only some of the original payout has been redeposited. Back-calculate the
|
|
86
|
+
// fee that corresponds to the remaining net amount (using `standardFeeAmountResultingIn` so
|
|
87
|
+
// the fee project never gets shorted on dust). Shrink the stored gross amount by both the
|
|
88
|
+
// refunded net and its fee, leaving the slot live so the leftover can be refunded later.
|
|
89
|
+
feeAmount = JBFees.standardFeeAmountResultingIn(leftoverAmount);
|
|
90
|
+
unchecked {
|
|
91
|
+
// `JBFee.amount` is `uint224`; the subtraction operand is `uint256`. Narrow the operand to
|
|
92
|
+
// `uint224` for the in-place update. The held gross amount was itself stored as `uint224`
|
|
93
|
+
// and the partial refund is bounded above by it, so the narrowed subtrahend always fits.
|
|
94
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
95
|
+
heldFeesOf[projectId][token][i].amount -= uint224(leftoverAmount + feeAmount);
|
|
96
|
+
returnedFees += feeAmount;
|
|
97
|
+
}
|
|
98
|
+
// All of the incoming amount has been matched — exit the loop next iteration.
|
|
99
|
+
leftoverAmount = 0;
|
|
100
|
+
}
|
|
101
|
+
unchecked {
|
|
102
|
+
++i;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Persist the new tombstone boundary only if any entry was fully refunded — partial refunds leave the
|
|
107
|
+
// boundary alone so they can be revisited.
|
|
108
|
+
if (startIndex != newStartIndex) nextHeldFeeIndexOf[projectId][token] = newStartIndex;
|
|
109
|
+
|
|
110
|
+
// Emit through the interface qualifier so the event topic matches what `IJBFeeTerminal` declares. The
|
|
111
|
+
// log surfaces under the calling terminal's address (we're inside its DELEGATECALL frame), so off-chain
|
|
112
|
+
// consumers filter on the terminal as usual.
|
|
113
|
+
emit IJBFeeTerminal.ReturnHeldFees({
|
|
114
|
+
projectId: projectId,
|
|
115
|
+
token: token,
|
|
116
|
+
amount: amount,
|
|
117
|
+
returnedFees: returnedFees,
|
|
118
|
+
leftoverAmount: leftoverAmount,
|
|
119
|
+
caller: caller
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/// @notice Returns up to `count` held fees for a project/token, starting from the next unprocessed index.
|
|
124
|
+
/// @param heldFeesOf The terminal's held-fee storage mapping (project => token => array).
|
|
125
|
+
/// @param nextHeldFeeIndexOf The terminal's per-project/token next-index storage mapping.
|
|
126
|
+
/// @param projectId The ID of the project to read held fees for.
|
|
127
|
+
/// @param token The token the fees are denominated in.
|
|
128
|
+
/// @param count The maximum number of held fees to return.
|
|
129
|
+
/// @return heldFees A view-only copy of the unprocessed held fees, in storage order.
|
|
130
|
+
function viewHeldFees(
|
|
131
|
+
mapping(uint256 => mapping(address => JBFee[])) storage heldFeesOf,
|
|
132
|
+
mapping(uint256 => mapping(address => uint256)) storage nextHeldFeeIndexOf,
|
|
133
|
+
uint256 projectId,
|
|
134
|
+
address token,
|
|
135
|
+
uint256 count
|
|
136
|
+
)
|
|
137
|
+
external
|
|
138
|
+
view
|
|
139
|
+
returns (JBFee[] memory heldFees)
|
|
140
|
+
{
|
|
141
|
+
// The first slot not yet returned, processed, or forgiven. Slots before this are tombstones (zeroed by
|
|
142
|
+
// `processHeldFeesOf` / `returnHeldFees`) and must be skipped so callers only see live fees.
|
|
143
|
+
uint256 startIndex = nextHeldFeeIndexOf[projectId][token];
|
|
144
|
+
|
|
145
|
+
// Total entries ever appended for this project/token (live + tombstoned). Used as the upper bound when
|
|
146
|
+
// bounding `count`.
|
|
147
|
+
uint256 numberOfHeldFees = heldFeesOf[projectId][token].length;
|
|
148
|
+
|
|
149
|
+
// Nothing live to return — either the array was never populated, or every entry has already been processed
|
|
150
|
+
// since the last full-array delete. Return an empty array so callers can branch on `.length == 0`.
|
|
151
|
+
if (startIndex >= numberOfHeldFees) return new JBFee[](0);
|
|
152
|
+
|
|
153
|
+
// Cap `count` to the number of live entries so we don't index past `length`. Keeps the common
|
|
154
|
+
// "ask for more than there is" case from over-allocating the return buffer.
|
|
155
|
+
if (startIndex + count > numberOfHeldFees) count = numberOfHeldFees - startIndex;
|
|
156
|
+
|
|
157
|
+
// Allocate the return buffer at exactly the size we'll fill — saves the caller from filtering trailing
|
|
158
|
+
// empty entries.
|
|
159
|
+
heldFees = new JBFee[](count);
|
|
160
|
+
|
|
161
|
+
// Copy live entries from storage into the return buffer. `++i` is unchecked because `i < count` already
|
|
162
|
+
// bounds it well below `type(uint256).max`.
|
|
163
|
+
for (uint256 i; i < count;) {
|
|
164
|
+
heldFees[i] = heldFeesOf[projectId][token][startIndex + i];
|
|
165
|
+
unchecked {
|
|
166
|
+
++i;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
package/src/structs/JBFee.sol
CHANGED
|
@@ -2,11 +2,22 @@
|
|
|
2
2
|
pragma solidity ^0.8.0;
|
|
3
3
|
|
|
4
4
|
/// @custom:member amount The total amount the fee was taken from, as a fixed point number with the same number of
|
|
5
|
-
/// decimals as the token's accounting context.
|
|
5
|
+
/// decimals as the token's accounting context. `uint224` covers any realistic per-call fee basis (max
|
|
6
|
+
/// ~2.7e49 ETH-equivalent at 18 decimals) and is the same width the protocol's payout/surplus-allowance limits use.
|
|
7
|
+
/// @custom:member referralChainId The EIP-155 chain ID of the referrer's home chain, captured from the upper bits of
|
|
8
|
+
/// the transient referral encoding at fee-take time. A value of `0` is the "current chain" sentinel — a referrer that
|
|
9
|
+
/// only encoded a project ID without a chain prefix is credited on the chain the fee is paid on. Packing this beside
|
|
10
|
+
/// `amount` in the same storage slot avoids growing `JBFee` to a third slot.
|
|
6
11
|
/// @custom:member beneficiary The address that will receive the tokens that are minted as a result of the fee payment.
|
|
7
12
|
/// @custom:member unlockTimestamp The timestamp at which the fee is unlocked and can be processed.
|
|
13
|
+
/// @custom:member referralProjectId The referrer's project ID on `referralChainId`. Captured from the lower bits of
|
|
14
|
+
/// `currentReferralProjectId` at fee-take time so held-fee attribution survives the 28-day hold window. The on-chain
|
|
15
|
+
/// transient slot, function argument, and `feeVolumeByReferralOf` mapping key all use the packed `uint256` form
|
|
16
|
+
/// `(referralChainId << 48) | referralProjectId`; this struct stores the two halves separately for cheap storage.
|
|
8
17
|
struct JBFee {
|
|
9
|
-
|
|
18
|
+
uint224 amount;
|
|
19
|
+
uint32 referralChainId;
|
|
10
20
|
address beneficiary;
|
|
11
21
|
uint48 unlockTimestamp;
|
|
22
|
+
uint48 referralProjectId;
|
|
12
23
|
}
|
|
@@ -18,7 +18,8 @@ contract MaliciousPayoutBeneficiary is IERC721Receiver, Test {
|
|
|
18
18
|
amount: 5 * 10 ** 18,
|
|
19
19
|
currency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
20
20
|
token: JBConstants.NATIVE_TOKEN,
|
|
21
|
-
minTokensPaidOut: 0
|
|
21
|
+
minTokensPaidOut: 0,
|
|
22
|
+
referralProjectId: 0
|
|
22
23
|
});
|
|
23
24
|
assertEq(_reentrantPayout, 0);
|
|
24
25
|
}
|
|
@@ -49,7 +50,8 @@ contract MaliciousAllowanceBeneficiary is IERC721Receiver, Test {
|
|
|
49
50
|
minTokensPaidOut: 0,
|
|
50
51
|
beneficiary: payable(address(this)),
|
|
51
52
|
feeBeneficiary: payable(0x000000000000000000000000000000000000007B),
|
|
52
|
-
memo: "MEMO"
|
|
53
|
+
memo: "MEMO",
|
|
54
|
+
referralProjectId: 0
|
|
53
55
|
});
|
|
54
56
|
}
|
|
55
57
|
|
package/CHANGELOG.md
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## Scope
|
|
4
|
-
|
|
5
|
-
This file describes the verified change from `nana-core-v5` to the current `nana-core-v6` repo.
|
|
6
|
-
|
|
7
|
-
## Current v6 surface
|
|
8
|
-
|
|
9
|
-
- `JBController`
|
|
10
|
-
- `JBMultiTerminal`
|
|
11
|
-
- `JBTerminalStore`
|
|
12
|
-
- `JBRulesets`
|
|
13
|
-
- `JBTokens`
|
|
14
|
-
- the shared core interfaces, structs, and libraries under `src/`
|
|
15
|
-
|
|
16
|
-
## Summary
|
|
17
|
-
|
|
18
|
-
- v6 adds explicit preview APIs for pay and cash-out flows. Integrations can simulate more of the terminal path directly from the core contracts.
|
|
19
|
-
- Token metadata is more editable than in v5. The controller now exposes a dedicated token-metadata update path.
|
|
20
|
-
- Approval-hook handling is safer. The v6 codebase and tests are built around preventing a bad approval hook from freezing project behavior.
|
|
21
|
-
- Fee accounting is tighter than in v5, especially around fee-free surplus behavior and cross-flow bookkeeping.
|
|
22
|
-
- The repo carries much broader test coverage than the v5 tree, including dedicated review, invariant, fork, and formal-style suites.
|
|
23
|
-
- The implementation baseline moved from the v5 `0.8.23` world to `0.8.28`.
|
|
24
|
-
|
|
25
|
-
## Verified deltas
|
|
26
|
-
|
|
27
|
-
- `IJBTerminal.previewPayFor(...)` is new.
|
|
28
|
-
- `IJBCashOutTerminal.previewCashOutFrom(...)` and `IJBTerminalStore.previewCashOutFrom(...)` are new preview surfaces.
|
|
29
|
-
- `IJBController.setTokenMetadataOf(uint256,string,string)` is new.
|
|
30
|
-
- `IJBController.addPriceFeed(...)` became `addPriceFeedFor(...)`.
|
|
31
|
-
- `IJBTerminal.currentSurplusOf(...)` now takes `address[] calldata tokens` instead of the old accounting-context array input.
|
|
32
|
-
- The interface surface adds explicit hook-spec return types to preview flows, which changes what off-chain callers can and should decode.
|
|
33
|
-
|
|
34
|
-
## Breaking ABI changes
|
|
35
|
-
|
|
36
|
-
- `IJBController.addPriceFeed(...)` was renamed to `addPriceFeedFor(...)`.
|
|
37
|
-
- `IJBController.setTokenMetadataOf(...)` is new and sits on the core controller surface.
|
|
38
|
-
- `IJBController.previewMintOf(...)` is new.
|
|
39
|
-
- `IJBTerminal.previewPayFor(...)` is new.
|
|
40
|
-
- `IJBCashOutTerminal.previewCashOutFrom(...)` is new.
|
|
41
|
-
- `IJBTerminalStore.previewPayFrom(...)` and `previewCashOutFrom(...)` are new.
|
|
42
|
-
- `IJBTerminal.currentSurplusOf(...)` changed parameter shape.
|
|
43
|
-
|
|
44
|
-
## Indexer impact
|
|
45
|
-
|
|
46
|
-
- `SplitHookReverted` is a new controller event.
|
|
47
|
-
- Preview functions do not emit events, but they change what frontends and simulation services can derive without sending transactions.
|
|
48
|
-
- If your indexer inferred token metadata immutability from v5, that assumption is no longer safe once `setTokenMetadataOf(...)` is in use.
|
|
49
|
-
|
|
50
|
-
## Migration notes
|
|
51
|
-
|
|
52
|
-
- Rebuild against the v6 interfaces. This repo is too central for hand-maintained ABI diffs to be trustworthy.
|
|
53
|
-
- Review any integration that assumed old ruleset-cache behavior, old preview availability, or old token-metadata immutability.
|
|
54
|
-
- If your system relied on subtle fee-free cash-out behavior from v5, re-validate it against the v6 accounting model.
|
|
55
|
-
|
|
56
|
-
## ABI appendix
|
|
57
|
-
|
|
58
|
-
- Added functions
|
|
59
|
-
- `IJBTerminal.previewPayFor(...)`
|
|
60
|
-
- `IJBCashOutTerminal.previewCashOutFrom(...)`
|
|
61
|
-
- `IJBTerminalStore.previewPayFrom(...)`
|
|
62
|
-
- `IJBTerminalStore.previewCashOutFrom(...)`
|
|
63
|
-
- `IJBController.previewMintOf(...)`
|
|
64
|
-
- `IJBController.setTokenMetadataOf(...)`
|
|
65
|
-
- Renamed functions
|
|
66
|
-
- `IJBController.addPriceFeed(...)` -> `addPriceFeedFor(...)`
|
|
67
|
-
- Changed function shapes
|
|
68
|
-
- `IJBTerminal.currentSurplusOf(...)`
|
|
69
|
-
- Added events
|
|
70
|
-
- `SplitHookReverted`
|
|
71
|
-
- Added migration-sensitive capabilities
|
|
72
|
-
- mutable token metadata
|
|
73
|
-
- preview-oriented hook-spec decoding
|