@rev-net/core-v6 0.0.56 → 0.0.58

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rev-net/core-v6",
3
- "version": "0.0.56",
3
+ "version": "0.0.58",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -26,14 +26,14 @@
26
26
  "artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'revnet-core-v6'"
27
27
  },
28
28
  "dependencies": {
29
- "@bananapus/721-hook-v6": "^0.0.50",
30
- "@bananapus/buyback-hook-v6": "^0.0.46",
31
- "@bananapus/core-v6": "^0.0.53",
32
- "@bananapus/ownable-v6": "^0.0.24",
29
+ "@bananapus/721-hook-v6": "^0.0.51",
30
+ "@bananapus/buyback-hook-v6": "^0.0.47",
31
+ "@bananapus/core-v6": "^0.0.54",
32
+ "@bananapus/ownable-v6": "^0.0.25",
33
33
  "@bananapus/permission-ids-v6": "^0.0.25",
34
- "@bananapus/router-terminal-v6": "^0.0.43",
35
- "@bananapus/suckers-v6": "^0.0.46",
36
- "@croptop/core-v6": "^0.0.42",
34
+ "@bananapus/router-terminal-v6": "^0.0.44",
35
+ "@bananapus/suckers-v6": "^0.0.47",
36
+ "@croptop/core-v6": "^0.0.48",
37
37
  "@openzeppelin/contracts": "5.6.1",
38
38
  "@uniswap/permit2": "github:Uniswap/permit2#cc56ad0f3439c502c246fc5cfcc3db92bb8b7219"
39
39
  },
