@rev-net/core-v6 0.0.29 → 0.0.31

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 (77) hide show
  1. package/ADMINISTRATION.md +19 -9
  2. package/ARCHITECTURE.md +3 -0
  3. package/AUDIT_INSTRUCTIONS.md +11 -1
  4. package/CHANGELOG.md +26 -0
  5. package/README.md +1 -0
  6. package/RISKS.md +28 -4
  7. package/SKILLS.md +2 -1
  8. package/USER_JOURNEYS.md +28 -3
  9. package/package.json +8 -8
  10. package/references/operations.md +1 -1
  11. package/script/Deploy.s.sol +26 -4
  12. package/src/REVDeployer.sol +4 -2
  13. package/src/REVHiddenTokens.sol +149 -0
  14. package/src/REVLoans.sol +192 -199
  15. package/src/REVOwner.sol +51 -14
  16. package/src/interfaces/IREVHiddenTokens.sol +53 -0
  17. package/src/interfaces/IREVLoans.sol +8 -6
  18. package/test/REV.integrations.t.sol +12 -2
  19. package/test/REVAutoIssuanceFuzz.t.sol +12 -2
  20. package/test/REVDeployerRegressions.t.sol +14 -3
  21. package/test/REVInvincibility.t.sol +27 -8
  22. package/test/REVInvincibilityHandler.sol +1 -1
  23. package/test/REVLifecycle.t.sol +14 -3
  24. package/test/REVLoans.invariants.t.sol +15 -4
  25. package/test/REVLoansAttacks.t.sol +19 -7
  26. package/test/REVLoansFeeRecovery.t.sol +24 -13
  27. package/test/REVLoansFindings.t.sol +16 -5
  28. package/test/REVLoansRegressions.t.sol +15 -4
  29. package/test/REVLoansSourceFeeRecovery.t.sol +16 -5
  30. package/test/REVLoansSourced.t.sol +60 -25
  31. package/test/REVLoansUnSourced.t.sol +15 -4
  32. package/test/TestBurnHeldTokens.t.sol +14 -3
  33. package/test/TestCEIPattern.t.sol +19 -7
  34. package/test/TestCashOutCallerValidation.t.sol +15 -4
  35. package/test/TestConversionDocumentation.t.sol +14 -3
  36. package/test/TestCrossCurrencyReclaim.t.sol +14 -3
  37. package/test/TestCrossSourceReallocation.t.sol +15 -4
  38. package/test/TestERC2771MetaTx.t.sol +18 -5
  39. package/test/TestEmptyBuybackSpecs.t.sol +14 -3
  40. package/test/TestFlashLoanSurplus.t.sol +15 -4
  41. package/test/TestHiddenTokens.t.sol +431 -0
  42. package/test/TestHookArrayOOB.t.sol +14 -3
  43. package/test/TestLiquidationBehavior.t.sol +16 -5
  44. package/test/TestLoanSourceRotation.t.sol +20 -7
  45. package/test/TestLoansCashOutDelay.t.sol +18 -7
  46. package/test/TestLongTailEconomics.t.sol +14 -3
  47. package/test/TestLowFindings.t.sol +25 -9
  48. package/test/TestMixedFixes.t.sol +19 -8
  49. package/test/TestPermit2Signatures.t.sol +15 -4
  50. package/test/TestReallocationSandwich.t.sol +16 -4
  51. package/test/TestRevnetRegressions.t.sol +16 -5
  52. package/test/TestSplitWeightAdjustment.t.sol +16 -4
  53. package/test/TestSplitWeightE2E.t.sol +18 -4
  54. package/test/TestSplitWeightFork.t.sol +16 -3
  55. package/test/TestStageTransitionBorrowable.t.sol +14 -3
  56. package/test/TestSwapTerminalPermission.t.sol +14 -3
  57. package/test/TestUint112Overflow.t.sol +15 -4
  58. package/test/TestZeroAmountLoanGuard.t.sol +15 -4
  59. package/test/TestZeroRepayment.t.sol +15 -4
  60. package/test/audit/CodexPhantomSurplusTerminal.t.sol +367 -0
  61. package/test/audit/LoanIdOverflowGuard.t.sol +16 -5
  62. package/test/audit/NemesisOperatorDelegation.t.sol +289 -0
  63. package/test/fork/ForkTestBase.sol +18 -4
  64. package/test/fork/TestLoanBorrowFork.t.sol +2 -1
  65. package/test/fork/TestLoanERC20Fork.t.sol +4 -2
  66. package/test/fork/TestLoanTransferFork.t.sol +12 -2
  67. package/test/helpers/MaliciousContracts.sol +1 -1
  68. package/test/mock/MockBuybackCashOutRecorder.sol +2 -0
  69. package/test/mock/MockBuybackDataHook.sol +3 -1
  70. package/test/mock/MockBuybackDataHookMintPath.sol +2 -0
  71. package/test/mock/MockSuckerRegistry.sol +17 -0
  72. package/test/regression/TestBurnPermissionRequired.t.sol +16 -5
  73. package/test/regression/TestCashOutBuybackFeeLeak.t.sol +16 -3
  74. package/test/regression/TestCrossRevnetLiquidation.t.sol +14 -3
  75. package/test/regression/TestCumulativeLoanCounter.t.sol +15 -4
  76. package/test/regression/TestLiquidateGapHandling.t.sol +15 -4
  77. package/test/regression/TestZeroPriceFeed.t.sol +17 -6
