PR with Payload
The BalancerContractRegistry
was recently deployed to all networks, followed by the initializer contract.
Balancer Contract Registry Overview
The purpose of the contract is to maintain a registry of official Balancer Factories, Routers, Hooks, and valid ERC4626 tokens, for two main purposes.
On-chain use case
The first is to support the many instances where we need to know that a contract is “trusted” (i.e., is safe and behaves in the required manner). For instance, some hooks depend critically on the identity of the msg.sender
, which must be passed down through the Router. Since Routers are permissionless, a malicious one could spoof the sender and “fool” the hook. The hook must therefore “trust” the Router.
It is also important for the front-end to know when a particular wrapped token should be used with buffers. Not all “ERC4626” wrapped tokens are fully conforming, and buffer operations with non-conforming tokens may fail in various unexpected ways. It is not enough to simply check whether a buffer exists (e.g., by calling getBufferAsset
), since best practice is for the pool creator to initialize buffers for all such tokens regardless. They are permissionless, and could otherwise be initialized by anyone in unexpected ways.
This registry could be used to keep track of “known good” buffers, such that isActiveBalancerContract(ContractType.ERC4626, <address>)
returns true for fully-compliant tokens with properly initialized buffers.
Current solutions involve passing in the address of the trusted Router on deployment: but what if it needs to support multiple Routers? Or if the Router is deprecated and replaced? Instead, we can pass the registry address, and query this contract to determine whether the Router is a “trusted” one.
Off-chain use case
The second use case is for off-chain queries, or other protocols that need to easily determine, say, the “latest” Weighted Pool Factory. This contract provides isActiveBalancerContract(type, address)
for the first case, and getBalancerContract(type, name)
for the second. It is also possible to query all known information about an address, using getBalancerContractInfo(address)
, which returns a struct with the detailed state.
The Initialization Contract
The initialization process is similar to a migration - there is a contract that must be granted admin permission, which it renounces after completing the operation - though in this case we’re not really “migrating” anything, just populating the registry with the addresses of standard Balancer contracts, and adding some convenient aliases.
This contract adds the following contracts to the registry:
ContractType.ROUTER
- 20250307-v3-router-v2 (note this is the second deployment of the main router)
- 20241205-v3-batch-router
- 20241205-v3-buffer-router
- 20250123-v3-composite-liquidity-router-v2 (note this is also the second version)
ContractType.POOL_FACTORY
- 20241205-v3-weighted-pool
- 20241205-v3-stable-pool
- 20250121-v3-stable-surge
- 20250307-v3-liquidity-bootstrapping-pool
Aliases
- WeightedPool → 20241205-v3-weighted-pool
- StablePool → 20241205-v3-stable-pool
- Router → 20250307-v3-router-v2
- BatchRouter → 20241205-v3-batch-router
Future additions
In order to add more contracts (e.g., new versions, or contracts in other categories, such as ERC4626 wrapped tokens), governance would need to grant permission to call registerBalancerContract
. To add or update aliases, the permissioned function is addOrUpdateBalancerContractAlias
.
For instance, if we released a new BatchRouter with the task 20250612-v3-batch-router-v2, the registry would be updated as follows:
registerBalancerContract(ContractType.ROUTER, ‘20250612-v3-batch-router-v2’, addr)
addOrUpdateBalancerContractAlias(’BatchRouter’, addr)
There are additional permissioned functions to handle edge cases: deprecateBalancerContract
to indicate that a contract should no longer be used. There is also deregisterBalancerContract
, which removes an entry entirely (e.g., in case it was added erroneously, or with an incorrect type).
Technical Specifications
DAO multi-sig address: 0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f
Admin role: 0x0000000000000000000000000000000000000000000000000000000000000000
BalancerContractRegistryInitializer
contract addresses (to receive Admin permission; i.e., the DAO multi-sig grants the Admin role to the migration contracts):
- Arbitrum:
0x687b8C9b41E01Be8B591725fac5d5f52D0564d79
- Base:
0x7EE4D172Ae50C627a1BFA9A99E1260C54dA26fdF
- Gnosis:
0x0c6052254551EAe3ECac77B01DFcf1025418828f
- Mainnet:
0x91F17800Ca7DD29f2124D9D9eA8D4794693192C7
- Sepolia:
0x6B7C56698bEb33E1f0226e73C5D417e575315106
The permissionless function to call after granting admin permission is initializeBalancerContractRegistry
. This can only be called once.
Edit Maxis:
- added payload