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

Allow PAs to set cancellation fees #105

Merged
merged 1 commit into from
Nov 29, 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
5 changes: 3 additions & 2 deletions contracts/Pool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -482,9 +482,10 @@ contract Pool is IPool, ERC20 {
maxRequestCancellation(owner) >= shares,
"Pool: InsufficientBalance"
);
withdrawController.performRequestCancellation(owner, shares);
uint256 feeShares = (shares);
uint256 feeShares = poolController.requestCancellationFee(shares);
_burn(owner, feeShares);
withdrawController.performRequestCancellation(owner, shares);

emit WithdrawRequestCancelled(owner, assets, shares);
}

Expand Down
25 changes: 25 additions & 0 deletions contracts/controllers/PoolController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,31 @@ contract PoolController is IPoolController {
);
}

/**
* @inheritdoc IPoolController
*/
function setRequestCancellationFee(uint256 feeBps)
external
onlyAdmin
atState(IPoolLifeCycleState.Initialized)
{
_settings.requestCancellationFeeBps = feeBps;
}

/**
* @inheritdoc IPoolController
*/
function requestCancellationFee(uint256 sharesOrAssets)
public
view
returns (uint256 feeShares)
{
feeShares = PoolLib.calculateCancellationFee(
sharesOrAssets,
_settings.requestCancellationFeeBps
);
}

/**
* @inheritdoc IPoolController
*/
Expand Down
12 changes: 12 additions & 0 deletions contracts/controllers/interfaces/IPoolController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,18 @@ interface IPoolController {
*/
function requestFee(uint256) external view returns (uint256);

/**
* @dev Allow the current pool admin to update the pool cancellation fees
* before the pool has been activated.
*/
function setRequestCancellationFee(uint256) external;

/**
* @dev Returns the cancellation fee for a given withdrawal request at the
* current block. The fee is the number of shares that will be charged.
*/
function requestCancellationFee(uint256) external view returns (uint256);

/**
* @dev Allow the current pool admin to update the withdraw gate at any
* time if the pool is Initialized or Active
Expand Down
51 changes: 51 additions & 0 deletions test/controllers/PoolController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,57 @@ describe("PoolController", () => {
});
});

describe("setRequestCancellationFee()", () => {
it("sets the request fee in Bps", async () => {
const { poolController, poolAdmin } = await loadFixture(loadPoolFixture);

const originalSettings = await poolController.settings();
expect(originalSettings.requestCancellationFeeBps).to.equal(100);

await poolController.connect(poolAdmin).setRequestCancellationFee(1000);

const settings = await poolController.settings();
expect(settings.requestCancellationFeeBps).to.equal(1000);
});

it("does not let anyone except the admin to set the fee", async () => {
const { poolController, otherAccount } = await loadFixture(
loadPoolFixture
);

const originalSettings = await poolController.settings();
expect(originalSettings.requestCancellationFeeBps).to.equal(100);

await expect(
poolController.connect(otherAccount).setRequestCancellationFee(10)
).to.be.revertedWith("Pool: caller is not admin");
});

it("does not allow setting the request fee once the pool is active", async () => {
const { pool, poolController, poolAdmin, liquidityAsset } =
await loadFixture(loadPoolFixture);

const originalSettings = await poolController.settings();
expect(originalSettings.requestCancellationFeeBps).to.equal(100);

await activatePool(pool, poolAdmin, liquidityAsset);

await expect(
poolController.connect(poolAdmin).setRequestCancellationFee(10)
).to.be.revertedWith("Pool: FunctionInvalidAtThisLifeCycleState");
});
});

describe("requestCancellationFee()", () => {
it("returns the number of shares that will be charged to make this request", async () => {
const { poolController, poolAdmin } = await loadFixture(loadPoolFixture);

await poolController.connect(poolAdmin).setRequestCancellationFee(500); // 5%

expect(await poolController.requestCancellationFee(1_000)).to.equal(50);
});
});

describe("setWithdrawGate()", () => {
it("sets the withdraw gate in Bps", async () => {
const { pool, poolController, poolAdmin, liquidityAsset } =
Expand Down
4 changes: 4 additions & 0 deletions test/scenarios/pool/withdraw-request.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,11 @@ describe("Withdraw Requests", () => {
expect(await pool.maxRequestCancellation(bobLender.address)).to.equal(3);

// Cancel Bob's request
const bobBalance = await pool.balanceOf(bobLender.address);
expect(await pool.connect(bobLender).cancelRedeemRequest(3));

// Expect a fee to be paid
expect(await pool.balanceOf(bobLender.address)).to.equal(bobBalance.sub(1));
expect(await pool.maxRequestCancellation(bobLender.address)).to.equal(0);
});
});