S Price: $0.428031 (+0.79%)

Contract Diff Checker

Contract Name:
CurveXChainLiquidityGaugeFactory

Contract Source Code:

File 1 of 1 : CurveXChainLiquidityGaugeFactory

# pragma version 0.3.10
# pragma evm-version paris
"""
@title CurveXChainLiquidityGaugeFactory
@custom:version 2.0.1
@author Curve.Fi
@license Copyright (c) Curve.Fi, 2020-2024 - all rights reserved
@notice Layer2/Cross-Chain Gauge Factory for Curve
"""

version: public(constant(String[8])) = "2.0.1"


from vyper.interfaces import ERC20

interface ChildGauge:
    def initialize(_lp_token: address, _root: address, _manager: address): nonpayable
    def integrate_fraction(_user: address) -> uint256: view
    def user_checkpoint(_user: address) -> bool: nonpayable

interface CallProxy:
    def anyCall(
        _to: address, _data: Bytes[1024], _fallback: address, _to_chain_id: uint256
    ): nonpayable


event DeployedGauge:
    _implementation: indexed(address)
    _lp_token: indexed(address)
    _deployer: indexed(address)
    _salt: bytes32
    _gauge: address

event Minted:
    _user: indexed(address)
    _gauge: indexed(address)
    _new_total: uint256

event UpdateImplementation:
    _old_implementation: address
    _new_implementation: address

event UpdateVotingEscrow:
    _old_voting_escrow: address
    _new_voting_escrow: address

event UpdateRoot:
    _factory: address
    _implementation: address

event UpdateManager:
    _manager: address

event UpdateCallProxy:
    _old_call_proxy: address
    _new_call_proxy: address

event UpdateMirrored:
    _gauge: indexed(address)
    _mirrored: bool

event TransferOwnership:
    _old_owner: address
    _new_owner: address


WEEK: constant(uint256) = 86400 * 7


crv: public(ERC20)


get_implementation: public(address)
voting_escrow: public(address)

owner: public(address)
future_owner: public(address)
manager: public(address)

root_factory: public(address)
root_implementation: public(address)
call_proxy: public(address)
# [last_request][has_counterpart][is_valid_gauge]
gauge_data: public(HashMap[address, uint256])
# user -> gauge -> value
minted: public(HashMap[address, HashMap[address, uint256]])

get_gauge_from_lp_token: public(HashMap[address, address])
get_gauge_count: public(uint256)
get_gauge: public(address[max_value(int128)])

deployer: immutable(address)


@external
def __init__(_root_factory: address, _root_impl: address, _crv: address):
    """
    @param _root_factory Root factory to anchor to
    @param _root_impl Address of root gauge implementation to calculate mirror (can be updated)
    @param _crv Bridged CRV token address (might be zero if not known yet)
    """
    self.crv = ERC20(_crv)

    assert _root_factory != empty(address)
    assert _root_impl != empty(address)
    self.root_factory = _root_factory
    self.root_implementation = _root_impl
    log UpdateRoot(_root_factory, _root_impl)

    self.owner = msg.sender
    log TransferOwnership(empty(address), msg.sender)

    self.manager = msg.sender
    log UpdateManager(msg.sender)

    deployer = msg.sender


@external
def set_owner(_owner: address):

    assert msg.sender == deployer
    assert self.owner == deployer
    assert _owner != deployer

    log TransferOwnership(self.owner, _owner)
    self.owner = _owner


