ChildGaugeFactory
The ChildGaugeFactory
contract is used to deploy liquidity gauges on the child chains. It serves as some sort of registry for the child gauges by storing information such as the gauge data, minted amounts, and more. It is also the contract where CRV emissions are claimed from.
ChildGaugeFactory.vy
The source code for the ChildGaugeFactory.vy
contract can be found on GitHub. The contract is written using Vyper version 0.3.10
A full list of all deployed ChildGaugeFactory
contracts can be found here.
Deploy Child Gauge¶
Child gauges can either be deployed from the RootChainFactory
or directly from the according ChildGaugeFactory
.
deploy_gauge
¶
ChildGaugeFactory.deploy_gauge(_lp_token: address, _salt: bytes32, _manager: address = msg.sender) -> address
Function to deploy a new gauge.
Returns: newly deployed gauge (address
).
Emits: DeployedGauge
event.
Input | Type | Description |
---|---|---|
_lp_token | address | LP token to deploy gauge for |
_salt | bytes32 | Salt to deterministically deploy gauge |
_manager | address | Address to set as manager of the gauge; defaults to msg.sender |
Source code
interface ChildGauge:
def initialize(_lp_token: address, _root: address, _manager: address): nonpayable
event DeployedGauge:
_implementation: indexed(address)
_lp_token: indexed(address)
_deployer: indexed(address)
_salt: bytes32
_gauge: 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)])
@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
Minting Emissions¶
CRV emissions are minted directly from the child gauge and can be claimed by the user. They can not be claimed from the ChildGauge
contract itself.
When claiming emissions via claim
or claim_many
, and is_mirrored
is set to True
and last_request
is not the current week, a call to the root chain is made to transmit the emissions to the child gauge.
mint
¶
ChildGaugeFactory.mint(_gauge: address)
Function to mint all CRV emissions belonging to msg.sender
from a given gauge.
Emits: Minted
Input | Type | Description |
---|---|---|
_gauge | address | Gauge to mint CRV emissions from |
Source code
event Minted:
_user: indexed(address)
_gauge: indexed(address)
_new_total: uint256
WEEK: constant(uint256) = 86400 * 7
crv: public(ERC20)
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)])
@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)
@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)
mint_many
¶
ChildGaugeFactory.mint_many(_gauges: address[32]) -> bool
Function to mint all CRV emissions belonging to msg.sender
from multiple gauges.
Emits: Minted
Input | Type | Description |
---|---|---|
_gauges | address[32] | Array of gauges to mint CRV emissions from |
Source code
event Minted:
_user: indexed(address)
_gauge: indexed(address)
_new_total: uint256
WEEK: constant(uint256) = 86400 * 7
crv: public(ERC20)
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)])
@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)
@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)
minted
¶
ChildGaugeFactory.minted(_user: address, _gauge: address) -> uint256
Getter to check the amount of CRV emissions minted for a user from a given gauge.
Returns: Amount of CRV emissions minted (uint256
).
Input | Type | Description |
---|---|---|
_user | address | User to check minted amount for |
_gauge | address | Gauge to check minted amount for |
Gauge Data¶
The ChildGaugeFactory
contract stores different gauge data for all the child gauges deployed via the factory.
gauge_data
¶
ChildGaugeFactory.gauge_data(_gauge: address) -> uint256
Getter to check gauge data. The variable stores a uint256
value where the bits are stored as follows:
[0:2]
:is_valid_gauge
[2:3]
:has_counterpart
[3:256]
:last_request
Returns: gauge data (uint256
).
Input | Type | Description |
---|---|---|
_gauge | address | Gauge to check data for |
Source code
is_valid_gauge
¶
ChildGaugeFactory.is_valid_gauge(_gauge: address) -> bool
Getter to check if a gauge is valid.
Returns: True
if the gauge is valid, False
otherwise (bool
).
Input | Type | Description |
---|---|---|
_gauge | address | Gauge to check validity for |
Source code
get_gauge_from_lp_token
¶
ChildGaugeFactory.get_gauge_from_lp_token(_lp_token: address) -> address
Getter for gauge associated with a given LP token.
Returns: gauge (address
).
Input | Type | Description |
---|---|---|
_lp_token | address | LP token to check gauge for |
get_gauge_count
¶
ChildGaugeFactory.get_gauge_count() -> uint256
Getter for the number of gauges deployed.
Returns: number of gauges deployed (uint256
).
get_gauge
¶
ChildGaugeFactory.get_gauge(_idx: uint256) -> address
Getter for the gauge address at a given index. First gauge has index 0
, second has index 1
, etc.
Returns: gauge (address
).
Input | Type | Description |
---|---|---|
_idx | uint256 | Index to check gauge for |
last_request
¶
ChildGaugeFactory.last_request(_gauge: address) -> uint256
Getter for the last request timestamp for a gauge. This variable updates whenever CRV emissions were minted from the according gauge.
Returns: last request timestamp (uint256
).
Input | Type | Description |
---|---|---|
_gauge | address | Gauge to check last request timestamp for |
Source code
# [last_request][has_counterpart][is_valid_gauge]
gauge_data: public(HashMap[address, uint256])
@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
is_mirrored
¶
ChildGaugeFactory.is_mirrored(_gauge: address) -> bool
Getter to check if a gauge is mirrored.
Returns: True
if the gauge is mirrored, False
otherwise (bool
).
Input | Type | Description |
---|---|---|
_gauge | address | Gauge to check mirrored status for |
Source code
# [last_request][has_counterpart][is_valid_gauge]
gauge_data: public(HashMap[address, uint256])
@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
set_mirrored
¶
ChildGaugeFactory.set_mirrored(_gauge: address, _is_mirrored: bool)
Guarded Method
This function is only callable by the owner
of the contract.
Function to set the mirrored status of a gauge.
Returns: True
if the gauge is mirrored, False
otherwise (bool
).
Emits: UpdateMirrored
event.
Input | Type | Description |
---|---|---|
_gauge | address | Gauge to set mirrored status for |
_is_mirrored | bool | New mirrored status |
Source code
event UpdateMirrored:
_gauge: indexed(address)
_mirrored: bool
# [last_request][has_counterpart][is_valid_gauge]
gauge_data: public(HashMap[address, uint256])
@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)
Child Gauge Implementation¶
get_implementation
¶
ChildGaugeFactory.get_implementation() -> address: view
Getter for the child gauge implementation address.
Returns: ChildGauge
implementation contract (address
).
set_implementation
¶
ChildGaugeFactory.set_implementation(_implementation: address)
Guarded Method
This function is only callable by the owner
of the contract.
Function to set the implementation address.
Emits: UpdateImplementation
event.
Input | Type | Description |
---|---|---|
_implementation | address | New implementation address |
Source code
event UpdateImplementation:
_old_implementation: address
_new_implementation: address
get_implementation: public(address)
@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
Root Factory and Implementation¶
The root_factory
and root_implementation
variables store the addresses of the root factory and implementation, respectively. They are only used as helper variables within this contract. Both variables can be updated by the owner
or manager
of the contract via the set_root
function.
root_factory
¶
ChildGaugeFactory.root_factory() -> address: view
Getter for the root factory address.
Returns: RootGaugeFactory
contract on Ethereum (address
).
Source code
event UpdateRoot:
_factory: address
_implementation: address
root_factory: public(address)
@external
def __init__(_call_proxy: address, _root_factory: address, _root_impl: address, _crv: address, _owner: address):
"""
@param _call_proxy Contract for
@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)
@param _owner Owner of factory (xgov)
"""
...
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)
...
root_implementation
¶
ChildGaugeFactory.root_implementation() -> address: view
Getter for the root implementation address.
Returns: RootGauge
implementation contract on Ethereum (address
).
Source code
root_implementation: public(address)
@external
def __init__(_call_proxy: address, _root_factory: address, _root_impl: address, _crv: address, _owner: address):
"""
@param _call_proxy Contract for
@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)
@param _owner Owner of factory (xgov)
"""
...
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)
...
set_root
¶
ChildGaugeFactory.set_root(_factory: address, _implementation: address)
Guarded Method
This function is only callable by the owner
or manager
of the contract.
Function to set the root_factory
and root_implementation
addresses.
Emits: UpdateRoot
event.
Input | Type | Description |
---|---|---|
_factory | address | New RootGaugeFactory address |
_implementation | address | New RootGauge implementation address |
Source code
root_factory: public(address)
root_implementation: public(address)
@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)
CRV Token and Voting Escrow¶
The crv
and voting_escrow
variables store the addresses of the CRV token and VotingEscrow
contract, respectively. crv
represents a bridged version of the CRV token, whereas voting_escrow
represents a L2 VotingEscrow Oracle
contract. This oracle is responsible for providing data from the VotingEscrow
contract on Ethereum to the child chain in order to make boosts on sidechains work. If there is no L2 VotingEscrow Oracle
set, the boosts on the child chain will not work.
crv
¶
ChildGaugeFactory.crv() -> address: view
Getter for the CRV token address of the child chain.
Returns: CRV token on the child chain (address
).
Source code
crv: public(ERC20)
@external
def __init__(_call_proxy: address, _root_factory: address, _root_impl: address, _crv: address, _owner: address):
"""
@param _call_proxy Contract for
@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)
@param _owner Owner of factory (xgov)
"""
self.crv = ERC20(_crv)
...
set_crv
¶
ChildGaugeFactory.set_crv(_crv: address)
Guarded Method
This function is only callable by the owner
of the contract.
Function to set the CRV token address.
Emits: UpdateCRV
event.
Input | Type | Description |
---|---|---|
_crv | address | New CRV token address |
Source code
crv: public(ERC20)
@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)
self.crv = _crv
voting_escrow
¶
ChildGaugeFactory.voting_escrow() -> address: view
Getter for the VotingEscrow
contract.
Returns: VotingEscrow
contract (address
).
set_voting_escrow
¶
ChildGaugeFactory.set_voting_escrow(_voting_escrow: address)
Guarded Method
This function is only callable by the owner
of the contract.
Function to set the VotingEscrow
contract.
Emits: UpdateVotingEscrow
event.
Input | Type | Description |
---|---|---|
_voting_escrow | address | New voting escrow address |
Source code
event UpdateVotingEscrow:
_old_voting_escrow: address
_new_voting_escrow: address
voting_escrow: public(address)
@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
Manager¶
manager
¶
ChildGaugeFactory.manager() -> address: view
Getter for the manager address. This variable is set at initialization and can be changed via the set_manager
function.
Returns: manager (address
).
Source code
event UpdateManager:
_manager: address
manager: public(address)
@external
def __init__(_call_proxy: address, _root_factory: address, _root_impl: address, _crv: address, _owner: address):
"""
@param _call_proxy Contract for
@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)
@param _owner Owner of factory (xgov)
"""
...
self.manager = msg.sender
log UpdateManager(msg.sender)
set_manager
¶
ChildGaugeFactory.set_manager(_new_manager: address)
Guarded Method
This function is only callable by the owner
or manager
of the contract.
Function to change the manager address.
Emits: UpdateManager
event.
Input | Type | Description |
---|---|---|
_new_manager | address | New manager address |
Source code
Call Proxy¶
call_proxy
¶
ChildGaugeFactory.call_proxy() -> address: view
Getter for the call proxy contract. This contract acts as an intermediary to facilitate cross-chain calls.
Returns: call proxy address (address
).
Source code
event UpdateCallProxy:
_old_call_proxy: address
_new_call_proxy: address
call_proxy: public(address)
@external
def __init__(_call_proxy: address, _root_factory: address, _root_impl: address, _crv: address, _owner: address):
"""
@param _call_proxy Contract for
@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)
@param _owner Owner of factory (xgov)
"""
...
self.call_proxy = _call_proxy
log UpdateCallProxy(empty(address), _call_proxy)
...
set_call_proxy
¶
ChildGaugeFactory.set_call_proxy(_new_call_proxy: address)
Guarded Method
This function is only callable by the owner
of the contract.
Function to set or update the call proxy address.
Emits: UpdateCallProxy
event.
Input | Type | Description |
---|---|---|
_new_call_proxy | address | New call proxy address |
Source code
event UpdateCallProxy:
_old_call_proxy: address
_new_call_proxy: address
call_proxy: public(address)
@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
Ownership¶
For contract ownership details, see here.