@rev-net/core-v6 0.0.11 → 0.0.12

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 (49) hide show
  1. package/ADMINISTRATION.md +7 -7
  2. package/ARCHITECTURE.md +11 -11
  3. package/README.md +7 -4
  4. package/RISKS.md +2 -2
  5. package/SKILLS.md +8 -10
  6. package/STYLE_GUIDE.md +14 -1
  7. package/package.json +9 -9
  8. package/script/Deploy.s.sol +85 -35
  9. package/script/helpers/RevnetCoreDeploymentLib.sol +12 -5
  10. package/src/REVDeployer.sol +121 -129
  11. package/src/REVLoans.sol +14 -13
  12. package/src/interfaces/IREVDeployer.sol +25 -22
  13. package/src/structs/REVDeploy721TiersHookConfig.sol +12 -14
  14. package/test/REV.integrations.t.sol +17 -8
  15. package/test/REVAutoIssuanceFuzz.t.sol +9 -4
  16. package/test/REVDeployerRegressions.t.sol +13 -6
  17. package/test/REVInvincibility.t.sol +26 -12
  18. package/test/REVLifecycle.t.sol +9 -4
  19. package/test/REVLoans.invariants.t.sol +13 -6
  20. package/test/REVLoansAttacks.t.sol +12 -5
  21. package/test/REVLoansFeeRecovery.t.sol +12 -5
  22. package/test/REVLoansFindings.t.sol +16 -7
  23. package/test/REVLoansRegressions.t.sol +9 -4
  24. package/test/REVLoansSourced.t.sol +24 -11
  25. package/test/REVLoansUnSourced.t.sol +13 -6
  26. package/test/TestBurnHeldTokens.t.sol +16 -7
  27. package/test/TestCEIPattern.t.sol +12 -5
  28. package/test/TestCashOutCallerValidation.t.sol +12 -5
  29. package/test/TestConversionDocumentation.t.sol +25 -9
  30. package/test/TestCrossSourceReallocation.t.sol +12 -5
  31. package/test/TestEmptyBuybackSpecs.t.sol +23 -8
  32. package/test/TestFlashLoanSurplus.t.sol +12 -5
  33. package/test/TestHookArrayOOB.t.sol +19 -10
  34. package/test/TestLiquidationBehavior.t.sol +12 -5
  35. package/test/TestLowFindings.t.sol +16 -7
  36. package/test/TestMixedFixes.t.sol +16 -7
  37. package/test/TestSplitWeightAdjustment.t.sol +29 -12
  38. package/test/TestSplitWeightE2E.t.sol +24 -16
  39. package/test/TestSplitWeightFork.t.sol +20 -14
  40. package/test/TestStageTransitionBorrowable.t.sol +15 -5
  41. package/test/TestSwapTerminalPermission.t.sol +15 -5
  42. package/test/TestUint112Overflow.t.sol +12 -5
  43. package/test/TestZeroRepayment.t.sol +12 -5
  44. package/test/fork/ForkTestBase.sol +20 -14
  45. package/test/fork/TestCashOutFork.t.sol +8 -2
  46. package/test/fork/TestLoanCrossRulesetFork.t.sol +8 -2
  47. package/test/helpers/REVEmpty721Config.sol +45 -0
  48. package/test/regression/TestCumulativeLoanCounter.t.sol +12 -5
  49. package/test/regression/TestLiquidateGapHandling.t.sol +12 -5
@@ -31,6 +31,8 @@ import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
31
31
  import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
32
32
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
33
33
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
34
+ import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
35
+ import {REVCroptopAllowedPost} from "../src/structs/REVCroptopAllowedPost.sol";
34
36
 
35
37
  struct FeeProjectConfig {
36
38
  REVConfig configuration;
@@ -262,8 +264,9 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
262
264
 
263
265
  HOOK_STORE = new JB721TiersHookStore();
264
266
 
265
- EXAMPLE_HOOK =
266
- new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
267
+ EXAMPLE_HOOK = new JB721TiersHook(
268
+ jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
269
+ );
267
270
 
268
271
  ADDRESS_REGISTRY = new JBAddressRegistry();
269
272
 
@@ -317,18 +320,22 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
317
320
  revnetId: FEE_PROJECT_ID, // Zero to deploy a new revnet
318
321
  configuration: feeProjectConfig.configuration,
319
322
  terminalConfigurations: feeProjectConfig.terminalConfigurations,
320
- suckerDeploymentConfiguration: feeProjectConfig.suckerDeploymentConfiguration
323
+ suckerDeploymentConfiguration: feeProjectConfig.suckerDeploymentConfiguration,
324
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
325
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
321
326
  });
