@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
@@ -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 Validates that liquidateExpiredLoansFrom rejects loan number ranges that would overflow into another
44
47
  /// revnet's namespace.
@@ -87,7 +90,14 @@ contract TestCrossRevnetLiquidation is TestBaseWorkflow {
87
90
  SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
88
91
  HOOK_STORE = new JB721TiersHookStore();
89
92
  EXAMPLE_HOOK = new JB721TiersHook(
90
- jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
93
+ jbDirectory(),
94
+ jbPermissions(),
95
+ jbPrices(),
96
+ jbRulesets(),
97
+ HOOK_STORE,
98
+ jbSplits(),
99
+ IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
100
+ multisig()
91
101
  );
92
102
  ADDRESS_REGISTRY = new JBAddressRegistry();
93
103
  HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
@@ -100,7 +110,7 @@ contract TestCrossRevnetLiquidation is TestBaseWorkflow {
100
110
  .addPriceFeedFor(0, uint32(uint160(address(TOKEN))), uint32(uint160(JBConstants.NATIVE_TOKEN)), priceFeed);
101
111
  LOANS_CONTRACT = new REVLoans({
102
112
  controller: jbController(),
103
- projects: jbProjects(),
113
+ suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
104
114
  revId: FEE_PROJECT_ID,
105
115
  owner: address(this),
106
116
  permit2: permit2(),
@@ -111,7 +121,8 @@ contract TestCrossRevnetLiquidation is TestBaseWorkflow {
111
121
  jbDirectory(),
112
122
  FEE_PROJECT_ID,
113
123
  SUCKER_REGISTRY,
114
- address(LOANS_CONTRACT)
124
+ address(LOANS_CONTRACT),
125
+ address(0)
115
126
  );
116
127
 
117
128
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
@@ -35,11 +35,14 @@ 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 {REVEmpty721Config} from "../helpers/REVEmpty721Config.sol";
41
43
  import {REVOwner} from "../../src/REVOwner.sol";
42
44
  import {IREVDeployer} from "../../src/interfaces/IREVDeployer.sol";
45
+ import {MockSuckerRegistry} from "../mock/MockSuckerRegistry.sol";
43
46
 
44
47
  /// @notice totalLoansBorrowedFor is a cumulative counter, not an active loan count.
45
48
  /// @dev The rename from numberOfLoansFor to totalLoansBorrowedFor clarifies that the counter only increments
@@ -90,7 +93,14 @@ contract TestCumulativeLoanCounter is TestBaseWorkflow {
90
93
  SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
91
94
  HOOK_STORE = new JB721TiersHookStore();
92
95
  EXAMPLE_HOOK = new JB721TiersHook(
93
- jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
96
+ jbDirectory(),
97
+ jbPermissions(),
98
+ jbPrices(),
99
+ jbRulesets(),
100
+ HOOK_STORE,
101
+ jbSplits(),
102
+ IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
103
+ multisig()
94
104
  );
95
105
  ADDRESS_REGISTRY = new JBAddressRegistry();
96
106
  HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
@@ -104,7 +114,7 @@ contract TestCumulativeLoanCounter is TestBaseWorkflow {
104
114
  );
105
115
  LOANS_CONTRACT = new REVLoans({
106
116
  controller: jbController(),
107
- projects: jbProjects(),
117
+ suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
108
118
  revId: FEE_PROJECT_ID,
109
119
  owner: address(this),
110
120
  permit2: permit2(),
@@ -115,7 +125,8 @@ contract TestCumulativeLoanCounter is TestBaseWorkflow {
115
125
  jbDirectory(),
116
126
  FEE_PROJECT_ID,
117
127
  SUCKER_REGISTRY,
118
- address(LOANS_CONTRACT)
128
+ address(LOANS_CONTRACT),
129
+ address(0)
119
130
  );
120
131
 
121
132
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
@@ -242,7 +253,7 @@ contract TestCumulativeLoanCounter is TestBaseWorkflow {
242
253
  );
243
254
  REVLoanSource memory source = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
244
255
  vm.prank(user);
245
- (loanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(user), 25);
256
+ (loanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(user), 25, user);
246
257
  }
247
258
 
248
259
  /// @notice Verifies totalLoansBorrowedFor never decrements after loan repayment.
@@ -36,11 +36,14 @@ import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
36
36
  import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
37
37
  import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
38
38
  import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
39
+ import {JB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
40
+ import {IJB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721CheckpointsDeployer.sol";
39
41
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
40
42
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
41
43
  import {REVEmpty721Config} from "../helpers/REVEmpty721Config.sol";
42
44
  import {REVOwner} from "../../src/REVOwner.sol";
43
45
  import {IREVDeployer} from "../../src/interfaces/IREVDeployer.sol";
46
+ import {MockSuckerRegistry} from "../mock/MockSuckerRegistry.sol";
44
47
 
45
48
  /// @notice liquidateExpiredLoansFrom halts on deleted loan gaps.
46
49
  /// @dev Before the fix, the function used `break` when encountering a deleted loan (createdAt == 0),
@@ -93,7 +96,14 @@ contract TestLiquidateGapHandling is TestBaseWorkflow {
93
96
  SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
94
97
  HOOK_STORE = new JB721TiersHookStore();
95
98
  EXAMPLE_HOOK = new JB721TiersHook(
96
- jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
99
+ jbDirectory(),
100
+ jbPermissions(),
101
+ jbPrices(),
102
+ jbRulesets(),
103
+ HOOK_STORE,
104
+ jbSplits(),
105
+ IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
106
+ multisig()
97
107
  );
98
108
  ADDRESS_REGISTRY = new JBAddressRegistry();
99
109
  HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
@@ -106,7 +116,7 @@ contract TestLiquidateGapHandling is TestBaseWorkflow {
106
116
  .addPriceFeedFor(0, uint32(uint160(address(TOKEN))), uint32(uint160(JBConstants.NATIVE_TOKEN)), priceFeed);
107
117
  LOANS_CONTRACT = new REVLoans({
108
118
  controller: jbController(),
109
- projects: jbProjects(),
119
+ suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
110
120
  revId: FEE_PROJECT_ID,
111
121
  owner: address(this),
112
122
  permit2: permit2(),
@@ -117,7 +127,8 @@ contract TestLiquidateGapHandling is TestBaseWorkflow {
117
127
  jbDirectory(),
118
128
  FEE_PROJECT_ID,
119
129
  SUCKER_REGISTRY,
120
- address(LOANS_CONTRACT)
130
+ address(LOANS_CONTRACT),
131
+ address(0)
121
132
  );
122
133
 
123
134
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
@@ -246,7 +257,7 @@ contract TestLiquidateGapHandling is TestBaseWorkflow {
246
257
  );
247
258
  REVLoanSource memory source = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
248
259
  vm.prank(user);
249
- (loanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(user), 25);
260
+ (loanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(user), 25, user);
250
261
  }
251
262
 
252
263
  /// @notice Liquidation should continue past deleted loan gaps.
@@ -36,12 +36,15 @@ import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
36
36
  import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
37
37
  import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
38
38
  import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
39
+ import {JB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
40
+ import {IJB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721CheckpointsDeployer.sol";
39
41
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
40
42
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
41
43
  import {REVEmpty721Config} from "../helpers/REVEmpty721Config.sol";
42
44
  import {IJBPrices} from "@bananapus/core-v6/src/interfaces/IJBPrices.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 Verifies that `_totalBorrowedFrom` gracefully handles zero-price feeds.
47
50
  /// @dev When a cross-currency price feed returns 0 (e.g., inverse truncation at low decimals), the affected source
@@ -96,7 +99,14 @@ contract TestZeroPriceFeed is TestBaseWorkflow {
96
99
  SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
97
100
  HOOK_STORE = new JB721TiersHookStore();
98
101
  EXAMPLE_HOOK = new JB721TiersHook(
99
- jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
102
+ jbDirectory(),
103
+ jbPermissions(),
104
+ jbPrices(),
105
+ jbRulesets(),
106
+ HOOK_STORE,
107
+ jbSplits(),
108
+ IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
109
+ multisig()
100
110
  );
101
111
  ADDRESS_REGISTRY = new JBAddressRegistry();
102
112
  HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
@@ -114,7 +124,7 @@ contract TestZeroPriceFeed is TestBaseWorkflow {
114
124
 
115
125
  LOANS_CONTRACT = new REVLoans({
116
126
  controller: jbController(),
117
- projects: jbProjects(),
127
+ suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
118
128
  revId: FEE_PROJECT_ID,
119
129
  owner: address(this),
120
130
  permit2: permit2(),
@@ -126,7 +136,8 @@ contract TestZeroPriceFeed is TestBaseWorkflow {
126
136
  jbDirectory(),
127
137
  FEE_PROJECT_ID,
128
138
  SUCKER_REGISTRY,
129
- address(LOANS_CONTRACT)
139
+ address(LOANS_CONTRACT),
140
+ address(0)
130
141
  );
131
142
 
132
143
  REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
@@ -300,7 +311,7 @@ contract TestZeroPriceFeed is TestBaseWorkflow {
300
311
  _mockBurnPermission();
301
312
  REVLoanSource memory ethSource = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
302
313
  vm.prank(USER);
303
- LOANS_CONTRACT.borrowFrom(REVNET_ID, ethSource, 0, ethCollateral, payable(USER), 25);
314
+ LOANS_CONTRACT.borrowFrom(REVNET_ID, ethSource, 0, ethCollateral, payable(USER), 25, USER);
304
315
 
305
316
  // Step 3: Fund the terminal with TOKEN and borrow from TOKEN source.
306
317
  uint256 tokenFunding = 1_000_000e6;
@@ -312,7 +323,7 @@ contract TestZeroPriceFeed is TestBaseWorkflow {
312
323
  _mockBurnPermission();
313
324
  REVLoanSource memory tokenSource = REVLoanSource({token: address(TOKEN), terminal: jbMultiTerminal()});
314
325
  vm.prank(USER);
315
- LOANS_CONTRACT.borrowFrom(REVNET_ID, tokenSource, 0, tokenCollateral, payable(USER), 25);
326
+ LOANS_CONTRACT.borrowFrom(REVNET_ID, tokenSource, 0, tokenCollateral, payable(USER), 25, USER);
316
327
 
317
328
  // Verify both sources have nonzero totalBorrowedFrom.
318
329
  uint256 borrowedFromEth =
@@ -384,7 +395,7 @@ contract TestZeroPriceFeed is TestBaseWorkflow {
384
395
  _mockBurnPermission();
385
396
  REVLoanSource memory ethSource = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
386
397
  vm.prank(USER);
387
- LOANS_CONTRACT.borrowFrom(REVNET_ID, ethSource, 0, revnetTokens / 2, payable(USER), 25);
398
+ LOANS_CONTRACT.borrowFrom(REVNET_ID, ethSource, 0, revnetTokens / 2, payable(USER), 25, USER);
388
399
 
389
400
  // Step 3: Get borrowable amount.
390
401
  vm.prank(USER);