@rev-net/core-v6 0.0.11 → 0.0.13

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 (81) hide show
  1. package/ADMINISTRATION.md +7 -7
  2. package/ARCHITECTURE.md +11 -11
  3. package/AUDIT_INSTRUCTIONS.md +295 -0
  4. package/CHANGE_LOG.md +316 -0
  5. package/README.md +9 -6
  6. package/RISKS.md +180 -35
  7. package/SKILLS.md +9 -11
  8. package/STYLE_GUIDE.md +14 -1
  9. package/USER_JOURNEYS.md +489 -0
  10. package/package.json +9 -9
  11. package/script/Deploy.s.sol +124 -40
  12. package/script/helpers/RevnetCoreDeploymentLib.sol +19 -6
  13. package/src/REVDeployer.sol +183 -175
  14. package/src/REVLoans.sol +65 -28
  15. package/src/interfaces/IREVDeployer.sol +25 -23
  16. package/src/structs/REV721TiersHookFlags.sol +1 -0
  17. package/src/structs/REVAutoIssuance.sol +1 -0
  18. package/src/structs/REVBaseline721HookConfig.sol +1 -0
  19. package/src/structs/REVConfig.sol +1 -0
  20. package/src/structs/REVCroptopAllowedPost.sol +1 -0
  21. package/src/structs/REVDeploy721TiersHookConfig.sol +13 -14
  22. package/src/structs/REVDescription.sol +1 -0
  23. package/src/structs/REVLoan.sol +1 -0
  24. package/src/structs/REVLoanSource.sol +1 -0
  25. package/src/structs/REVStageConfig.sol +1 -0
  26. package/src/structs/REVSuckerDeploymentConfig.sol +1 -0
  27. package/test/REV.integrations.t.sol +148 -19
  28. package/test/REVAutoIssuanceFuzz.t.sol +31 -6
  29. package/test/REVDeployerRegressions.t.sol +47 -9
  30. package/test/REVInvincibility.t.sol +83 -19
  31. package/test/REVInvincibilityHandler.sol +29 -0
  32. package/test/REVLifecycle.t.sol +36 -6
  33. package/test/REVLoans.invariants.t.sol +64 -10
  34. package/test/REVLoansAttacks.t.sol +54 -9
  35. package/test/REVLoansFeeRecovery.t.sol +61 -15
  36. package/test/REVLoansFindings.t.sol +42 -9
  37. package/test/REVLoansRegressions.t.sol +33 -6
  38. package/test/REVLoansSourceFeeRecovery.t.sol +491 -0
  39. package/test/REVLoansSourced.t.sol +79 -17
  40. package/test/REVLoansUnSourced.t.sol +61 -10
  41. package/test/TestBurnHeldTokens.t.sol +47 -11
  42. package/test/TestCEIPattern.t.sol +37 -6
  43. package/test/TestCashOutCallerValidation.t.sol +41 -8
  44. package/test/TestConversionDocumentation.t.sol +50 -13
  45. package/test/TestCrossCurrencyReclaim.t.sol +584 -0
  46. package/test/TestCrossSourceReallocation.t.sol +37 -6
  47. package/test/TestERC2771MetaTx.t.sol +557 -0
  48. package/test/TestEmptyBuybackSpecs.t.sol +45 -10
  49. package/test/TestFlashLoanSurplus.t.sol +39 -7
  50. package/test/TestHookArrayOOB.t.sol +42 -13
  51. package/test/TestLiquidationBehavior.t.sol +37 -7
  52. package/test/TestLoanSourceRotation.t.sol +525 -0
  53. package/test/TestLongTailEconomics.t.sol +651 -0
  54. package/test/TestLowFindings.t.sol +80 -8
  55. package/test/TestMixedFixes.t.sol +43 -9
  56. package/test/TestPermit2Signatures.t.sol +657 -0
  57. package/test/TestReallocationSandwich.t.sol +384 -0
  58. package/test/TestRevnetRegressions.t.sol +324 -0
  59. package/test/TestSplitWeightAdjustment.t.sol +52 -13
  60. package/test/TestSplitWeightE2E.t.sol +53 -18
  61. package/test/TestSplitWeightFork.t.sol +66 -21
  62. package/test/TestStageTransitionBorrowable.t.sol +38 -6
  63. package/test/TestSwapTerminalPermission.t.sol +37 -7
  64. package/test/TestUint112Overflow.t.sol +39 -6
  65. package/test/TestZeroRepayment.t.sol +37 -6
  66. package/test/fork/ForkTestBase.sol +66 -17
  67. package/test/fork/TestCashOutFork.t.sol +9 -3
  68. package/test/fork/TestLoanBorrowFork.t.sol +1 -0
  69. package/test/fork/TestLoanCrossRulesetFork.t.sol +11 -3
  70. package/test/fork/TestLoanLiquidationFork.t.sol +1 -0
  71. package/test/fork/TestLoanReallocateFork.t.sol +1 -0
  72. package/test/fork/TestLoanRepayFork.t.sol +1 -0
  73. package/test/fork/TestLoanTransferFork.t.sol +133 -0
  74. package/test/fork/TestSplitWeightFork.t.sol +3 -0
  75. package/test/helpers/REVEmpty721Config.sol +46 -0
  76. package/test/mock/MockBuybackDataHook.sol +1 -0
  77. package/test/regression/TestBurnPermissionRequired.t.sol +267 -0
  78. package/test/regression/TestCrossRevnetLiquidation.t.sol +228 -0
  79. package/test/regression/TestCumulativeLoanCounter.t.sol +38 -8
  80. package/test/regression/TestLiquidateGapHandling.t.sol +40 -8
  81. package/test/regression/TestZeroPriceFeed.t.sol +396 -0
