RootGaugeFactory
The RootGaugeFactory contract is used to deploy liquidity gauges on the Ethereum mainnet. These gauges can then be voted on to be added to the GaugeController. If successful, the gauges will be able to receive CRV emissions, which then can be bridged via a Bridger contract to the child chains ChildGauge.
RootGaugeFactory.vy
The source code for the RootGaugeFactory.vy contract can be found on GitHub. The contract is written using Vyper version 0.3.10
The contract is deployed on Ethereum at 0x306A45a1478A000dC701A6e1f7a569afb8D9DCD6.
Deploying Gauges¶
The RootGaugeFactory allows the deployment of root gauges on Ethereum and child gauges on the child chains. Root gauges can only be deployed if there is a bridger contract set for the given chain ID, otherwise the chain is not supported.
Supported Chains
If get_bridger(chain_id) returns a non-zero address, the chain is supported and a RootGauge can be deployed.
deploy_gauge¶
RootGaugeFactory.deploy_gauge(_chain_id: uint256, _salt: bytes32) -> RootGauge
Function to deploy and initialize a new root gauge for a given chain ID. This function call reverts if there is no bridger contract set for the given _chain_id.
Returns: newly deployed gauge (RootGauge).
| Input | Type | Description |
|---|---|---|
_chain_id | uint256 | Chain ID of the child gauge |
_salt | bytes32 | Salt for the child gauge |
Source code
event DeployedGauge:
_implementation: indexed(address)
_chain_id: indexed(uint256)
_deployer: indexed(address)
_salt: bytes32
_gauge: RootGauge
interface RootGauge:
def bridger() -> Bridger: view
def initialize(_bridger: Bridger, _chain_id: uint256, _child: address): nonpayable
def transmit_emissions(): nonpayable
call_proxy: public(CallProxy)
get_bridger: public(HashMap[uint256, Bridger])
get_child_factory: public(HashMap[uint256, address])
get_child_implementation: public(HashMap[uint256, address])
get_implementation: public(address)
get_gauge: public(HashMap[uint256, RootGauge[max_value(uint256)]])
get_gauge_count: public(HashMap[uint256, uint256])
is_valid_gauge: public(HashMap[RootGauge, bool])
@payable
@external
def deploy_gauge(_chain_id: uint256, _salt: bytes32) -> RootGauge:
"""
@notice Deploy a root liquidity gauge
@param _chain_id The chain identifier of the counterpart child gauge
@param _salt A value to deterministically deploy a gauge
"""
bridger: Bridger = self.get_bridger[_chain_id]
assert bridger != empty(Bridger) # dev: chain id not supported
implementation: address = self.get_implementation
salt: bytes32 = keccak256(_abi_encode(_chain_id, _salt))
gauge: RootGauge = RootGauge(create_minimal_proxy_to(
implementation,
value=msg.value,
salt=salt,
))
child: address = self._get_child(_chain_id, salt)
idx: uint256 = self.get_gauge_count[_chain_id]
self.get_gauge[_chain_id][idx] = gauge
self.get_gauge_count[_chain_id] = idx + 1
self.is_valid_gauge[gauge] = True
gauge.initialize(bridger, _chain_id, child)
log DeployedGauge(implementation, _chain_id, msg.sender, _salt, gauge)
return gauge
@internal
def _get_child(_chain_id: uint256, salt: bytes32) -> address:
"""
@dev zkSync address derivation is ignored, so need to set child address through a vote manually
"""
child_factory: address = self.get_child_factory[_chain_id]
child_impl: bytes20 = convert(self.get_child_implementation[_chain_id], bytes20)
assert child_factory != empty(address) # dev: child factory not set
assert child_impl != empty(bytes20) # dev: child implementation not set
gauge_codehash: bytes32 = keccak256(
concat(0x602d3d8160093d39f3363d3d373d3d3d363d73, child_impl, 0x5af43d82803e903d91602b57fd5bf3))
digest: bytes32 = keccak256(concat(0xFF, convert(child_factory, bytes20), salt, gauge_codehash))
return convert(convert(digest, uint256) & convert(max_value(uint160), uint256), address)
@external
def initialize(_bridger: Bridger, _chain_id: uint256, _child: address):
"""
@notice Proxy initialization method
"""
assert self.factory == empty(Factory) # dev: already initialized
self.child_gauge = _child
self.chain_id = _chain_id
self.bridger = _bridger
self.factory = Factory(msg.sender)
inflation_params: InflationParams = InflationParams({
rate: CRV.rate(),
finish_time: CRV.future_epoch_time_write()
})
assert inflation_params.rate != 0
self.inflation_params = inflation_params
self.last_period = block.timestamp / WEEK
CRV.approve(_bridger.address, max_value(uint256))
deploy_child_gauge¶
RootGaugeFactory.deploy_child_gauge(_chain_id: uint256, _lp_token: address, _salt: bytes32, _manager: address = msg.sender)
Important
This function will only work if a call_proxy is set. Otherwise, the function will revert.
Function to deploy a new child gauge on the child chain.
| Input | Type | Description |
|---|---|---|
_chain_id | uint256 | Chain ID of the child gauge |
_lp_token | address | Address of the LP token |
_salt | bytes32 | Salt for the child gauge |
_manager | address | Address of the manager |
Source code
call_proxy: public(CallProxy)
get_bridger: public(HashMap[uint256, Bridger])
@external
def deploy_child_gauge(_chain_id: uint256, _lp_token: address, _salt: bytes32, _manager: address = msg.sender):
bridger: Bridger = self.get_bridger[_chain_id]
assert bridger != empty(Bridger) # dev: chain id not supported
self.call_proxy.anyCall(
self,
_abi_encode(
_lp_token,
_salt,
_manager,
method_id=method_id("deploy_gauge(address,bytes32,address)")
),
empty(address),
_chain_id
)
This example deploys a ChildGauge on Optimism for the 0xb757fc30bb2d96782188c45b6ebf20defe165ac7 LP token. 0x1234567890123456789012345678901234567890 is specified as the manager.
Transmitting Emissions¶
Once a root gauge has received emissions, they can be transmitted to the child gauge. This is done by calling the transmit_emissions function. Emissions can only be transmitted from the RootGaugeFactory.
Transmitting emissions is permissionless. Anyone can do it.
transmit_emissions¶
RootGaugeFactory.transmit_emissions(_gauge: RootGauge)
Function to mint and transmit emissions to the ChildGauge on the destination chain. This function is permissionsless and can be called by anyone.
| Input | Type | Description |
|---|---|---|
_gauge | address | Root gauge to transmit emissions for |
Source code
interface Bridger:
def check(_addr: address) -> bool: view
interface RootGauge:
def transmit_emissions(): nonpayable
@external
def transmit_emissions(_gauge: RootGauge):
"""
@notice Call `transmit_emissions` on a root gauge
@dev Entrypoint to request emissions for a child gauge.
The way that gauges work, this can also be called on the root
chain without a request.
"""
# in most cases this will return True
# for special bridges *cough cough Multichain, we can only do
# one bridge per tx, therefore this will verify msg.sender in [tx.origin, self.call_proxy]
assert _gauge.bridger().check(msg.sender)
_gauge.transmit_emissions()
@external
def transmit_emissions():
"""
@notice Mint any new emissions and transmit across to child gauge
"""
assert msg.sender == self.factory.address # dev: call via factory
MINTER.mint(self)
minted: uint256 = CRV.balanceOf(self)
assert minted != 0 # dev: nothing minted
bridger: Bridger = self.bridger
bridger.bridge(CRV, self.child_gauge, minted, value=bridger.cost())
@pure
@external
def check(_account: address) -> bool:
"""
@notice Verify if `_account` is allowed to bridge using `transmit_emissions`
@param _account The account calling `transmit_emissions`
"""
return True
@external
@payable
def bridge(_token: IERC20, _to: address, _amount: uint256, _min_amount: uint256=0) -> uint256:
"""
@notice Bridge `_token` through XDAO Layer Zero
@param _token The ERC20 asset to bridge
@param _to The receiver on `_chain_id`
@param _amount The amount of `_token` to deposit, 2^256-1 for the whole balance
@param _min_amount Minimum amount when to bridge
@return Bridged amount
"""
amount: uint256 = _amount
if amount == max_value(uint256):
amount = min(staticcall _token.balanceOf(msg.sender), staticcall _token.allowance(msg.sender, self))
assert amount >= _min_amount, "Amount too small"
assert extcall _token.transferFrom(msg.sender, self, amount)
extcall BRIDGE.bridge(_to, amount, msg.sender, value=self.balance)
return amount
get_bridger¶
RootGaugeFactory.get_bridger(_chain_id: uint256) -> address: view
Getter for the bridger for a given chain ID. This contract is used to bridge CRV emissions to the ChildGauge.
Returns: bridger (address).
| Input | Type | Description |
|---|---|---|
_chain_id | uint256 | Chain ID of the child gauge |
Source code
This example returns the Bridger contract for a given chain ID. If the chain ID is not supported, the function will return 0x0000000000000000000000000000000000000000.
>>> RootGaugeFactory.get_bridger(
)
Gauge Information¶
The RootGaugeFactory contract also provides a few getters to retrieve information about the deployed RootGauges.
get_gauge¶
RootGaugeFactory.get_gauge(_chain_id: uint256, _idx: uint256) -> RootGauge
Getter for gauges on a given chain ID and index.
Returns: gauge (address).
| Input | Type | Description |
|---|---|---|
_chain_id | uint256 | Chain ID of the child gauge |
_idx | uint256 | Index of the gauge |
This example returns the RootGauge address for a given chain ID and index.
>>> RootGaugeFactory.get_gauge(
Chain ID:
Gauge: )
get_gauge_count¶
RootGaugeFactory.get_gauge_count(_chain_id: uint256) -> uint256
Getter to get the number of gauges for a given chain ID. This value is incremented by one for each new gauge deployed.
Returns: number of gauges (uint256).
| Input | Type | Description |
|---|---|---|
_chain_id | uint256 | Chain ID of the child gauge |
This example returns the number of deployed RootGauges for a given chain ID.
>>> RootGaugeFactory.get_gauge_count(
)
is_valid_gauge¶
RootGaugeFactory.is_valid_gauge(_gauge: RootGauge) -> bool
Getter to check if a gauge is valid.
Returns: True if the gauge is valid, False otherwise (bool).
| Input | Type | Description |
|---|---|---|
_gauge | address | Root gauge to check validity for |
This example checks if a given RootGauge is valid.
>>> RootGaugeFactory.is_valid_gauge(
)
Child and Root Implementations and Factories¶
The RootGaugeFactory contract also provides a few getters to retrieve information about the deployed ChildGauge implementations and factories.
get_implementation¶
RootGaugeFactory.get_implementation() -> address: view
Getter for the RootGauge implementation contract. This implementation contract is used to deploy new RootGauge contracts using
Returns: implementation address (address).
This example returns the current RootGauge implementation contract address.
>>> RootGaugeFactory.get_implementation()
set_implementation¶
RootGaugeFactory.set_implementation(_implementation: address)
Guarded Method
This function is only callable by the owner of the contract.
Warning
Changing the implementation contract requires a change on all child factories.
Function to set the implementation contract of the RootGauge.
Emits: UpdateImplementation event.
| Input | Type | Description |
|---|---|---|
_implementation | address | Address of the new implementation |
Source code
event UpdateImplementation:
_old_implementation: address
_new_implementation: address
get_implementation: public(address)
owner: public(address)
@external
def set_implementation(_implementation: address):
"""
@notice Set the implementation
@dev Changing implementation require change on all child factories
@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
This example sets the RootGauge implementation to the address 0x6233394c3C466A45A505EFA4857489743168E9Fa.
get_child_factory¶
RootGaugeFactory.get_child_factory(_chain_id: uint256) -> address: view
Getter for the child factory for a given chain ID.
Returns: child factory address (address).
| Input | Type | Description |
|---|---|---|
_chain_id | uint256 | Chain ID of the child gauge |
This example returns the ChildGaugeFactory contract address for a specific chain ID.
>>> RootGaugeFactory.get_child_factory(
)
get_child_implementation¶
RootGaugeFactory.get_child_implementation(_chain_id: uint256) -> address: view
Getter for the child implementation for a given chain ID.
Returns: child implementation address (address).
| Input | Type | Description |
|---|---|---|
_chain_id | uint256 | Chain ID of the child gauge |
This example returns the ChildGauge implementation contract address for a given chain ID.
>>> RootGaugeFactory.get_child_implementation(
)
set_child¶
RootGaugeFactory.set_child(_chain_id: uint256, _bridger: Bridger, _child_factory: address, _child_impl: address)
Guarded Method
This function is only callable by the owner of the contract.
Function to set different child properties for a given chain ID such as the bridger contract, ChildGaugeFactory and ChildGauge implementation.
Emits: ChildUpdated event.
| Input | Type | Description |
|---|---|---|
_chain_id | uint256 | Chain ID of the child gauge |
_bridger | Bridger | Bridger contract for the child gauge |
_child_factory | address | Address of the new ChildGaugeFactory |
_child_impl | address | Address of the new ChildGauge implementation |
Source code
event ChildUpdated:
_chain_id: indexed(uint256)
_new_bridger: Bridger
_new_factory: address
_new_implementation: address
get_bridger: public(HashMap[uint256, Bridger])
get_child_factory: public(HashMap[uint256, address])
get_child_implementation: public(HashMap[uint256, address])
owner: public(address)
@external
def set_child(_chain_id: uint256, _bridger: Bridger, _child_factory: address, _child_impl: address):
"""
@notice Set the bridger for `_chain_id`
@param _chain_id The chain identifier to set the bridger for
@param _bridger The bridger contract to use
@param _child_factory Address of factory on L2 (needed in price derivation)
@param _child_impl Address of gauge implementation on L2 (needed in price derivation)
"""
assert msg.sender == self.owner # dev: only owner
log ChildUpdated(_chain_id, _bridger, _child_factory, _child_impl)
self.get_bridger[_chain_id] = _bridger
self.get_child_factory[_chain_id] = _child_factory
self.get_child_implementation[_chain_id] = _child_impl
This example sets the following properties for chain ID 252:
- Bridger:
0x0199429171bcE183048dccf1d5546Ca519EA9717 - ChildGaugeFactory:
0x0B8D6B6CeFC7Aa1C2852442e518443B1b22e1C52 - ChildGauge implementation:
0x6A611215540555A7feBCB64CB0Ed11Ac90F165Af
Call Proxy¶
call_proxy¶
RootGaugeFactory.call_proxy() -> CallProxy: view
Getter to get the call proxy which is used for inter-chain communication. This variable is initially set at contract initialization and can be changed via the set_call_proxy function.
Returns: call proxy (CallProxy).
Source code
interface CallProxy:
def anyCall(
_to: address, _data: Bytes[1024], _fallback: address, _to_chain_id: uint256
): nonpayable
call_proxy: public(CallProxy)
@external
def __init__(_call_proxy: CallProxy, _owner: address):
self.call_proxy = _call_proxy
log UpdateCallProxy(empty(CallProxy), _call_proxy)
self.owner = _owner
log TransferOwnership(empty(address), _owner)
This example returns the current call proxy contract address.
>>> RootGaugeFactory.call_proxy()
set_call_proxy¶
RootGaugeFactory.set_call_proxy(_call_proxy: CallProxy)
Guarded Method
This function is only callable by the owner of the contract.
Function to set the call proxy.
Emits: UpdateCallProxy event.
| Input | Type | Description |
|---|---|---|
_call_proxy | CallProxy | Call proxy to set |
Source code
event UpdateCallProxy:
_old_call_proxy: CallProxy
_new_call_proxy: CallProxy
call_proxy: public(CallProxy)
@external
def set_call_proxy(_call_proxy: CallProxy):
"""
@notice Set CallProxy
@param _call_proxy Contract to use for inter-chain communication
"""
assert msg.sender == self.owner
self.call_proxy = _call_proxy
log UpdateCallProxy(empty(CallProxy), _call_proxy)
This example sets the call proxy to 0x1234567890123456789012345678901234567890.
Contract Ownership¶
For contract ownership details, see here.