@@ -23,6 +23,8 @@ import "@croptop/core-v6/script/helpers/CroptopDeploymentLib.sol";
23
23
  // forge-lint: disable-next-line(unaliased-plain-import)
24
24
  import "@bananapus/router-terminal-v6/script/helpers/RouterTerminalDeploymentLib.sol";
25
25
 
26
+ import {JBPermissioned} from "@bananapus/core-v6/src/abstract/JBPermissioned.sol";
27
+ import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
26
28
  import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
27
29
  import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
28
30
  import {MockPriceFeed} from "@bananapus/core-v6/test/mock/MockPriceFeed.sol";
@@ -38,11 +40,14 @@ import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
38
40
  import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
39
41
  import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
40
42
  import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
43
+ import {JB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
44
+ import {IJB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721CheckpointsDeployer.sol";
41
45
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
42
46
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
43
47
  import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
44
48
  import {REVOwner} from "../src/REVOwner.sol";
45
49
  import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
50
+ import {MockSuckerRegistry} from "./mock/MockSuckerRegistry.sol";
46
51
 
47
52
  struct FeeProjectConfig {
48
53
  REVConfig configuration;
@@ -304,7 +309,14 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
304
309
  HOOK_STORE = new JB721TiersHookStore();
305
310
 
306
311
  EXAMPLE_HOOK = new JB721TiersHook(
307
- jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
312
+ jbDirectory(),
313
+ jbPermissions(),
314
+ jbPrices(),
315
+ jbRulesets(),
316
+ HOOK_STORE,
317
+ jbSplits(),
318
+ IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
319
+ multisig()
308
320
  );
309
321
 
310
322
  ADDRESS_REGISTRY = new JBAddressRegistry();
@@ -328,7 +340,7 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
328
340
 
329
341
  LOANS_CONTRACT = new REVLoans({
330
342
  controller: jbController(),
331
- projects: jbProjects(),
343
+ suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
332
344
  revId: FEE_PROJECT_ID,
333
345
  owner: address(this),
334
346
  permit2: permit2(),
@@ -340,7 +352,8 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
340
352
  jbDirectory(),
341
353
  FEE_PROJECT_ID,
342
354
  SUCKER_REGISTRY,
343
- address(LOANS_CONTRACT)
355
+ address(LOANS_CONTRACT),
356
+ address(0)
344
357
  );
345
358
 
346
359
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
@@ -428,7 +441,8 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
428
441
  REVLoanSource memory sauce = REVLoanSource({token: address(TOKEN), terminal: jbMultiTerminal()});
429
442
 
430
443
  vm.prank(USER);
431
- (uint256 newLoanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), prepaidFee);
444
+ (uint256 newLoanId,) =
445
+ LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), prepaidFee, USER);
432
446
 
433
447
  REVLoan memory loan = LOANS_CONTRACT.loanOf(newLoanId);
434
448
  assertEq(loan.amount, loanable);
@@ -480,7 +494,8 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
480
494
  REVLoanSource memory sauce = REVLoanSource({token: address(TOKEN), terminal: jbMultiTerminal()});
481
495
 
482
496
  vm.prank(USER);
483
- (uint256 newLoanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), prepaidFee);
497
+ (uint256 newLoanId,) =
498
+ LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), prepaidFee, USER);
484
499
 
