@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
@@ -0,0 +1,367 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.28;
3
+
4
+ import "forge-std/Test.sol";
5
+ import "@bananapus/core-v6/test/helpers/TestBaseWorkflow.sol";
6
+ import "@bananapus/core-v6/script/helpers/CoreDeploymentLib.sol";
7
+ import "@bananapus/721-hook-v6/script/helpers/Hook721DeploymentLib.sol";
8
+ import "@bananapus/suckers-v6/script/helpers/SuckerDeploymentLib.sol";
9
+ import "@croptop/core-v6/script/helpers/CroptopDeploymentLib.sol";
10
+ import "@bananapus/router-terminal-v6/script/helpers/RouterTerminalDeploymentLib.sol";
11
+ import "@croptop/core-v6/src/CTPublisher.sol";
12
+ import "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
13
+ import "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
14
+ import "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
15
+ import "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
16
+ import "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
17
+ import {IJB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721CheckpointsDeployer.sol";
18
+ import "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
19
+ import "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
20
+ import "@bananapus/buyback-hook-v6/src/interfaces/IJBBuybackHookRegistry.sol";
21
+ import "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
22
+ import "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
23
+ import "@bananapus/core-v6/src/interfaces/IJBPayoutTerminal.sol";
24
+ import "@bananapus/core-v6/src/libraries/JBCashOuts.sol";
25
+ import "@bananapus/core-v6/src/libraries/JBConstants.sol";
26
+ import "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
27
+ import "@bananapus/core-v6/src/structs/JBPermissionsData.sol";
28
+ import "@bananapus/core-v6/src/structs/JBTerminalConfig.sol";
29
+ import "@bananapus/core-v6/src/structs/JBSplit.sol";
30
+ import "@bananapus/core-v6/src/structs/JBRuleset.sol";
31
+ import "@bananapus/core-v6/src/structs/JBPayHookSpecification.sol";
32
+ import "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
33
+ import "@bananapus/suckers-v6/src/structs/JBSuckerDeployerConfig.sol";
34
+ import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
35
+
36
+ import {MockBuybackDataHook} from "../mock/MockBuybackDataHook.sol";
37
+ import {REVEmpty721Config} from "../helpers/REVEmpty721Config.sol";
38
+ import {REVDeployer} from "../../src/REVDeployer.sol";
39
+ import {REVLoans} from "../../src/REVLoans.sol";
40
+ import {REVOwner} from "../../src/REVOwner.sol";
41
+ import {IREVLoans} from "../../src/interfaces/IREVLoans.sol";
42
+ import {IREVDeployer} from "../../src/interfaces/IREVDeployer.sol";
43
+ import {REVConfig} from "../../src/structs/REVConfig.sol";
44
+ import {REVDescription} from "../../src/structs/REVDescription.sol";
45
+ import {REVLoanSource} from "../../src/structs/REVLoanSource.sol";
46
+ import {REVStageConfig} from "../../src/structs/REVStageConfig.sol";
47
+ import {REVAutoIssuance} from "../../src/structs/REVStageConfig.sol";
48
+ import {REVSuckerDeploymentConfig} from "../../src/structs/REVSuckerDeploymentConfig.sol";
49
+ import {MockSuckerRegistry} from "../mock/MockSuckerRegistry.sol";
50
+
51
+ contract PhantomSurplusTerminal is ERC165, IJBPayoutTerminal {
52
+ uint256 public fakeSurplus;
53
+
54
+ function setFakeSurplus(uint256 newFakeSurplus) external {
55
+ fakeSurplus = newFakeSurplus;
56
+ }
57
+
58
+ function currentSurplusOf(uint256, address[] calldata, uint256, uint256) external view override returns (uint256) {
59
+ return fakeSurplus;
60
+ }
61
+
62
+ function accountingContextForTokenOf(uint256, address) external pure override returns (JBAccountingContext memory) {
63
+ return JBAccountingContext({
64
+ token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
65
+ });
66
+ }
67
+
68
+ function accountingContextsOf(uint256) external pure override returns (JBAccountingContext[] memory) {
69
+ return new JBAccountingContext[](0);
70
+ }
71
+
72
+ function addAccountingContextsFor(uint256, JBAccountingContext[] calldata) external override {}
73
+
74
+ function addToBalanceOf(
75
+ uint256,
76
+ address,
77
+ uint256,
78
+ bool,
79
+ string calldata,
80
+ bytes calldata
81
+ )
82
+ external
83
+ payable
84
+ override
85
+ {}
86
+
87
+ function migrateBalanceOf(uint256, address, IJBTerminal) external pure override returns (uint256) {
88
+ return 0;
89
+ }
90
+
91
+ function pay(
92
+ uint256,
93
+ address,
94
+ uint256,
95
+ address,
96
+ uint256,
97
+ string calldata,
98
+ bytes calldata
99
+ )
100
+ external
101
+ payable
102
+ override
103
+ returns (uint256)
104
+ {
105
+ return 0;
106
+ }
107
+
108
+ function previewPayFor(
109
+ uint256,
110
+ address,
111
+ uint256,
112
+ address,
113
+ bytes calldata
114
+ )
115
+ external
116
+ pure
117
+ override
118
+ returns (JBRuleset memory, uint256, uint256, JBPayHookSpecification[] memory)
119
+ {
120
+ JBRuleset memory ruleset;
121
+ return (ruleset, 0, 0, new JBPayHookSpecification[](0));
122
+ }
123
+
124
+ function sendPayoutsOf(uint256, address, uint256, uint256, uint256) external pure override returns (uint256) {
125
+ return 0;
126
+ }
127
+
128
+ function supportsInterface(bytes4 interfaceId) public view override(ERC165, IERC165) returns (bool) {
129
+ return interfaceId == type(IJBTerminal).interfaceId || interfaceId == type(IJBPayoutTerminal).interfaceId
130
+ || super.supportsInterface(interfaceId);
131
+ }
132
+
133
+ function useAllowanceOf(
134
+ uint256,
135
+ address,
136
+ uint256,
137
+ uint256,
138
+ uint256,
139
+ address payable,
140
+ address payable,
141
+ string calldata
142
+ )
143
+ external
144
+ pure
145
+ override
146
+ returns (uint256)
147
+ {
148
+ return 0;
149
+ }
150
+ }
151
+
152
+ contract CodexPhantomSurplusTerminalTest is TestBaseWorkflow {
153
+ bytes32 internal constant REV_DEPLOYER_SALT = "REVDeployer";
154
+ address internal constant TRUSTED_FORWARDER = 0xB2b5841DBeF766d4b521221732F9B618fCf34A87;
155
+
156
+ address internal USER = makeAddr("user");
157
+
158
+ REVDeployer internal REV_DEPLOYER;
159
+ REVOwner internal REV_OWNER;
160
+ REVLoans internal LOANS;
161
+ JB721TiersHook internal EXAMPLE_HOOK;
162
+ IJB721TiersHookDeployer internal HOOK_DEPLOYER;
163
+ IJB721TiersHookStore internal HOOK_STORE;
164
+ IJBAddressRegistry internal ADDRESS_REGISTRY;
165
+ IJBSuckerRegistry internal SUCKER_REGISTRY;
166
+ CTPublisher internal PUBLISHER;
167
+ MockBuybackDataHook internal MOCK_BUYBACK;
168
+ PhantomSurplusTerminal internal PHANTOM_TERMINAL;
169
+
170
+ uint256 internal FEE_PROJECT_ID;
171
+ uint256 internal REVNET_ID;
172
+
173
+ function setUp() public override {
174
+ super.setUp();
175
+
176
+ FEE_PROJECT_ID = jbProjects().createFor(multisig());
177
+ SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
178
+ HOOK_STORE = new JB721TiersHookStore();
179
+ EXAMPLE_HOOK = new JB721TiersHook(
180
+ jbDirectory(),
181
+ jbPermissions(),
182
+ jbPrices(),
183
+ jbRulesets(),
184
+ HOOK_STORE,
185
+ jbSplits(),
186
+ IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
187
+ multisig()
188
+ );
189
+ ADDRESS_REGISTRY = new JBAddressRegistry();
190
+ HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
191
+ PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
192
+ MOCK_BUYBACK = new MockBuybackDataHook();
193
+ LOANS = new REVLoans({
194
+ controller: jbController(),
195
+ suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
196
+ revId: FEE_PROJECT_ID,
197
+ owner: address(this),
198
+ permit2: permit2(),
199
+ trustedForwarder: TRUSTED_FORWARDER
200
+ });
201
+ REV_OWNER = new REVOwner(
202
+ IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
203
+ jbDirectory(),
204
+ FEE_PROJECT_ID,
205
+ SUCKER_REGISTRY,
206
+ address(LOANS),
207
+ address(0)
208
+ );
209
+ REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
210
+ jbController(),
211
+ SUCKER_REGISTRY,
212
+ FEE_PROJECT_ID,
213
+ HOOK_DEPLOYER,
214
+ PUBLISHER,
215
+ IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
216
+ address(LOANS),
217
+ TRUSTED_FORWARDER,
218
+ address(REV_OWNER)
219
+ );
220
+ REV_OWNER.setDeployer(IREVDeployer(REV_DEPLOYER));
221
+ PHANTOM_TERMINAL = new PhantomSurplusTerminal();
222
+
223
+ vm.prank(multisig());
224
+ jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
225
+
226
+ _deployFeeProject();
227
+ REVNET_ID = _deployRevnetWithPhantomTerminal();
228
+ vm.deal(USER, 200 ether);
229
+ }
230
+
231
+ function test_registeredPhantomTerminalInflatesBorrowAgainstRealTreasury() public {
232
+ uint256 realSurplus = 100 ether;
233
+ uint256 phantomSurplus = 100 ether;
234
+
235
+ PHANTOM_TERMINAL.setFakeSurplus(phantomSurplus);
236
+
237
+ vm.prank(USER);
238
+ uint256 userTokens = jbMultiTerminal().pay{value: realSurplus}(
239
+ REVNET_ID, JBConstants.NATIVE_TOKEN, realSurplus, USER, 0, "", ""
240
+ );
241
+
242
+ uint256 collateral = userTokens / 10;
243
+ uint256 taxRate = 5000;
244
+
245
+ uint256 honestBorrowable = JBCashOuts.cashOutFrom({
246
+ surplus: realSurplus, cashOutCount: collateral, totalSupply: userTokens, cashOutTaxRate: taxRate
247
+ });
248
+
249
+ uint256 inflatedBorrowable =
250
+ LOANS.borrowableAmountFrom(REVNET_ID, collateral, 18, uint32(uint160(JBConstants.NATIVE_TOKEN)));
251
+
252
+ assertGt(inflatedBorrowable, honestBorrowable, "phantom terminal should inflate borrow quote");
253
+ assertLe(inflatedBorrowable, realSurplus, "PoC should remain payable by the honest terminal");
254
+
255
+ _grantBurnPermission(USER, REVNET_ID, address(LOANS));
256
+
257
+ uint256 balanceBefore = USER.balance;
258
+ vm.prank(USER);
259
+ LOANS.borrowFrom(
260
+ REVNET_ID,
261
+ REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()}),
262
+ inflatedBorrowable,
263
+ collateral,
264
+ payable(USER),
265
+ 25,
266
+ USER
267
+ );
268
+
269
+ uint256 balanceDelta = USER.balance - balanceBefore;
270
+ assertGt(balanceDelta, honestBorrowable, "borrower extracts more ETH than real treasury surplus supports");
271
+ }
272
+
273
+ function _deployFeeProject() internal {
274
+ JBAccountingContext[] memory acc = new JBAccountingContext[](1);
275
+ acc[0] = JBAccountingContext({
276
+ token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
277
+ });
278
+ JBTerminalConfig[] memory tc = new JBTerminalConfig[](1);
279
+ tc[0] = JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: acc});
280
+ REVStageConfig[] memory stages = new REVStageConfig[](1);
281
+ stages[0] = REVStageConfig({
282
+ startsAtOrAfter: uint40(block.timestamp),
283
+ autoIssuances: new REVAutoIssuance[](0),
284
+ splitPercent: 0,
285
+ splits: new JBSplit[](0),
286
+ initialIssuance: uint112(1000e18),
287
+ issuanceCutFrequency: 0,
288
+ issuanceCutPercent: 0,
289
+ cashOutTaxRate: 0,
290
+ extraMetadata: 0
291
+ });
292
+ REVConfig memory feeConfig = REVConfig({
293
+ description: REVDescription("Fee Revnet", "FEE", "", "FEE_TOKEN"),
294
+ baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
295
+ splitOperator: multisig(),
296
+ stageConfigurations: stages
297
+ });
298
+ vm.prank(multisig());
299
+ REV_DEPLOYER.deployFor({
300
+ revnetId: FEE_PROJECT_ID,
301
+ configuration: feeConfig,
302
+ terminalConfigurations: tc,
303
+ suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
304
+ deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("FEE")
305
+ }),
306
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
307
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
308
+ });
309
+ }
310
+
311
+ function _deployRevnetWithPhantomTerminal() internal returns (uint256 revnetId) {
312
+ JBAccountingContext[] memory acc = new JBAccountingContext[](1);
313
+ acc[0] = JBAccountingContext({
314
+ token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
315
+ });
316
+ JBTerminalConfig[] memory tc = new JBTerminalConfig[](2);
317
+ tc[0] = JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: acc});
318
+ tc[1] = JBTerminalConfig({terminal: PHANTOM_TERMINAL, accountingContextsToAccept: acc});
319
+
320
+ REVStageConfig[] memory stages = new REVStageConfig[](1);
321
+ JBSplit[] memory splits = new JBSplit[](1);
322
+ splits[0].beneficiary = payable(multisig());
323
+ splits[0].percent = 10_000;
324
+ stages[0] = REVStageConfig({
325
+ startsAtOrAfter: uint40(block.timestamp),
326
+ autoIssuances: new REVAutoIssuance[](0),
327
+ splitPercent: 0,
328
+ splits: splits,
329
+ initialIssuance: uint112(1000e18),
330
+ issuanceCutFrequency: 0,
331
+ issuanceCutPercent: 0,
332
+ cashOutTaxRate: 5000,
333
+ extraMetadata: 0
334
+ });
335
+
336
+ REVConfig memory config = REVConfig({
337
+ description: REVDescription("Phantom", "PHM", "", "PHM_TOKEN"),
338
+ baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
339
+ splitOperator: multisig(),
340
+ stageConfigurations: stages
341
+ });
342
+
343
+ vm.prank(multisig());
344
+ (revnetId,) = REV_DEPLOYER.deployFor({
345
+ revnetId: 0,
346
+ configuration: config,
347
+ terminalConfigurations: tc,
348
+ suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
349
+ deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("PHANTOM")
350
+ }),
351
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
352
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
353
+ });
354
+ }
355
+
356
+ function _grantBurnPermission(address account, uint256 revnetId, address operator) internal {
357
+ uint8[] memory permissionIds = new uint8[](1);
358
+ permissionIds[0] = JBPermissionIds.BURN_TOKENS;
359
+
360
+ vm.prank(account);
361
+ jbPermissions()
362
+ .setPermissionsFor(
363
+ account,
364
+ JBPermissionsData({operator: operator, projectId: uint56(revnetId), permissionIds: permissionIds})
365
+ );
366
+ }
367
+ }
@@ -38,12 +38,15 @@ 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
  // Helper that provides empty 721 tier configs for revnet deployment.
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
  /// @notice Regression tests for the loan ID overflow guard in REVLoans.