@internal
def _psuedo_mint(_gauge: address, _user: address):
    gauge_data: uint256 = self.gauge_data[_gauge]
    assert gauge_data != 0  # dev: invalid gauge

    # if is_mirrored and last_request != this week
    if gauge_data & 2 != 0 and (gauge_data >> 2) / WEEK != block.timestamp / WEEK:
        CallProxy(self.call_proxy).anyCall(
            self,
            _abi_encode(_gauge, method_id=method_id("transmit_emissions(address)")),
            empty(address),
            1,
        )
        # update last request time
        self.gauge_data[_gauge] = block.timestamp << 2 + 3

    assert ChildGauge(_gauge).user_checkpoint(_user)
    total_mint: uint256 = ChildGauge(_gauge).integrate_fraction(_user)
    to_mint: uint256 = total_mint - self.minted[_user][_gauge]

    if to_mint != 0 and self.crv != empty(ERC20):
        assert self.crv.transfer(_user, to_mint, default_return_value=True)
        self.minted[_user][_gauge] = total_mint

        log Minted(_user, _gauge, total_mint)


@external
@nonreentrant("lock")
def mint(_gauge: address):
    """
    @notice Mint everything which belongs to `msg.sender` and send to them
    @param _gauge `LiquidityGauge` address to get mintable amount from
    """
    self._psuedo_mint(_gauge, msg.sender)


@external
@nonreentrant("lock")
def mint_many(_gauges: address[32]):
    """
    @notice Mint everything which belongs to `msg.sender` across multiple gauges
    @param _gauges List of `LiquidityGauge` addresses
    """
    for i in range(32):
        if _gauges[i] == empty(address):
            pass
        self._psuedo_mint(_gauges[i], msg.sender)


@external
def deploy_gauge(_lp_token: address, _salt: bytes32, _manager: address = msg.sender) -> address:
    """
    @notice Deploy a liquidity gauge
    @param _lp_token The token to deposit in the gauge
    @param _salt A value to deterministically deploy a gauge
    @param _manager The address to set as manager of the gauge
    """
    if self.get_gauge_from_lp_token[_lp_token] != empty(address):
        # overwriting lp_token -> gauge mapping requires
        assert msg.sender == self.owner  # dev: only owner

    gauge_data: uint256 = 1  # set is_valid_gauge = True
    implementation: address = self.get_implementation
    salt: bytes32 = keccak256(_abi_encode(chain.id, _salt))
    gauge: address = create_minimal_proxy_to(
        implementation, salt=salt
    )

    if msg.sender == self.call_proxy:
        gauge_data += 2  # set mirrored = True
        log UpdateMirrored(gauge, True)
        # issue a call to the root chain to deploy a root gauge
        CallProxy(self.call_proxy).anyCall(
            self,
            _abi_encode(chain.id, _salt, method_id=method_id("deploy_gauge(uint256,bytes32)")),
            empty(address),
            1
        )

    self.gauge_data[gauge] = gauge_data

    idx: uint256 = self.get_gauge_count
    self.get_gauge[idx] = gauge
    self.get_gauge_count = idx + 1
    self.get_gauge_from_lp_token[_lp_token] = gauge

    # derive root gauge address
    gauge_codehash: bytes32 = keccak256(
        concat(
            0x602d3d8160093d39f3363d3d373d3d3d363d73,
            convert(self.root_implementation, bytes20),
            0x5af43d82803e903d91602b57fd5bf3,
        )
    )
    digest: bytes32 = keccak256(concat(0xFF, convert(self.root_factory, bytes20), salt, gauge_codehash))
    root: address = convert(convert(digest, uint256) & convert(max_value(uint160), uint256), address)

    # If root is uninitialized, self.owner can always set the root gauge manually
    # on the gauge contract itself via set_root_gauge method
    ChildGauge(gauge).initialize(_lp_token, root, _manager)

    log DeployedGauge(implementation, _lp_token, msg.sender, _salt, gauge)
    return gauge


@external
def set_crv(_crv: ERC20):
    """
    @notice Sets CRV token address
    @dev Child gauges reference the factory to fetch CRV address
         If empty, the gauges do not mint any CRV tokens.
    @param _crv address of CRV token on child chain
    """
    assert msg.sender == self.owner
    assert _crv != empty(ERC20)
    assert self.crv == empty(ERC20), "CRV already set"

    self.crv = _crv


