@rev-net/core-v6 0.0.28 → 0.0.29
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 +7 -7
- package/src/REVDeployer.sol +39 -12
- package/src/REVLoans.sol +124 -30
- package/src/interfaces/IREVLoans.sol +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rev-net/core-v6",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.29",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
"artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'revnet-core-v6'"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@bananapus/721-hook-v6": "^0.0.
|
|
23
|
-
"@bananapus/buyback-hook-v6": "^0.0.
|
|
24
|
-
"@bananapus/core-v6": "^0.0.
|
|
22
|
+
"@bananapus/721-hook-v6": "^0.0.32",
|
|
23
|
+
"@bananapus/buyback-hook-v6": "^0.0.26",
|
|
24
|
+
"@bananapus/core-v6": "^0.0.32",
|
|
25
25
|
"@bananapus/ownable-v6": "^0.0.17",
|
|
26
26
|
"@bananapus/permission-ids-v6": "^0.0.15",
|
|
27
|
-
"@bananapus/router-terminal-v6": "^0.0.
|
|
28
|
-
"@bananapus/suckers-v6": "^0.0.
|
|
29
|
-
"@croptop/core-v6": "^0.0.
|
|
27
|
+
"@bananapus/router-terminal-v6": "^0.0.26",
|
|
28
|
+
"@bananapus/suckers-v6": "^0.0.22",
|
|
29
|
+
"@croptop/core-v6": "^0.0.30",
|
|
30
30
|
"@openzeppelin/contracts": "^5.6.1",
|
|
31
31
|
"@uniswap/v4-core": "^1.0.2",
|
|
32
32
|
"@uniswap/v4-periphery": "^1.0.3"
|
package/src/REVDeployer.sol
CHANGED
|
@@ -275,8 +275,11 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
275
275
|
{
|
|
276
276
|
// Count the total number of accounting contexts across all terminals.
|
|
277
277
|
uint256 count;
|
|
278
|
-
for (uint256 i; i < terminalConfigurations.length;
|
|
278
|
+
for (uint256 i; i < terminalConfigurations.length;) {
|
|
279
279
|
count += terminalConfigurations[i].accountingContextsToAccept.length;
|
|
280
|
+
unchecked {
|
|
281
|
+
++i;
|
|
282
|
+
}
|
|
280
283
|
}
|
|
281
284
|
|
|
282
285
|
// Initialize the fund access limit groups.
|
|
@@ -284,9 +287,9 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
284
287
|
|
|
285
288
|
// Set up the fund access limits.
|
|
286
289
|
uint256 index;
|
|
287
|
-
for (uint256 i; i < terminalConfigurations.length;
|
|
290
|
+
for (uint256 i; i < terminalConfigurations.length;) {
|
|
288
291
|
JBTerminalConfig calldata terminalConfiguration = terminalConfigurations[i];
|
|
289
|
-
for (uint256 j; j < terminalConfiguration.accountingContextsToAccept.length;
|
|
292
|
+
for (uint256 j; j < terminalConfiguration.accountingContextsToAccept.length;) {
|
|
290
293
|
JBAccountingContext calldata accountingContext = terminalConfiguration.accountingContextsToAccept[j];
|
|
291
294
|
|
|
292
295
|
// Set up an unlimited allowance for the loan contract to use.
|
|
@@ -300,6 +303,12 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
300
303
|
payoutLimits: new JBCurrencyAmount[](0),
|
|
301
304
|
surplusAllowances: loanAllowances
|
|
302
305
|
});
|
|
306
|
+
unchecked {
|
|
307
|
+
++j;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
unchecked {
|
|
311
|
+
++i;
|
|
303
312
|
}
|
|
304
313
|
}
|
|
305
314
|
}
|
|
@@ -378,8 +387,11 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
378
387
|
allOperatorPermissions[8] = JBPermissionIds.SET_TOKEN_METADATA;
|
|
379
388
|
|
|
380
389
|
// Copy the custom permissions into the array.
|
|
381
|
-
for (uint256 i; i < customSplitOperatorPermissionIndexes.length;
|
|
390
|
+
for (uint256 i; i < customSplitOperatorPermissionIndexes.length;) {
|
|
382
391
|
allOperatorPermissions[9 + i] = customSplitOperatorPermissionIndexes[i];
|
|
392
|
+
unchecked {
|
|
393
|
+
++i;
|
|
394
|
+
}
|
|
383
395
|
}
|
|
384
396
|
}
|
|
385
397
|
|
|
@@ -566,7 +578,7 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
566
578
|
}
|
|
567
579
|
|
|
568
580
|
// Store the tiered ERC-721 hook in the owner contract.
|
|
569
|
-
REVOwner(OWNER).setTiered721HookOf(revnetId, hook);
|
|
581
|
+
REVOwner(OWNER).setTiered721HookOf({revnetId: revnetId, hook: hook});
|
|
570
582
|
|
|
571
583
|
// Grant the split operator all 721 permissions (no prevent* flags for default config).
|
|
572
584
|
_extraOperatorPermissions[revnetId].push(JBPermissionIds.ADJUST_721_TIERS);
|
|
@@ -690,7 +702,7 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
690
702
|
});
|
|
691
703
|
|
|
692
704
|
// Store the tiered ERC-721 hook in the owner contract.
|
|
693
|
-
REVOwner(OWNER).setTiered721HookOf(revnetId, hook);
|
|
705
|
+
REVOwner(OWNER).setTiered721HookOf({revnetId: revnetId, hook: hook});
|
|
694
706
|
|
|
695
707
|
// Give the split operator permission to add and remove tiers unless prevented.
|
|
696
708
|
if (!tiered721HookConfiguration.preventSplitOperatorAdjustingTiers) {
|
|
@@ -722,7 +734,7 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
722
734
|
CTAllowedPost[] memory formattedAllowedPosts = new CTAllowedPost[](allowedPosts.length);
|
|
723
735
|
|
|
724
736
|
// Iterate through each post to add it to the formatted list.
|
|
725
|
-
for (uint256 i; i < allowedPosts.length;
|
|
737
|
+
for (uint256 i; i < allowedPosts.length;) {
|
|
726
738
|
// Set the post being iterated on.
|
|
727
739
|
REVCroptopAllowedPost memory post = allowedPosts[i];
|
|
728
740
|
|
|
@@ -736,6 +748,9 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
736
748
|
maximumSplitPercent: post.maximumSplitPercent,
|
|
737
749
|
allowedAddresses: post.allowedAddresses
|
|
738
750
|
});
|
|
751
|
+
unchecked {
|
|
752
|
+
++i;
|
|
753
|
+
}
|
|
739
754
|
}
|
|
740
755
|
|
|
741
756
|
// Set up the allowed posts in the publisher.
|
|
@@ -830,15 +845,21 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
830
845
|
});
|
|
831
846
|
|
|
832
847
|
// Now that the ERC-20 exists, initialize buyback pools for each terminal token.
|
|
833
|
-
for (uint256 i; i < terminalConfigurations.length;
|
|
848
|
+
for (uint256 i; i < terminalConfigurations.length;) {
|
|
834
849
|
JBTerminalConfig calldata terminalConfiguration = terminalConfigurations[i];
|
|
835
|
-
for (uint256 j; j < terminalConfiguration.accountingContextsToAccept.length;
|
|
850
|
+
for (uint256 j; j < terminalConfiguration.accountingContextsToAccept.length;) {
|
|
836
851
|
// slither-disable-next-line calls-loop
|
|
837
852
|
_tryInitializeBuybackPoolFor({
|
|
838
853
|
revnetId: revnetId,
|
|
839
854
|
terminalToken: terminalConfiguration.accountingContextsToAccept[j].token,
|
|
840
855
|
initialIssuance: configuration.stageConfigurations[0].initialIssuance
|
|
841
856
|
});
|
|
857
|
+
unchecked {
|
|
858
|
+
++j;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
unchecked {
|
|
862
|
+
++i;
|
|
842
863
|
}
|
|
843
864
|
}
|
|
844
865
|
|
|
@@ -937,7 +958,7 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
937
958
|
_makeLoanFundAccessLimits({terminalConfigurations: terminalConfigurations});
|
|
938
959
|
|
|
939
960
|
// Iterate through each stage to set up its ruleset.
|
|
940
|
-
for (uint256 i; i < configuration.stageConfigurations.length;
|
|
961
|
+
for (uint256 i; i < configuration.stageConfigurations.length;) {
|
|
941
962
|
// Set the stage being iterated on.
|
|
942
963
|
REVStageConfig calldata stageConfiguration = configuration.stageConfigurations[i];
|
|
943
964
|
|
|
@@ -1019,6 +1040,9 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
1019
1040
|
// slither-disable-next-line reentrancy-benign
|
|
1020
1041
|
amountToAutoIssue[revnetId][block.timestamp + i][autoIssuance.beneficiary] += autoIssuance.count;
|
|
1021
1042
|
}
|
|
1043
|
+
unchecked {
|
|
1044
|
+
++i;
|
|
1045
|
+
}
|
|
1022
1046
|
}
|
|
1023
1047
|
|
|
1024
1048
|
// Hash the encoded configuration.
|
|
@@ -1039,7 +1063,7 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
1039
1063
|
uint256 cashOutDelay = block.timestamp + CASH_OUT_DELAY;
|
|
1040
1064
|
|
|
1041
1065
|
// Store the cash out delay in the owner contract.
|
|
1042
|
-
REVOwner(OWNER).setCashOutDelayOf(revnetId, cashOutDelay);
|
|
1066
|
+
REVOwner(OWNER).setCashOutDelayOf({revnetId: revnetId, cashOutDelay: cashOutDelay});
|
|
1043
1067
|
|
|
1044
1068
|
emit SetCashOutDelay({revnetId: revnetId, cashOutDelay: cashOutDelay, caller: _msgSender()});
|
|
1045
1069
|
}
|
|
@@ -1089,8 +1113,11 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
1089
1113
|
uint256[] memory permissionIndexes = _splitOperatorPermissionIndexesOf(revnetId);
|
|
1090
1114
|
uint8[] memory permissionIds = new uint8[](permissionIndexes.length);
|
|
1091
1115
|
|
|
1092
|
-
for (uint256 i; i < permissionIndexes.length;
|
|
1116
|
+
for (uint256 i; i < permissionIndexes.length;) {
|
|
1093
1117
|
permissionIds[i] = uint8(permissionIndexes[i]);
|
|
1118
|
+
unchecked {
|
|
1119
|
+
++i;
|
|
1120
|
+
}
|
|
1094
1121
|
}
|
|
1095
1122
|
|
|
1096
1123
|
_setPermissionsFor({
|
package/src/REVLoans.sol
CHANGED
|
@@ -227,15 +227,20 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
227
227
|
view
|
|
228
228
|
returns (uint256)
|
|
229
229
|
{
|
|
230
|
+
// Cache the current ruleset once — used by both _cashOutDelayOf and _borrowableAmountFrom.
|
|
231
|
+
// slither-disable-next-line unused-return
|
|
232
|
+
(JBRuleset memory currentRuleset,) = CONTROLLER.currentRulesetOf(revnetId);
|
|
233
|
+
|
|
230
234
|
// If the cash out delay hasn't passed yet, no amount is borrowable.
|
|
231
|
-
if (_cashOutDelayOf(revnetId) > block.timestamp) return 0;
|
|
235
|
+
if (_cashOutDelayOf({revnetId: revnetId, currentRuleset: currentRuleset}) > block.timestamp) return 0;
|
|
232
236
|
|
|
233
237
|
return _borrowableAmountFrom({
|
|
234
238
|
revnetId: revnetId,
|
|
235
239
|
collateralCount: collateralCount,
|
|
236
240
|
decimals: decimals,
|
|
237
241
|
currency: currency,
|
|
238
|
-
terminals: DIRECTORY.terminalsOf(revnetId)
|
|
242
|
+
terminals: DIRECTORY.terminalsOf(revnetId),
|
|
243
|
+
currentStage: currentRuleset
|
|
239
244
|
});
|
|
240
245
|
}
|
|
241
246
|
|
|
@@ -298,6 +303,14 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
298
303
|
// slither-disable-next-line unused-return
|
|
299
304
|
(JBRuleset memory currentRuleset,) = CONTROLLER.currentRulesetOf(revnetId);
|
|
300
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) {
|
|
301
314
|
// Extract the data hook address from the ruleset's packed metadata.
|
|
302
315
|
address dataHook = currentRuleset.dataHook();
|
|
303
316
|
|
|
@@ -360,6 +373,29 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
360
373
|
// slither-disable-next-line unused-return
|
|
361
374
|
(JBRuleset memory currentStage,) = CONTROLLER.currentRulesetOf(revnetId);
|
|
362
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
|
+
function _borrowableAmountFrom(
|
|
388
|
+
uint256 revnetId,
|
|
389
|
+
uint256 collateralCount,
|
|
390
|
+
uint256 decimals,
|
|
391
|
+
uint256 currency,
|
|
392
|
+
IJBTerminal[] memory terminals,
|
|
393
|
+
JBRuleset memory currentStage
|
|
394
|
+
)
|
|
395
|
+
internal
|
|
396
|
+
view
|
|
397
|
+
returns (uint256)
|
|
398
|
+
{
|
|
363
399
|
// Get the surplus of all the revnet's terminals in terms of the native currency.
|
|
364
400
|
uint256 totalSurplus = JBSurplus.currentSurplusOf({
|
|
365
401
|
projectId: revnetId, terminals: terminals, tokens: new address[](0), decimals: decimals, currency: currency
|
|
@@ -421,6 +457,37 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
421
457
|
});
|
|
422
458
|
}
|
|
423
459
|
|
|
460
|
+
/// @dev Overload that accepts a pre-fetched ruleset to avoid redundant `currentRulesetOf` calls.
|
|
461
|
+
function _borrowAmountFrom(
|
|
462
|
+
REVLoan storage loan,
|
|
463
|
+
uint256 revnetId,
|
|
464
|
+
uint256 collateralCount,
|
|
465
|
+
JBRuleset memory currentRuleset
|
|
466
|
+
)
|
|
467
|
+
internal
|
|
468
|
+
view
|
|
469
|
+
returns (uint256)
|
|
470
|
+
{
|
|
471
|
+
// If there's no collateral, there's no loan.
|
|
472
|
+
if (collateralCount == 0) return 0;
|
|
473
|
+
|
|
474
|
+
// Get a reference to the accounting context for the source.
|
|
475
|
+
JBAccountingContext memory accountingContext =
|
|
476
|
+
loan.source.terminal.accountingContextForTokenOf({projectId: revnetId, token: loan.source.token});
|
|
477
|
+
|
|
478
|
+
// Keep a reference to the revnet's terminals.
|
|
479
|
+
IJBTerminal[] memory terminals = DIRECTORY.terminalsOf(revnetId);
|
|
480
|
+
|
|
481
|
+
return _borrowableAmountFrom({
|
|
482
|
+
revnetId: revnetId,
|
|
483
|
+
collateralCount: collateralCount,
|
|
484
|
+
decimals: accountingContext.decimals,
|
|
485
|
+
currency: accountingContext.currency,
|
|
486
|
+
terminals: terminals,
|
|
487
|
+
currentStage: currentRuleset
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
|
|
424
491
|
/// @dev `ERC-2771` specifies the context as being a single address (20 bytes).
|
|
425
492
|
function _contextSuffixLength() internal view override(ERC2771Context, Context) returns (uint256) {
|
|
426
493
|
return super._contextSuffixLength();
|
|
@@ -503,12 +570,13 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
503
570
|
returns (uint256 borrowedAmount)
|
|
504
571
|
{
|
|
505
572
|
// Keep a reference to all sources being used to loaned out from this revnet.
|
|
506
|
-
|
|
573
|
+
// Use storage ref to avoid bulk-copying the entire array to memory.
|
|
574
|
+
REVLoanSource[] storage sources = _loanSourcesOf[revnetId];
|
|
507
575
|
|
|
508
576
|
// Iterate over all sources being used to loaned out.
|
|
509
577
|
for (uint256 i; i < sources.length; i++) {
|
|
510
578
|
// Get a reference to the token being iterated on.
|
|
511
|
-
REVLoanSource
|
|
579
|
+
REVLoanSource storage source = sources[i];
|
|
512
580
|
|
|
513
581
|
// Get a reference to the accounting context for the source.
|
|
514
582
|
// slither-disable-next-line calls-loop
|
|
@@ -603,9 +671,13 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
603
671
|
);
|
|
604
672
|
}
|
|
605
673
|
|
|
674
|
+
// Cache the current ruleset once — used by both _cashOutDelayOf and _borrowAmountFrom.
|
|
675
|
+
// slither-disable-next-line unused-return
|
|
676
|
+
(JBRuleset memory currentRuleset,) = CONTROLLER.currentRulesetOf(revnetId);
|
|
677
|
+
|
|
606
678
|
// Enforce the cash out delay.
|
|
607
679
|
{
|
|
608
|
-
uint256 cashOutDelay = _cashOutDelayOf(revnetId);
|
|
680
|
+
uint256 cashOutDelay = _cashOutDelayOf({revnetId: revnetId, currentRuleset: currentRuleset});
|
|
609
681
|
if (cashOutDelay > block.timestamp) {
|
|
610
682
|
revert REVLoans_CashOutDelayNotFinished(cashOutDelay, block.timestamp);
|
|
611
683
|
}
|
|
@@ -628,8 +700,10 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
628
700
|
loan.prepaidDuration =
|
|
629
701
|
uint32(mulDiv({x: prepaidFeePercent, y: LOAN_LIQUIDATION_DURATION, denominator: MAX_PREPAID_FEE_PERCENT}));
|
|
630
702
|
|
|
631
|
-
// Get the amount of the loan.
|
|
632
|
-
uint256 borrowAmount = _borrowAmountFrom({
|
|
703
|
+
// Get the amount of the loan, using the cached ruleset.
|
|
704
|
+
uint256 borrowAmount = _borrowAmountFrom({
|
|
705
|
+
loan: loan, revnetId: revnetId, collateralCount: collateralCount, currentRuleset: currentRuleset
|
|
706
|
+
});
|
|
633
707
|
|
|
634
708
|
// Revert if the bonding curve returns zero to prevent creating zero-amount loans.
|
|
635
709
|
if (borrowAmount == 0) revert REVLoans_ZeroBorrowAmount();
|
|
@@ -651,8 +725,11 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
651
725
|
beneficiary: beneficiary
|
|
652
726
|
});
|
|
653
727
|
|
|
728
|
+
// Cache the sender to avoid repeated ERC2771 context reads.
|
|
729
|
+
address sender = _msgSender();
|
|
730
|
+
|
|
654
731
|
// Mint the loan.
|
|
655
|
-
_mint({to:
|
|
732
|
+
_mint({to: sender, tokenId: loanId});
|
|
656
733
|
|
|
657
734
|
emit Borrow({
|
|
658
735
|
loanId: loanId,
|
|
@@ -663,7 +740,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
663
740
|
collateralCount: collateralCount,
|
|
664
741
|
sourceFeeAmount: sourceFeeAmount,
|
|
665
742
|
beneficiary: beneficiary,
|
|
666
|
-
caller:
|
|
743
|
+
caller: sender
|
|
667
744
|
});
|
|
668
745
|
|
|
669
746
|
return (loanId, loan);
|
|
@@ -686,18 +763,21 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
686
763
|
// Prevent cross-revnet accounting corruption: loan numbers must stay within the revnet's ID namespace.
|
|
687
764
|
if (startingLoanId + count > _ONE_TRILLION) revert REVLoans_LoanIdOverflow();
|
|
688
765
|
|
|
766
|
+
// Cache the sender to avoid repeated ERC2771 context reads inside the loop.
|
|
767
|
+
address sender = _msgSender();
|
|
768
|
+
|
|
689
769
|
// Iterate over the desired number of loans to check for liquidation.
|
|
690
770
|
for (uint256 i; i < count; i++) {
|
|
691
771
|
// Get a reference to the next loan ID.
|
|
692
772
|
uint256 loanId = _generateLoanId({revnetId: revnetId, loanNumber: startingLoanId + i});
|
|
693
773
|
|
|
774
|
+
// Check createdAt via storage ref first to avoid loading the full struct for empty slots.
|
|
775
|
+
// slither-disable-next-line incorrect-equality
|
|
776
|
+
if (_loanOf[loanId].createdAt == 0) continue;
|
|
777
|
+
|
|
694
778
|
// Get a reference to the loan being iterated on.
|
|
695
779
|
REVLoan memory loan = _loanOf[loanId];
|
|
696
780
|
|
|
697
|
-
// If the loan doesn't exist (repaid or already liquidated), skip past this gap and continue.
|
|
698
|
-
// slither-disable-next-line incorrect-equality
|
|
699
|
-
if (loan.createdAt == 0) continue;
|
|
700
|
-
|
|
701
781
|
// Keep a reference to the loan's owner.
|
|
702
782
|
address owner = _ownerOf(loanId);
|
|
703
783
|
|
|
@@ -722,7 +802,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
722
802
|
totalBorrowedFrom[revnetId][loan.source.terminal][loan.source.token] -= loan.amount;
|
|
723
803
|
}
|
|
724
804
|
|
|
725
|
-
emit Liquidate({loanId: loanId, revnetId: revnetId, loan: loan, caller:
|
|
805
|
+
emit Liquidate({loanId: loanId, revnetId: revnetId, loan: loan, caller: sender});
|
|
726
806
|
}
|
|
727
807
|
}
|
|
728
808
|
|
|
@@ -756,8 +836,14 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
756
836
|
override
|
|
757
837
|
returns (uint256 reallocatedLoanId, uint256 newLoanId, REVLoan memory reallocatedLoan, REVLoan memory newLoan)
|
|
758
838
|
{
|
|
839
|
+
// Cache the sender to avoid repeated ERC2771 context reads.
|
|
840
|
+
address sender = _msgSender();
|
|
841
|
+
|
|
759
842
|
// Make sure only the loan's owner can manage it.
|
|
760
|
-
|
|
843
|
+
{
|
|
844
|
+
address loanOwner = _ownerOf(loanId);
|
|
845
|
+
if (loanOwner != sender) revert REVLoans_Unauthorized(sender, loanOwner);
|
|
846
|
+
}
|
|
761
847
|
|
|
762
848
|
// Make sure the loan hasn't expired.
|
|
763
849
|
if (block.timestamp - _loanOf[loanId].createdAt > LOAN_LIQUIDATION_DURATION) {
|
|
@@ -815,8 +901,14 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
815
901
|
override
|
|
816
902
|
returns (uint256 paidOffLoanId, REVLoan memory paidOffloan)
|
|
817
903
|
{
|
|
904
|
+
// Cache the sender to avoid repeated ERC2771 context reads.
|
|
905
|
+
address sender = _msgSender();
|
|
906
|
+
|
|
818
907
|
// Make sure only the loan's owner can manage it.
|
|
819
|
-
|
|
908
|
+
{
|
|
909
|
+
address loanOwner = _ownerOf(loanId);
|
|
910
|
+
if (loanOwner != sender) revert REVLoans_Unauthorized(sender, loanOwner);
|
|
911
|
+
}
|
|
820
912
|
|
|
821
913
|
// Keep a reference to the fee being iterated on.
|
|
822
914
|
REVLoan storage loan = _loanOf[loanId];
|
|
@@ -887,7 +979,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
887
979
|
if (maxRepayBorrowAmount > repayBorrowAmount) {
|
|
888
980
|
_transferFrom({
|
|
889
981
|
from: address(this),
|
|
890
|
-
to: payable(
|
|
982
|
+
to: payable(sender),
|
|
891
983
|
token: sourceToken,
|
|
892
984
|
amount: maxRepayBorrowAmount - repayBorrowAmount
|
|
893
985
|
});
|
|
@@ -915,7 +1007,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
915
1007
|
function _acceptFundsFor(
|
|
916
1008
|
address token,
|
|
917
1009
|
uint256 amount,
|
|
918
|
-
JBSingleAllowance
|
|
1010
|
+
JBSingleAllowance calldata allowance
|
|
919
1011
|
)
|
|
920
1012
|
internal
|
|
921
1013
|
returns (uint256)
|
|
@@ -1095,6 +1187,10 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1095
1187
|
)
|
|
1096
1188
|
internal
|
|
1097
1189
|
{
|
|
1190
|
+
// Cache frequently-read storage fields to avoid repeated SLOAD.
|
|
1191
|
+
address sourceToken = loan.source.token;
|
|
1192
|
+
IJBPayoutTerminal sourceTerminal = loan.source.terminal;
|
|
1193
|
+
|
|
1098
1194
|
// Snapshot deltas from current state before writing.
|
|
1099
1195
|
uint256 addedBorrowAmount = newBorrowAmount > loan.amount ? newBorrowAmount - loan.amount : 0;
|
|
1100
1196
|
uint256 repaidBorrowAmount = loan.amount > newBorrowAmount ? loan.amount - newBorrowAmount : 0;
|
|
@@ -1143,9 +1239,8 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1143
1239
|
// cannot block all loan operations (matching the REV fee pattern above).
|
|
1144
1240
|
if (sourceFeeAmount > 0) {
|
|
1145
1241
|
// Increase the allowance for the source terminal.
|
|
1146
|
-
uint256 payValue =
|
|
1147
|
-
to: address(
|
|
1148
|
-
});
|
|
1242
|
+
uint256 payValue =
|
|
1243
|
+
_beforeTransferTo({to: address(sourceTerminal), token: sourceToken, amount: sourceFeeAmount});
|
|
1149
1244
|
|
|
1150
1245
|
// Pay the fee. If it fails, reclaim the allowance and give the amount back to the borrower.
|
|
1151
1246
|
// NOTE: When terminal.pay() reverts (e.g. due to a misconfigured terminal or paused payments),
|
|
@@ -1153,9 +1248,9 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1153
1248
|
// source fee portion. This is acceptable — it requires a broken/misconfigured source terminal and
|
|
1154
1249
|
// the borrower still pays the REV fee and protocol fee.
|
|
1155
1250
|
// slither-disable-next-line unused-return,arbitrary-send-eth
|
|
1156
|
-
try
|
|
1251
|
+
try sourceTerminal.pay{value: payValue}({
|
|
1157
1252
|
projectId: revnetId,
|
|
1158
|
-
token:
|
|
1253
|
+
token: sourceToken,
|
|
1159
1254
|
amount: sourceFeeAmount,
|
|
1160
1255
|
beneficiary: beneficiary,
|
|
1161
1256
|
minReturnedTokens: 0,
|
|
@@ -1165,13 +1260,11 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1165
1260
|
catch (bytes memory) {
|
|
1166
1261
|
// If the fee can't be processed, decrease the ERC-20 allowance and return the amount
|
|
1167
1262
|
// to the beneficiary instead.
|
|
1168
|
-
if (
|
|
1169
|
-
IERC20(
|
|
1170
|
-
.safeDecreaseAllowance({
|
|
1171
|
-
spender: address(loan.source.terminal), requestedDecrease: sourceFeeAmount
|
|
1172
|
-
});
|
|
1263
|
+
if (sourceToken != JBConstants.NATIVE_TOKEN) {
|
|
1264
|
+
IERC20(sourceToken)
|
|
1265
|
+
.safeDecreaseAllowance({spender: address(sourceTerminal), requestedDecrease: sourceFeeAmount});
|
|
1173
1266
|
}
|
|
1174
|
-
_transferFrom({from: address(this), to: beneficiary, token:
|
|
1267
|
+
_transferFrom({from: address(this), to: beneficiary, token: sourceToken, amount: sourceFeeAmount});
|
|
1175
1268
|
}
|
|
1176
1269
|
}
|
|
1177
1270
|
}
|
|
@@ -1364,7 +1457,8 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1364
1457
|
// Get a reference to the loan being paid off.
|
|
1365
1458
|
REVLoan storage paidOffLoan = _loanOf[paidOffLoanId];
|
|
1366
1459
|
|
|
1367
|
-
//
|
|
1460
|
+
// Copy the original loan's values. amount and collateral are written here so _adjust
|
|
1461
|
+
// can compute correct deltas, then _adjust overwrites them with the final values.
|
|
1368
1462
|
paidOffLoan.amount = loan.amount;
|
|
1369
1463
|
paidOffLoan.collateral = loan.collateral;
|
|
1370
1464
|
paidOffLoan.createdAt = loan.createdAt;
|
|
@@ -9,8 +9,8 @@ import {IJBProjects} from "@bananapus/core-v6/src/interfaces/IJBProjects.sol";
|
|
|
9
9
|
import {IJBTokenUriResolver} from "@bananapus/core-v6/src/interfaces/IJBTokenUriResolver.sol";
|
|
10
10
|
import {JBSingleAllowance} from "@bananapus/core-v6/src/structs/JBSingleAllowance.sol";
|
|
11
11
|
import {IPermit2} from "@uniswap/permit2/src/interfaces/IPermit2.sol";
|
|
12
|
-
import {REVLoan} from "
|
|
13
|
-
import {REVLoanSource} from "
|
|
12
|
+
import {REVLoan} from "../structs/REVLoan.sol";
|
|
13
|
+
import {REVLoanSource} from "../structs/REVLoanSource.sol";
|
|
14
14
|
|
|
15
15
|
/// @notice Manages loans against revnet token collateral.
|
|
16
16
|
interface IREVLoans {
|