Sonic Blaze Testnet

Contract Diff Checker

Contract Name:
FeeRecipientFactory

Contract Source Code:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import {IFeeRecipientFactory} from "../interfaces/IFeeRecipientFactory.sol";
import {FeeRecipient} from "./../FeeRecipient.sol";

contract FeeRecipientFactory is IFeeRecipientFactory {
    /// @inheritdoc IFeeRecipientFactory
    address public lastFeeRecipient;

    /// @inheritdoc IFeeRecipientFactory
    address public treasury;

    address public accessHub;

    address public immutable voter;

    /// @inheritdoc IFeeRecipientFactory
    uint256 public feeToTreasury;

    /// @inheritdoc IFeeRecipientFactory
    mapping(address pair => address feeRecipient) public feeRecipientForPair;

    event SetFeeToTreasury(uint256 indexed feeToTreasury);

    modifier onlyGovernance() {
        require(msg.sender == accessHub);
        _;
    }

    constructor(address _treasury, address _voter, address _accessHub) {
        treasury = _treasury;
        voter = _voter;
        accessHub = _accessHub;
        /// @dev start at 8%
        feeToTreasury = 800;
    }
    /// @inheritdoc IFeeRecipientFactory
    function createFeeRecipient(
        address pair
    ) external returns (address _feeRecipient) {
        /// @dev ensure caller is the voter
        require(msg.sender == voter, NOT_AUTHORIZED());
        /// @dev create a new feeRecipient
        _feeRecipient = address(
            new FeeRecipient(pair, msg.sender, address(this))
        );
        /// @dev dont need to ensure that a feeRecipient wasn't already made previously
        feeRecipientForPair[pair] = _feeRecipient;
        lastFeeRecipient = _feeRecipient;
    }
    /// @inheritdoc IFeeRecipientFactory
    function setFeeToTreasury(uint256 _feeToTreasury) external onlyGovernance {
        /// @dev ensure fee to treasury isn't too high
        require(_feeToTreasury <= 10_000, INVALID_TREASURY_FEE());
        feeToTreasury = _feeToTreasury;
        emit SetFeeToTreasury(_feeToTreasury);
    }

    /// @inheritdoc IFeeRecipientFactory
    function setTreasury(address _treasury) external onlyGovernance {
        treasury = _treasury;
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.26;

interface IFeeRecipientFactory {
    error INVALID_TREASURY_FEE();
    error NOT_AUTHORIZED();

    /// @notice the pair fees for a specific pair
    /// @param pair the pair to check
    /// @return feeRecipient the feeRecipient contract address for the pair
    function feeRecipientForPair(
        address pair
    ) external view returns (address feeRecipient);

    /// @notice the last feeRecipient address created
    /// @return _feeRecipient the address of the last pair fees contract
    function lastFeeRecipient() external view returns (address _feeRecipient);
    /// @notice create the pair fees for a pair
    /// @param pair the address of the pair
    /// @return _feeRecipient the address of the newly created feeRecipient
    function createFeeRecipient(
        address pair
    ) external returns (address _feeRecipient);

    /// @notice the fee % going to the treasury
    /// @return _feeToTreasury the fee %
    function feeToTreasury() external view returns (uint256 _feeToTreasury);

    /// @notice get the treasury address
    /// @return _treasury address of the treasury
    function treasury() external view returns (address _treasury);

    /// @notice set the fee % to be sent to the treasury
    /// @param _feeToTreasury the fee % to be sent to the treasury
    function setFeeToTreasury(uint256 _feeToTreasury) external;

    /// @notice set a new treasury address
    /// @param _treasury the new address
    function setTreasury(address _treasury) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {IFeeDistributor} from "./interfaces/IFeeDistributor.sol";
import {IFeeRecipient} from "./interfaces/IFeeRecipient.sol";
import {IFeeRecipientFactory} from "./interfaces/IFeeRecipientFactory.sol";

/// @notice Pair Fees contract is used as a 1:1 pair relationship to split out fees, this ensures that the curve does not need to be modified for LP shares
contract FeeRecipient is IFeeRecipient {
    /// @notice The pair it is bonded to
    address public immutable pair;
    /// @notice voter contract which fees are gated to be claimed by
    address public immutable voter;
    /// @notice feedist contract where fees will be sent to
    address public feeDistributor;
    /// @notice factory contract for feeRecipient (legacy fees)
    address public immutable feeRecipientFactory;

    constructor(address _pair, address _voter, address _feeRecipientFactory) {
        pair = _pair;
        voter = _voter;
        feeRecipientFactory = _feeRecipientFactory;
    }

    /// @notice initialize the FeeRecipient contract and approve the LP tokens to the feeDist, gated to voter
    function initialize(address _feeDistributor) external {
        require(msg.sender == voter, NOT_AUTHORIZED());
        feeDistributor = _feeDistributor;
        IERC20(pair).approve(_feeDistributor, type(uint256).max);
    }

    /// @notice notifies the fees
    function notifyFees() external {
        /// @dev limit calling notifyFees() to the voter contract
        require(msg.sender == voter, NOT_AUTHORIZED());

        /// @dev fetch balance of LP in the contract
        uint256 amount = IERC20(pair).balanceOf(address(this));
        /// @dev terminate early if there's no rewards
        if (amount == 0) return;
        /// @dev calculate treasury share
        uint256 feeToTreasury = IFeeRecipientFactory(feeRecipientFactory)
            .feeToTreasury();
        /// @dev if any to treasury
        if (feeToTreasury > 0) {
            /// @dev fetch treasury from factory
            address treasury = IFeeRecipientFactory(feeRecipientFactory)
                .treasury();
            /// @dev mulDiv
            uint256 amountToTreasury = (amount * feeToTreasury) / 10_000;
            /// @dev decrement amount
            amount -= amountToTreasury;
            /// @dev naked transfer to treasury, no staking
            IERC20(pair).transfer(treasury, amountToTreasury);
        }

        /// @dev if there's any fees
        if (amount > 0) {
            IFeeDistributor(feeDistributor).notifyRewardAmount(pair, amount);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26;

interface IFeeDistributor {
    error NOT_AUTHORIZED();
    error ZERO_AMOUNT();
    error NOT_FINALIZED();
    error TOKEN_ERROR(address);

    event Deposit(address owner, uint256 amount);

    event Withdraw(address owner, uint256 amount);

    event NotifyReward(
        address indexed from,
        address indexed reward,
        uint256 amount,
        uint256 period
    );

    event VotesIncentivized(
        address indexed from,
        address indexed reward,
        uint256 amount,
        uint256 period
    );

    event ClaimRewards(
        uint256 period,
        address owner,
        address receiver,
        address reward,
        uint256 amount
    );

    event RewardsRemoved(address _reward);
    /// @notice the address of the voter contract
    function voter() external view returns (address);
    /// @notice the address of the voting module
    function voteModule() external view returns (address);
    /// @notice the address of the feeRecipient contract
    function feeRecipient() external view returns (address);

    /// @notice the first period (epoch) that this contract was deployed
    function firstPeriod() external view returns (uint256);

    /// @notice balance of the voting power for a user
    /// @param owner the owner
    /// @return amount the amount of voting share
    function balanceOf(address owner) external view returns (uint256 amount);

    /// @notice total cumulative amount of voting power per epoch
    /// @param period the period to check
    /// @return weight the amount of total voting power
    function votes(uint256 period) external view returns (uint256 weight);

    /// @notice "internal" function gated to voter to add votes
    /// @dev internal notation inherited from original solidly, kept for continuity
    function _deposit(uint256 amount, address owner) external;
    /// @notice "internal" function gated to voter to remove votes
    /// @dev internal notation inherited from original solidly, kept for continuity
    function _withdraw(uint256 amount, address owner) external;

    /// @notice function to claim rewards on behalf of another
    /// @param owner owner's address
    /// @param tokens an array of the tokens
    function getRewardForOwner(address owner, address[] memory tokens) external;

    /// @notice function for sending fees directly to be claimable (in system where fees are distro'd through the week)
    /// @dev for lumpsum - this would operate similarly to incentivize
    /// @param token the address of the token to send for notifying
    /// @param amount the amount of token to send
    function notifyRewardAmount(address token, uint256 amount) external;

    /// @notice gives an array of reward tokens for the feedist
    /// @return _rewards array of rewards
    function getRewardTokens()
        external
        view
        returns (address[] memory _rewards);

    /// @notice shows the earned incentives in the feedist
    /// @param token the token address to check
    /// @param owner owner's address
    /// @return reward the amount earned/claimable
    function earned(
        address token,
        address owner
    ) external view returns (uint256 reward);

    /// @notice function to submit incentives to voters for the upcoming flip
    /// @param token the address of the token to send for incentivization
    /// @param amount the amount of token to send
    function incentivize(address token, uint256 amount) external;

    /// @notice get the rewards for a specific period
    /// @param owner owner's address
    function getPeriodReward(
        uint256 period,
        address owner,
        address token
    ) external;
    /// @notice get the fees and incentives
    function getReward(address owner, address[] memory tokens) external;

    /// @notice remove a reward from the set
    function removeReward(address _token) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

interface IFeeRecipient {
    error STF();
    error NOT_AUTHORIZED();
    
    function initialize(address _feeDistributor) external;
    function notifyFees() external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):