49
52
  /// @dev The totalLoansBorrowedFor counter must never exceed _ONE_TRILLION (1e12).
@@ -129,7 +132,14 @@ contract LoanIdOverflowGuard is TestBaseWorkflow {
129
132
 
130
133
  // Deploy the example 721 hook (needed as the implementation for the deployer).
131
134
  EXAMPLE_HOOK = new JB721TiersHook(
132
- jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
135
+ jbDirectory(),
136
+ jbPermissions(),
137
+ jbPrices(),
138
+ jbRulesets(),
139
+ HOOK_STORE,
140
+ jbSplits(),
141
+ IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
142
+ multisig()
133
143
  );
134
144
 
135
145
  // Deploy the address registry (used by the hook deployer).
@@ -155,7 +165,7 @@ contract LoanIdOverflowGuard is TestBaseWorkflow {
155
165
  // Deploy the REVLoans contract.
156
166
  LOANS_CONTRACT = new REVLoans({
157
167
  controller: jbController(),
158
- projects: jbProjects(),
168
+ suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
159
169
  revId: FEE_PROJECT_ID,
160
170
  owner: address(this),
161
171
  permit2: permit2(),
@@ -168,7 +178,8 @@ contract LoanIdOverflowGuard is TestBaseWorkflow {
168
178
  jbDirectory(),
169
179
  FEE_PROJECT_ID,
170
180
  SUCKER_REGISTRY,
171
- address(LOANS_CONTRACT)
181
+ address(LOANS_CONTRACT),
182
+ address(0)
172
183
  );
173
184
 
174
185
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
@@ -349,7 +360,7 @@ contract LoanIdOverflowGuard is TestBaseWorkflow {
349
360
 
350
361
  // Borrow with minimum fee percent (25 = 2.5%).
351
362
  vm.prank(user);
352
- (loanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(user), 25);
363
+ (loanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(user), 25, user);
353
364
  }
354
365
 
355
366
  /// @dev Computes the storage slot for totalLoansBorrowedFor[revnetId].
@@ -401,7 +412,7 @@ contract LoanIdOverflowGuard is TestBaseWorkflow {
401
412
 
402
413
  // Attempt to borrow -- should revert because the counter is at the limit.
403
414
  vm.prank(USER);
404
- LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokens, payable(USER), 25);
415
+ LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokens, payable(USER), 25, USER);
405
416
  }
406
417
 
407
418
  // ---------------------------------------------------------------