@bananapus/core-v6 0.0.19 → 0.0.21

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.
Files changed (44) hide show
  1. package/ADMINISTRATION.md +3 -0
  2. package/ARCHITECTURE.md +24 -0
  3. package/AUDIT_INSTRUCTIONS.md +4 -2
  4. package/CHANGE_LOG.md +17 -4
  5. package/README.md +12 -2
  6. package/RISKS.md +10 -2
  7. package/SKILLS.md +5 -2
  8. package/USER_JOURNEYS.md +4 -2
  9. package/foundry.toml +1 -0
  10. package/package.json +1 -1
  11. package/src/JBController.sol +52 -5
  12. package/src/JBMultiTerminal.sol +197 -179
  13. package/src/JBTerminalStore.sol +17 -7
  14. package/src/interfaces/IJBCashOutTerminal.sol +30 -0
  15. package/src/interfaces/IJBController.sol +15 -0
  16. package/src/interfaces/IJBTerminal.sol +28 -0
  17. package/src/interfaces/IJBTerminalStore.sol +1 -5
  18. package/src/libraries/JBPayoutSplitGroupLib.sol +157 -0
  19. package/src/structs/JBCashOutHookSpecification.sol +2 -0
  20. package/src/structs/JBPayHookSpecification.sol +2 -0
  21. package/test/CoreExploitTests.t.sol +21 -10
  22. package/test/TestCashOutHooks.sol +6 -4
  23. package/test/TestDataHookFuzzing.sol +6 -2
  24. package/test/TestPayHooks.sol +1 -1
  25. package/test/TestRulesetQueueing.sol +4 -5
  26. package/test/TestRulesetQueuingStress.sol +5 -3
  27. package/test/TestTerminalPreviewParity.sol +208 -0
  28. package/test/fork/TestTerminalPreviewParityFork.sol +109 -0
  29. package/test/units/static/JBController/TestPreviewMintOf.sol +116 -0
  30. package/test/units/static/JBMultiTerminal/TestCashOutTokensOf.sol +144 -25
  31. package/test/units/static/JBMultiTerminal/TestExecutePayout.sol +11 -1
  32. package/test/units/static/JBMultiTerminal/TestExecuteProcessFee.sol +15 -2
  33. package/test/units/static/JBMultiTerminal/TestPay.sol +64 -2
  34. package/test/units/static/JBMultiTerminal/TestPreviewCashOutFrom.sol +116 -0
  35. package/test/units/static/JBMultiTerminal/TestPreviewPayFor.sol +98 -0
  36. package/test/units/static/JBMultiTerminal/TestProcessHeldFeesOf.sol +11 -2
  37. package/test/units/static/JBRulesets/TestCurrentOf.sol +8 -6
  38. package/test/units/static/JBRulesets/TestRulesets.sol +25 -24
  39. package/test/units/static/JBRulesets/TestUpcomingRulesetOf.sol +4 -17
  40. package/test/units/static/JBSurplus/TestSurplusFuzz.sol +49 -2
  41. package/test/units/static/JBTerminalStore/TestPreviewCashOutFrom.sol +96 -4
  42. package/test/units/static/JBTerminalStore/TestPreviewPayFrom.sol +81 -32
  43. package/test/units/static/JBTerminalStore/TestRecordCashOutsFor.sol +113 -2
  44. 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(