Skip to content

Commit

Permalink
Merge branch 'master' into origination-fees
Browse files Browse the repository at this point in the history
  • Loading branch information
bricestacey committed Oct 26, 2022
2 parents 3cee101 + 838c973 commit 57c320a
Show file tree
Hide file tree
Showing 25 changed files with 785 additions and 61 deletions.
11 changes: 9 additions & 2 deletions contracts/Loan.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ contract Loan is ILoan {
uint256 public immutable payment;
uint256 public paymentsRemaining;
uint256 public paymentDueDate;
uint256 public latePaymentFee;
uint256 public originationFeeBps;
uint256 public originationFee;

Expand Down Expand Up @@ -99,6 +100,7 @@ contract Loan is ILoan {
address liquidityAsset_,
uint256 principal_,
uint256 dropDeadTimestamp,
uint256 latePaymentFee_,
uint256 originationFeeBps_
) {
_serviceConfiguration = serviceConfiguration;
Expand All @@ -114,6 +116,7 @@ contract Loan is ILoan {
apr = apr_;
liquidityAsset = liquidityAsset_;
principal = principal_;
latePaymentFee = latePaymentFee_;

LoanLib.validateLoan(
serviceConfiguration,
Expand Down Expand Up @@ -320,7 +323,9 @@ contract Loan is ILoan {
.previewFees(
payment,
_serviceConfiguration.firstLossFeeBps(),
IPool(_pool).poolFeePercentOfInterest()
IPool(_pool).poolFeePercentOfInterest(),
latePaymentFee,
paymentDueDate
);

LoanLib.payFees(
Expand Down Expand Up @@ -349,7 +354,9 @@ contract Loan is ILoan {
.previewFees(
amount,
_serviceConfiguration.firstLossFeeBps(),
IPool(_pool).poolFeePercentOfInterest()
IPool(_pool).poolFeePercentOfInterest(),
latePaymentFee,
paymentDueDate
);

LoanLib.payFees(
Expand Down
2 changes: 2 additions & 0 deletions contracts/LoanFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ contract LoanFactory {
address liquidityAsset,
uint256 principal,
uint256 dropDeadDate,
uint256 latePaymentFee,
uint256 originationFee
) public virtual returns (address LoanAddress) {
require(
Expand All @@ -59,6 +60,7 @@ contract LoanFactory {
liquidityAsset,
principal,
dropDeadDate,
latePaymentFee,
originationFee
);
address addr = address(loan);
Expand Down
104 changes: 96 additions & 8 deletions contracts/Pool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -511,8 +511,21 @@ contract Pool is IPool, ERC20 {
}

/**
* @dev
* @dev Returns the maximum number of `shares` that can be
* cancelled from being requested for a redemption.
*
* Note: This is equivalent of EIP-4626 `maxRedeem`
*/
function maxRequestCancellation(address owner)
public
view
returns (uint256 maxShares)
{
maxShares = PoolLib.calculateMaxCancellation(
_withdrawState[owner],
_poolSettings.requestCancellationFeeBps
);
}

/**
* @dev Returns the maximum number of `shares` that can be
Expand Down Expand Up @@ -558,7 +571,7 @@ contract Pool is IPool, ERC20 {
* returns an estimated amount of underlying that will be received if this
* were immeidately executed.
*
* Emits a {RedeemRequested} event.
* Emits a {WithdrawRequested} event.
*/
function requestRedeem(uint256 shares)
external
Expand All @@ -570,6 +583,23 @@ contract Pool is IPool, ERC20 {
_requestWithdraw(msg.sender, assets, shares);
}

/**
* @dev Cancels a redeem request for a specific number of `shares` from
* owner and returns an estimated amnount of underlying that equates to
* this number of shares.
*
* Emits a {WithdrawRequestCancelled} event.
*/
function cancelRedeemRequest(uint256 shares)
external
onlyActivatedPool
onlyLender
returns (uint256 assets)
{
assets = convertToAssets(shares);
_cancelWithdraw(msg.sender, assets, shares);
}

/**
* @dev Returns the maximum amount of underlying `assets` that can be
* requested to be withdrawn from the owner balance with a single
Expand Down Expand Up @@ -622,6 +652,23 @@ contract Pool is IPool, ERC20 {
_requestWithdraw(msg.sender, assets, shares);
}

/**
* @dev Cancels a withdraw request for a specific values of `assets` from
* owner and returns an estimated number of shares that equates to
* this number of assets.
*
* Emits a {WithdrawRequestCancelled} event.
*/
function cancelWithdrawRequest(uint256 assets)
external
onlyActivatedPool
onlyLender
returns (uint256 shares)
{
shares = convertToShares(assets);
_cancelWithdraw(msg.sender, assets, shares);
}

/**
* @dev Returns the amount of shares that should be considered interest
* bearing for a given owner. This number is their balance, minus their
Expand Down Expand Up @@ -720,7 +767,7 @@ contract Pool is IPool, ERC20 {
}

/**
* @dev Performs a redeem request for the owner, including paying any fees.
* @dev Performs a withdraw request for the owner, including paying any fees.
*/
function _requestWithdraw(
address owner,
Expand All @@ -730,7 +777,6 @@ contract Pool is IPool, ERC20 {
require(maxRedeemRequest(owner) >= shares, "Pool: InsufficientBalance");

uint256 currentPeriod = withdrawPeriod();
uint256 nextPeriod = withdrawPeriod().add(1);
uint256 feeShares = PoolLib.calculateRequestFee(
shares,
_poolSettings.requestFeeBps
Expand All @@ -740,27 +786,69 @@ contract Pool is IPool, ERC20 {
_burn(owner, feeShares);

// Update the requested amount from the user
_withdrawState[owner] = PoolLib.caclulateWithdrawState(
_withdrawState[owner] = PoolLib.calculateWithdrawStateForRequest(
_withdrawState[owner],
currentPeriod,
nextPeriod,
shares
);

// Add the address to the addresslist
_withdrawAddresses.add(owner);

// Update the global amount
_globalWithdrawState = PoolLib.caclulateWithdrawState(
_globalWithdrawState = PoolLib.calculateWithdrawStateForRequest(
_globalWithdrawState,
currentPeriod,
nextPeriod,
shares
);

emit WithdrawRequested(msg.sender, assets, shares);
}

/**
* @dev Cancels a withdraw request for the owner, including paying any fees.
* A cancellation can only occur before the
*/
function _cancelWithdraw(
address owner,
uint256 assets,
uint256 shares
) internal {
// TODO: If we move to a lighter crank, we must run it here before this method continues
require(
maxRequestCancellation(owner) >= shares,
"Pool: InsufficientBalance"
);

uint256 currentPeriod = withdrawPeriod();
uint256 feeShares = PoolLib.calculateRequestFee(
shares,
_poolSettings.requestFeeBps
);

// Pay the Fee
_burn(owner, feeShares);

// Update the requested amount from the user
_withdrawState[owner] = PoolLib.calculateWithdrawStateForCancellation(
_withdrawState[owner],
currentPeriod,
shares
);

// Add the address to the addresslist
_withdrawAddresses.add(owner);

// Update the global amount
_globalWithdrawState = PoolLib.calculateWithdrawStateForCancellation(
_globalWithdrawState,
currentPeriod,
shares
);

emit WithdrawRequestCancelled(msg.sender, assets, shares);
}

/**
* @dev Set the pool lifecycle state. If the state changes, this method
* will also update the poolActivatedAt variable
Expand Down
19 changes: 19 additions & 0 deletions contracts/ServiceConfiguration.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ contract ServiceConfiguration is AccessControl, IServiceConfiguration {

uint256 public firstLossFeeBps = 500;

address public tosAcceptanceRegistry;

/**
* @dev Holds a reference to valid LoanFactories
*/
Expand Down Expand Up @@ -53,6 +55,11 @@ contract ServiceConfiguration is AccessControl, IServiceConfiguration {
*/
event LoanFactorySet(address indexed factory, bool isValid);

/**
* @dev Emitted when the TermsOfServiceRegistry is set
*/
event TermsOfServiceRegistrySet(address indexed registry);

/**
* @dev Constructor for the contract, which sets up the default roles and
* owners.
Expand Down Expand Up @@ -107,4 +114,16 @@ contract ServiceConfiguration is AccessControl, IServiceConfiguration {
isLoanFactory[addr] = isValid;
emit LoanFactorySet(addr, isValid);
}

/**
* @inheritdoc IServiceConfiguration
*/
function setToSAcceptanceRegistry(address addr)
external
override
onlyOperator
{
tosAcceptanceRegistry = addr;
emit TermsOfServiceRegistrySet(addr);
}
}
10 changes: 10 additions & 0 deletions contracts/interfaces/IPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ struct IPoolConfigurableSettings {
uint256 maxCapacity; // amount
uint256 endDate; // epoch seconds
uint256 requestFeeBps; // bips
uint256 requestCancellationFeeBps; // bips
uint256 withdrawGateBps; // Percent of liquidity pool available to withdraw, represented in BPS
uint256 firstLossInitialMinimum; // amount
uint256 withdrawRequestPeriodDuration; // seconds (e.g. 30 days)
Expand Down Expand Up @@ -86,6 +87,15 @@ interface IPool is IERC4626 {
uint256 shares
);

/**
* @dev Emitted when a withdrawal is requested.
*/
event WithdrawRequestCancelled(
address indexed lender,
uint256 assets,
uint256 shares
);

/**
* @dev Emitted when pool settings are updated.
*/
Expand Down
8 changes: 8 additions & 0 deletions contracts/interfaces/IServiceConfiguration.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ interface IServiceConfiguration is IAccessControl {

function isLiquidityAsset(address addr) external view returns (bool);

function tosAcceptanceRegistry() external view returns (address);

/**
* @dev checks if an address is a valid loan factory
* @param addr Address of loan factory
Expand All @@ -31,4 +33,10 @@ interface IServiceConfiguration is IAccessControl {
* @param isValid Whether the loan factory is valid
*/
function setLoanFactory(address addr, bool isValid) external;

/**
* @dev Sets the ToSAcceptanceRegistry for the protocol
* @param addr Address of registry
*/
function setToSAcceptanceRegistry(address addr) external;
}
15 changes: 12 additions & 3 deletions contracts/libraries/LoanLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -234,10 +234,12 @@ library LoanLib {
function previewFees(
uint256 payment,
uint256 firstLoss,
uint256 poolFeePercentOfInterest
uint256 poolFeePercentOfInterest,
uint256 latePaymentFee,
uint256 paymentDueDate
)
public
pure
view
returns (
uint256,
uint256,
Expand All @@ -252,7 +254,14 @@ library LoanLib {
.mul(payment)
.div(10000)
.div(RAY);
uint256 poolPayment = payment - poolFee - firstLossFee;

// Late fee is applied on top of interest payment
uint256 lateFee;
if (block.timestamp > paymentDueDate) {
lateFee = latePaymentFee;
}

uint256 poolPayment = payment - poolFee - firstLossFee + lateFee;

return (poolPayment, firstLossFee, poolFee);
}
Expand Down
Loading

0 comments on commit 57c320a

Please sign in to comment.