322
327
 
323
328
  // Configure second revnet
324
329
  FeeProjectConfig memory fee2Config = getSecondProjectConfig();
325
330
 
326
331
  // Configure the project.
327
- REVNET_ID = REV_DEPLOYER.deployFor({
332
+ (REVNET_ID,) = REV_DEPLOYER.deployFor({
328
333
  revnetId: 0, // Zero to deploy a new revnet
329
334
  configuration: fee2Config.configuration,
330
335
  terminalConfigurations: fee2Config.terminalConfigurations,
331
- suckerDeploymentConfiguration: fee2Config.suckerDeploymentConfiguration
336
+ suckerDeploymentConfiguration: fee2Config.suckerDeploymentConfiguration,
337
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
338
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
332
339
  });
333
340
 
334
341
  // Give Eth for the user experience
@@ -631,11 +638,13 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
631
638
  projectConfig.configuration.stageConfigurations = stageConfigurations;
632
639
  projectConfig.configuration.description.salt = "FeeChange";
633
640
 
634
- revnetProjectId = REV_DEPLOYER.deployFor({
641
+ (revnetProjectId,) = REV_DEPLOYER.deployFor({
635
642
  revnetId: 0, // Zero to deploy a new revnet
636
643
  configuration: projectConfig.configuration,
637
644
  terminalConfigurations: projectConfig.terminalConfigurations,
638
- suckerDeploymentConfiguration: projectConfig.suckerDeploymentConfiguration
645
+ suckerDeploymentConfiguration: projectConfig.suckerDeploymentConfiguration,
646
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
647
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
639
648
  });
640
649
  }
641
650
 
@@ -1141,11 +1150,13 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
1141
1150
  projectConfig.configuration.stageConfigurations = stageConfigurations;
1142
1151
  projectConfig.configuration.description.salt = "FeeChange";
1143
1152
 
1144
- revnetProjectId = REV_DEPLOYER.deployFor({
1153
+ (revnetProjectId,) = REV_DEPLOYER.deployFor({
1145
1154
  revnetId: 0, // Zero to deploy a new revnet
1146
1155
  configuration: projectConfig.configuration,
1147
1156
  terminalConfigurations: projectConfig.terminalConfigurations,
1148
- suckerDeploymentConfiguration: projectConfig.suckerDeploymentConfiguration
1157
+ suckerDeploymentConfiguration: projectConfig.suckerDeploymentConfiguration,
1158
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
1159
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
1149
1160
  });
1150
1161
  }
1151
1162
 
@@ -1237,11 +1248,13 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
1237
1248
  projectConfig.configuration.stageConfigurations = stageConfigurations;
1238
1249
  projectConfig.configuration.description.salt = "FeeChange";
1239
1250
 
1240
- uint256 revnetProjectId = REV_DEPLOYER.deployFor({
1251
+ (uint256 revnetProjectId,) = REV_DEPLOYER.deployFor({
1241
1252
  revnetId: 0, // Zero to deploy a new revnet
1242
1253
  configuration: projectConfig.configuration,
1243
1254
  terminalConfigurations: projectConfig.terminalConfigurations,
1244
- suckerDeploymentConfiguration: projectConfig.suckerDeploymentConfiguration
1255
+ suckerDeploymentConfiguration: projectConfig.suckerDeploymentConfiguration,
1256
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
1257
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
1245
1258
  });
1246
1259
 
1247
1260
  vm.startPrank(USER);
@@ -7,6 +7,8 @@ import /* {*} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
7
7
  import /* {*} from */ "./../src/REVDeployer.sol";
8
8
  import "@croptop/core-v6/src/CTPublisher.sol";
9
9
  import {MockBuybackDataHook} from "./mock/MockBuybackDataHook.sol";
10
+ import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
11
+ import {REVCroptopAllowedPost} from "../src/structs/REVCroptopAllowedPost.sol";
10
12
 
11
13
  import "@bananapus/core-v6/script/helpers/CoreDeploymentLib.sol";
12
14
  import "@bananapus/721-hook-v6/script/helpers/Hook721DeploymentLib.sol";
@@ -249,8 +251,9 @@ contract REVLoansUnsourcedTests is TestBaseWorkflow {
249
251
 
250
252
  HOOK_STORE = new JB721TiersHookStore();
251
253
 
252
- EXAMPLE_HOOK =
253
- new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
254
+ EXAMPLE_HOOK = new JB721TiersHook(
255
+ jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
256
+ );
254
257
 
255
258
  ADDRESS_REGISTRY = new JBAddressRegistry();
256
259
 
@@ -288,22 +291,26 @@ contract REVLoansUnsourcedTests is TestBaseWorkflow {
288
291
 
289
292
  vm.prank(address(multisig()));
290
293
  // Configure the project.
291
- REVNET_ID = REV_DEPLOYER.deployFor({
294
+ (REVNET_ID,) = REV_DEPLOYER.deployFor({
292
295
  revnetId: FEE_PROJECT_ID, // Zero to deploy a new revnet
293
296
  configuration: feeProjectConfig.configuration,
294
297
  terminalConfigurations: feeProjectConfig.terminalConfigurations,
295
- suckerDeploymentConfiguration: feeProjectConfig.suckerDeploymentConfiguration
298
+ suckerDeploymentConfiguration: feeProjectConfig.suckerDeploymentConfiguration,
299
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
300
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
296
301
  });
297
302
 
298
303
  // Configure second revnet
299
304
  FeeProjectConfig memory fee2Config = getSecondProjectConfig();
300
305
 
301
306
  // Configure the project.
302
- REVNET_ID = REV_DEPLOYER.deployFor({
307
+ (REVNET_ID,) = REV_DEPLOYER.deployFor({
303
308
  revnetId: 0, // Zero to deploy a new revnet
304
309
  configuration: fee2Config.configuration,
305
310
  terminalConfigurations: fee2Config.terminalConfigurations,
306
- suckerDeploymentConfiguration: fee2Config.suckerDeploymentConfiguration
311
+ suckerDeploymentConfiguration: fee2Config.suckerDeploymentConfiguration,
312
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
313
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
307
314
  });
308
315
 
309
316
  // Give Eth for the user experience
@@ -7,6 +7,8 @@ import /* {*} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
7
7
  import /* {*} from */ "./../src/REVDeployer.sol";
8
8
  import "@croptop/core-v6/src/CTPublisher.sol";
9
9
  import {MockBuybackDataHook} from "./mock/MockBuybackDataHook.sol";
10
+ import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
11
+ import {REVCroptopAllowedPost} from "../src/structs/REVCroptopAllowedPost.sol";
10
12
 
11
13
  import "@bananapus/core-v6/script/helpers/CoreDeploymentLib.sol";
12
14
  import "@bananapus/721-hook-v6/script/helpers/Hook721DeploymentLib.sol";
@@ -147,13 +149,15 @@ contract TestBurnHeldTokens is TestBaseWorkflow {
147
149
  stageConfigurations: stageConfigurations
148
150
  });
149
151
 
150
- revnetId = REV_DEPLOYER.deployFor({
152
+ (revnetId,) = REV_DEPLOYER.deployFor({
151
153
  revnetId: 0,
152
154
  configuration: revnetConfiguration,
153
155
  terminalConfigurations: terminalConfigurations,
154
156
  suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
155
157
  deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256(abi.encodePacked("PRT"))
156
- })
158
+ }),
159
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
160
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
157
161
  });
158
162
  }
159
163
 
@@ -163,8 +167,9 @@ contract TestBurnHeldTokens is TestBaseWorkflow {
163
167
  FEE_PROJECT_ID = jbProjects().createFor(multisig());
164
168
  SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
165
169
  HOOK_STORE = new JB721TiersHookStore();
166
- EXAMPLE_HOOK =
167
- new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
170
+ EXAMPLE_HOOK = new JB721TiersHook(
171
+ jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
172
+ );
168
173
  ADDRESS_REGISTRY = new JBAddressRegistry();
169
174
  HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
170
175
  PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
@@ -201,7 +206,9 @@ contract TestBurnHeldTokens is TestBaseWorkflow {
201
206
  revnetId: FEE_PROJECT_ID,
202
207
  configuration: feeProjectConfig.configuration,
203
208
  terminalConfigurations: feeProjectConfig.terminalConfigurations,
204
- suckerDeploymentConfiguration: feeProjectConfig.suckerDeploymentConfiguration
209
+ suckerDeploymentConfiguration: feeProjectConfig.suckerDeploymentConfiguration,
210
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
211
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
205
212
  });
206
213
 
207
214
  // Deploy revnet with partial splits.
@@ -290,7 +297,7 @@ contract TestBurnHeldTokens is TestBaseWorkflow {
290
297
  extraMetadata: 0
291
298
  });
292
299
 
293
- uint256 fullSplitRevnetId = REV_DEPLOYER.deployFor({
300
+ (uint256 fullSplitRevnetId,) = REV_DEPLOYER.deployFor({
294
301
  revnetId: 0,
295
302
  configuration: REVConfig({
296
303
  description: REVDescription("Full", "$FUL", "ipfs://test", "FUL_TOKEN"),
@@ -301,7 +308,9 @@ contract TestBurnHeldTokens is TestBaseWorkflow {
301
308
  terminalConfigurations: terminalConfigurations,
302
309
  suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
303
310
  deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256(abi.encodePacked("FUL"))
304
- })
311
+ }),
312
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
313
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
305
314
  });
306
315
 
307
316
  // REVDeployer should have no tokens for this revnet.
@@ -30,6 +30,8 @@ import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
30
30
  import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
31
31
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
32
32
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
33
+ import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
34
+ import {REVCroptopAllowedPost} from "../src/structs/REVCroptopAllowedPost.sol";
33
35
 
34
36
  /// @notice Contract that reenters REVLoans when it receives ETH during a borrow payout.
35
37
  /// Records the loan state it observes during reentrancy to verify CEI correctness.
@@ -94,8 +96,9 @@ contract TestCEIPattern is TestBaseWorkflow {
94
96
  FEE_PROJECT_ID = jbProjects().createFor(multisig());
95
97
  SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
96
98
  HOOK_STORE = new JB721TiersHookStore();
97
- EXAMPLE_HOOK =
98
- new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
99
+ EXAMPLE_HOOK = new JB721TiersHook(
100
+ jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
101
+ );
99
102
  ADDRESS_REGISTRY = new JBAddressRegistry();
100
103
  HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
101
104
  PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
@@ -168,7 +171,9 @@ contract TestCEIPattern is TestBaseWorkflow {
168
171
  terminalConfigurations: tc,
169
172
  suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
170
173
  deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("FEE")
171
- })
174
+ }),
175
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
176
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
172
177
  });
173
178
  }
174
179
 
@@ -205,13 +210,15 @@ contract TestCEIPattern is TestBaseWorkflow {
205
210
  splitOperator: multisig(),
206
211
  stageConfigurations: stages
207
212
  });
208
- REVNET_ID = REV_DEPLOYER.deployFor({
213
+ (REVNET_ID,) = REV_DEPLOYER.deployFor({
209
214
  revnetId: 0,
210
215
  configuration: cfg,
211
216
  terminalConfigurations: tc,
212
217
  suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
213
218
  deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("NANA")
214
- })
219
+ }),
220
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
221
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
215
222
  });
