// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/access/AccessControl.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; /** * @title CWProtocolTreasury * @notice Holds protocol-owned liquidity proceeds and fee routing targets for POL flywheel. */ contract CWProtocolTreasury is AccessControl { using SafeERC20 for IERC20; bytes32 public constant FEE_ROUTER_ROLE = keccak256("FEE_ROUTER_ROLE"); bytes32 public constant POL_OPERATOR_ROLE = keccak256("POL_OPERATOR_ROLE"); address public buybackExecutor; event BuybackExecutorUpdated(address indexed executor); event FeeRouted(address indexed token, uint256 amount, address indexed destination); event PolPositionRegistered(bytes32 indexed positionId, address indexed venue, address pair, uint256 lpBalance); error ZeroAddress(); mapping(bytes32 => bool) public polPositionRegistered; constructor(address admin, address buybackExecutor_) { if (admin == address(0)) { revert ZeroAddress(); } _grantRole(DEFAULT_ADMIN_ROLE, admin); _grantRole(FEE_ROUTER_ROLE, admin); _grantRole(POL_OPERATOR_ROLE, admin); buybackExecutor = buybackExecutor_; } function setBuybackExecutor(address executor_) external onlyRole(DEFAULT_ADMIN_ROLE) { buybackExecutor = executor_; emit BuybackExecutorUpdated(executor_); } function routeFees(address token, uint256 amount, address destination) external onlyRole(FEE_ROUTER_ROLE) { IERC20(token).safeTransfer(destination, amount); emit FeeRouted(token, amount, destination); } function registerPolPosition( bytes32 positionId, address venue, address pair, uint256 lpBalance ) external onlyRole(POL_OPERATOR_ROLE) { polPositionRegistered[positionId] = true; emit PolPositionRegistered(positionId, venue, pair, lpBalance); } }