Bush Contract Registry

Overview

The Bush Contract Registry (BushContractRegistry) is the onchain source of truth for official Bush contracts. It keeps the canonical list of trusted routers, pool factories, hooks, and ERC4626 wrappers, so you can discover and validate protocol contracts on every supported network.

Pool factories carry extra metadata (their pool type and hook configuration). This lets aggregators, solvers, indexers, and onchain integrations answer two more questions:

  1. Which factories are official, and what do they deploy? See getAllPoolFactories and getPoolFactoriesByType.
  2. Did an official factory deploy this pool? If so, what type is it and what hook does it have? See getFactoryForPool and isPoolFromRegisteredFactory.

The registry address is on the Deployment Addresses page, under Peripherals (deployment task 20260923-v3-contract-registry-v2). The ABI is on the Bush Contract Registry ABI page.

Purpose

The registry enables:

  • Router Validation: Verify trusted routers before allowing transactions with the Vault
  • Pool Validation: Check that an official factory deployed a pool, and find its pool type and hook
  • Contract Discovery: Query deployed pool factories, their pool types, and their deployment status
  • Alias Management: Use human-readable names instead of addresses
  • Integration Safety: Ensure applications only interact with authorized contracts

Contract Types

The registry categorizes contracts into the following types:

TypeValueDescription
OTHER0Unspecified contract type (catch-all for helper contracts, etc.)
POOL_FACTORY1Factory contracts that deploy pools
ROUTER2Router contracts for swaps and liquidity operations
HOOK3Hook contracts for custom pool behavior
ERC46264ERC4626 wrapper contracts (restricted to wrappers known to be compatible)

Each contract is registered under a unique name and address, and has exactly one type. Governance registers pool factories with registerPoolFactory, which records their metadata. registerBushContract rejects POOL_FACTORY with UseRegisterPoolFactory. Every contract type uses the same functions for deprecation, deregistration, and aliases.

Lifecycle Semantics

Deregistration and deprecation mean different things, so consumers need to understand all three states:

StateMeaning
Registered + activeThe contract is trusted. A factory in this state may still create new pools.
Registered + deprecated (isActive == false)The contract has been superseded. A deprecated factory should not be used to create new pools. Pools it already deployed are still valid, official pools. Deprecation does not mean you should stop routing through existing pools. It mirrors calling disable() on the factory.
Not registered (isRegistered == false)The contract is not, or is no longer, trusted. Governance uses deregistration to correct mistakes and to revoke trust in an unsafe contract. Pools from a deregistered factory no longer resolve via getFactoryForPool.

Contract Aliases

Aliases are friendly names for common contracts. The aliases below are illustrative. Governance can change an alias target at any time, for example when moving to a new pool version such as LBP V3 to V4.

To get the current target, query the registry onchain with getBushContract(type, alias). For factories, you can also use getPoolFactory(alias).

AliasTypeTarget
WeightedPoolPOOL_FACTORYWeightedPoolFactory
StablePoolPOOL_FACTORYStablePoolFactory
RouterROUTERRouter v2
BatchRouterROUTERBatchRouter

Pool Factory Metadata

Hook Modes

Each registered factory has a HookMode, which describes how it attaches hooks to the pools it deploys:

ModeValueMeaning
NONE0The factory always deploys pools without a hook.
OPTIONAL1The factory accepts a hook address at pool creation time, and the address may be zero. The pool creator decides whether the pool has a hook.
SPECIFIC2The factory attaches the same hook to every pool it deploys (for example, StableSurgePoolFactory). The registry stores this hook address with the factory.

A pool from an OPTIONAL factory may or may not have a hook. To find out, read the hook from the Vault with IVault.getHooksConfig(pool).hooksContract. A pool from a SPECIFIC factory always has the registered hook.

Data Structures

ContractInfo: the stored state for any registered contract:

FieldTypeDescription
contractTypeContractTypeThe type of contract.
isRegisteredboolWhether there is an entry for the associated address.
isActiveboolIf registered, whether it is active or deprecated.

FactoryInfo: the registry's stored state for a pool factory. isRegistered and isActive match the factory's ContractInfo:

