@rev-net/core-v6 0.0.7 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/ADMINISTRATION.md +186 -0
  2. package/ARCHITECTURE.md +87 -0
  3. package/README.md +4 -2
  4. package/RISKS.md +49 -0
  5. package/SKILLS.md +22 -2
  6. package/STYLE_GUIDE.md +482 -0
  7. package/foundry.toml +6 -6
  8. package/package.json +13 -10
  9. package/script/Deploy.s.sol +3 -2
  10. package/src/REVDeployer.sol +129 -72
  11. package/src/REVLoans.sol +174 -165
  12. package/src/interfaces/IREVDeployer.sol +111 -72
  13. package/src/interfaces/IREVLoans.sol +116 -76
  14. package/src/structs/REV721TiersHookFlags.sol +14 -0
  15. package/src/structs/REVBaseline721HookConfig.sol +27 -0
  16. package/src/structs/REVDeploy721TiersHookConfig.sol +2 -2
  17. package/test/REV.integrations.t.sol +4 -3
  18. package/test/REVAutoIssuanceFuzz.t.sol +12 -8
  19. package/test/REVDeployerAuditRegressions.t.sol +4 -3
  20. package/test/REVInvincibility.t.sol +8 -6
  21. package/test/REVInvincibilityHandler.sol +1 -0
  22. package/test/REVLifecycle.t.sol +4 -3
  23. package/test/REVLoans.invariants.t.sol +5 -3
  24. package/test/REVLoansAttacks.t.sol +4 -3
  25. package/test/REVLoansAuditRegressions.t.sol +13 -24
  26. package/test/REVLoansFeeRecovery.t.sol +4 -3
  27. package/test/REVLoansSourced.t.sol +4 -3
  28. package/test/REVLoansUnSourced.t.sol +4 -3
  29. package/test/REVLoans_AuditFindings.t.sol +644 -0
  30. package/test/TestEmptyBuybackSpecs.t.sol +4 -3
  31. package/test/TestPR09_ConversionDocumentation.t.sol +4 -3
  32. package/test/TestPR10_LiquidationBehavior.t.sol +4 -3
  33. package/test/TestPR11_LowFindings.t.sol +4 -3
  34. package/test/TestPR12_FlashLoanSurplus.t.sol +4 -3
  35. package/test/TestPR13_CrossSourceReallocation.t.sol +4 -3
  36. package/test/TestPR15_CashOutCallerValidation.t.sol +4 -3
  37. package/test/TestPR16_ZeroRepayment.t.sol +4 -3
  38. package/test/TestPR21_Uint112Overflow.t.sol +4 -3
  39. package/test/TestPR22_HookArrayOOB.t.sol +4 -3
  40. package/test/TestPR26_BurnHeldTokens.t.sol +4 -3
  41. package/test/TestPR27_CEIPattern.t.sol +4 -3
  42. package/test/TestPR29_SwapTerminalPermission.t.sol +4 -3
  43. package/test/TestPR32_MixedFixes.t.sol +4 -3
  44. package/test/TestSplitWeightAdjustment.t.sol +445 -0
  45. package/test/TestSplitWeightE2E.t.sol +528 -0
  46. package/test/TestSplitWeightFork.t.sol +821 -0
  47. package/test/TestStageTransitionBorrowable.t.sol +4 -3
  48. package/test/fork/ForkTestBase.sol +617 -0
  49. package/test/fork/TestCashOutFork.t.sol +245 -0
  50. package/test/fork/TestLoanBorrowFork.t.sol +163 -0
  51. package/test/fork/TestLoanLiquidationFork.t.sol +129 -0
  52. package/test/fork/TestLoanReallocateFork.t.sol +103 -0
  53. package/test/fork/TestLoanRepayFork.t.sol +184 -0
  54. package/test/fork/TestSplitWeightFork.t.sol +186 -0
  55. package/test/mock/MockBuybackDataHook.sol +11 -4
  56. package/test/mock/MockBuybackDataHookMintPath.sol +11 -3
  57. package/test/regression/TestI20_CumulativeLoanCounter.t.sol +6 -5
  58. package/test/regression/TestL27_LiquidateGapHandling.t.sol +7 -6
  59. package/SECURITY.md +0 -68
