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

Fees #65

Open
wants to merge 5 commits into
base: development
Choose a base branch
from
Open

Fees #65

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
8 changes: 7 additions & 1 deletion README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ All Smart Pools are fully upgradeable to make it easy to add features and optimi

Clone this repo. And copy the contents of ``env.example`` to a new file called ``.env`` and edit the the relevant values inside. **DO NOT** share this file with anyone as it will contain sensitive data.

Install all dependencies:
Install all dependencies:
```
yarn
```
Expand Down Expand Up @@ -65,6 +65,12 @@ To get the underlying tokens and amounts needed to mint a certain amount of pool
function calcTokensForAmount(uint256 _amount) external view returns(address[] memory tokens, uint256[] memory amounts);
```

To get the underlying tokens and amounts redeemed when exiting a certain amount of pool shares call:

```solidity
function calcTokensForAmountExit(uint256 _amount) external view returns(address[] memory tokens, uint256[] memory amounts);
```

#### Balancer smart pool specific
Get the address of the underlying balancer pool:

Expand Down
42 changes: 37 additions & 5 deletions contracts/interfaces/IPV2SmartPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ interface IPV2SmartPool is IERC20 {
) external;

/**
@notice Set the address that can set public swap enabled or disabled.
@notice Set the address that can set public swap enabled or disabled.
Can only be called by the controller
@param _swapSetter Address of the new swapSetter
*/
Expand Down Expand Up @@ -68,6 +68,16 @@ interface IPV2SmartPool is IERC20 {
*/
function setFeeRecipient(address _newRecipient) external;

/**
@notice Set the exit fee percentage users have to pay.
*/
function setExitFee(uint256 _fee) external;

/**
@notice Sets the recipient fee percentage share. Can only be set by the current controller
*/
function setExitFeeRecipientShare(uint256 _share) external;

/**
@notice Set the controller address. Can only be called by the current address
@param _controller Address of the new controller
Expand Down Expand Up @@ -98,7 +108,7 @@ interface IPV2SmartPool is IERC20 {
*/
function updateWeight(address _token, uint256 _newWeight) external;

/**
/**
@notice Gradually adjust the weights of a token. Can only be called by the controller
@param _newWeights Target weights
@param _startBlock Block to start weight adjustment
Expand All @@ -120,7 +130,7 @@ interface IPV2SmartPool is IERC20 {
*/
function applyAddToken() external;

/**
/**
@notice Commit a token to be added. Can only be called by the controller
@param _token Address of the token to add
@param _balance Amount of token to add
Expand All @@ -143,7 +153,7 @@ interface IPV2SmartPool is IERC20 {
*/
function approveTokens() external;

/**
/**
@notice Mint pool tokens, locking underlying assets
@param _amount Amount of pool tokens
*/
Expand Down Expand Up @@ -284,7 +294,7 @@ interface IPV2SmartPool is IERC20 {
*/
function isPublicSwap() external view returns (bool);

/**
/**
@notice Get the current tokens in the smart pool
@return Addresses of the tokens in the smart pool
*/
Expand All @@ -308,6 +318,18 @@ interface IPV2SmartPool is IERC20 {
*/
function getFeeRecipient() external view returns (address);

/**
@notice Get the percetage the recipient gets of the total exit fee.
@return Fee recipient share percentage
*/
function getExitFeeRecipientShare() external view returns (uint256);

/**
@notice Get the exit fee percentage
@return Exit fee percentage
*/
function getExitFee() external view returns (uint256);

/**
@notice Get the denormalized weight of a token
@param _token Address of the token
Expand Down Expand Up @@ -389,6 +411,16 @@ interface IPV2SmartPool is IERC20 {
view
returns (address[] memory tokens, uint256[] memory amounts);

/**
@notice Calculate the amount of underlying redeemed when exiting with a certain amount
@return tokens Addresses of the underlying tokens
@return amounts Amounts of the underlying tokens
*/
function calcTokensForAmountExit(uint256 _amount)
external
view
returns (address[] memory tokens, uint256[] memory amounts);

/**
@notice Calculate the amount of pool tokens out given underlying in
@param _token Underlying asset to deposit
Expand Down
11 changes: 11 additions & 0 deletions contracts/libraries/LibFees.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,15 @@ library LibFees {
emit AnnualFeeChanged(P2Storage.load().annualFee, _newFee);
P2Storage.load().annualFee = _newFee;
}

function setExitFee(uint256 _fee) internal {
require(_fee <= 10**17, "FEE_TOO_BIG"); // max 10%
P2Storage.load().exitFee = _fee;
}

function setExitFeeRecipientShare(uint256 _share) internal {
require(_share <= 10**18, "FEE_SHARE_TOO_BIG");
P2Storage.load().exitFeeRecipientShare = _share;
}

}
17 changes: 16 additions & 1 deletion contracts/libraries/LibPoolEntryExit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pragma solidity 0.6.4;

import {PBasicSmartPoolStorage as PBStorage} from "../storage/PBasicSmartPoolStorage.sol";
import {PCTokenStorage as PCStorage} from "../storage/PCTokenStorage.sol";
import {PV2SmartPoolStorage as P2Storage} from "../storage/PV2SmartPoolStorage.sol";
import "./LibFees.sol";

import "./LibPoolToken.sol";
Expand Down Expand Up @@ -42,9 +43,11 @@ library LibPoolEntryExit {

function _exitPool(uint256 _amount, uint256[] memory _minAmountsOut) internal lockBPoolSwap {
IBPool bPool = PBStorage.load().bPool;
P2Storage.StorageStruct storage ws = P2Storage.load();
LibFees.chargeOutstandingAnnualFee();
uint256 feeAmount = _amount.bmul(ws.exitFee).bdiv(10**18);
uint256 poolTotal = PCStorage.load().totalSupply;
uint256 ratio = _amount.bdiv(poolTotal);
uint256 ratio = _amount.bsub(feeAmount).bdiv(poolTotal);
require(ratio != 0);

LibPoolToken._burn(msg.sender, _amount);
Expand All @@ -64,6 +67,18 @@ library LibPoolEntryExit {
emit LOG_EXIT(msg.sender, token, tokenAmountOut);
LibUnderlying._pushUnderlying(token, msg.sender, tokenAmountOut, balance);
}

if(
feeAmount != 0 &&
ws.exitFeeRecipientShare != 0 &&
ws.feeRecipient != address(0)
) {
uint256 feeRecipientShare = feeAmount.mul(ws.exitFeeRecipientShare).div(10**18);
if(feeRecipientShare != 0) {
LibPoolToken._mint(ws.feeRecipient, feeRecipientShare);
}
}

emit PoolExited(msg.sender, _amount);
}

Expand Down
29 changes: 29 additions & 0 deletions contracts/libraries/LibPoolMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,35 @@ library LibPoolMath {
}
}

/**
@notice Gets the underlying assets and amounts redeemed when exiting specific pool shares.
@param _amount Amount of pool shares to calculate the values for
@return tokens The addresses of the tokens
@return amounts The amounts of tokens redeemed when exiting that amount of pool shares
*/
function calcTokensForAmountExit(uint256 _amount)
external
view
returns (address[] memory tokens, uint256[] memory amounts)
{
P2Storage.StorageStruct storage ws = P2Storage.load();
uint256 feeAmount = _amount.bmul(ws.exitFee).bdiv(10**18);

tokens = PBStorage.load().bPool.getCurrentTokens();
amounts = new uint256[](tokens.length);
uint256 ratio = _amount.bsub(feeAmount).bdiv(
PCStorage.load().totalSupply.badd(LibFees.calcOutstandingAnnualFee())
);

for (uint256 i = 0; i < tokens.length; i++) {
address t = tokens[i];

uint256 bal = PBStorage.load().bPool.getBalance(t);
uint256 amount = ratio.bmul(bal);
amounts[i] = amount;
}
}

/**
@notice Calculate the amount of pool tokens out for a given amount in
@param _token Address of the input token
Expand Down
41 changes: 40 additions & 1 deletion contracts/smart-pools/PV2SmartPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,22 @@ contract PV2SmartPool is IPV2SmartPool, PCToken, ReentryProtection {
LibFees.setFeeRecipient(_newRecipient);
}

/**
@notice Sets the exit fee percentage. Can only be set by the current controller
@param _fee Fee percentage, max 10 percent
*/
function setExitFee(uint256 _fee) external override onlyController noReentry {
LibFees.setExitFee(_fee);
}

/**
@notice Sets the recipient fee percentage share. Can only be set by the current controller
@param _share Percentage of fee for the recipient
*/
function setExitFeeRecipientShare(uint256 _share) external override onlyController noReentry {
LibFees.setExitFeeRecipientShare(_share);
}

/**
@notice Trip the circuit breaker which disabled exit, join and swaps
*/
Expand Down Expand Up @@ -575,6 +591,21 @@ contract PV2SmartPool is IPV2SmartPool, PCToken, ReentryProtection {
return LibPoolMath.calcTokensForAmount(_amount);
}

/**
@notice Gets the underlying assets and amounts redeemed when exiting specific pool shares.
@param _amount Amount of pool shares to calculate the values for
@return tokens The addresses of the tokens
@return amounts The amounts of tokens redeemed when exiting that amount of pool shares
*/
function calcTokensForAmountExit(uint256 _amount)
external
override
view
returns (address[] memory tokens, uint256[] memory amounts)
{
return LibPoolMath.calcTokensForAmountExit(_amount);
}

/**
@notice Calculate the amount of pool tokens out for a given amount in
@param _token Address of the input token
Expand Down Expand Up @@ -645,7 +676,7 @@ contract PV2SmartPool is IPV2SmartPool, PCToken, ReentryProtection {

/**
@notice Get the address of the controller
@return The address of the pool
@return The address of the controller
*/
function getController() external override view returns (address) {
return PBStorage.load().controller;
Expand Down Expand Up @@ -699,6 +730,14 @@ contract PV2SmartPool is IPV2SmartPool, PCToken, ReentryProtection {
return P2Storage.load().feeRecipient;
}

function getExitFeeRecipientShare() external override view returns (uint256) {
return P2Storage.load().exitFeeRecipientShare;
}

function getExitFee() external override view returns (uint256) {
return P2Storage.load().exitFee;
}

/**
@notice Get the denormalized weight of a specific token in the underlying balancer pool
@return the normalized weight of the token in uint
Expand Down
2 changes: 2 additions & 0 deletions contracts/storage/PV2SmartPoolStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ library PV2SmartPoolStorage {
uint256 lastAnnualFeeClaimed;
address feeRecipient;
address circuitBreaker;
uint256 exitFeeRecipientShare;
uint256 exitFee;
Comment on lines +17 to +18
Copy link
Member

Choose a reason for hiding this comment

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

Can you verify this does not cause any storage corruption issues when upgrading existing contracts to this? @Evert0x

}

struct NewToken {
Expand Down
18 changes: 17 additions & 1 deletion test/advancedPoolFunctionality.ts
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,23 @@ describe("Advanced Pool Functionality", function () {
expect(feeRecipientPoolBalanceAfter).to.eq(expectedMint);
});
});
});
describe("Exit fee", async () => {
it("Setting the exit fee should work", async () => {
const tenPercent = parseEther("0.1"); // 10%

await smartpool.setExitFee("1");
expect(await smartpool.getExitFee()).to.eq("1");
expect(smartpool.setExitFee(tenPercent.add(1))).to.be.revertedWith("FEE_TOO_BIG");
});
it("Setting the exit fee recipient share should work", async () => {
const hundrerdPercent = constants.One.mul(10).pow(18) // 100%

await smartpool.setExitFeeRecipientShare("1");
expect(await smartpool.getExitFeeRecipientShare()).to.eq("1");
expect(smartpool.setExitFeeRecipientShare(hundrerdPercent.add(1))).to.be.revertedWith("FEE_SHARE_TOO_BIG");
});
})
});
});

function createBigNumberArray(length: number, value: BigNumber): BigNumber[] {
Expand Down
Loading