-
Notifications
You must be signed in to change notification settings - Fork 13
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
Add helper to set protocol fees by factory #897
Open
EndymionJkb
wants to merge
45
commits into
main
Choose a base branch
from
factory-fee
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+541
−14
Open
Changes from 11 commits
Commits
Show all changes
45 commits
Select commit
Hold shift + click to select a range
8ade135
checkpoint
EndymionJkb 37e7d85
Merge branch 'main' into factory-fee
EndymionJkb 11e2a7e
Merge branch 'main' into factory-fee
EndymionJkb d354182
checkpoint - most general
EndymionJkb 963b158
refactor: add interface, and simplify (remove completely generic pool…
EndymionJkb 507dfb5
lint
EndymionJkb 68b65e2
test: add tests for new fee controller getter
EndymionJkb b2089b6
test: add percentages provider tests
EndymionJkb d384e0e
docs: clarify permission requirements
EndymionJkb 0814c98
refactor: use constants in test instead of hard-coding
EndymionJkb 9e9a6f2
refactor: remove unnecessary permission set
EndymionJkb 4b2d40d
refactor: add event
EndymionJkb e428ffd
Merge branch 'main' into factory-fee
EndymionJkb 1301cd4
Merge branch 'main' into factory-fee
EndymionJkb b1552f0
Merge branch 'main' into factory-fee
EndymionJkb c4bcf2c
Merge branch 'main' into factory-fee
EndymionJkb b0b09f5
Merge branch 'main' into factory-fee
EndymionJkb 557b97d
chore: update gas
EndymionJkb 9310d7e
Merge branch 'main' into factory-fee
EndymionJkb 7e44de5
Merge branch 'main' into factory-fee
EndymionJkb 13658d0
Merge branch 'main' into factory-fee
EndymionJkb d559356
Merge branch 'main' into factory-fee
EndymionJkb 227a52e
Merge branch 'main' into factory-fee
EndymionJkb 0dd8cbf
Merge branch 'main' into factory-fee
EndymionJkb 2ba62a0
Merge branch 'main' into factory-fee
EndymionJkb f99fb6e
Merge branch 'main' into factory-fee
EndymionJkb 5ce27b3
Merge branch 'main' into factory-fee
EndymionJkb e4c32d4
fix: import
EndymionJkb a621374
Merge branch 'main' into factory-fee
EndymionJkb 81da628
refactor: expose the precision check
EndymionJkb fa46b99
feat: validate precision in percentages provider
EndymionJkb 77591d5
Merge branch 'main' into factory-fee
EndymionJkb a089892
Merge branch 'main' into factory-fee
EndymionJkb 8e99571
Merge branch 'main' into factory-fee
EndymionJkb e3be8ee
Merge branch 'main' into factory-fee
EndymionJkb b2556d4
Merge branch 'main' into factory-fee
EndymionJkb d243d8c
Merge branch 'main' into factory-fee
EndymionJkb 18f1a51
Merge branch 'main' into factory-fee
EndymionJkb d02643f
Merge branch 'main' into factory-fee
EndymionJkb 2def1d8
Merge branch 'main' into factory-fee
EndymionJkb 6b0a96c
Merge branch 'main' into factory-fee
EndymionJkb 2a78301
Merge branch 'main' into factory-fee
EndymionJkb 01f9464
Merge branch 'main' into factory-fee
EndymionJkb 0c62abc
Merge branch 'main' into factory-fee
EndymionJkb 048182c
Merge branch 'main' into factory-fee
EndymionJkb File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
76 changes: 76 additions & 0 deletions
76
pkg/interfaces/contracts/vault/IProtocolFeePercentagesProvider.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
|
||
pragma solidity ^0.8.24; | ||
|
||
import { IProtocolFeeController } from "./IProtocolFeeController.sol"; | ||
|
||
interface IProtocolFeePercentagesProvider { | ||
/** | ||
* @notice `setFactorySpecificProtocolFeePercentages` has not been called for this factory address. | ||
* @dev This error can by thrown by `getFactorySpecificProtocolFeePercentages` or | ||
* `setProtocolFeePercentagesForPools`, as both require that valid fee percentages have been set. | ||
* | ||
* @param factory The unregistered factory address | ||
*/ | ||
error FactoryNotRegistered(address factory); | ||
|
||
/** | ||
* @notice The factory address provided is not a valid `IBasePoolFactory`. | ||
* @dev This means it responds incorrectly to `isPoolFromFactory` (e.g., always responds true). If it doesn't | ||
* implement `isPoolFromFactory` or isn't a contract at all, calls on `setFactorySpecificProtocolFeePercentages` | ||
* will revert with no data. | ||
* | ||
* @param factory The address of the invalid factory | ||
*/ | ||
error InvalidFactory(address factory); | ||
|
||
/** | ||
* @notice The given pool is not from the expected factory. | ||
* @dev Occurs when one of the pools supplied to `setProtocolFeePercentagesForPools` is not from the given factory. | ||
* @param pool The address of the unrecognized pool | ||
* @param factory The address of the factory | ||
*/ | ||
error PoolNotFromFactory(address pool, address factory); | ||
|
||
/** | ||
* @notice Get the address of the `ProtocolFeeController` used to set fees. | ||
* @return protocolFeeController The address of the fee controller | ||
*/ | ||
function getProtocolFeeController() external view returns (IProtocolFeeController); | ||
|
||
/** | ||
* @notice Query the protocol fee percentages for a given factory. | ||
* @param factory The address of the factory | ||
* @return protocolSwapFeePercentage The protocol swap fee percentage set for that factory | ||
* @return protocolYieldFeePercentage The protocol yield fee percentage set for that factory | ||
*/ | ||
function getFactorySpecificProtocolFeePercentages( | ||
address factory | ||
) external view returns (uint256 protocolSwapFeePercentage, uint256 protocolYieldFeePercentage); | ||
|
||
/** | ||
* @notice Assign intended protocol fee percentages for a given factory. | ||
* @dev This is a permissioned call. After the fee percentages have been set, and governance has granted | ||
* this contract permission to set fee percentages on pools, anyone can call `setProtocolFeePercentagesForPools` | ||
* to update the fee percentages on a set of pools from that factory. | ||
* | ||
* @param factory The address of the factory | ||
* @param protocolSwapFeePercentage The new protocol swap fee percentage | ||
* @param protocolYieldFeePercentage The new protocol yield fee percentage | ||
*/ | ||
function setFactorySpecificProtocolFeePercentages( | ||
address factory, | ||
uint256 protocolSwapFeePercentage, | ||
uint256 protocolYieldFeePercentage | ||
) external; | ||
|
||
/** | ||
* @notice Update the protocol fees for a set of pools from a given factory. | ||
* @dev This call is permissionless. Anyone can update the fee percentages, once they're set by governance. | ||
* Note that goverance must also grant this contract permmission to set protocol fee percentages on pools. | ||
* | ||
* @param factory The address of the factory | ||
* @param pools The pools whose fees will be set according to `setFactorySpecificProtocolFeePercentages` | ||
*/ | ||
function setProtocolFeePercentagesForPools(address factory, address[] memory pools) external; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
|
||
pragma solidity ^0.8.24; | ||
|
||
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; | ||
|
||
import { | ||
IProtocolFeePercentagesProvider | ||
} from "@balancer-labs/v3-interfaces/contracts/vault/IProtocolFeePercentagesProvider.sol"; | ||
import { IProtocolFeeController } from "@balancer-labs/v3-interfaces/contracts/vault/IProtocolFeeController.sol"; | ||
import { IBasePoolFactory } from "@balancer-labs/v3-interfaces/contracts/vault/IBasePoolFactory.sol"; | ||
import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol"; | ||
|
||
import { | ||
SingletonAuthentication | ||
} from "@balancer-labs/v3-solidity-utils/contracts/helpers/SingletonAuthentication.sol"; | ||
|
||
contract ProtocolFeePercentagesProvider is IProtocolFeePercentagesProvider, SingletonAuthentication { | ||
using SafeCast for uint256; | ||
|
||
/// @notice The protocol fee controller was configured with an incorrect Vault address. | ||
error WrongProtocolFeeControllerDeployment(); | ||
|
||
/** | ||
* @dev Data structure to store default protocol fees by factory. Fee percentages are 18-decimal floating point | ||
* numbers, so we know they fit in 64 bits, allowing the fees to be stored in a single slot. | ||
* | ||
* @param protocolSwapFee The protocol swap fee | ||
* @param protocolYieldFee The protocol yield fee | ||
* @param isFactoryRegistered Flag indicating fees have been set (allows zero values) | ||
*/ | ||
struct FactoryProtocolFees { | ||
uint64 protocolSwapFeePercentage; | ||
uint64 protocolYieldFeePercentage; | ||
bool isFactoryRegistered; | ||
} | ||
|
||
IProtocolFeeController private immutable _protocolFeeController; | ||
|
||
uint256 private immutable _maxProtocolSwapFeePercentage; | ||
uint256 private immutable _maxProtocolYieldFeePercentage; | ||
|
||
// Factory address => FactoryProtocolFees | ||
mapping(IBasePoolFactory => FactoryProtocolFees) private _factoryDefaultFeePercentages; | ||
|
||
constructor(IVault vault, IProtocolFeeController protocolFeeController) SingletonAuthentication(vault) { | ||
_protocolFeeController = protocolFeeController; | ||
|
||
if (protocolFeeController.vault() != vault) { | ||
revert WrongProtocolFeeControllerDeployment(); | ||
} | ||
|
||
// These values are constant in the `ProtocolFeeController`. | ||
(_maxProtocolSwapFeePercentage, _maxProtocolYieldFeePercentage) = protocolFeeController | ||
.getMaximumProtocolFeePercentages(); | ||
} | ||
|
||
/// @inheritdoc IProtocolFeePercentagesProvider | ||
function getProtocolFeeController() external view returns (IProtocolFeeController) { | ||
return _protocolFeeController; | ||
} | ||
|
||
/// @inheritdoc IProtocolFeePercentagesProvider | ||
function getFactorySpecificProtocolFeePercentages( | ||
address factory | ||
) external view returns (uint256 protocolSwapFeePercentage, uint256 protocolYieldFeePercentage) { | ||
FactoryProtocolFees memory factoryFees = _getValidatedProtocolFees(factory); | ||
|
||
protocolSwapFeePercentage = factoryFees.protocolSwapFeePercentage; | ||
protocolYieldFeePercentage = factoryFees.protocolYieldFeePercentage; | ||
} | ||
|
||
/// @inheritdoc IProtocolFeePercentagesProvider | ||
function setFactorySpecificProtocolFeePercentages( | ||
address factory, | ||
uint256 protocolSwapFeePercentage, | ||
uint256 protocolYieldFeePercentage | ||
) external authenticate { | ||
// Validate the fee percentages; don't store values that the `ProtocolFeeCollector` will reject. | ||
if (protocolSwapFeePercentage > _maxProtocolSwapFeePercentage) { | ||
revert IProtocolFeeController.ProtocolSwapFeePercentageTooHigh(); | ||
} | ||
|
||
if (protocolYieldFeePercentage > _maxProtocolYieldFeePercentage) { | ||
revert IProtocolFeeController.ProtocolYieldFeePercentageTooHigh(); | ||
} | ||
|
||
// Best effort check that `factory` is the address of an IBasePoolFactory. | ||
bool poolFromFactory = IBasePoolFactory(factory).isPoolFromFactory(address(0)); | ||
if (poolFromFactory) { | ||
revert InvalidFactory(factory); | ||
} | ||
|
||
// Store the default fee percentages, and mark the factory as registered. | ||
_factoryDefaultFeePercentages[IBasePoolFactory(factory)] = FactoryProtocolFees({ | ||
protocolSwapFeePercentage: protocolSwapFeePercentage.toUint64(), | ||
protocolYieldFeePercentage: protocolYieldFeePercentage.toUint64(), | ||
isFactoryRegistered: true | ||
}); | ||
} | ||
|
||
/// @inheritdoc IProtocolFeePercentagesProvider | ||
function setProtocolFeePercentagesForPools(address factory, address[] memory pools) external { | ||
FactoryProtocolFees memory factoryFees = _getValidatedProtocolFees(factory); | ||
|
||
for (uint256 i = 0; i < pools.length; ++i) { | ||
address currentPool = pools[i]; | ||
|
||
if (IBasePoolFactory(factory).isPoolFromFactory(currentPool) == false) { | ||
revert PoolNotFromFactory(currentPool, factory); | ||
} | ||
|
||
_setPoolProtocolFees( | ||
currentPool, | ||
factoryFees.protocolSwapFeePercentage, | ||
factoryFees.protocolYieldFeePercentage | ||
); | ||
} | ||
} | ||
|
||
function _getValidatedProtocolFees(address factory) private view returns (FactoryProtocolFees memory factoryFees) { | ||
factoryFees = _factoryDefaultFeePercentages[IBasePoolFactory(factory)]; | ||
|
||
if (factoryFees.isFactoryRegistered == false) { | ||
revert FactoryNotRegistered(factory); | ||
} | ||
} | ||
|
||
// These are permissioned functions on `ProtocolFeeController`, so governance will need to allow this contract | ||
// to call `setProtocolSwapFeePercentage` and `setProtocolYieldFeePercentage`. | ||
function _setPoolProtocolFees( | ||
address pool, | ||
uint256 protocolSwapFeePercentage, | ||
uint256 protocolYieldFeePercentage | ||
) private { | ||
_protocolFeeController.setProtocolSwapFeePercentage(pool, protocolSwapFeePercentage); | ||
_protocolFeeController.setProtocolYieldFeePercentage(pool, protocolYieldFeePercentage); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically don't need SafeCast here, as I've validated the range above; could just cast it. But we don't care too much about gas here.