@@ -67,54 +67,31 @@ contract DeployScript is Script, Sphinx {
67
67
  SuckerDeployment suckers;
68
68
  /// @notice tracks the deployment of the router terminal.
69
69
  RouterTerminalDeployment routerTerminal;
70
-
71
- // forge-lint: disable-next-line(mixed-case-variable)
72
- uint32 PREMINT_CHAIN_ID = 1;
73
- // forge-lint: disable-next-line(mixed-case-variable)
74
- string NAME = "Revnet";
75
- // forge-lint: disable-next-line(mixed-case-variable)
76
- string SYMBOL = "REV";
77
- // forge-lint: disable-next-line(mixed-case-variable)
78
- string PROJECT_URI = "ipfs://QmcCBD5fM927LjkLDSJWtNEU9FohcbiPSfqtGRHXFHzJ4W";
79
- // forge-lint: disable-next-line(mixed-case-variable)
80
- uint32 NATIVE_CURRENCY = uint32(uint160(JBConstants.NATIVE_TOKEN));
81
- // forge-lint: disable-next-line(mixed-case-variable)
82
- uint32 ETH_CURRENCY = JBCurrencyIds.ETH;
83
- // forge-lint: disable-next-line(mixed-case-variable)
84
- uint8 DECIMALS = 18;
85
- // forge-lint: disable-next-line(mixed-case-variable)
86
- uint256 DECIMAL_MULTIPLIER = 10 ** DECIMALS;
87
- // forge-lint: disable-next-line(mixed-case-variable)
88
- bytes32 ERC20_SALT = "_REV_ERC20_SALT_V6_";
89
- // forge-lint: disable-next-line(mixed-case-variable)
90
- bytes32 SUCKER_SALT = "_REV_SUCKER_SALT_V6_";
91
- // forge-lint: disable-next-line(mixed-case-variable)
92
- bytes32 DEPLOYER_SALT = "_REV_DEPLOYER_SALT_V6_";
93
- // forge-lint: disable-next-line(mixed-case-variable)
94
- bytes32 REVLOANS_SALT = "_REV_LOANS_SALT_V6_";
95
- // forge-lint: disable-next-line(mixed-case-variable)
96
- bytes32 REVOWNER_SALT = "_REV_OWNER_SALT_V6_";
97
- // forge-lint: disable-next-line(mixed-case-variable)
98
- address LOANS_OWNER;
99
- // forge-lint: disable-next-line(mixed-case-variable)
100
- address OPERATOR;
101
- // forge-lint: disable-next-line(mixed-case-variable)
102
- address TRUSTED_FORWARDER;
103
- // forge-lint: disable-next-line(mixed-case-variable)
104
- IPermit2 PERMIT2;
105
- // forge-lint: disable-next-line(mixed-case-variable)
106
- uint48 REV_START_TIME = 1_740_089_444;
107
- // forge-lint: disable-next-line(mixed-case-variable)
108
- uint104 REV_MAINNET_AUTO_ISSUANCE_ = 1_050_482_341_387_116_262_330_122;
109
- // forge-lint: disable-next-line(mixed-case-variable)
110
- uint104 REV_BASE_AUTO_ISSUANCE_ = 38_544_322_230_437_559_731_228;
111
- // forge-lint: disable-next-line(mixed-case-variable)
112
- uint104 REV_OP_AUTO_ISSUANCE_ = 32_069_388_242_375_817_844;
113
- // forge-lint: disable-next-line(mixed-case-variable)
114
- uint104 REV_ARB_AUTO_ISSUANCE_ = 3_479_431_776_906_850_000_000;
70
+ uint32 private constant _PREMINT_CHAIN_ID = 1;
71
+ string private constant _NAME = "Revnet";
72
+ string private constant _SYMBOL = "REV";
73
+ string private constant _PROJECT_URI = "ipfs://QmcCBD5fM927LjkLDSJWtNEU9FohcbiPSfqtGRHXFHzJ4W";
74
+ uint32 private constant _NATIVE_CURRENCY = uint32(uint160(JBConstants.NATIVE_TOKEN));
75
+ uint32 private constant _ETH_CURRENCY = JBCurrencyIds.ETH;
76
+ uint8 private constant _DECIMALS = 18;
77
+ uint256 private constant _DECIMAL_MULTIPLIER = 10 ** _DECIMALS;
78
+ bytes32 private constant _ERC20_SALT = "_REV_ERC20_SALT_V6_";
79
+ bytes32 private constant _SUCKER_SALT = "_REV_SUCKER_SALT_V6_";
80
+ bytes32 private constant _DEPLOYER_SALT = "_REV_DEPLOYER_SALT_V6_";
81
+ bytes32 private constant _REVLOANS_SALT = "_REV_LOANS_SALT_V6_";
82
+ bytes32 private constant _REVOWNER_SALT = "_REV_OWNER_SALT_V6_";
83
+ address private loansOwner;
84
+ address private operator;
85
+ address private trustedForwarder;
86
+ IPermit2 private permit2;
87
+ uint48 private constant _REV_START_TIME = 1_740_089_444;
88
+ uint104 private constant _REV_MAINNET_AUTO_ISSUANCE = 1_050_482_341_387_116_262_330_122;
89
+ uint104 private constant _REV_BASE_AUTO_ISSUANCE = 38_544_322_230_437_559_731_228;
90
+ uint104 private constant _REV_OP_AUTO_ISSUANCE = 32_069_388_242_375_817_844;
91
+ uint104 private constant _REV_ARB_AUTO_ISSUANCE = 3_479_431_776_906_850_000_000;
115
92
 
116
93
  function configureSphinx() public override {
117
- // TODO: Update to contain revnet devs.
94
+ // Safe owners and threshold are resolved by the Sphinx project config.
118
95
  sphinxConfig.projectName = "revnet-core-v6";
119
96
  sphinxConfig.mainnets = ["ethereum", "optimism", "base", "arbitrum"];
120
97
  sphinxConfig.testnets = ["ethereum_sepolia", "optimism_sepolia", "base_sepolia", "arbitrum_sepolia"];
@@ -122,9 +99,9 @@ contract DeployScript is Script, Sphinx {
122
99
 
123
100
  function run() public {
124
101
  // Get the operator address.
125
- OPERATOR = safeAddress();
102
+ operator = safeAddress();
126
103
  // Get the loans owner address.
127
- LOANS_OWNER = safeAddress();
104
+ loansOwner = safeAddress();
128
105
 
129
106
  // Get the deployment addresses for the nana CORE for this chain.
130
107
  // We want to do this outside of the `sphinx` modifier.
@@ -169,8 +146,8 @@ contract DeployScript is Script, Sphinx {
169
146
  );
170
147
 
171
148
  // We use the same trusted forwarder and permit2 as the core deployment.
172
- TRUSTED_FORWARDER = core.controller.trustedForwarder();
173
- PERMIT2 = core.terminal.PERMIT2();
149
+ trustedForwarder = core.controller.trustedForwarder();
150
+ permit2 = core.terminal.PERMIT2();
174
151
 
175
152
  // Perform the deployment transactions.
176
153
  deploy();
@@ -182,7 +159,7 @@ contract DeployScript is Script, Sphinx {
182
159
 
183
160
  // Accept the chain's native currency through the multi terminal.
184
161
  accountingContextsToAccept[0] =
185
- JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: DECIMALS, currency: NATIVE_CURRENCY});
162
+ JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: _DECIMALS, currency: _NATIVE_CURRENCY});
186
163
 
187
164
  // The terminals that the project will accept funds through.
188
165
  JBTerminalConfig[] memory terminalConfigurations = new JBTerminalConfig[](2);
@@ -202,25 +179,25 @@ contract DeployScript is Script, Sphinx {
202
179
  preferAddToBalance: false,
203
180
  percent: JBConstants.SPLITS_TOTAL_PERCENT,
204
181
  projectId: 0,
205
- beneficiary: payable(OPERATOR),
182
+ beneficiary: payable(operator),
206
183
  lockedUntil: 0,
207
184
  hook: IJBSplitHook(address(0))
208
185
  });
209
186
 