@external
def set_root(_factory: address, _implementation: address):
    """
    @notice Update root addresses
    @dev Addresses are used only as helper methods
    @param _factory Root gauge factory
    @param _implementation Root gauge
    """
    assert msg.sender in [self.owner, self.manager]  # dev: access denied

    self.root_factory = _factory
    self.root_implementation = _implementation
    log UpdateRoot(_factory, _implementation)


@external
def set_voting_escrow(_voting_escrow: address):
    """
    @notice Update the voting escrow contract
    @param _voting_escrow Contract to use as the voting escrow oracle
    """
    assert msg.sender == self.owner  # dev: only owner

    log UpdateVotingEscrow(self.voting_escrow, _voting_escrow)
    self.voting_escrow = _voting_escrow


@external
def set_implementation(_implementation: address):
    """
    @notice Set the implementation
    @param _implementation The address of the implementation to use
    """
    assert msg.sender == self.owner  # dev: only owner

    log UpdateImplementation(self.get_implementation, _implementation)
    self.get_implementation = _implementation


@external
def set_mirrored(_gauge: address, _mirrored: bool):
    """
    @notice Set the mirrored bit of the gauge data for `_gauge`
    @param _gauge The gauge of interest
    @param _mirrored Boolean deteremining whether to set the mirrored bit to True/False
    """
    gauge_data: uint256 = self.gauge_data[_gauge]
    assert gauge_data != 0  # dev: invalid gauge
    assert msg.sender == self.owner  # dev: only owner

    gauge_data = gauge_data | 1  # set is_valid_gauge = True
    if _mirrored:
        gauge_data += 2  # set is_mirrored = True

    self.gauge_data[_gauge] = gauge_data
    log UpdateMirrored(_gauge, _mirrored)


@external
def set_call_proxy(_new_call_proxy: address):
    """
    @notice Set the address of the call proxy used
    @dev _new_call_proxy should adhere to the same interface as defined
    @param _new_call_proxy Address of the cross chain call proxy
    """
    assert msg.sender == self.owner

    log UpdateCallProxy(self.call_proxy, _new_call_proxy)
    self.call_proxy = _new_call_proxy


@external
def set_manager(_new_manager: address):
    assert msg.sender in [self.owner, self.manager]  # dev: access denied

    self.manager = _new_manager
    log UpdateManager(_new_manager)


@external
def commit_transfer_ownership(_future_owner: address):
    """
    @notice Transfer ownership to `_future_owner`
    @param _future_owner The account to commit as the future owner
    """
    assert msg.sender == self.owner  # dev: only owner

    self.future_owner = _future_owner


@external
def accept_transfer_ownership():
    """
    @notice Accept the transfer of ownership
    @dev Only the committed future owner can call this function
    """
    assert msg.sender == self.future_owner  # dev: only future owner

    log TransferOwnership(self.owner, msg.sender)
    self.owner = msg.sender


@view
@external
def is_valid_gauge(_gauge: address) -> bool:
    """
    @notice Query whether the gauge is a valid one deployed via the factory
    @param _gauge The address of the gauge of interest
    """
    return self.gauge_data[_gauge] != 0


@view
@external
def is_mirrored(_gauge: address) -> bool:
    """
    @notice Query whether the gauge is mirrored on Ethereum mainnet
    @param _gauge The address of the gauge of interest
    """
    return (self.gauge_data[_gauge] & 2) != 0


@view
@external
def last_request(_gauge: address) -> uint256:
    """
    @notice Query the timestamp of the last cross chain request for emissions
    @param _gauge The address of the gauge of interest
    """
    return self.gauge_data[_gauge] >> 2

Contract Name:
CurveMetaRegistry

Contract Source Code:

File 1 of 1 : CurveMetaRegistry

# pragma version 0.3.10
# pragma evm-version paris
"""
@title CurveMetaRegistry
@custom:version 1.1.0
@author Curve.Fi
@license Copyright (c) Curve.Fi, 2020-2024 - all rights reserved
"""

