@rev-net/core-v6 0.0.18 → 0.0.19

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 (64) hide show
  1. package/ADMINISTRATION.md +14 -4
  2. package/ARCHITECTURE.md +13 -10
  3. package/AUDIT_INSTRUCTIONS.md +39 -18
  4. package/CHANGE_LOG.md +79 -1
  5. package/README.md +10 -5
  6. package/RISKS.md +12 -11
  7. package/SKILLS.md +27 -12
  8. package/USER_JOURNEYS.md +15 -14
  9. package/foundry.toml +1 -1
  10. package/package.json +1 -1
  11. package/script/Deploy.s.sol +42 -4
  12. package/src/REVDeployer.sol +20 -305
  13. package/src/REVLoans.sol +24 -29
  14. package/src/REVOwner.sol +430 -0
  15. package/src/interfaces/IREVDeployer.sol +4 -10
  16. package/src/interfaces/IREVOwner.sol +10 -0
  17. package/test/REV.integrations.t.sol +14 -1
  18. package/test/REVAutoIssuanceFuzz.t.sol +14 -1
  19. package/test/REVDeployerRegressions.t.sol +17 -2
  20. package/test/REVInvincibility.t.sol +31 -3
  21. package/test/REVLifecycle.t.sol +16 -1
  22. package/test/REVLoans.invariants.t.sol +16 -1
  23. package/test/REVLoansAttacks.t.sol +16 -1
  24. package/test/REVLoansFeeRecovery.t.sol +16 -1
  25. package/test/REVLoansFindings.t.sol +16 -1
  26. package/test/REVLoansRegressions.t.sol +16 -1
  27. package/test/REVLoansSourceFeeRecovery.t.sol +16 -1
  28. package/test/REVLoansSourced.t.sol +16 -1
  29. package/test/REVLoansUnSourced.t.sol +16 -1
  30. package/test/TestBurnHeldTokens.t.sol +16 -1
  31. package/test/TestCEIPattern.t.sol +16 -1
  32. package/test/TestCashOutCallerValidation.t.sol +19 -4
  33. package/test/TestConversionDocumentation.t.sol +16 -1
  34. package/test/TestCrossCurrencyReclaim.t.sol +16 -1
  35. package/test/TestCrossSourceReallocation.t.sol +16 -1
  36. package/test/TestERC2771MetaTx.t.sol +16 -1
  37. package/test/TestEmptyBuybackSpecs.t.sol +18 -3
  38. package/test/TestFlashLoanSurplus.t.sol +16 -1
  39. package/test/TestHookArrayOOB.t.sol +17 -2
  40. package/test/TestLiquidationBehavior.t.sol +16 -1
  41. package/test/TestLoanSourceRotation.t.sol +16 -1
  42. package/test/TestLoansCashOutDelay.t.sol +21 -6
  43. package/test/TestLongTailEconomics.t.sol +16 -1
  44. package/test/TestLowFindings.t.sol +16 -1
  45. package/test/TestMixedFixes.t.sol +16 -1
  46. package/test/TestPermit2Signatures.t.sol +16 -1
  47. package/test/TestReallocationSandwich.t.sol +16 -1
  48. package/test/TestRevnetRegressions.t.sol +16 -1
  49. package/test/TestSplitWeightAdjustment.t.sol +43 -19
  50. package/test/TestSplitWeightE2E.t.sol +26 -2
  51. package/test/TestSplitWeightFork.t.sol +16 -1
  52. package/test/TestStageTransitionBorrowable.t.sol +16 -1
  53. package/test/TestSwapTerminalPermission.t.sol +16 -1
  54. package/test/TestUint112Overflow.t.sol +16 -1
  55. package/test/TestZeroRepayment.t.sol +16 -1
  56. package/test/audit/LoanIdOverflowGuard.t.sol +16 -1
  57. package/test/fork/ForkTestBase.sol +16 -1
  58. package/test/fork/TestPermit2PaymentFork.t.sol +4 -3
  59. package/test/regression/TestBurnPermissionRequired.t.sol +16 -1
  60. package/test/regression/TestCashOutBuybackFeeLeak.t.sol +15 -1
  61. package/test/regression/TestCrossRevnetLiquidation.t.sol +16 -1
  62. package/test/regression/TestCumulativeLoanCounter.t.sol +16 -1
  63. package/test/regression/TestLiquidateGapHandling.t.sol +16 -1
  64. package/test/regression/TestZeroPriceFeed.t.sol +16 -1
@@ -42,6 +42,8 @@ import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStor
42
42
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
43
43
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
44
44
  import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
45
+ import {REVOwner} from "../src/REVOwner.sol";
46
+ import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
45
47
 
