@rev-net/core-v6 0.0.29 → 0.0.30
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/ADMINISTRATION.md +19 -9
- package/ARCHITECTURE.md +3 -0
- package/AUDIT_INSTRUCTIONS.md +11 -1
- package/CHANGELOG.md +26 -0
- package/README.md +1 -0
- package/RISKS.md +28 -4
- package/SKILLS.md +2 -1
- package/USER_JOURNEYS.md +17 -3
- package/package.json +2 -2
- package/references/operations.md +1 -1
- package/script/Deploy.s.sol +25 -6
- package/src/REVHiddenTokens.sol +149 -0
- package/src/REVLoans.sol +115 -144
- package/src/REVOwner.sol +11 -3
- package/src/interfaces/IREVHiddenTokens.sol +53 -0
- package/src/interfaces/IREVLoans.sol +3 -6
- package/test/REV.integrations.t.sol +2 -1
- package/test/REVAutoIssuanceFuzz.t.sol +2 -1
- package/test/REVDeployerRegressions.t.sol +2 -2
- package/test/REVInvincibility.t.sol +6 -6
- package/test/REVInvincibilityHandler.sol +1 -1
- package/test/REVLifecycle.t.sol +2 -2
- package/test/REVLoans.invariants.t.sol +3 -3
- package/test/REVLoansAttacks.t.sol +7 -6
- package/test/REVLoansFeeRecovery.t.sol +12 -12
- package/test/REVLoansFindings.t.sol +4 -4
- package/test/REVLoansRegressions.t.sol +3 -3
- package/test/REVLoansSourceFeeRecovery.t.sol +4 -4
- package/test/REVLoansSourced.t.sol +48 -24
- package/test/REVLoansUnSourced.t.sol +3 -3
- package/test/TestBurnHeldTokens.t.sol +2 -2
- package/test/TestCEIPattern.t.sol +7 -6
- package/test/TestCashOutCallerValidation.t.sol +2 -2
- package/test/TestConversionDocumentation.t.sol +2 -2
- package/test/TestCrossCurrencyReclaim.t.sol +2 -2
- package/test/TestCrossSourceReallocation.t.sol +3 -3
- package/test/TestERC2771MetaTx.t.sol +6 -4
- package/test/TestEmptyBuybackSpecs.t.sol +2 -2
- package/test/TestFlashLoanSurplus.t.sol +3 -3
- package/test/TestHiddenTokens.t.sol +420 -0
- package/test/TestHookArrayOOB.t.sol +2 -2
- package/test/TestLiquidationBehavior.t.sol +4 -4
- package/test/TestLoanSourceRotation.t.sol +8 -6
- package/test/TestLoansCashOutDelay.t.sol +6 -6
- package/test/TestLongTailEconomics.t.sol +2 -2
- package/test/TestLowFindings.t.sol +13 -8
- package/test/TestMixedFixes.t.sol +7 -7
- package/test/TestPermit2Signatures.t.sol +3 -3
- package/test/TestReallocationSandwich.t.sol +4 -3
- package/test/TestRevnetRegressions.t.sol +3 -4
- package/test/TestSplitWeightAdjustment.t.sol +4 -3
- package/test/TestSplitWeightE2E.t.sol +4 -3
- package/test/TestSplitWeightFork.t.sol +2 -2
- package/test/TestStageTransitionBorrowable.t.sol +2 -2
- package/test/TestSwapTerminalPermission.t.sol +2 -2
- package/test/TestUint112Overflow.t.sol +3 -3
- package/test/TestZeroAmountLoanGuard.t.sol +3 -3
- package/test/TestZeroRepayment.t.sol +3 -3
- package/test/audit/LoanIdOverflowGuard.t.sol +4 -4
- package/test/audit/NemesisOperatorDelegation.t.sol +278 -0
- package/test/fork/ForkTestBase.sol +4 -3
- package/test/fork/TestLoanBorrowFork.t.sol +2 -1
- package/test/fork/TestLoanERC20Fork.t.sol +4 -2
- package/test/fork/TestLoanTransferFork.t.sol +12 -2
- package/test/helpers/MaliciousContracts.sol +1 -1
- package/test/regression/TestBurnPermissionRequired.t.sol +4 -4
- package/test/regression/TestCashOutBuybackFeeLeak.t.sol +2 -2
- package/test/regression/TestCrossRevnetLiquidation.t.sol +2 -2
- package/test/regression/TestCumulativeLoanCounter.t.sol +3 -3
- package/test/regression/TestLiquidateGapHandling.t.sol +3 -3
- package/test/regression/TestZeroPriceFeed.t.sol +5 -5
package/src/REVLoans.sol
CHANGED
|
@@ -4,10 +4,12 @@ pragma solidity 0.8.28;
|
|
|
4
4
|
import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
|
|
5
5
|
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
6
6
|
import {IJBPayoutTerminal} from "@bananapus/core-v6/src/interfaces/IJBPayoutTerminal.sol";
|
|
7
|
+
import {IJBPermissioned} from "@bananapus/core-v6/src/interfaces/IJBPermissioned.sol";
|
|
8
|
+
import {JBPermissioned} from "@bananapus/core-v6/src/abstract/JBPermissioned.sol";
|
|
7
9
|
import {IJBPrices} from "@bananapus/core-v6/src/interfaces/IJBPrices.sol";
|
|
8
|
-
import {IJBProjects} from "@bananapus/core-v6/src/interfaces/IJBProjects.sol";
|
|
9
10
|
import {IJBTerminal} from "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
|
|
10
11
|
import {IJBTokenUriResolver} from "@bananapus/core-v6/src/interfaces/IJBTokenUriResolver.sol";
|
|
12
|
+
import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
|
|
11
13
|
import {JBCashOuts} from "@bananapus/core-v6/src/libraries/JBCashOuts.sol";
|
|
12
14
|
import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
13
15
|
import {JBFees} from "@bananapus/core-v6/src/libraries/JBFees.sol";
|
|
@@ -45,7 +47,7 @@ import {REVLoanSource} from "./structs/REVLoanSource.sol";
|
|
|
45
47
|
/// cannot be
|
|
46
48
|
/// recouped.
|
|
47
49
|
/// @dev The loaned amounts include the fees taken, meaning the amount paid back is the amount borrowed plus the fees.
|
|
48
|
-
contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
50
|
+
contract REVLoans is ERC721, ERC2771Context, JBPermissioned, Ownable, IREVLoans {
|
|
49
51
|
// A library that parses the packed ruleset metadata into a friendlier format.
|
|
50
52
|
using JBRulesetMetadataResolver for JBRuleset;
|
|
51
53
|
|
|
@@ -71,7 +73,6 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
71
73
|
error REVLoans_PermitAllowanceNotEnough(uint256 allowanceAmount, uint256 requiredAmount);
|
|
72
74
|
error REVLoans_ReallocatingMoreCollateralThanBorrowedAmountAllows(uint256 newBorrowAmount, uint256 loanAmount);
|
|
73
75
|
error REVLoans_SourceMismatch();
|
|
74
|
-
error REVLoans_Unauthorized(address caller, address owner);
|
|
75
76
|
error REVLoans_UnderMinBorrowAmount(uint256 minBorrowAmount, uint256 borrowAmount);
|
|
76
77
|
error REVLoans_ZeroBorrowAmount();
|
|
77
78
|
error REVLoans_ZeroCollateralLoanIsInvalid();
|
|
@@ -120,9 +121,6 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
120
121
|
/// @notice A contract that stores prices for each revnet.
|
|
121
122
|
IJBPrices public immutable override PRICES;
|
|
122
123
|
|
|
123
|
-
/// @notice Mints ERC-721s that represent revnet ownership and transfers.
|
|
124
|
-
IJBProjects public immutable override PROJECTS;
|
|
125
|
-
|
|
126
124
|
/// @notice The ID of the REV revnet that will receive the fees.
|
|
127
125
|
uint256 public immutable override REV_ID;
|
|
128
126
|
|
|
@@ -182,14 +180,12 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
182
180
|
//*********************************************************************//
|
|
183
181
|
|
|
184
182
|
/// @param controller The controller that manages revnets using this loans contract.
|
|
185
|
-
/// @param projects The contract that mints ERC-721s representing project ownership.
|
|
186
183
|
/// @param revId The ID of the REV revnet that will receive the fees.
|
|
187
184
|
/// @param owner The owner of the contract that can set the URI resolver.
|
|
188
185
|
/// @param permit2 A permit2 utility.
|
|
189
186
|
/// @param trustedForwarder A trusted forwarder of transactions to this contract.
|
|
190
187
|
constructor(
|
|
191
188
|
IJBController controller,
|
|
192
|
-
IJBProjects projects,
|
|
193
189
|
uint256 revId,
|
|
194
190
|
address owner,
|
|
195
191
|
IPermit2 permit2,
|
|
@@ -197,12 +193,12 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
197
193
|
)
|
|
198
194
|
ERC721("REV Loans", "$REVLOAN")
|
|
199
195
|
ERC2771Context(trustedForwarder)
|
|
196
|
+
JBPermissioned(IJBPermissioned(address(controller)).PERMISSIONS())
|
|
200
197
|
Ownable(owner)
|
|
201
198
|
{
|
|
202
199
|
CONTROLLER = controller;
|
|
203
200
|
DIRECTORY = controller.DIRECTORY();
|
|
204
201
|
PRICES = controller.PRICES();
|
|
205
|
-
PROJECTS = projects;
|
|
206
202
|
REV_ID = revId;
|
|
207
203
|
PERMIT2 = permit2;
|
|
208
204
|
}
|
|
@@ -228,8 +224,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
228
224
|
returns (uint256)
|
|
229
225
|
{
|
|
230
226
|
// Cache the current ruleset once — used by both _cashOutDelayOf and _borrowableAmountFrom.
|
|
231
|
-
|
|
232
|
-
(JBRuleset memory currentRuleset,) = CONTROLLER.currentRulesetOf(revnetId);
|
|
227
|
+
JBRuleset memory currentRuleset = _currentRulesetOf(revnetId);
|
|
233
228
|
|
|
234
229
|
// If the cash out delay hasn't passed yet, no amount is borrowable.
|
|
235
230
|
if (_cashOutDelayOf({revnetId: revnetId, currentRuleset: currentRuleset}) > block.timestamp) return 0;
|
|
@@ -239,7 +234,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
239
234
|
collateralCount: collateralCount,
|
|
240
235
|
decimals: decimals,
|
|
241
236
|
currency: currency,
|
|
242
|
-
terminals:
|
|
237
|
+
terminals: _terminalsOf(revnetId),
|
|
243
238
|
currentStage: currentRuleset
|
|
244
239
|
});
|
|
245
240
|
}
|
|
@@ -295,32 +290,6 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
295
290
|
// -------------------------- internal views ------------------------- //
|
|
296
291
|
//*********************************************************************//
|
|
297
292
|
|
|
298
|
-
/// @notice Returns the cash out delay timestamp for a revnet by resolving the data hook from the current ruleset.
|
|
299
|
-
/// @param revnetId The ID of the revnet.
|
|
300
|
-
/// @return The cash out delay timestamp. Returns 0 if no data hook is set or no delay exists.
|
|
301
|
-
function _cashOutDelayOf(uint256 revnetId) internal view returns (uint256) {
|
|
302
|
-
// Get the revnet's current ruleset to find its data hook (the REVOwner contract).
|
|
303
|
-
// slither-disable-next-line unused-return
|
|
304
|
-
(JBRuleset memory currentRuleset,) = CONTROLLER.currentRulesetOf(revnetId);
|
|
305
|
-
|
|
306
|
-
return _cashOutDelayOf({revnetId: revnetId, currentRuleset: currentRuleset});
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/// @notice Returns the cash out delay timestamp using a pre-fetched ruleset (avoids redundant external call).
|
|
310
|
-
/// @param revnetId The ID of the revnet.
|
|
311
|
-
/// @param currentRuleset The pre-fetched current ruleset.
|
|
312
|
-
/// @return The cash out delay timestamp. Returns 0 if no data hook is set or no delay exists.
|
|
313
|
-
function _cashOutDelayOf(uint256 revnetId, JBRuleset memory currentRuleset) internal view returns (uint256) {
|
|
314
|
-
// Extract the data hook address from the ruleset's packed metadata.
|
|
315
|
-
address dataHook = currentRuleset.dataHook();
|
|
316
|
-
|
|
317
|
-
// If there's no data hook, this isn't a revnet — no cash out delay applies.
|
|
318
|
-
if (dataHook == address(0)) return 0;
|
|
319
|
-
|
|
320
|
-
// Read the cash out delay from the REVOwner contract (the data hook).
|
|
321
|
-
return IREVOwner(dataHook).cashOutDelayOf(revnetId);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
293
|
/// @notice Checks this contract's balance of a specific token.
|
|
325
294
|
/// @param token The address of the token to get this contract's balance of.
|
|
326
295
|
/// @return This contract's balance.
|
|
@@ -357,33 +326,8 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
357
326
|
/// @param decimals The decimals the resulting fixed point value will include.
|
|
358
327
|
/// @param currency The currency that the resulting amount should be in terms of.
|
|
359
328
|
/// @param terminals The terminals that the funds are being borrowed from.
|
|
329
|
+
/// @param currentStage The pre-fetched current ruleset.
|
|
360
330
|
/// @return borrowableAmount The amount that can be borrowed from the revnet.
|
|
361
|
-
function _borrowableAmountFrom(
|
|
362
|
-
uint256 revnetId,
|
|
363
|
-
uint256 collateralCount,
|
|
364
|
-
uint256 decimals,
|
|
365
|
-
uint256 currency,
|
|
366
|
-
IJBTerminal[] memory terminals
|
|
367
|
-
)
|
|
368
|
-
internal
|
|
369
|
-
view
|
|
370
|
-
returns (uint256)
|
|
371
|
-
{
|
|
372
|
-
// Keep a reference to the current stage.
|
|
373
|
-
// slither-disable-next-line unused-return
|
|
374
|
-
(JBRuleset memory currentStage,) = CONTROLLER.currentRulesetOf(revnetId);
|
|
375
|
-
|
|
376
|
-
return _borrowableAmountFrom({
|
|
377
|
-
revnetId: revnetId,
|
|
378
|
-
collateralCount: collateralCount,
|
|
379
|
-
decimals: decimals,
|
|
380
|
-
currency: currency,
|
|
381
|
-
terminals: terminals,
|
|
382
|
-
currentStage: currentStage
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/// @dev Overload that accepts a pre-fetched ruleset to avoid redundant `currentRulesetOf` calls.
|
|
387
331
|
function _borrowableAmountFrom(
|
|
388
332
|
uint256 revnetId,
|
|
389
333
|
uint256 collateralCount,
|
|
@@ -428,36 +372,8 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
428
372
|
/// @param loan The loan having its borrow amount determined.
|
|
429
373
|
/// @param revnetId The ID of the revnet to check for borrowable assets from.
|
|
430
374
|
/// @param collateralCount The amount of collateral that the loan will be collateralized with.
|
|
375
|
+
/// @param currentRuleset The pre-fetched current ruleset.
|
|
431
376
|
/// @return borrowAmount The amount of the loan that should be borrowed.
|
|
432
|
-
function _borrowAmountFrom(
|
|
433
|
-
REVLoan storage loan,
|
|
434
|
-
uint256 revnetId,
|
|
435
|
-
uint256 collateralCount
|
|
436
|
-
)
|
|
437
|
-
internal
|
|
438
|
-
view
|
|
439
|
-
returns (uint256)
|
|
440
|
-
{
|
|
441
|
-
// If there's no collateral, there's no loan.
|
|
442
|
-
if (collateralCount == 0) return 0;
|
|
443
|
-
|
|
444
|
-
// Get a reference to the accounting context for the source.
|
|
445
|
-
JBAccountingContext memory accountingContext =
|
|
446
|
-
loan.source.terminal.accountingContextForTokenOf({projectId: revnetId, token: loan.source.token});
|
|
447
|
-
|
|
448
|
-
// Keep a reference to the revnet's terminals.
|
|
449
|
-
IJBTerminal[] memory terminals = DIRECTORY.terminalsOf(revnetId);
|
|
450
|
-
|
|
451
|
-
return _borrowableAmountFrom({
|
|
452
|
-
revnetId: revnetId,
|
|
453
|
-
collateralCount: collateralCount,
|
|
454
|
-
decimals: accountingContext.decimals,
|
|
455
|
-
currency: accountingContext.currency,
|
|
456
|
-
terminals: terminals
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
/// @dev Overload that accepts a pre-fetched ruleset to avoid redundant `currentRulesetOf` calls.
|
|
461
377
|
function _borrowAmountFrom(
|
|
462
378
|
REVLoan storage loan,
|
|
463
379
|
uint256 revnetId,
|
|
@@ -476,7 +392,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
476
392
|
loan.source.terminal.accountingContextForTokenOf({projectId: revnetId, token: loan.source.token});
|
|
477
393
|
|
|
478
394
|
// Keep a reference to the revnet's terminals.
|
|
479
|
-
IJBTerminal[] memory terminals =
|
|
395
|
+
IJBTerminal[] memory terminals = _terminalsOf(revnetId);
|
|
480
396
|
|
|
481
397
|
return _borrowableAmountFrom({
|
|
482
398
|
revnetId: revnetId,
|
|
@@ -488,11 +404,34 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
488
404
|
});
|
|
489
405
|
}
|
|
490
406
|
|
|
407
|
+
/// @notice Returns the cash out delay timestamp using a pre-fetched ruleset.
|
|
408
|
+
/// @param revnetId The ID of the revnet.
|
|
409
|
+
/// @param currentRuleset The pre-fetched current ruleset.
|
|
410
|
+
/// @return The cash out delay timestamp. Returns 0 if no data hook is set or no delay exists.
|
|
411
|
+
function _cashOutDelayOf(uint256 revnetId, JBRuleset memory currentRuleset) internal view returns (uint256) {
|
|
412
|
+
// Extract the data hook address from the ruleset's packed metadata.
|
|
413
|
+
address dataHook = currentRuleset.dataHook();
|
|
414
|
+
|
|
415
|
+
// If there's no data hook, this isn't a revnet — no cash out delay applies.
|
|
416
|
+
if (dataHook == address(0)) return 0;
|
|
417
|
+
|
|
418
|
+
// Read the cash out delay from the REVOwner contract (the data hook).
|
|
419
|
+
return IREVOwner(dataHook).cashOutDelayOf(revnetId);
|
|
420
|
+
}
|
|
421
|
+
|
|
491
422
|
/// @dev `ERC-2771` specifies the context as being a single address (20 bytes).
|
|
492
423
|
function _contextSuffixLength() internal view override(ERC2771Context, Context) returns (uint256) {
|
|
493
424
|
return super._contextSuffixLength();
|
|
494
425
|
}
|
|
495
426
|
|
|
427
|
+
/// @notice Returns the current ruleset for a revnet. Consolidates ABI encode/decode to a single site.
|
|
428
|
+
/// @param revnetId The ID of the revnet.
|
|
429
|
+
/// @return currentRuleset The current ruleset.
|
|
430
|
+
function _currentRulesetOf(uint256 revnetId) internal view returns (JBRuleset memory currentRuleset) {
|
|
431
|
+
// slither-disable-next-line unused-return
|
|
432
|
+
(currentRuleset,) = CONTROLLER.currentRulesetOf(revnetId);
|
|
433
|
+
}
|
|
434
|
+
|
|
496
435
|
/// @notice Determines the source fee amount for a loan being paid off a certain amount.
|
|
497
436
|
/// @param loan The loan having its source fee amount determined.
|
|
498
437
|
/// @param amount The amount being paid off.
|
|
@@ -550,6 +489,13 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
550
489
|
return ERC2771Context._msgSender();
|
|
551
490
|
}
|
|
552
491
|
|
|
492
|
+
/// @notice Returns the terminals for a revnet. Consolidates ABI encode/decode to a single site.
|
|
493
|
+
/// @param revnetId The ID of the revnet.
|
|
494
|
+
/// @return The terminals registered for the revnet.
|
|
495
|
+
function _terminalsOf(uint256 revnetId) internal view returns (IJBTerminal[] memory) {
|
|
496
|
+
return DIRECTORY.terminalsOf(revnetId);
|
|
497
|
+
}
|
|
498
|
+
|
|
553
499
|
/// @notice The total borrowed amount from a revnet, aggregated across all loan sources.
|
|
554
500
|
/// @dev Each source's `totalBorrowedFrom` is stored in the source token's native decimals (e.g. 6 for USDC,
|
|
555
501
|
/// 18 for ETH). Before aggregation, each amount is normalized to the target `decimals` to prevent mixed-decimal
|
|
@@ -632,6 +578,8 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
632
578
|
/// @dev Collateral tokens are permanently burned when the loan is created. They are re-minted to the borrower
|
|
633
579
|
/// only upon repayment. If the loan expires (after LOAN_LIQUIDATION_DURATION), the collateral is permanently
|
|
634
580
|
/// lost and cannot be recovered.
|
|
581
|
+
/// @dev A delegated operator (with OPEN_LOAN permission) can set `beneficiary` to any address, directing borrowed
|
|
582
|
+
/// funds away from the holder. Holders should only grant OPEN_LOAN to fully trusted operators.
|
|
635
583
|
/// @param revnetId The ID of the revnet being borrowed from.
|
|
636
584
|
/// @param source The source of the loan being borrowed.
|
|
637
585
|
/// @param minBorrowAmount The minimum amount being borrowed, denominated in the token of the source's accounting
|
|
@@ -648,12 +596,17 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
648
596
|
uint256 minBorrowAmount,
|
|
649
597
|
uint256 collateralCount,
|
|
650
598
|
address payable beneficiary,
|
|
651
|
-
uint256 prepaidFeePercent
|
|
599
|
+
uint256 prepaidFeePercent,
|
|
600
|
+
address holder
|
|
652
601
|
)
|
|
653
602
|
public
|
|
654
603
|
override
|
|
655
604
|
returns (uint256 loanId, REVLoan memory)
|
|
656
605
|
{
|
|
606
|
+
// Only the holder or a permissioned operator can open a loan on the holder's behalf.
|
|
607
|
+
// Note: the operator controls `beneficiary`, so they can direct borrowed funds to any address.
|
|
608
|
+
_requirePermissionFrom({account: holder, projectId: revnetId, permissionId: JBPermissionIds.OPEN_LOAN});
|
|
609
|
+
|
|
657
610
|
// A loan needs to have collateral.
|
|
658
611
|
if (collateralCount == 0) revert REVLoans_ZeroCollateralLoanIsInvalid();
|
|
659
612
|
|
|
@@ -672,8 +625,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
672
625
|
}
|
|
673
626
|
|
|
674
627
|
// Cache the current ruleset once — used by both _cashOutDelayOf and _borrowAmountFrom.
|
|
675
|
-
|
|
676
|
-
(JBRuleset memory currentRuleset,) = CONTROLLER.currentRulesetOf(revnetId);
|
|
628
|
+
JBRuleset memory currentRuleset = _currentRulesetOf(revnetId);
|
|
677
629
|
|
|
678
630
|
// Enforce the cash out delay.
|
|
679
631
|
{
|
|
@@ -722,14 +674,12 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
722
674
|
newBorrowAmount: borrowAmount,
|
|
723
675
|
newCollateralCount: collateralCount,
|
|
724
676
|
sourceFeeAmount: sourceFeeAmount,
|
|
725
|
-
beneficiary: beneficiary
|
|
677
|
+
beneficiary: beneficiary,
|
|
678
|
+
holder: holder
|
|
726
679
|
});
|
|
727
680
|
|
|
728
|
-
//
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
// Mint the loan.
|
|
732
|
-
_mint({to: sender, tokenId: loanId});
|
|
681
|
+
// Mint the loan NFT to the holder.
|
|
682
|
+
_mint({to: holder, tokenId: loanId});
|
|
733
683
|
|
|
734
684
|
emit Borrow({
|
|
735
685
|
loanId: loanId,
|
|
@@ -740,7 +690,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
740
690
|
collateralCount: collateralCount,
|
|
741
691
|
sourceFeeAmount: sourceFeeAmount,
|
|
742
692
|
beneficiary: beneficiary,
|
|
743
|
-
caller:
|
|
693
|
+
caller: _msgSender()
|
|
744
694
|
});
|
|
745
695
|
|
|
746
696
|
return (loanId, loan);
|
|
@@ -811,6 +761,8 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
811
761
|
/// @dev Refinancing a loan will burn the original and create two new loans.
|
|
812
762
|
/// @dev This function is intentionally not payable — it only moves existing collateral between loans and does
|
|
813
763
|
/// not accept new funds. Any ETH sent with the call will be rejected by the EVM.
|
|
764
|
+
/// @dev A delegated operator (with REALLOCATE_LOAN permission) can set `beneficiary` to any address, directing
|
|
765
|
+
/// borrowed funds from the new loan away from the loan owner. Grant this permission only to trusted operators.
|
|
814
766
|
/// @param loanId The ID of the loan to reallocate collateral from.
|
|
815
767
|
/// @param collateralCountToTransfer The amount of collateral to transfer from the original loan.
|
|
816
768
|
/// @param source The source of the loan to create.
|
|
@@ -836,14 +788,13 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
836
788
|
override
|
|
837
789
|
returns (uint256 reallocatedLoanId, uint256 newLoanId, REVLoan memory reallocatedLoan, REVLoan memory newLoan)
|
|
838
790
|
{
|
|
839
|
-
//
|
|
840
|
-
|
|
791
|
+
// Keep a reference to the revnet ID of the loan being reallocated.
|
|
792
|
+
uint256 revnetId = revnetIdOfLoanWith(loanId);
|
|
841
793
|
|
|
842
|
-
//
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
}
|
|
794
|
+
// Only the loan owner or a permissioned operator can reallocate.
|
|
795
|
+
// Note: the operator controls `beneficiary`, so they can direct new loan proceeds to any address.
|
|
796
|
+
address loanOwner = _ownerOf(loanId);
|
|
797
|
+
_requirePermissionFrom({account: loanOwner, projectId: revnetId, permissionId: JBPermissionIds.REALLOCATE_LOAN});
|
|
847
798
|
|
|
848
799
|
// Make sure the loan hasn't expired.
|
|
849
800
|
if (block.timestamp - _loanOf[loanId].createdAt > LOAN_LIQUIDATION_DURATION) {
|
|
@@ -860,27 +811,28 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
860
811
|
|
|
861
812
|
// Note: this function is not payable, so the EVM prevents sending ETH at the call level.
|
|
862
813
|
|
|
863
|
-
// Keep a reference to the revnet ID of the loan being reallocated.
|
|
864
|
-
uint256 revnetId = revnetIdOfLoanWith(loanId);
|
|
865
|
-
|
|
866
814
|
// Refinance the loan.
|
|
867
815
|
(reallocatedLoanId, reallocatedLoan) = _reallocateCollateralFromLoan({
|
|
868
|
-
loanId: loanId, revnetId: revnetId, collateralCountToRemove: collateralCountToTransfer
|
|
816
|
+
loanId: loanId, revnetId: revnetId, collateralCountToRemove: collateralCountToTransfer, loanOwner: loanOwner
|
|
869
817
|
});
|
|
870
818
|
|
|
871
819
|
// Make a new loan with the leftover collateral from reallocating.
|
|
820
|
+
// The loan owner is the holder for the new loan (their tokens are used as collateral).
|
|
872
821
|
(newLoanId, newLoan) = borrowFrom({
|
|
873
822
|
revnetId: revnetId,
|
|
874
823
|
source: source,
|
|
875
824
|
minBorrowAmount: minBorrowAmount,
|
|
876
825
|
collateralCount: collateralCountToTransfer + collateralCountToAdd,
|
|
877
826
|
beneficiary: beneficiary,
|
|
878
|
-
prepaidFeePercent: prepaidFeePercent
|
|
827
|
+
prepaidFeePercent: prepaidFeePercent,
|
|
828
|
+
holder: loanOwner
|
|
879
829
|
});
|
|
880
830
|
}
|
|
881
831
|
|
|
882
832
|
/// @notice Allows the owner of a loan to pay it back or receive returned collateral no longer necessary to support
|
|
883
833
|
/// the loan.
|
|
834
|
+
/// @dev A delegated operator (with REPAY_LOAN permission) can set `beneficiary` to any address, directing returned
|
|
835
|
+
/// collateral tokens away from the loan owner. Grant this permission only to trusted operators.
|
|
884
836
|
/// @param loanId The ID of the loan being adjusted.
|
|
885
837
|
/// @param maxRepayBorrowAmount The maximum amount being paid off, denominated in the token of the source's
|
|
886
838
|
/// accounting context.
|
|
@@ -904,11 +856,12 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
904
856
|
// Cache the sender to avoid repeated ERC2771 context reads.
|
|
905
857
|
address sender = _msgSender();
|
|
906
858
|
|
|
907
|
-
//
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
859
|
+
// Only the loan owner or a permissioned operator can repay.
|
|
860
|
+
// Note: the operator controls `beneficiary`, so they can direct returned collateral to any address.
|
|
861
|
+
address loanOwner = _ownerOf(loanId);
|
|
862
|
+
_requirePermissionFrom({
|
|
863
|
+
account: loanOwner, projectId: revnetIdOfLoanWith(loanId), permissionId: JBPermissionIds.REPAY_LOAN
|
|
864
|
+
});
|
|
912
865
|
|
|
913
866
|
// Keep a reference to the fee being iterated on.
|
|
914
867
|
REVLoan storage loan = _loanOf[loanId];
|
|
@@ -920,12 +873,18 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
920
873
|
// Get a reference to the revnet ID of the loan being repaid.
|
|
921
874
|
uint256 revnetId = revnetIdOfLoanWith(loanId);
|
|
922
875
|
|
|
876
|
+
// Cache the current ruleset once for borrow amount calculation.
|
|
877
|
+
JBRuleset memory currentRuleset = _currentRulesetOf(revnetId);
|
|
878
|
+
|
|
923
879
|
// Scope to limit newBorrowAmount's stack lifetime.
|
|
924
880
|
uint256 repayBorrowAmount;
|
|
925
881
|
{
|
|
926
882
|
// Get the new borrow amount.
|
|
927
883
|
uint256 newBorrowAmount = _borrowAmountFrom({
|
|
928
|
-
loan: loan,
|
|
884
|
+
loan: loan,
|
|
885
|
+
revnetId: revnetId,
|
|
886
|
+
collateralCount: loan.collateral - collateralCountToReturn,
|
|
887
|
+
currentRuleset: currentRuleset
|
|
929
888
|
});
|
|
930
889
|
|
|
931
890
|
// If the remaining collateral yields zero borrow amount, treat as full repay.
|
|
@@ -972,7 +931,8 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
972
931
|
repayBorrowAmount: repayBorrowAmount,
|
|
973
932
|
sourceFeeAmount: sourceFeeAmount,
|
|
974
933
|
collateralCountToReturn: collateralCountToReturn,
|
|
975
|
-
beneficiary: beneficiary
|
|
934
|
+
beneficiary: beneficiary,
|
|
935
|
+
loanOwner: loanOwner
|
|
976
936
|
});
|
|
977
937
|
|
|
978
938
|
// If the max repay amount is greater than the repay amount, return the difference back to the payer.
|
|
@@ -1054,14 +1014,12 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1054
1014
|
/// loan is repaid. If the loan expires and is liquidated, the burned collateral is permanently lost.
|
|
1055
1015
|
/// @param revnetId The ID of the revnet the loan is being added in.
|
|
1056
1016
|
/// @param amount The new amount of collateral being added to the loan.
|
|
1057
|
-
function _addCollateralTo(uint256 revnetId, uint256 amount) internal {
|
|
1017
|
+
function _addCollateralTo(uint256 revnetId, uint256 amount, address holder) internal {
|
|
1058
1018
|
// Increment the total amount of collateral tokens.
|
|
1059
1019
|
totalCollateralOf[revnetId] += amount;
|
|
1060
1020
|
|
|
1061
1021
|
// Permanently burn the tokens that are tracked as collateral. These are only re-minted upon repayment.
|
|
1062
|
-
CONTROLLER.burnTokensOf({
|
|
1063
|
-
holder: _msgSender(), projectId: revnetId, tokenCount: amount, memo: "Adding collateral to loan"
|
|
1064
|
-
});
|
|
1022
|
+
CONTROLLER.burnTokensOf({holder: holder, projectId: revnetId, tokenCount: amount, memo: ""});
|
|
1065
1023
|
}
|
|
1066
1024
|
|
|
1067
1025
|
/// @notice Add a new amount to the loan that is greater than the previous amount.
|
|
@@ -1108,7 +1066,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1108
1066
|
minTokensPaidOut: 0,
|
|
1109
1067
|
beneficiary: payable(address(this)),
|
|
1110
1068
|
feeBeneficiary: beneficiary,
|
|
1111
|
-
memo: "
|
|
1069
|
+
memo: ""
|
|
1112
1070
|
});
|
|
1113
1071
|
}
|
|
1114
1072
|
|
|
@@ -1137,7 +1095,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1137
1095
|
amount: revFeeAmount,
|
|
1138
1096
|
beneficiary: beneficiary,
|
|
1139
1097
|
minReturnedTokens: 0,
|
|
1140
|
-
memo: "
|
|
1098
|
+
memo: "",
|
|
1141
1099
|
metadata: bytes(abi.encodePacked(revnetId))
|
|
1142
1100
|
}) {}
|
|
1143
1101
|
catch (bytes memory) {
|
|
@@ -1177,13 +1135,15 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1177
1135
|
/// @param newCollateralCount The new amount of collateral backing the loan.
|
|
1178
1136
|
/// @param sourceFeeAmount The amount of the fee being taken from the revnet acting as the source of the loan.
|
|
1179
1137
|
/// @param beneficiary The address receiving the returned collateral and any tokens resulting from paying fees.
|
|
1138
|
+
/// @param holder The address whose tokens are used as collateral (burned).
|
|
1180
1139
|
function _adjust(
|
|
1181
1140
|
REVLoan storage loan,
|
|
1182
1141
|
uint256 revnetId,
|
|
1183
1142
|
uint256 newBorrowAmount,
|
|
1184
1143
|
uint256 newCollateralCount,
|
|
1185
1144
|
uint256 sourceFeeAmount,
|
|
1186
|
-
address payable beneficiary
|
|
1145
|
+
address payable beneficiary,
|
|
1146
|
+
address holder
|
|
1187
1147
|
)
|
|
1188
1148
|
internal
|
|
1189
1149
|
{
|
|
@@ -1227,7 +1187,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1227
1187
|
|
|
1228
1188
|
// Add collateral if needed...
|
|
1229
1189
|
if (addedCollateralCount > 0) {
|
|
1230
|
-
_addCollateralTo({revnetId: revnetId, amount: addedCollateralCount});
|
|
1190
|
+
_addCollateralTo({revnetId: revnetId, amount: addedCollateralCount, holder: holder});
|
|
1231
1191
|
// ... or return collateral if needed.
|
|
1232
1192
|
} else if (returnedCollateralCount > 0) {
|
|
1233
1193
|
_returnCollateralFrom({
|
|
@@ -1254,7 +1214,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1254
1214
|
amount: sourceFeeAmount,
|
|
1255
1215
|
beneficiary: beneficiary,
|
|
1256
1216
|
minReturnedTokens: 0,
|
|
1257
|
-
memo: "
|
|
1217
|
+
memo: "",
|
|
1258
1218
|
metadata: bytes(abi.encodePacked(REV_ID))
|
|
1259
1219
|
}) {}
|
|
1260
1220
|
catch (bytes memory) {
|
|
@@ -1291,7 +1251,8 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1291
1251
|
function _reallocateCollateralFromLoan(
|
|
1292
1252
|
uint256 loanId,
|
|
1293
1253
|
uint256 revnetId,
|
|
1294
|
-
uint256 collateralCountToRemove
|
|
1254
|
+
uint256 collateralCountToRemove,
|
|
1255
|
+
address loanOwner
|
|
1295
1256
|
)
|
|
1296
1257
|
internal
|
|
1297
1258
|
returns (uint256 reallocatedLoanId, REVLoan storage reallocatedLoan)
|
|
@@ -1308,8 +1269,13 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1308
1269
|
// Keep a reference to the new collateral amount.
|
|
1309
1270
|
uint256 newCollateralCount = loan.collateral - collateralCountToRemove;
|
|
1310
1271
|
|
|
1272
|
+
// Cache the current ruleset for borrow amount calculation.
|
|
1273
|
+
JBRuleset memory currentRuleset = _currentRulesetOf(revnetId);
|
|
1274
|
+
|
|
1311
1275
|
// Keep a reference to the new borrow amount.
|
|
1312
|
-
uint256 borrowAmount = _borrowAmountFrom({
|
|
1276
|
+
uint256 borrowAmount = _borrowAmountFrom({
|
|
1277
|
+
loan: loan, revnetId: revnetId, collateralCount: newCollateralCount, currentRuleset: currentRuleset
|
|
1278
|
+
});
|
|
1313
1279
|
|
|
1314
1280
|
// Make sure the borrow amount is not less than the original loan's amount.
|
|
1315
1281
|
if (borrowAmount < loan.amount) {
|
|
@@ -1340,12 +1306,13 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1340
1306
|
newBorrowAmount: reallocatedLoan.amount, // Don't change the borrow amount.
|
|
1341
1307
|
newCollateralCount: newCollateralCount,
|
|
1342
1308
|
sourceFeeAmount: 0,
|
|
1343
|
-
beneficiary: payable(
|
|
1309
|
+
beneficiary: payable(loanOwner), // Return collateral to the loan owner, who will have the returned
|
|
1344
1310
|
// collateral tokens debited from their balance for the new loan.
|
|
1311
|
+
holder: loanOwner // Only used if collateral is added (not the case here — collateral is being returned).
|
|
1345
1312
|
});
|
|
1346
1313
|
|
|
1347
|
-
// Mint the replacement loan.
|
|
1348
|
-
_mint({to:
|
|
1314
|
+
// Mint the replacement loan to the loan owner.
|
|
1315
|
+
_mint({to: loanOwner, tokenId: reallocatedLoanId});
|
|
1349
1316
|
|
|
1350
1317
|
// Clear stale loan data for gas refund.
|
|
1351
1318
|
delete _loanOf[loanId];
|
|
@@ -1381,7 +1348,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1381
1348
|
token: loan.source.token,
|
|
1382
1349
|
amount: repaidBorrowAmount,
|
|
1383
1350
|
shouldReturnHeldFees: false,
|
|
1384
|
-
memo: "
|
|
1351
|
+
memo: "",
|
|
1385
1352
|
metadata: bytes(abi.encodePacked(REV_ID))
|
|
1386
1353
|
});
|
|
1387
1354
|
}
|
|
@@ -1394,6 +1361,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1394
1361
|
/// @param sourceFeeAmount The amount of the fee being taken from the revnet acting as the source of the loan.
|
|
1395
1362
|
/// @param collateralCountToReturn The amount of collateral being returned that the loan no longer requires.
|
|
1396
1363
|
/// @param beneficiary The address receiving the returned collateral and any tokens resulting from paying fees.
|
|
1364
|
+
/// @param loanOwner The owner of the loan NFT (receives replacement loan if partial repay).
|
|
1397
1365
|
// slither-disable-next-line reentrancy-eth,reentrancy-events
|
|
1398
1366
|
function _repayLoan(
|
|
1399
1367
|
uint256 loanId,
|
|
@@ -1402,7 +1370,8 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1402
1370
|
uint256 repayBorrowAmount,
|
|
1403
1371
|
uint256 sourceFeeAmount,
|
|
1404
1372
|
uint256 collateralCountToReturn,
|
|
1405
|
-
address payable beneficiary
|
|
1373
|
+
address payable beneficiary,
|
|
1374
|
+
address loanOwner
|
|
1406
1375
|
)
|
|
1407
1376
|
internal
|
|
1408
1377
|
returns (uint256, REVLoan memory)
|
|
@@ -1423,7 +1392,8 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1423
1392
|
newBorrowAmount: 0,
|
|
1424
1393
|
newCollateralCount: 0,
|
|
1425
1394
|
sourceFeeAmount: sourceFeeAmount,
|
|
1426
|
-
beneficiary: beneficiary
|
|
1395
|
+
beneficiary: beneficiary,
|
|
1396
|
+
holder: _msgSender() // Only used if collateral is added (not the case here — collateral is returned).
|
|
1427
1397
|
});
|
|
1428
1398
|
|
|
1429
1399
|
// Snapshot the zeroed loan for the return value (reflects post-repay state).
|
|
@@ -1466,8 +1436,8 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1466
1436
|
paidOffLoan.prepaidDuration = loan.prepaidDuration;
|
|
1467
1437
|
paidOffLoan.source = loan.source;
|
|
1468
1438
|
|
|
1469
|
-
// Mint the replacement loan FIRST so it exists before _adjust writes data.
|
|
1470
|
-
_mint({to:
|
|
1439
|
+
// Mint the replacement loan to the loan owner FIRST so it exists before _adjust writes data.
|
|
1440
|
+
_mint({to: loanOwner, tokenId: paidOffLoanId});
|
|
1471
1441
|
|
|
1472
1442
|
// Then adjust the loan data.
|
|
1473
1443
|
_adjust({
|
|
@@ -1476,7 +1446,8 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1476
1446
|
newBorrowAmount: paidOffLoan.amount - (repayBorrowAmount - sourceFeeAmount),
|
|
1477
1447
|
newCollateralCount: paidOffLoan.collateral - collateralCountToReturn,
|
|
1478
1448
|
sourceFeeAmount: sourceFeeAmount,
|
|
1479
|
-
beneficiary: beneficiary
|
|
1449
|
+
beneficiary: beneficiary,
|
|
1450
|
+
holder: _msgSender() // Only used if collateral is added (not the case here — collateral is returned).
|
|
1480
1451
|
});
|
|
1481
1452
|
|
|
1482
1453
|
emit RepayLoan({
|
|
@@ -1513,7 +1484,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1513
1484
|
projectId: revnetId,
|
|
1514
1485
|
tokenCount: collateralCount,
|
|
1515
1486
|
beneficiary: beneficiary,
|
|
1516
|
-
memo: "
|
|
1487
|
+
memo: "",
|
|
1517
1488
|
useReservedPercent: false
|
|
1518
1489
|
});
|
|
1519
1490
|
}
|
package/src/REVOwner.sol
CHANGED
|
@@ -59,6 +59,9 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
|
|
|
59
59
|
/// @notice The Juicebox project ID of the revnet that receives cash out fees.
|
|
60
60
|
uint256 public immutable FEE_REVNET_ID;
|
|
61
61
|
|
|
62
|
+
/// @notice The hidden tokens contract used by all revnets.
|
|
63
|
+
address public immutable HIDDEN_TOKENS;
|
|
64
|
+
|
|
62
65
|
/// @notice The loan contract used by all revnets.
|
|
63
66
|
address public immutable LOANS;
|
|
64
67
|
|
|
@@ -99,12 +102,14 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
|
|
|
99
102
|
/// @param feeRevnetId The Juicebox project ID of the fee revnet.
|
|
100
103
|
/// @param suckerRegistry The sucker registry.
|
|
101
104
|
/// @param loans The loan contract address.
|
|
105
|
+
/// @param hiddenTokens The hidden tokens contract address.
|
|
102
106
|
constructor(
|
|
103
107
|
IJBBuybackHookRegistry buybackHook,
|
|
104
108
|
IJBDirectory directory,
|
|
105
109
|
uint256 feeRevnetId,
|
|
106
110
|
IJBSuckerRegistry suckerRegistry,
|
|
107
|
-
address loans
|
|
111
|
+
address loans,
|
|
112
|
+
address hiddenTokens
|
|
108
113
|
) {
|
|
109
114
|
BUYBACK_HOOK = buybackHook;
|
|
110
115
|
DIRECTORY = directory;
|
|
@@ -112,6 +117,8 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
|
|
|
112
117
|
SUCKER_REGISTRY = suckerRegistry;
|
|
113
118
|
// slither-disable-next-line missing-zero-check
|
|
114
119
|
LOANS = loans;
|
|
120
|
+
// slither-disable-next-line missing-zero-check
|
|
121
|
+
HIDDEN_TOKENS = hiddenTokens;
|
|
115
122
|
_DEPLOYER_BINDER = msg.sender;
|
|
116
123
|
}
|
|
117
124
|
|
|
@@ -302,8 +309,9 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
|
|
|
302
309
|
override
|
|
303
310
|
returns (bool)
|
|
304
311
|
{
|
|
305
|
-
// The loans contract, buyback hook (and its delegates), and suckers are allowed to mint
|
|
306
|
-
|
|
312
|
+
// The loans contract, hidden tokens contract, buyback hook (and its delegates), and suckers are allowed to mint
|
|
313
|
+
// the revnet's tokens.
|
|
314
|
+
return addr == LOANS || addr == HIDDEN_TOKENS || addr == address(BUYBACK_HOOK)
|
|
307
315
|
|| BUYBACK_HOOK.hasMintPermissionFor({projectId: revnetId, ruleset: ruleset, addr: addr})
|
|
308
316
|
|| _isSuckerOf({revnetId: revnetId, addr: addr});
|
|
309
317
|
}
|