version: public(constant(String[8])) = "1.1.0"


# ---- interfaces ---- #
interface AddressProvider:
    def admin() -> address: view


# registry and registry handlers are considered to be the same here.
# registry handlers are just wrapper contracts that simplify/fix underlying registries
# for integrating it into the Metaregistry.
interface RegistryHandler:
    def find_pool_for_coins(_from: address, _to: address, i: uint256 = 0) -> address: view
    def get_admin_balances(_pool: address) -> uint256[MAX_COINS]: view
    def get_balances(_pool: address) -> uint256[MAX_COINS]: view
    def get_base_pool(_pool: address) -> address: view
    def get_coins(_pool: address) -> address[MAX_COINS]: view
    def get_coin_indices(_pool: address, _from: address, _to: address) -> (int128, int128, bool): view
    def get_decimals(_pool: address) -> uint256[MAX_COINS]: view
    def get_fees(_pool: address) -> uint256[10]: view
    def get_lp_token(_pool: address) -> address: view
    def get_n_coins(_pool: address) -> uint256: view
    def get_n_underlying_coins(_pool: address) -> uint256: view
    def get_pool_asset_type(_pool: address) -> uint256: view
    def get_pool_from_lp_token(_lp_token: address) -> address: view
    def get_pool_name(_pool: address) -> String[64]: view
    def get_pool_params(_pool: address) -> uint256[20]: view
    def get_underlying_balances(_pool: address) -> uint256[MAX_COINS]: view
    def get_underlying_coins(_pool: address) -> address[MAX_COINS]: view
    def get_underlying_decimals(_pool: address) -> uint256[MAX_COINS]: view
    def is_meta(_pool: address) -> bool: view
    def is_registered(_pool: address) -> bool: view
    def pool_count() -> uint256: view
    def pool_list(_index: uint256) -> address: view
    def get_virtual_price_from_lp_token(_addr: address) -> uint256: view
    def base_registry() -> address: view

interface GaugeFactory:
    def get_gauge_from_lp_token(_lp_token: address) -> address: view


# ---- events ---- #
event CommitNewAdmin:
    deadline: indexed(uint256)
    admin: indexed(address)

event NewAdmin:
    admin: indexed(address)


# ---- constants ---- #
MAX_REGISTRIES: constant(uint256) = 10
MAX_COINS: constant(uint256) = 8
MAX_POOL_PARAMS: constant(uint256) = 20
ADMIN_ACTIONS_DELAY: constant(uint256) = 3 * 86400


# ---- storage variables ---- #
admin: public(address)
future_admin: public(address)

# get registry/registry_handler by index, index starts at 0:
get_registry: public(HashMap[uint256, address])
registry_length: public(uint256)
gauge_factory: public(GaugeFactory)
gauge_type: public(int128)

deployer: immutable(address)

# ---- constructor ---- #
@external
def __init__(_gauge_factory: address, _gauge_type: int128):
    self.admin = msg.sender
    self.gauge_factory = GaugeFactory(_gauge_factory)
    self.gauge_type = _gauge_type

    deployer = msg.sender


@external
def set_owner(_owner: address):
    
    assert msg.sender == deployer
    assert self.admin == deployer
    assert _owner != deployer

    self.admin = _owner
    log NewAdmin(_owner)


# ---- internal methods ---- #
@internal
def _update_single_registry(_index: uint256, _registry_handler: address):
    assert _index <= self.registry_length

    if _index == self.registry_length:
        self.registry_length += 1

    self.get_registry[_index] = _registry_handler


@internal
@view
def _get_pool_from_lp_token(_token: address) -> address:
    registry_length: uint256 = self.registry_length
    for i in range(registry_length, bound=MAX_REGISTRIES):

        handler: address = self.get_registry[i]
        pool: address = RegistryHandler(handler).get_pool_from_lp_token(_token)
        if pool != empty(address):
            return pool
    return empty(address)