46
48
  struct FeeProjectConfig {
47
49
  REVConfig configuration;
@@ -58,6 +60,8 @@ contract TestCashOutCallerValidation is TestBaseWorkflow {
58
60
  // forge-lint: disable-next-line(mixed-case-variable)
59
61
  REVDeployer REV_DEPLOYER;
60
62
  // forge-lint: disable-next-line(mixed-case-variable)
63
+ REVOwner REV_OWNER;
64
+ // forge-lint: disable-next-line(mixed-case-variable)
61
65
  JB721TiersHook EXAMPLE_HOOK;
62
66
  // forge-lint: disable-next-line(mixed-case-variable)
63
67
  IJB721TiersHookDeployer HOOK_DEPLOYER;
@@ -223,6 +227,14 @@ contract TestCashOutCallerValidation is TestBaseWorkflow {
223
227
  trustedForwarder: TRUSTED_FORWARDER
224
228
  });
225
229
 
230
+ REV_OWNER = new REVOwner(
231
+ IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
232
+ jbDirectory(),
233
+ FEE_PROJECT_ID,
234
+ SUCKER_REGISTRY,
235
+ address(LOANS_CONTRACT)
236
+ );
237
+
226
238
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
227
239
  jbController(),
228
240
  SUCKER_REGISTRY,
@@ -231,9 +243,12 @@ contract TestCashOutCallerValidation is TestBaseWorkflow {
231
243
  PUBLISHER,
232
244
  IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
233
245
  address(LOANS_CONTRACT),
234
- TRUSTED_FORWARDER
246
+ TRUSTED_FORWARDER,
247
+ address(REV_OWNER)
235
248
  );
236
249
 
250
+ REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
251
+
237
252
  // Approve the deployer to configure the fee project.
238
253
  vm.prank(multisig());
239
254
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
@@ -341,7 +356,7 @@ contract TestCashOutCallerValidation is TestBaseWorkflow {
341
356
  uint256 cashOutCount,
342
357
  uint256 totalSupply,
343
358
  JBCashOutHookSpecification[] memory hookSpecifications
344
- ) = REV_DEPLOYER.beforeCashOutRecordedWith(context);
359
+ ) = REV_OWNER.beforeCashOutRecordedWith(context);
345
360
 
346
361
  uint256 feeCashOutCount = context.cashOutCount * REV_DEPLOYER.FEE() / JBConstants.MAX_FEE;
347
362
  uint256 nonFeeCashOutCount = context.cashOutCount - feeCashOutCount;