FieldTypeDescription
namestringThe factory's name, usually the deployment task id (e.g. "20260803-v3-weighted-pool").
poolTypestringThe type of pool deployed by this factory (e.g. "WEIGHTED", "STABLE"). Free-form and case-sensitive.
hookModeHookModeWhether pools from this factory have no hook, an optional hook, or a specific hook.
hookaddressThe hook attached to every pool from this factory (zero unless hookMode is SPECIFIC).
isRegisteredboolWhether there is an entry for the associated address.
isActiveboolIf registered, whether it is active or deprecated.

FactoryDetails: FactoryInfo plus live data read from the factory itself (via IVersion, IPoolVersion, and IBasePoolFactory). Use it to get everything about a factory in one call:

FieldTypeDescription
factoryaddressThe address of the factory (zero if not registered).
infoFactoryInfoThe registry's stored information about the factory.
factoryVersionstringThe factory's own version string (IVersion.version()).
poolVersionstringThe version string of pools deployed by this factory (IPoolVersion.getPoolVersion()).
isDisabledboolWhether the factory has been disabled onchain (IBasePoolFactory.isDisabled()). A disabled factory can never create new pools.
poolCountuint256The number of pools the factory has deployed. Use it to page through getPoolsInRange on the factory.

The registry stores only info. The other FactoryDetails fields are read from the factory on each call, so they always show its current state. If a factory doesn't support one of these calls, the field is left blank (an empty string, false, or 0).

Query Functions

Contract queries

FunctionReturns
isTrustedRouter(router)true if the address is an active ROUTER.
isActiveBushContract(contractType, contractAddress)true if the address is a registered, active contract of that type.
getBushContract(contractType, name)The address for a name or alias, and whether it is active.
getBushContractInfo(contractAddress)The ContractInfo for an address.

Factory queries

FunctionReturns
isActivePoolFactory(factory)true if the address is a registered, active factory.
isActivePoolFactoryOfType(poolType, factory)true if the address is a registered, active factory of that pool type.
isRegisteredPoolFactory(factory)true if the address is a registered factory (active or deprecated).
getPoolFactory(name)The factory address and FactoryInfo for a name or alias.
getPoolFactoryInfo(factory)The FactoryInfo for a factory address.
getPoolFactoryHook(factory)The factory's hook (zero unless SPECIFIC).
getPoolFactoryDetails(factory)The FactoryDetails for a factory address.

Pool queries

These functions loop over every registered factory, so they are intended for offchain use. To validate a pool onchain, see Verify a pool came from an official factory.

FunctionReturns
getFactoryForPool(pool)The registered factory that deployed the pool, and its FactoryInfo (zero / blank if none).
getFactoryDetailsForPool(pool)The FactoryDetails of the factory that deployed the pool.
isPoolFromRegisteredFactory(pool)true if any registered factory (active or deprecated) deployed the pool.
isPoolOfType(poolType, pool)true if a registered factory of that pool type deployed the pool.

Factory enumeration

FunctionReturns
getPoolFactoryCount() / getPoolFactoryAt(index)Indexed access to registered factories. The order can change when a factory is deregistered.
getPoolFactories()All registered factory addresses, including deprecated ones.
getAllPoolFactories()All registered factory addresses and their FactoryInfo, including deprecated ones.
getAllPoolFactoryDetails()FactoryDetails for every registered factory. This makes several external calls per factory, so it is intended for offchain use.
getPoolFactoriesByType(poolType, activeOnly)Factory addresses of a given pool type, optionally excluding deprecated ones.

Usage Examples

Validate a Router

Check if a router address is trusted before allowing transactions:

// In your smart contract
IBushContractRegistry registry = IBushContractRegistry(REGISTRY_ADDRESS);

function executeSwap(address router, SwapParams calldata params) external {
    require(registry.isTrustedRouter(router), "Untrusted router");
    // Execute swap...
}

Verify a pool came from an official factory

The pool queries (getFactoryForPool, getFactoryDetailsForPool, isPoolFromRegisteredFactory, and isPoolOfType) are meant for offchain lookups. Each one calls IBasePoolFactory.isPoolFromFactory on every registered factory, active or deprecated, and skips any factory that reverts. Their gas cost therefore grows with the number of registered factories.

To check a pool onchain, your contract must already have the address of the factory that should have deployed it. For example, the caller can pass it in, or you can look it up by alias with getPoolFactory. Then run two checks:

  1. Ask the registry whether that address is an approved factory.
  2. Ask the factory whether it deployed the pool.