216
223
  }
217
224
 
@@ -30,6 +30,8 @@ import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
30
30
  import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
31
31
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
32
32
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
33
+ import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
34
+ import {REVCroptopAllowedPost} from "../src/structs/REVCroptopAllowedPost.sol";
33
35
 
34
36
  struct FeeProjectConfig {
35
37
  REVConfig configuration;
@@ -173,8 +175,9 @@ contract TestCashOutCallerValidation is TestBaseWorkflow {
173
175
  FEE_PROJECT_ID = jbProjects().createFor(multisig());
174
176
  SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
175
177
  HOOK_STORE = new JB721TiersHookStore();
176
- EXAMPLE_HOOK =
177
- new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
178
+ EXAMPLE_HOOK = new JB721TiersHook(
179
+ jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
180
+ );
178
181
  ADDRESS_REGISTRY = new JBAddressRegistry();
179
182
  HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
180
183
  PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
@@ -212,16 +215,20 @@ contract TestCashOutCallerValidation is TestBaseWorkflow {
212
215
  revnetId: FEE_PROJECT_ID,
213
216
  configuration: feeProjectConfig.configuration,
214
217
  terminalConfigurations: feeProjectConfig.terminalConfigurations,
215
- suckerDeploymentConfiguration: feeProjectConfig.suckerDeploymentConfiguration
218
+ suckerDeploymentConfiguration: feeProjectConfig.suckerDeploymentConfiguration,
219
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
220
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
216
221
  });
217
222
 
218
223
  // Deploy the revnet.
219
224
  FeeProjectConfig memory revnetConfig = getRevnetConfig();
220
- REVNET_ID = REV_DEPLOYER.deployFor({
225
+ (REVNET_ID,) = REV_DEPLOYER.deployFor({
221
226
  revnetId: 0,
222
227
  configuration: revnetConfig.configuration,
223
228
  terminalConfigurations: revnetConfig.terminalConfigurations,
224
- suckerDeploymentConfiguration: revnetConfig.suckerDeploymentConfiguration
229
+ suckerDeploymentConfiguration: revnetConfig.suckerDeploymentConfiguration,
230
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
231
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
225
232
  });
226
233
 
227
234
  vm.deal(USER, 100 ether);
@@ -7,6 +7,8 @@ import /* {*} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
7
7
  import /* {*} from */ "./../src/REVDeployer.sol";
8
8
  import "@croptop/core-v6/src/CTPublisher.sol";
9
9
  import {MockBuybackDataHook} from "./mock/MockBuybackDataHook.sol";
10
+ import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
11
+ import {REVCroptopAllowedPost} from "../src/structs/REVCroptopAllowedPost.sol";
10
12
 
11
13
  import "@bananapus/core-v6/script/helpers/CoreDeploymentLib.sol";
12
14
  import "@bananapus/721-hook-v6/script/helpers/Hook721DeploymentLib.sol";
@@ -112,8 +114,9 @@ contract TestConversionDocumentation is TestBaseWorkflow {
112
114
  FEE_PROJECT_ID = jbProjects().createFor(multisig());
113
115
  SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
114
116
  HOOK_STORE = new JB721TiersHookStore();
115
- EXAMPLE_HOOK =
116
- new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
117
+ EXAMPLE_HOOK = new JB721TiersHook(
118
+ jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
119
+ );
117
120
  ADDRESS_REGISTRY = new JBAddressRegistry();
118
121
  HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
119
122
  PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
@@ -152,7 +155,9 @@ contract TestConversionDocumentation is TestBaseWorkflow {
152
155
  revnetId: FEE_PROJECT_ID,
153
156
  configuration: cfg,
154
157
  terminalConfigurations: terms,
155
- suckerDeploymentConfiguration: suckerCfg
158
+ suckerDeploymentConfiguration: suckerCfg,
159
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
160
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
156
161
  });
157
162
  }
158
163
 
@@ -172,11 +177,13 @@ contract TestConversionDocumentation is TestBaseWorkflow {
172
177
 
173
178
  // Deploy as revnet — should succeed since project is blank.
174
179
  vm.prank(USER);
175
- uint256 deployed = REV_DEPLOYER.deployFor({
180
+ (uint256 deployed,) = REV_DEPLOYER.deployFor({
176
181
  revnetId: blankId,
177
182
  configuration: cfg,
178
183
  terminalConfigurations: terms,
179
- suckerDeploymentConfiguration: suckerCfg
184
+ suckerDeploymentConfiguration: suckerCfg,
185
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
186
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
180
187
  });
181
188
 
182
189
  assertEq(deployed, blankId, "Should return the same project ID");
@@ -253,7 +260,9 @@ contract TestConversionDocumentation is TestBaseWorkflow {
253
260
  revnetId: projectId,
254
261
  configuration: cfg,
255
262
  terminalConfigurations: terms2,
256
- suckerDeploymentConfiguration: suckerCfg
263
+ suckerDeploymentConfiguration: suckerCfg,
264
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
265
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
257
266
  });
258
267
  }
259
268
 
@@ -277,7 +286,9 @@ contract TestConversionDocumentation is TestBaseWorkflow {
277
286
  revnetId: blankId,
278
287
  configuration: cfg,
279
288
  terminalConfigurations: terms,
280
- suckerDeploymentConfiguration: suckerCfg
289
+ suckerDeploymentConfiguration: suckerCfg,
290
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
291
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
281
292
  });
282
293
 
283
294
  // Verify the project's owner is now the REVDeployer (NFT transferred permanently).
@@ -292,8 +303,13 @@ contract TestConversionDocumentation is TestBaseWorkflow {
292
303
  (REVConfig memory cfg, JBTerminalConfig[] memory terms, REVSuckerDeploymentConfig memory suckerCfg) =
293
304
  _getRevnetConfig("NewRevnet", "$NEW", "NEW_TOKEN");
294
305
 
295
- uint256 newId = REV_DEPLOYER.deployFor({
296
- revnetId: 0, configuration: cfg, terminalConfigurations: terms, suckerDeploymentConfiguration: suckerCfg
306
+ (uint256 newId,) = REV_DEPLOYER.deployFor({
307
+ revnetId: 0,
308
+ configuration: cfg,
309
+ terminalConfigurations: terms,
310
+ suckerDeploymentConfiguration: suckerCfg,
311
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
312
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
297
313
  });
298
314
 
299
315
  // Verify the project was created (ID > fee project).
@@ -29,6 +29,8 @@ import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
29
29
  import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
30
30
  import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
31
31
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
32
+ import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
33
+ import {REVCroptopAllowedPost} from "../src/structs/REVCroptopAllowedPost.sol";
32
34
 
33
35
  /// @notice Tests for PR #13: cross-source reallocation prevention.
34
36
  contract TestCrossSourceReallocation is TestBaseWorkflow {
@@ -57,8 +59,9 @@ contract TestCrossSourceReallocation is TestBaseWorkflow {
57
59
  FEE_PROJECT_ID = jbProjects().createFor(multisig());
58
60
  SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
59
61
  HOOK_STORE = new JB721TiersHookStore();
60
- EXAMPLE_HOOK =
61
- new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
62
+ EXAMPLE_HOOK = new JB721TiersHook(
63
+ jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
64
+ );
62
65
  ADDRESS_REGISTRY = new JBAddressRegistry();
63
66
  HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
64
67
  PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
@@ -131,7 +134,9 @@ contract TestCrossSourceReallocation is TestBaseWorkflow {
131
134
  terminalConfigurations: tc,
132
135
  suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
133
136
  deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("FEE")
134
- })
137
+ }),
138
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
139
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
135
140
  });
136
141
  }
137
142
 
@@ -168,13 +173,15 @@ contract TestCrossSourceReallocation is TestBaseWorkflow {
168
173
  splitOperator: multisig(),
169
174
  stageConfigurations: stages
170
175
  });