@@ -367,7 +382,7 @@ contract TestCashOutCallerValidation is TestBaseWorkflow {
367
382
  assertEq(hookSpecifications[0].amount, 0, "Buyback sell-side spec should preserve its forwarded amount");
368
383
  assertEq(hookSpecifications[0].metadata, buybackMetadata, "Buyback metadata should be preserved");
369
384
 
370
- assertEq(address(hookSpecifications[1].hook), address(REV_DEPLOYER), "Second hook spec should charge fee");
385
+ assertEq(address(hookSpecifications[1].hook), address(REV_OWNER), "Second hook spec should charge fee");
371
386
  assertEq(hookSpecifications[1].amount, feeAmount, "Fee spec amount should match the revnet fee math");
372
387
  assertEq(
373
388
  hookSpecifications[1].metadata,
@@ -416,7 +431,7 @@ contract TestCashOutCallerValidation is TestBaseWorkflow {
416
431
  // This should NOT revert with any authorization error.
417
432
  // The caller sends ETH which gets paid as a fee to the fee project.
418
433
  vm.prank(RANDOM_CALLER);
419
- REV_DEPLOYER.afterCashOutRecordedWith{value: 1 ether}(context);
434
+ REV_OWNER.afterCashOutRecordedWith{value: 1 ether}(context);
420
435
 
421
436
  // Verify the fee project received the ETH (donation).
422
437
  uint256 feeBalanceAfter =
@@ -37,6 +37,8 @@ import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
37
37
  import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
38
38
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
39
39
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
40
+ import {REVOwner} from "../src/REVOwner.sol";
41
+ import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
40
42
 
41
43
  contract TestConversionDocumentation is TestBaseWorkflow {
42
44
  // forge-lint: disable-next-line(mixed-case-variable)
@@ -47,6 +49,8 @@ contract TestConversionDocumentation is TestBaseWorkflow {
47
49
  // forge-lint: disable-next-line(mixed-case-variable)
48
50
  REVDeployer REV_DEPLOYER;
49
51
  // forge-lint: disable-next-line(mixed-case-variable)
52
+ REVOwner REV_OWNER;
53
+ // forge-lint: disable-next-line(mixed-case-variable)
50
54
  JB721TiersHook EXAMPLE_HOOK;
51
55
  // forge-lint: disable-next-line(mixed-case-variable)
52
56
  IJB721TiersHookDeployer HOOK_DEPLOYER;
@@ -153,6 +157,14 @@ contract TestConversionDocumentation is TestBaseWorkflow {
153
157
  trustedForwarder: TRUSTED_FORWARDER
154
158
  });
155
159
 
160
+ REV_OWNER = new REVOwner(
161
+ IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
162
+ jbDirectory(),
163
+ FEE_PROJECT_ID,
164
+ SUCKER_REGISTRY,
165
+ address(LOANS_CONTRACT)
166
+ );
167
+
156
168
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
157
169
  jbController(),
158
170
  SUCKER_REGISTRY,
@@ -161,9 +173,12 @@ contract TestConversionDocumentation is TestBaseWorkflow {
161
173
  PUBLISHER,
162
174
  IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
163
175
  address(LOANS_CONTRACT),
164
- TRUSTED_FORWARDER
176
+ TRUSTED_FORWARDER,
177
+ address(REV_OWNER)
165
178
  );
166
179
 
180
+ REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
181
+
167
182
  // Deploy fee project as revnet.
168
183
  vm.prank(multisig());
169
184
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
@@ -37,6 +37,8 @@ import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStor
37
37
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
38
38
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
39
39
  import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
40
+ import {REVOwner} from "../src/REVOwner.sol";
41
+ import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
40
42
 
41
43
  /// @notice Cross-currency reclaim tests: verify cash-out behavior when a revnet's baseCurrency differs from the
42
44
  /// terminal token currency, and when price feeds return various values.
@@ -47,6 +49,8 @@ contract TestCrossCurrencyReclaim is TestBaseWorkflow {
47
49
  // forge-lint: disable-next-line(mixed-case-variable)
48
50
  REVDeployer REV_DEPLOYER;
49
51
  // forge-lint: disable-next-line(mixed-case-variable)
52
+ REVOwner REV_OWNER;
53
+ // forge-lint: disable-next-line(mixed-case-variable)
50
54
  JB721TiersHook EXAMPLE_HOOK;
51
55
  // forge-lint: disable-next-line(mixed-case-variable)
52
56
  IJB721TiersHookDeployer HOOK_DEPLOYER;
@@ -104,6 +108,14 @@ contract TestCrossCurrencyReclaim is TestBaseWorkflow {
104
108
  trustedForwarder: TRUSTED_FORWARDER
105
109
  });
106
110
 
111
+ REV_OWNER = new REVOwner(
112
+ IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
113
+ jbDirectory(),
114
+ FEE_PROJECT_ID,
115
+ SUCKER_REGISTRY,
116
+ address(LOANS_CONTRACT)
117
+ );
118
+
107
119
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
108
120
  jbController(),
109
121
  SUCKER_REGISTRY,
@@ -112,9 +124,12 @@ contract TestCrossCurrencyReclaim is TestBaseWorkflow {
112
124
  PUBLISHER,
113
125
  IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
114
126
  address(LOANS_CONTRACT),
115
- TRUSTED_FORWARDER
127
+ TRUSTED_FORWARDER,
128
+ address(REV_OWNER)
116
129
  );
117
130
 
131
+ REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
132
+
118
133
  vm.prank(multisig());
119
134
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
120
135
 
@@ -39,6 +39,8 @@ import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStor
39
39
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
40
40
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
41
41
  import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
42
+ import {REVOwner} from "../src/REVOwner.sol";
43
+ import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
42
44
 
43
45
  /// @notice Tests for PR #13: cross-source reallocation prevention.
44
46
  contract TestCrossSourceReallocation is TestBaseWorkflow {
@@ -48,6 +50,8 @@ contract TestCrossSourceReallocation is TestBaseWorkflow {
48
50
  // forge-lint: disable-next-line(mixed-case-variable)
49
51
  REVDeployer REV_DEPLOYER;
50
52
  // forge-lint: disable-next-line(mixed-case-variable)
53
+ REVOwner REV_OWNER;
54
+ // forge-lint: disable-next-line(mixed-case-variable)
51
55
  JB721TiersHook EXAMPLE_HOOK;
52
56
  // forge-lint: disable-next-line(mixed-case-variable)
53
57
  IJB721TiersHookDeployer HOOK_DEPLOYER;
@@ -101,6 +105,14 @@ contract TestCrossSourceReallocation is TestBaseWorkflow {
101
105
  permit2: permit2(),
102
106
  trustedForwarder: TRUSTED_FORWARDER
103
107
  });
108
+ REV_OWNER = new REVOwner(
109
+ IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
110
+ jbDirectory(),
111
+ FEE_PROJECT_ID,
112
+ SUCKER_REGISTRY,
113
+ address(LOANS_CONTRACT)
114
+ );
115
+
104
116
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
105
117
  jbController(),
106
118
  SUCKER_REGISTRY,
@@ -109,8 +121,11 @@ contract TestCrossSourceReallocation is TestBaseWorkflow {
109
121
  PUBLISHER,
110
122
  IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
111
123
  address(LOANS_CONTRACT),
112
- TRUSTED_FORWARDER
124
+ TRUSTED_FORWARDER,
125
+ address(REV_OWNER)
113
126
  );
127
+
128
+ REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
114
129
  vm.prank(multisig());
115
130
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
116
131
  _deployFeeProject();
@@ -43,6 +43,8 @@ import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/
43
43
  import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
44
44
  import {ERC2771Forwarder} from "@openzeppelin/contracts/metatx/ERC2771Forwarder.sol";
45
45
  import {ERC2771ForwarderMock, ForwardRequest} from "@bananapus/core-v6/test/mock/ERC2771ForwarderMock.sol";
46
+ import {REVOwner} from "../src/REVOwner.sol";
47
+ import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
46
48
 
47
49
  struct MetaTxProjectConfig {
48
50
  REVConfig configuration;
@@ -63,6 +65,8 @@ contract TestERC2771MetaTx is TestBaseWorkflow {
63
65
  // forge-lint: disable-next-line(mixed-case-variable)
64
66
  REVDeployer REV_DEPLOYER;
65
67
  // forge-lint: disable-next-line(mixed-case-variable)
68
+ REVOwner REV_OWNER;
69
+ // forge-lint: disable-next-line(mixed-case-variable)
66
70
  JB721TiersHook EXAMPLE_HOOK;
67
71
  // forge-lint: disable-next-line(mixed-case-variable)
68
72
  IJB721TiersHookDeployer HOOK_DEPLOYER;
@@ -294,6 +298,14 @@ contract TestERC2771MetaTx is TestBaseWorkflow {
294
298
  trustedForwarder: FORWARDER_ADDRESS
295
299
  });
296
300
 
301
+ REV_OWNER = new REVOwner(
302
+ IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
303
+ jbDirectory(),
304
+ FEE_PROJECT_ID,
305
+ SUCKER_REGISTRY,
306
+ address(LOANS_CONTRACT)
307
+ );
308
+
297
309
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
298
310
  jbController(),
299
311
  SUCKER_REGISTRY,
@@ -302,9 +314,12 @@ contract TestERC2771MetaTx is TestBaseWorkflow {
302
314
  PUBLISHER,
303
315
  IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
304
316
  address(LOANS_CONTRACT),
305
- FORWARDER_ADDRESS
317
+ FORWARDER_ADDRESS,
318
+ address(REV_OWNER)
306
319
  );
307
320
 
321
+ REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
322
+
308
323
  // Approve the deployer to configure the project.
309
324
  vm.prank(address(multisig()));
310
325
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
@@ -37,6 +37,8 @@ import {JBBeforePayRecordedContext} from "@bananapus/core-v6/src/structs/JBBefor
37
37
  import {JBPayHookSpecification} from "@bananapus/core-v6/src/structs/JBPayHookSpecification.sol";
38
38
  import {JBTokenAmount} from "@bananapus/core-v6/src/structs/JBTokenAmount.sol";
39
39
  import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
40
+ import {REVOwner} from "../src/REVOwner.sol";
41
+ import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
40
42
 
41
43
  /// @notice Regression tests for the empty buyback hook specifications fix.
42
44
  /// When JBBuybackHook determines minting is cheaper than swapping, it returns an empty
@@ -49,6 +51,8 @@ contract TestEmptyBuybackSpecs is TestBaseWorkflow {
49
51
  // forge-lint: disable-next-line(mixed-case-variable)
50
52
  REVDeployer REV_DEPLOYER;
51
53
  // forge-lint: disable-next-line(mixed-case-variable)
54
+ REVOwner REV_OWNER;
55
+ // forge-lint: disable-next-line(mixed-case-variable)
52
56
  JB721TiersHook EXAMPLE_HOOK;
53
57
  // forge-lint: disable-next-line(mixed-case-variable)
54
58
  IJB721TiersHookDeployer HOOK_DEPLOYER;
@@ -92,6 +96,14 @@ contract TestEmptyBuybackSpecs is TestBaseWorkflow {
92
96
  permit2: permit2(),
93
97
  trustedForwarder: TRUSTED_FORWARDER
94
98
  });
99
+ REV_OWNER = new REVOwner(
100
+ IJBBuybackHookRegistry(address(MOCK_BUYBACK_MINT_PATH)),
101
+ jbDirectory(),
102
+ FEE_PROJECT_ID,
103
+ SUCKER_REGISTRY,
104
+ address(LOANS_CONTRACT)
105
+ );
106
+
95
107
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
96
108
  jbController(),
97
109
  SUCKER_REGISTRY,
@@ -100,8 +112,11 @@ contract TestEmptyBuybackSpecs is TestBaseWorkflow {
100
112
  PUBLISHER,
101
113
  IJBBuybackHookRegistry(address(MOCK_BUYBACK_MINT_PATH)),
102
114
  address(LOANS_CONTRACT),
103
- TRUSTED_FORWARDER
115
+ TRUSTED_FORWARDER,
116
+ address(REV_OWNER)
104
117
  );
118
+
119
+ REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
105
120
  vm.prank(multisig());
106
121
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
107
122
  }
@@ -260,13 +275,13 @@ contract TestEmptyBuybackSpecs is TestBaseWorkflow {
260
275
  metadata: ""
261
276
  });
262
277
 
263
- (uint256 weight, JBPayHookSpecification[] memory specs) = REV_DEPLOYER.beforePayRecordedWith(context);
278
+ (uint256 weight, JBPayHookSpecification[] memory specs) = REV_OWNER.beforePayRecordedWith(context);
264
279
 
265
280
  assertEq(weight, context.weight, "Weight should pass through from buyback hook");
266
281
  assertEq(specs.length, 1, "Should return only the 721 hook spec when buyback hook returns empty");
267
282
  assertEq(
268
283
  address(specs[0].hook),
269
- address(REV_DEPLOYER.tiered721HookOf(revnetId)),
284
+ address(REV_OWNER.tiered721HookOf(revnetId)),
270
285
  "Spec hook should be the revnet's 721 hook"
271
286
  );
272
287
  }
@@ -38,6 +38,8 @@ import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
38
38
  import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
39
39
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
40
40
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
41
+ import {REVOwner} from "../src/REVOwner.sol";
42
+ import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
41
43
 
42
44
  /// @notice Tests showing that flash loan surplus manipulation is economically unprofitable.
43
45
  contract TestFlashLoanSurplus is TestBaseWorkflow {
@@ -49,6 +51,8 @@ contract TestFlashLoanSurplus is TestBaseWorkflow {
49
51
  // forge-lint: disable-next-line(mixed-case-variable)
50
52
  REVDeployer REV_DEPLOYER;
51
53
  // forge-lint: disable-next-line(mixed-case-variable)
54
+ REVOwner REV_OWNER;
55
+ // forge-lint: disable-next-line(mixed-case-variable)
52
56
  JB721TiersHook EXAMPLE_HOOK;
53
57
  // forge-lint: disable-next-line(mixed-case-variable)
54
58
  IJB721TiersHookDeployer HOOK_DEPLOYER;
@@ -104,6 +108,14 @@ contract TestFlashLoanSurplus is TestBaseWorkflow {
104
108
  permit2: permit2(),
105
109
  trustedForwarder: TRUSTED_FORWARDER
106
110
  });
111
+ REV_OWNER = new REVOwner(
112
+ IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
113
+ jbDirectory(),
114
+ FEE_PROJECT_ID,
115
+ SUCKER_REGISTRY,
116
+ address(LOANS_CONTRACT)
117
+ );
118
+
107
119
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
108
120
  jbController(),
109
121
  SUCKER_REGISTRY,
@@ -112,8 +124,11 @@ contract TestFlashLoanSurplus is TestBaseWorkflow {
112
124
  PUBLISHER,
113
125
  IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
114
126
  address(LOANS_CONTRACT),
115
- TRUSTED_FORWARDER
127
+ TRUSTED_FORWARDER,
128
+ address(REV_OWNER)
116
129
  );
130
+
131
+ REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
117
132
  vm.prank(multisig());
118
133
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
119
134
  _deployFeeProject();
@@ -38,6 +38,8 @@ import {JBBeforePayRecordedContext} from "@bananapus/core-v6/src/structs/JBBefor
38
38
  import {JBPayHookSpecification} from "@bananapus/core-v6/src/structs/JBPayHookSpecification.sol";
39
39
  import {JBTokenAmount} from "@bananapus/core-v6/src/structs/JBTokenAmount.sol";
40
40
  import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
41
+ import {REVOwner} from "../src/REVOwner.sol";
42
+ import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
41
43
 
42
44
  /// @notice Tests for PR #22: fix/c2-hook-array-oob
43
45
  /// Verifies that the fix for the hook array out-of-bounds bug works correctly.
@@ -51,6 +53,8 @@ contract TestHookArrayOOB is TestBaseWorkflow {
51
53
  // forge-lint: disable-next-line(mixed-case-variable)
52
54
  REVDeployer REV_DEPLOYER;
53
55
  // forge-lint: disable-next-line(mixed-case-variable)
56
+ REVOwner REV_OWNER;
57
+ // forge-lint: disable-next-line(mixed-case-variable)
54
58
  JB721TiersHook EXAMPLE_HOOK;
55
59
  // forge-lint: disable-next-line(mixed-case-variable)
56
60
  IJB721TiersHookDeployer HOOK_DEPLOYER;
@@ -94,6 +98,14 @@ contract TestHookArrayOOB is TestBaseWorkflow {
94
98
  permit2: permit2(),
95
99
  trustedForwarder: TRUSTED_FORWARDER
96
100
  });
101
+ REV_OWNER = new REVOwner(
102
+ IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
103
+ jbDirectory(),
104
+ FEE_PROJECT_ID,
105
+ SUCKER_REGISTRY,
106
+ address(LOANS_CONTRACT)
107
+ );
108
+
97
109
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
98
110
  jbController(),
99
111
  SUCKER_REGISTRY,
@@ -102,8 +114,11 @@ contract TestHookArrayOOB is TestBaseWorkflow {
102
114
  PUBLISHER,
103
115
  IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
104
116
  address(LOANS_CONTRACT),
105
- TRUSTED_FORWARDER
117
+ TRUSTED_FORWARDER,
118
+ address(REV_OWNER)
106
119
  );
120
+
121
+ REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
107
122
  vm.prank(multisig());
108
123
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
109
124
  }
@@ -242,7 +257,7 @@ contract TestHookArrayOOB is TestBaseWorkflow {
242
257
  metadata: ""
243
258
  });
244
259
 
245
- (uint256 weight, JBPayHookSpecification[] memory specs) = REV_DEPLOYER.beforePayRecordedWith(context);
260
+ (uint256 weight, JBPayHookSpecification[] memory specs) = REV_OWNER.beforePayRecordedWith(context);
246
261
 
247
262
  // Every revnet has both the buyback hook and the 721 hook.
248
263
  assertEq(weight, context.weight, "Weight should be the default context weight");
@@ -40,6 +40,8 @@ import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStor
40
40
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
41
41
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
42
42
  import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
43
+ import {REVOwner} from "../src/REVOwner.sol";
44
+ import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
43
45
 
44
46
  /// @notice Tests for PR #10: liquidation behavior documentation and collateral burn mechanics.
45
47
  contract TestLiquidationBehavior is TestBaseWorkflow {
@@ -49,6 +51,8 @@ contract TestLiquidationBehavior is TestBaseWorkflow {
49
51
  // forge-lint: disable-next-line(mixed-case-variable)
50
52
  REVDeployer REV_DEPLOYER;
51
53
  // forge-lint: disable-next-line(mixed-case-variable)
54
+ REVOwner REV_OWNER;
55
+ // forge-lint: disable-next-line(mixed-case-variable)
52
56
  JB721TiersHook EXAMPLE_HOOK;
53
57
  // forge-lint: disable-next-line(mixed-case-variable)
54
58
  IJB721TiersHookDeployer HOOK_DEPLOYER;
@@ -102,6 +106,14 @@ contract TestLiquidationBehavior is TestBaseWorkflow {
102
106
  permit2: permit2(),
103
107
  trustedForwarder: TRUSTED_FORWARDER
104
108
  });
109
+ REV_OWNER = new REVOwner(
110
+ IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
111
+ jbDirectory(),
112
+ FEE_PROJECT_ID,
113
+ SUCKER_REGISTRY,
114
+ address(LOANS_CONTRACT)
115
+ );
116
+
105
117
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
106
118
  jbController(),
107
119
  SUCKER_REGISTRY,
@@ -110,8 +122,11 @@ contract TestLiquidationBehavior is TestBaseWorkflow {
110
122
  PUBLISHER,
111
123
  IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
112
124
  address(LOANS_CONTRACT),
113
- TRUSTED_FORWARDER
125
+ TRUSTED_FORWARDER,
126
+ address(REV_OWNER)
114
127
  );
128
+
129
+ REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
115
130
  vm.prank(multisig());
116
131
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
117
132
  _deployFeeProject();
@@ -40,6 +40,8 @@ import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressReg
40
40
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
41
41
  import {JBSingleAllowance} from "@bananapus/core-v6/src/structs/JBSingleAllowance.sol";
42
42
  import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
43
+ import {REVOwner} from "../src/REVOwner.sol";
44
+ import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
43
45
 
44
46
  /// @notice Tests for loan source rotation: verify behavior when loans are taken from different sources (tokens)
45
47
  /// and that existing loans remain valid and repayable after new sources are introduced.
@@ -50,6 +52,8 @@ contract TestLoanSourceRotation is TestBaseWorkflow {
50
52
  // forge-lint: disable-next-line(mixed-case-variable)
51
53
  REVDeployer REV_DEPLOYER;
52
54
  // forge-lint: disable-next-line(mixed-case-variable)
55
+ REVOwner REV_OWNER;
56
+ // forge-lint: disable-next-line(mixed-case-variable)
53
57
  JB721TiersHook EXAMPLE_HOOK;
54
58
  // forge-lint: disable-next-line(mixed-case-variable)
55
59
  IJB721TiersHookDeployer HOOK_DEPLOYER;
@@ -111,6 +115,14 @@ contract TestLoanSourceRotation is TestBaseWorkflow {
111
115
  trustedForwarder: TRUSTED_FORWARDER
112
116
  });
113
117
 
118
+ REV_OWNER = new REVOwner(
119
+ IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
120
+ jbDirectory(),
121
+ FEE_PROJECT_ID,
122
+ SUCKER_REGISTRY,
123
+ address(LOANS_CONTRACT)
124
+ );
125
+
114
126
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
115
127
  jbController(),
116
128
  SUCKER_REGISTRY,
@@ -119,9 +131,12 @@ contract TestLoanSourceRotation is TestBaseWorkflow {
119
131
  PUBLISHER,
120
132
  IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
121
133
  address(LOANS_CONTRACT),
122
- TRUSTED_FORWARDER
134
+ TRUSTED_FORWARDER,
135
+ address(REV_OWNER)
123
136
  );
124
137
 
138
+ REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
139
+
125
140
  vm.prank(multisig());
126
141
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
127
142
 
@@ -37,6 +37,8 @@ import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStor
37
37
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
38
38
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
39
39
  import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
40
+ import {REVOwner} from "../src/REVOwner.sol";
41
+ import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
40
42
 
41
43
  struct FeeProjectConfig {
42
44
  REVConfig configuration;
@@ -54,6 +56,8 @@ contract TestLoansCashOutDelay is TestBaseWorkflow {
54
56
  // forge-lint: disable-next-line(mixed-case-variable)
55
57
  REVDeployer REV_DEPLOYER;
56
58
  // forge-lint: disable-next-line(mixed-case-variable)
59
+ REVOwner REV_OWNER;
60
+ // forge-lint: disable-next-line(mixed-case-variable)
57
61
  JB721TiersHook EXAMPLE_HOOK;
58
62
  // forge-lint: disable-next-line(mixed-case-variable)
59
63
  IJB721TiersHookDeployer HOOK_DEPLOYER;
@@ -214,6 +218,14 @@ contract TestLoansCashOutDelay is TestBaseWorkflow {
214
218
  trustedForwarder: TRUSTED_FORWARDER
215
219
  });
216
220
 
221
+ REV_OWNER = new REVOwner(
222
+ IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
223
+ jbDirectory(),
224
+ FEE_PROJECT_ID,
225
+ SUCKER_REGISTRY,
226
+ address(LOANS_CONTRACT)
227
+ );
228
+
217
229
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
218
230
  jbController(),
219
231
  SUCKER_REGISTRY,
@@ -222,9 +234,12 @@ contract TestLoansCashOutDelay is TestBaseWorkflow {
222
234
  PUBLISHER,
223
235
  IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
224
236
  address(LOANS_CONTRACT),
225
- TRUSTED_FORWARDER
237
+ TRUSTED_FORWARDER,
238
+ address(REV_OWNER)
226
239
  );
227
240
 
241
+ REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
242
+
228
243
  // Approve the deployer to configure the fee project.
229
244
  vm.prank(multisig());
230
245
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
@@ -293,13 +308,13 @@ contract TestLoansCashOutDelay is TestBaseWorkflow {
293
308
 
294
309
  /// @notice Verify the deployer actually set a cash out delay for the delayed revnet.
295
310
  function test_delayedRevnet_hasCashOutDelay() public view {
296
- uint256 cashOutDelay = REV_DEPLOYER.cashOutDelayOf(DELAYED_REVNET_ID);
311
+ uint256 cashOutDelay = REV_OWNER.cashOutDelayOf(DELAYED_REVNET_ID);
297
312
  assertGt(cashOutDelay, block.timestamp, "Cash out delay should be in the future");
298
313
  }
299
314
 
300
315
  /// @notice Verify the normal revnet has no cash out delay.
301
316
  function test_normalRevnet_noCashOutDelay() public view {
302
- uint256 cashOutDelay = REV_DEPLOYER.cashOutDelayOf(NORMAL_REVNET_ID);
317
+ uint256 cashOutDelay = REV_OWNER.cashOutDelayOf(NORMAL_REVNET_ID);
303
318
  assertEq(cashOutDelay, 0, "Normal revnet should have no cash out delay");
304
319
  }
305
320
 
@@ -327,7 +342,7 @@ contract TestLoansCashOutDelay is TestBaseWorkflow {
327
342
  REVLoanSource memory source = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
328
343
 
329
344
  // Attempt to borrow — should revert with CashOutDelayNotFinished.
330
- uint256 cashOutDelay = REV_DEPLOYER.cashOutDelayOf(DELAYED_REVNET_ID);
345
+ uint256 cashOutDelay = REV_OWNER.cashOutDelayOf(DELAYED_REVNET_ID);
331
346
  vm.expectRevert(
332
347
  abi.encodeWithSelector(REVLoans.REVLoans_CashOutDelayNotFinished.selector, cashOutDelay, block.timestamp)
333
348
  );
@@ -429,7 +444,7 @@ contract TestLoansCashOutDelay is TestBaseWorkflow {
429
444
  uint256 tokenCount = _payAndGetTokens(DELAYED_REVNET_ID, 1 ether);
430
445
 
431
446
  // Warp to exactly the delay timestamp (not past it).
432
- uint256 cashOutDelay = REV_DEPLOYER.cashOutDelayOf(DELAYED_REVNET_ID);
447
+ uint256 cashOutDelay = REV_OWNER.cashOutDelayOf(DELAYED_REVNET_ID);
433
448
  vm.warp(cashOutDelay);
434
449
 
435
450
  // borrowableAmountFrom should still return 0 (cashOutDelay > block.timestamp is false, but == is not >).
@@ -446,7 +461,7 @@ contract TestLoansCashOutDelay is TestBaseWorkflow {
446
461
  uint256 tokenCount = _payAndGetTokens(DELAYED_REVNET_ID, 1 ether);
447
462
 
448
463
  // Warp to 1 second before the delay expires.
449
- uint256 cashOutDelay = REV_DEPLOYER.cashOutDelayOf(DELAYED_REVNET_ID);
464
+ uint256 cashOutDelay = REV_OWNER.cashOutDelayOf(DELAYED_REVNET_ID);
450
465
  vm.warp(cashOutDelay - 1);
451
466
 
452
467
  // borrowableAmountFrom should return 0.
@@ -35,6 +35,8 @@ import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStor
35
35
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
36
36
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
37
37
  import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
38
+ import {REVOwner} from "../src/REVOwner.sol";
39
+ import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
38
40
 
39
41
  /// @notice Long-tail economic simulation: run a revnet through multiple stage transitions with many payments
40
42
  /// and cash outs, verifying value conservation and bonding curve consistency.
@@ -45,6 +47,8 @@ contract TestLongTailEconomics is TestBaseWorkflow {
45
47
  // forge-lint: disable-next-line(mixed-case-variable)
46
48
  REVDeployer REV_DEPLOYER;
47
49
  // forge-lint: disable-next-line(mixed-case-variable)
50
+ REVOwner REV_OWNER;
51
+ // forge-lint: disable-next-line(mixed-case-variable)
48
52
  JB721TiersHook EXAMPLE_HOOK;
49
53
  // forge-lint: disable-next-line(mixed-case-variable)
50
54
  IJB721TiersHookDeployer HOOK_DEPLOYER;
@@ -104,6 +108,14 @@ contract TestLongTailEconomics is TestBaseWorkflow {
104
108
  trustedForwarder: TRUSTED_FORWARDER
105
109
  });
106
110
 
111
+ REV_OWNER = new REVOwner(
112
+ IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
113
+ jbDirectory(),
114
+ FEE_PROJECT_ID,
115
+ SUCKER_REGISTRY,
116
+ address(LOANS_CONTRACT)
117
+ );
118
+
107
119
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
108
120
  jbController(),
109
121
  SUCKER_REGISTRY,
@@ -112,9 +124,12 @@ contract TestLongTailEconomics is TestBaseWorkflow {
112
124
  PUBLISHER,
113
125
  IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
114
126
  address(LOANS_CONTRACT),
115
- TRUSTED_FORWARDER
127
+ TRUSTED_FORWARDER,
128
+ address(REV_OWNER)
116
129
  );
117
130
 
131
+ REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
132
+
118
133
  vm.prank(multisig());
119
134
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
120
135