485
500
  REVLoan memory loan = LOANS_CONTRACT.loanOf(newLoanId);
486
501
  assertEq(loan.amount, loanable);
@@ -539,7 +554,8 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
539
554
  uint256 balanceBeforeLoan = TOKEN.balanceOf(USER);
540
555
 
541
556
  // Create the new loan.
542
- (uint256 newLoanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, source, loanable, tokens, payable(USER), prepaidFee);
557
+ (uint256 newLoanId,) =
558
+ LOANS_CONTRACT.borrowFrom(REVNET_ID, source, loanable, tokens, payable(USER), prepaidFee, USER);
543
559
  REVLoan memory loan = LOANS_CONTRACT.loanOf(newLoanId);
544
560
 
545
561
  // Check what amount we actually received.
@@ -610,7 +626,8 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
610
626
  REVLoanSource memory sauce = REVLoanSource({token: address(TOKEN), terminal: jbMultiTerminal()});
611
627
 
612
628
  vm.prank(USER);
613
- (uint256 newLoanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), prepaidFee);
629
+ (uint256 newLoanId,) =
630
+ LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), prepaidFee, USER);
614
631
 
615
632
  REVLoan memory loan = LOANS_CONTRACT.loanOf(newLoanId);
616
633
  assertEq(loan.amount, loanable);
@@ -815,7 +832,7 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
815
832
  uint256 balanceBefore = USER.balance;
816
833
 
817
834
  vm.prank(USER);
818
- (uint256 newLoanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 500);
835
+ (uint256 newLoanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 500, USER);
819
836
 
820
837
  REVLoan memory loan = LOANS_CONTRACT.loanOf(newLoanId);
821
838
  assertEq(loan.amount, loanable);
@@ -869,7 +886,7 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
869
886
 
870
887
  vm.prank(USER);
871
888
  (newLoanId,) =
872
- LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), prepaidFeePercent);
889
+ LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), prepaidFeePercent, USER);
873
890
  }
874
891
 
875
892
  REVLoan memory loan = LOANS_CONTRACT.loanOf(newLoanId);
@@ -976,7 +993,7 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
976
993
  REVLoanSource memory sauce = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
977
994
 
978
995
  vm.prank(USER);
979
- (uint256 newLoanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 500);
996
+ (uint256 newLoanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 500, USER);
980
997
 
981
998
  REVLoan memory loan = LOANS_CONTRACT.loanOf(newLoanId);
982
999
 
@@ -1058,7 +1075,7 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
1058
1075
  REVLoanSource memory sauce = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
1059
1076
 
1060
1077
  vm.prank(USER);
1061
- (uint256 newLoanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 500);
1078
+ (uint256 newLoanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 500, USER);
1062
1079
 
1063
1080
  REVLoan memory loan = LOANS_CONTRACT.loanOf(newLoanId);
1064
1081
 
@@ -1121,7 +1138,7 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
1121
1138
  REVLoanSource memory sauce = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
1122
1139
 
1123
1140
  vm.prank(USER);
1124
- (uint256 newLoanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 500);
1141
+ (uint256 newLoanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 500, USER);
1125
1142
 
1126
1143
  REVLoan memory loan = LOANS_CONTRACT.loanOf(newLoanId);
1127
1144
 
@@ -1151,7 +1168,15 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
1151
1168
  );
1152
1169
 
1153
1170
  address unauthorized = address(1);
1154
- vm.expectRevert(abi.encodeWithSelector(REVLoans.REVLoans_Unauthorized.selector, unauthorized, USER));
1171
+ vm.expectRevert(
1172
+ abi.encodeWithSelector(
1173
+ JBPermissioned.JBPermissioned_Unauthorized.selector,
1174
+ USER,
1175
+ unauthorized,
1176
+ REVNET_ID,
1177
+ JBPermissionIds.REALLOCATE_LOAN
1178
+ )
1179
+ );
1155
1180
 