import { IBasePoolFactory } from "@bush.fi/v3-interfaces/contracts/vault/IBasePoolFactory.sol";

function validatePool(address pool, address factory) external view {
    // 1. The factory must be registered. Pools from deprecated factories are still official
    // pools, so check isRegistered. Use isActivePoolFactory(factory) instead only if the
    // factory must also be the current one.
    IBushContractRegistry.FactoryInfo memory info = registry.getPoolFactoryInfo(factory);
    require(info.isRegistered, "Factory not registered");

    // 2. The factory must have deployed this pool
    require(IBasePoolFactory(factory).isPoolFromFactory(pool), "Pool not from factory");

    // info.poolType, info.hookMode, info.hook are now available
}

If you only need to know that the factory is registered and active for a given pool type, use isActivePoolFactoryOfType(poolType, factory) as the first check instead.

When you don't know the factory, look it up offchain with getFactoryForPool(pool) (see Query the registry offchain), then pass that address into your onchain call.

Discover active factories for a pool type

address[] memory weightedFactories = registry.getPoolFactoriesByType("WEIGHTED", true); // activeOnly = true

A frontend or router can use this to offer pool creation only through the current recommended factory for a pool type, and to ignore deprecated factories.

Lookup by Alias

Find contracts using human-readable aliases:

(address factoryAddress, IBushContractRegistry.FactoryInfo memory info) = registry.getPoolFactory("WeightedPool");

require(info.isActive, "Factory not active");
// Use factoryAddress to create pool...

The generic getBushContract(ContractType.POOL_FACTORY, "WeightedPool") also works. It returns only the address and the active flag.

Query the registry offchain

Resources:

getAllPoolFactoryDetails() returns the same snapshot, plus each factory's live version and pool count. It makes several extra external calls per factory. Use it only if you need factoryVersion, poolVersion, isDisabled, or poolCount up front.

Events

EventEmitted when
BushContractRegistered(contractType, contractName, contractAddress)Any contract is registered, including pool factories.
PoolFactoryRegistered(factory, name, poolType, hookMode, hook)A pool factory is registered. It is emitted alongside BushContractRegistered. Its strings are not indexed, so you can read them from the event data.
BushContractDeregistered(contractType, contractName, contractAddress)Any contract is deregistered. contractType is indexed, so you can filter for POOL_FACTORY.
BushContractDeprecated(contractAddress)Any contract is deprecated.
ContractAliasUpdated(contractAlias, contractAddress)An alias is added or pointed at a new contract.

Integration Guide

For Frontend Developers

  1. List available pool types with getAllPoolFactories(). To find the current recommended factory for one pool type, use getPoolFactoriesByType(poolType, true).
  2. Display user-friendly names using aliases.
  3. Validate router addresses before transactions.
  4. When a user enters a pool address, resolve it with getFactoryForPool before showing it as an official Bush pool.

For Smart Contract Developers

When integrating with Bush:

  1. Always validate routers with isTrustedRouter().
  2. Validate a pool before relying on standard Bush pool behavior. Starting from its expected factory, check that the factory is registered (getPoolFactoryInfo or isRegisteredPoolFactory), then call IBasePoolFactory(factory).isPoolFromFactory(pool). Don't call getFactoryForPool or the other pool queries onchain, because they loop over every registered factory.
  3. Pools from a deprecated factory are still valid. Skip only pools whose factory is deregistered (isRegistered == false).
  4. Don't assume a pool has no hook. Read hookMode and hook from the registry. For an OPTIONAL factory, check the actual hook with IVault.getHooksConfig.
  5. Consider caching active contracts to reduce gas.

For Aggregators and Indexers

  1. At startup, query the trusted routers. Call getAllPoolFactories() to load the full set of known factories. Use getAllPoolFactoryDetails() instead if you also need live version and pool-count data.
  2. To keep the set current, listen for PoolFactoryRegistered, BushContractDeregistered, and BushContractDeprecated. BushContractDeprecated is emitted for every contract type. Match its address against your factory and router sets, or check it with getBushContractInfo.
  3. As you index the PoolCreated events emitted by the factories, classify each pool with its factory's poolType and hookMode from FactoryInfo. You don't need a hosted API.
  4. Update your routing logic when routers or factories become inactive. Keep routing through existing pools from deprecated factories.

See Also