@bananapus/core-v6 0.0.16 → 0.0.17
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/ADMINISTRATION.md +1 -1
- package/ARCHITECTURE.md +2 -1
- package/AUDIT_INSTRUCTIONS.md +342 -0
- package/CHANGE_LOG.md +375 -0
- package/README.md +4 -4
- package/RISKS.md +171 -50
- package/SKILLS.md +9 -6
- package/USER_JOURNEYS.md +622 -0
- package/package.json +2 -2
- package/script/DeployPeriphery.s.sol +7 -1
- package/src/JBController.sol +5 -0
- package/src/JBDeadline.sol +3 -0
- package/src/JBDirectory.sol +2 -1
- package/src/JBMultiTerminal.sol +50 -9
- package/src/JBPermissions.sol +2 -0
- package/src/JBPrices.sol +8 -2
- package/src/JBRulesets.sol +3 -0
- package/src/JBSplits.sol +9 -5
- package/src/JBTerminalStore.sol +54 -47
- package/src/JBTokens.sol +3 -0
- package/src/interfaces/IJBTerminalStore.sol +3 -0
- package/src/libraries/JBFees.sol +2 -0
- package/src/libraries/JBMetadataResolver.sol +17 -4
- package/src/structs/JBBeforeCashOutRecordedContext.sol +4 -0
- package/test/TestAuditResponseDesignProofs.sol +434 -0
- package/test/TestDataHookFuzzing.sol +520 -0
- package/test/TestFeeFreeCashOutBypass.sol +617 -0
- package/test/TestL2SequencerPriceFeed.sol +292 -0
- package/test/TestMetadataOffsetOverflow.sol +179 -0
- package/test/TestMultiTerminalSurplus.sol +348 -0
- package/test/TestPermit2DataHook.t.sol +360 -0
- package/test/TestRulesetQueueing.sol +1 -2
- package/test/TestRulesetWeightCaching.sol +122 -124
- package/test/WeirdTokenTests.t.sol +37 -0
- package/test/regression/HoldFeesCashOutReserved.t.sol +415 -0
- package/test/regression/WeightCacheBoundary.t.sol +291 -0
- package/test/units/static/JBMultiTerminal/TestAddToBalanceOf.sol +2 -2
- package/test/units/static/JBMultiTerminal/TestCashOutTokensOf.sol +18 -17
- package/test/units/static/JBMultiTerminal/TestPay.sol +6 -4
- package/test/units/static/JBMultiTerminal/TestProcessHeldFeesOf.sol +206 -18
- package/test/units/static/JBMultiTerminal/TestUseAllowanceOf.sol +280 -0
- package/test/units/static/JBSplits/TestSelfManagedSplitGroups.sol +55 -12
- package/test/units/static/JBTerminalStore/TestRecordCashOutsFor.sol +72 -0
|
@@ -57,9 +57,9 @@ contract TestSelfManagedSplitGroups_Local is JBSplitsSetup {
|
|
|
57
57
|
// ──────────────────
|
|
58
58
|
|
|
59
59
|
function test_CallerCanSetSplitsInOwnGroupNamespace() external {
|
|
60
|
-
//
|
|
60
|
+
// Self-auth requires non-zero upper 96 bits + lower 160 bits == msg.sender.
|
|
61
61
|
address caller = makeAddr("hookContract");
|
|
62
|
-
uint256 groupId = uint256(uint160(caller));
|
|
62
|
+
uint256 groupId = (1 << 160) | uint256(uint160(caller));
|
|
63
63
|
|
|
64
64
|
JBSplitGroup[] memory groups = _makeSplitGroup(groupId, JBConstants.SPLITS_TOTAL_PERCENT / 2, _bene);
|
|
65
65
|
|
|
@@ -121,7 +121,7 @@ contract TestSelfManagedSplitGroups_Local is JBSplitsSetup {
|
|
|
121
121
|
function test_CallerCanSetMultipleSplitsInOwnGroup() external {
|
|
122
122
|
// Multiple splits in the same self-managed group.
|
|
123
123
|
address caller = makeAddr("hookContract");
|
|
124
|
-
uint256 groupId = uint256(uint160(caller));
|
|
124
|
+
uint256 groupId = (1 << 160) | uint256(uint160(caller));
|
|
125
125
|
|
|
126
126
|
address payable bene1 = payable(makeAddr("bene1"));
|
|
127
127
|
address payable bene2 = payable(makeAddr("bene2"));
|
|
@@ -158,7 +158,7 @@ contract TestSelfManagedSplitGroups_Local is JBSplitsSetup {
|
|
|
158
158
|
function test_CallerCanOverwriteOwnSplits() external {
|
|
159
159
|
// Caller can overwrite their own splits.
|
|
160
160
|
address caller = makeAddr("hookContract");
|
|
161
|
-
uint256 groupId = uint256(uint160(caller));
|
|
161
|
+
uint256 groupId = (1 << 160) | uint256(uint160(caller));
|
|
162
162
|
|
|
163
163
|
address payable beneOld = payable(makeAddr("beneOld"));
|
|
164
164
|
address payable beneNew = payable(makeAddr("beneNew"));
|
|
@@ -183,7 +183,7 @@ contract TestSelfManagedSplitGroups_Local is JBSplitsSetup {
|
|
|
183
183
|
function test_SelfManagedSplitsEmitSetSplitEvent() external {
|
|
184
184
|
// Setting self-managed splits emits SetSplit with correct caller.
|
|
185
185
|
address caller = makeAddr("hookContract");
|
|
186
|
-
uint256 groupId = uint256(uint160(caller));
|
|
186
|
+
uint256 groupId = (1 << 160) | uint256(uint160(caller));
|
|
187
187
|
|
|
188
188
|
JBSplitGroup[] memory groups = _makeSplitGroup(groupId, JBConstants.SPLITS_TOTAL_PERCENT, _bene);
|
|
189
189
|
|
|
@@ -197,7 +197,7 @@ contract TestSelfManagedSplitGroups_Local is JBSplitsSetup {
|
|
|
197
197
|
function test_SelfManagedSplitsWorkAcrossRulesets() external {
|
|
198
198
|
// Caller can set splits in the same group but different rulesets.
|
|
199
199
|
address caller = makeAddr("hookContract");
|
|
200
|
-
uint256 groupId = uint256(uint160(caller));
|
|
200
|
+
uint256 groupId = (1 << 160) | uint256(uint160(caller));
|
|
201
201
|
|
|
202
202
|
uint256 rulesetA = 100;
|
|
203
203
|
uint256 rulesetB = 200;
|
|
@@ -227,7 +227,7 @@ contract TestSelfManagedSplitGroups_Local is JBSplitsSetup {
|
|
|
227
227
|
|
|
228
228
|
function test_SelfManagedSplitsRevertOnZeroPercent() external {
|
|
229
229
|
address caller = makeAddr("hookContract");
|
|
230
|
-
uint256 groupId = uint256(uint160(caller));
|
|
230
|
+
uint256 groupId = (1 << 160) | uint256(uint160(caller));
|
|
231
231
|
|
|
232
232
|
JBSplitGroup[] memory groups = new JBSplitGroup[](1);
|
|
233
233
|
JBSplit[] memory splits = new JBSplit[](1);
|
|
@@ -248,7 +248,7 @@ contract TestSelfManagedSplitGroups_Local is JBSplitsSetup {
|
|
|
248
248
|
|
|
249
249
|
function test_SelfManagedSplitsRevertOnExcessPercent() external {
|
|
250
250
|
address caller = makeAddr("hookContract");
|
|
251
|
-
uint256 groupId = uint256(uint160(caller));
|
|
251
|
+
uint256 groupId = (1 << 160) | uint256(uint160(caller));
|
|
252
252
|
|
|
253
253
|
JBSplitGroup[] memory groups = new JBSplitGroup[](1);
|
|
254
254
|
JBSplit[] memory splits = new JBSplit[](2);
|
|
@@ -278,7 +278,7 @@ contract TestSelfManagedSplitGroups_Local is JBSplitsSetup {
|
|
|
278
278
|
function test_SelfManagedSplitsEnforceLocks() external {
|
|
279
279
|
// Locked splits in a self-managed group cannot be removed.
|
|
280
280
|
address caller = makeAddr("hookContract");
|
|
281
|
-
uint256 groupId = uint256(uint160(caller));
|
|
281
|
+
uint256 groupId = (1 << 160) | uint256(uint160(caller));
|
|
282
282
|
|
|
283
283
|
// Set a locked split.
|
|
284
284
|
JBSplitGroup[] memory groups = new JBSplitGroup[](1);
|
|
@@ -357,6 +357,46 @@ contract TestSelfManagedSplitGroups_Local is JBSplitsSetup {
|
|
|
357
357
|
_splits.setSplitGroupsOf(_projectId, _rulesetId, groups);
|
|
358
358
|
}
|
|
359
359
|
|
|
360
|
+
// ──────────────── Bare-address groupIds require controller auth
|
|
361
|
+
// ───────────────
|
|
362
|
+
|
|
363
|
+
function test_BareAddressGroupIdRequiresControllerEvenForMatchingSender() external {
|
|
364
|
+
// A contract cannot self-auth for groupId == uint256(uint160(self)) (upper 96 bits = 0).
|
|
365
|
+
// This prevents token contracts from hijacking terminal payout splits.
|
|
366
|
+
address caller = makeAddr("maliciousToken");
|
|
367
|
+
uint256 groupId = uint256(uint160(caller)); // bare address, upper bits = 0
|
|
368
|
+
|
|
369
|
+
JBSplitGroup[] memory groups = _makeSplitGroup(groupId, JBConstants.SPLITS_TOTAL_PERCENT, _bene);
|
|
370
|
+
|
|
371
|
+
_mockController(makeAddr("realController"));
|
|
372
|
+
|
|
373
|
+
vm.prank(caller);
|
|
374
|
+
vm.expectRevert(
|
|
375
|
+
abi.encodeWithSelector(
|
|
376
|
+
JBControlled.JBControlled_ControllerUnauthorized.selector, makeAddr("realController")
|
|
377
|
+
)
|
|
378
|
+
);
|
|
379
|
+
_splits.setSplitGroupsOf(_projectId, _rulesetId, groups);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function test_BareAddressGroupIdSucceedsWithControllerAuth() external {
|
|
383
|
+
// The controller CAN still set splits for bare-address groupIds (terminal payout groups).
|
|
384
|
+
address controller = makeAddr("controller");
|
|
385
|
+
address token = makeAddr("someToken");
|
|
386
|
+
uint256 groupId = uint256(uint160(token));
|
|
387
|
+
|
|
388
|
+
JBSplitGroup[] memory groups = _makeSplitGroup(groupId, JBConstants.SPLITS_TOTAL_PERCENT, _bene);
|
|
389
|
+
|
|
390
|
+
_mockController(controller);
|
|
391
|
+
|
|
392
|
+
vm.prank(controller);
|
|
393
|
+
_splits.setSplitGroupsOf(_projectId, _rulesetId, groups);
|
|
394
|
+
|
|
395
|
+
JBSplit[] memory result = _splits.splitsOf(_projectId, _rulesetId, groupId);
|
|
396
|
+
assertEq(result.length, 1);
|
|
397
|
+
assertEq(result[0].beneficiary, _bene);
|
|
398
|
+
}
|
|
399
|
+
|
|
360
400
|
// ───────────────────── Controller can still set any group
|
|
361
401
|
// ──────────────────
|
|
362
402
|
|
|
@@ -382,9 +422,10 @@ contract TestSelfManagedSplitGroups_Local is JBSplitsSetup {
|
|
|
382
422
|
// ─────────────────────────────
|
|
383
423
|
|
|
384
424
|
function test_MixedSelfManagedAndControllerGroupsInOneCall() external {
|
|
385
|
-
// A single setSplitGroupsOf call with one self-managed group and one controller-gated
|
|
425
|
+
// A single setSplitGroupsOf call with one self-managed group (non-zero upper bits) and one controller-gated
|
|
426
|
+
// group.
|
|
386
427
|
address caller = makeAddr("hookContract");
|
|
387
|
-
uint256 selfGroupId = uint256(uint160(caller));
|
|
428
|
+
uint256 selfGroupId = (1 << 160) | uint256(uint160(caller));
|
|
388
429
|
uint256 otherGroupId = 0;
|
|
389
430
|
|
|
390
431
|
// Mock: caller IS the controller.
|
|
@@ -427,7 +468,7 @@ contract TestSelfManagedSplitGroups_Local is JBSplitsSetup {
|
|
|
427
468
|
function test_MixedCallRevertsIfNonControllerSetsOtherGroup() external {
|
|
428
469
|
// A call with one self-managed group (ok) and one non-owned group (reverts) in the same call.
|
|
429
470
|
address caller = makeAddr("hookContract");
|
|
430
|
-
uint256 selfGroupId = uint256(uint160(caller));
|
|
471
|
+
uint256 selfGroupId = (1 << 160) | uint256(uint160(caller));
|
|
431
472
|
uint256 otherGroupId = 0;
|
|
432
473
|
|
|
433
474
|
_mockController(makeAddr("realController"));
|
|
@@ -472,6 +513,8 @@ contract TestSelfManagedSplitGroups_Local is JBSplitsSetup {
|
|
|
472
513
|
function testFuzz_AnyAddressCanSetOwnNamespace(address caller, uint96 upperBits, uint32 percent) external {
|
|
473
514
|
vm.assume(caller != address(0));
|
|
474
515
|
vm.assume(percent > 0 && percent <= JBConstants.SPLITS_TOTAL_PERCENT);
|
|
516
|
+
// Self-auth requires non-zero upper bits.
|
|
517
|
+
vm.assume(upperBits > 0);
|
|
475
518
|
|
|
476
519
|
uint256 groupId = (uint256(upperBits) << 160) | uint256(uint160(caller));
|
|
477
520
|
|
|
@@ -336,6 +336,7 @@ contract TestRecordCashOutsFor_Local is JBTerminalStoreSetup {
|
|
|
336
336
|
cashOutCount: _cashOutCount,
|
|
337
337
|
accountingContext: _accountingContexts,
|
|
338
338
|
balanceAccountingContexts: _balanceContexts,
|
|
339
|
+
beneficiaryIsFeeless: false,
|
|
339
340
|
metadata: ""
|
|
340
341
|
});
|
|
341
342
|
}
|
|
@@ -369,6 +370,7 @@ contract TestRecordCashOutsFor_Local is JBTerminalStoreSetup {
|
|
|
369
370
|
cashOutCount: _cashOutCount,
|
|
370
371
|
accountingContext: _accountingContexts,
|
|
371
372
|
balanceAccountingContexts: _balanceContexts,
|
|
373
|
+
beneficiaryIsFeeless: false,
|
|
372
374
|
metadata: ""
|
|
373
375
|
});
|
|
374
376
|
|
|
@@ -420,6 +422,7 @@ contract TestRecordCashOutsFor_Local is JBTerminalStoreSetup {
|
|
|
420
422
|
surplus: _reclaimedTokenAmount,
|
|
421
423
|
useTotalSurplus: true,
|
|
422
424
|
cashOutTaxRate: 0,
|
|
425
|
+
beneficiaryIsFeeless: false,
|
|
423
426
|
metadata: ""
|
|
424
427
|
});
|
|
425
428
|
|
|
@@ -442,6 +445,7 @@ contract TestRecordCashOutsFor_Local is JBTerminalStoreSetup {
|
|
|
442
445
|
cashOutCount: _cashOutCount,
|
|
443
446
|
accountingContext: _accountingContexts,
|
|
444
447
|
balanceAccountingContexts: _balanceContexts,
|
|
448
|
+
beneficiaryIsFeeless: false,
|
|
445
449
|
metadata: ""
|
|
446
450
|
});
|
|
447
451
|
|
|
@@ -487,10 +491,78 @@ contract TestRecordCashOutsFor_Local is JBTerminalStoreSetup {
|
|
|
487
491
|
cashOutCount: _cashOutCount,
|
|
488
492
|
accountingContext: _accountingContexts,
|
|
489
493
|
balanceAccountingContexts: _balanceContexts,
|
|
494
|
+
beneficiaryIsFeeless: false,
|
|
490
495
|
metadata: ""
|
|
491
496
|
});
|
|
492
497
|
}
|
|
493
498
|
|
|
499
|
+
function test_GivenBeneficiaryIsFeeless_DataHookReceivesTrue()
|
|
500
|
+
external
|
|
501
|
+
whenCurrentRulesetUseTotalSurplusForCashOutsEqTrueWithHook
|
|
502
|
+
{
|
|
503
|
+
// it will pass beneficiaryIsFeeless=true through to the data hook context
|
|
504
|
+
|
|
505
|
+
// mock JBController totalTokenSupplyWithReservedTokensOf
|
|
506
|
+
mockExpect(
|
|
507
|
+
address(_controller),
|
|
508
|
+
abi.encodeCall(IJBController.totalTokenSupplyWithReservedTokensOf, (_projectId)),
|
|
509
|
+
abi.encode(_totalSupply)
|
|
510
|
+
);
|
|
511
|
+
|
|
512
|
+
// call params
|
|
513
|
+
JBAccountingContext memory _accountingContexts =
|
|
514
|
+
JBAccountingContext({token: address(_token), decimals: 18, currency: _currency});
|
|
515
|
+
JBAccountingContext[] memory _balanceContexts = new JBAccountingContext[](1);
|
|
516
|
+
|
|
517
|
+
_balanceContexts[0] = JBAccountingContext({token: address(_token), decimals: 18, currency: _currency});
|
|
518
|
+
|
|
519
|
+
uint256 _cashOutCount = 1e18;
|
|
520
|
+
uint256 expectedCashOuts = mulDiv(3e18, _cashOutCount, _totalSupply);
|
|
521
|
+
|
|
522
|
+
// Create the expected context — beneficiaryIsFeeless should be true.
|
|
523
|
+
JBBeforeCashOutRecordedContext memory _context = JBBeforeCashOutRecordedContext({
|
|
524
|
+
terminal: address(this),
|
|
525
|
+
holder: address(this),
|
|
526
|
+
projectId: _projectId,
|
|
527
|
+
rulesetId: uint48(block.timestamp),
|
|
528
|
+
cashOutCount: _cashOutCount,
|
|
529
|
+
totalSupply: _totalSupply,
|
|
530
|
+
surplus: JBTokenAmount({
|
|
531
|
+
token: _accountingContexts.token,
|
|
532
|
+
value: 3e18,
|
|
533
|
+
decimals: _accountingContexts.decimals,
|
|
534
|
+
currency: _accountingContexts.currency
|
|
535
|
+
}),
|
|
536
|
+
useTotalSurplus: true,
|
|
537
|
+
cashOutTaxRate: 0,
|
|
538
|
+
beneficiaryIsFeeless: true,
|
|
539
|
+
metadata: ""
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
// return data
|
|
543
|
+
JBCashOutHookSpecification[] memory _spec = new JBCashOutHookSpecification[](1);
|
|
544
|
+
_spec[0] = JBCashOutHookSpecification({hook: _cashOutHook, amount: 0, metadata: ""});
|
|
545
|
+
|
|
546
|
+
// The mock will only match if the context has beneficiaryIsFeeless=true.
|
|
547
|
+
mockExpect(
|
|
548
|
+
address(_dataHook),
|
|
549
|
+
abi.encodeCall(IJBRulesetDataHook.beforeCashOutRecordedWith, (_context)),
|
|
550
|
+
abi.encode(0, 1e18, _totalSupply, _spec)
|
|
551
|
+
);
|
|
552
|
+
|
|
553
|
+
(, uint256 reclaimed,,) = _store.recordCashOutFor({
|
|
554
|
+
holder: address(this),
|
|
555
|
+
projectId: _projectId,
|
|
556
|
+
cashOutCount: _cashOutCount,
|
|
557
|
+
accountingContext: _accountingContexts,
|
|
558
|
+
balanceAccountingContexts: _balanceContexts,
|
|
559
|
+
beneficiaryIsFeeless: true,
|
|
560
|
+
metadata: ""
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
assertEq(expectedCashOuts, reclaimed);
|
|
564
|
+
}
|
|
565
|
+
|
|
494
566
|
// Probably unnecessary even though it may give us a bit of cov %.. skipping for now
|
|
495
567
|
/* function test_WhenTheCurrentRulesetUseTotalSurplusForCashOutsEqFalse() external {
|
|
496
568
|
// it will use the standard surplus calculation
|