@bananapus/core-v6 0.0.42 → 0.0.44
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/CHANGELOG.md +1 -1
- package/README.md +3 -3
- package/package.json +2 -2
- package/references/entrypoints.md +3 -1
- package/references/types-errors-events.md +2 -3
- package/src/JBChainlinkV3PriceFeed.sol +14 -6
- package/src/JBChainlinkV3SequencerPriceFeed.sol +5 -6
- package/src/JBController.sol +84 -68
- package/src/JBDirectory.sol +4 -7
- package/src/JBERC20.sol +8 -7
- package/src/JBFeelessAddresses.sol +32 -13
- package/src/JBFundAccessLimits.sol +12 -4
- package/src/JBMultiTerminal.sol +39 -61
- package/src/JBPermissions.sol +6 -3
- package/src/JBPrices.sol +18 -12
- package/src/JBRulesets.sol +4 -25
- package/src/JBSplits.sol +13 -5
- package/src/JBTerminalStore.sol +46 -82
- package/src/JBTokens.sol +11 -13
- package/src/abstract/JBControlled.sol +1 -2
- package/src/interfaces/IJBController.sol +0 -6
- package/src/interfaces/IJBFeelessAddresses.sol +17 -7
- package/src/libraries/JBCashOuts.sol +6 -2
- package/src/libraries/JBCurrencyIds.sol +3 -0
- package/src/libraries/JBMetadataResolver.sol +20 -12
- package/src/libraries/JBPayoutSplitGroupLib.sol +8 -3
- package/src/libraries/JBRulesetMetadataResolver.sol +4 -4
- package/src/libraries/JBSplitGroupIds.sol +1 -0
- package/src/periphery/JBDeadline1Day.sol +1 -0
- package/src/periphery/JBDeadline3Days.sol +1 -0
- package/src/periphery/JBDeadline3Hours.sol +1 -0
- package/src/periphery/JBDeadline7Days.sol +1 -0
- package/src/structs/JBBeforeCashOutRecordedContext.sol +3 -2
- package/src/structs/JBRulesetMetadata.sol +3 -3
- package/test/helpers/JBTest.sol +3 -3
package/src/JBTokens.sol
CHANGED
|
@@ -19,8 +19,8 @@ contract JBTokens is JBControlled, IJBTokens {
|
|
|
19
19
|
// --------------------------- custom errors ------------------------- //
|
|
20
20
|
//*********************************************************************//
|
|
21
21
|
|
|
22
|
-
error JBTokens_EmptyName();
|
|
23
|
-
error JBTokens_EmptySymbol();
|
|
22
|
+
error JBTokens_EmptyName(uint256 projectId);
|
|
23
|
+
error JBTokens_EmptySymbol(uint256 projectId);
|
|
24
24
|
error JBTokens_EmptyToken(uint256 projectId);
|
|
25
25
|
error JBTokens_InsufficientCredits(uint256 count, uint256 creditBalance);
|
|
26
26
|
error JBTokens_InsufficientTokensToBurn(uint256 count, uint256 tokenBalance);
|
|
@@ -28,7 +28,7 @@ contract JBTokens is JBControlled, IJBTokens {
|
|
|
28
28
|
error JBTokens_ProjectAlreadyHasToken(IJBToken token);
|
|
29
29
|
error JBTokens_TokenAlreadyBeingUsed(uint256 projectId);
|
|
30
30
|
error JBTokens_TokenCantBeAdded(uint256 projectId);
|
|
31
|
-
error JBTokens_TokenNotFound();
|
|
31
|
+
error JBTokens_TokenNotFound(uint256 projectId);
|
|
32
32
|
error JBTokens_TokensMustHave18Decimals(uint256 decimals);
|
|
33
33
|
|
|
34
34
|
//*********************************************************************//
|
|
@@ -49,7 +49,6 @@ contract JBTokens is JBControlled, IJBTokens {
|
|
|
49
49
|
|
|
50
50
|
/// @notice The project ID that a given ERC-20 token is associated with.
|
|
51
51
|
/// @custom:param token The address of the token associated with the project.
|
|
52
|
-
// slither-disable-next-line unused-return
|
|
53
52
|
mapping(IJBToken token => uint256) public override projectIdOf;
|
|
54
53
|
|
|
55
54
|
/// @notice Each project's attached token contract.
|
|
@@ -92,7 +91,7 @@ contract JBTokens is JBControlled, IJBTokens {
|
|
|
92
91
|
|
|
93
92
|
// There must be enough tokens to burn across the holder's combined token and credit balance.
|
|
94
93
|
if (count > tokenBalance + creditBalance) {
|
|
95
|
-
revert JBTokens_InsufficientTokensToBurn(count, tokenBalance + creditBalance);
|
|
94
|
+
revert JBTokens_InsufficientTokensToBurn({count: count, tokenBalance: tokenBalance + creditBalance});
|
|
96
95
|
}
|
|
97
96
|
|
|
98
97
|
// The amount of tokens to burn.
|
|
@@ -153,7 +152,7 @@ contract JBTokens is JBControlled, IJBTokens {
|
|
|
153
152
|
IJBToken token = tokenOf[projectId];
|
|
154
153
|
|
|
155
154
|
// The project must have a token contract attached.
|
|
156
|
-
if (token == IJBToken(address(0))) revert JBTokens_TokenNotFound();
|
|
155
|
+
if (token == IJBToken(address(0))) revert JBTokens_TokenNotFound({projectId: projectId});
|
|
157
156
|
|
|
158
157
|
// Get a reference to the amount of credits the holder has.
|
|
159
158
|
uint256 creditBalance = creditBalanceOf[holder][projectId];
|
|
@@ -204,10 +203,10 @@ contract JBTokens is JBControlled, IJBTokens {
|
|
|
204
203
|
returns (IJBToken token)
|
|
205
204
|
{
|
|
206
205
|
// There must be a name.
|
|
207
|
-
if (bytes(name).length == 0) revert JBTokens_EmptyName();
|
|
206
|
+
if (bytes(name).length == 0) revert JBTokens_EmptyName({projectId: projectId});
|
|
208
207
|
|
|
209
208
|
// There must be a symbol.
|
|
210
|
-
if (bytes(symbol).length == 0) revert JBTokens_EmptySymbol();
|
|
209
|
+
if (bytes(symbol).length == 0) revert JBTokens_EmptySymbol({projectId: projectId});
|
|
211
210
|
|
|
212
211
|
// The project shouldn't already have a token.
|
|
213
212
|
if (tokenOf[projectId] != IJBToken(address(0))) revert JBTokens_ProjectAlreadyHasToken(tokenOf[projectId]);
|
|
@@ -263,12 +262,11 @@ contract JBTokens is JBControlled, IJBTokens {
|
|
|
263
262
|
|
|
264
263
|
// The total supply after minting can't exceed the maximum value storable in a uint208.
|
|
265
264
|
if (supply + count > type(uint208).max) {
|
|
266
|
-
revert JBTokens_OverflowAlert(supply + count, type(uint208).max);
|
|
265
|
+
revert JBTokens_OverflowAlert({value: supply + count, limit: type(uint208).max});
|
|
267
266
|
}
|
|
268
267
|
|
|
269
268
|
if (tokensWereClaimed) {
|
|
270
269
|
// If tokens should be claimed, mint tokens into the holder's wallet.
|
|
271
|
-
// slither-disable-next-line reentrancy-events
|
|
272
270
|
token.mint({account: holder, amount: count});
|
|
273
271
|
} else {
|
|
274
272
|
// Otherwise, add the tokens to their credits and the credit supply.
|
|
@@ -333,13 +331,13 @@ contract JBTokens is JBControlled, IJBTokens {
|
|
|
333
331
|
IJBToken token = tokenOf[projectId];
|
|
334
332
|
|
|
335
333
|
// The project must have a token contract attached.
|
|
336
|
-
if (token == IJBToken(address(0))) revert JBTokens_TokenNotFound();
|
|
334
|
+
if (token == IJBToken(address(0))) revert JBTokens_TokenNotFound({projectId: projectId});
|
|
337
335
|
|
|
338
336
|
// There must be a name.
|
|
339
|
-
if (bytes(name).length == 0) revert JBTokens_EmptyName();
|
|
337
|
+
if (bytes(name).length == 0) revert JBTokens_EmptyName({projectId: projectId});
|
|
340
338
|
|
|
341
339
|
// There must be a symbol.
|
|
342
|
-
if (bytes(symbol).length == 0) revert JBTokens_EmptySymbol();
|
|
340
|
+
if (bytes(symbol).length == 0) revert JBTokens_EmptySymbol({projectId: projectId});
|
|
343
341
|
|
|
344
342
|
emit SetTokenMetadata({projectId: projectId, name: name, symbol: symbol, caller: msg.sender});
|
|
345
343
|
|
|
@@ -48,10 +48,9 @@ abstract contract JBControlled is IJBControlled {
|
|
|
48
48
|
/// @notice Only allows the controller of the specified project to proceed.
|
|
49
49
|
function _onlyControllerOf(uint256 projectId) internal view {
|
|
50
50
|
// Cache the controller address to avoid a redundant external call on revert.
|
|
51
|
-
// slither-disable-next-line calls-loop
|
|
52
51
|
address controller = address(DIRECTORY.controllerOf(projectId));
|
|
53
52
|
if (controller != msg.sender) {
|
|
54
|
-
revert JBControlled_ControllerUnauthorized(controller);
|
|
53
|
+
revert JBControlled_ControllerUnauthorized({controller: controller});
|
|
55
54
|
}
|
|
56
55
|
}
|
|
57
56
|
}
|
|
@@ -81,12 +81,6 @@ interface IJBController is IERC165, IJBProjectUriRegistry, IJBDirectoryAccessCon
|
|
|
81
81
|
address caller
|
|
82
82
|
);
|
|
83
83
|
|
|
84
|
-
/// @notice A project was prepared for migration from another controller.
|
|
85
|
-
/// @param projectId The ID of the project to prepare for migration.
|
|
86
|
-
/// @param from The controller to migrate from.
|
|
87
|
-
/// @param caller The address that called the prep migration function.
|
|
88
|
-
event PrepMigration(uint256 indexed projectId, address from, address caller);
|
|
89
|
-
|
|
90
84
|
/// @notice Rulesets were queued for a project.
|
|
91
85
|
/// @param rulesetId The ID of the first queued ruleset.
|
|
92
86
|
/// @param projectId The ID of the project.
|
|
@@ -1,21 +1,31 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity ^0.8.0;
|
|
3
3
|
|
|
4
|
-
/// @notice Tracks addresses that are exempt from fees.
|
|
4
|
+
/// @notice Tracks addresses that are exempt from fees, both globally and on a per-project basis.
|
|
5
|
+
/// @dev `projectId = 0` is the wildcard — an address feeless for project 0 is feeless for ALL projects.
|
|
5
6
|
interface IJBFeelessAddresses {
|
|
6
|
-
/// @notice An address's feeless status was set.
|
|
7
|
+
/// @notice An address's feeless status was set for a project (or globally if projectId is 0).
|
|
8
|
+
/// @param projectId The project the feeless status applies to. 0 means all projects.
|
|
7
9
|
/// @param addr The address whose feeless status was set.
|
|
8
10
|
/// @param isFeeless Whether the address is feeless.
|
|
9
11
|
/// @param caller The address that set the feeless status.
|
|
10
|
-
event SetFeelessAddress(address indexed addr, bool indexed isFeeless, address caller);
|
|
12
|
+
event SetFeelessAddress(uint256 indexed projectId, address indexed addr, bool indexed isFeeless, address caller);
|
|
11
13
|
|
|
12
|
-
/// @notice Returns whether the specified address is feeless
|
|
14
|
+
/// @notice Returns whether the specified address is feeless for a specific project, considering both the wildcard
|
|
15
|
+
/// (projectId 0) and project-specific feeless status.
|
|
13
16
|
/// @param addr The address to check.
|
|
14
|
-
/// @
|
|
15
|
-
|
|
17
|
+
/// @param projectId The ID of the project to check.
|
|
18
|
+
/// @return A flag indicating whether the address is feeless (globally or for the project).
|
|
19
|
+
function isFeelessFor(address addr, uint256 projectId) external view returns (bool);
|
|
16
20
|
|
|
17
|
-
/// @notice Sets whether an address is feeless.
|
|
21
|
+
/// @notice Sets whether an address is feeless globally (for all projects).
|
|
18
22
|
/// @param addr The address to set the feeless status of.
|
|
19
23
|
/// @param flag A flag indicating whether the address should be feeless.
|
|
20
24
|
function setFeelessAddress(address addr, bool flag) external;
|
|
25
|
+
|
|
26
|
+
/// @notice Sets whether an address is feeless for a specific project.
|
|
27
|
+
/// @param projectId The ID of the project. 0 means all projects (same as `setFeelessAddress`).
|
|
28
|
+
/// @param addr The address to set the feeless status of.
|
|
29
|
+
/// @param flag A flag indicating whether the address should be feeless for the project.
|
|
30
|
+
function setFeelessAddressFor(uint256 projectId, address addr, bool flag) external;
|
|
21
31
|
}
|
|
@@ -13,7 +13,7 @@ import {JBConstants} from "./JBConstants.sol";
|
|
|
13
13
|
/// reclaim amount.
|
|
14
14
|
library JBCashOuts {
|
|
15
15
|
/// @notice Thrown when the desired output cannot be achieved (e.g., cash out tax rate is 100%).
|
|
16
|
-
error JBCashOuts_DesiredOutputNotAchievable();
|
|
16
|
+
error JBCashOuts_DesiredOutputNotAchievable(uint256 desiredOutput, uint256 cashOutTaxRate, uint256 maxTaxRate);
|
|
17
17
|
|
|
18
18
|
/// @notice Returns the amount of surplus terminal tokens which can be reclaimed based on the total surplus, the
|
|
19
19
|
/// number of tokens to cash out, the total token supply, and the ruleset's cash out tax rate.
|
|
@@ -81,7 +81,11 @@ library JBCashOuts {
|
|
|
81
81
|
|
|
82
82
|
// If the cash out tax rate is at maximum, no output is achievable.
|
|
83
83
|
if (cashOutTaxRate == JBConstants.MAX_CASH_OUT_TAX_RATE) {
|
|
84
|
-
revert JBCashOuts_DesiredOutputNotAchievable(
|
|
84
|
+
revert JBCashOuts_DesiredOutputNotAchievable({
|
|
85
|
+
desiredOutput: desiredOutput,
|
|
86
|
+
cashOutTaxRate: cashOutTaxRate,
|
|
87
|
+
maxTaxRate: JBConstants.MAX_CASH_OUT_TAX_RATE
|
|
88
|
+
});
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
// If the desired output meets or exceeds the surplus, the entire supply must be cashed out.
|
|
@@ -4,6 +4,9 @@ pragma solidity 0.8.28;
|
|
|
4
4
|
/// @notice Well-known currency IDs used as the `baseCurrency` in ruleset metadata. These are distinct from accounting
|
|
5
5
|
/// context currencies (which use `uint32(uint160(tokenAddress))`). Only used for price feed lookups in `JBPrices`.
|
|
6
6
|
library JBCurrencyIds {
|
|
7
|
+
/// @notice The currency ID used for ETH-denominated price feeds.
|
|
7
8
|
uint32 public constant ETH = 1;
|
|
9
|
+
|
|
10
|
+
/// @notice The currency ID used for USD-denominated price feeds.
|
|
8
11
|
uint32 public constant USD = 2;
|
|
9
12
|
}
|
|
@@ -25,10 +25,10 @@ pragma solidity 0.8.28;
|
|
|
25
25
|
* +-----------------------+
|
|
26
26
|
*/
|
|
27
27
|
library JBMetadataResolver {
|
|
28
|
-
error JBMetadataResolver_DataNotPadded();
|
|
29
|
-
error JBMetadataResolver_LengthMismatch();
|
|
30
|
-
error JBMetadataResolver_MetadataTooLong();
|
|
31
|
-
error JBMetadataResolver_MetadataTooShort();
|
|
28
|
+
error JBMetadataResolver_DataNotPadded(uint256 dataLength);
|
|
29
|
+
error JBMetadataResolver_LengthMismatch(uint256 idsLength, uint256 datasLength);
|
|
30
|
+
error JBMetadataResolver_MetadataTooLong(uint256 offset, uint256 maxOffset);
|
|
31
|
+
error JBMetadataResolver_MetadataTooShort(uint256 metadataLength, uint256 minMetadataLength);
|
|
32
32
|
|
|
33
33
|
// The various sizes used in bytes.
|
|
34
34
|
uint256 constant ID_SIZE = 4;
|
|
@@ -61,7 +61,7 @@ library JBMetadataResolver {
|
|
|
61
61
|
returns (bytes memory newMetadata)
|
|
62
62
|
{
|
|
63
63
|
// Validate data padding upfront so that the fast path below cannot bypass the check.
|
|
64
|
-
if (dataToAdd.length < 32) revert JBMetadataResolver_DataNotPadded();
|
|
64
|
+
if (dataToAdd.length < 32) revert JBMetadataResolver_DataNotPadded({dataLength: dataToAdd.length});
|
|
65
65
|
|
|
66
66
|
// Empty original metadata and maybe something in the first 32 bytes: create new metadata
|
|
67
67
|
if (originalMetadata.length <= RESERVED_SIZE) {
|
|
@@ -70,7 +70,11 @@ library JBMetadataResolver {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
// There is something in the table offset, but not a valid entry - avoid overwriting
|
|
73
|
-
if (originalMetadata.length < RESERVED_SIZE + ID_SIZE + 1)
|
|
73
|
+
if (originalMetadata.length < RESERVED_SIZE + ID_SIZE + 1) {
|
|
74
|
+
revert JBMetadataResolver_MetadataTooShort({
|
|
75
|
+
metadataLength: originalMetadata.length, minMetadataLength: RESERVED_SIZE + ID_SIZE + 1
|
|
76
|
+
});
|
|
77
|
+
}
|
|
74
78
|
|
|
75
79
|
// Get the first data offset - upcast to avoid overflow (same for other offset)...
|
|
76
80
|
uint256 firstOffset = uint8(originalMetadata[RESERVED_SIZE + ID_SIZE]);
|
|
@@ -104,7 +108,9 @@ library JBMetadataResolver {
|
|
|
104
108
|
// Increment every offset by 1 (as the table now takes one more word)
|
|
105
109
|
for (uint256 j = RESERVED_SIZE + ID_SIZE; j < lastOffsetIndex + 1; j += TOTAL_ID_SIZE) {
|
|
106
110
|
uint256 incremented = uint256(uint8(originalMetadata[j])) + 1;
|
|
107
|
-
if (incremented > 255)
|
|
111
|
+
if (incremented > 255) {
|
|
112
|
+
revert JBMetadataResolver_MetadataTooLong({offset: incremented, maxOffset: 255});
|
|
113
|
+
}
|
|
108
114
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
109
115
|
newMetadata[j] = bytes1(uint8(incremented));
|
|
110
116
|
}
|
|
@@ -121,7 +127,7 @@ library JBMetadataResolver {
|
|
|
121
127
|
// taken by the last data
|
|
122
128
|
// Compute in uint256 first — casting directly to uint8 silently wraps offsets > 255.
|
|
123
129
|
uint256 newOffset = lastOffset + numberOfWordslastData;
|
|
124
|
-
if (newOffset > 255) revert JBMetadataResolver_MetadataTooLong();
|
|
130
|
+
if (newOffset > 255) revert JBMetadataResolver_MetadataTooLong({offset: newOffset, maxOffset: 255});
|
|
125
131
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
126
132
|
newMetadata = abi.encodePacked(newMetadata, idToAdd, bytes1(uint8(newOffset)));
|
|
127
133
|
|
|
@@ -167,7 +173,9 @@ library JBMetadataResolver {
|
|
|
167
173
|
/// @param datas The list of corresponding datas
|
|
168
174
|
/// @return metadata The resulting metadata
|
|
169
175
|
function createMetadata(bytes4[] memory ids, bytes[] memory datas) internal pure returns (bytes memory metadata) {
|
|
170
|
-
if (ids.length != datas.length)
|
|
176
|
+
if (ids.length != datas.length) {
|
|
177
|
+
revert JBMetadataResolver_LengthMismatch({idsLength: ids.length, datasLength: datas.length});
|
|
178
|
+
}
|
|
171
179
|
|
|
172
180
|
// Return empty bytes for empty input arrays to avoid underflow in offset calculation below.
|
|
173
181
|
if (ids.length == 0) return bytes("");
|
|
@@ -191,7 +199,7 @@ library JBMetadataResolver {
|
|
|
191
199
|
bytes memory data = datas[i];
|
|
192
200
|
|
|
193
201
|
if (data.length < 32 || data.length % JBMetadataResolver.WORD_SIZE != 0) {
|
|
194
|
-
revert JBMetadataResolver_DataNotPadded();
|
|
202
|
+
revert JBMetadataResolver_DataNotPadded({dataLength: data.length});
|
|
195
203
|
}
|
|
196
204
|
|
|
197
205
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
@@ -199,7 +207,7 @@ library JBMetadataResolver {
|
|
|
199
207
|
offset += data.length / JBMetadataResolver.WORD_SIZE;
|
|
200
208
|
|
|
201
209
|
// Overflowing a bytes1?
|
|
202
|
-
if (offset > 255) revert JBMetadataResolver_MetadataTooLong();
|
|
210
|
+
if (offset > 255) revert JBMetadataResolver_MetadataTooLong({offset: offset, maxOffset: 255});
|
|
203
211
|
}
|
|
204
212
|
|
|
205
213
|
// Pad the table to a multiple of 32B
|
|
@@ -277,7 +285,7 @@ library JBMetadataResolver {
|
|
|
277
285
|
/// @param purpose A string describing the purpose associated with the id
|
|
278
286
|
/// @return id The resulting ID.
|
|
279
287
|
function getId(string memory purpose) internal view returns (bytes4) {
|
|
280
|
-
return getId(purpose, address(this));
|
|
288
|
+
return getId({purpose: purpose, target: address(this)});
|
|
281
289
|
}
|
|
282
290
|
|
|
283
291
|
/// @notice Returns an unique id following a suggested format (`xor(address(this), purpose name)` where purpose name
|
|
@@ -12,6 +12,13 @@ import {JBConstants} from "./JBConstants.sol";
|
|
|
12
12
|
/// @dev Kept local to this file because `executePayout(...)` is an implementation detail, not a shared public
|
|
13
13
|
/// interface.
|
|
14
14
|
interface IJBPayoutSplitGroupExecutor {
|
|
15
|
+
/// @notice Executes one payout split from the terminal that is using this library.
|
|
16
|
+
/// @param split The split to pay.
|
|
17
|
+
/// @param projectId The ID of the project paying the split.
|
|
18
|
+
/// @param token The token being paid out.
|
|
19
|
+
/// @param amount The amount assigned to the split.
|
|
20
|
+
/// @param originalMessageSender The account that started the payout flow.
|
|
21
|
+
/// @return netPayoutAmount The amount that was actually paid after fees or hook behavior.
|
|
15
22
|
function executePayout(
|
|
16
23
|
JBSplit calldata split,
|
|
17
24
|
uint256 projectId,
|
|
@@ -30,6 +37,7 @@ interface IJBPayoutSplitGroupExecutor {
|
|
|
30
37
|
/// are emitted from the terminal's address.
|
|
31
38
|
library JBPayoutSplitGroupLib {
|
|
32
39
|
event PayoutReverted(uint256 indexed projectId, JBSplit split, uint256 amount, bytes reason, address caller);
|
|
40
|
+
|
|
33
41
|
event SendPayoutToSplit(
|
|
34
42
|
uint256 indexed projectId,
|
|
35
43
|
uint256 indexed rulesetId,
|
|
@@ -80,7 +88,6 @@ library JBPayoutSplitGroupLib {
|
|
|
80
88
|
uint256 payoutAmount = mulDiv(leftoverAmount, split.percent, leftoverPercentage);
|
|
81
89
|
|
|
82
90
|
// The final payout amount after taking out any fees.
|
|
83
|
-
// slither-disable-next-line calls-loop
|
|
84
91
|
uint256 netPayoutAmount = _sendPayoutToSplit({
|
|
85
92
|
store: store, split: split, projectId: projectId, token: token, amount: payoutAmount, caller: caller
|
|
86
93
|
});
|
|
@@ -140,7 +147,6 @@ library JBPayoutSplitGroupLib {
|
|
|
140
147
|
// split from DoS-ing the entire payout. Failed splits' amounts are returned to the project balance via
|
|
141
148
|
// `recordAddedBalanceFor`. Payout limit consumption is correct because the project authorized the
|
|
142
149
|
// distribution.
|
|
143
|
-
// slither-disable-next-line reentrancy-events,calls-loop
|
|
144
150
|
try IJBPayoutSplitGroupExecutor(address(this))
|
|
145
151
|
.executePayout({
|
|
146
152
|
split: split, projectId: projectId, token: token, amount: amount, originalMessageSender: caller
|
|
@@ -154,7 +160,6 @@ library JBPayoutSplitGroupLib {
|
|
|
154
160
|
});
|
|
155
161
|
|
|
156
162
|
// Add balance back to the project.
|
|
157
|
-
// slither-disable-next-line calls-loop
|
|
158
163
|
store.recordAddedBalanceFor({projectId: projectId, token: token, amount: amount});
|
|
159
164
|
|
|
160
165
|
// Since the payout failed the netPayoutAmount is zero.
|
|
@@ -67,7 +67,7 @@ library JBRulesetMetadataResolver {
|
|
|
67
67
|
return ((ruleset.metadata >> 78) & 1) == 1;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
function
|
|
70
|
+
function scopeCashOutsToLocalBalances(JBRuleset memory ruleset) internal pure returns (bool) {
|
|
71
71
|
return ((ruleset.metadata >> 79) & 1) == 1;
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -123,8 +123,8 @@ library JBRulesetMetadataResolver {
|
|
|
123
123
|
if (rulesetMetadata.ownerMustSendPayouts) packed |= 1 << 77;
|
|
124
124
|
// hold fees in bit 78.
|
|
125
125
|
if (rulesetMetadata.holdFees) packed |= 1 << 78;
|
|
126
|
-
//
|
|
127
|
-
if (rulesetMetadata.
|
|
126
|
+
// scopeCashOutsToLocalBalances in bit 79.
|
|
127
|
+
if (rulesetMetadata.scopeCashOutsToLocalBalances) packed |= 1 << 79;
|
|
128
128
|
// use pay data source in bit 80.
|
|
129
129
|
if (rulesetMetadata.useDataHookForPay) packed |= 1 << 80;
|
|
130
130
|
// use cash out data source in bit 81.
|
|
@@ -154,7 +154,7 @@ library JBRulesetMetadataResolver {
|
|
|
154
154
|
allowAddPriceFeed: allowAddPriceFeed(ruleset),
|
|
155
155
|
ownerMustSendPayouts: ownerMustSendPayouts(ruleset),
|
|
156
156
|
holdFees: holdFees(ruleset),
|
|
157
|
-
|
|
157
|
+
scopeCashOutsToLocalBalances: scopeCashOutsToLocalBalances(ruleset),
|
|
158
158
|
useDataHookForPay: useDataHookForPay(ruleset),
|
|
159
159
|
useDataHookForCashOut: useDataHookForCashOut(ruleset),
|
|
160
160
|
dataHook: dataHook(ruleset),
|
|
@@ -4,5 +4,6 @@ pragma solidity 0.8.28;
|
|
|
4
4
|
/// @notice Well-known split group IDs. The reserved tokens group (ID 1) defines how a project's reserved tokens are
|
|
5
5
|
/// distributed. Payout split groups use the token address cast to `uint256(uint160(token))` as their group ID.
|
|
6
6
|
library JBSplitGroupIds {
|
|
7
|
+
/// @notice The split group ID used for reserved token distributions.
|
|
7
8
|
uint256 public constant RESERVED_TOKENS = 1;
|
|
8
9
|
}
|
|
@@ -14,7 +14,8 @@ import {JBTokenAmount} from "./JBTokenAmount.sol";
|
|
|
14
14
|
/// @custom:member surplus The surplus amount used for the calculation, as a fixed point number with 18 decimals.
|
|
15
15
|
/// Includes the token of the surplus, the surplus value, the number of decimals
|
|
16
16
|
/// included, and the currency of the surplus.
|
|
17
|
-
/// @custom:member
|
|
17
|
+
/// @custom:member scopeCashOutsToLocalBalances If true, omnichain hooks should use only local chain balances for cash
|
|
18
|
+
/// outs (skip cross-chain aggregation).
|
|
18
19
|
/// @custom:member cashOutTaxRate The cash out tax rate of the ruleset the cash out is made during, out of
|
|
19
20
|
/// `JBConstants.MAX_CASH_OUT_TAX_RATE`.
|
|
20
21
|
/// @custom:member beneficiaryIsFeeless Whether the cash out's beneficiary is a feeless address. Useful for data hooks
|
|
@@ -29,7 +30,7 @@ struct JBBeforeCashOutRecordedContext {
|
|
|
29
30
|
uint256 cashOutCount;
|
|
30
31
|
uint256 totalSupply;
|
|
31
32
|
JBTokenAmount surplus;
|
|
32
|
-
bool
|
|
33
|
+
bool scopeCashOutsToLocalBalances;
|
|
33
34
|
uint256 cashOutTaxRate;
|
|
34
35
|
bool beneficiaryIsFeeless;
|
|
35
36
|
bytes metadata;
|
|
@@ -21,8 +21,8 @@ pragma solidity ^0.8.0;
|
|
|
21
21
|
/// @custom:member allowAddPriceFeed If `true`, the project can register new price feeds in `JBPrices`.
|
|
22
22
|
/// @custom:member ownerMustSendPayouts If `true`, only the project owner can trigger payout distribution.
|
|
23
23
|
/// @custom:member holdFees If `true`, fees are accumulated but not processed until a future ruleset (or manually).
|
|
24
|
-
/// @custom:member
|
|
25
|
-
///
|
|
24
|
+
/// @custom:member scopeCashOutsToLocalBalances If `true`, omnichain cash-out calculations use only the local chain's
|
|
25
|
+
/// balances (not cross-chain aggregates).
|
|
26
26
|
/// @custom:member useDataHookForPay If `true`, the data hook is called before recording payments.
|
|
27
27
|
/// @custom:member useDataHookForCashOut If `true`, the data hook is called before recording cash outs.
|
|
28
28
|
/// @custom:member dataHook Contract called before pay/cash-out to potentially override token counts or add hooks.
|
|
@@ -42,7 +42,7 @@ struct JBRulesetMetadata {
|
|
|
42
42
|
bool allowAddPriceFeed;
|
|
43
43
|
bool ownerMustSendPayouts;
|
|
44
44
|
bool holdFees;
|
|
45
|
-
bool
|
|
45
|
+
bool scopeCashOutsToLocalBalances;
|
|
46
46
|
bool useDataHookForPay;
|
|
47
47
|
bool useDataHookForCashOut;
|
|
48
48
|
address dataHook;
|
package/test/helpers/JBTest.sol
CHANGED
|
@@ -43,7 +43,7 @@ contract JBTest is Test {
|
|
|
43
43
|
allowAddAccountingContext: true,
|
|
44
44
|
allowAddPriceFeed: true,
|
|
45
45
|
holdFees: false,
|
|
46
|
-
|
|
46
|
+
scopeCashOutsToLocalBalances: true,
|
|
47
47
|
useDataHookForPay: false,
|
|
48
48
|
useDataHookForCashOut: false,
|
|
49
49
|
dataHook: address(0),
|
|
@@ -81,7 +81,7 @@ contract JBTest is Test {
|
|
|
81
81
|
allowAddAccountingContext: true,
|
|
82
82
|
allowAddPriceFeed: false,
|
|
83
83
|
holdFees: false,
|
|
84
|
-
|
|
84
|
+
scopeCashOutsToLocalBalances: false,
|
|
85
85
|
useDataHookForPay: false,
|
|
86
86
|
useDataHookForCashOut: false,
|
|
87
87
|
dataHook: address(0),
|
|
@@ -105,7 +105,7 @@ contract JBTest is Test {
|
|
|
105
105
|
allowAddAccountingContext: false,
|
|
106
106
|
allowAddPriceFeed: false,
|
|
107
107
|
holdFees: true,
|
|
108
|
-
|
|
108
|
+
scopeCashOutsToLocalBalances: true,
|
|
109
109
|
useDataHookForPay: false,
|
|
110
110
|
useDataHookForCashOut: false,
|
|
111
111
|
dataHook: address(0),
|