Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update PoolController to check PoolAdmin verification on every action #158

Merged
merged 5 commits into from
Dec 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 37 additions & 3 deletions contracts/controllers/PoolController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ contract PoolController is IPoolController, BeaconImplementation {
_;
}

/**
* @dev Modifier that can be overriden by derived classes to enforce
* access control.
*/
modifier onlyPermittedAdmin() virtual {
_;
}

/**
* @dev Modifier that checks that the caller is the pool's admin.
*/
Expand Down Expand Up @@ -180,6 +188,7 @@ contract PoolController is IPoolController, BeaconImplementation {
function setRequestFee(uint256 feeBps)
external
onlyNotPaused
onlyPermittedAdmin
onlyAdmin
atState(IPoolLifeCycleState.Initialized)
{
Expand Down Expand Up @@ -207,6 +216,7 @@ contract PoolController is IPoolController, BeaconImplementation {
function setRequestCancellationFee(uint256 feeBps)
external
onlyNotPaused
onlyPermittedAdmin
onlyAdmin
atState(IPoolLifeCycleState.Initialized)
{
Expand Down Expand Up @@ -234,6 +244,7 @@ contract PoolController is IPoolController, BeaconImplementation {
function setWithdrawGate(uint256 _withdrawGateBps)
external
onlyNotPaused
onlyPermittedAdmin
onlyAdmin
atInitializedOrActiveState
{
Expand Down Expand Up @@ -271,6 +282,7 @@ contract PoolController is IPoolController, BeaconImplementation {
function setPoolCapacity(uint256 newCapacity)
external
onlyNotPaused
onlyPermittedAdmin
onlyAdmin
{
require(newCapacity >= pool.totalAssets(), "Pool: invalid capacity");
Expand All @@ -281,7 +293,12 @@ contract PoolController is IPoolController, BeaconImplementation {
/**
* @inheritdoc IPoolController
*/
function setPoolEndDate(uint256 endDate) external onlyNotPaused onlyAdmin {
function setPoolEndDate(uint256 endDate)
external
onlyNotPaused
onlyPermittedAdmin
onlyAdmin
{
require(_settings.endDate > endDate, "Pool: can't move end date up");
require(
endDate > block.timestamp,
Expand Down Expand Up @@ -311,6 +328,7 @@ contract PoolController is IPoolController, BeaconImplementation {
function setServiceFeeBps(uint256 serviceFeeBps)
external
onlyNotPaused
onlyPermittedAdmin
onlyAdmin
{
require(serviceFeeBps <= 10000, "Pool: invalid service fee");
Expand All @@ -324,6 +342,7 @@ contract PoolController is IPoolController, BeaconImplementation {
function setFixedFee(uint256 amount, uint256 interval)
external
onlyNotPaused
onlyPermittedAdmin
onlyAdmin
{
if (amount > 0) {
Expand Down Expand Up @@ -399,6 +418,7 @@ contract PoolController is IPoolController, BeaconImplementation {
function depositFirstLoss(uint256 amount, address spender)
external
onlyNotPaused
onlyPermittedAdmin
onlyAdmin
atInitializedOrActiveState
{
Expand Down Expand Up @@ -429,6 +449,7 @@ contract PoolController is IPoolController, BeaconImplementation {
function withdrawFirstLoss(uint256 amount, address receiver)
external
onlyNotPaused
onlyPermittedAdmin
onlyAdmin
atState(IPoolLifeCycleState.Closed)
returns (uint256)
Expand Down Expand Up @@ -461,6 +482,7 @@ contract PoolController is IPoolController, BeaconImplementation {
function fundLoan(address addr)
external
onlyNotPaused
onlyPermittedAdmin
onlyAdmin
atState(IPoolLifeCycleState.Active)
isPoolLoan(addr)
Expand All @@ -474,6 +496,7 @@ contract PoolController is IPoolController, BeaconImplementation {
function defaultLoan(address loan)
external
onlyNotPaused
onlyPermittedAdmin
onlyAdmin
atActiveOrClosedState
onlySnapshottedPool
Expand All @@ -496,7 +519,12 @@ contract PoolController is IPoolController, BeaconImplementation {
/**
* @inheritdoc IPoolController
*/
function claimFixedFee() external onlyNotPaused onlyAdmin {
function claimFixedFee()
external
onlyNotPaused
onlyPermittedAdmin
onlyAdmin
{
pool.claimFixedFee(
msg.sender,
_settings.fixedFee,
Expand All @@ -511,7 +539,13 @@ contract PoolController is IPoolController, BeaconImplementation {
/**
* @inheritdoc IPoolController
*/
function snapshot() external override onlyNotPaused onlyAdmin {
function snapshot()
external
override
onlyNotPaused
onlyPermittedAdmin
onlyAdmin
{
pool.snapshot();
}
}
23 changes: 23 additions & 0 deletions contracts/permissioned/controllers/PermissionedPoolController.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

import "../../controllers/PoolController.sol";
import "../interfaces/IPermissionedServiceConfiguration.sol";

/**
* @title PermissionedLoan
*/
contract PermissionedPoolController is PoolController {
/**
* @dev a modifier to only allow Verified pool admins to perform an action
*/
modifier onlyPermittedAdmin() override {
require(
IPermissionedServiceConfiguration(serviceConfiguration)
.poolAdminAccessControl()
.isAllowed(msg.sender),
"ADMIN_NOT_ALLOWED"
);
_;
}
}
21 changes: 14 additions & 7 deletions scripts/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,19 +180,26 @@ async function main() {
console.log(
`PoolControllerFactory deployed to ${poolControllerFactory.address}`
);
const PoolController = await ethers.getContractFactory("PoolController", {
libraries: {
PoolLib: poolLib.address
const PoolController = await ethers.getContractFactory(
"PermissionedPoolController",
{
libraries: {
PoolLib: poolLib.address
}
}
});
);
const poolController = await PoolController.deploy();
await poolController.deployed();
console.log(`PoolController deployed to ${poolController.address}`);
console.log(
`PermissionedPoolController deployed to ${poolController.address}`
);
tx = await poolControllerFactory
.connect(deployer)
.setImplementation(poolController.address);
await tx.wait();
console.log(`PoolController set as implementation for its factory`);
console.log(
`PermissionedPoolController set as implementation for its factory`
);

// Deploy VaultFactory
const VaultFactory = await ethers.getContractFactory("VaultFactory");
Expand Down Expand Up @@ -231,7 +238,7 @@ async function main() {
console.log(`Pool deployed to ${pool.address}`);
tx = await poolFactory.connect(deployer).setImplementation(pool.address);
await tx.wait();
console.log(`Pool set as imlementation for its factory`);
console.log(`Pool set as implementation for its factory`);

// Deploy LoanFactory
const LoanFactory = await ethers.getContractFactory(
Expand Down
36 changes: 32 additions & 4 deletions test/permissioned/PermissionedPool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,23 @@ import {
depositToPool,
progressWithdrawWindow
} from "../support/pool";
import { getCommonSigners } from "../support/utils";
import { performVeriteVerification } from "../support/verite";

describe("PermissionedPool", () => {
async function loadPoolFixture() {
const [poolAdmin, otherAccount, thirdAccount, allowedLender] =
await ethers.getSigners();
const {
operator,
poolAdmin,
otherAccount,
aliceLender: thirdAccount,
bobLender: allowedLender
} = await getCommonSigners();
const {
pool,
liquidityAsset,
poolAccessControl,
poolAdminAccessControl,
tosAcceptanceRegistry,
poolController
} = await deployPermissionedPool({
Expand All @@ -30,9 +38,11 @@ describe("PermissionedPool", () => {
.allowParticipant(allowedLender.address);

return {
operator,
pool,
poolController,
poolAccessControl,
poolAdminAccessControl,
liquidityAsset,
poolAdmin,
otherAccount,
Expand Down Expand Up @@ -232,14 +242,32 @@ describe("PermissionedPool", () => {
});

it("snapshots the pool if PA via poolController", async () => {
const { pool, poolAdmin, poolController, liquidityAsset } =
await loadFixture(loadPoolFixture);
const {
pool,
poolAdmin,
poolAdminAccessControl,
poolController,
liquidityAsset,
operator
} = await loadFixture(loadPoolFixture);

await activatePool(pool, poolAdmin, liquidityAsset);

const { withdrawRequestPeriodDuration } = await pool.settings();
await time.increase(withdrawRequestPeriodDuration);

// expect the snapshot to require an up to date Verification
await expect(
poolController.connect(poolAdmin).snapshot()
).to.be.revertedWith("ADMIN_NOT_ALLOWED");

// Perform Verite Verification
await performVeriteVerification(
poolAdminAccessControl,
operator,
poolAdmin
);

await expect(poolController.connect(poolAdmin).snapshot()).to.emit(
pool,
"PoolSnapshotted"
Expand Down
31 changes: 24 additions & 7 deletions test/scenarios/business/permissioned/1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { deployPermissionedPool, activatePool } from "../../../support/pool";
import { deployLoan, fundLoan } from "../../../support/loan";
import { deployMockERC20 } from "../../../support/erc20";
import { performVeriteVerification } from "../../../support/verite";
import { getCommonSigners } from "../../../support/utils";

const ONE_DAY = 3600 * 24;

Expand Down Expand Up @@ -54,8 +55,11 @@ describe("Permissioned Business Scenario 1", () => {
}

async function loadFixtures() {
const [poolAdmin, lenderA, lenderB, borrowerOne, borrowerTwo] =
await ethers.getSigners();
const { operator, poolAdmin, otherAccounts } = await getCommonSigners();
const [lenderA, lenderB, borrowerOne, borrowerTwo] = otherAccounts.slice(
0,
4
);

const startTime = await time.latest();
const endTime = startTime + 5_184_000; // 60 days.
Expand All @@ -70,6 +74,7 @@ describe("Permissioned Business Scenario 1", () => {
);
const {
pool,
poolAdminAccessControl,
serviceConfiguration,
poolController,
poolAccessControl,
Expand Down Expand Up @@ -122,8 +127,10 @@ describe("Permissioned Business Scenario 1", () => {

return {
startTime,
operator,
pool,
poolController,
poolAdminAccessControl,
poolAccessControl,
tosAcceptanceRegistry,
lenderA,
Expand All @@ -140,8 +147,10 @@ describe("Permissioned Business Scenario 1", () => {
it("runs simulation", async () => {
const {
startTime,
operator,
pool,
poolController,
poolAdminAccessControl,
poolAccessControl,
tosAcceptanceRegistry,
lenderA,
Expand Down Expand Up @@ -193,6 +202,11 @@ describe("Permissioned Business Scenario 1", () => {

// +4 days, loanOne is funded
await advanceToDay(startTime, 4);
await performVeriteVerification(
poolAdminAccessControl,
operator,
poolAdmin
);
await fundLoan(loanOne, poolController, poolAdmin);
await loanOne.connect(borrowerOne).drawdown(INPUTS.loanOne.principal);

Expand All @@ -206,12 +220,15 @@ describe("Permissioned Business Scenario 1", () => {
// sanity-check lenderB is cleared out
expect(await mockUSDC.balanceOf(lenderB.address)).to.equal(0);
// check pool tokens minted
expect(await pool.balanceOf(lenderB.address)).to.equal(
199_667_222_259 - 28_84
); // Gas for the verification
expect(await pool.balanceOf(lenderB.address)).to.equal(199_667_222_259);

// +9 days, loanTwo funded
await advanceToDay(startTime, 9);
await performVeriteVerification(
poolAdminAccessControl,
operator,
poolAdmin
);
await fundLoan(loanTwo, poolController, poolAdmin);
await loanTwo.connect(borrowerTwo).drawdown(INPUTS.loanTwo.principal);

Expand Down Expand Up @@ -286,7 +303,7 @@ describe("Permissioned Business Scenario 1", () => {
.sub(INPUTS.lenderBDepositAmount);

expect(totalEarnings).to.equal(1_754_861_106);
expect(lenderABalance).to.equal(501_491_881_908);
expect(lenderBBalance).to.equal(200_262_979_198);
expect(lenderABalance).to.equal(501_491_879_840);
expect(lenderBBalance).to.equal(200_262_981_266);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These values are updated to match the non-permissioned values.

});
});
Loading