@internal
@view
def _get_registry_handlers_from_pool(_pool: address) -> address[MAX_REGISTRIES]:
    """
    @notice Get registry handler that handles the registry api for a pool
    @dev sometimes a factory pool can be registered in a manual registry
         because of this, we always take the last registry a pool is
         registered in and not the first, as manual registries are first
         and factories come later
    @param _pool address of the pool
    @return registry_handlers: address[MAX_REGISTRIES]
    """

    pool_registry_handler: address[MAX_REGISTRIES] = empty(address[MAX_REGISTRIES])
    c: uint256 = 0
    registry_length: uint256 = self.registry_length
    for i in range(registry_length, bound=MAX_REGISTRIES):

        handler: address = self.get_registry[i]

        if RegistryHandler(handler).is_registered(_pool):
            pool_registry_handler[c] = handler
            c += 1

    if pool_registry_handler[0] == empty(address):
        raise "no registry"

    return pool_registry_handler


# ---- most used methods: Admin / DAO privileged methods ---- #
@external
def add_registry_handler(_registry_handler: address):
    """
    @notice Adds a registry from the address provider entry
    @param _registry_handler Address of the handler contract
    """
    assert msg.sender == self.admin  # dev: only admin

    self._update_single_registry(self.registry_length, _registry_handler)


@external
def update_registry_handler(_index: uint256, _registry_handler: address):
    """
    @notice Updates the contract used to handle a registry
    @param _index The index of the registry in get_registry
    @param _registry_handler Address of the new handler contract
    """
    assert msg.sender == self.admin  # dev: only admin
    assert _index < self.registry_length

    self._update_single_registry(_index, _registry_handler)


@external
def update_gauge_data(_gauge_factory: address, _gauge_type: int128):
    assert msg.sender == self.admin
    self.gauge_factory = GaugeFactory(_gauge_factory)
    self.gauge_type = _gauge_type


# ---- view methods (API) of the contract ---- #


@external
@view
def get_registry_handlers_from_pool(_pool: address) -> address[MAX_REGISTRIES]:
    """
    @notice Get the registry handlers associated with a pool
    @param _pool Pool address
    @return List of registry handlers
    """
    return self._get_registry_handlers_from_pool(_pool)


@external
@view
def get_base_registry(registry_handler: address) -> address:
    """
    @notice Get the registry associated with a registry handler
    @param registry_handler Registry Handler address
    @return Address of base registry
    """
    return RegistryHandler(registry_handler).base_registry()


@view
@external
def find_pool_for_coins(
    _from: address, _to: address, i: uint256 = 0
) -> address:
    """
    @notice Find the ith available pool containing the input pair
    @param _from Address of coin to be sent
    @param _to Address of coin to be received
    @param i Index of the pool to return
    @return Pool address
    """
    pools_found: uint256 = 0
    pool: address = empty(address)
    registry: address = empty(address)

    registry_length: uint256 = self.registry_length
    for registry_index in range(registry_length, bound=MAX_REGISTRIES):

        registry = self.get_registry[registry_index]

        for j in range(0, 65536):

            pool = RegistryHandler(registry).find_pool_for_coins(_from, _to, j)

            if pool == empty(address):
                break

            pools_found += 1
            if pools_found > i:
                return pool

    return pool


@view
@external
def find_pools_for_coins(_from: address, _to: address) -> DynArray[address, 1000]:
    """
    @notice Find all pools that contain the input pair
    @param _from Address of coin to be sent
    @param _to Address of coin to be received
    @return Pool addresses
    """
    pools_found: DynArray[address, 1000]= empty(DynArray[address, 1000])
    pool: address = empty(address)
    registry: address = empty(address)

    registry_length: uint256 = self.registry_length
    for registry_index in range(registry_length, bound=MAX_REGISTRIES):

        registry = self.get_registry[registry_index]

        for j in range(0, 65536):

            pool = RegistryHandler(registry).find_pool_for_coins(_from, _to, j)
            if pool == empty(address):
                break
            pools_found.append(pool)

    return pools_found