171
- REVNET_ID = REV_DEPLOYER.deployFor({
176
+ (REVNET_ID,) = REV_DEPLOYER.deployFor({
172
177
  revnetId: 0,
173
178
  configuration: cfg,
174
179
  terminalConfigurations: tc,
175
180
  suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
176
181
  deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("NANA")
177
- })
182
+ }),
183
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
184
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
178
185
  });
179
186
  }
180
187
 
@@ -29,6 +29,8 @@ import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/
29
29
  import {JBBeforePayRecordedContext} from "@bananapus/core-v6/src/structs/JBBeforePayRecordedContext.sol";
30
30
  import {JBPayHookSpecification} from "@bananapus/core-v6/src/structs/JBPayHookSpecification.sol";
31
31
  import {JBTokenAmount} from "@bananapus/core-v6/src/structs/JBTokenAmount.sol";
32
+ import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
33
+ import {REVCroptopAllowedPost} from "../src/structs/REVCroptopAllowedPost.sol";
32
34
 
33
35
  /// @notice Regression tests for the empty buyback hook specifications fix.
34
36
  /// When JBBuybackHook determines minting is cheaper than swapping, it returns an empty
@@ -57,8 +59,9 @@ contract TestEmptyBuybackSpecs is TestBaseWorkflow {
57
59
  FEE_PROJECT_ID = jbProjects().createFor(multisig());
58
60
  SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
59
61
  HOOK_STORE = new JB721TiersHookStore();
60
- EXAMPLE_HOOK =
61
- new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
62
+ EXAMPLE_HOOK = new JB721TiersHook(
63
+ jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
64
+ );
62
65
  ADDRESS_REGISTRY = new JBAddressRegistry();
63
66
  HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
64
67
  PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
@@ -134,15 +137,22 @@ contract TestEmptyBuybackSpecs is TestBaseWorkflow {
134
137
  revnetId: FEE_PROJECT_ID,
135
138
  configuration: feeCfg,
136
139
  terminalConfigurations: feeTc,
137
- suckerDeploymentConfiguration: feeSdc
140
+ suckerDeploymentConfiguration: feeSdc,
141
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
142
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
138
143
  });
