Skip to content

Commit

Permalink
feat: gauge notify admin (#127)
Browse files Browse the repository at this point in the history
  • Loading branch information
simplyoptimistic authored Jan 20, 2024
1 parent 5769a39 commit 05bd032
Show file tree
Hide file tree
Showing 12 changed files with 71 additions and 6 deletions.
7 changes: 5 additions & 2 deletions contracts/gauge/CLGauge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/ERC721Holder.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import {ICLGauge} from "contracts/gauge/interfaces/ICLGauge.sol";
import {ICLGaugeFactory} from "contracts/gauge/interfaces/ICLGaugeFactory.sol";
import {IVoter} from "contracts/core/interfaces/IVoter.sol";
import {IVotingEscrow} from "contracts/core/interfaces/IVotingEscrow.sol";
import {ICLPool} from "contracts/core/interfaces/ICLPool.sol";
import {INonfungiblePositionManager} from "contracts/periphery/interfaces/INonfungiblePositionManager.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
Expand All @@ -29,6 +29,8 @@ contract CLGauge is ICLGauge, ERC721Holder, ReentrancyGuard {
IVoter public override voter;
/// @inheritdoc ICLGauge
ICLPool public override pool;
/// @inheritdoc ICLGauge
ICLGaugeFactory public override gaugeFactory;

/// @inheritdoc ICLGauge
address public override feesVotingReward;
Expand Down Expand Up @@ -78,6 +80,7 @@ contract CLGauge is ICLGauge, ERC721Holder, ReentrancyGuard {
bool _isPool
) external override {
require(address(pool) == address(0), "AI");
gaugeFactory = ICLGaugeFactory(msg.sender);
pool = ICLPool(_pool);
feesVotingReward = _feesVotingReward;
rewardToken = _rewardToken;
Expand Down Expand Up @@ -335,7 +338,7 @@ contract CLGauge is ICLGauge, ERC721Holder, ReentrancyGuard {
/// @inheritdoc ICLGauge
function notifyRewardWithoutClaim(uint256 _amount) external override nonReentrant {
address sender = msg.sender;
require(sender == IVotingEscrow(voter.ve()).team(), "NT");
require(sender == gaugeFactory.notifyAdmin(), "NA");
require(_amount != 0, "ZR");
_notifyRewardAmount(sender, _amount);
}
Expand Down
11 changes: 11 additions & 0 deletions contracts/gauge/CLGaugeFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,25 @@ contract CLGaugeFactory is ICLGaugeFactory {
address public immutable override implementation;
/// @inheritdoc ICLGaugeFactory
address public override nft;
/// @inheritdoc ICLGaugeFactory
address public override notifyAdmin;
address private owner;

constructor(address _voter, address _implementation) {
voter = _voter;
owner = msg.sender;
notifyAdmin = msg.sender;
implementation = _implementation;
}

/// @inheritdoc ICLGaugeFactory
function setNotifyAdmin(address _admin) external override {
require(notifyAdmin == msg.sender, "NA");
require(_admin != address(0), "ZA");
notifyAdmin = _admin;
emit SetNotifyAdmin(_admin);
}

/// @inheritdoc ICLGaugeFactory
function setNonfungiblePositionManager(address _nft) external override {
require(nft == address(0), "AI");
Expand Down
4 changes: 4 additions & 0 deletions contracts/gauge/interfaces/ICLGauge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity =0.7.6;
import {INonfungiblePositionManager} from "contracts/periphery/interfaces/INonfungiblePositionManager.sol";
import {IVoter} from "contracts/core/interfaces/IVoter.sol";
import {ICLPool} from "contracts/core/interfaces/ICLPool.sol";
import {ICLGaugeFactory} from "contracts/gauge/interfaces/ICLGaugeFactory.sol";

interface ICLGauge {
event NotifyReward(address indexed from, uint256 amount);
Expand All @@ -21,6 +22,9 @@ interface ICLGauge {
/// @notice Address of the CL pool linked to the gauge
function pool() external view returns (ICLPool);

/// @notice Address of the factory that created this gauge
function gaugeFactory() external view returns (ICLGaugeFactory);

/// @notice Address of the FeesVotingReward contract linked to the gauge
function feesVotingReward() external view returns (address);

Expand Down
9 changes: 9 additions & 0 deletions contracts/gauge/interfaces/ICLGaugeFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
pragma solidity =0.7.6;

interface ICLGaugeFactory {
event SetNotifyAdmin(address indexed notifyAdmin);

/// @notice Address of the voter contract
function voter() external view returns (address);

Expand All @@ -11,11 +13,18 @@ interface ICLGaugeFactory {
/// @notice Address of the NonfungiblePositionManager used to create nfts that gauges will accept
function nft() external view returns (address);

/// @notice Administrator that can call `notifyRewardWithoutClaim` on gauges
function notifyAdmin() external view returns (address);

/// @notice Set Nonfungible Position Manager
/// @dev Callable once only on initialize
/// @param _nft The nonfungible position manager that will manage positions for this Factory
function setNonfungiblePositionManager(address _nft) external;

/// @notice Set notifyAdmin value on gauge factory
/// @param _admin New administrator that will be able to call `notifyRewardWithoutClaim` on gauges.
function setNotifyAdmin(address _admin) external;

/// @notice Called by the voter contract via factory.createPool
/// @param _forwarder The address of the forwarder contract
/// @param _pool The address of the pool
Expand Down
3 changes: 3 additions & 0 deletions script/DeployCL.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ contract DeployCL is Script {
address public factoryRegistry;
address public poolFactoryOwner;
address public feeManager;
address public notifyAdmin;
address public factoryV2;

// deployed contracts
Expand All @@ -57,6 +58,7 @@ contract DeployCL is Script {
factoryRegistry = abi.decode(vm.parseJson(jsonConstants, ".FactoryRegistry"), (address));
poolFactoryOwner = abi.decode(vm.parseJson(jsonConstants, ".poolFactoryOwner"), (address));
feeManager = abi.decode(vm.parseJson(jsonConstants, ".feeManager"), (address));
notifyAdmin = abi.decode(vm.parseJson(jsonConstants, ".notifyAdmin"), (address));
factoryV2 = abi.decode(vm.parseJson(jsonConstants, ".factoryV2"), (address));

require(address(voter) != address(0)); // sanity check for constants file fillled out correctly
Expand Down Expand Up @@ -87,6 +89,7 @@ contract DeployCL is Script {

// set nft manager in the factories
gaugeFactory.setNonfungiblePositionManager(address(nft));
gaugeFactory.setNotifyAdmin(notifyAdmin);
poolFactory.setNonfungiblePositionManager(address(nft));

// deploy fee modules
Expand Down
1 change: 1 addition & 0 deletions script/constants/Optimism.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"poolFactoryOwner": "0x0000000000000000000000000000000000000001",
"feeManager": "0x0000000000000000000000000000000000000001",
"notifyAdmin": "0x0000000000000000000000000000000000000001",
"FactoryRegistry": "0x062C88B4ba954955746eDA6f475C26eeaC04614B",
"Voter": "0x8b50264507f84Edc75087aeAf63a016E89b7a441",
"WETH": "0x4200000000000000000000000000000000000006",
Expand Down
1 change: 1 addition & 0 deletions test/BaseFixture.sol
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ abstract contract BaseFixture is Test, Constants, Events, PoolUtils {

// set nftmanager in the factories
gaugeFactory.setNonfungiblePositionManager(address(nft));
gaugeFactory.setNotifyAdmin(users.owner);
poolFactory.setNonfungiblePositionManager(address(nft));
vm.stopPrank();

Expand Down
4 changes: 2 additions & 2 deletions test/fork/notifyRewardAmountWithoutClaim.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ contract NotifyRewardAmountWithoutClaimForkTest is BaseForkFixture {

skipToNextEpoch(0);

vm.startPrank(escrow.team());
deal(address(rewardToken), escrow.team(), 604_800);
vm.startPrank(users.owner);
deal(address(rewardToken), users.owner, 604_800);
rewardToken.approve(address(gauge), 604_800);
gauge.notifyRewardWithoutClaim(604_800); // requires minimum value of 604800

Expand Down
4 changes: 2 additions & 2 deletions test/unit/concrete/CLGauge/notifyRewardWithoutClaim.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ contract NotifyRewardWithoutClaimTest is CLGaugeTest {
skipToNextEpoch(0);
}

function test_RevertIf_NotTeam() public {
function test_RevertIf_NotNotifyAdmin() public {
vm.startPrank(users.charlie);
vm.expectRevert(abi.encodePacked("NT"));
vm.expectRevert(abi.encodePacked("NA"));
gauge.notifyRewardWithoutClaim(TOKEN_1);
}

Expand Down
1 change: 1 addition & 0 deletions test/unit/concrete/CLGaugeFactory/createGauge.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ contract CreateGaugeTest is CLGaugeFactoryTest {
assertEq(address(gauge.pool()), pool);
assertEq(gauge.feesVotingReward(), address(feesVotingReward));
assertEq(gauge.rewardToken(), address(rewardToken));
assertEq(address(gauge.gaugeFactory()), address(gaugeFactory));
assertEq(gauge.isPool(), true);
}
}
29 changes: 29 additions & 0 deletions test/unit/concrete/CLGaugeFactory/setNotifyAdmin.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
pragma solidity ^0.7.6;
pragma abicoder v2;

import "./CLGaugeFactory.t.sol";

contract SetNotifyAdminTest is CLGaugeFactoryTest {
event SetNotifyAdmin(address indexed notifyAdmin);

function test_RevertIf_NotNotifyAdmin() public {
vm.startPrank({msgSender: users.alice});
vm.expectRevert(abi.encodePacked("NA"));
gaugeFactory.setNotifyAdmin({_admin: users.alice});
}

function test_RevertIf_ZeroAddress() public {
vm.startPrank({msgSender: users.owner});
vm.expectRevert(abi.encodePacked("ZA"));
gaugeFactory.setNotifyAdmin({_admin: address(0)});
}

function test_SetNotifyAdmin() public {
vm.prank({msgSender: users.owner});
vm.expectEmit(true, false, false, false, address(gaugeFactory));
emit SetNotifyAdmin(users.alice);
gaugeFactory.setNotifyAdmin({_admin: users.alice});

assertEq(gaugeFactory.notifyAdmin(), address(users.alice));
}
}
3 changes: 3 additions & 0 deletions test/unit/concrete/script/DeployCL.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ contract DeployCLTest is Test {
address public factoryRegistry;
address public poolFactoryOwner;
address public feeManager;
address public notifyAdmin;

// deployed contracts
CLPool public poolImplementation;
Expand All @@ -56,6 +57,7 @@ contract DeployCLTest is Test {
factoryRegistry = abi.decode(vm.parseJson(jsonConstants, ".FactoryRegistry"), (address));
poolFactoryOwner = abi.decode(vm.parseJson(jsonConstants, ".poolFactoryOwner"), (address));
feeManager = abi.decode(vm.parseJson(jsonConstants, ".feeManager"), (address));
notifyAdmin = abi.decode(vm.parseJson(jsonConstants, ".notifyAdmin"), (address));

deal(address(deployerAddress), 10 ether);
}
Expand Down Expand Up @@ -105,6 +107,7 @@ contract DeployCLTest is Test {
assertEq(gaugeFactory.voter(), voter);
assertEq(gaugeFactory.implementation(), address(gaugeImplementation));
assertEq(gaugeFactory.nft(), address(nft));
assertEq(gaugeFactory.notifyAdmin(), notifyAdmin);

assertTrue(address(swapFeeModule) != address(0));
assertEq(swapFeeModule.MAX_FEE(), 30_000); // 3%, using pip denomination
Expand Down

0 comments on commit 05bd032

Please sign in to comment.