@bananapus/core-v6 0.0.61 → 0.0.63
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 +1 -1
- package/script/helpers/CoreDeploymentLib.sol +24 -1
- package/src/JBController.sol +13 -5
- package/src/JBMultiTerminal.sol +43 -15
- package/src/JBTerminalStore.sol +19 -5
- package/src/interfaces/IJBController.sol +2 -1
- package/src/interfaces/IJBTerminalStore.sol +3 -1
- package/src/libraries/JBRulesetMetadataResolver.sol +4 -1
package/package.json
CHANGED
|
@@ -97,8 +97,11 @@ library CoreDeploymentLib {
|
|
|
97
97
|
})
|
|
98
98
|
);
|
|
99
99
|
|
|
100
|
+
// Controller is loaded best-effort so the periphery deploy script can bootstrap on a fresh chain that
|
|
101
|
+
// hasn't yet produced `JBController.json` — that script is the one that creates the controller. Other
|
|
102
|
+
// core artifacts are still required.
|
|
100
103
|
deployment.controller = JBController(
|
|
101
|
-
|
|
104
|
+
_tryGetDeploymentAddress({
|
|
102
105
|
path: path, projectName: PROJECT_NAME, networkName: networkName, contractName: "JBController"
|
|
103
106
|
})
|
|
104
107
|
);
|
|
@@ -164,4 +167,24 @@ library CoreDeploymentLib {
|
|
|
164
167
|
vm.readFile(string.concat(path, projectName, "/", networkName, "/", contractName, ".json"));
|
|
165
168
|
return stdJson.readAddress({json: deploymentJson, key: ".address"});
|
|
166
169
|
}
|
|
170
|
+
|
|
171
|
+
/// @notice Best-effort variant of `_getDeploymentAddress`. Returns `address(0)` when the artifact file does
|
|
172
|
+
/// not exist on disk, rather than reverting. Used for fields that may legitimately be absent during a
|
|
173
|
+
/// fresh-chain bootstrap (e.g. the controller artifact when the periphery script is the one creating it).
|
|
174
|
+
function _tryGetDeploymentAddress(
|
|
175
|
+
string memory path,
|
|
176
|
+
string memory projectName,
|
|
177
|
+
string memory networkName,
|
|
178
|
+
string memory contractName
|
|
179
|
+
)
|
|
180
|
+
internal
|
|
181
|
+
view
|
|
182
|
+
returns (address)
|
|
183
|
+
{
|
|
184
|
+
string memory filePath = string.concat(path, projectName, "/", networkName, "/", contractName, ".json");
|
|
185
|
+
// forge-lint: disable-next-line(unsafe-cheatcode)
|
|
186
|
+
if (!vm.exists(filePath)) return address(0);
|
|
187
|
+
// forge-lint: disable-next-line(unsafe-cheatcode)
|
|
188
|
+
return stdJson.readAddress({json: vm.readFile(filePath), key: ".address"});
|
|
189
|
+
}
|
|
167
190
|
}
|
package/src/JBController.sol
CHANGED
|
@@ -172,8 +172,10 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
|
|
|
172
172
|
//*********************************************************************//
|
|
173
173
|
|
|
174
174
|
/// @notice Registers a price feed so the project can use a new currency for payout limits or surplus allowances.
|
|
175
|
-
/// @dev Can only be called by the project's owner or an operator with `ADD_PRICE_FEED` permission.
|
|
176
|
-
/// ruleset must have `allowAddPriceFeed` enabled.
|
|
175
|
+
/// @dev Can only be called by the project's owner or an operator with `ADD_PRICE_FEED` permission. When a current
|
|
176
|
+
/// ruleset exists, it must have `allowAddPriceFeed` enabled. If no current ruleset exists (`ruleset.id == 0`),
|
|
177
|
+
/// the call is allowed unconditionally — this is the symmetric counterpart to `setTokenFor`, which mirrors the
|
|
178
|
+
/// gap-state allowance so projects can configure prerequisites before their first ruleset activates.
|
|
177
179
|
/// @param projectId The ID of the project to add the feed for.
|
|
178
180
|
/// @param pricingCurrency The currency the feed's output price is in terms of.
|
|
179
181
|
/// @param unitCurrency The currency the feed prices.
|
|
@@ -194,8 +196,12 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
|
|
|
194
196
|
|
|
195
197
|
JBRuleset memory ruleset = _currentRulesetOf(projectId);
|
|
196
198
|
|
|
197
|
-
//
|
|
198
|
-
|
|
199
|
+
// When a current ruleset exists, it must allow adding price feeds. If no current ruleset exists
|
|
200
|
+
// (`ruleset.id == 0`), the call is allowed unconditionally so projects can register feeds during the
|
|
201
|
+
// pre-launch or gap state — matching the gap-state semantics of `setTokenFor`.
|
|
202
|
+
if (ruleset.id != 0 && !ruleset.allowAddPriceFeed()) {
|
|
203
|
+
revert JBController_AddingPriceFeedNotAllowed(projectId);
|
|
204
|
+
}
|
|
199
205
|
|
|
200
206
|
PRICES.addPriceFeedFor({
|
|
201
207
|
projectId: projectId, pricingCurrency: pricingCurrency, unitCurrency: unitCurrency, feed: feed
|
|
@@ -1102,7 +1108,9 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
|
|
|
1102
1108
|
})
|
|
1103
1109
|
) {}
|
|
1104
1110
|
catch (bytes memory reason) {
|
|
1105
|
-
emit SplitHookReverted({
|
|
1111
|
+
emit SplitHookReverted({
|
|
1112
|
+
projectId: projectId, hook: address(split.hook), reason: reason, caller: messageSender
|
|
1113
|
+
});
|
|
1106
1114
|
}
|
|
1107
1115
|
|
|
1108
1116
|
// For ERC-20 tokens, burn any tokens the hook did not consume.
|
package/src/JBMultiTerminal.sol
CHANGED
|
@@ -501,8 +501,11 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
501
501
|
/// @param token The token the fee is paid in.
|
|
502
502
|
/// @param amount The fee amount, as a fixed point number with the same number of decimals as the token's
|
|
503
503
|
/// accounting context.
|
|
504
|
-
/// @param beneficiary The address to mint tokens to (
|
|
505
|
-
///
|
|
504
|
+
/// @param beneficiary The address to mint fee-project tokens to (and pass along to the fee project's
|
|
505
|
+
/// data/pay hooks). If `address(0)`, the fee is routed via `addToBalanceOf` instead of `pay`, crediting the
|
|
506
|
+
/// fee project's balance directly without minting fee-project tokens. This honors the protocol-fee intent
|
|
507
|
+
/// (the fee project still receives the value) when no beneficiary is specified, instead of letting `pay`
|
|
508
|
+
/// revert inside `mintTokensOf` and having the catch path forgive the fee.
|
|
506
509
|
/// @param feeTerminal The terminal that'll receive the fees.
|
|
507
510
|
function executeProcessFee(
|
|
508
511
|
uint256 projectId,
|
|
@@ -523,14 +526,24 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
523
526
|
// Send the projectId in the metadata.
|
|
524
527
|
bytes memory metadata = bytes(abi.encodePacked(projectId));
|
|
525
528
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
529
|
+
if (beneficiary == address(0)) {
|
|
530
|
+
_efficientAddToBalance({
|
|
531
|
+
terminal: feeTerminal,
|
|
532
|
+
projectId: JBConstants.FEE_BENEFICIARY_PROJECT_ID,
|
|
533
|
+
token: token,
|
|
534
|
+
amount: amount,
|
|
535
|
+
metadata: metadata
|
|
536
|
+
});
|
|
537
|
+
} else {
|
|
538
|
+
_efficientPay({
|
|
539
|
+
terminal: feeTerminal,
|
|
540
|
+
projectId: JBConstants.FEE_BENEFICIARY_PROJECT_ID,
|
|
541
|
+
token: token,
|
|
542
|
+
amount: amount,
|
|
543
|
+
beneficiary: beneficiary,
|
|
544
|
+
metadata: metadata
|
|
545
|
+
});
|
|
546
|
+
}
|
|
534
547
|
}
|
|
535
548
|
|
|
536
549
|
/// @notice Transfer funds to an address.
|
|
@@ -613,7 +626,11 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
613
626
|
}
|
|
614
627
|
|
|
615
628
|
// Transfer the balance minus the fee to the new terminal.
|
|
616
|
-
uint256 migrationAmount
|
|
629
|
+
uint256 migrationAmount;
|
|
630
|
+
// `_takeFeeFrom` calculated `feeAmount` from `balance`, so it cannot exceed `balance`.
|
|
631
|
+
unchecked {
|
|
632
|
+
migrationAmount = balance - feeAmount;
|
|
633
|
+
}
|
|
617
634
|
|
|
618
635
|
_externalAddToBalance({
|
|
619
636
|
terminal: to, projectId: projectId, token: token, amount: migrationAmount, metadata: bytes("")
|
|
@@ -677,7 +694,10 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
677
694
|
|
|
678
695
|
// Set the beneficiary token count.
|
|
679
696
|
if (beneficiaryBalanceAfter > beneficiaryBalanceBefore) {
|
|
680
|
-
|
|
697
|
+
// Guarded by the comparison above.
|
|
698
|
+
unchecked {
|
|
699
|
+
beneficiaryTokenCount = beneficiaryBalanceAfter - beneficiaryBalanceBefore;
|
|
700
|
+
}
|
|
681
701
|
}
|
|
682
702
|
|
|
683
703
|
// The token count for the beneficiary must be greater than or equal to the specified minimum.
|
|
@@ -724,7 +744,10 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
724
744
|
// reverting.
|
|
725
745
|
// A `FeeReverted` event is emitted so the forgiveness is observable off-chain.
|
|
726
746
|
delete _heldFeesOf[projectId][token][currentIndex];
|
|
727
|
-
|
|
747
|
+
// `currentIndex` was proven to be within the held-fee array.
|
|
748
|
+
unchecked {
|
|
749
|
+
_nextHeldFeeIndexOf[projectId][token] = currentIndex + 1;
|
|
750
|
+
}
|
|
728
751
|
|
|
729
752
|
// Restore the originating fee-paying call's referral project for the duration of this fee's processing
|
|
730
753
|
// so the credit in `_processFee` attributes to the right (chain, project) pair. No save needed:
|
|
@@ -1866,6 +1889,10 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
1866
1889
|
});
|
|
1867
1890
|
|
|
1868
1891
|
_recordAddedBalanceFor({projectId: projectId, token: token, amount: amount});
|
|
1892
|
+
// The store balance was credited first; this mirrors that bounded increase for fee recovery.
|
|
1893
|
+
unchecked {
|
|
1894
|
+
_feeFreeSurplusOf[projectId][token] += amount;
|
|
1895
|
+
}
|
|
1869
1896
|
}
|
|
1870
1897
|
}
|
|
1871
1898
|
|
|
@@ -1874,7 +1901,7 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
1874
1901
|
/// @param token The token to record the added balance for.
|
|
1875
1902
|
/// @param amount The amount of the token to record, as a fixed point number with the same number of decimals as
|
|
1876
1903
|
/// this terminal.
|
|
1877
|
-
function _recordAddedBalanceFor(uint256 projectId, address token, uint256 amount)
|
|
1904
|
+
function _recordAddedBalanceFor(uint256 projectId, address token, uint256 amount) private {
|
|
1878
1905
|
STORE.recordAddedBalanceFor({projectId: projectId, token: token, amount: amount});
|
|
1879
1906
|
}
|
|
1880
1907
|
|
|
@@ -2158,7 +2185,8 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
2158
2185
|
projectId: projectId,
|
|
2159
2186
|
token: token,
|
|
2160
2187
|
amount: amountPaidOut,
|
|
2161
|
-
// The
|
|
2188
|
+
// The `feeBeneficiary` will receive the fee-project tokens minted in exchange for the
|
|
2189
|
+
// platform fee paid in terminal tokens.
|
|
2162
2190
|
beneficiary: feeBeneficiary,
|
|
2163
2191
|
shouldHoldFees: ruleset.holdFees()
|
|
2164
2192
|
}));
|
package/src/JBTerminalStore.sol
CHANGED
|
@@ -43,6 +43,7 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
43
43
|
error JBTerminalStore_AccountingContextDecimalsMismatch(
|
|
44
44
|
address token, uint256 providedDecimals, uint256 expectedDecimals
|
|
45
45
|
);
|
|
46
|
+
error JBTerminalStore_AccountingContextDecimalsOutOfRange(address token, uint256 decimals);
|
|
46
47
|
error JBTerminalStore_AddingAccountingContextNotAllowed(uint256 projectId, uint256 rulesetId, address terminal);
|
|
47
48
|
error JBTerminalStore_InadequateControllerAllowance(uint256 amount, uint256 allowance);
|
|
48
49
|
|
|
@@ -212,6 +213,12 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
212
213
|
revert JBTerminalStore_AccountingContextAlreadySet({token: context.token});
|
|
213
214
|
}
|
|
214
215
|
|
|
216
|
+
if (context.decimals > 36) {
|
|
217
|
+
revert JBTerminalStore_AccountingContextDecimalsOutOfRange({
|
|
218
|
+
token: context.token, decimals: context.decimals
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
215
222
|
// Keep track of a flag indicating if we know the provided decimals are incorrect.
|
|
216
223
|
bool knownInvalidDecimals;
|
|
217
224
|
uint256 expectedDecimals;
|
|
@@ -222,16 +229,22 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
222
229
|
expectedDecimals = 18;
|
|
223
230
|
} else if (context.token != JBConstants.NATIVE_TOKEN && context.token.code.length > 0) {
|
|
224
231
|
try IERC20Metadata(context.token).decimals() returns (uint8 decimals) {
|
|
225
|
-
|
|
226
|
-
knownInvalidDecimals = true;
|
|
227
|
-
expectedDecimals = decimals;
|
|
228
|
-
}
|
|
232
|
+
expectedDecimals = decimals;
|
|
229
233
|
} catch {
|
|
230
234
|
// The token didn't support `decimals`.
|
|
231
235
|
// @dev Non-standard ERC20s that revert on `decimals()` will bypass decimal validation.
|
|
232
236
|
// The caller is responsible for providing the correct decimals for such tokens.
|
|
233
237
|
knownInvalidDecimals = false;
|
|
238
|
+
expectedDecimals = context.decimals;
|
|
234
239
|
}
|
|
240
|
+
|
|
241
|
+
if (expectedDecimals > 36) {
|
|
242
|
+
revert JBTerminalStore_AccountingContextDecimalsOutOfRange({
|
|
243
|
+
token: context.token, decimals: expectedDecimals
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (context.decimals != expectedDecimals) knownInvalidDecimals = true;
|
|
235
248
|
}
|
|
236
249
|
|
|
237
250
|
// Make sure the decimals are correct.
|
|
@@ -1442,7 +1455,8 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
1442
1455
|
referralChainId: referralChainId,
|
|
1443
1456
|
referralProjectId: bareProjectId,
|
|
1444
1457
|
amount: amount,
|
|
1445
|
-
newTotal: newTotal
|
|
1458
|
+
newTotal: newTotal,
|
|
1459
|
+
caller: msg.sender
|
|
1446
1460
|
});
|
|
1447
1461
|
}
|
|
1448
1462
|
|
|
@@ -102,7 +102,8 @@ interface IJBController is IERC165, IJBProjectUriRegistry, IJBDirectoryAccessCon
|
|
|
102
102
|
/// @param projectId The ID of the project.
|
|
103
103
|
/// @param hook The split hook that reverted.
|
|
104
104
|
/// @param reason The revert reason.
|
|
105
|
-
|
|
105
|
+
/// @param caller The address that called the distribution function.
|
|
106
|
+
event SplitHookReverted(uint256 indexed projectId, address hook, bytes reason, address caller);
|
|
106
107
|
|
|
107
108
|
/// @notice Reserved tokens were sent to a specific split.
|
|
108
109
|
/// @param projectId The ID of the project.
|
|
@@ -25,12 +25,14 @@ interface IJBTerminalStore {
|
|
|
25
25
|
/// @param referralProjectId The referrer's bare project ID on `referralChainId` (no chain bits).
|
|
26
26
|
/// @param amount The fee amount credited, in the terminal's accounting-context units.
|
|
27
27
|
/// @param newTotal The new value of `totalFeeVolumeOf[terminal]` after this credit.
|
|
28
|
+
/// @param caller The address that recorded the credit.
|
|
28
29
|
event ReferralCredit(
|
|
29
30
|
address indexed terminal,
|
|
30
31
|
uint256 indexed referralChainId,
|
|
31
32
|
uint256 indexed referralProjectId,
|
|
32
33
|
uint256 amount,
|
|
33
|
-
uint256 newTotal
|
|
34
|
+
uint256 newTotal,
|
|
35
|
+
address caller
|
|
34
36
|
);
|
|
35
37
|
|
|
36
38
|
/// @notice The directory of terminals and controllers for projects.
|
|
@@ -92,7 +92,10 @@ library JBRulesetMetadataResolver {
|
|
|
92
92
|
|
|
93
93
|
/// @notice Pack the funding cycle metadata.
|
|
94
94
|
/// @param rulesetMetadata The ruleset metadata to validate and pack.
|
|
95
|
-
/// @return packed The packed uint256 of all metadata params. The first
|
|
95
|
+
/// @return packed The packed uint256 of all metadata params. The first 4 bits (bits 0-3) specify the version;
|
|
96
|
+
/// supported values are 0-15. A future protocol version that needs more than 16 distinct metadata layouts must
|
|
97
|
+
/// widen this field before assigning a new version number, otherwise the high bits will silently spill into
|
|
98
|
+
/// the `reservedPercent` field that begins at bit 4.
|
|
96
99
|
function packRulesetMetadata(JBRulesetMetadata memory rulesetMetadata) internal pure returns (uint256 packed) {
|
|
97
100
|
// version 1 in the bits 0-3 (4 bits).
|
|
98
101
|
packed = 1;
|