@external
@view
def get_admin_balances(_pool: address, _handler_id: uint256 = 0) -> uint256[MAX_COINS]:
    """
    @notice Get the current admin balances (uncollected fees) for a pool
    @dev _handler_id < 1 if pool is registry in one handler, more than 0 otherwise
    @param _pool Pool address
    @param _handler_id id of registry handler
    @return List of uint256 admin balances
    """
    return RegistryHandler(self._get_registry_handlers_from_pool(_pool)[_handler_id]).get_admin_balances(_pool)


@external
@view
def get_balances(_pool: address, _handler_id: uint256 = 0)  -> uint256[MAX_COINS]:
    """
    @notice Get balances for each coin within a pool
    @dev For metapools, these are the wrapped coin balances
    @param _pool Pool address
    @param _handler_id id of registry handler
    @return uint256 list of balances
    """
    return RegistryHandler(self._get_registry_handlers_from_pool(_pool)[_handler_id]).get_balances(_pool)


@external
@view
def get_base_pool(_pool: address, _handler_id: uint256 = 0) -> address:
    """
    @notice Get the base pool for a given factory metapool
    @dev Will return empty(address) if pool is not a metapool
    @param _pool Metapool address
    @param _handler_id id of registry handler
    @return Address of base pool
    """
    return RegistryHandler(self._get_registry_handlers_from_pool(_pool)[_handler_id]).get_base_pool(_pool)


@view
@external
def get_coin_indices(_pool: address, _from: address, _to: address, _handler_id: uint256 = 0) -> (int128, int128, bool):
    """
    @notice Convert coin addresses to indices for use with pool methods
    @param _pool Pool address
    @param _from Coin address to be used as `i` within a pool
    @param _to Coin address to be used as `j` within a pool
    @param _handler_id id of registry handler
    @return from index, to index, is the market underlying ?
    """
    return RegistryHandler(self._get_registry_handlers_from_pool(_pool)[_handler_id]).get_coin_indices(_pool, _from, _to)


@external
@view
def get_coins(_pool: address, _handler_id: uint256 = 0) -> address[MAX_COINS]:
    """
    @notice Get the coins within a pool
    @dev For metapools, these are the wrapped coin addresses
    @param _pool Pool address
    @param _handler_id id of registry handler
    @return List of coin addresses
    """
    return RegistryHandler(self._get_registry_handlers_from_pool(_pool)[_handler_id]).get_coins(_pool)


@external
@view
def get_decimals(_pool: address, _handler_id: uint256 = 0) -> uint256[MAX_COINS]:
    """
    @notice Get decimal places for each coin within a pool
    @dev For metapools, these are the wrapped coin decimal places
    @param _pool Pool address
    @param _handler_id id of registry handler
    @return uint256 list of decimals
    """
    return RegistryHandler(self._get_registry_handlers_from_pool(_pool)[_handler_id]).get_decimals(_pool)


@external
@view
def get_fees(_pool: address, _handler_id: uint256 = 0) -> uint256[10]:
    """
    @notice Get pool fees
    @dev Fees are expressed as integers
    @param _pool Pool address
    @param _handler_id id of registry handler
    @return Pool fee as uint256 with 1e10 precision
            Admin fee as 1e10 percentage of pool fee
            Mid fee
            Out fee
            6 blank spots for future use cases
    """
    return RegistryHandler(self._get_registry_handlers_from_pool(_pool)[_handler_id]).get_fees(_pool)