@@ -1,22 +1,32 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  pragma solidity 0.8.26;
3
3
 
4
+ // forge-lint: disable-next-line(unaliased-plain-import)
4
5
  import "forge-std/Test.sol";
5
6
  import {StdInvariant} from "forge-std/StdInvariant.sol";
7
+ // forge-lint: disable-next-line(unaliased-plain-import)
6
8
  import /* {*} from */ "@bananapus/core-v6/test/helpers/TestBaseWorkflow.sol";
7
- import /* {*} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
9
+ // import /* {*} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
10
+ // forge-lint: disable-next-line(unaliased-plain-import)
8
11
  import /* {*} from */ "./../src/REVDeployer.sol";
12
+ // forge-lint: disable-next-line(unaliased-plain-import)
9
13
  import /* {*} from */ "./../src/REVLoans.sol";
14
+ // forge-lint: disable-next-line(unaliased-plain-import)
10
15
  import "@croptop/core-v6/src/CTPublisher.sol";
11
16
  import {MockBuybackDataHook} from "./mock/MockBuybackDataHook.sol";
17
+ import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
12
18
 
19
+ // forge-lint: disable-next-line(unaliased-plain-import)
13
20
  import "@bananapus/core-v6/script/helpers/CoreDeploymentLib.sol";
21
+ // forge-lint: disable-next-line(unaliased-plain-import)
14
22
  import "@bananapus/721-hook-v6/script/helpers/Hook721DeploymentLib.sol";
23
+ // forge-lint: disable-next-line(unaliased-plain-import)
15
24
  import "@bananapus/suckers-v6/script/helpers/SuckerDeploymentLib.sol";
25
+ // forge-lint: disable-next-line(unaliased-plain-import)
16
26
  import "@croptop/core-v6/script/helpers/CroptopDeploymentLib.sol";
27
+ // forge-lint: disable-next-line(unaliased-plain-import)
17
28
  import "@bananapus/router-terminal-v6/script/helpers/RouterTerminalDeploymentLib.sol";
18
29
 
19
- import {JBCashOuts} from "@bananapus/core-v6/src/libraries/JBCashOuts.sol";
20
30
  import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
21
31
  import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
22
32
  import {REVLoans} from "../src/REVLoans.sol";
@@ -40,17 +50,28 @@ struct FeeProjectConfig {
40
50
  }
41
51
 