139
144
 
140
145
  (REVConfig memory cfg, JBTerminalConfig[] memory tc, REVSuckerDeploymentConfig memory sdc) =
141
146
  _buildMinimalConfig();
142
147
  cfg.description = REVDescription("Test2", "TS2", "ipfs://test2", "TEST_SALT_2");
143
148
 
144
- revnetId = REV_DEPLOYER.deployFor({
145
- revnetId: 0, configuration: cfg, terminalConfigurations: tc, suckerDeploymentConfiguration: sdc
149
+ (revnetId,) = REV_DEPLOYER.deployFor({
150
+ revnetId: 0,
151
+ configuration: cfg,
152
+ terminalConfigurations: tc,
153
+ suckerDeploymentConfiguration: sdc,
154
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
155
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
146
156
  });
147
157
  }
148
158
 
@@ -209,8 +219,8 @@ contract TestEmptyBuybackSpecs is TestBaseWorkflow {
209
219
  }
210
220
  }
211
221
 
212
- /// @notice Verify beforePayRecordedWith returns empty hookSpecifications when buyback returns empty.
213
- function test_beforePayRecordedWith_emptyBuybackSpecs_returnsEmptyArray() public {
222
+ /// @notice Verify beforePayRecordedWith returns only the 721 hook spec when buyback returns empty.
223
+ function test_beforePayRecordedWith_emptyBuybackSpecs_returnsOnly721Hook() public {
214
224
  uint256 revnetId = _deployFeeAndRevnet();
215
225
 
216
226
  JBBeforePayRecordedContext memory context = JBBeforePayRecordedContext({
@@ -233,6 +243,11 @@ contract TestEmptyBuybackSpecs is TestBaseWorkflow {
233
243
  (uint256 weight, JBPayHookSpecification[] memory specs) = REV_DEPLOYER.beforePayRecordedWith(context);
234
244
 
235
245
  assertEq(weight, context.weight, "Weight should pass through from buyback hook");
236
- assertEq(specs.length, 0, "Should return empty specs when buyback hook returns empty and no 721 hook");
246
+ assertEq(specs.length, 1, "Should return only the 721 hook spec when buyback hook returns empty");
247
+ assertEq(
248
+ address(specs[0].hook),
249
+ address(REV_DEPLOYER.tiered721HookOf(revnetId)),
250
+ "Spec hook should be the revnet's 721 hook"
251
+ );
237
252
  }
238
253
  }
@@ -7,6 +7,8 @@ import /* {*} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
7
7
  import /* {*} from */ "./../src/REVDeployer.sol";
8
8
  import "@croptop/core-v6/src/CTPublisher.sol";
9
9
  import {MockBuybackDataHook} from "./mock/MockBuybackDataHook.sol";
10
+ import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
11
+ import {REVCroptopAllowedPost} from "../src/structs/REVCroptopAllowedPost.sol";
10
12
  import "@bananapus/core-v6/script/helpers/CoreDeploymentLib.sol";
11
13
  import "@bananapus/721-hook-v6/script/helpers/Hook721DeploymentLib.sol";
12
14
  import "@bananapus/suckers-v6/script/helpers/SuckerDeploymentLib.sol";
@@ -59,8 +61,9 @@ contract TestFlashLoanSurplus is TestBaseWorkflow {
59
61
  FEE_PROJECT_ID = jbProjects().createFor(multisig());
60
62
  SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
61
63
  HOOK_STORE = new JB721TiersHookStore();
62
- EXAMPLE_HOOK =
63
- new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
64
+ EXAMPLE_HOOK = new JB721TiersHook(
65
+ jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
66
+ );
64
67
  ADDRESS_REGISTRY = new JBAddressRegistry();
65
68
  HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
66
69
  PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
@@ -134,7 +137,9 @@ contract TestFlashLoanSurplus is TestBaseWorkflow {
134
137
  terminalConfigurations: tc,
135
138
  suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
136
139
  deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("FEE")
137
- })
140
+ }),
141
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
142
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
138
143
  });
