Skip to content

Commit

Permalink
Merge branch 'master' into matt/val-43-borrower-revocation
Browse files Browse the repository at this point in the history
  • Loading branch information
venables committed Dec 1, 2022
2 parents c045bec + f175276 commit 47aec66
Show file tree
Hide file tree
Showing 9 changed files with 291 additions and 109 deletions.
103 changes: 56 additions & 47 deletions contracts/Loan.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import "./FundingVault.sol";
*/
contract Loan is ILoan {
using SafeMath for uint256;
uint256 constant RAY = 10**27;
uint256 constant RAY = 10 ** 27;

IServiceConfiguration private immutable _serviceConfiguration;
address private immutable _factory;
Expand Down Expand Up @@ -133,7 +133,6 @@ contract Loan is ILoan {
serviceConfiguration,
settings.duration,
settings.paymentPeriod,
settings.loanType,
settings.principal,
liquidityAsset
);
Expand Down Expand Up @@ -235,7 +234,10 @@ contract Loan is ILoan {
/**
* @dev Post ERC20 tokens as collateral
*/
function postFungibleCollateral(address asset, uint256 amount)
function postFungibleCollateral(
address asset,
uint256 amount
)
external
virtual
onlyPermittedBorrower
Expand All @@ -261,7 +263,10 @@ contract Loan is ILoan {
/**
* @dev Post ERC721 tokens as collateral
*/
function postNonFungibleCollateral(address asset, uint256 tokenId)
function postNonFungibleCollateral(
address asset,
uint256 tokenId
)
external
virtual
onlyPermittedBorrower
Expand Down Expand Up @@ -320,13 +325,9 @@ contract Loan is ILoan {
/**
* @dev Drawdown the Loan
*/
function drawdown(uint256 amount)
external
virtual
onlyPermittedBorrower
onlyBorrower
returns (uint256)
{
function drawdown(
uint256 amount
) external virtual onlyPermittedBorrower onlyBorrower returns (uint256) {
(_state, paymentDueDate) = LoanLib.drawdown(
amount,
fundingVault,
Expand All @@ -344,11 +345,9 @@ contract Loan is ILoan {
* @dev Prepay principal.
* @dev Only callable by open term loans
*/
function paydownPrincipal(uint256 amount)
external
onlyPermittedBorrower
onlyBorrower
{
function paydownPrincipal(
uint256 amount
) external onlyPermittedBorrower onlyBorrower {
require(outstandingPrincipal >= amount, "Loan: amount too high");
require(settings.loanType == ILoanType.Open, "Loan: invalid loan type");
LoanLib.paydownPrincipal(liquidityAsset, amount, fundingVault);
Expand All @@ -367,15 +366,21 @@ contract Loan is ILoan {
{
require(paymentsRemaining > 0, "Loan: No more payments remain");

ILoanFees memory _fees = previewFees(payment);
ILoanFees memory _fees = LoanLib.previewFees(
settings,
payment,
_serviceConfiguration.firstLossFeeBps(),
IPool(_pool).poolFeePercentOfInterest(),
block.timestamp,
paymentDueDate,
RAY
);

LoanLib.payFees(
liquidityAsset,
IPool(_pool).firstLossVault(),
_fees.firstLossFee,
IPool(_pool).feeVault(),
_fees.serviceFee,
_fees.originationFee
_fees
);

LoanLib.completePayment(
Expand All @@ -392,19 +397,18 @@ contract Loan is ILoan {
* @dev Preview fees for a given interest payment amount.
* @param amount allows previewing the fee for a full or prorated payment.
*/
function previewFees(uint256 amount)
public
view
returns (ILoanFees memory)
{
function previewFees(
uint256 amount
) public view returns (ILoanFees memory) {
return
LoanLib.previewFees(
settings,
amount,
_serviceConfiguration.firstLossFeeBps(),
IPool(_pool).poolFeePercentOfInterest(),
block.timestamp,
paymentDueDate
paymentDueDate,
RAY
);
}

Expand All @@ -418,35 +422,40 @@ contract Loan is ILoan {
atState(ILoanLifeCycleState.Active)
returns (uint256)
{
uint256 amount = payment.mul(paymentsRemaining);
uint256 scalingValue = RAY;

// We will pro-rate open term loans for their last month of service
// If payment is overdue, we use default value of RAY. scalingValue is in RAYS.
if (
settings.loanType == ILoanType.Open &&
paymentDueDate > block.timestamp
) {
// Calculate the scaling value
// RAY - ((paymentDueDate - blocktimestamp) * RAY / paymentPeriod (seconds))
scalingValue = RAY.sub(
(paymentDueDate - block.timestamp).mul(RAY).div(
settings.paymentPeriod * 1 days
)
);
// Adjust payment accordingly
amount = (payment * scalingValue) / RAY;
if (settings.loanType == ILoanType.Open) {
// If an open term loan payment is not overdue, we will prorate the
// payment
if (paymentDueDate > block.timestamp) {
// Calculate the scaling value
// RAY - ((paymentDueDate - blocktimestamp) * RAY / paymentPeriod (seconds))
scalingValue = RAY.sub(
(paymentDueDate - block.timestamp).mul(RAY).div(
settings.paymentPeriod * 1 days
)
);
}
} else {
// Fixed term loans must pay all outstanding interest payments and fees.
scalingValue = RAY.mul(paymentsRemaining);
}

ILoanFees memory _fees = previewFees(amount);
ILoanFees memory _fees = LoanLib.previewFees(
settings,
payment,
_serviceConfiguration.firstLossFeeBps(),
IPool(_pool).poolFeePercentOfInterest(),
block.timestamp,
paymentDueDate,
scalingValue
);

LoanLib.payFees(
liquidityAsset,
IPool(_pool).firstLossVault(),
_fees.firstLossFee,
IPool(_pool).feeVault(),
_fees.serviceFee,
_fees.originationFee.mul(scalingValue).div(RAY)
_fees
);

LoanLib.completePayment(
Expand All @@ -462,7 +471,7 @@ contract Loan is ILoan {
_state = ILoanLifeCycleState.Matured;

IPool(_pool).notifyLoanStateTransitioned();
return amount;
return payment;
}

/**
Expand Down
13 changes: 13 additions & 0 deletions contracts/controllers/PoolController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,19 @@ contract PoolController is IPoolController {
return _settings.withdrawGateBps;
}

/**
* @inheritdoc IPoolController
*/
function withdrawRequestPeriodDuration() public view returns (uint256) {
return
Math.min(
_settings.withdrawRequestPeriodDuration,
state() == IPoolLifeCycleState.Closed
? 1 days
: _settings.withdrawRequestPeriodDuration
);
}

/**
* @inheritdoc IPoolController
*/
Expand Down
27 changes: 13 additions & 14 deletions contracts/controllers/WithdrawController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ contract WithdrawController is IWithdrawController {
period = PoolLib.calculateCurrentWithdrawPeriod(
block.timestamp,
_pool.activatedAt(),
_pool.settings().withdrawRequestPeriodDuration
_pool.poolController().withdrawRequestPeriodDuration()
);
}

Expand Down Expand Up @@ -257,9 +257,7 @@ contract WithdrawController is IWithdrawController {
view
returns (uint256 shares)
{
IPoolWithdrawState memory withdrawState = _currentWithdrawState(
msg.sender
);
IPoolWithdrawState memory withdrawState = _currentWithdrawState(owner);
shares = PoolLib.calculateConversion(
assets,
withdrawState.redeemableShares,
Expand Down Expand Up @@ -495,8 +493,12 @@ contract WithdrawController is IWithdrawController {
/**
* @dev Cranks a lender
*/
function crankLender(address addr) internal {
_withdrawState[addr] = _currentWithdrawState(addr);
function crankLender(address addr)
internal
returns (IPoolWithdrawState memory state)
{
state = _currentWithdrawState(addr);
_withdrawState[addr] = state;
}

/*//////////////////////////////////////////////////////////////
Expand All @@ -511,18 +513,17 @@ contract WithdrawController is IWithdrawController {
onlyPool
returns (uint256 assets)
{
crankLender(owner);
IPoolWithdrawState memory state = crankLender(owner);

// Calculate how many assets should be transferred
IPoolWithdrawState memory state = _currentWithdrawState(owner);
assets = PoolLib.calculateConversion(
shares,
state.withdrawableAssets,
state.redeemableShares,
false
);

_performWithdraw(owner, shares, assets);
_performWithdraw(owner, state, shares, assets);
}

/**
Expand All @@ -533,30 +534,28 @@ contract WithdrawController is IWithdrawController {
onlyPool
returns (uint256 shares)
{
crankLender(owner);
IPoolWithdrawState memory state = crankLender(owner);

// Calculate how many shares should be burned
IPoolWithdrawState memory state = _currentWithdrawState(owner);
shares = PoolLib.calculateConversion(
assets,
state.redeemableShares,
state.withdrawableAssets,
true
);

_performWithdraw(owner, shares, assets);
_performWithdraw(owner, state, shares, assets);
}

/**
* @dev Perform the state update for a withdraw
*/
function _performWithdraw(
address owner,
IPoolWithdrawState memory currentState,
uint256 shares,
uint256 assets
) internal {
IPoolWithdrawState memory currentState = _currentWithdrawState(owner);

require(
assets <= currentState.withdrawableAssets,
"Pool: InsufficientBalance"
Expand Down
6 changes: 6 additions & 0 deletions contracts/controllers/interfaces/IPoolController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ interface IPoolController {
*/
function withdrawGate() external view returns (uint256);

/**
* @dev Returns the current withdraw request period duration in seconds. If the pool is closed,
* this is lowered (if needed) to 1 day.
*/
function withdrawRequestPeriodDuration() external view returns (uint256);

/**
* @dev
*/
Expand Down
Loading

0 comments on commit 47aec66

Please sign in to comment.