1156
1181
  vm.prank(unauthorized);
1157
1182
  LOANS_CONTRACT.reallocateCollateralFromLoan(
@@ -1251,7 +1276,8 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
1251
1276
  revnetProjectId, JBConstants.NATIVE_TOKEN, paymentPerBorrow, USER, 0, "", ""
1252
1277
  );
1253
1278
 
1254
- (, REVLoan memory loan) = LOANS_CONTRACT.borrowFrom(revnetProjectId, source, 0, tokens, payable(USER), 500);
1279
+ (, REVLoan memory loan) =
1280
+ LOANS_CONTRACT.borrowFrom(revnetProjectId, source, 0, tokens, payable(USER), 500, USER);
1255
1281
 
1256
1282
  if (i == 0) {
1257
1283
  initialBorrow = loan.amount;
@@ -1336,7 +1362,7 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
1336
1362
  uint256 balanceBefore = USER.balance;
1337
1363
  REVLoanSource memory source = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
1338
1364
  (uint256 newLoanId, REVLoan memory loan) =
1339
- LOANS_CONTRACT.borrowFrom(revnetProjectId, source, loanable, tokens, payable(USER), 500);
1365
+ LOANS_CONTRACT.borrowFrom(revnetProjectId, source, loanable, tokens, payable(USER), 500, USER);
1340
1366
 
1341
1367
  // Ensure loans contract isn't hodling
1342
1368
  assertEq(address(LOANS_CONTRACT).balance, 0);
@@ -1395,7 +1421,7 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
1395
1421
  REVLoanSource memory sauce = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
1396
1422
 
1397
1423
  vm.prank(USER);
1398
- (uint256 newLoanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 500);
1424
+ (uint256 newLoanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 500, USER);
1399
1425
 
1400
1426
  REVLoan memory loan = LOANS_CONTRACT.loanOf(newLoanId);
1401
1427
 
@@ -1480,7 +1506,7 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
1480
1506
  REVLoanSource memory sauce = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
1481
1507
 
1482
1508
  vm.prank(USER);
1483
- (uint256 newLoanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 500);
1509
+ (uint256 newLoanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 500, USER);
1484
1510
 
1485
1511
  REVLoan memory loan = LOANS_CONTRACT.loanOf(newLoanId);
1486
1512
 
@@ -1553,7 +1579,8 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
1553
1579
  assertEq(sources.length, 0);
1554
1580
 
1555
1581
  vm.prank(USER);
1556
- (, REVLoan memory loan) = LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 100);
1582
+ (, REVLoan memory loan) =
1583
+ LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 100, USER);
1557
1584
 
1558
1585
  // Source should exist after a borrow
1559
1586
  REVLoanSource[] memory sourcesUpdated = LOANS_CONTRACT.loanSourcesOf(REVNET_ID);
@@ -1596,7 +1623,7 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
1596
1623
  vm.expectRevert(
1597
1624
  abi.encodeWithSelector(REVLoans.REVLoans_InvalidPrepaidFeePercent.selector, feePercentage, 25, 500)
1598
1625
  );
1599
- LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, 1, tokens, payable(USER), feePercentage);
1626
+ LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, 1, tokens, payable(USER), feePercentage, USER);
1600
1627
  }
1601
1628
 
1602
1629
  function test_liquidateLoans() external {
@@ -1618,7 +1645,7 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
1618
1645
 
1619
1646
  vm.prank(USER);
1620
1647
  (uint256 loanId, REVLoan memory loan) =
1621
- LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 100);
1648
+ LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 100, USER);
1622
1649
 
1623
1650
  // Take out another loan
1624
1651
  vm.prank(USER);
@@ -1636,7 +1663,7 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
1636
1663
 
1637
1664
  vm.prank(USER);
1638
1665
  (uint256 loanId2, REVLoan memory loan2) =
1639
- LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable2, tokens2, payable(USER), 50);
1666
+ LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable2, tokens2, payable(USER), 50, USER);
1640
1667
 
1641
1668
  // Warp further than the loan liquidation duration.
1642
1669
  vm.warp(block.timestamp + 10_000 days);
@@ -1674,7 +1701,7 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
1674
1701
 
