Skip to content

Commit

Permalink
Pool fees setting should be per pool, not service level (#56)
Browse files Browse the repository at this point in the history
* Move pool fee to pool

* Add parameterized fixture
  • Loading branch information
bricestacey authored Oct 21, 2022
1 parent 18dc192 commit 56eaa58
Show file tree
Hide file tree
Showing 11 changed files with 50 additions and 30 deletions.
4 changes: 2 additions & 2 deletions contracts/Loan.sol
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ contract Loan is ILoan {
.previewFees(
payment,
_serviceConfiguration.firstLossFeeBps(),
_serviceConfiguration.poolFeePercentOfInterest()
IPool(_pool).poolFeePercentOfInterest()
);

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

LoanLib.payFees(
Expand Down
7 changes: 7 additions & 0 deletions contracts/Pool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,13 @@ contract Pool is IPool, ERC20 {
return _accountings;
}

/**
* @dev The fee
*/
function poolFeePercentOfInterest() external view returns (uint256) {
return _poolSettings.poolFeePercentOfInterest;
}

/**
* @dev Supplies first-loss to the pool. Can only be called by the Pool Manager.
*/
Expand Down
6 changes: 4 additions & 2 deletions contracts/PoolFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ contract PoolFactory {
uint256 endDate,
uint256 requestFeeBps,
uint256 withdrawGateBps,
uint256 withdrawRequestPeriodDuration
uint256 withdrawRequestPeriodDuration,
uint256 poolFeePercentOfInterest
) public virtual returns (address poolAddress) {
require(
_serviceConfiguration.paused() == false,
Expand All @@ -50,7 +51,8 @@ contract PoolFactory {
requestFeeBps,
withdrawGateBps,
firstLossInitialMinimum,
withdrawRequestPeriodDuration
withdrawRequestPeriodDuration,
poolFeePercentOfInterest
);
Pool pool = new Pool(
liquidityAsset,
Expand Down
7 changes: 0 additions & 7 deletions contracts/ServiceConfiguration.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ contract ServiceConfiguration is AccessControl, IServiceConfiguration {

uint256 public firstLossFeeBps = 500;

uint256 public poolFeePercentOfInterest = 0;

/**
* @dev Holds a reference to valid LoanFactories
*/
Expand Down Expand Up @@ -91,11 +89,6 @@ contract ServiceConfiguration is AccessControl, IServiceConfiguration {
emit ProtocolPaused(paused);
}

function setPoolFeePercentOfInterest(uint256 amount) public onlyOperator {
poolFeePercentOfInterest = amount;
emit ParameterSet("pooFeePercentOfInterest", amount);
}

/**
* @dev Check that `msg.sender` is an Operator.
*/
Expand Down
6 changes: 6 additions & 0 deletions contracts/interfaces/IPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ struct IPoolConfigurableSettings {
uint256 withdrawGateBps; // Percent of liquidity pool available to withdraw, represented in BPS
uint256 firstLossInitialMinimum; // amount
uint256 withdrawRequestPeriodDuration; // seconds (e.g. 30 days)
uint256 poolFeePercentOfInterest; // bips
}

/**
Expand Down Expand Up @@ -134,6 +135,11 @@ interface IPool is IERC4626 {
*/
function accountings() external view returns (IPoolAccountings memory);

/**
* @dev The pool fee, in bps, taken from each interest payment
*/
function poolFeePercentOfInterest() external view returns (uint256);

/**
* @dev Deposits first-loss to the pool. Can only be called by the Pool Manager.
*/
Expand Down
2 changes: 0 additions & 2 deletions contracts/interfaces/IServiceConfiguration.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ interface IServiceConfiguration is IAccessControl {

function paused() external view returns (bool);

function poolFeePercentOfInterest() external view returns (uint256);

function firstLossFeeBps() external view returns (uint256);

function isLiquidityAsset(address addr) external view returns (bool);
Expand Down
6 changes: 4 additions & 2 deletions contracts/permissioned/PermissionedPoolFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ contract PermissionedPoolFactory is PoolFactory {
uint256 endDate,
uint256 requestFeeBps,
uint256 withdrawGateBps,
uint256 withdrawRequestPeriodDuration
uint256 withdrawRequestPeriodDuration,
uint256 poolFeePercentOfInterest
) public override onlyVerifiedPoolManager returns (address poolAddress) {
require(
withdrawRequestPeriodDuration > 0,
Expand All @@ -60,7 +61,8 @@ contract PermissionedPoolFactory is PoolFactory {
requestFeeBps,
withdrawGateBps,
firstLossInitialMinimum,
withdrawRequestPeriodDuration
withdrawRequestPeriodDuration,
poolFeePercentOfInterest
);
PermissionedPool pool = new PermissionedPool(
liquidityAsset,
Expand Down
27 changes: 17 additions & 10 deletions test/Loan.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe("Loan", () => {
const SEVEN_DAYS = 6 * 60 * 60 * 24;
const THIRTY_DAYS = 30 * 60 * 60 * 24;

async function deployFixture() {
async function deployFixture(poolSettings = DEFAULT_POOL_SETTINGS) {
// Contracts are deployed using the first signer/account by default
const [operator, poolManager, borrower, lender, other] =
await ethers.getSigners();
Expand Down Expand Up @@ -62,11 +62,12 @@ describe("Loan", () => {
.connect(poolManager)
.createPool(
liquidityAsset.address,
DEFAULT_POOL_SETTINGS.maxCapacity,
DEFAULT_POOL_SETTINGS.endDate,
DEFAULT_POOL_SETTINGS.requestFeeBps,
DEFAULT_POOL_SETTINGS.withdrawGateBps,
DEFAULT_POOL_SETTINGS.withdrawRequestPeriodDuration
poolSettings.maxCapacity,
poolSettings.endDate,
poolSettings.requestFeeBps,
poolSettings.withdrawGateBps,
poolSettings.withdrawRequestPeriodDuration,
poolSettings.poolFeePercentOfInterest
);
const tx1Receipt = await tx1.wait();

Expand Down Expand Up @@ -147,6 +148,13 @@ describe("Loan", () => {
};
}

async function deployFixturePoolFees() {
const poolSettings = Object.assign({}, DEFAULT_POOL_SETTINGS, {
poolFeePercentOfInterest: 100
});
return deployFixture(poolSettings);
}

describe("after initialization", () => {
it("is initialized!", async () => {
const { loan, pool, borrower, loanFactory } = await loadFixture(
Expand Down Expand Up @@ -889,15 +897,14 @@ describe("Loan", () => {
});

it("can collect pool fees from the next payment", async () => {
const fixture = await loadFixture(deployFixture);
const fixture = await loadFixture(deployFixturePoolFees);
const {
borrower,
collateralAsset,
liquidityAsset,
loan,
pool,
poolManager,
serviceConfiguration
poolManager
} = fixture;

// Setup
Expand All @@ -907,11 +914,11 @@ describe("Loan", () => {
.postFungibleCollateral(collateralAsset.address, 100);
await pool.connect(poolManager).fundLoan(loan.address);
await loan.connect(borrower).drawdown();
expect(await pool.poolFeePercentOfInterest()).to.equal(100);

// Make payment
const firstLoss = await pool.firstLossVault();
const feeVault = await pool.feeVault();
await serviceConfiguration.setPoolFeePercentOfInterest(100);
const dueDate = await loan.paymentDueDate();
expect(await loan.paymentsRemaining()).to.equal(6);
await liquidityAsset.connect(borrower).approve(loan.address, 2083);
Expand Down
6 changes: 4 additions & 2 deletions test/PoolFactory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ describe("PoolFactory", () => {
/* endDate */ 0,
/* requestFeeBps */ 0,
/* withdrawGateBps */ 0,
/* withdrawRequestPeriodDuration: */ 0
/* withdrawRequestPeriodDuration: */ 0,
/* poolFeePercentOfInterest */ 0
)
).to.be.revertedWith("PoolFactory: Invalid duration");
});
Expand All @@ -61,7 +62,8 @@ describe("PoolFactory", () => {
/* endDate */ 0,
/* requestFeeBps */ 0,
/* withdrawGateBps */ 0,
/* withdrawRequestPeriodDuration: */ 1
/* withdrawRequestPeriodDuration: */ 1,
/* poolFeePercentOfInterest */ 0
)
).to.emit(poolFactory, "PoolCreated");
});
Expand Down
6 changes: 4 additions & 2 deletions test/permissioned/PermissionedPoolFactory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ describe("PermissionedPoolFactory", () => {
/* endDate */ 0,
/* requestFeeBps */ 0,
/* withdrawGateBps */ 0,
/* withdrawRequestPeriodDuration: */ 1
/* withdrawRequestPeriodDuration: */ 1,
/* poolFeePercentOfInterest */ 0
)
).to.emit(poolFactory, "PoolCreated");
});
Expand All @@ -104,7 +105,8 @@ describe("PermissionedPoolFactory", () => {
/* endDate */ 0,
/* requestFeeBps */ 0,
/* withdrawGateBps */ 0,
/* withdrawRequestPeriodDuration: */ 1
/* withdrawRequestPeriodDuration: */ 1,
/* poolFeePercentOfInterest */ 0
)
).to.be.revertedWith("caller is not a pool manager");
});
Expand Down
3 changes: 2 additions & 1 deletion test/support/pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export const DEFAULT_POOL_SETTINGS = {
requestFeeBps: 500, // bps (1%)
withdrawGateBps: 10_000, // bps (100%)
firstLossInitialMinimum: 100_000,
withdrawRequestPeriodDuration: 30 * 24 * 60 * 60 // 30 days
withdrawRequestPeriodDuration: 30 * 24 * 60 * 60, // 30 days
poolFeePercentOfInterest: 0 // bps (0%)
};

/**
Expand Down

0 comments on commit 56eaa58

Please sign in to comment.