@external
@view
def get_gauge(_pool: address, gauge_idx: uint256 = 0, _handler_id: uint256 = 0) -> address:
    """
    @notice Get a single liquidity gauge contract associated with a pool
    @param _pool Pool address
    @param gauge_idx Index of gauge to return
    @param _handler_id id of registry handler
    @return Address of gauge
    """
    lp_token: address = RegistryHandler(self._get_registry_handlers_from_pool(_pool)[_handler_id]).get_lp_token(_pool)
    return self.gauge_factory.get_gauge_from_lp_token(lp_token)


@external
@view
def get_gauge_type(_pool: address, gauge_idx: uint256 = 0, _handler_id: uint256 = 0) -> int128:
    """
    @notice Get gauge_type of a single liquidity gauge contract associated with a pool
    @param _pool Pool address
    @param gauge_idx Index of gauge to return
    @param _handler_id id of registry handler
    @return Address of gauge
    """
    return self.gauge_type


@external
@view
def get_lp_token(_pool: address, _handler_id: uint256 = 0) -> address:
    """
    @notice Get the address of the LP token of a pool
    @param _pool Pool address
    @param _handler_id id of registry handler
    @return Address of the LP token
    """
    return RegistryHandler(self._get_registry_handlers_from_pool(_pool)[_handler_id]).get_lp_token(_pool)


@external
@view
def get_n_coins(_pool: address, _handler_id: uint256 = 0) -> uint256:
    """
    @notice Get the number of coins in a pool
    @dev For metapools, it is tokens + wrapping/lending token (no underlying)
    @param _pool Pool address
    @return Number of coins
    """
    return RegistryHandler(self._get_registry_handlers_from_pool(_pool)[_handler_id]).get_n_coins(_pool)


@external
@view
def get_n_underlying_coins(_pool: address, _handler_id: uint256 = 0) -> uint256:
    """
    @notice Get the number of underlying coins in a pool
    @dev For non metapools, returns the same as get_n_coins
    @param _pool Pool address
    @return Number of coins
    """
    return RegistryHandler(self._get_registry_handlers_from_pool(_pool)[_handler_id]).get_n_underlying_coins(_pool)


@external
@view
def get_pool_asset_type(_pool: address, _handler_id: uint256 = 0) -> uint256:
    """
    @notice Query the asset type of `_pool`
    @param _pool Pool Address
    @return The asset type as an unstripped string
    @dev 0 : USD, 1: ETH, 2: BTC, 3: Other, 4: CryptoSwap
    """
    return RegistryHandler(self._get_registry_handlers_from_pool(_pool)[_handler_id]).get_pool_asset_type(_pool)


@external
@view
def get_pool_from_lp_token(_token: address, _handler_id: uint256 = 0) -> address:
    """
    @notice Get the pool associated with an LP token
    @param _token LP token address
    @return Pool address
    """
    return self._get_pool_from_lp_token(_token)


@external
@view
def get_pool_params(_pool: address, _handler_id: uint256 = 0) -> uint256[MAX_POOL_PARAMS]:
    """
    @notice Get the parameters of a pool
    @param _pool Pool address
    @param _handler_id id of registry handler
    @return Pool parameters
    """
    registry_handler: address = self._get_registry_handlers_from_pool(_pool)[_handler_id]
    return RegistryHandler(registry_handler).get_pool_params(_pool)


@external
@view
def get_pool_name(_pool: address, _handler_id: uint256 = 0) -> String[64]:
    """
    @notice Get the given name for a pool
    @param _pool Pool address
    @return The name of a pool
    """
    return RegistryHandler(self._get_registry_handlers_from_pool(_pool)[_handler_id]).get_pool_name(_pool)


@external
@view
def get_underlying_balances(_pool: address, _handler_id: uint256 = 0) -> uint256[MAX_COINS]:
    """
    @notice Get balances for each underlying coin within a pool
    @dev For non metapools, returns the same value as `get_balances`
    @param _pool Pool address
    @param _handler_id id of registry handler
    @return uint256 List of underlyingbalances
    """
    return RegistryHandler(self._get_registry_handlers_from_pool(_pool)[_handler_id]).get_underlying_balances(_pool)