package/src/REVLoans.sol CHANGED
@@ -57,14 +57,15 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
57
57
 
58
58
  error REVLoans_CollateralExceedsLoan(uint256 collateralToReturn, uint256 loanCollateral);
59
59
  error REVLoans_InvalidPrepaidFeePercent(uint256 prepaidFeePercent, uint256 min, uint256 max);
60
- error REVLoans_NotEnoughCollateral();
61
- error REVLoans_OverflowAlert(uint256 value, uint256 limit);
62
- error REVLoans_OverMaxRepayBorrowAmount(uint256 maxRepayBorrowAmount, uint256 repayBorrowAmount);
63
- error REVLoans_PermitAllowanceNotEnough(uint256 allowanceAmount, uint256 requiredAmount);
60
+ error REVLoans_InvalidTerminal(address terminal, uint256 revnetId);
61
+ error REVLoans_LoanExpired(uint256 timeSinceLoanCreated, uint256 loanLiquidationDuration);
64
62
  error REVLoans_NewBorrowAmountGreaterThanLoanAmount(uint256 newBorrowAmount, uint256 loanAmount);
65
63
  error REVLoans_NoMsgValueAllowed();
64
+ error REVLoans_NotEnoughCollateral();
66
65
  error REVLoans_NothingToRepay();
67
- error REVLoans_LoanExpired(uint256 timeSinceLoanCreated, uint256 loanLiquidationDuration);
66
+ error REVLoans_OverMaxRepayBorrowAmount(uint256 maxRepayBorrowAmount, uint256 repayBorrowAmount);
67
+ error REVLoans_OverflowAlert(uint256 value, uint256 limit);
68
+ error REVLoans_PermitAllowanceNotEnough(uint256 allowanceAmount, uint256 requiredAmount);
68
69
  error REVLoans_ReallocatingMoreCollateralThanBorrowedAmountAllows(uint256 newBorrowAmount, uint256 loanAmount);
69
70
  error REVLoans_SourceMismatch();
70
71
  error REVLoans_Unauthorized(address caller, address owner);
@@ -257,6 +258,13 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
257
258
  return _determineSourceFeeAmount(loan, amount);
258
259
  }
259
260
 
261
+ /// @notice The revnet ID for the loan with the provided loan ID.
262
+ /// @param loanId The loan ID of the loan to get the revnet ID of.
263
+ /// @return The ID of the revnet.
264
+ function revnetIdOfLoanWith(uint256 loanId) public pure override returns (uint256) {
265
+ return loanId / _ONE_TRILLION;
266
+ }
267
+
260
268
  /// @notice Returns the URI where the ERC-721 standard JSON of a loan is hosted.
261
269
  /// @param loanId The ID of the loan to get a URI of.
262
270
  /// @return The token URI to use for the provided `loanId`.
@@ -271,13 +279,6 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
271
279
  return resolver.getUri(loanId);
272
280
  }
273
281
 
274
- /// @notice The revnet ID for the loan with the provided loan ID.
275
- /// @param loanId The loan ID of the loan to get the revnet ID of.
276
- /// @return The ID of the revnet.
277
- function revnetIdOfLoanWith(uint256 loanId) public pure override returns (uint256) {
278
- return loanId / _ONE_TRILLION;
279
- }
280
-
281
282
  //*********************************************************************//
282
283
  // -------------------------- internal views ------------------------- //
283
284
  //*********************************************************************//
@@ -556,6 +557,11 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
556
557
  // A loan needs to have collateral.
557
558
  if (collateralCount == 0) revert REVLoans_ZeroCollateralLoanIsInvalid();
558
559
 
