contract StakingPoolProducerImpl is StakingPoolProducer, StakingPoolData {
    IERC20 public immutable ctsi;
    Fee public fee;

    constructor(address _ctsi) {
        ctsi = IERC20(_ctsi);
    }

    function __StakingPoolProducer_init(address _fee, address _pos) internal {
        fee = Fee(_fee);
        pos = IPoS(_pos);
    }

    /// @notice routes produceBlock to POS contract and
    /// updates internal states of the pool
    /// @return true when everything went fine
    function produceBlock(uint256 _index) external override returns (bool) {
        IRewardManager rewardManager = IRewardManager(
            pos.getRewardManagerAddress(_index)
        );

        // get block reward
        uint256 reward = rewardManager.getCurrentReward();

        // produce block in the PoS
        require(
            pos.produceBlock(_index),
            "StakingPoolProducerImpl: failed to produce block"
        );

        // calculate pool commission
        uint256 commission = fee.getCommission(_index, reward);
        require(
            commission <= reward,
            "StakingPoolProducerImpl: commission is greater than block reward"
        );

        uint256 remainingReward = reward - commission; // this is a safety check
        // if commission is over the reward amount, it will underflow

        // increase pool amount, this will change the pool exchange rate
        amount += remainingReward;

        // send commission directly to pool owner
        if (commission > 0) {
            require(
                ctsi.transfer(owner(), commission),
                "StakingPoolProducerImpl: failed to transfer commission"
            );
        }

        // remainingReward is part of the balance, so it will automatically be staked by StakingPoolStakingImpl
        emit BlockProduced(reward, commission);

        return true;
    }
}
function produceBlock(uint256 _index) external override returns (bool) {
    IRewardManager rewardManager = IRewardManager(
        pos.getRewardManagerAddress(_index)
    );

    // get block reward
    uint256 reward = rewardManager.getCurrentReward();

    // produce block in the PoS
    require(
        pos.produceBlock(_index),
        "StakingPoolProducerImpl: failed to produce block"
    );

    // calculate pool commission
    uint256 commission = fee.getCommission(_index, reward);
    require(
        commission <= reward,
        "StakingPoolProducerImpl: commission is greater than block reward"
    );

    uint256 remainingReward = reward - commission; // this is a safety check
    // if commission is over the reward amount, it will underflow

    // increase pool amount, this will change the pool exchange rate
    amount += remainingReward;

    // send commission directly to pool owner
    if (commission > 0) {
        require(
            ctsi.transfer(owner(), commission),
            "StakingPoolProducerImpl: failed to transfer commission"
        );
    }

    // remainingReward is part of the balance, so it will automatically be staked by StakingPoolStakingImpl
    emit BlockProduced(reward, commission);

    return true;
}

/// @notice routes produceBlock to POS contract and
/// updates internal states of the pool
/// @return true when everything went fine