@bananapus/core-v6 0.0.42 → 0.0.43
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 +1 -1
- package/references/types-errors-events.md +0 -1
- package/src/JBChainlinkV3PriceFeed.sol +14 -6
- package/src/JBChainlinkV3SequencerPriceFeed.sol +5 -6
- package/src/JBController.sol +61 -50
- package/src/JBDirectory.sol +4 -7
- package/src/JBERC20.sol +8 -7
- package/src/JBFundAccessLimits.sol +12 -4
- package/src/JBMultiTerminal.sol +14 -39
- 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 +36 -25
- package/src/JBTokens.sol +11 -13
- package/src/abstract/JBControlled.sol +1 -2
- package/src/interfaces/IJBController.sol +0 -6
- 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/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/JBTerminalStore.sol
CHANGED
|
@@ -40,8 +40,10 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
40
40
|
//*********************************************************************//
|
|
41
41
|
|
|
42
42
|
error JBTerminalStore_AccountingContextAlreadySet(address token);
|
|
43
|
-
error JBTerminalStore_AccountingContextDecimalsMismatch(
|
|
44
|
-
|
|
43
|
+
error JBTerminalStore_AccountingContextDecimalsMismatch(
|
|
44
|
+
address token, uint256 providedDecimals, uint256 expectedDecimals
|
|
45
|
+
);
|
|
46
|
+
error JBTerminalStore_AddingAccountingContextNotAllowed(uint256 projectId, uint256 rulesetId, address terminal);
|
|
45
47
|
error JBTerminalStore_InadequateControllerAllowance(uint256 amount, uint256 allowance);
|
|
46
48
|
error JBTerminalStore_InadequateControllerPayoutLimit(uint256 amount, uint256 limit);
|
|
47
49
|
error JBTerminalStore_InadequateTerminalStoreBalance(uint256 amount, uint256 balance);
|
|
@@ -49,10 +51,10 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
49
51
|
error JBTerminalStore_InvalidAmountToForwardHook(uint256 amount, uint256 paidAmount);
|
|
50
52
|
error JBTerminalStore_NoopHookSpecHasAmount(uint256 amount);
|
|
51
53
|
error JBTerminalStore_RulesetNotFound(uint256 projectId);
|
|
52
|
-
error JBTerminalStore_RulesetPaymentPaused();
|
|
53
|
-
error JBTerminalStore_TerminalMigrationNotAllowed();
|
|
54
|
+
error JBTerminalStore_RulesetPaymentPaused(uint256 projectId, uint256 rulesetId);
|
|
55
|
+
error JBTerminalStore_TerminalMigrationNotAllowed(uint256 projectId, uint256 rulesetId);
|
|
54
56
|
error JBTerminalStore_Uint224Overflow(uint256 value);
|
|
55
|
-
error JBTerminalStore_ZeroAccountingContextCurrency();
|
|
57
|
+
error JBTerminalStore_ZeroAccountingContextCurrency(address token);
|
|
56
58
|
|
|
57
59
|
//*********************************************************************//
|
|
58
60
|
// -------------------------- internal constants --------------------- //
|
|
@@ -176,7 +178,9 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
176
178
|
|
|
177
179
|
// Make sure that if there's a ruleset, it allows adding accounting contexts.
|
|
178
180
|
if (ruleset.id != 0 && !ruleset.allowAddAccountingContext()) {
|
|
179
|
-
revert JBTerminalStore_AddingAccountingContextNotAllowed(
|
|
181
|
+
revert JBTerminalStore_AddingAccountingContextNotAllowed({
|
|
182
|
+
projectId: projectId, rulesetId: ruleset.id, terminal: msg.sender
|
|
183
|
+
});
|
|
180
184
|
}
|
|
181
185
|
|
|
182
186
|
// Record each accounting context.
|
|
@@ -185,20 +189,22 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
185
189
|
|
|
186
190
|
// Make sure the token accounting context isn't already set.
|
|
187
191
|
if (_accountingContextForTokenOf[msg.sender][projectId][context.token].token != address(0)) {
|
|
188
|
-
revert JBTerminalStore_AccountingContextAlreadySet(context.token);
|
|
192
|
+
revert JBTerminalStore_AccountingContextAlreadySet({token: context.token});
|
|
189
193
|
}
|
|
190
194
|
|
|
191
195
|
// Keep track of a flag indicating if we know the provided decimals are incorrect.
|
|
192
196
|
bool knownInvalidDecimals;
|
|
197
|
+
uint256 expectedDecimals;
|
|
193
198
|
|
|
194
199
|
// Check if the token is the native token and has the correct decimals.
|
|
195
200
|
if (context.token == JBConstants.NATIVE_TOKEN && context.decimals != 18) {
|
|
196
201
|
knownInvalidDecimals = true;
|
|
202
|
+
expectedDecimals = 18;
|
|
197
203
|
} else if (context.token != JBConstants.NATIVE_TOKEN && context.token.code.length > 0) {
|
|
198
|
-
// slither-disable-next-line calls-loop
|
|
199
204
|
try IERC20Metadata(context.token).decimals() returns (uint8 decimals) {
|
|
200
205
|
if (context.decimals != decimals) {
|
|
201
206
|
knownInvalidDecimals = true;
|
|
207
|
+
expectedDecimals = decimals;
|
|
202
208
|
}
|
|
203
209
|
} catch {
|
|
204
210
|
// The token didn't support `decimals`.
|
|
@@ -210,11 +216,13 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
210
216
|
|
|
211
217
|
// Make sure the decimals are correct.
|
|
212
218
|
if (knownInvalidDecimals) {
|
|
213
|
-
revert JBTerminalStore_AccountingContextDecimalsMismatch(
|
|
219
|
+
revert JBTerminalStore_AccountingContextDecimalsMismatch({
|
|
220
|
+
token: context.token, providedDecimals: context.decimals, expectedDecimals: expectedDecimals
|
|
221
|
+
});
|
|
214
222
|
}
|
|
215
223
|
|
|
216
224
|
// Make sure the currency is non-zero.
|
|
217
|
-
if (context.currency == 0) revert JBTerminalStore_ZeroAccountingContextCurrency();
|
|
225
|
+
if (context.currency == 0) revert JBTerminalStore_ZeroAccountingContextCurrency({token: context.token});
|
|
218
226
|
|
|
219
227
|
// Store the accounting context.
|
|
220
228
|
_accountingContextForTokenOf[msg.sender][projectId][context.token] = context;
|
|
@@ -303,7 +311,7 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
303
311
|
|
|
304
312
|
// The amount being reclaimed must be within the project's balance.
|
|
305
313
|
if (balanceDiff > currentBalance) {
|
|
306
|
-
revert JBTerminalStore_InadequateTerminalStoreBalance(balanceDiff, currentBalance);
|
|
314
|
+
revert JBTerminalStore_InadequateTerminalStoreBalance({amount: balanceDiff, balance: currentBalance});
|
|
307
315
|
}
|
|
308
316
|
|
|
309
317
|
// Remove the reclaimed funds from the project's balance.
|
|
@@ -404,7 +412,7 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
404
412
|
|
|
405
413
|
// The amount being paid out must be available.
|
|
406
414
|
if (amountPaidOut > currentBalance) {
|
|
407
|
-
revert JBTerminalStore_InadequateTerminalStoreBalance(amountPaidOut, currentBalance);
|
|
415
|
+
revert JBTerminalStore_InadequateTerminalStoreBalance({amount: amountPaidOut, balance: currentBalance});
|
|
408
416
|
}
|
|
409
417
|
|
|
410
418
|
// The new total amount which has been paid out during this ruleset.
|
|
@@ -420,7 +428,7 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
420
428
|
|
|
421
429
|
// Make sure the new used amount is within the payout limit.
|
|
422
430
|
if (newUsedPayoutLimitOf > payoutLimit || payoutLimit == 0) {
|
|
423
|
-
revert JBTerminalStore_InadequateControllerPayoutLimit(newUsedPayoutLimitOf, payoutLimit);
|
|
431
|
+
revert JBTerminalStore_InadequateControllerPayoutLimit({amount: newUsedPayoutLimitOf, limit: payoutLimit});
|
|
424
432
|
}
|
|
425
433
|
|
|
426
434
|
// Removed the paid out funds from the project's token balance.
|
|
@@ -444,7 +452,7 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
444
452
|
|
|
445
453
|
// Terminal migration must be allowed.
|
|
446
454
|
if (!ruleset.allowTerminalMigration()) {
|
|
447
|
-
revert JBTerminalStore_TerminalMigrationNotAllowed();
|
|
455
|
+
revert JBTerminalStore_TerminalMigrationNotAllowed({projectId: projectId, rulesetId: ruleset.id});
|
|
448
456
|
}
|
|
449
457
|
|
|
450
458
|
// Return the current balance, which is the amount being migrated.
|
|
@@ -525,7 +533,9 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
525
533
|
|
|
526
534
|
// Make sure the new used amount is within the allowance.
|
|
527
535
|
if (newUsedSurplusAllowanceOf > surplusAllowance || surplusAllowance == 0) {
|
|
528
|
-
revert JBTerminalStore_InadequateControllerAllowance(
|
|
536
|
+
revert JBTerminalStore_InadequateControllerAllowance({
|
|
537
|
+
amount: newUsedSurplusAllowanceOf, allowance: surplusAllowance
|
|
538
|
+
});
|
|
529
539
|
}
|
|
530
540
|
|
|
531
541
|
// Cache the balance slot to avoid redundant storage reads.
|
|
@@ -915,7 +925,7 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
915
925
|
// Noop specifications are informational only, so they can't also request forwarded funds.
|
|
916
926
|
for (uint256 i; i < hookSpecifications.length;) {
|
|
917
927
|
if (hookSpecifications[i].noop && hookSpecifications[i].amount != 0) {
|
|
918
|
-
revert JBTerminalStore_NoopHookSpecHasAmount(hookSpecifications[i].amount);
|
|
928
|
+
revert JBTerminalStore_NoopHookSpecHasAmount({amount: hookSpecifications[i].amount});
|
|
919
929
|
}
|
|
920
930
|
unchecked {
|
|
921
931
|
++i;
|
|
@@ -983,7 +993,7 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
983
993
|
|
|
984
994
|
// Can't cash out more tokens than are in the supply.
|
|
985
995
|
if (cashOutCount > effectiveTotalSupply) {
|
|
986
|
-
revert JBTerminalStore_InsufficientTokens(cashOutCount, effectiveTotalSupply);
|
|
996
|
+
revert JBTerminalStore_InsufficientTokens({count: cashOutCount, totalSupply: effectiveTotalSupply});
|
|
987
997
|
}
|
|
988
998
|
|
|
989
999
|
// SECURITY NOTE: The data hook has absolute control over cash-out pricing.
|
|
@@ -991,7 +1001,7 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
991
1001
|
// completely overriding the terminal's bonding curve math. For example, setting
|
|
992
1002
|
// effectiveTotalSupply = surplus makes reclaimAmount = effectiveCashOutCount, bypassing the curve.
|
|
993
1003
|
// The terminal still burns the caller-supplied cashOutCount after pricing completes.
|
|
994
|
-
// Project owners
|
|
1004
|
+
// Project owners must review their data hooks with the same rigor as the terminal.
|
|
995
1005
|
|
|
996
1006
|
// If the ruleset has a data hook which is enabled for cash outs, use it to derive a claim amount and memo.
|
|
997
1007
|
if (ruleset.useDataHookForCashOut() && ruleset.dataHook() != address(0)) {
|
|
@@ -1067,7 +1077,9 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
1067
1077
|
if (ruleset.cycleNumber == 0) revert JBTerminalStore_RulesetNotFound(projectId);
|
|
1068
1078
|
|
|
1069
1079
|
// The ruleset must not have payments paused.
|
|
1070
|
-
if (ruleset.pausePay())
|
|
1080
|
+
if (ruleset.pausePay()) {
|
|
1081
|
+
revert JBTerminalStore_RulesetPaymentPaused({projectId: projectId, rulesetId: ruleset.id});
|
|
1082
|
+
}
|
|
1071
1083
|
|
|
1072
1084
|
// The weight according to which new tokens are to be minted, as a fixed point number with 18 decimals.
|
|
1073
1085
|
uint256 weight;
|
|
@@ -1075,7 +1087,7 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
1075
1087
|
// SECURITY NOTE: The data hook has absolute control over payment token minting.
|
|
1076
1088
|
// It can return an arbitrary weight (overriding the ruleset's weight) and hook specifications
|
|
1077
1089
|
// that divert payment funds to external hooks before they reach the project's balance.
|
|
1078
|
-
// Project owners
|
|
1090
|
+
// Project owners must review their data hooks with the same rigor as the terminal.
|
|
1079
1091
|
|
|
1080
1092
|
// If the ruleset has a data hook enabled for payments, use it to derive a weight and memo.
|
|
1081
1093
|
if (ruleset.useDataHookForPay() && ruleset.dataHook() != address(0)) {
|
|
@@ -1105,7 +1117,7 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
1105
1117
|
// Ensure that the specifications have valid amounts.
|
|
1106
1118
|
for (uint256 i; i < hookSpecifications.length;) {
|
|
1107
1119
|
if (hookSpecifications[i].noop && hookSpecifications[i].amount != 0) {
|
|
1108
|
-
revert JBTerminalStore_NoopHookSpecHasAmount(hookSpecifications[i].amount);
|
|
1120
|
+
revert JBTerminalStore_NoopHookSpecHasAmount({amount: hookSpecifications[i].amount});
|
|
1109
1121
|
}
|
|
1110
1122
|
|
|
1111
1123
|
uint256 specifiedAmount = hookSpecifications[i].amount;
|
|
@@ -1113,7 +1125,9 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
1113
1125
|
// Can't send more to hook than was paid.
|
|
1114
1126
|
if (specifiedAmount != 0) {
|
|
1115
1127
|
if (specifiedAmount > balanceDiff) {
|
|
1116
|
-
revert JBTerminalStore_InvalidAmountToForwardHook(
|
|
1128
|
+
revert JBTerminalStore_InvalidAmountToForwardHook({
|
|
1129
|
+
amount: specifiedAmount, paidAmount: balanceDiff
|
|
1130
|
+
});
|
|
1117
1131
|
}
|
|
1118
1132
|
|
|
1119
1133
|
// Decrement the total amount being added to the local balance.
|
|
@@ -1319,7 +1333,6 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
1319
1333
|
});
|
|
1320
1334
|
|
|
1321
1335
|
// Add up all the balances.
|
|
1322
|
-
// slither-disable-next-line calls-loop
|
|
1323
1336
|
surplus = (surplus == 0 || accountingContext.currency == targetCurrency)
|
|
1324
1337
|
? surplus
|
|
1325
1338
|
: mulDiv({
|
|
@@ -1335,7 +1348,6 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
1335
1348
|
});
|
|
1336
1349
|
|
|
1337
1350
|
// Get a reference to the payout limit during the ruleset for the token.
|
|
1338
|
-
// slither-disable-next-line calls-loop
|
|
1339
1351
|
JBCurrencyAmount[] memory payoutLimits = IJBController(address(DIRECTORY.controllerOf(projectId)))
|
|
1340
1352
|
.FUND_ACCESS_LIMITS()
|
|
1341
1353
|
.payoutLimitsOf({
|
|
@@ -1375,7 +1387,6 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
1375
1387
|
|
|
1376
1388
|
// Convert the `payoutLimit`'s amount to be in terms of the provided currency.
|
|
1377
1389
|
if (payoutLimit.amount != 0 && payoutLimit.currency != targetCurrency) {
|
|
1378
|
-
// slither-disable-next-line calls-loop
|
|
1379
1390
|
uint256 converted = mulDiv({
|
|
1380
1391
|
x: payoutLimit.amount,
|
|
1381
1392
|
y: 10 ** _MAX_FIXED_POINT_FIDELITY, // Use `_MAX_FIXED_POINT_FIDELITY` to keep as much of the
|
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.
|
|
@@ -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.
|
|
@@ -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
|
}
|