@bananapus/core-v6 0.0.18 → 0.0.20
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 +3 -0
- package/ARCHITECTURE.md +24 -0
- package/AUDIT_INSTRUCTIONS.md +4 -2
- package/CHANGE_LOG.md +29 -1
- package/README.md +12 -2
- package/RISKS.md +10 -2
- package/SKILLS.md +9 -0
- package/USER_JOURNEYS.md +6 -0
- package/foundry.toml +1 -0
- package/package.json +1 -1
- package/src/JBController.sol +52 -5
- package/src/JBMultiTerminal.sol +197 -179
- package/src/JBTerminalStore.sol +367 -171
- package/src/interfaces/IJBCashOutTerminal.sol +30 -0
- package/src/interfaces/IJBController.sol +15 -0
- package/src/interfaces/IJBTerminal.sol +28 -0
- package/src/interfaces/IJBTerminalStore.sol +66 -0
- package/src/libraries/JBPayoutSplitGroupLib.sol +157 -0
- package/src/structs/JBCashOutHookSpecification.sol +2 -0
- package/src/structs/JBPayHookSpecification.sol +2 -0
- package/test/CoreExploitTests.t.sol +21 -10
- package/test/TestCashOutHooks.sol +6 -4
- package/test/TestDataHookFuzzing.sol +6 -2
- package/test/TestPayHooks.sol +1 -1
- package/test/TestRulesetQueueing.sol +4 -5
- package/test/TestRulesetQueuingStress.sol +5 -3
- package/test/TestTerminalPreviewParity.sol +208 -0
- package/test/fork/TestSequencerPriceFeedFork.sol +168 -0
- package/test/fork/TestTerminalPreviewParityFork.sol +109 -0
- package/test/units/static/JBController/TestPreviewMintOf.sol +116 -0
- package/test/units/static/JBMultiTerminal/TestCashOutTokensOf.sol +144 -25
- package/test/units/static/JBMultiTerminal/TestExecutePayout.sol +11 -1
- package/test/units/static/JBMultiTerminal/TestExecuteProcessFee.sol +15 -2
- package/test/units/static/JBMultiTerminal/TestPay.sol +64 -2
- package/test/units/static/JBMultiTerminal/TestPreviewCashOutFrom.sol +116 -0
- package/test/units/static/JBMultiTerminal/TestPreviewPayFor.sol +98 -0
- package/test/units/static/JBMultiTerminal/TestProcessHeldFeesOf.sol +11 -2
- package/test/units/static/JBRulesets/TestCurrentOf.sol +8 -6
- package/test/units/static/JBRulesets/TestRulesets.sol +25 -24
- package/test/units/static/JBRulesets/TestUpcomingRulesetOf.sol +4 -17
- package/test/units/static/JBSurplus/TestSurplusFuzz.sol +49 -2
- package/test/units/static/JBTerminalStore/TestCurrentReclaimableSurplusOf.sol +215 -0
- package/test/units/static/JBTerminalStore/TestPreviewCashOutFrom.sol +475 -0
- package/test/units/static/JBTerminalStore/TestPreviewPayFrom.sol +464 -0
- package/test/units/static/JBTerminalStore/TestRecordCashOutsFor.sol +113 -2
- package/test/units/static/JBTerminalStore/TestRecordPaymentFrom.sol +227 -5
|
@@ -194,7 +194,7 @@ contract TestRecordPaymentFrom_Local is JBTerminalStoreSetup {
|
|
|
194
194
|
|
|
195
195
|
// return data
|
|
196
196
|
JBPayHookSpecification[] memory _spec = new JBPayHookSpecification[](1);
|
|
197
|
-
_spec[0] = JBPayHookSpecification({hook: _payHook, amount: _defaultValue / 2, metadata: ""});
|
|
197
|
+
_spec[0] = JBPayHookSpecification({hook: _payHook, noop: false, amount: _defaultValue / 2, metadata: ""});
|
|
198
198
|
|
|
199
199
|
// mock call to the configured JBRulesetDataHook beforePayRecordedWith
|
|
200
200
|
bytes memory _beforePayCall = abi.encodeCall(IJBRulesetDataHook.beforePayRecordedWith, (_context));
|
|
@@ -274,7 +274,7 @@ contract TestRecordPaymentFrom_Local is JBTerminalStoreSetup {
|
|
|
274
274
|
|
|
275
275
|
// return data
|
|
276
276
|
JBPayHookSpecification[] memory _spec = new JBPayHookSpecification[](1);
|
|
277
|
-
_spec[0] = JBPayHookSpecification({hook: _payHook, amount: _defaultValue * 2, metadata: ""});
|
|
277
|
+
_spec[0] = JBPayHookSpecification({hook: _payHook, noop: false, amount: _defaultValue * 2, metadata: ""});
|
|
278
278
|
|
|
279
279
|
// mock call to the configured JBRulesetDataHook beforePayRecordedWith
|
|
280
280
|
bytes memory _beforePayCall = abi.encodeCall(IJBRulesetDataHook.beforePayRecordedWith, (_context));
|
|
@@ -293,6 +293,228 @@ contract TestRecordPaymentFrom_Local is JBTerminalStoreSetup {
|
|
|
293
293
|
});
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
+
function test_GivenTheHookReturnsANoopSpecWithAmount()
|
|
297
|
+
external
|
|
298
|
+
whenCurrentRulesetUseDataHookForPayEqTrueAndTheHookDneqZeroAddress
|
|
299
|
+
{
|
|
300
|
+
JBTokenAmount memory _tokenAmount = JBTokenAmount({
|
|
301
|
+
token: address(_token), value: _defaultValue, decimals: _defaultDecimals, currency: _currency
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
JBRulesetMetadata memory _metadata = JBRulesetMetadata({
|
|
305
|
+
reservedPercent: 0,
|
|
306
|
+
cashOutTaxRate: JBConstants.MAX_CASH_OUT_TAX_RATE,
|
|
307
|
+
baseCurrency: uint32(uint160(address(_token))),
|
|
308
|
+
pausePay: false,
|
|
309
|
+
pauseCreditTransfers: false,
|
|
310
|
+
allowOwnerMinting: false,
|
|
311
|
+
allowSetCustomToken: false,
|
|
312
|
+
allowTerminalMigration: false,
|
|
313
|
+
allowSetTerminals: false,
|
|
314
|
+
ownerMustSendPayouts: false,
|
|
315
|
+
allowSetController: false,
|
|
316
|
+
allowAddAccountingContext: true,
|
|
317
|
+
allowAddPriceFeed: false,
|
|
318
|
+
holdFees: false,
|
|
319
|
+
useTotalSurplusForCashOuts: false,
|
|
320
|
+
useDataHookForPay: true,
|
|
321
|
+
useDataHookForCashOut: false,
|
|
322
|
+
dataHook: address(_dataHook),
|
|
323
|
+
metadata: 0
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
uint256 _packedMetadata = JBRulesetMetadataResolver.packRulesetMetadata(_metadata);
|
|
327
|
+
|
|
328
|
+
JBRuleset memory _returnedRuleset = JBRuleset({
|
|
329
|
+
cycleNumber: uint48(block.timestamp),
|
|
330
|
+
id: uint48(block.timestamp),
|
|
331
|
+
basedOnId: 0,
|
|
332
|
+
start: uint48(block.timestamp),
|
|
333
|
+
duration: uint32(block.timestamp + 1000),
|
|
334
|
+
weight: 1e18,
|
|
335
|
+
weightCutPercent: 0,
|
|
336
|
+
approvalHook: IJBRulesetApprovalHook(address(0)),
|
|
337
|
+
metadata: _packedMetadata
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
mockExpect(address(rulesets), abi.encodeCall(IJBRulesets.currentOf, (_projectId)), abi.encode(_returnedRuleset));
|
|
341
|
+
|
|
342
|
+
JBBeforePayRecordedContext memory _context = JBBeforePayRecordedContext({
|
|
343
|
+
terminal: address(this),
|
|
344
|
+
payer: address(this),
|
|
345
|
+
amount: _tokenAmount,
|
|
346
|
+
projectId: _projectId,
|
|
347
|
+
rulesetId: uint48(block.timestamp),
|
|
348
|
+
beneficiary: address(this),
|
|
349
|
+
weight: _returnedRuleset.weight,
|
|
350
|
+
reservedPercent: _returnedRuleset.reservedPercent(),
|
|
351
|
+
metadata: ""
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
JBPayHookSpecification[] memory _spec = new JBPayHookSpecification[](1);
|
|
355
|
+
_spec[0] = JBPayHookSpecification({hook: _payHook, noop: true, amount: 1, metadata: ""});
|
|
356
|
+
|
|
357
|
+
mockExpect(
|
|
358
|
+
address(_dataHook),
|
|
359
|
+
abi.encodeCall(IJBRulesetDataHook.beforePayRecordedWith, (_context)),
|
|
360
|
+
abi.encode(1e18 / 2, _spec)
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
vm.expectRevert(abi.encodeWithSelector(JBTerminalStore.JBTerminalStore_NoopHookSpecHasAmount.selector, 1));
|
|
364
|
+
_store.recordPaymentFrom({
|
|
365
|
+
payer: address(this), amount: _tokenAmount, projectId: _projectId, beneficiary: address(this), metadata: ""
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function test_GivenALaterHookReturnsANoopSpecWithAmount()
|
|
370
|
+
external
|
|
371
|
+
whenCurrentRulesetUseDataHookForPayEqTrueAndTheHookDneqZeroAddress
|
|
372
|
+
{
|
|
373
|
+
JBTokenAmount memory _tokenAmount = JBTokenAmount({
|
|
374
|
+
token: address(_token), value: _defaultValue, decimals: _defaultDecimals, currency: _currency
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
JBRulesetMetadata memory _metadata = JBRulesetMetadata({
|
|
378
|
+
reservedPercent: 0,
|
|
379
|
+
cashOutTaxRate: JBConstants.MAX_CASH_OUT_TAX_RATE,
|
|
380
|
+
baseCurrency: uint32(uint160(address(_token))),
|
|
381
|
+
pausePay: false,
|
|
382
|
+
pauseCreditTransfers: false,
|
|
383
|
+
allowOwnerMinting: false,
|
|
384
|
+
allowSetCustomToken: false,
|
|
385
|
+
allowTerminalMigration: false,
|
|
386
|
+
allowSetTerminals: false,
|
|
387
|
+
ownerMustSendPayouts: false,
|
|
388
|
+
allowSetController: false,
|
|
389
|
+
allowAddAccountingContext: true,
|
|
390
|
+
allowAddPriceFeed: false,
|
|
391
|
+
holdFees: false,
|
|
392
|
+
useTotalSurplusForCashOuts: false,
|
|
393
|
+
useDataHookForPay: true,
|
|
394
|
+
useDataHookForCashOut: false,
|
|
395
|
+
dataHook: address(_dataHook),
|
|
396
|
+
metadata: 0
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
uint256 _packedMetadata = JBRulesetMetadataResolver.packRulesetMetadata(_metadata);
|
|
400
|
+
|
|
401
|
+
JBRuleset memory _returnedRuleset = JBRuleset({
|
|
402
|
+
cycleNumber: uint48(block.timestamp),
|
|
403
|
+
id: uint48(block.timestamp),
|
|
404
|
+
basedOnId: 0,
|
|
405
|
+
start: uint48(block.timestamp),
|
|
406
|
+
duration: uint32(block.timestamp + 1000),
|
|
407
|
+
weight: 1e18,
|
|
408
|
+
weightCutPercent: 0,
|
|
409
|
+
approvalHook: IJBRulesetApprovalHook(address(0)),
|
|
410
|
+
metadata: _packedMetadata
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
mockExpect(address(rulesets), abi.encodeCall(IJBRulesets.currentOf, (_projectId)), abi.encode(_returnedRuleset));
|
|
414
|
+
|
|
415
|
+
JBBeforePayRecordedContext memory _context = JBBeforePayRecordedContext({
|
|
416
|
+
terminal: address(this),
|
|
417
|
+
payer: address(this),
|
|
418
|
+
amount: _tokenAmount,
|
|
419
|
+
projectId: _projectId,
|
|
420
|
+
rulesetId: uint48(block.timestamp),
|
|
421
|
+
beneficiary: address(this),
|
|
422
|
+
weight: _returnedRuleset.weight,
|
|
423
|
+
reservedPercent: _returnedRuleset.reservedPercent(),
|
|
424
|
+
metadata: ""
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
JBPayHookSpecification[] memory _spec = new JBPayHookSpecification[](2);
|
|
428
|
+
_spec[0] = JBPayHookSpecification({hook: _payHook, noop: false, amount: 0, metadata: ""});
|
|
429
|
+
_spec[1] = JBPayHookSpecification({hook: _payHook, noop: true, amount: 1, metadata: ""});
|
|
430
|
+
|
|
431
|
+
mockExpect(
|
|
432
|
+
address(_dataHook),
|
|
433
|
+
abi.encodeCall(IJBRulesetDataHook.beforePayRecordedWith, (_context)),
|
|
434
|
+
abi.encode(1e18 / 2, _spec)
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
vm.expectRevert(abi.encodeWithSelector(JBTerminalStore.JBTerminalStore_NoopHookSpecHasAmount.selector, 1));
|
|
438
|
+
_store.recordPaymentFrom({
|
|
439
|
+
payer: address(this), amount: _tokenAmount, projectId: _projectId, beneficiary: address(this), metadata: ""
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function test_GivenTheHookReturnsANoopSpecWithAmountThatExceedsPayment()
|
|
444
|
+
external
|
|
445
|
+
whenCurrentRulesetUseDataHookForPayEqTrueAndTheHookDneqZeroAddress
|
|
446
|
+
{
|
|
447
|
+
JBTokenAmount memory _tokenAmount = JBTokenAmount({
|
|
448
|
+
token: address(_token), value: _defaultValue, decimals: _defaultDecimals, currency: _currency
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
JBRulesetMetadata memory _metadata = JBRulesetMetadata({
|
|
452
|
+
reservedPercent: 0,
|
|
453
|
+
cashOutTaxRate: JBConstants.MAX_CASH_OUT_TAX_RATE,
|
|
454
|
+
baseCurrency: uint32(uint160(address(_token))),
|
|
455
|
+
pausePay: false,
|
|
456
|
+
pauseCreditTransfers: false,
|
|
457
|
+
allowOwnerMinting: false,
|
|
458
|
+
allowSetCustomToken: false,
|
|
459
|
+
allowTerminalMigration: false,
|
|
460
|
+
allowSetTerminals: false,
|
|
461
|
+
ownerMustSendPayouts: false,
|
|
462
|
+
allowSetController: false,
|
|
463
|
+
allowAddAccountingContext: true,
|
|
464
|
+
allowAddPriceFeed: false,
|
|
465
|
+
holdFees: false,
|
|
466
|
+
useTotalSurplusForCashOuts: false,
|
|
467
|
+
useDataHookForPay: true,
|
|
468
|
+
useDataHookForCashOut: false,
|
|
469
|
+
dataHook: address(_dataHook),
|
|
470
|
+
metadata: 0
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
uint256 _packedMetadata = JBRulesetMetadataResolver.packRulesetMetadata(_metadata);
|
|
474
|
+
|
|
475
|
+
JBRuleset memory _returnedRuleset = JBRuleset({
|
|
476
|
+
cycleNumber: uint48(block.timestamp),
|
|
477
|
+
id: uint48(block.timestamp),
|
|
478
|
+
basedOnId: 0,
|
|
479
|
+
start: uint48(block.timestamp),
|
|
480
|
+
duration: uint32(block.timestamp + 1000),
|
|
481
|
+
weight: 1e18,
|
|
482
|
+
weightCutPercent: 0,
|
|
483
|
+
approvalHook: IJBRulesetApprovalHook(address(0)),
|
|
484
|
+
metadata: _packedMetadata
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
mockExpect(address(rulesets), abi.encodeCall(IJBRulesets.currentOf, (_projectId)), abi.encode(_returnedRuleset));
|
|
488
|
+
|
|
489
|
+
JBBeforePayRecordedContext memory _context = JBBeforePayRecordedContext({
|
|
490
|
+
terminal: address(this),
|
|
491
|
+
payer: address(this),
|
|
492
|
+
amount: _tokenAmount,
|
|
493
|
+
projectId: _projectId,
|
|
494
|
+
rulesetId: uint48(block.timestamp),
|
|
495
|
+
beneficiary: address(this),
|
|
496
|
+
weight: _returnedRuleset.weight,
|
|
497
|
+
reservedPercent: _returnedRuleset.reservedPercent(),
|
|
498
|
+
metadata: ""
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
JBPayHookSpecification[] memory _spec = new JBPayHookSpecification[](1);
|
|
502
|
+
_spec[0] = JBPayHookSpecification({hook: _payHook, noop: true, amount: _defaultValue * 2, metadata: ""});
|
|
503
|
+
|
|
504
|
+
mockExpect(
|
|
505
|
+
address(_dataHook),
|
|
506
|
+
abi.encodeCall(IJBRulesetDataHook.beforePayRecordedWith, (_context)),
|
|
507
|
+
abi.encode(1e18 / 2, _spec)
|
|
508
|
+
);
|
|
509
|
+
|
|
510
|
+
vm.expectRevert(
|
|
511
|
+
abi.encodeWithSelector(JBTerminalStore.JBTerminalStore_NoopHookSpecHasAmount.selector, _defaultValue * 2)
|
|
512
|
+
);
|
|
513
|
+
_store.recordPaymentFrom({
|
|
514
|
+
payer: address(this), amount: _tokenAmount, projectId: _projectId, beneficiary: address(this), metadata: ""
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
|
|
296
518
|
function test_GivenWeightReturnedByTheHookIsZero()
|
|
297
519
|
external
|
|
298
520
|
whenCurrentRulesetUseDataHookForPayEqTrueAndTheHookDneqZeroAddress
|
|
@@ -359,7 +581,7 @@ contract TestRecordPaymentFrom_Local is JBTerminalStoreSetup {
|
|
|
359
581
|
|
|
360
582
|
// return data
|
|
361
583
|
JBPayHookSpecification[] memory _spec = new JBPayHookSpecification[](1);
|
|
362
|
-
_spec[0] = JBPayHookSpecification({hook: _payHook, amount: _defaultValue / 2, metadata: ""});
|
|
584
|
+
_spec[0] = JBPayHookSpecification({hook: _payHook, noop: false, amount: _defaultValue / 2, metadata: ""});
|
|
363
585
|
|
|
364
586
|
// mock call to the configured JBRulesetDataHook beforePayRecordedWith
|
|
365
587
|
bytes memory _beforePayCall = abi.encodeCall(IJBRulesetDataHook.beforePayRecordedWith, (_context));
|
|
@@ -424,7 +646,7 @@ contract TestRecordPaymentFrom_Local is JBTerminalStoreSetup {
|
|
|
424
646
|
|
|
425
647
|
// return data
|
|
426
648
|
JBPayHookSpecification[] memory _spec = new JBPayHookSpecification[](1);
|
|
427
|
-
_spec[0] = JBPayHookSpecification({hook: _payHook, amount: _defaultValue / 2, metadata: ""});
|
|
649
|
+
_spec[0] = JBPayHookSpecification({hook: _payHook, noop: false, amount: _defaultValue / 2, metadata: ""});
|
|
428
650
|
|
|
429
651
|
(, uint256 tokenCount,) = _store.recordPaymentFrom({
|
|
430
652
|
payer: address(this), amount: _tokenAmount, projectId: _projectId, beneficiary: address(this), metadata: ""
|
|
@@ -496,7 +718,7 @@ contract TestRecordPaymentFrom_Local is JBTerminalStoreSetup {
|
|
|
496
718
|
|
|
497
719
|
// return data
|
|
498
720
|
JBPayHookSpecification[] memory _spec = new JBPayHookSpecification[](1);
|
|
499
|
-
_spec[0] = JBPayHookSpecification({hook: _payHook, amount: _defaultValue / 2, metadata: ""});
|
|
721
|
+
_spec[0] = JBPayHookSpecification({hook: _payHook, noop: false, amount: _defaultValue / 2, metadata: ""});
|
|
500
722
|
|
|
501
723
|
// mock call to the configured JBRulesetDataHook beforePayRecordedWith
|
|
502
724
|
mockExpect(
|