Skip to content

Commit

Permalink
veBALDeploymentCoordinator gauge deployment (#1187)
Browse files Browse the repository at this point in the history
* fix: fill out MockGaugeController to satisfy IGaugeController

* feat: deploy Ethereum gauges and add them to GaugeController

* feat: add a check to prevent attempting to create 2 gauges for a single pool

* feat: add deployments for single recipient gauges

* docs: update comment on deployment of single recipient gauges

* style: linting

* style: linting fixes

* Apply suggestions from code review

Co-authored-by: Nicolás Venturo <nicolas.venturo@gmail.com>

Co-authored-by: Nicolás Venturo <nicolas.venturo@gmail.com>
  • Loading branch information
TomAFrench and nventuro authored Mar 22, 2022
1 parent 4e4d3b9 commit bf43cc9
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ interface ICurrentAuthorizer is IAuthorizer {
function revokeRole(bytes32 role, address account) external;
}

interface IEthereumLiquidityGaugeFactory is ILiquidityGaugeFactory {
function deploy(address pool) external returns (ILiquidityGauge);
}

interface ISingleRecipientLiquidityGaugeFactory is ILiquidityGaugeFactory {
function deploy(address recipient) external returns (ILiquidityGauge);
}

// https://vote.balancer.fi/#/proposal/0x9fe19c491cf90ed2e3ed9c15761c43d39fd1fb732a940aba8058ff69787ee90a
// solhint-disable-next-line contract-name-camelcase
contract veBALDeploymentCoordinator is ReentrancyGuard {
Expand All @@ -48,6 +56,12 @@ contract veBALDeploymentCoordinator is ReentrancyGuard {
IBalancerToken private immutable _balancerToken;
IBalancerMinter private immutable _balancerMinter;
IGaugeController private immutable _gaugeController;
IGaugeAdder private immutable _gaugeAdder;
IEthereumLiquidityGaugeFactory private immutable _ethereumGaugeFactory;
ISingleRecipientLiquidityGaugeFactory private immutable _singleRecipientGaugeFactory;

address[] private _initialPools;
address[4] private _recipients;

enum DeploymentStage { PENDING, FIRST_STAGE_DONE, SECOND_STAGE_DONE }

Expand All @@ -67,9 +81,23 @@ contract veBALDeploymentCoordinator is ReentrancyGuard {
constructor(
IBalancerMinter balancerMinter,
IAuthorizerAdaptor authorizerAdaptor,
IGaugeAdder gaugeAdder,
IEthereumLiquidityGaugeFactory ethereumGaugeFactory,
ISingleRecipientLiquidityGaugeFactory singleRecipientGaugeFactory,
address[] memory initialPools,
address[4] memory recipients,
uint256 activationScheduledTime,
uint256 secondStageDelay
) {
// Only a single gauge may exist for a given pool so repeated pool addresses
// will cause the activation to fail
uint256 poolsLength = initialPools.length;
for (uint256 i = 1; i < poolsLength; i++) {
_require(initialPools[i - 1] < initialPools[i], Errors.UNSORTED_ARRAY);
}
// We do not apply a similar protection for `recipients` as they must be sorted
// to match the desired gauge types (LMCommittee, veBAL, Polygon, Arbitrum)

_currentDeploymentStage = DeploymentStage.PENDING;

IBalancerTokenAdmin balancerTokenAdmin = balancerMinter.getBalancerTokenAdmin();
Expand All @@ -80,6 +108,12 @@ contract veBALDeploymentCoordinator is ReentrancyGuard {
_balancerToken = balancerTokenAdmin.getBalancerToken();
_balancerMinter = balancerMinter;
_gaugeController = IGaugeController(balancerMinter.getGaugeController());
_gaugeAdder = gaugeAdder;
_ethereumGaugeFactory = ethereumGaugeFactory;
_singleRecipientGaugeFactory = singleRecipientGaugeFactory;

_initialPools = initialPools;
_recipients = recipients;

_activationScheduledTime = activationScheduledTime;
_secondStageDelay = secondStageDelay;
Expand Down Expand Up @@ -187,19 +221,67 @@ contract veBALDeploymentCoordinator is ReentrancyGuard {
authorizer.revokeRole(authorizerAdaptor.getActionId(IGaugeController.add_type.selector), address(this));
}

// Step 4: create gauges for the single-gauge gauge types
// Step 4: setup the GaugeAdder contract to be in charge of adding gauges to the Gauge Controller.
//
// The GaugeAdder contract performs checks on addresses being added to the Gauge Controller to ensure
// that they have been deployed by a factory contract which has been associated with the gauge type
// to which the proposed gauge is being added. This is intended to prevent common mistakes when adding gauges.

authorizer.grantRole(authorizerAdaptor.getActionId(IGaugeController.add_gauge.selector), address(_gaugeAdder));

// Step 5: create gauges for a preselected list of pools on Ethereum.

// Allowlist the provided LiquidityGaugeFactory on the GaugeAdder
// so its gauges may be added to the "Ethereum" gauge type.
{
authorizer.grantRole(_gaugeAdder.getActionId(IGaugeAdder.addGaugeFactory.selector), address(this));

_gaugeAdder.addGaugeFactory(_ethereumGaugeFactory, IGaugeAdder.GaugeType.Ethereum);

authorizer.revokeRole(_gaugeAdder.getActionId(IGaugeAdder.addGaugeFactory.selector), address(this));
}

// Deploy initial gauges and add them to the Gauge Controller
{
authorizer.grantRole(_gaugeAdder.getActionId(IGaugeAdder.addEthereumGauge.selector), address(this));

uint256 poolsLength = _initialPools.length;
for (uint256 i = 0; i < poolsLength; i++) {
ILiquidityGauge gauge = _ethereumGaugeFactory.deploy(_initialPools[i]);
_gaugeAdder.addEthereumGauge(address(gauge));
}

authorizer.revokeRole(_gaugeAdder.getActionId(IGaugeAdder.addEthereumGauge.selector), address(this));
}

// Step 6: create gauges for the single-recipient gauge types
//
// The LM committee and veBAL gauge types will have a single gauge of the single-recipient gauge kind
// The LM committee gauge will be remain as a SingleRecipientGauge permanently,
// however the gauges for veBAL, Polygon and Arbitrum types are temporary pending an automated solution.
// These three gauges will in time be retired (killed) and replaced with new gauge implementations
// which automate the distribution of BAL to BPT stakers on other networks and veBAL holders.
{
authorizer.grantRole(authorizerAdaptor.getActionId(IGaugeController.add_gauge.selector), address(this));

// Permanent
ILiquidityGauge LMCommitteeGauge = _singleRecipientGaugeFactory.deploy(_recipients[0]);
_addGauge(LMCommitteeGauge, IGaugeAdder.GaugeType.LiquidityMiningCommittee);

// grant self power to add gauges
// deploy lm, vebal, merkle gauges. add gauges to gauge controller
// Temporary
ILiquidityGauge tempVeBALGauge = _singleRecipientGaugeFactory.deploy(_recipients[1]);
_addGauge(tempVeBALGauge, IGaugeAdder.GaugeType.veBAL);

// grant gauge adder authority over controller
// grant self power over guage adder
// add l1 and l2 factories to gauge adder
// deploy l1 and l2 gauges, add to controller via gauge adder
// Temporary
ILiquidityGauge tempPolygonGauge = _singleRecipientGaugeFactory.deploy(_recipients[2]);
_addGauge(tempPolygonGauge, IGaugeAdder.GaugeType.Polygon);

// Temporary
ILiquidityGauge tempArbitrumGauge = _singleRecipientGaugeFactory.deploy(_recipients[3]);
_addGauge(tempArbitrumGauge, IGaugeAdder.GaugeType.Arbitrum);

authorizer.revokeRole(authorizerAdaptor.getActionId(IGaugeController.add_gauge.selector), address(this));
}

// add tokens to l1 and l2 gauges
// grant batch relayer permissions

firstStageActivationTime = block.timestamp;
Expand Down Expand Up @@ -242,6 +324,13 @@ contract veBALDeploymentCoordinator is ReentrancyGuard {
_currentDeploymentStage = DeploymentStage.SECOND_STAGE_DONE;
}

function _addGauge(ILiquidityGauge gauge, IGaugeAdder.GaugeType gaugeType) private {
getAuthorizerAdaptor().performAction(
address(_gaugeController),
abi.encodeWithSelector(IGaugeController.add_gauge.selector, gauge, gaugeType)
);
}

function _addGaugeType(string memory name) private {
getAuthorizerAdaptor().performAction(
address(_gaugeController),
Expand Down
9 changes: 4 additions & 5 deletions pkg/liquidity-mining/contracts/test/MockGaugeController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@ contract MockGaugeController is IGaugeController {
mapping(address => int128) private _gaugeType;

IAuthorizerAdaptor public override admin;
// solhint-disable-next-line var-name-mixedcase
IVotingEscrow public override voting_escrow;

// solhint-disable-next-line func-param-name-mixedcase, var-name-mixedcase
event NewGauge(address addr, int128 gauge_type, uint256 weight);

constructor(IAuthorizerAdaptor authorizerAdaptor) {
constructor(IVotingEscrow votingEscrow, IAuthorizerAdaptor authorizerAdaptor) {
voting_escrow = votingEscrow;
admin = authorizerAdaptor;
}

Expand All @@ -54,10 +57,6 @@ contract MockGaugeController is IGaugeController {
_numGaugeTypes += 1;
}

function voting_escrow() external view override returns (IVotingEscrow) {
// solhint-disable-previous-line no-empty-blocks
}

function checkpoint_gauge(address) external override {
// solhint-disable-previous-line no-empty-blocks
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/liquidity-mining/test/GaugeAdder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ describe('GaugeAdder', () => {
authorizer = vault.authorizer;

adaptor = await deploy('AuthorizerAdaptor', { args: [vault.address] });
gaugeController = await deploy('MockGaugeController', { args: [adaptor.address] });
gaugeController = await deploy('MockGaugeController', { args: [ZERO_ADDRESS, adaptor.address] });

gaugeFactory = await deploy('MockLiquidityGaugeFactory');
gaugeAdder = await deploy('GaugeAdder', { args: [gaugeController.address] });
Expand Down

0 comments on commit bf43cc9

Please sign in to comment.