@external
@view
def get_underlying_coins(_pool: address, _handler_id: uint256 = 0) -> address[MAX_COINS]:
    """
    @notice Get the underlying coins within a pool
    @dev For non metapools, returns the same value as `get_coins`
    @param _pool Pool address
    @param _handler_id id of registry handler
    @return List of coin addresses
    """
    return RegistryHandler(self._get_registry_handlers_from_pool(_pool)[_handler_id]).get_underlying_coins(_pool)


@external
@view
def get_underlying_decimals(_pool: address, _handler_id: uint256 = 0) -> uint256[MAX_COINS]:
    """
    @notice Get decimal places for each underlying coin within a pool
    @dev For non metapools, returns the same value as `get_decimals`
    @param _pool Pool address
    @param _handler_id id of registry handler
    @return uint256 list of decimals
    """
    return RegistryHandler(self._get_registry_handlers_from_pool(_pool)[_handler_id]).get_underlying_decimals(_pool)


@external
@view
def get_virtual_price_from_lp_token(_token: address, _handler_id: uint256 = 0) -> uint256:
    """
    @notice Get the virtual price of a pool LP token
    @param _token LP token address
    @param _handler_id id of registry handler
    @return uint256 Virtual price
    """
    pool: address = self._get_pool_from_lp_token(_token)
    registry_handler: address = self._get_registry_handlers_from_pool(pool)[_handler_id]
    return RegistryHandler(registry_handler).get_virtual_price_from_lp_token(_token)


@external
@view
def is_meta(_pool: address, _handler_id: uint256 = 0) -> bool:
    """
    @notice Verify `_pool` is a metapool
    @param _pool Pool address
    @param _handler_id id of registry handler
    @return True if `_pool` is a metapool
    """
    return RegistryHandler(self._get_registry_handlers_from_pool(_pool)[_handler_id]).is_meta(_pool)


@external
@view
def is_registered(_pool: address, _handler_id: uint256 = 0) -> bool:
    """
    @notice Check if a pool is in the metaregistry using get_n_coins
    @param _pool The address of the pool
    @param _handler_id id of registry handler
    @return A bool corresponding to whether the pool belongs or not
    """
    return self._get_registry_handlers_from_pool(_pool)[_handler_id] != empty(address)


@external
@view
def pool_count() -> uint256:
    """
    @notice Return the total number of pools tracked by the metaregistry
    @return uint256 The number of pools in the metaregistry
    """
    total_pools: uint256 = 0
    registry_length: uint256 = self.registry_length
    for i in range(registry_length, bound=MAX_REGISTRIES):

        handler: address = self.get_registry[i]
        total_pools += RegistryHandler(handler).pool_count()
    return total_pools


@external
@view
def pool_list(_index: uint256) -> address:
    """
    @notice Return the pool at a given index in the metaregistry
    @param _index The index of the pool in the metaregistry
    @return The address of the pool at the given index
    """
    pools_skip: uint256 = 0
    registry_length: uint256 = self.registry_length
    for i in range(registry_length, bound=MAX_REGISTRIES):

        handler: address = self.get_registry[i]
        count: uint256 = RegistryHandler(handler).pool_count()
        if _index - pools_skip < count:
            return RegistryHandler(handler).pool_list(_index - pools_skip)
        pools_skip += count
    return empty(address)


@external
def commit_transfer_ownership(_addr: address):
    """
    @notice Transfer ownership of this contract to `addr`
    @param _addr Address of the new owner
    """
    assert msg.sender == self.admin  # dev: admin only
    self.future_admin = _addr


@external
def accept_transfer_ownership():
    """
    @notice Accept a pending ownership transfer
    @dev Only callable by the new owner
    """
    _admin: address = self.future_admin
    assert msg.sender == _admin  # dev: future admin only

    self.admin = _admin
    self.future_admin = empty(address)

Context size (optional):