560
+ // Make sure the source terminal is registered in the directory for this revnet.
561
+ if (!DIRECTORY.isTerminalOf(revnetId, IJBTerminal(address(source.terminal)))) {
562
+ revert REVLoans_InvalidTerminal(address(source.terminal), revnetId);
563
+ }
564
+
559
565
  // Make sure the prepaid fee percent is between `MIN_PREPAID_FEE_PERCENT` and `MAX_PREPAID_FEE_PERCENT`. Meaning
560
566
  // an 16 year loan can be paid upfront with a
561
567
  // payment of 50% of the borrowed assets, the cheapest possible rate.
@@ -839,6 +845,56 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
839
845
  // --------------------- internal transactions ----------------------- //
840
846
  //*********************************************************************//
841
847
 
848
+ /// @notice Accepts an incoming token.
849
+ /// @param token The token being accepted.
850
+ /// @param amount The number of tokens being accepted.
851
+ /// @param allowance The permit2 context.
852
+ /// @return amount The number of tokens which have been accepted.
853
+ function _acceptFundsFor(
854
+ address token,
855
+ uint256 amount,
856
+ JBSingleAllowance memory allowance
857
+ )
858
+ internal
859
+ returns (uint256)
860
+ {
861
+ // If the token is the native token, override `amount` with `msg.value`.
862
+ if (token == JBConstants.NATIVE_TOKEN) return msg.value;
863
+
864
+ // If the token is not native, revert if there is a non-zero `msg.value`.
865
+ if (msg.value != 0) revert REVLoans_NoMsgValueAllowed();
866
+
867
+ // Check if the metadata contains permit data.
868
+ if (allowance.amount != 0) {
869
+ // Make sure the permit allowance is enough for this payment. If not we revert early.
870
+ if (allowance.amount < amount) {
871
+ revert REVLoans_PermitAllowanceNotEnough(allowance.amount, amount);
872
+ }
873
+
874
+ // Keep a reference to the permit rules.
875
+ IAllowanceTransfer.PermitSingle memory permitSingle = IAllowanceTransfer.PermitSingle({
876
+ details: IAllowanceTransfer.PermitDetails({
877
+ token: token, amount: allowance.amount, expiration: allowance.expiration, nonce: allowance.nonce
878
+ }),
879
+ spender: address(this),
880
+ sigDeadline: allowance.sigDeadline
881
+ });
882
+
883
+ // Set the allowance to `spend` tokens for the user.
884
+ try PERMIT2.permit({owner: _msgSender(), permitSingle: permitSingle, signature: allowance.signature}) {}
885
+ catch (bytes memory) {}
886
+ }
887
+
888
+ // Get a reference to the balance before receiving tokens.
889
+ uint256 balanceBefore = _balanceOf(token);
890
+
891
+ // Transfer tokens to this terminal from the msg sender.
892
+ _transferFrom({from: _msgSender(), to: payable(address(this)), token: token, amount: amount});
893
+
894
+ // The amount should reflect the change in balance.
895
+ return _balanceOf(token) - balanceBefore;
896
+ }
897
+
842
898
  /// @notice Adds collateral to a loan by burning the collateral tokens permanently.
843
899
  /// @dev The collateral tokens are burned via the controller, not held in escrow. They are only re-minted if the
844
900
  /// loan is repaid. If the loan expires and is liquidated, the burned collateral is permanently lost.
@@ -1028,67 +1084,118 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
1028
1084
  }
1029
1085
  }
1030
1086
 