1675
1702
  vm.prank(USER);
1676
1703
  (uint256 loanId, REVLoan memory loan) =
1677
- LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 100);
1704
+ LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 100, USER);
1678
1705
 
1679
1706
  // Attempt to liquidate before the loan is expired and loop will break
1680
1707
  LOANS_CONTRACT.liquidateExpiredLoansFrom(REVNET_ID, 0, 2);
@@ -1747,14 +1774,22 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
1747
1774
  REVLoanSource memory sauce = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
1748
1775
 
1749
1776
  vm.prank(USER);
1750
- (uint256 loanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 100);
1777
+ (uint256 loanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 100, USER);
1751
1778
 
1752
1779
  // empty allowance data
1753
1780
  JBSingleAllowance memory allowance;
1754
1781
 
1755
1782
  // call to pay-down the loan
1756
1783
  /* vm.prank(USER); */
1757
- vm.expectRevert(abi.encodeWithSelector(REVLoans.REVLoans_Unauthorized.selector, address(this), USER));
1784
+ vm.expectRevert(
1785
+ abi.encodeWithSelector(
1786
+ JBPermissioned.JBPermissioned_Unauthorized.selector,
1787
+ USER,
1788
+ address(this),
1789
+ REVNET_ID,
1790
+ JBPermissionIds.REPAY_LOAN
1791
+ )
1792
+ );
1758
1793
  LOANS_CONTRACT.repayLoan{value: 0}(loanId, 0, 0, payable(USER), allowance);
1759
1794
  }
1760
1795
 
@@ -1777,7 +1812,7 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
1777
1812
 
1778
1813
  vm.prank(USER);
1779
1814
  (uint256 loanId, REVLoan memory loan) =
1780
- LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 100);
1815
+ LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 100, USER);
1781
1816
 
1782
1817
  // empty allowance data
1783
1818
  JBSingleAllowance memory allowance;
@@ -38,10 +38,13 @@ import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
38
38
  import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
39
39
  import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
40
40
  import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