139
144
  }
140
145
 
@@ -171,13 +176,15 @@ contract TestFlashLoanSurplus is TestBaseWorkflow {
171
176
  splitOperator: multisig(),
172
177
  stageConfigurations: stages
173
178
  });
174
- REVNET_ID = REV_DEPLOYER.deployFor({
179
+ (REVNET_ID,) = REV_DEPLOYER.deployFor({
175
180
  revnetId: 0,
176
181
  configuration: cfg,
177
182
  terminalConfigurations: tc,
178
183
  suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
179
184
  deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("NANA")
180
- })
185
+ }),
186
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
187
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
181
188
  });
182
189
  }
183
190
 
@@ -30,6 +30,8 @@ import {JBBeforePayRecordedContext} from "@bananapus/core-v6/src/structs/JBBefor
30
30
  import {JBPayHookSpecification} from "@bananapus/core-v6/src/structs/JBPayHookSpecification.sol";
31
31
  import {JBTokenAmount} from "@bananapus/core-v6/src/structs/JBTokenAmount.sol";
32
32
  import {JBRuleset} from "@bananapus/core-v6/src/structs/JBRuleset.sol";
33
+ import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
34
+ import {REVCroptopAllowedPost} from "../src/structs/REVCroptopAllowedPost.sol";
33
35
 