1031
- /// @notice Accepts an incoming token.
1032
- /// @param token The token being accepted.
1033
- /// @param amount The number of tokens being accepted.
1034
- /// @param allowance The permit2 context.
1035
- /// @return amount The number of tokens which have been accepted.
1036
- function _acceptFundsFor(
1037
- address token,
1038
- uint256 amount,
1039
- JBSingleAllowance memory allowance
1087
+ /// @notice Logic to be triggered before transferring tokens from this contract.
1088
+ /// @param to The address the transfer is going to.
1089
+ /// @param token The token being transferred.
1090
+ /// @param amount The number of tokens being transferred, as a fixed point number with the same number of decimals
1091
+ /// as the token specifies.
1092
+ /// @return payValue The value to attach to the transaction being sent.
1093
+ function _beforeTransferTo(address to, address token, uint256 amount) internal returns (uint256) {
1094
+ // If the token is the native token, no allowance needed.
1095
+ if (token == JBConstants.NATIVE_TOKEN) return amount;
1096
+ IERC20(token).safeIncreaseAllowance(to, amount);
1097
+ return 0;
1098
+ }
1099
+
1100
+ /// @notice Reallocates collateral from a loan by making a new loan based on the original, with reduced collateral.
1101
+ /// @param loanId The ID of the loan to reallocate collateral from.
1102
+ /// @param revnetId The ID of the revnet the loan is from.
1103
+ /// @param collateralCountToRemove The amount of collateral to remove from the loan.
1104
+ /// @return reallocatedLoanId The ID of the loan.
1105
+ /// @return reallocatedLoan The reallocated loan.
1106
+ function _reallocateCollateralFromLoan(
1107
+ uint256 loanId,
1108
+ uint256 revnetId,
1109
+ uint256 collateralCountToRemove
1040
1110
  )
1041
1111
  internal
1042
- returns (uint256)
1112
+ returns (uint256 reallocatedLoanId, REVLoan storage reallocatedLoan)
1043
1113
  {
1044
- // If the token is the native token, override `amount` with `msg.value`.
1045
- if (token == JBConstants.NATIVE_TOKEN) return msg.value;
1114
+ // Burn the original loan.
1115
+ _burn(loanId);
1046
1116
 
1047
- // If the token is not native, revert if there is a non-zero `msg.value`.
1048
- if (msg.value != 0) revert REVLoans_NoMsgValueAllowed();
1117
+ // Keep a reference to loan having its collateral reduced.
1118
+ REVLoan storage loan = _loanOf[loanId];
1049
1119
 
1050
- // Check if the metadata contains permit data.
1051
- if (allowance.amount != 0) {
1052
- // Make sure the permit allowance is enough for this payment. If not we revert early.
1053
- if (allowance.amount < amount) {
1054
- revert REVLoans_PermitAllowanceNotEnough(allowance.amount, amount);
1055
- }
1120
+ // Make sure there is enough collateral to transfer.
1121
+ if (collateralCountToRemove > loan.collateral) revert REVLoans_NotEnoughCollateral();
1056
1122
 
1057
- // Keep a reference to the permit rules.
1058
- IAllowanceTransfer.PermitSingle memory permitSingle = IAllowanceTransfer.PermitSingle({
1059
- details: IAllowanceTransfer.PermitDetails({
1060
- token: token, amount: allowance.amount, expiration: allowance.expiration, nonce: allowance.nonce
1061
- }),
1062
- spender: address(this),
1063
- sigDeadline: allowance.sigDeadline
1064
- });
1123
+ // Keep a reference to the new collateral amount.
1124
+ uint256 newCollateralCount = loan.collateral - collateralCountToRemove;
1065
1125
 
1066
- // Set the allowance to `spend` tokens for the user.
1067
- try PERMIT2.permit({owner: _msgSender(), permitSingle: permitSingle, signature: allowance.signature}) {}
1068
- catch (bytes memory) {}
1126
+ // Keep a reference to the new borrow amount.
1127
+ uint256 borrowAmount = _borrowAmountFrom({loan: loan, revnetId: revnetId, collateralCount: newCollateralCount});
1128
+
1129
+ // Make sure the borrow amount is not less than the original loan's amount.
1130
+ if (borrowAmount < loan.amount) {
1131
+ revert REVLoans_ReallocatingMoreCollateralThanBorrowedAmountAllows(borrowAmount, loan.amount);
1069
1132
  }
1070
1133
 
1071
- // Get a reference to the balance before receiving tokens.
1072
- uint256 balanceBefore = _balanceOf(token);
1134
+ // Get a reference to the replacement loan ID.
1135
+ reallocatedLoanId = _generateLoanId({revnetId: revnetId, loanNumber: ++totalLoansBorrowedFor[revnetId]});
1073
1136
 
1074
- // Transfer tokens to this terminal from the msg sender.
1075
- _transferFrom({from: _msgSender(), to: payable(address(this)), token: token, amount: amount});
1137
+ // Get a reference to the loan being created.
1138
+ reallocatedLoan = _loanOf[reallocatedLoanId];
1076
1139
 
1077
- // The amount should reflect the change in balance.
1078
- return _balanceOf(token) - balanceBefore;
1140
+ // Set the reallocated loan's values the same as the original loan.
1141
+ reallocatedLoan.amount = loan.amount;
1142
+ reallocatedLoan.collateral = loan.collateral;
1143
+ reallocatedLoan.createdAt = loan.createdAt;
1144
+ reallocatedLoan.prepaidFeePercent = loan.prepaidFeePercent;
1145
+ reallocatedLoan.prepaidDuration = loan.prepaidDuration;
1146
+ reallocatedLoan.source = loan.source;
1147
+
1148
+ // Reduce the collateral of the reallocated loan.
1149
+ _adjust({
1150
+ loan: reallocatedLoan,
1151
+ revnetId: revnetId,
1152
+ newBorrowAmount: reallocatedLoan.amount, // Don't change the borrow amount.
1153
+ newCollateralCount: newCollateralCount,
1154
+ sourceFeeAmount: 0,
1155
+ beneficiary: payable(_msgSender()) // use the msgSender as the beneficiary, who will have the returned
1156
+ // collateral tokens debited from their balance for the new loan.
1157
+ });
1158
+
1159
+ // Mint the replacement loan.
1160
+ _mint({to: _msgSender(), tokenId: reallocatedLoanId});
1161
+
1162
+ // Clear stale loan data for gas refund.
1163
+ delete _loanOf[loanId];
1164
+
1165
+ emit ReallocateCollateral({
1166
+ loanId: loanId,
1167
+ revnetId: revnetId,
1168
+ reallocatedLoanId: reallocatedLoanId,
1169
+ reallocatedLoan: reallocatedLoan,
1170
+ removedCollateralCount: collateralCountToRemove,
1171
+ caller: _msgSender()
1172
+ });
1079
1173
  }
1080
1174
 
1081
- /// @notice Logic to be triggered before transferring tokens from this contract.
1082
- /// @param to The address the transfer is going to.
1083
- /// @param token The token being transferred.
1084
- /// @param amount The number of tokens being transferred, as a fixed point number with the same number of decimals
1085
- /// as the token specifies.
1086
- /// @return payValue The value to attach to the transaction being sent.
1087
- function _beforeTransferTo(address to, address token, uint256 amount) internal returns (uint256) {
1088
- // If the token is the native token, no allowance needed.
1089
- if (token == JBConstants.NATIVE_TOKEN) return amount;
1090
- IERC20(token).safeIncreaseAllowance(to, amount);
1091
- return 0;
1175
+ /// @notice Pays off a loan.
1176
+ /// @param loan The loan being paid off.
1177
+ /// @param revnetId The ID of the revnet the loan is being paid off in.
1178
+ /// @param repaidBorrowAmount The amount being paid off, denominated in the token of the source's accounting
1179
+ /// context.
1180
+ function _removeFrom(REVLoan memory loan, uint256 revnetId, uint256 repaidBorrowAmount) internal {
1181
+ // Decrement the total amount of a token being loaned out by the revnet from its terminal.
1182
+ totalBorrowedFrom[revnetId][loan.source.terminal][loan.source.token] -= repaidBorrowAmount;
1183
+
1184
+ // Increase the allowance for the beneficiary.
1185
+ uint256 payValue = _beforeTransferTo({
1186
+ to: address(loan.source.terminal), token: loan.source.token, amount: repaidBorrowAmount
1187
+ });
1188
+
1189
+ // Add the loaned amount back to the revnet.
1190
+ // slither-disable-next-line arbitrary-send-eth
1191
+ loan.source.terminal.addToBalanceOf{value: payValue}({
1192
+ projectId: revnetId,
1193
+ token: loan.source.token,
1194
+ amount: repaidBorrowAmount,
1195
+ shouldReturnHeldFees: false,
1196
+ memo: "Paying off loan",
1197
+ metadata: bytes(abi.encodePacked(REV_ID))
1198
+ });
1092
1199
  }
1093
1200
 
1094
1201
  /// @notice Pays down a loan.
@@ -1117,6 +1224,9 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
1117
1224
  // If the loan will carry no more amount or collateral, store its changes directly.
1118
1225
  // slither-disable-next-line incorrect-equality
1119
1226
  if (collateralCountToReturn == loan.collateral) {
1227
+ // Snapshot the loan to memory BEFORE _adjust zeroes the storage pointer.
1228
+ REVLoan memory loanSnapshot = loan;
1229
+
1120
1230
  // Borrow in.
1121
1231
  _adjust({
1122
1232
  loan: loan,
@@ -1127,15 +1237,15 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
1127
1237
  beneficiary: beneficiary
1128
1238
  });
1129
1239
 
1130
- // Snapshot the loan to memory before deleting storage.
1131
- REVLoan memory loanSnapshot = loan;
1240
+ // Snapshot the zeroed loan for the return value (reflects post-repay state).
1241
+ REVLoan memory paidOffSnapshot = loan;
1132
1242
 
1133
1243
  emit RepayLoan({
1134
1244
  loanId: loanId,
1135
1245
  revnetId: revnetId,
1136
1246
  paidOffLoanId: loanId,
1137
1247
  loan: loanSnapshot,
1138
- paidOffLoan: loanSnapshot,
1248
+ paidOffLoan: paidOffSnapshot,
1139
1249
  repayBorrowAmount: repayBorrowAmount,
1140
1250
  sourceFeeAmount: sourceFeeAmount,
1141
1251
  collateralCountToReturn: collateralCountToReturn,
@@ -1146,7 +1256,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
1146
1256
  // Clear stale loan data for gas refund.
1147
1257
  delete _loanOf[loanId];
1148
1258
 
1149
- return (loanId, loanSnapshot);
1259
+ return (loanId, paidOffSnapshot);
1150
1260
  } else {
1151
1261
  // Make a new loan with the remaining amount and collateral.
1152
1262
  // Get a reference to the replacement loan ID.
@@ -1196,107 +1306,6 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
1196
1306
  }
1197
1307
  }
1198
1308
 
1199
- /// @notice Reallocates collateral from a loan by making a new loan based on the original, with reduced collateral.
1200
- /// @param loanId The ID of the loan to reallocate collateral from.
1201
- /// @param revnetId The ID of the revnet the loan is from.
1202
- /// @param collateralCountToRemove The amount of collateral to remove from the loan.
1203
- /// @return reallocatedLoanId The ID of the loan.
1204
- /// @return reallocatedLoan The reallocated loan.
1205
- function _reallocateCollateralFromLoan(
1206
- uint256 loanId,
1207
- uint256 revnetId,
1208
- uint256 collateralCountToRemove
1209
- )
1210
- internal
1211
- returns (uint256 reallocatedLoanId, REVLoan storage reallocatedLoan)
1212
- {
1213
- // Burn the original loan.
1214
- _burn(loanId);
1215
-
1216
- // Keep a reference to loan having its collateral reduced.
1217
- REVLoan storage loan = _loanOf[loanId];
1218
-
1219
- // Make sure there is enough collateral to transfer.
1220
- if (collateralCountToRemove > loan.collateral) revert REVLoans_NotEnoughCollateral();
1221
-
1222
- // Keep a reference to the new collateral amount.
1223
- uint256 newCollateralCount = loan.collateral - collateralCountToRemove;
1224
-
1225
- // Keep a reference to the new borrow amount.
1226
- uint256 borrowAmount = _borrowAmountFrom({loan: loan, revnetId: revnetId, collateralCount: newCollateralCount});
1227
-
1228
- // Make sure the borrow amount is not less than the original loan's amount.
1229
- if (borrowAmount < loan.amount) {
1230
- revert REVLoans_ReallocatingMoreCollateralThanBorrowedAmountAllows(borrowAmount, loan.amount);
1231
- }
1232
-
1233
- // Get a reference to the replacement loan ID.
1234
- reallocatedLoanId = _generateLoanId({revnetId: revnetId, loanNumber: ++totalLoansBorrowedFor[revnetId]});
1235
-
1236
- // Get a reference to the loan being created.
1237
- reallocatedLoan = _loanOf[reallocatedLoanId];
1238
-
1239
- // Set the reallocated loan's values the same as the original loan.
1240
- reallocatedLoan.amount = loan.amount;
1241
- reallocatedLoan.collateral = loan.collateral;
1242
- reallocatedLoan.createdAt = loan.createdAt;
1243
- reallocatedLoan.prepaidFeePercent = loan.prepaidFeePercent;
1244
- reallocatedLoan.prepaidDuration = loan.prepaidDuration;
1245
- reallocatedLoan.source = loan.source;
1246
-
1247
- // Reduce the collateral of the reallocated loan.
1248
- _adjust({
1249
- loan: reallocatedLoan,
1250
- revnetId: revnetId,
1251
- newBorrowAmount: reallocatedLoan.amount, // Don't change the borrow amount.
1252
- newCollateralCount: newCollateralCount,
1253
- sourceFeeAmount: 0,
1254
- beneficiary: payable(_msgSender()) // use the msgSender as the beneficiary, who will have the returned
1255
- // collateral tokens debited from their balance for the new loan.
1256
- });
1257
-
1258
- // Mint the replacement loan.
1259
- _mint({to: _msgSender(), tokenId: reallocatedLoanId});
1260
-
1261
- // Clear stale loan data for gas refund.
1262
- delete _loanOf[loanId];
1263
-
1264
- emit ReallocateCollateral({
1265
- loanId: loanId,
1266
- revnetId: revnetId,
1267
- reallocatedLoanId: reallocatedLoanId,
1268
- reallocatedLoan: reallocatedLoan,
1269
- removedCollateralCount: collateralCountToRemove,
1270
- caller: _msgSender()
1271
- });
1272
- }
1273
-
1274
- /// @notice Pays off a loan.
1275
- /// @param loan The loan being paid off.
1276
- /// @param revnetId The ID of the revnet the loan is being paid off in.
1277
- /// @param repaidBorrowAmount The amount being paid off, denominated in the token of the source's accounting
1278
- /// context.
1279
- function _removeFrom(REVLoan memory loan, uint256 revnetId, uint256 repaidBorrowAmount) internal {
1280
- // Decrement the total amount of a token being loaned out by the revnet from its terminal.
1281
- totalBorrowedFrom[revnetId][loan.source.terminal][loan.source.token] -= repaidBorrowAmount;
1282
-
1283
- // Increase the allowance for the beneficiary.
1284
- uint256 payValue = _beforeTransferTo({
1285
- to: address(loan.source.terminal), token: loan.source.token, amount: repaidBorrowAmount
1286
- });
1287
-
1288
- // Add the loaned amount back to the revnet.
1289
- // slither-disable-next-line arbitrary-send-eth
1290
- loan.source.terminal.addToBalanceOf{value: payValue}({
1291
- projectId: revnetId,
1292
- token: loan.source.token,
1293
- amount: repaidBorrowAmount,
1294
- shouldReturnHeldFees: false,
1295
- memo: "Paying off loan",
1296
- metadata: bytes(abi.encodePacked(REV_ID))
1297
- });
1298
- }
1299
-
1300
1309
  /// @notice Returns collateral from a loan.
1301
1310
  /// @param revnetId The ID of the revnet the loan is being returned in.
1302
1311
  /// @param collateralCount The amount of collateral being returned from the loan.