41
+ import {JB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
42
+ import {IJB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721CheckpointsDeployer.sol";
41
43
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
42
44
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
43
45
  import {REVOwner} from "../src/REVOwner.sol";
44
46
  import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
47
+ import {MockSuckerRegistry} from "./mock/MockSuckerRegistry.sol";
45
48
 
46
49
  struct FeeProjectConfig {
47
50
  REVConfig configuration;
@@ -292,7 +295,14 @@ contract REVLoansUnsourcedTests is TestBaseWorkflow {
292
295
  HOOK_STORE = new JB721TiersHookStore();
293
296
 
294
297
  EXAMPLE_HOOK = new JB721TiersHook(
295
- jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
298
+ jbDirectory(),
299
+ jbPermissions(),
300
+ jbPrices(),
301
+ jbRulesets(),
302
+ HOOK_STORE,
303
+ jbSplits(),
304
+ IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
305
+ multisig()
296
306
  );
297
307
 
298
308
  ADDRESS_REGISTRY = new JBAddressRegistry();
@@ -304,7 +314,7 @@ contract REVLoansUnsourcedTests is TestBaseWorkflow {
304
314
 
305
315
  LOANS_CONTRACT = new REVLoans({
306
316
  controller: jbController(),
307
- projects: jbProjects(),
317
+ suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
308
318
  revId: FEE_PROJECT_ID,
309
319
  owner: address(this),
310
320
  permit2: permit2(),
@@ -316,7 +326,8 @@ contract REVLoansUnsourcedTests is TestBaseWorkflow {
316
326
  jbDirectory(),
317
327
  FEE_PROJECT_ID,
318
328
  SUCKER_REGISTRY,
319
- address(LOANS_CONTRACT)
329
+ address(LOANS_CONTRACT),
330
+ address(0)
320
331
  );
321
332
 
322
333
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
@@ -393,6 +404,6 @@ contract REVLoansUnsourcedTests is TestBaseWorkflow {
393
404
  REVLoanSource memory sauce = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
394
405
 
395
406
  vm.prank(USER);
396
- LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 500);
407
+ LOANS_CONTRACT.borrowFrom(REVNET_ID, sauce, loanable, tokens, payable(USER), 500, USER);
397
408
  }
398
409
  }
@@ -35,10 +35,13 @@ import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
35
35
  import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
36
36
  import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
37
37
  import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
38
+ import {JB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
39
+ import {IJB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721CheckpointsDeployer.sol";
38
40
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
39
41
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
40
42
  import {REVOwner} from "../src/REVOwner.sol";
41
43
  import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
44
+ import {MockSuckerRegistry} from "./mock/MockSuckerRegistry.sol";
42
45
 
43
46
  struct FeeProjectConfig {
44
47
  REVConfig configuration;
@@ -197,7 +200,14 @@ contract TestBurnHeldTokens is TestBaseWorkflow {
197
200
  SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
198
201
  HOOK_STORE = new JB721TiersHookStore();
199
202
  EXAMPLE_HOOK = new JB721TiersHook(
200
- jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
203
+ jbDirectory(),
204
+ jbPermissions(),
205
+ jbPrices(),
206
+ jbRulesets(),
207
+ HOOK_STORE,
208
+ jbSplits(),
209
+ IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
210
+ multisig()
201
211
  );
202
212
  ADDRESS_REGISTRY = new JBAddressRegistry();
203
213
  HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
@@ -207,7 +217,7 @@ contract TestBurnHeldTokens is TestBaseWorkflow {
207
217
 
208
218
  LOANS_CONTRACT = new REVLoans({
209
219
  controller: jbController(),
210
- projects: jbProjects(),
220
+ suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
211
221
  revId: FEE_PROJECT_ID,
212
222
  owner: address(this),
213
223
  permit2: permit2(),
@@ -219,7 +229,8 @@ contract TestBurnHeldTokens is TestBaseWorkflow {
219
229
  jbDirectory(),
220
230
  FEE_PROJECT_ID,
221
231
  SUCKER_REGISTRY,
222
- address(LOANS_CONTRACT)
232
+ address(LOANS_CONTRACT),
233
+ address(0)
223
234
  );
224
235
 
225
236
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
@@ -37,11 +37,14 @@ import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
37
37
  import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
38
38
  import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
39
39
  import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
40
+ import {JB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
41
+ import {IJB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721CheckpointsDeployer.sol";
40
42
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
41
43
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
42
44
  import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
43
45
  import {REVOwner} from "../src/REVOwner.sol";
44
46
  import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
47
+ import {MockSuckerRegistry} from "./mock/MockSuckerRegistry.sol";
45
48
 
46
49
  /// @notice Contract that reenters REVLoans when it receives ETH during a borrow payout.
47
50
  /// Records the loan state it observes during reentrancy to verify CEI correctness.
@@ -123,7 +126,14 @@ contract TestCEIPattern is TestBaseWorkflow {
123
126
  SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
124
127
  HOOK_STORE = new JB721TiersHookStore();
125
128
  EXAMPLE_HOOK = new JB721TiersHook(
126
- jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
129
+ jbDirectory(),
130
+ jbPermissions(),
131
+ jbPrices(),
132
+ jbRulesets(),
133
+ HOOK_STORE,
134
+ jbSplits(),
135
+ IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
136
+ multisig()
127
137
  );
128
138
  ADDRESS_REGISTRY = new JBAddressRegistry();
129
139
  HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
@@ -136,7 +146,7 @@ contract TestCEIPattern is TestBaseWorkflow {
136
146
  .addPriceFeedFor(0, uint32(uint160(address(TOKEN))), uint32(uint160(JBConstants.NATIVE_TOKEN)), priceFeed);
137
147
  LOANS_CONTRACT = new REVLoans({
138
148
  controller: jbController(),
139
- projects: jbProjects(),
149
+ suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
140
150
  revId: FEE_PROJECT_ID,
141
151
  owner: address(this),
142
152
  permit2: permit2(),
@@ -147,7 +157,8 @@ contract TestCEIPattern is TestBaseWorkflow {
147
157
  jbDirectory(),
148
158
  FEE_PROJECT_ID,
149
159
  SUCKER_REGISTRY,
150
- address(LOANS_CONTRACT)
160
+ address(LOANS_CONTRACT),
161
+ address(0)
151
162
  );
152
163
 
153
164
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
@@ -283,7 +294,7 @@ contract TestCEIPattern is TestBaseWorkflow {
283
294
  );
284
295
  REVLoanSource memory source = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
285
296
  vm.prank(user);
286
- (loanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(user), prepaidFee);
297
+ (loanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(user), prepaidFee, user);
287
298
  }
288
299
 
289
300
  /// @notice After borrowing, loan.amount and loan.collateral are set correctly (CEI: state written before external
@@ -347,7 +358,7 @@ contract TestCEIPattern is TestBaseWorkflow {
347
358
  );
348
359
  REVLoanSource memory source = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
349
360
  vm.prank(USER);
350
- LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokens2, payable(USER), 25);
361
+ LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokens2, payable(USER), 25, USER);
351
362
  }
352
363
 
353
364
  // Total collateral should equal sum of both loans' collateral
@@ -389,7 +400,8 @@ contract TestCEIPattern is TestBaseWorkflow {
389
400
 
390
401
  // Borrow with attacker as beneficiary — attacker's receive() will fire when ETH arrives.
391
402
  vm.prank(address(attacker));
392
- (uint256 loanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokens, payable(address(attacker)), 25);
403
+ (uint256 loanId,) =
404
+ LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokens, payable(address(attacker)), 25, address(attacker));
393
405
 
394
406
  assertEq(loanId, expectedLoanId, "LoanId should match pre-computed value");
395
407
 
@@ -472,7 +484,7 @@ contract TestCEIPattern is TestBaseWorkflow {
472
484
  REVLoanSource memory source = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
473
485
 
474
486
  vm.prank(USER);
475
- (uint256 loanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokens, payable(USER), 25);
487
+ (uint256 loanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokens, payable(USER), 25, USER);
476
488
 
477
489
  REVLoan memory loan = LOANS_CONTRACT.loanOf(loanId);
478
490
 
@@ -39,11 +39,14 @@ import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
39
39
  import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
40
40
  import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
41
41
  import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
42
+ import {JB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
43
+ import {IJB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721CheckpointsDeployer.sol";
42
44
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
43
45
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
44
46
  import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
45
47
  import {REVOwner} from "../src/REVOwner.sol";
46
48
  import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
49
+ import {MockSuckerRegistry} from "./mock/MockSuckerRegistry.sol";
47
50
 
48
51
  struct FeeProjectConfig {
49
52
  REVConfig configuration;
@@ -210,7 +213,14 @@ contract TestCashOutCallerValidation is TestBaseWorkflow {
210
213
  SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
211
214
  HOOK_STORE = new JB721TiersHookStore();
212
215
  EXAMPLE_HOOK = new JB721TiersHook(
213
- jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
216
+ jbDirectory(),
217
+ jbPermissions(),
218
+ jbPrices(),
219
+ jbRulesets(),
220
+ HOOK_STORE,
221
+ jbSplits(),
222
+ IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
223
+ multisig()
214
224
  );
215
225
  ADDRESS_REGISTRY = new JBAddressRegistry();
216
226
  HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
@@ -220,7 +230,7 @@ contract TestCashOutCallerValidation is TestBaseWorkflow {
220
230
 
221
231
  LOANS_CONTRACT = new REVLoans({
222
232
  controller: jbController(),
223
- projects: jbProjects(),
233
+ suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
224
234
  revId: FEE_PROJECT_ID,
225
235
  owner: address(this),
226
236
  permit2: permit2(),
@@ -232,7 +242,8 @@ contract TestCashOutCallerValidation is TestBaseWorkflow {
232
242
  jbDirectory(),
233
243
  FEE_PROJECT_ID,
234
244
  SUCKER_REGISTRY,
235
- address(LOANS_CONTRACT)
245
+ address(LOANS_CONTRACT),
246
+ address(0)
236
247
  );
237
248
 
238
249
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
@@ -354,7 +365,7 @@ contract TestCashOutCallerValidation is TestBaseWorkflow {
354
365
  (
355
366
  uint256 cashOutTaxRate,
356
367
  uint256 cashOutCount,
357
- uint256 totalSupply,
368
+ uint256 totalSupply,,
358
369
  JBCashOutHookSpecification[] memory hookSpecifications
359
370
  ) = REV_OWNER.beforeCashOutRecordedWith(context);
360
371
 
@@ -35,10 +35,13 @@ import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
35
35
  import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
36
36
  import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
37
37
  import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
38
+ import {JB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
39
+ import {IJB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721CheckpointsDeployer.sol";
38
40
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
39
41
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
40
42
  import {REVOwner} from "../src/REVOwner.sol";
41
43
  import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
44
+ import {MockSuckerRegistry} from "./mock/MockSuckerRegistry.sol";
42
45
 
43
46
  contract TestConversionDocumentation is TestBaseWorkflow {
44
47
  // forge-lint: disable-next-line(mixed-case-variable)
@@ -140,7 +143,14 @@ contract TestConversionDocumentation is TestBaseWorkflow {
140
143
  SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
141
144
  HOOK_STORE = new JB721TiersHookStore();
142
145
  EXAMPLE_HOOK = new JB721TiersHook(
143
- jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
146
+ jbDirectory(),
147
+ jbPermissions(),
148
+ jbPrices(),
149
+ jbRulesets(),
150
+ HOOK_STORE,
151
+ jbSplits(),
152
+ IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
153
+ multisig()
144
154
  );
145
155
  ADDRESS_REGISTRY = new JBAddressRegistry();
146
156
  HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
@@ -150,7 +160,7 @@ contract TestConversionDocumentation is TestBaseWorkflow {
150
160
 
151
161
  LOANS_CONTRACT = new REVLoans({
152
162
  controller: jbController(),
153
- projects: jbProjects(),
163
+ suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
154
164
  revId: FEE_PROJECT_ID,
155
165
  owner: address(this),
156
166
  permit2: permit2(),
@@ -162,7 +172,8 @@ contract TestConversionDocumentation is TestBaseWorkflow {
162
172
  jbDirectory(),
163
173
  FEE_PROJECT_ID,
164
174
  SUCKER_REGISTRY,
165
- address(LOANS_CONTRACT)
175
+ address(LOANS_CONTRACT),
176
+ address(0)
166
177
  );
167
178
 
168
179
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
@@ -34,11 +34,14 @@ import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
34
34
  import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
35
35
  import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
36
36
  import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
37
+ import {JB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
38
+ import {IJB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721CheckpointsDeployer.sol";
37
39
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
38
40
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
39
41
  import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
40
42
  import {REVOwner} from "../src/REVOwner.sol";
41
43
  import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
44
+ import {MockSuckerRegistry} from "./mock/MockSuckerRegistry.sol";
42
45
 
43
46
  /// @notice Cross-currency reclaim tests: verify cash-out behavior when a revnet's baseCurrency differs from the
44
47
  /// terminal token currency, and when price feeds return various values.
@@ -89,7 +92,14 @@ contract TestCrossCurrencyReclaim is TestBaseWorkflow {
89
92
  SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
90
93
  HOOK_STORE = new JB721TiersHookStore();
91
94
  EXAMPLE_HOOK = new JB721TiersHook(
92
- jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
95
+ jbDirectory(),
96
+ jbPermissions(),
97
+ jbPrices(),
98
+ jbRulesets(),
99
+ HOOK_STORE,
100
+ jbSplits(),
101
+ IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
102
+ multisig()
93
103
  );
94
104
  ADDRESS_REGISTRY = new JBAddressRegistry();
95
105
  HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
@@ -101,7 +111,7 @@ contract TestCrossCurrencyReclaim is TestBaseWorkflow {
101
111
 
102
112
  LOANS_CONTRACT = new REVLoans({
103
113
  controller: jbController(),
104
- projects: jbProjects(),
114
+ suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
105
115
  revId: FEE_PROJECT_ID,
106
116
  owner: address(this),
107
117
  permit2: permit2(),
@@ -113,7 +123,8 @@ contract TestCrossCurrencyReclaim is TestBaseWorkflow {
113
123
  jbDirectory(),
114
124
  FEE_PROJECT_ID,
115
125
  SUCKER_REGISTRY,
116
- address(LOANS_CONTRACT)
126
+ address(LOANS_CONTRACT),
127
+ address(0)
117
128
  );
118
129
 
119
130
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(