34
36
  /// @notice Tests for PR #22: fix/c2-hook-array-oob
35
37
  /// Verifies that the fix for the hook array out-of-bounds bug works correctly.
@@ -59,8 +61,9 @@ contract TestHookArrayOOB is TestBaseWorkflow {
59
61
  FEE_PROJECT_ID = jbProjects().createFor(multisig());
60
62
  SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
61
63
  HOOK_STORE = new JB721TiersHookStore();
62
- EXAMPLE_HOOK =
63
- new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
64
+ EXAMPLE_HOOK = new JB721TiersHook(
65
+ jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
66
+ );
64
67
  ADDRESS_REGISTRY = new JBAddressRegistry();
65
68
  HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
66
69
  PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
@@ -139,7 +142,9 @@ contract TestHookArrayOOB is TestBaseWorkflow {
139
142
  revnetId: FEE_PROJECT_ID,
140
143
  configuration: feeCfg,
141
144
  terminalConfigurations: feeTc,
142
- suckerDeploymentConfiguration: feeSdc
145
+ suckerDeploymentConfiguration: feeSdc,
146
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
147
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
143
148
  });
144
149
 
145
150
  // Deploy a new test revnet (revnetId: 0 = create new)
@@ -148,8 +153,13 @@ contract TestHookArrayOOB is TestBaseWorkflow {
148
153
  // Use a different salt so the ERC20 deploy doesn't clash
149
154
  cfg.description = REVDescription("Test2", "TS2", "ipfs://test2", "TEST_SALT_2");
150
155
 
151
- revnetId = REV_DEPLOYER.deployFor({
152
- revnetId: 0, configuration: cfg, terminalConfigurations: tc, suckerDeploymentConfiguration: sdc
156
+ (revnetId,) = REV_DEPLOYER.deployFor({
157
+ revnetId: 0,
158
+ configuration: cfg,
159
+ terminalConfigurations: tc,
160
+ suckerDeploymentConfiguration: sdc,
161
+ tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
162
+ allowedPosts: REVEmpty721Config.emptyAllowedPosts()
153
163
  });
154
164
  }
155
165
 
@@ -190,8 +200,8 @@ contract TestHookArrayOOB is TestBaseWorkflow {
190
200
  }
191
201
 
192
202
  /// @notice Test that beforePayRecordedWith returns correct hook specification counts.
193
- /// When no hooks are configured, should return empty array.
194
- function test_beforePayRecordedWith_noHooks_returnsEmptySpecs() public {
203
+ /// Every revnet now has both a buyback hook and a 721 hook.
204
+ function test_beforePayRecordedWith_returnsHookSpecs() public {
195
205
  uint256 revnetId = _deployFeeAndRevnet();
196
206
 
197
207
  // Build a mock context for beforePayRecordedWith
@@ -214,9 +224,8 @@ contract TestHookArrayOOB is TestBaseWorkflow {
214
224
 
215
225
  (uint256 weight, JBPayHookSpecification[] memory specs) = REV_DEPLOYER.beforePayRecordedWith(context);
216
226
 
217
- // With the global buyback hook but no 721 hook, weight should be the context weight
218
- // and specs should contain exactly the buyback hook specification.
227
+ // Every revnet has both the buyback hook and the 721 hook.
219
228
  assertEq(weight, context.weight, "Weight should be the default context weight");
220
- assertEq(specs.length, 1, "Should have exactly the buyback hook specification");
229
+ assertEq(specs.length, 2, "Should have buyback hook and 721 hook specifications");
221
230
  }
222
231
  }