42
52
  contract REVLoansCallHandler is JBTest {
53
+ // forge-lint: disable-next-line(mixed-case-variable)
43
54
  uint256 public COLLATERAL_SUM;
55
+ // forge-lint: disable-next-line(mixed-case-variable)
44
56
  uint256 public COLLATERAL_RETURNED;
57
+ // forge-lint: disable-next-line(mixed-case-variable)
45
58
  uint256 public BORROWED_SUM;
59
+ // forge-lint: disable-next-line(mixed-case-variable)
46
60
  uint256 public RUNS;
61
+ // forge-lint: disable-next-line(mixed-case-variable)
47
62
  uint256 REVNET_ID;
63
+ // forge-lint: disable-next-line(mixed-case-variable)
48
64
  uint256 LAST_LOAN_MODIFIED;
65
+ // forge-lint: disable-next-line(mixed-case-variable)
49
66
  address USER;
50
67
 
68
+ // forge-lint: disable-next-line(mixed-case-variable)
51
69
  IJBMultiTerminal TERMINAL;
70
+ // forge-lint: disable-next-line(mixed-case-variable)
52
71
  IREVLoans LOANS;
72
+ // forge-lint: disable-next-line(mixed-case-variable)
53
73
  IJBPermissions PERMS;
74
+ // forge-lint: disable-next-line(mixed-case-variable)
54
75
  IJBTokens TOKENS;
55
76
 
56
77
  constructor(
@@ -258,34 +279,50 @@ contract InvariantREVLoansTests is StdInvariant, TestBaseWorkflow {
258
279
  using JBRulesetMetadataResolver for JBRuleset;
259
280
 
260
281
  /// @notice the salts that are used to deploy the contracts.
282
+ // forge-lint: disable-next-line(mixed-case-variable)
261
283
  bytes32 REV_DEPLOYER_SALT = "REVDeployer";
284
+ // forge-lint: disable-next-line(mixed-case-variable)
262
285
  bytes32 ERC20_SALT = "REV_TOKEN";
263
286
 
264
287
  // Handlers
288
+ // forge-lint: disable-next-line(mixed-case-variable)
265
289
  REVLoansCallHandler PAY_HANDLER;
266
290
 
291
+ // forge-lint: disable-next-line(mixed-case-variable)
267
292
  REVDeployer REV_DEPLOYER;
293
+ // forge-lint: disable-next-line(mixed-case-variable)
268
294
  JB721TiersHook EXAMPLE_HOOK;
269
295
 
270
296
  /// @notice Deploys tiered ERC-721 hooks for revnets.
297
+ // forge-lint: disable-next-line(mixed-case-variable)
271
298
  IJB721TiersHookDeployer HOOK_DEPLOYER;
299
+ // forge-lint: disable-next-line(mixed-case-variable)
272
300
  IJB721TiersHookStore HOOK_STORE;
301
+ // forge-lint: disable-next-line(mixed-case-variable)
273
302
  IJBAddressRegistry ADDRESS_REGISTRY;
274
303
 
304
+ // forge-lint: disable-next-line(mixed-case-variable)
275
305
  IREVLoans LOANS_CONTRACT;
276
306
 
277
307
  /// @notice Deploys and tracks suckers for revnets.
308
+ // forge-lint: disable-next-line(mixed-case-variable)
278
309
  IJBSuckerRegistry SUCKER_REGISTRY;
279
310
 
311
+ // forge-lint: disable-next-line(mixed-case-variable)
280
312
  CTPublisher PUBLISHER;
313
+ // forge-lint: disable-next-line(mixed-case-variable)
281
314
  MockBuybackDataHook MOCK_BUYBACK;
282
315
 
283
316
  // When the second project is deployed, track the block.timestamp.
317
+ // forge-lint: disable-next-line(mixed-case-variable)
284
318
  uint256 INITIAL_TIMESTAMP;
285
319
 
320
+ // forge-lint: disable-next-line(mixed-case-variable)
286
321
  uint256 FEE_PROJECT_ID;
322
+ // forge-lint: disable-next-line(mixed-case-variable)
287
323
  uint256 REVNET_ID;
288
324
 
325
+ // forge-lint: disable-next-line(mixed-case-variable)
289
326
  address USER = makeAddr("user");
290
327
 
291
328
  /// @notice The address that is allowed to forward calls.
@@ -322,7 +359,11 @@ contract InvariantREVLoansTests is StdInvariant, TestBaseWorkflow {
322
359
  {
323
360
  REVAutoIssuance[] memory issuanceConfs = new REVAutoIssuance[](1);
324
361
  issuanceConfs[0] = REVAutoIssuance({
325
- chainId: uint32(block.chainid), count: uint104(70_000 * decimalMultiplier), beneficiary: multisig()
362
+ // forge-lint: disable-next-line(unsafe-typecast)
363
+ chainId: uint32(block.chainid),
364
+ // forge-lint: disable-next-line(unsafe-typecast)
365
+ count: uint104(70_000 * decimalMultiplier),
366
+ beneficiary: multisig()
326
367
  });
327
368
 
328
369
  stageConfigurations[0] = REVStageConfig({
@@ -330,6 +371,7 @@ contract InvariantREVLoansTests is StdInvariant, TestBaseWorkflow {
330
371
  autoIssuances: issuanceConfs,
331
372
  splitPercent: 2000, // 20%
332
373
  splits: splits,
374
+ // forge-lint: disable-next-line(unsafe-typecast)
333
375
  initialIssuance: uint112(1000 * decimalMultiplier),
334
376
  issuanceCutFrequency: 90 days,
335
377
  issuanceCutPercent: JBConstants.MAX_WEIGHT_CUT_PERCENT / 2,
@@ -364,6 +406,7 @@ contract InvariantREVLoansTests is StdInvariant, TestBaseWorkflow {
364
406
 
365
407
  // The project's revnet configuration
366
408
  REVConfig memory revnetConfiguration = REVConfig({
409
+ // forge-lint: disable-next-line(named-struct-fields)
367
410
  description: REVDescription(name, symbol, projectUri, ERC20_SALT),
368
411
  baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
369
412
  splitOperator: multisig(),
@@ -410,7 +453,11 @@ contract InvariantREVLoansTests is StdInvariant, TestBaseWorkflow {
410
453
  {
411
454
  REVAutoIssuance[] memory issuanceConfs = new REVAutoIssuance[](1);
412
455
  issuanceConfs[0] = REVAutoIssuance({
413
- chainId: uint32(block.chainid), count: uint104(70_000 * decimalMultiplier), beneficiary: multisig()
456
+ // forge-lint: disable-next-line(unsafe-typecast)
457
+ chainId: uint32(block.chainid),
458
+ // forge-lint: disable-next-line(unsafe-typecast)
459
+ count: uint104(70_000 * decimalMultiplier),
460
+ beneficiary: multisig()
414
461
  });
415
462
 
416
463
  stageConfigurations[0] = REVStageConfig({
@@ -418,6 +465,7 @@ contract InvariantREVLoansTests is StdInvariant, TestBaseWorkflow {
418
465
  autoIssuances: issuanceConfs,
419
466
  splitPercent: 2000, // 20%
420
467
  splits: splits,
468
+ // forge-lint: disable-next-line(unsafe-typecast)
421
469
  initialIssuance: uint112(1000 * decimalMultiplier),
422
470
  issuanceCutFrequency: 90 days,
423
471
  issuanceCutPercent: JBConstants.MAX_WEIGHT_CUT_PERCENT / 2,
@@ -452,6 +500,7 @@ contract InvariantREVLoansTests is StdInvariant, TestBaseWorkflow {
452
500
 
453
501
  // The project's revnet configuration
454
502
  REVConfig memory revnetConfiguration = REVConfig({
503
+ // forge-lint: disable-next-line(named-struct-fields)
455
504
  description: REVDescription(name, symbol, projectUri, "NANA_TOKEN"),
456
505
  baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
457
506
  splitOperator: multisig(),
@@ -476,8 +525,9 @@ contract InvariantREVLoansTests is StdInvariant, TestBaseWorkflow {
476
525
 
477
526
  HOOK_STORE = new JB721TiersHookStore();
478
527
 
479
- EXAMPLE_HOOK =
480
- new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
528
+ EXAMPLE_HOOK = new JB721TiersHook(
529
+ jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
530
+ );
481
531
 
482
532
  ADDRESS_REGISTRY = new JBAddressRegistry();
483
533
 
@@ -515,22 +565,26 @@ contract InvariantREVLoansTests is StdInvariant, TestBaseWorkflow {
515
565
 
516
566
  // Configure the project.
517
567
  vm.prank(address(multisig()));
518
- REVNET_ID = REV_DEPLOYER.deployFor({
568
+ (REVNET_ID,) = REV_DEPLOYER.deployFor({
519
569
  revnetId: FEE_PROJECT_ID, // Zero to deploy a new revnet
520
570
  configuration: feeProjectConfig.configuration,
521
571
  terminalConfigurations: feeProjectConfig.terminalConfigurations,
522
- suckerDeploymentConfiguration: feeProjectConfig.suckerDeploymentConfiguration
572
+ suckerDeploymentConfiguration: feeProjectConfig.suckerDeploymentConfiguration,
573
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
574
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
523
575
  });
524
576
 
525
577
  // Configure second revnet
526
578
  FeeProjectConfig memory fee2Config = getSecondProjectConfig();
527
579
 
528
580
  // Configure the second project.
529
- REVNET_ID = REV_DEPLOYER.deployFor({
581
+ (REVNET_ID,) = REV_DEPLOYER.deployFor({
530
582
  revnetId: 0, // Zero to deploy a new revnet
531
583
  configuration: fee2Config.configuration,
532
584
  terminalConfigurations: fee2Config.terminalConfigurations,
533
- suckerDeploymentConfiguration: fee2Config.suckerDeploymentConfiguration
585
+ suckerDeploymentConfiguration: fee2Config.suckerDeploymentConfiguration,
586
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
587
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
534
588
  });
535
589
 
536
590
  INITIAL_TIMESTAMP = block.timestamp;
@@ -1,17 +1,26 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  pragma solidity 0.8.26;
3
3
 
4
+ // forge-lint: disable-next-line(unaliased-plain-import)
4
5
  import "forge-std/Test.sol";
6
+ // forge-lint: disable-next-line(unaliased-plain-import)
5
7
  import /* {*} from */ "@bananapus/core-v6/test/helpers/TestBaseWorkflow.sol";
6
- import /* {*} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
8
+ // import /* {*} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
9
+ // forge-lint: disable-next-line(unaliased-plain-import)
7
10
  import /* {*} from */ "./../src/REVDeployer.sol";
11
+ // forge-lint: disable-next-line(unaliased-plain-import)
8
12
  import "@croptop/core-v6/src/CTPublisher.sol";
9
13
  import {MockBuybackDataHook} from "./mock/MockBuybackDataHook.sol";
10
14
 
15
+ // forge-lint: disable-next-line(unaliased-plain-import)
11
16
  import "@bananapus/core-v6/script/helpers/CoreDeploymentLib.sol";
17
+ // forge-lint: disable-next-line(unaliased-plain-import)
12
18
  import "@bananapus/721-hook-v6/script/helpers/Hook721DeploymentLib.sol";
19
+ // forge-lint: disable-next-line(unaliased-plain-import)
13
20
  import "@bananapus/suckers-v6/script/helpers/SuckerDeploymentLib.sol";
21
+ // forge-lint: disable-next-line(unaliased-plain-import)
14
22
  import "@croptop/core-v6/script/helpers/CroptopDeploymentLib.sol";
23
+ // forge-lint: disable-next-line(unaliased-plain-import)
15
24
  import "@bananapus/router-terminal-v6/script/helpers/RouterTerminalDeploymentLib.sol";
16
25
 
17
26
  import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
@@ -31,8 +40,8 @@ import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
31
40
  import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
32
41
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
33
42
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
43
+ import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
34
44
  import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
35
- import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
36
45
 
37
46
  /// @notice A malicious terminal that re-enters REVLoans during fee payment in _adjust().
38
47
  /// @dev Reentrancy during pay() callback in _adjust.
@@ -177,24 +186,40 @@ struct AttackProjectConfig {
177
186
  /// @notice Attack tests for REVLoans covering uint112 truncation, reentrancy,
178
187
  /// collateral race conditions, liquidation edge cases, and fuzz testing.
179
188
  contract REVLoansAttacks is TestBaseWorkflow {
189
+ // forge-lint: disable-next-line(mixed-case-variable)
180
190
  bytes32 REV_DEPLOYER_SALT = "REVDeployer";
191
+ // forge-lint: disable-next-line(mixed-case-variable)
181
192
  bytes32 ERC20_SALT = "REV_TOKEN";
182
193
 
194
+ // forge-lint: disable-next-line(mixed-case-variable)
183
195
  REVDeployer REV_DEPLOYER;
196
+ // forge-lint: disable-next-line(mixed-case-variable)
184
197
  JB721TiersHook EXAMPLE_HOOK;
198
+ // forge-lint: disable-next-line(mixed-case-variable)
185
199
  IJB721TiersHookDeployer HOOK_DEPLOYER;
200
+ // forge-lint: disable-next-line(mixed-case-variable)
186
201
  IJB721TiersHookStore HOOK_STORE;
202
+ // forge-lint: disable-next-line(mixed-case-variable)
187
203
  IJBAddressRegistry ADDRESS_REGISTRY;
204
+ // forge-lint: disable-next-line(mixed-case-variable)
188
205
  IREVLoans LOANS_CONTRACT;
206
+ // forge-lint: disable-next-line(mixed-case-variable)
189
207
  MockERC20 TOKEN;
208
+ // forge-lint: disable-next-line(mixed-case-variable)
190
209
  IJBSuckerRegistry SUCKER_REGISTRY;
210
+ // forge-lint: disable-next-line(mixed-case-variable)
191
211
  CTPublisher PUBLISHER;
212
+ // forge-lint: disable-next-line(mixed-case-variable)
192
213
  MockBuybackDataHook MOCK_BUYBACK;
193
214
 
215
+ // forge-lint: disable-next-line(mixed-case-variable)
194
216
  uint256 FEE_PROJECT_ID;
217
+ // forge-lint: disable-next-line(mixed-case-variable)
195
218
  uint256 REVNET_ID;
196
219
 
220
+ // forge-lint: disable-next-line(mixed-case-variable)
197
221
  address USER = makeAddr("user");
222
+ // forge-lint: disable-next-line(mixed-case-variable)
198
223
  address ATTACKER = makeAddr("attacker");
199
224
 
200
225
  address private constant TRUSTED_FORWARDER = 0xB2b5841DBeF766d4b521221732F9B618fCf34A87;
@@ -224,7 +249,11 @@ contract REVLoansAttacks is TestBaseWorkflow {
224
249
 
225
250
  REVAutoIssuance[] memory issuanceConfs = new REVAutoIssuance[](1);
226
251
  issuanceConfs[0] = REVAutoIssuance({
227
- chainId: uint32(block.chainid), count: uint104(70_000 * decimalMultiplier), beneficiary: multisig()
252
+ // forge-lint: disable-next-line(unsafe-typecast)
253
+ chainId: uint32(block.chainid),
254
+ // forge-lint: disable-next-line(unsafe-typecast)
255
+ count: uint104(70_000 * decimalMultiplier),
256
+ beneficiary: multisig()
228
257
  });
229
258
 
230
259
  stageConfigurations[0] = REVStageConfig({
@@ -232,6 +261,7 @@ contract REVLoansAttacks is TestBaseWorkflow {
232
261
  autoIssuances: issuanceConfs,
233
262
  splitPercent: 2000,
234
263
  splits: splits,
264
+ // forge-lint: disable-next-line(unsafe-typecast)
235
265
  initialIssuance: uint112(1000 * decimalMultiplier),
236
266
  issuanceCutFrequency: 90 days,
237
267
  issuanceCutPercent: JBConstants.MAX_WEIGHT_CUT_PERCENT / 2,
@@ -240,6 +270,7 @@ contract REVLoansAttacks is TestBaseWorkflow {
240
270
  });
241
271
 
242
272
  REVConfig memory revnetConfiguration = REVConfig({
273
+ // forge-lint: disable-next-line(named-struct-fields)
243
274
  description: REVDescription(name, symbol, projectUri, ERC20_SALT),
244
275
  baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
245
276
  splitOperator: multisig(),
@@ -280,7 +311,11 @@ contract REVLoansAttacks is TestBaseWorkflow {
280
311
  REVStageConfig[] memory stageConfigurations = new REVStageConfig[](1);
281
312
  REVAutoIssuance[] memory issuanceConfs = new REVAutoIssuance[](1);
282
313
  issuanceConfs[0] = REVAutoIssuance({
283
- chainId: uint32(block.chainid), count: uint104(70_000 * decimalMultiplier), beneficiary: multisig()
314
+ // forge-lint: disable-next-line(unsafe-typecast)
315
+ chainId: uint32(block.chainid),
316
+ // forge-lint: disable-next-line(unsafe-typecast)
317
+ count: uint104(70_000 * decimalMultiplier),
318
+ beneficiary: multisig()
284
319
  });
285
320
 
286
321
  stageConfigurations[0] = REVStageConfig({
@@ -288,6 +323,7 @@ contract REVLoansAttacks is TestBaseWorkflow {
288
323
  autoIssuances: issuanceConfs,
289
324
  splitPercent: 2000,
290
325
  splits: splits,
326
+ // forge-lint: disable-next-line(unsafe-typecast)
291
327
  initialIssuance: uint112(1000 * decimalMultiplier),
292
328
  issuanceCutFrequency: 90 days,
293
329
  issuanceCutPercent: JBConstants.MAX_WEIGHT_CUT_PERCENT / 2,
@@ -296,6 +332,7 @@ contract REVLoansAttacks is TestBaseWorkflow {
296
332
  });
297
333
 
298
334
  REVConfig memory revnetConfiguration = REVConfig({
335
+ // forge-lint: disable-next-line(named-struct-fields)
299
336
  description: REVDescription(name, symbol, projectUri, "NANA_TOKEN"),
300
337
  baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
301
338
  splitOperator: multisig(),
@@ -318,8 +355,9 @@ contract REVLoansAttacks is TestBaseWorkflow {
318
355
 
319
356
  SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
320
357
  HOOK_STORE = new JB721TiersHookStore();
321
- EXAMPLE_HOOK =
322
- new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
358
+ EXAMPLE_HOOK = new JB721TiersHook(
359
+ jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
360
+ );
323
361
  ADDRESS_REGISTRY = new JBAddressRegistry();
324
362
  HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
325
363
  PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
@@ -361,16 +399,20 @@ contract REVLoansAttacks is TestBaseWorkflow {
361
399
  revnetId: FEE_PROJECT_ID,
362
400
  configuration: feeProjectConfig.configuration,
363
401
  terminalConfigurations: feeProjectConfig.terminalConfigurations,
364
- suckerDeploymentConfiguration: feeProjectConfig.suckerDeploymentConfiguration
402
+ suckerDeploymentConfiguration: feeProjectConfig.suckerDeploymentConfiguration,
403
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
404
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
365
405
  });
366
406
 
367
407
  // Deploy second revnet with loans enabled
368
408
  AttackProjectConfig memory revnetConfig = _getRevnetConfig();
369
- REVNET_ID = REV_DEPLOYER.deployFor({
409
+ (REVNET_ID,) = REV_DEPLOYER.deployFor({
370
410
  revnetId: 0,
371
411
  configuration: revnetConfig.configuration,
372
412
  terminalConfigurations: revnetConfig.terminalConfigurations,
373
- suckerDeploymentConfiguration: revnetConfig.suckerDeploymentConfiguration
413
+ suckerDeploymentConfiguration: revnetConfig.suckerDeploymentConfiguration,
414
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
415
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
374
416
  });
375
417
 
376
418
  vm.deal(USER, 1000e18);
@@ -440,11 +482,13 @@ contract REVLoansAttacks is TestBaseWorkflow {
440
482
  // Now verify that the uint112 cast would truncate if somehow a larger value were used.
441
483
  // We can directly verify the truncation behavior:
442
484
  uint256 overflowValue = uint256(type(uint112).max) + 1;
485
+ // forge-lint: disable-next-line(unsafe-typecast)
443
486
  uint112 truncated = uint112(overflowValue);
444
487
  assertEq(truncated, 0, "uint112 truncation of max+1 should wrap to 0");
445
488
 
446
489
  // And for a value just slightly above max:
447
490
  uint256 slightlyOver = uint256(type(uint112).max) + 1000;
491
+ // forge-lint: disable-next-line(unsafe-typecast)
448
492
  uint112 truncated2 = uint112(slightlyOver);
449
493
  assertEq(truncated2, 999, "uint112 truncation should wrap around");
450
494
  }
@@ -460,6 +504,7 @@ contract REVLoansAttacks is TestBaseWorkflow {
460
504
  uint256 overflowCollateral = maxCollateral + 1;
461
505
 
462
506
  // Direct cast would truncate
507
+ // forge-lint: disable-next-line(unsafe-typecast)
463
508
  uint112 truncated = uint112(overflowCollateral);
464
509
  assertEq(truncated, 0, "Collateral overflow should truncate to 0");
465
510
 
@@ -1,17 +1,26 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  pragma solidity 0.8.26;
3
3
 
4
+ // forge-lint: disable-next-line(unaliased-plain-import)
4
5
  import "forge-std/Test.sol";
6
+ // forge-lint: disable-next-line(unaliased-plain-import)
5
7
  import /* {*} from */ "@bananapus/core-v6/test/helpers/TestBaseWorkflow.sol";
6
- import /* {*} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
8
+ // import /* {*} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
9
+ // forge-lint: disable-next-line(unaliased-plain-import)
7
10
  import /* {*} from */ "./../src/REVDeployer.sol";
11
+ // forge-lint: disable-next-line(unaliased-plain-import)
8
12
  import "@croptop/core-v6/src/CTPublisher.sol";
9
13
  import {MockBuybackDataHook} from "./mock/MockBuybackDataHook.sol";
10
14
 
15
+ // forge-lint: disable-next-line(unaliased-plain-import)
11
16
  import "@bananapus/core-v6/script/helpers/CoreDeploymentLib.sol";
17
+ // forge-lint: disable-next-line(unaliased-plain-import)
12
18
  import "@bananapus/721-hook-v6/script/helpers/Hook721DeploymentLib.sol";
19
+ // forge-lint: disable-next-line(unaliased-plain-import)
13
20
  import "@bananapus/suckers-v6/script/helpers/SuckerDeploymentLib.sol";
21
+ // forge-lint: disable-next-line(unaliased-plain-import)
14
22
  import "@croptop/core-v6/script/helpers/CroptopDeploymentLib.sol";
23
+ // forge-lint: disable-next-line(unaliased-plain-import)
15
24
  import "@bananapus/router-terminal-v6/script/helpers/RouterTerminalDeploymentLib.sol";
16
25
 
17
26
  import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
@@ -33,7 +42,7 @@ import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStor
33
42
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
34
43
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
35
44
  import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
36
- import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
45
+ import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
37
46
 
38
47
  /// @notice A terminal mock that always reverts on pay(), used to simulate fee payment failure.
39
48
  contract RevertingFeeTerminal is ERC165, IJBPayoutTerminal {
@@ -137,24 +146,40 @@ struct FeeRecoveryProjectConfig {
137
146
  /// @dev When feeTerminal.pay() reverts, the borrower should receive the fee amount back
138
147
  /// instead of losing it. For ERC-20 tokens, the dangling allowance must also be cleaned up.
139
148
  contract REVLoansFeeRecovery is TestBaseWorkflow {
149
+ // forge-lint: disable-next-line(mixed-case-variable)
140
150
  bytes32 REV_DEPLOYER_SALT = "REVDeployer";
151
+ // forge-lint: disable-next-line(mixed-case-variable)
141
152
  bytes32 ERC20_SALT = "REV_TOKEN";
142
153
 
154
+ // forge-lint: disable-next-line(mixed-case-variable)
143
155
  REVDeployer REV_DEPLOYER;
156
+ // forge-lint: disable-next-line(mixed-case-variable)
144
157
  JB721TiersHook EXAMPLE_HOOK;
158
+ // forge-lint: disable-next-line(mixed-case-variable)
145
159
  IJB721TiersHookDeployer HOOK_DEPLOYER;
160
+ // forge-lint: disable-next-line(mixed-case-variable)
146
161
  IJB721TiersHookStore HOOK_STORE;
162
+ // forge-lint: disable-next-line(mixed-case-variable)
147
163
  IJBAddressRegistry ADDRESS_REGISTRY;
164
+ // forge-lint: disable-next-line(mixed-case-variable)
148
165
  IREVLoans LOANS_CONTRACT;
166
+ // forge-lint: disable-next-line(mixed-case-variable)
149
167
  MockERC20 TOKEN;
168
+ // forge-lint: disable-next-line(mixed-case-variable)
150
169
  IJBSuckerRegistry SUCKER_REGISTRY;
170
+ // forge-lint: disable-next-line(mixed-case-variable)
151
171
  CTPublisher PUBLISHER;
172
+ // forge-lint: disable-next-line(mixed-case-variable)
152
173
  MockBuybackDataHook MOCK_BUYBACK;
174
+ // forge-lint: disable-next-line(mixed-case-variable)
153
175
  RevertingFeeTerminal REVERTING_TERMINAL;
154
176
 
177
+ // forge-lint: disable-next-line(mixed-case-variable)
155
178
  uint256 FEE_PROJECT_ID;
179
+ // forge-lint: disable-next-line(mixed-case-variable)
156
180
  uint256 REVNET_ID;
157
181
 
182
+ // forge-lint: disable-next-line(mixed-case-variable)
158
183
  address USER = makeAddr("user");
159
184
  address private constant TRUSTED_FORWARDER = 0xB2b5841DBeF766d4b521221732F9B618fCf34A87;
160
185
 
@@ -180,7 +205,11 @@ contract REVLoansFeeRecovery is TestBaseWorkflow {
180
205
 
181
206
  REVAutoIssuance[] memory issuanceConfs = new REVAutoIssuance[](1);
182
207
  issuanceConfs[0] = REVAutoIssuance({
183
- chainId: uint32(block.chainid), count: uint104(70_000 * decimalMultiplier), beneficiary: multisig()
208
+ // forge-lint: disable-next-line(unsafe-typecast)
209
+ chainId: uint32(block.chainid),
210
+ // forge-lint: disable-next-line(unsafe-typecast)
211
+ count: uint104(70_000 * decimalMultiplier),
212
+ beneficiary: multisig()
184
213
  });
185
214
 
186
215
  stageConfigurations[0] = REVStageConfig({
@@ -188,6 +217,7 @@ contract REVLoansFeeRecovery is TestBaseWorkflow {
188
217
  autoIssuances: issuanceConfs,
189
218
  splitPercent: 2000,
190
219
  splits: splits,
220
+ // forge-lint: disable-next-line(unsafe-typecast)
191
221
  initialIssuance: uint112(1000 * decimalMultiplier),
192
222
  issuanceCutFrequency: 90 days,
193
223
  issuanceCutPercent: JBConstants.MAX_WEIGHT_CUT_PERCENT / 2,
@@ -196,9 +226,12 @@ contract REVLoansFeeRecovery is TestBaseWorkflow {
196
226
  });
197
227
 
198
228
  REVConfig memory revnetConfiguration = REVConfig({
199
- description: REVDescription(
200
- "Revnet", "$REV", "ipfs://QmNRHT91HcDgMcenebYX7rJigt77cgNcosvuhX21wkF3tx", ERC20_SALT
201
- ),
229
+ description: REVDescription({
230
+ name: "Revnet",
231
+ ticker: "$REV",
232
+ uri: "ipfs://QmNRHT91HcDgMcenebYX7rJigt77cgNcosvuhX21wkF3tx",
233
+ salt: ERC20_SALT
234
+ }),
202
235
  baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
203
236
  splitOperator: multisig(),
204
237
  stageConfigurations: stageConfigurations
@@ -235,7 +268,11 @@ contract REVLoansFeeRecovery is TestBaseWorkflow {
235
268
  REVStageConfig[] memory stageConfigurations = new REVStageConfig[](1);
236
269
  REVAutoIssuance[] memory issuanceConfs = new REVAutoIssuance[](1);
237
270
  issuanceConfs[0] = REVAutoIssuance({
238
- chainId: uint32(block.chainid), count: uint104(70_000 * decimalMultiplier), beneficiary: multisig()
271
+ // forge-lint: disable-next-line(unsafe-typecast)
272
+ chainId: uint32(block.chainid),
273
+ // forge-lint: disable-next-line(unsafe-typecast)
274
+ count: uint104(70_000 * decimalMultiplier),
275
+ beneficiary: multisig()
239
276
  });
240
277
 
241
278
  stageConfigurations[0] = REVStageConfig({
@@ -243,6 +280,7 @@ contract REVLoansFeeRecovery is TestBaseWorkflow {
243
280
  autoIssuances: issuanceConfs,
244
281
  splitPercent: 2000,
245
282
  splits: splits,
283
+ // forge-lint: disable-next-line(unsafe-typecast)
246
284
  initialIssuance: uint112(1000 * decimalMultiplier),
247
285
  issuanceCutFrequency: 90 days,
248
286
  issuanceCutPercent: JBConstants.MAX_WEIGHT_CUT_PERCENT / 2,
@@ -255,9 +293,12 @@ contract REVLoansFeeRecovery is TestBaseWorkflow {
255
293
  _loanSources[1] = REVLoanSource({token: address(TOKEN), terminal: jbMultiTerminal()});
256
294
 
257
295
  REVConfig memory revnetConfiguration = REVConfig({
258
- description: REVDescription(
259
- "NANA", "$NANA", "ipfs://QmNRHT91HcDgMcenebYX7rJigt77cgNxosvuhX21wkF3tx", "NANA_TOKEN"
260
- ),
296
+ description: REVDescription({
297
+ name: "NANA",
298
+ ticker: "$NANA",
299
+ uri: "ipfs://QmNRHT91HcDgMcenebYX7rJigt77cgNxosvuhX21wkF3tx",
300
+ salt: "NANA_TOKEN"
301
+ }),
261
302
  baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
262
303
  splitOperator: multisig(),
263
304
  stageConfigurations: stageConfigurations
@@ -279,8 +320,9 @@ contract REVLoansFeeRecovery is TestBaseWorkflow {
279
320
 
280
321
  SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
281
322
  HOOK_STORE = new JB721TiersHookStore();
282
- EXAMPLE_HOOK =
283
- new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
323
+ EXAMPLE_HOOK = new JB721TiersHook(
324
+ jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
325
+ );
284
326
  ADDRESS_REGISTRY = new JBAddressRegistry();
285
327
  HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
286
328
  PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
@@ -323,16 +365,20 @@ contract REVLoansFeeRecovery is TestBaseWorkflow {
323
365
  revnetId: FEE_PROJECT_ID,
324
366
  configuration: feeProjectConfig.configuration,
325
367
  terminalConfigurations: feeProjectConfig.terminalConfigurations,
326
- suckerDeploymentConfiguration: feeProjectConfig.suckerDeploymentConfiguration
368
+ suckerDeploymentConfiguration: feeProjectConfig.suckerDeploymentConfiguration,
369
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
370
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
327
371
  });
328
372
 
329
373
  // Deploy revnet with loans enabled.
330
374
  FeeRecoveryProjectConfig memory revnetConfig = _getRevnetConfig();
331
- REVNET_ID = REV_DEPLOYER.deployFor({
375
+ (REVNET_ID,) = REV_DEPLOYER.deployFor({
332
376
  revnetId: 0,
333
377
  configuration: revnetConfig.configuration,
334
378
  terminalConfigurations: revnetConfig.terminalConfigurations,
335
- suckerDeploymentConfiguration: revnetConfig.suckerDeploymentConfiguration
379
+ suckerDeploymentConfiguration: revnetConfig.suckerDeploymentConfiguration,
380
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
381
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
336
382
  });
337
383
 
338
384
  vm.deal(USER, 1000e18);