Contract Name:
LauncherPlugin
Contract Source Code:
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.24;
import {ILauncherPlugin} from "./interfaces/ILauncherPlugin.sol";
import {IVoter} from "./interfaces/IVoter.sol";
/// @author ShadowDEX on Sonic
/// @title LauncherPlugins contract for modular plug-n-play with Sonic memes
/** @dev There are two trusted roles in the LauncherPlugin system
* Authority: Whitelisted external launchers, e.g. DegenExpress
* Operator: Shadow operational multisig, or other timelocked/secure system
* AccessHub: central authority management contract
* These roles are to be managed securely, and with diligence to prevent abuse
* However, the system already has checks in place to mitigate any possible abuse situations ahead of time
*/
contract LauncherPlugin is ILauncherPlugin {
/// @inheritdoc ILauncherPlugin
address public accessHub;
/// @inheritdoc ILauncherPlugin
address public operator;
/// @notice the voter contract
IVoter public immutable voter;
/// @inheritdoc ILauncherPlugin
mapping(address pool => bool isEnabled) public launcherPluginEnabled;
/// @inheritdoc ILauncherPlugin
mapping(address pool => LauncherConfigs) public poolConfigs;
/// @inheritdoc ILauncherPlugin
mapping(address pool => address feeDist) public feeDistToPool;
/// @inheritdoc ILauncherPlugin
mapping(address who => bool authority) public authorityMap;
/// @inheritdoc ILauncherPlugin
mapping(address authority => string name) public nameOfAuthority;
/// @inheritdoc ILauncherPlugin
uint256 public constant DENOM = 10_000;
modifier onlyAuthority() {
/// @dev authority check of either the operator of authority in the mapping
require(
authorityMap[msg.sender] || msg.sender == accessHub,
NOT_AUTHORITY()
);
_;
}
modifier onlyOperator() {
/// @dev redundant `operator` address put here as a safeguard for input errors on transferring roles
require(
msg.sender == accessHub || msg.sender == operator,
NOT_OPERATOR()
);
_;
}
constructor(address _voter, address _accessHub, address _operator) {
/// @dev initialize the voter
voter = IVoter(_voter);
/// @dev operator and team initially are the same
accessHub = _accessHub;
operator = _operator;
}
/// @inheritdoc ILauncherPlugin
/// @dev should be called by another contract with proper batching of function calls
function setConfigs(
address _pool,
uint256 _take,
address _recipient
) external onlyAuthority {
/// @dev ensure launcherPlugins are enabled
require(launcherPluginEnabled[_pool], NOT_ENABLED());
/// @dev ensure the fee is <= 100%
require(_take <= DENOM, INVALID_TAKE());
/// @dev store launcher configs in pool to struct mapping
LauncherConfigs memory lc = LauncherConfigs(_take, _recipient);
/// @dev store the pool configs in the mapping
poolConfigs[_pool] = lc;
/// @dev emit an event for configuration
emit Configured(_pool, _take, _recipient);
}
/// @inheritdoc ILauncherPlugin
/// @dev should be called by another contract with proper batching of function calls
function enablePool(address _pool) external onlyAuthority {
/// @dev require that the plugin is enabled
require(!launcherPluginEnabled[_pool], ENABLED());
/// @dev fetch the feeDistributor address
address _feeDist = voter.feeDistributorForGauge(
voter.gaugeForPool(_pool)
);
/// @dev set the feeDist for the pool
feeDistToPool[_feeDist] = _pool;
launcherPluginEnabled[_pool] = true;
/// @dev emit with the name of the authority
emit EnabledPool(_pool, nameOfAuthority[msg.sender]);
}
/// @inheritdoc ILauncherPlugin
function migratePool(address _oldPool, address _newPool) external {
require(
msg.sender == address(voter) || msg.sender == operator,
IVoter.NOT_AUTHORIZED(msg.sender)
);
require(launcherPluginEnabled[_oldPool], NOT_ENABLED());
launcherPluginEnabled[_newPool] = true;
/// @dev fetch the feedists for each pool
(address _feeDist, address _newFeeDist) = (
voter.feeDistributorForGauge(voter.gaugeForPool(_oldPool)),
voter.feeDistributorForGauge(voter.gaugeForPool(_newPool))
);
/// @dev set the new pool's feedist
feeDistToPool[_newFeeDist] = _newPool;
/// @dev copy over the values
poolConfigs[_newPool] = poolConfigs[_oldPool];
/// @dev delete old values
delete poolConfigs[_oldPool];
/// @dev set to disabled
launcherPluginEnabled[_oldPool] = false;
/// @dev set the old fee dist to the new one as a safety measure
feeDistToPool[_feeDist] = feeDistToPool[_newFeeDist];
emit MigratedPool(_oldPool, _newPool);
}
/// @inheritdoc ILauncherPlugin
function disablePool(address _pool) external onlyOperator {
/// @dev require the plugin is already enabled
require(launcherPluginEnabled[_pool], NOT_ENABLED());
/// @dev wipe struct
delete poolConfigs[_pool];
/// @dev wipe the mapping for feeDist to the pool, incase the feeDist is overwritten
delete feeDistToPool[
voter.feeDistributorForGauge(voter.gaugeForPool(_pool))
];
/// @dev set to disabled
launcherPluginEnabled[_pool] = false;
/// @dev emit an event
emit DisabledPool(_pool);
}
/// @inheritdoc ILauncherPlugin
function setOperator(address _newOperator) external onlyOperator {
/// @dev ensure the new operator is not already the operator
require(operator != _newOperator, ALREADY_OPERATOR());
/// @dev store the oldOperator to use in the event, for info purposes
address oldOperator = operator;
/// @dev set operator as the new operator
operator = _newOperator;
/// @dev emit operator change event
emit NewOperator(oldOperator, operator);
}
/// @inheritdoc ILauncherPlugin
function grantAuthority(
address _newAuthority,
string calldata _name
) external onlyOperator {
/// @dev ensure the proposed _newAuthority is not already one
require(!authorityMap[_newAuthority], ALREADY_AUTHORITY());
/// @dev set the mapping to true
authorityMap[_newAuthority] = true;
/// @dev emit the new authority event
emit NewAuthority(_newAuthority);
/// @dev label the authority
_labelAuthority(_newAuthority, _name);
}
/// @inheritdoc ILauncherPlugin
function revokeAuthority(address _oldAuthority) external onlyOperator {
/// @dev ensure _oldAuthority is already an authority
require(authorityMap[_oldAuthority], NOT_AUTHORITY());
/// @dev set the mapping to false
authorityMap[_oldAuthority] = false;
/// @dev emit the remove authority event
emit RemovedAuthority(_oldAuthority);
}
/// @inheritdoc ILauncherPlugin
function label(
address _authority,
string calldata _label
) external onlyOperator {
_labelAuthority(_authority, _label);
}
/// @inheritdoc ILauncherPlugin
function values(
address _feeDist
) external view returns (uint256 _take, address _recipient) {
/// @dev fetch the poolConfigs from the mapping
LauncherConfigs memory _tmp = poolConfigs[feeDistToPool[_feeDist]];
/// @dev return the existing values
return (_tmp.launcherTake, _tmp.takeRecipient);
}
/// @dev internal function called on creation and manually
function _labelAuthority(
address _authority,
string calldata _label
) internal {
/// @dev ensure they are an authority
require(authorityMap[_authority]);
/// @dev label the authority
nameOfAuthority[_authority] = _label;
/// @dev emit on label
emit Labeled(_authority, _label);
}
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.24;
interface ILauncherPlugin {
error NOT_AUTHORITY();
error ALREADY_AUTHORITY();
error NOT_OPERATOR();
error ALREADY_OPERATOR();
error NOT_ENABLED();
error ENABLED();
error INVALID_TAKE();
/// @dev struct that holds the configurations of each specific pool
struct LauncherConfigs {
uint256 launcherTake;
address takeRecipient;
}
event NewOperator(address indexed _old, address indexed _new);
event NewAuthority(address indexed _newAuthority);
event RemovedAuthority(address indexed _previousAuthority);
event EnabledPool(address indexed pool, string indexed _name);
event DisabledPool(address indexed pool);
event MigratedPool(address indexed oldPool, address indexed newPool);
event Configured(
address indexed pool,
uint256 take,
address indexed recipient
);
event Labeled(address indexed authority, string indexed label);
/// @notice address of the accessHub
function accessHub() external view returns (address _accessHub);
/// @notice protocol operator address
function operator() external view returns (address _operator);
/// @notice the denominator constant
function DENOM() external view returns (uint256 _denominator);
/// @notice whether configs are enabled for a pool
/// @param _pool address of the pool
/// @return bool
function launcherPluginEnabled(address _pool) external view returns (bool);
/// @notice maps whether an address is an authority or not
/// @param _who the address to check
/// @return _is true or false
function authorityMap(address _who) external view returns (bool _is);
/// @notice allows migrating the parameters from one pool to the other
/// @param _oldPool the current address of the pair
/// @param _newPool the new pool's address
function migratePool(address _oldPool, address _newPool) external;
/// @notice fetch the launcher configs if any
/// @param _pool address of the pool
/// @return LauncherConfigs the configs
function poolConfigs(
address _pool
) external view returns (uint256, address);
/// @notice view functionality to see who is an authority
function nameOfAuthority(address) external view returns (string memory);
/// @notice returns the pool address for a feeDist
/// @param _feeDist address of the feeDist
/// @return _pool the pool address from the mapping
function feeDistToPool(
address _feeDist
) external view returns (address _pool);
/// @notice set launcher configurations for a pool
/// @param _pool address of the pool
/// @param _take the fee that goes to the designated recipient
/// @param _recipient the address that receives the fees
function setConfigs(
address _pool,
uint256 _take,
address _recipient
) external;
/// @notice enables the pool for LauncherConfigs
/// @param _pool address of the pool
function enablePool(address _pool) external;
/// @notice disables the pool for LauncherConfigs
/// @dev clears mappings
/// @param _pool address of the pool
function disablePool(address _pool) external;
/// @notice sets a new operator address
/// @param _newOperator new operator address
function setOperator(address _newOperator) external;
/// @notice gives authority to a new contract/address
/// @param _newAuthority the suggested new authority
function grantAuthority(address _newAuthority, string calldata) external;
/// @notice removes authority from a contract/address
/// @param _oldAuthority the to-be-removed authority
function revokeAuthority(address _oldAuthority) external;
/// @notice labels an authority
function label(address, string calldata) external;
/// @notice returns the values for the launcherConfig of the specific feeDist
/// @param _feeDist the address of the feeDist
/// @return _launcherTake fee amount taken
/// @return _recipient address that receives the fees
function values(
address _feeDist
) external view returns (uint256 _launcherTake, address _recipient);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26;
pragma abicoder v2;
interface IVoter {
error ACTIVE_GAUGE(address gauge);
error GAUGE_INACTIVE(address gauge);
error ALREADY_WHITELISTED();
error NOT_AUTHORIZED(address caller);
error NOT_WHITELISTED();
error NOT_POOL();
error FORBIDDEN();
error NOT_INIT();
error LENGTH_MISMATCH();
error NO_GAUGE();
error ALREADY_DISTRIBUTED(address gauge, uint256 period);
error ZERO_VOTE(address pool);
error RATIO_TOO_HIGH();
error NOT_GT_ZERO();
error VOTE_UNSUCCESSFUL();
error UNAUTHORIZED();
event GaugeCreated(
address indexed gauge,
address creator,
address feeDistributor,
address indexed pool
);
event GaugeKilled(address indexed gauge);
event GaugeRevived(address indexed gauge);
event Voted(address indexed owner, uint256 weight, address indexed pool);
event Abstained(address indexed owner, uint256 weight);
event Deposit(
address indexed lp,
address indexed gauge,
address indexed owner,
uint256 amount
);
event Withdraw(
address indexed lp,
address indexed gauge,
address indexed owner,
uint256 amount
);
event NotifyReward(
address indexed sender,
address indexed reward,
uint256 amount
);
event DistributeReward(
address indexed sender,
address indexed gauge,
uint256 amount
);
event EmissionsRatio(
address indexed caller,
uint256 oldRatio,
uint256 newRatio
);
event NewGovernor(address indexed sender, address indexed governor);
event Whitelisted(address indexed whitelister, address indexed token);
event WhitelistRevoked(
address indexed forbidder,
address indexed token,
bool status
);
event CustomGaugeCreated(
address indexed gauge,
address creator,
address indexed token
);
event MainTickSpacingChanged(
address indexed token0,
address indexed token1,
int24 indexed newMainTickSpacing
);
/// @notice returns the address of the current governor
/// @return _governor address of the governor
function governor() external view returns (address _governor);
/// @notice the address of the vote module
/// @return _voteModule the vote module contract address
function voteModule() external view returns (address _voteModule);
/// @notice the address of the shadow launcher plugin to enable third party launchers
/// @return _launcherPlugin the address of the plugin
function launcherPlugin() external view returns (address _launcherPlugin);
/// @notice distributes emissions from the minter to the voter
/// @param amount the amount of tokens to notify
function notifyRewardAmount(uint256 amount) external;
/// @notice distributes the emissions for a specific gauge
/// @param _gauge the gauge address
function distribute(address _gauge) external;
/// @notice returns the address of the gauge factory
/// @param _gaugeFactory gauge factory address
function gaugeFactory() external view returns (address _gaugeFactory);
/// @notice returns the address of the feeDistributor factory
/// @return _feeDistributorFactory feeDist factory address
function feeDistributorFactory()
external
view
returns (address _feeDistributorFactory);
/// @notice returns the address of the minter contract
/// @return _minter address of the minter
function minter() external view returns (address _minter);
/// @notice check if the gauge is active for governance use
/// @param _gauge address of the gauge
/// @return _trueOrFalse if the gauge is alive
function isAlive(address _gauge) external view returns (bool _trueOrFalse);
/// @notice allows the token to be paired with other whitelisted assets to participate in governance
/// @param _token the address of the token
function whitelist(address _token) external;
/// @notice effectively disqualifies a token from governance
/// @param _token the address of the token
function revokeWhitelist(address _token) external;
/// @notice returns if the address is a gauge
/// @param gauge address of the gauge
/// @return _trueOrFalse boolean if the address is a gauge
function isGauge(address gauge) external view returns (bool _trueOrFalse);
/// @notice disable a gauge from governance
/// @param _gauge address of the gauge
function killGauge(address _gauge) external;
/// @notice re-activate a dead gauge
/// @param _gauge address of the gauge
function reviveGauge(address _gauge) external;
/// @notice re-cast a tokenID's votes
/// @param owner address of the owner
function poke(address owner) external;
/// @notice sets the main tickspacing of a token pairing
/// @param tokenA address of tokenA
/// @param tokenB address of tokenB
/// @param tickSpacing the main tickspacing to set to
function setMainTickSpacing(
address tokenA,
address tokenB,
int24 tickSpacing
) external;
/// @notice create a legacy-type gauge for an arbitrary token
/// @param _token 'token' to be used
/// @return _arbitraryGauge the address of the new custom gauge
function createArbitraryGauge(
address _token
) external returns (address _arbitraryGauge);
/// @notice returns if the address is a fee distributor
/// @param _feeDistributor address of the feeDist
/// @return _trueOrFalse if the address is a fee distributor
function isFeeDistributor(
address _feeDistributor
) external view returns (bool _trueOrFalse);
/// @notice returns the address of the emission's token
/// @return _emissionsToken emissions token contract address
function emissionsToken() external view returns (address _emissionsToken);
/// @notice returns the address of the pool's gauge, if any
/// @param _pool pool address
/// @return _gauge gauge address
function gaugeForPool(address _pool) external view returns (address _gauge);
/// @notice returns the address of the pool's feeDistributor, if any
/// @param _gauge address of the gauge
/// @return _feeDistributor address of the pool's feedist
function feeDistributorForGauge(
address _gauge
) external view returns (address _feeDistributor);
/// @notice returns the new toPool that was redirected fromPool
/// @param fromPool address of the original pool
/// @return toPool the address of the redirected pool
function poolRedirect(
address fromPool
) external view returns (address toPool);
/// @notice returns the gauge address of a CL pool
/// @param tokenA address of token A in the pair
/// @param tokenB address of token B in the pair
/// @param tickSpacing tickspacing of the pool
/// @return gauge address of the gauge
function gaugeForClPool(
address tokenA,
address tokenB,
int24 tickSpacing
) external view returns (address gauge);
/// @notice returns the array of all tickspacings for the tokenA/tokenB combination
/// @param tokenA address of token A in the pair
/// @param tokenB address of token B in the pair
/// @return _ts array of all the tickspacings
function tickSpacingsForPair(
address tokenA,
address tokenB
) external view returns (int24[] memory _ts);
/// @notice returns the main tickspacing used in the gauge/governance process
/// @param tokenA address of token A in the pair
/// @param tokenB address of token B in the pair
/// @return _ts the main tickspacing
function mainTickSpacingForPair(
address tokenA,
address tokenB
) external view returns (int24 _ts);
/// @notice returns the block.timestamp divided by 1 week in seconds
/// @return period the period used for gauges
function getPeriod() external view returns (uint256 period);
/// @notice cast a vote to direct emissions to gauges and earn incentives
/// @param owner address of the owner
/// @param _pools the list of pools to vote on
/// @param _weights an arbitrary weight per pool which will be normalized to 100% regardless of numerical inputs
function vote(
address owner,
address[] calldata _pools,
uint256[] calldata _weights
) external;
/// @notice reset the vote of an address
/// @param owner address of the owner
function reset(address owner) external;
/// @notice set the governor address
/// @param _governor the new governor address
function setGovernor(address _governor) external;
/// @notice recover stuck emissions
/// @param _gauge the gauge address
/// @param _period the period
function stuckEmissionsRecovery(address _gauge, uint256 _period) external;
/// @notice whitelists extra rewards for a gauge
/// @param _gauge the gauge to whitelist rewards to
/// @param _reward the reward to whitelist
function whitelistGaugeRewards(address _gauge, address _reward) external;
/// @notice removes a reward from the gauge whitelist
/// @param _gauge the gauge to remove the whitelist from
/// @param _reward the reward to remove from the whitelist
function removeGaugeRewardWhitelist(
address _gauge,
address _reward
) external;
/// @notice creates a legacy gauge for the pool
/// @param _pool pool's address
/// @return _gauge address of the new gauge
function createGauge(address _pool) external returns (address _gauge);
/// @notice create a concentrated liquidity gauge
/// @param tokenA the address of tokenA
/// @param tokenB the address of tokenB
/// @param tickSpacing the tickspacing of the pool
/// @return _clGauge address of the new gauge
function createCLGauge(
address tokenA,
address tokenB,
int24 tickSpacing
) external returns (address _clGauge);
/// @notice claim concentrated liquidity gauge rewards for specific NFP token ids
/// @param _gauges array of gauges
/// @param _tokens two dimensional array for the tokens to claim
/// @param _nfpTokenIds two dimensional array for the NFPs
function claimClGaugeRewards(
address[] calldata _gauges,
address[][] calldata _tokens,
uint256[][] calldata _nfpTokenIds
) external;
/// @notice claim arbitrary rewards from specific feeDists
/// @param owner address of the owner
/// @param _feeDistributors address of the feeDists
/// @param _tokens two dimensional array for the tokens to claim
function claimIncentives(
address owner,
address[] calldata _feeDistributors,
address[][] calldata _tokens
) external;
/// @notice claim arbitrary rewards from specific gauges
/// @param _gauges address of the gauges
/// @param _tokens two dimensional array for the tokens to claim
function claimRewards(
address[] calldata _gauges,
address[][] calldata _tokens
) external;
/// @notice distribute emissions to a gauge for a specific period
/// @param _gauge address of the gauge
/// @param _period value of the period
function distributeForPeriod(address _gauge, uint256 _period) external;
/// @notice attempt distribution of emissions to all gauges
function distributeAll() external;
/// @notice distribute emissions to gauges by index
/// @param startIndex start of the loop
/// @param endIndex end of the loop
function batchDistributeByIndex(
uint256 startIndex,
uint256 endIndex
) external;
/// @notice returns the votes cast for a tokenID
/// @param owner address of the owner
/// @return votes an array of votes casted
/// @return weights an array of the weights casted per pool
function getVotes(
address owner,
uint256 period
) external view returns (address[] memory votes, uint256[] memory weights);
/// @notice returns an array of all the gauges
/// @return _gauges the array of gauges
function getAllGauges() external view returns (address[] memory _gauges);
/// @notice returns an array of all the feeDists
/// @return _feeDistributors the array of feeDists
function getAllFeeDistributors()
external
view
returns (address[] memory _feeDistributors);
/// @notice sets the xShadowRatio default
function setGlobalRatio(uint256 _xRatio) external;
/// @notice returns the array of all custom/arbitrary pools
function getAllCustomPools()
external
view
returns (address[] memory _customPools);
/// @notice whether the token is whitelisted in governance
function isWhitelisted(address _token) external view returns (bool _tf);
/// @notice function for removing malicious or stuffed tokens
function removeFeeDistributorReward(
address _feeDist,
address _token
) external;
}