210
187
  {
211
188
  REVAutoIssuance[] memory issuanceConfs = new REVAutoIssuance[](4);
212
- issuanceConfs[0] = REVAutoIssuance({chainId: 1, count: REV_MAINNET_AUTO_ISSUANCE_, beneficiary: OPERATOR});
213
- issuanceConfs[1] = REVAutoIssuance({chainId: 8453, count: REV_BASE_AUTO_ISSUANCE_, beneficiary: OPERATOR});
214
- issuanceConfs[2] = REVAutoIssuance({chainId: 10, count: REV_OP_AUTO_ISSUANCE_, beneficiary: OPERATOR});
215
- issuanceConfs[3] = REVAutoIssuance({chainId: 42_161, count: REV_ARB_AUTO_ISSUANCE_, beneficiary: OPERATOR});
189
+ issuanceConfs[0] = REVAutoIssuance({chainId: 1, count: _REV_MAINNET_AUTO_ISSUANCE, beneficiary: operator});
190
+ issuanceConfs[1] = REVAutoIssuance({chainId: 8453, count: _REV_BASE_AUTO_ISSUANCE, beneficiary: operator});
191
+ issuanceConfs[2] = REVAutoIssuance({chainId: 10, count: _REV_OP_AUTO_ISSUANCE, beneficiary: operator});
192
+ issuanceConfs[3] = REVAutoIssuance({chainId: 42_161, count: _REV_ARB_AUTO_ISSUANCE, beneficiary: operator});
216
193
 
217
194
  stageConfigurations[0] = REVStageConfig({
218
- startsAtOrAfter: REV_START_TIME,
195
+ startsAtOrAfter: _REV_START_TIME,
219
196
  autoIssuances: issuanceConfs,
220
197
  splitPercent: 3800, // 38%
221
198
  splits: splits,
222
199
  // forge-lint: disable-next-line(unsafe-typecast)
223
- initialIssuance: uint112(10_000 * DECIMAL_MULTIPLIER),
200
+ initialIssuance: uint112(10_000 * _DECIMAL_MULTIPLIER),
224
201
  issuanceCutFrequency: 90 days,
225
202
  issuanceCutPercent: 380_000_000, // 38%
226
203
  cashOutTaxRate: 1000, // 0.1
@@ -232,10 +209,10 @@ contract DeployScript is Script, Sphinx {
232
209
  REVAutoIssuance[] memory issuanceConfs = new REVAutoIssuance[](1);
233
210
  issuanceConfs[0] = REVAutoIssuance({
234
211
  // forge-lint: disable-next-line(unsafe-typecast)
235
- chainId: PREMINT_CHAIN_ID,
212
+ chainId: _PREMINT_CHAIN_ID,
236
213
  // forge-lint: disable-next-line(unsafe-typecast)
237
- count: uint104(1_550_000 * DECIMAL_MULTIPLIER),
238
- beneficiary: OPERATOR
214
+ count: uint104(1_550_000 * _DECIMAL_MULTIPLIER),
215
+ beneficiary: operator
239
216
  });
240
217
 
241
218
  stageConfigurations[1] = REVStageConfig({
@@ -265,9 +242,9 @@ contract DeployScript is Script, Sphinx {
265
242
 
266
243
  // The project's revnet configuration
267
244
  REVConfig memory revnetConfiguration = REVConfig({
268
- description: REVDescription({name: NAME, ticker: SYMBOL, uri: PROJECT_URI, salt: ERC20_SALT}),
269
- baseCurrency: ETH_CURRENCY,
270
- operator: OPERATOR,
245
+ description: REVDescription({name: _NAME, ticker: _SYMBOL, uri: _PROJECT_URI, salt: _ERC20_SALT}),
246
+ baseCurrency: _ETH_CURRENCY,
247
+ operator: operator,
271
248
  scopeCashOutsToLocalBalances: false,
272
249
  stageConfigurations: stageConfigurations
273
250
  });
@@ -314,7 +291,7 @@ contract DeployScript is Script, Sphinx {
314
291
  }
315
292
  // Specify all sucker deployments.
316
293
  suckerDeploymentConfiguration =
317
- REVSuckerDeploymentConfig({deployerConfigurations: suckerDeployerConfigurations, salt: SUCKER_SALT});
294
+ REVSuckerDeploymentConfig({deployerConfigurations: suckerDeployerConfigurations, salt: _SUCKER_SALT});
318
295
  }
319
296
 
320
297
  return FeeProjectConfig({
@@ -329,7 +306,7 @@ contract DeployScript is Script, Sphinx {
329
306
  tokenUriResolver: IJB721TokenUriResolver(address(0)),
330
307
  contractUri: "",
331
308
  tiersConfig: JB721InitTiersConfig({
332
- tiers: new JB721TierConfig[](0), currency: ETH_CURRENCY, decimals: 18
309
+ tiers: new JB721TierConfig[](0), currency: _ETH_CURRENCY, decimals: 18
333
310
  }),
334
311
  flags: REV721TiersHookFlags({
335
312
  noNewTiersWithReserves: false,
@@ -351,8 +328,7 @@ contract DeployScript is Script, Sphinx {
351
328
  function deploy() public sphinx {
352
329
  // Check if singletons are already deployed before creating a new fee project.
353
330
  // This prevents creating orphan projects on script restarts.
354
- // forge-lint: disable-next-line(mixed-case-variable)
355
- uint256 FEE_PROJECT_ID;
331
+ uint256 feeProjectId;
356
332
 
357
333
  // Predict the REVLoans address for an arbitrary fee project ID to check if it exists.
358
334
  // We can't predict without a fee project ID, so we first check the REVDeployer which also stores it.
@@ -360,7 +336,7 @@ contract DeployScript is Script, Sphinx {
360
336
 
361
337
  // First, check if REVLoans is already deployed by trying with project ID = 0 (placeholder).
362
338
  // We need to iterate: if either singleton exists, extract the fee project ID from it.
363
- // Since both encode FEE_PROJECT_ID, we check if any code exists at the predicted address
339
+ // Since both encode feeProjectId, we check if any code exists at the predicted address
364
340
  // for sequential project IDs starting from 1.
365
341
 
366
342
  // A simpler approach: predict the REVDeployer address for each possible fee project ID
@@ -385,10 +361,10 @@ contract DeployScript is Script, Sphinx {
385
361
  for (uint256 _candidateId = 1; _candidateId < _nextProjectId; _candidateId++) {
386
362
  // Predict REVLoans address for this candidate fee project ID.
387
363
  (address _candidateRevloansAddr, bool _candidateRevloansDeployed) = _isDeployed({
388
- salt: REVLOANS_SALT,
364
+ salt: _REVLOANS_SALT,
389
365
  creationCode: type(REVLoans).creationCode,
390
366
  arguments: abi.encode(
391
- core.controller, suckers.registry, _candidateId, LOANS_OWNER, PERMIT2, TRUSTED_FORWARDER
367
+ core.controller, suckers.registry, _candidateId, loansOwner, permit2, trustedForwarder
392
368
  )
393
369
  });
394
370
 
@@ -396,14 +372,14 @@ contract DeployScript is Script, Sphinx {
396
372
  // Verify the fee project ID matches by reading the immutable.
397
373
  if (REVLoans(payable(_candidateRevloansAddr)).REV_ID() == _candidateId) {
398
374
  // Record the fee project ID from the existing deployment.
399
- FEE_PROJECT_ID = _candidateId;
375
+ feeProjectId = _candidateId;
400
376
  // Record the existing REVLoans address.
401
377
  _existingRevloansAddr = _candidateRevloansAddr;
402
378
  _revloansExists = true;
403
379
 
404
380
  // Also predict and verify the owner.
405
381
  (_existingOwnerAddr, _revOwnerExists) = _isDeployed({
406
- salt: REVOWNER_SALT,
382
+ salt: _REVOWNER_SALT,
407
383
  creationCode: type(REVOwner).creationCode,
408
384
  arguments: abi.encode(
409
385
  IJBBuybackHookRegistry(address(buybackHook.registry)),
@@ -417,7 +393,7 @@ contract DeployScript is Script, Sphinx {
417
393
 
418
394
  // Also predict and verify the deployer.
419
395
  (_existingDeployerAddr, _revDeployerExists) = _isDeployed({
420
- salt: DEPLOYER_SALT,
396
+ salt: _DEPLOYER_SALT,
421
397
  creationCode: type(REVDeployer).creationCode,
422
398
  arguments: abi.encode(
423
399
  core.controller,
@@ -427,7 +403,7 @@ contract DeployScript is Script, Sphinx {
427
403
  croptop.publisher,
428
404
  IJBBuybackHookRegistry(address(buybackHook.registry)),
429
405
  _candidateRevloansAddr,
430
- TRUSTED_FORWARDER,
406
+ trustedForwarder,
431
407
  _existingOwnerAddr
432
408
  )
433
409
  });
@@ -445,91 +421,90 @@ contract DeployScript is Script, Sphinx {
445
421
  for (uint256 i = _nextProjectId - 1; i >= 1; i--) {
446
422
  if (core.projects.ownerOf(i) == safeAddress()) {
447
423
  if (address(core.controller.DIRECTORY().controllerOf(i)) == address(0)) {
448
- FEE_PROJECT_ID = i;
424
+ feeProjectId = i;
449
425
  _foundExisting = true;
450
426
  break;
451
427
  }
452
428
  }
453
429
  }
454
430
  if (!_foundExisting) {
455
- // forge-lint: disable-next-line(mixed-case-variable)
456
- FEE_PROJECT_ID = core.projects.createFor(safeAddress());
431
+ feeProjectId = core.projects.createFor(safeAddress());
457
432
  }
458
433
  }
459
434
 
460
435
  // Deploy REVLoans first — it only depends on the controller.
461
436
  REVLoans revloans = _revloansExists
462
437
  ? REVLoans(payable(_existingRevloansAddr))
463
- : new REVLoans{salt: REVLOANS_SALT}({
438
+ : new REVLoans{salt: _REVLOANS_SALT}({
464
439
  controller: core.controller,
465
440
  suckerRegistry: suckers.registry,
466
- revId: FEE_PROJECT_ID,
467
- owner: LOANS_OWNER,
468
- permit2: PERMIT2,
469
- trustedForwarder: TRUSTED_FORWARDER
441
+ revId: feeProjectId,
442
+ owner: loansOwner,
443
+ permit2: permit2,
444
+ trustedForwarder: trustedForwarder
470
445
  });
471
446
 
472
447
  // Deploy REVOwner — the runtime data hook that handles pay and cash out callbacks.
473
448
  REVOwner revOwner = _revOwnerExists
474
449
  ? REVOwner(_existingOwnerAddr)
475
- : new REVOwner{salt: REVOWNER_SALT}({
450
+ : new REVOwner{salt: _REVOWNER_SALT}({
476
451
  buybackHook: IJBBuybackHookRegistry(address(buybackHook.registry)),
477
452
  directory: core.controller.DIRECTORY(),
478
- feeRevnetId: FEE_PROJECT_ID,
453
+ feeRevnetId: feeProjectId,
479
454
  suckerRegistry: suckers.registry,
480
455
  loans: revloans,
481
- deployer: msg.sender
456
+ deployerAddress: msg.sender
482
457
  });
483
458
 
484
459
  // Deploy REVDeployer with the REVLoans, buyback hook, and REVOwner addresses.
485
460
  (address _deployerAddr, bool _deployerIsDeployed) = _revDeployerExists
486
461
  ? (_existingDeployerAddr, true)
487
462
  : _isDeployed({
488
- salt: DEPLOYER_SALT,
463
+ salt: _DEPLOYER_SALT,
489
464
  creationCode: type(REVDeployer).creationCode,
490
465
  arguments: abi.encode(
491
466
  core.controller,
492
467
  suckers.registry,
493
- FEE_PROJECT_ID,
468
+ feeProjectId,
494
469
  hook.hook_deployer,
495
470
  croptop.publisher,
496
471
  IJBBuybackHookRegistry(address(buybackHook.registry)),
497
472
  revloans,
498
- TRUSTED_FORWARDER,
473
+ trustedForwarder,
499
474
  address(revOwner)
500
475
  )
501
476
  });
502
- if (address(revOwner.DEPLOYER()) == address(0)) {
477
+ if (address(revOwner.deployer()) == address(0)) {
503
478
  revOwner.setDeployer(IREVDeployer(_deployerAddr));
504
479
  }
505
480
  REVDeployer _basicDeployer = _deployerIsDeployed
506
481
  ? REVDeployer(payable(_deployerAddr))
507
- : new REVDeployer{salt: DEPLOYER_SALT}({
482
+ : new REVDeployer{salt: _DEPLOYER_SALT}({
508
483
  controller: core.controller,
509
484
  suckerRegistry: suckers.registry,
510
- feeRevnetId: FEE_PROJECT_ID,
485
+ feeRevnetId: feeProjectId,
511
486
  hookDeployer: hook.hook_deployer,
512
487
  publisher: croptop.publisher,
513
488
  buybackHook: IJBBuybackHookRegistry(address(buybackHook.registry)),
514
489
  loans: revloans,
515
- trustedForwarder: TRUSTED_FORWARDER,
490
+ trustedForwarder: trustedForwarder,
516
491
  owner: address(revOwner)
517
492
  });
518
493
 
519
494
  // Only configure the fee project if it doesn't already have a controller.
520
495
  // This handles both fresh deploys and restarts where singletons exist but the fee project
521
496
  // was not yet configured.
522
- bool _feeProjectConfigured = address(core.controller.DIRECTORY().controllerOf(FEE_PROJECT_ID)) != address(0);
497
+ bool _feeProjectConfigured = address(core.controller.DIRECTORY().controllerOf(feeProjectId)) != address(0);
523
498
  if (!_feeProjectConfigured) {
524
499
  // Approve the basic deployer to configure the project.
525
- core.projects.approve({to: address(_basicDeployer), tokenId: FEE_PROJECT_ID});
500
+ core.projects.approve({to: address(_basicDeployer), tokenId: feeProjectId});
526
501
 
527
502
  // Build the config.
528
503
  FeeProjectConfig memory feeProjectConfig = getFeeProjectConfig();
529
504
 
530
505
  // Configure the project.
531
506
  _basicDeployer.deployFor({
532
- revnetId: FEE_PROJECT_ID,
507
+ revnetId: feeProjectId,
533
508
  configuration: feeProjectConfig.configuration,
534
509
  terminalConfigurations: feeProjectConfig.terminalConfigurations,
535
510
  suckerDeploymentConfiguration: feeProjectConfig.suckerDeploymentConfiguration,
@@ -10,8 +10,7 @@ import {IREVLoans} from "./../../src/interfaces/IREVLoans.sol";
10
10
  import {REVOwner} from "./../../src/REVOwner.sol";
11
11
 
12
12
  struct RevnetCoreDeployment {
13
- // forge-lint: disable-next-line(mixed-case-variable)
14
- IREVDeployer basic_deployer;
13
+ IREVDeployer basicDeployer;
15
14
  IREVLoans loans;
16
15
  REVOwner owner;
17
16
  }
@@ -23,17 +22,16 @@ library RevnetCoreDeploymentLib {
23
22
  Vm internal constant vm = Vm(VM_ADDRESS);
24
23
 
25
24
  function getDeployment(string memory path) internal returns (RevnetCoreDeployment memory deployment) {
26
- // get chainId for which we need to get the deployment.
25
+ // Match the current chain ID to the Sphinx network name used in deployment artifacts.
27
26
  uint256 chainId = block.chainid;
28
27
 
29
- // Deploy to get the constants.
30
- // TODO: get constants without deploy.
28
+ // `SphinxConstants` exposes Sphinx's supported chain ID to network name mapping.
31
29
  SphinxConstants sphinxConstants = new SphinxConstants();
32
30
  NetworkInfo[] memory networks = sphinxConstants.getNetworkInfoArray();
33
31
 
34
32
  for (uint256 _i; _i < networks.length; _i++) {
35
33
  if (networks[_i].chainId == chainId) {
36
- return getDeployment({path: path, network_name: networks[_i].name});
34
+ return getDeployment({path: path, networkName: networks[_i].name});
37
35
  }
38
36
  }
39
37
 
@@ -42,28 +40,27 @@ library RevnetCoreDeploymentLib {
42
40
 
43
41
  function getDeployment(
44
42
  string memory path,
45
- // forge-lint: disable-next-line(mixed-case-variable)
46
- string memory network_name
43
+ string memory networkName
47
44
  )
48
45
  internal
49
46
  view
50
47
  returns (RevnetCoreDeployment memory deployment)
51
48
  {
52
- deployment.basic_deployer = IREVDeployer(
49
+ deployment.basicDeployer = IREVDeployer(
53
50
  _getDeploymentAddress({
54
- path: path, project_name: "revnet-core-v6", network_name: network_name, contractName: "REVDeployer"
51
+ path: path, projectName: "revnet-core-v6", networkName: networkName, contractName: "REVDeployer"
55
52
  })
56
53
  );
57
54
 
58
55
  deployment.loans = IREVLoans(
59
56
  _getDeploymentAddress({
60
- path: path, project_name: "revnet-core-v6", network_name: network_name, contractName: "REVLoans"
57
+ path: path, projectName: "revnet-core-v6", networkName: networkName, contractName: "REVLoans"
61
58
  })
62
59
  );
63
60
 
64
61
  deployment.owner = REVOwner(
65
62
  _getDeploymentAddress({
66
- path: path, project_name: "revnet-core-v6", network_name: network_name, contractName: "REVOwner"
63
+ path: path, projectName: "revnet-core-v6", networkName: networkName, contractName: "REVOwner"
67
64
  })
68
65
  );
69
66
  }
@@ -75,10 +72,8 @@ library RevnetCoreDeploymentLib {
75
72
  /// @return The address of the contract.
76
73
  function _getDeploymentAddress(
77
74
  string memory path,
78
- // forge-lint: disable-next-line(mixed-case-variable)
79
- string memory project_name,
80
- // forge-lint: disable-next-line(mixed-case-variable)
81
- string memory network_name,
75
+ string memory projectName,
76
+ string memory networkName,
82
77
  string memory contractName
83
78
  )
84
79
  internal
@@ -87,7 +82,7 @@ library RevnetCoreDeploymentLib {
87
82
  {
88
83
  string memory deploymentJson =
89
84
  // forge-lint: disable-next-line(unsafe-cheatcode)
90
- vm.readFile(string.concat(path, project_name, "/", network_name, "/", contractName, ".json"));
85
+ vm.readFile(string.concat(path, projectName, "/", networkName, "/", contractName, ".json"));
91
86
  return stdJson.readAddress({json: deploymentJson, key: ".address"});
92
87
  }
93
88
  }
@@ -79,12 +79,6 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
79
79
  /// @dev 30 days, in seconds.
80
80
  uint256 public constant override CASH_OUT_DELAY = 2_592_000;
81
81
 
82
- /// @notice The cash out fee (as a fraction out of `JBConstants.MAX_FEE`).
83
- /// Cashout fees are paid to the revnet with the `FEE_REVNET_ID`.
84
- /// @dev Fees are charged on cashouts if the cash out tax rate is greater than 0%.
85
- /// @dev When suckers withdraw funds, they do not pay cash out fees.
86
- uint256 public constant override FEE = 25; // 2.5%
87
-
88
82
  /// @notice The default Uniswap pool fee tier used when auto-configuring buyback pools.
89
83
  /// @dev 10_000 = 1%. This is the standard fee tier for most project token pairs.
90
84
  uint24 public constant DEFAULT_BUYBACK_POOL_FEE = 10_000;
@@ -421,10 +415,12 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
421
415
  sqrtPriceX96 = uint160(1 << 96);
422
416
  } else if (normalizedTerminalToken < projectToken) {
423
417
  // token0 = terminal, token1 = project → price = issuance / terminalTokenUnit
424
- sqrtPriceX96 = uint160(sqrt(mulDiv(uint256(initialIssuance), 1 << 192, terminalTokenUnit)));
418
+ sqrtPriceX96 =
419
+ uint160(sqrt(mulDiv({x: uint256(initialIssuance), y: 1 << 192, denominator: terminalTokenUnit})));
425
420
  } else {
426
421
  // token0 = project, token1 = terminal → price = terminalTokenUnit / issuance
427
- sqrtPriceX96 = uint160(sqrt(mulDiv(terminalTokenUnit, 1 << 192, uint256(initialIssuance))));
422
+ sqrtPriceX96 =
423
+ uint160(sqrt(mulDiv({x: terminalTokenUnit, y: 1 << 192, denominator: uint256(initialIssuance)})));
428
424
  }
429
425
  }
430
426
 
package/src/REVLoans.sol CHANGED
@@ -747,6 +747,14 @@ contract REVLoans is ERC721, ERC2771Context, JBPermissioned, Ownable, IREVLoans
747
747
  address loanOwner = _ownerOf(loanId);
748
748
  _requirePermissionFrom({account: loanOwner, projectId: revnetId, permissionId: JBPermissionIds.REALLOCATE_LOAN});
749
749
 
750
+ // If the caller is adding fresh holder collateral on top of the reallocated amount, they must also have
751
+ // OPEN_LOAN permission for the loan owner: that fresh collateral comes from the owner's project-token
752
+ // balance and would otherwise let a REALLOCATE_LOAN-only operator open a brand-new loan against the owner's
753
+ // tokens through this entry point, bypassing the OPEN_LOAN gate that `borrowFrom` enforces.
754
+ if (collateralCountToAdd != 0) {
755
+ _requirePermissionFrom({account: loanOwner, projectId: revnetId, permissionId: JBPermissionIds.OPEN_LOAN});
756
+ }
757
+
750
758
  // Make sure the loan hasn't expired.
751
759
  // forge-lint: disable-next-line(block-timestamp)
752
760
  if (block.timestamp - _loanOf[loanId].createdAt > LOAN_LIQUIDATION_DURATION) {
package/src/REVOwner.sol CHANGED
@@ -9,6 +9,7 @@ import {IJBRulesetDataHook} from "@bananapus/core-v6/src/interfaces/IJBRulesetDa
9
9
  import {IJBTerminal} from "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
10
10
  import {JBCashOuts} from "@bananapus/core-v6/src/libraries/JBCashOuts.sol";
11
11
  import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
12
+ import {JBFees} from "@bananapus/core-v6/src/libraries/JBFees.sol";
12
13
  import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
13
14
  import {JBAfterCashOutRecordedContext} from "@bananapus/core-v6/src/structs/JBAfterCashOutRecordedContext.sol";
14
15
  import {JBBeforeCashOutRecordedContext} from "@bananapus/core-v6/src/structs/JBBeforeCashOutRecordedContext.sol";
@@ -46,15 +47,6 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook, IJBPeerChainAdjustedAcc
46
47
  error REVOwner_CashOutDelayNotFinished(uint256 cashOutDelay, uint256 blockTimestamp);
47
48
  error REVOwner_Unauthorized(address caller, address expectedCaller);
48
49
 
49
- //*********************************************************************//
50
- // ------------------------- public constants ------------------------ //
51
- //*********************************************************************//
52
-
53
- /// @notice The cash out fee (as a fraction out of `JBConstants.MAX_FEE`).
54
- /// @dev Cashout fees are paid to the revnet with the `FEE_REVNET_ID`.
55
- /// @dev When suckers withdraw funds, they do not pay cash out fees.
56
- uint256 public constant FEE = 25; // 2.5%
57
-
58
50
  //*********************************************************************//
59
51
  // --------------- public immutable stored properties ---------------- //
60
52
  //*********************************************************************//
@@ -89,7 +81,7 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook, IJBPeerChainAdjustedAcc
89
81
 
90
82
  /// @notice The deployer that manages revnet state.
91
83
  /// @dev Set once via `setDeployer()` using the precomputed canonical REVDeployer address.
92
- IREVDeployer public DEPLOYER;
84
+ IREVDeployer public deployer;
93
85
 
94
86
  //*********************************************************************//
95
87
  // -------------------- private stored properties -------------------- //
@@ -107,7 +99,7 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook, IJBPeerChainAdjustedAcc
107
99
  /// @param feeRevnetId The Juicebox project ID of the fee revnet.
108
100
  /// @param suckerRegistry The sucker registry.
109
101
  /// @param loans The loan contract.
110
- /// @param deployer The account allowed to bind the canonical deployer via `setDeployer`. Passed explicitly
102
+ /// @param deployerAddress The account allowed to bind the canonical deployer via `setDeployer`. Passed explicitly
111
103
  /// because CREATE2 deployments set `msg.sender` to the factory, not the intended operator.
112
104
  constructor(
113
105
  IJBBuybackHookRegistry buybackHook,
@@ -115,14 +107,14 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook, IJBPeerChainAdjustedAcc
115
107
  uint256 feeRevnetId,
116
108
  IJBSuckerRegistry suckerRegistry,
117
109
  IREVLoans loans,
118
- address deployer
110
+ address deployerAddress
119
111
  ) {
120
112
  BUYBACK_HOOK = buybackHook;
121
113
  DIRECTORY = directory;
122
114
  FEE_REVNET_ID = feeRevnetId;
123
115
  SUCKER_REGISTRY = suckerRegistry;
124
116
  LOANS = loans;
125
- _DEPLOYER = deployer;
117
+ _DEPLOYER = deployerAddress;
126
118
  }
127
119
 
128
120
  //*********************************************************************//
@@ -219,7 +211,7 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook, IJBPeerChainAdjustedAcc
219
211
  // a better price. This is by design.
220
212
  // Micro cash outs (< 40 wei at 2.5% fee) round feeCashOutCount to zero, bypassing the fee.
221
213
  // Economically insignificant: the gas cost of the transaction far exceeds the bypassed fee. No fix needed.
222
- uint256 feeCashOutCount = mulDiv({x: context.cashOutCount, y: FEE, denominator: JBConstants.MAX_FEE});
214
+ uint256 feeCashOutCount = JBFees.standardFeeAmountFrom(context.cashOutCount);
223
215
  uint256 nonFeeCashOutCount = context.cashOutCount - feeCashOutCount;
224
216
 
225
217
  // Compute the gross (effective-surplus) reclaim and fee amounts. The bonding curve uses cross-chain effective
@@ -516,14 +508,14 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook, IJBPeerChainAdjustedAcc
516
508
  /// @dev The deployer address is precomputed and supplied by the account that created this REVOwner instance.
517
509
  /// Only that deploy-time binder may call this, which avoids an ambient public initializer where any first caller
518
510
  /// could seize the deployer role before the deterministic REVDeployer is actually deployed.
519
- /// @param deployer The canonical REVDeployer instance that will manage revnet runtime state.
520
- function setDeployer(IREVDeployer deployer) external {
511
+ /// @param newDeployer The canonical REVDeployer instance that will manage revnet runtime state.
512
+ function setDeployer(IREVDeployer newDeployer) external {
521
513
  // Only the account that deployed this REVOwner may complete the one-time deployer binding.
522
514
  if (msg.sender != _DEPLOYER) revert REVOwner_Unauthorized({caller: msg.sender, expectedCaller: _DEPLOYER});
523
515
  // Prevent the deployer binding from being overwritten after initialization.
524
- if (address(DEPLOYER) != address(0)) revert REVOwner_AlreadyInitialized({deployer: address(DEPLOYER)});
516
+ if (address(deployer) != address(0)) revert REVOwner_AlreadyInitialized({deployer: address(deployer)});
525
517
  // Store the canonical REVDeployer that is authorized to manage runtime hook state.
526
- DEPLOYER = deployer;
518
+ deployer = newDeployer;
527
519
  }
528
520
 
529
521
  /// @notice Store the cash out delay for a revnet.
@@ -531,8 +523,8 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook, IJBPeerChainAdjustedAcc
531
523
  /// @param revnetId The ID of the revnet.
532
524
  /// @param cashOutDelay The timestamp after which cash outs are allowed.
533
525
  function setCashOutDelayOf(uint256 revnetId, uint256 cashOutDelay) external {
534
- if (msg.sender != address(DEPLOYER)) {
535
- revert REVOwner_Unauthorized({caller: msg.sender, expectedCaller: address(DEPLOYER)});
526
+ if (msg.sender != address(deployer)) {
527
+ revert REVOwner_Unauthorized({caller: msg.sender, expectedCaller: address(deployer)});
536
528
  }
537
529
  cashOutDelayOf[revnetId] = cashOutDelay;
538
530
  }
@@ -542,8 +534,8 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook, IJBPeerChainAdjustedAcc
542
534
  /// @param revnetId The ID of the revnet.
543
535
  /// @param hook The tiered ERC-721 hook.
544
536
  function setTiered721HookOf(uint256 revnetId, IJB721TiersHook hook) external {
545
- if (msg.sender != address(DEPLOYER)) {
546
- revert REVOwner_Unauthorized({caller: msg.sender, expectedCaller: address(DEPLOYER)});
537
+ if (msg.sender != address(deployer)) {
538
+ revert REVOwner_Unauthorized({caller: msg.sender, expectedCaller: address(deployer)});
547
539
  }
548
540
  tiered721HookOf[revnetId] = hook;
549
541
  }
@@ -119,10 +119,6 @@ interface IREVDeployer {
119
119
  /// @return The directory contract.
120
120
  function DIRECTORY() external view returns (IJBDirectory);
121
121
 
122
- /// @notice The cash out fee as a fraction out of `JBConstants.MAX_FEE`.
123
- /// @return The fee value.
124
- function FEE() external view returns (uint256);
125
-
126
122
  /// @notice The Juicebox project ID of the revnet that receives cash out fees.
127
123
  /// @return The fee revnet ID.
128
124
  function FEE_REVNET_ID() external view returns (uint256);
@@ -11,6 +11,6 @@ interface IREVOwner {
11
11
  function cashOutDelayOf(uint256 revnetId) external view returns (uint256);
12
12
 
13
13
  /// @notice Bind the canonical deployer exactly once.
14
- /// @param deployer The revnet deployer instance.
15
- function setDeployer(IREVDeployer deployer) external;
14
+ /// @param newDeployer The revnet deployer instance.
15
+ function setDeployer(IREVDeployer newDeployer) external;
16
16
  }