// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "@overnight-contracts/common/contracts/libraries/OvnMath.sol"; import "./interfaces/IStrategy.sol"; import "./interfaces/IRoleManager.sol"; abstract contract Strategy is IStrategy, Initializable, AccessControlUpgradeable, UUPSUpgradeable { bytes32 public constant PORTFOLIO_MANAGER = keccak256("PORTFOLIO_MANAGER"); bytes32 public constant PORTFOLIO_AGENT_ROLE = keccak256("PORTFOLIO_AGENT_ROLE"); address public portfolioManager; uint256 public swapSlippageBP; uint256 public navSlippageBP; uint256 public stakeSlippageBP; IRoleManager public roleManager; function __Strategy_init() internal initializer { __AccessControl_init(); __UUPSUpgradeable_init(); _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); swapSlippageBP = 20; navSlippageBP = 20; stakeSlippageBP = 4; } function _authorizeUpgrade(address newImplementation) internal onlyRole(DEFAULT_ADMIN_ROLE) override {} // --- modifiers modifier onlyPortfolioManager() { require(portfolioManager == msg.sender, "Restricted to PORTFOLIO_MANAGER"); _; } modifier onlyAdmin() { require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Restricted to admins"); _; } modifier onlyPortfolioAgent() { require(roleManager.hasRole(PORTFOLIO_AGENT_ROLE, msg.sender), "Restricted to Portfolio Agent"); _; } // --- setters function setStrategyParams(address _portfolioManager, address _roleManager) public onlyAdmin { require(_portfolioManager != address(0), "Zero address not allowed"); require(_roleManager != address(0), "Zero address not allowed"); portfolioManager = _portfolioManager; roleManager = IRoleManager(_roleManager); } function setSlippages( uint256 _swapSlippageBP, uint256 _navSlippageBP, uint256 _stakeSlippageBP ) public onlyPortfolioAgent { swapSlippageBP = _swapSlippageBP; navSlippageBP = _navSlippageBP; stakeSlippageBP = _stakeSlippageBP; emit SlippagesUpdated(_swapSlippageBP, _navSlippageBP, _stakeSlippageBP); } // --- logic function stake( address _asset, uint256 _amount ) external override onlyPortfolioManager { uint256 minNavExpected = OvnMath.subBasisPoints(this.netAssetValue(), navSlippageBP); _stake(_asset, IERC20(_asset).balanceOf(address(this))); require(this.netAssetValue() >= minNavExpected, "Strategy NAV less than expected"); emit Stake(_amount); } function unstake( address _asset, uint256 _amount, address _beneficiary, bool _targetIsZero ) external override onlyPortfolioManager returns (uint256) { uint256 minNavExpected = OvnMath.subBasisPoints(this.netAssetValue(), navSlippageBP); uint256 withdrawAmount; uint256 rewardAmount; if (_targetIsZero) { rewardAmount = _claimRewards(_beneficiary); withdrawAmount = _unstakeFull(_asset, _beneficiary); } else { withdrawAmount = _unstake(_asset, _amount, _beneficiary); require(withdrawAmount >= _amount, 'Returned value less than requested amount'); } require(this.netAssetValue() >= minNavExpected, "Strategy NAV less than expected"); IERC20(_asset).transfer(_beneficiary, withdrawAmount); emit Unstake(_amount, withdrawAmount); if (rewardAmount > 0) { emit Reward(rewardAmount); } return withdrawAmount; } function claimRewards(address _to) external override onlyPortfolioManager returns (uint256) { uint256 rewardAmount = _claimRewards(_to); if (rewardAmount > 0) { emit Reward(rewardAmount); } return rewardAmount; } function _stake( address _asset, uint256 _amount ) internal virtual { revert("Not implemented"); } function _unstake( address _asset, uint256 _amount, address _beneficiary ) internal virtual returns (uint256) { revert("Not implemented"); } function _unstakeFull( address _asset, address _beneficiary ) internal virtual returns (uint256) { revert("Not implemented"); } function _claimRewards(address _to) internal virtual returns (uint256) { revert("Not implemented"); } uint256[45] private __gap; }