// SPDX-License-Identifier: MIT pragma solidity >=0.8.4 <0.9.0; import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol'; import './TradeFactorySwapperHandler.sol'; import {ISwapperEnabled} from '../utils/ISwapperEnabled.sol'; interface ITradeFactoryPositionsHandler { struct EnabledTrade { address _strategy; address _tokenIn; address _tokenOut; } error InvalidTrade(); error AllowanceShouldBeZero(); function enabledTrades() external view returns (EnabledTrade[] memory _enabledTrades); function enable(address _tokenIn, address _tokenOut) external; function disable(address _tokenIn, address _tokenOut) external; function disableByAdmin( address _strategy, address _tokenIn, address _tokenOut ) external; } abstract contract TradeFactoryPositionsHandler is ITradeFactoryPositionsHandler, TradeFactorySwapperHandler { using EnumerableSet for EnumerableSet.AddressSet; bytes32 public constant STRATEGY = keccak256('STRATEGY'); bytes32 public constant STRATEGY_MANAGER = keccak256('STRATEGY_MANAGER'); EnumerableSet.AddressSet internal _strategies; // strategy -> tokenIn[] mapping(address => EnumerableSet.AddressSet) internal _tokensInByStrategy; // strategy -> tokenIn -> tokenOut[] mapping(address => mapping(address => EnumerableSet.AddressSet)) internal _tokensOutByStrategyAndTokenIn; constructor(address _strategyModifier) { if (_strategyModifier == address(0)) revert CommonErrors.ZeroAddress(); _setRoleAdmin(STRATEGY, STRATEGY_MANAGER); _setRoleAdmin(STRATEGY_MANAGER, MASTER_ADMIN); _setupRole(STRATEGY_MANAGER, _strategyModifier); } function enabledTrades() external view override returns (EnabledTrade[] memory _enabledTrades) { uint256 _totalEnabledTrades; for (uint256 i; i < _strategies.values().length; i++) { address _strategy = _strategies.at(i); address[] memory _tokensIn = _tokensInByStrategy[_strategy].values(); for (uint256 j; j < _tokensIn.length; j++) { address _tokenIn = _tokensIn[j]; _totalEnabledTrades += _tokensOutByStrategyAndTokenIn[_strategy][_tokenIn].length(); } } _enabledTrades = new EnabledTrade[](_totalEnabledTrades); uint256 _enabledTradesIndex; for (uint256 i; i < _strategies.values().length; i++) { address _strategy = _strategies.at(i); address[] memory _tokensIn = _tokensInByStrategy[_strategy].values(); for (uint256 j; j < _tokensIn.length; j++) { address _tokenIn = _tokensIn[j]; address[] memory _tokensOut = _tokensOutByStrategyAndTokenIn[_strategy][_tokenIn].values(); for (uint256 k; k < _tokensOut.length; k++) { _enabledTrades[_enabledTradesIndex] = EnabledTrade(_strategy, _tokenIn, _tokensOut[k]); _enabledTradesIndex++; } } } } function enable(address _tokenIn, address _tokenOut) external override onlyRole(STRATEGY) { if (_tokenIn == address(0) || _tokenOut == address(0)) revert CommonErrors.ZeroAddress(); _strategies.add(msg.sender); _tokensInByStrategy[msg.sender].add(_tokenIn); if (!_tokensOutByStrategyAndTokenIn[msg.sender][_tokenIn].add(_tokenOut)) revert InvalidTrade(); } function disable(address _tokenIn, address _tokenOut) external override onlyRole(STRATEGY) { _disable(msg.sender, _tokenIn, _tokenOut); } function disableByAdmin( address _strategy, address _tokenIn, address _tokenOut ) external override onlyRole(STRATEGY_MANAGER) { // strategy.disableTradeCallback() -> tradeFactory.disable() ISwapperEnabled(_strategy).disableTradeCallback(_tokenIn, _tokenOut); } function _disable( address _strategy, address _tokenIn, address _tokenOut ) internal { if (_tokenIn == address(0) || _tokenOut == address(0)) revert CommonErrors.ZeroAddress(); if (IERC20(_tokenIn).allowance(msg.sender, address(this)) != 0) revert AllowanceShouldBeZero(); if (!_tokensOutByStrategyAndTokenIn[_strategy][_tokenIn].remove(_tokenOut)) revert InvalidTrade(); if (_tokensOutByStrategyAndTokenIn[_strategy][_tokenIn].length() == 0) { _tokensInByStrategy[_strategy].remove(_tokenIn); if (_tokensInByStrategy[_strategy].length() == 0) { _strategies.remove(_strategy); } } } }