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

Add helper to set protocol fees by factory #897

Open
wants to merge 87 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
8ade135
checkpoint
EndymionJkb Aug 17, 2024
37e7d85
Merge branch 'main' into factory-fee
EndymionJkb Aug 17, 2024
11e2a7e
Merge branch 'main' into factory-fee
EndymionJkb Aug 17, 2024
d354182
checkpoint - most general
EndymionJkb Aug 17, 2024
963b158
refactor: add interface, and simplify (remove completely generic pool…
EndymionJkb Aug 18, 2024
507dfb5
lint
EndymionJkb Aug 18, 2024
68b65e2
test: add tests for new fee controller getter
EndymionJkb Aug 18, 2024
b2089b6
test: add percentages provider tests
EndymionJkb Aug 18, 2024
d384e0e
docs: clarify permission requirements
EndymionJkb Aug 18, 2024
0814c98
refactor: use constants in test instead of hard-coding
EndymionJkb Aug 18, 2024
9e9a6f2
refactor: remove unnecessary permission set
EndymionJkb Aug 18, 2024
4b2d40d
refactor: add event
EndymionJkb Aug 19, 2024
e428ffd
Merge branch 'main' into factory-fee
EndymionJkb Aug 23, 2024
1301cd4
Merge branch 'main' into factory-fee
EndymionJkb Aug 23, 2024
b1552f0
Merge branch 'main' into factory-fee
EndymionJkb Aug 26, 2024
c4bcf2c
Merge branch 'main' into factory-fee
EndymionJkb Aug 27, 2024
b0b09f5
Merge branch 'main' into factory-fee
EndymionJkb Aug 28, 2024
557b97d
chore: update gas
EndymionJkb Aug 28, 2024
9310d7e
Merge branch 'main' into factory-fee
EndymionJkb Aug 28, 2024
7e44de5
Merge branch 'main' into factory-fee
EndymionJkb Aug 28, 2024
13658d0
Merge branch 'main' into factory-fee
EndymionJkb Aug 28, 2024
d559356
Merge branch 'main' into factory-fee
EndymionJkb Aug 30, 2024
227a52e
Merge branch 'main' into factory-fee
EndymionJkb Aug 30, 2024
0dd8cbf
Merge branch 'main' into factory-fee
EndymionJkb Sep 1, 2024
2ba62a0
Merge branch 'main' into factory-fee
EndymionJkb Sep 2, 2024
f99fb6e
Merge branch 'main' into factory-fee
EndymionJkb Sep 2, 2024
5ce27b3
Merge branch 'main' into factory-fee
EndymionJkb Sep 2, 2024
e4c32d4
fix: import
EndymionJkb Sep 2, 2024
a621374
Merge branch 'main' into factory-fee
EndymionJkb Sep 4, 2024
81da628
refactor: expose the precision check
EndymionJkb Sep 4, 2024
fa46b99
feat: validate precision in percentages provider
EndymionJkb Sep 4, 2024
77591d5
Merge branch 'main' into factory-fee
EndymionJkb Sep 4, 2024
a089892
Merge branch 'main' into factory-fee
EndymionJkb Sep 12, 2024
8e99571
Merge branch 'main' into factory-fee
EndymionJkb Sep 12, 2024
e3be8ee
Merge branch 'main' into factory-fee
EndymionJkb Sep 16, 2024
b2556d4
Merge branch 'main' into factory-fee
EndymionJkb Sep 18, 2024
d243d8c
Merge branch 'main' into factory-fee
EndymionJkb Sep 19, 2024
18f1a51
Merge branch 'main' into factory-fee
EndymionJkb Sep 20, 2024
d02643f
Merge branch 'main' into factory-fee
EndymionJkb Sep 23, 2024
2def1d8
Merge branch 'main' into factory-fee
EndymionJkb Sep 24, 2024
6b0a96c
Merge branch 'main' into factory-fee
EndymionJkb Sep 24, 2024
2a78301
Merge branch 'main' into factory-fee
EndymionJkb Sep 25, 2024
01f9464
Merge branch 'main' into factory-fee
EndymionJkb Sep 27, 2024
0c62abc
Merge branch 'main' into factory-fee
EndymionJkb Sep 27, 2024
048182c
Merge branch 'main' into factory-fee
EndymionJkb Oct 1, 2024
92a2828
Merge branch 'main' into factory-fee
EndymionJkb Oct 3, 2024
f25cc1a
Merge branch 'main' into factory-fee
EndymionJkb Oct 9, 2024
3917321
Merge branch 'main' into factory-fee
EndymionJkb Oct 9, 2024
4e5467c
Merge branch 'main' into factory-fee
EndymionJkb Oct 9, 2024
56c98e0
Merge branch 'main' into factory-fee
EndymionJkb Oct 14, 2024
c178a85
Merge branch 'main' into factory-fee
EndymionJkb Oct 16, 2024
e2c454f
Merge branch 'main' into factory-fee
EndymionJkb Oct 18, 2024
a36b105
Merge branch 'main' into factory-fee
EndymionJkb Oct 24, 2024
322f040
Merge branch 'main' into factory-fee
EndymionJkb Oct 31, 2024
084eb19
Merge branch 'main' into factory-fee
EndymionJkb Nov 8, 2024
17afc3d
Merge branch 'main' into factory-fee
EndymionJkb Nov 12, 2024
adcd30c
Merge branch 'main' into factory-fee
EndymionJkb Nov 15, 2024
5c4d65e
Merge branch 'main' into factory-fee
EndymionJkb Nov 20, 2024
4f7a554
Merge branch 'main' into factory-fee
EndymionJkb Dec 6, 2024
20fdd85
fix: update constant names
EndymionJkb Dec 9, 2024
1e6b536
Merge branch 'main' into factory-fee
EndymionJkb Dec 10, 2024
845d0e8
Merge branch 'main' into factory-fee
EndymionJkb Dec 12, 2024
f67bd8c
Add registry of "trusted" contracts (#1179)
EndymionJkb Dec 17, 2024
78f5e33
Merge branch 'main' into factory-fee
EndymionJkb Dec 18, 2024
2de3fee
chore: update bytecode
EndymionJkb Dec 18, 2024
2a838bf
chore: update gas
EndymionJkb Dec 18, 2024
5b5a780
Merge branch 'main' into factory-fee
EndymionJkb Dec 24, 2024
2a7e078
Merge branch 'main' into factory-fee
EndymionJkb Dec 30, 2024
6cbd390
refactor: adjust to BaseVaultTest factoryMock changes
EndymionJkb Dec 30, 2024
bd967bd
feat: add BalancerContractRegistry
EndymionJkb Dec 31, 2024
7d20fef
test: add tests for BalancerContractRegistry
EndymionJkb Dec 31, 2024
cb88e34
Merge branch 'main' into contract-registry
EndymionJkb Jan 4, 2025
e334be3
feat: add OTHER category as a catch-all; rename success to isActive
EndymionJkb Jan 4, 2025
56ac5d9
Merge branch 'contract-registry' into factory-fee
EndymionJkb Jan 4, 2025
271d6c4
Merge branch 'main' into contract-registry
EndymionJkb Jan 6, 2025
d52248e
refactor: simplify data structures
EndymionJkb Jan 6, 2025
8191b9b
Merge branch 'contract-registry' into factory-fee
EndymionJkb Jan 6, 2025
90862ca
Merge branch 'main' into factory-fee
EndymionJkb Jan 14, 2025
3cbdf93
chore: update gas
EndymionJkb Jan 14, 2025
dec9eb4
Merge branch 'main' into factory-fee
EndymionJkb Jan 14, 2025
96a51d3
Merge branch 'main' into factory-fee
EndymionJkb Jan 16, 2025
8c5a3ac
Merge branch 'main' into factory-fee
EndymionJkb Jan 21, 2025
ac13b94
Merge branch 'main' into factory-fee
EndymionJkb Jan 21, 2025
029a878
Merge branch 'main' into factory-fee
EndymionJkb Jan 21, 2025
1ec59f9
Merge branch 'main' into factory-fee
EndymionJkb Jan 22, 2025
c80c6d9
fix: registry references
EndymionJkb Jan 22, 2025
c4c52e1
Merge branch 'main' into factory-fee
EndymionJkb Jan 29, 2025
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
Prev Previous commit
Next Next commit
refactor: add interface, and simplify (remove completely generic pool…
… list support)
  • Loading branch information
EndymionJkb committed Aug 18, 2024
commit 963b1584aadf1346814c5e4f9c275cbcd01fc03b
66 changes: 66 additions & 0 deletions pkg/interfaces/contracts/vault/IProtocolFeePercentagesProvider.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

import { IProtocolFeeController } from "./IProtocolFeeController.sol";

interface IProtocolFeePercentagesProvider {
/**
* @notice `setDefaultProtocolFees` has not been called for this factory address.
* @param factory The unregistered factory address
*/
error FactoryNotRegistered(address factory);

/**
* @notice The factory address provided is not a valid IBasePoolFactory.
* @dev This means it does not implement or responds incorrectly to `isPoolFromFactory`.
* @param factory The address of the invalid factory
*/
error InvalidFactory(address factory);

/**
* @notice The given pool is not from the expected factory.
* @param pool The address of the 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, anyone can call
* `setProtocolFeePercentagesForPools` to update the fees 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 fees, once they're set by governance.
* @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;
}
126 changes: 27 additions & 99 deletions pkg/vault/contracts/ProtocolFeePercentagesProvider.sol
Original file line number Diff line number Diff line change
@@ -4,19 +4,21 @@ 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";
import { EnumerableSet } from "@balancer-labs/v3-solidity-utils/contracts/openzeppelin/EnumerableSet.sol";

contract ProtocolFeePercentagesProvider is SingletonAuthentication {
using EnumerableSet for EnumerableSet.AddressSet;
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.
@@ -39,31 +41,6 @@ contract ProtocolFeePercentagesProvider is SingletonAuthentication {
// Factory address => FactoryProtocolFees
mapping(IBasePoolFactory => FactoryProtocolFees) private _factoryDefaultFeePercentages;

// Iterable list of the factories with default fees set.
EnumerableSet.AddressSet private _factories;

/// @notice The protocol fee controller was configured with an incorrect Vault address.
error WrongProtocolFeeControllerDeployment();

/**
* @notice `setDefaultProtocolFees` has not been called for this factory address.
* @param factory The unregistered factory address
*/
error FactoryNotRegistered(address factory);

/**
* @notice The factory address provided is not a valid IBasePoolFactory.
* @dev This means it does not implement or responds incorrectly to `isPoolFromFactory`.
* @param factory The address of the invalid factory
*/
error InvalidFactory(address factory);

/**
* @notice The given pool is not from any of the registered factories.
* @param pool The address of the pool
*/
error PoolNotFromRegisteredFactory(address pool);

constructor(IVault vault, IProtocolFeeController protocolFeeController) SingletonAuthentication(vault) {
_protocolFeeController = protocolFeeController;

@@ -76,11 +53,13 @@ contract ProtocolFeePercentagesProvider is SingletonAuthentication {
.getMaximumProtocolFeePercentages();
}

/// @inheritdoc IProtocolFeePercentagesProvider
function getProtocolFeeController() external view returns (IProtocolFeeController) {
return _protocolFeeController;
}

function getFactorySpecificProtocolFees(
/// @inheritdoc IProtocolFeePercentagesProvider
function getFactorySpecificProtocolFeePercentages(
address factory
) external view returns (uint256 protocolSwapFeePercentage, uint256 protocolYieldFeePercentage) {
FactoryProtocolFees memory factoryFees = _getValidatedProtocolFees(factory);
@@ -89,57 +68,8 @@ contract ProtocolFeePercentagesProvider is SingletonAuthentication {
protocolYieldFeePercentage = factoryFees.protocolYieldFeePercentage;
}

function setProtocolFeesForPools(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 PoolNotFromRegisteredFactory(currentPool);
}

_setPoolProtocolFees(
currentPool,
factoryFees.protocolSwapFeePercentage,
factoryFees.protocolYieldFeePercentage
);
}
}

function setProtocolFeesForPools(address[] memory pools) external {
uint256 numPools = pools.length;

if (numPools > 0) {
address currentPool = pools[0];

(
IBasePoolFactory currentFactory,
uint256 protocolSwapFeePercentage,
uint256 protocolYieldFeePercentage
) = _findFactoryForPool(currentPool);

_setPoolProtocolFees(currentPool, protocolSwapFeePercentage, protocolYieldFeePercentage);

// Common usage will be to call this for pools from the same factory. Or at a minimum, the pools will be
// grouped by factory. Check to see whether subsequent pools are from the `currentFactory`, to make as few
// expensive calls to `_findFactoryForPool` as possible. You can call this with an unordered set of pools,
// but it will be more expensive.
for (uint256 i = 1; i < numPools; ++i) {
currentPool = pools[i];

if (currentFactory.isPoolFromFactory(currentPool) == false) {
(currentFactory, protocolSwapFeePercentage, protocolYieldFeePercentage) = _findFactoryForPool(
currentPool
);
}

_setPoolProtocolFees(currentPool, protocolSwapFeePercentage, protocolYieldFeePercentage);
}
}
}

function setFactorySpecificProtocolFees(
/// @inheritdoc IProtocolFeePercentagesProvider
function setFactorySpecificProtocolFeePercentages(
address factory,
uint256 protocolSwapFeePercentage,
uint256 protocolYieldFeePercentage
@@ -153,7 +83,7 @@ contract ProtocolFeePercentagesProvider is SingletonAuthentication {
revert IProtocolFeeController.ProtocolYieldFeePercentageTooHigh();
}

// Best effort check that the factory is an IBasePoolFactory.
// Best effort check that `factory` is the address of an IBasePoolFactory.
bool poolFromFactory = IBasePoolFactory(factory).isPoolFromFactory(address(0));
if (poolFromFactory) {
revert InvalidFactory(factory);
@@ -165,27 +95,25 @@ contract ProtocolFeePercentagesProvider is SingletonAuthentication {
protocolYieldFeePercentage: protocolYieldFeePercentage.toUint64(),
isFactoryRegistered: true
});

// Add to iterable set. Ignore return value; it's possible to call this multiple times on a factory to update
// the fee percentages.
_factories.add(factory);
}

function _findFactoryForPool(address pool) private view returns (IBasePoolFactory, uint256, uint256) {
uint256 numFactories = _factories.length();
IBasePoolFactory basePoolFactory;

for (uint256 i = 0; i < numFactories; ++i) {
basePoolFactory = IBasePoolFactory(_factories.unchecked_at(i));
/// @inheritdoc IProtocolFeePercentagesProvider
function setProtocolFeePercentagesForPools(address factory, address[] memory pools) external {
FactoryProtocolFees memory factoryFees = _getValidatedProtocolFees(factory);

if (basePoolFactory.isPoolFromFactory(pool)) {
FactoryProtocolFees memory fees = _factoryDefaultFeePercentages[basePoolFactory];
for (uint256 i = 0; i < pools.length; ++i) {
address currentPool = pools[i];

return (basePoolFactory, fees.protocolSwapFeePercentage, fees.protocolYieldFeePercentage);
if (IBasePoolFactory(factory).isPoolFromFactory(currentPool) == false) {
elshan-eth marked this conversation as resolved.
Show resolved Hide resolved
revert PoolNotFromFactory(currentPool, factory);
}
}

revert PoolNotFromRegisteredFactory(pool);
_setPoolProtocolFees(
currentPool,
factoryFees.protocolSwapFeePercentage,
factoryFees.protocolYieldFeePercentage
);
}
}

function _getValidatedProtocolFees(address factory) private view returns (FactoryProtocolFees memory factoryFees) {
@@ -198,8 +126,8 @@ contract ProtocolFeePercentagesProvider is SingletonAuthentication {

// These are permissioned functions on `ProtocolFeeController`, so governance will need to allow this contract
// to call `setProtocolSwapFeePercentage` and `setProtocolYieldFeePercentage`.
function _setPoolProtocolFees(address pool, uint256 protocolSwapFee, uint256 protocolYieldFee) private {
_protocolFeeController.setProtocolSwapFeePercentage(pool, protocolSwapFee);
_protocolFeeController.setProtocolYieldFeePercentage(pool, protocolYieldFee);
function _setPoolProtocolFees(address pool, uint256 protocolSwapFeePercentage, uint256 protocolYieldFeePercentage) private {
_protocolFeeController.setProtocolSwapFeePercentage(pool, protocolSwapFeePercentage);
_protocolFeeController.setProtocolYieldFeePercentage(pool, protocolYieldFeePercentage);
}
}