S Price: $0.728819 (+8.30%)

Contract Diff Checker

Contract Name:
MigratorManager

Contract Source Code:

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

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC-20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC-721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC-1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}

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

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}

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

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";

// 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: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC-20
 * applications.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

    mapping(address account => mapping(address spender => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `value`.
     */
    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Skips emitting an {Approval} event indicating an allowance update. This is not
     * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    /**
     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
     * this function.
     *
     * Emits a {Transfer} event.
     */
    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            // Overflow check required: The rest of the code assumes that totalSupply never overflows
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                // Overflow not possible: value <= fromBalance <= totalSupply.
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                _totalSupply -= value;
            }
        } else {
            unchecked {
                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    /**
     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
     * Relies on the `_update` mechanism
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _mint(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(address(0), account, value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead
     */
    function _burn(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        _update(account, address(0), value);
    }

    /**
     * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    /**
     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
     *
     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
     * `Approval` event during `transferFrom` operations.
     *
     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
     * true using the following override:
     *
     * ```solidity
     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
     *     super._approve(owner, spender, value, true);
     * }
     * ```
     *
     * Requirements are the same as {_approve}.
     */
    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        _allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance < type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC20Burnable.sol)

pragma solidity ^0.8.20;

import {ERC20} from "../ERC20.sol";
import {Context} from "../../../utils/Context.sol";

/**
 * @dev Extension of {ERC20} that allows token holders to destroy both their own
 * tokens and those that they have an allowance for, in a way that can be
 * recognized off-chain (via event analysis).
 */
abstract contract ERC20Burnable is Context, ERC20 {
    /**
     * @dev Destroys a `value` amount of tokens from the caller.
     *
     * See {ERC20-_burn}.
     */
    function burn(uint256 value) public virtual {
        _burn(_msgSender(), value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, deducting from
     * the caller's allowance.
     *
     * See {ERC20-_burn} and {ERC20-allowance}.
     *
     * Requirements:
     *
     * - the caller must have allowance for ``accounts``'s tokens of at least
     * `value`.
     */
    function burnFrom(address account, uint256 value) public virtual {
        _spendAllowance(account, _msgSender(), value);
        _burn(account, value);
    }
}

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

pragma solidity ^0.8.20;

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

/**
 * @dev Interface for the optional metadata functions from the ERC-20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

// 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);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.20;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS
    }

    /**
     * @dev The signature derives the `address(0)`.
     */
    error ECDSAInvalidSignature();

    /**
     * @dev The signature has an invalid length.
     */
    error ECDSAInvalidSignatureLength(uint256 length);

    /**
     * @dev The signature has an S value that is in the upper half order.
     */
    error ECDSAInvalidSignatureS(bytes32 s);

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
     * return address(0) without also returning an error description. Errors are documented using an enum (error type)
     * and a bytes32 providing additional information about the error.
     *
     * If no error is returned, then the address can be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     */
    function tryRecover(
        bytes32 hash,
        bytes memory signature
    ) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly ("memory-safe") {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[ERC-2098 short signatures]
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
        unchecked {
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            // We do not check for an overflow here since the shift operation results in 0 or 1.
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            return tryRecover(hash, v, r, s);
        }
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS, s);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature, bytes32(0));
        }

        return (signer, RecoverError.NoError, bytes32(0));
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
     */
    function _throwError(RecoverError error, bytes32 errorArg) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert ECDSAInvalidSignature();
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert ECDSAInvalidSignatureLength(uint256(errorArg));
        } else if (error == RecoverError.InvalidSignatureS) {
            revert ECDSAInvalidSignatureS(errorArg);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

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

import "@openzeppelin/contracts/access/Ownable.sol";

contract ConfigManager is Ownable {

    address public platformWallet;

    address public susdToken;
    address public boomToken;
    address public feeManager;
    address public migratorManager;
    address public rewardManager;
    address public graduateFactory;
    address public graduateRouter;
    address public router;

    uint256 public minPurchaseAmount;
    uint256 public inactivePoolThreshold;        
    uint256 public creationFee;

    // Constants for virtual dollar thresholds
    uint256 public constant START_THRESHOLD = 10_000 * 1e6;    // 10,000 virtual dollars
    uint256 public constant FAMOUS_THRESHOLD = 20_000 * 1e6;   // 20,000 virtual dollars
    uint256 public constant VIRAL_THRESHOLD = 40_000 * 1e6;    // 40,000 virtual dollars
    uint256 public constant GRADUATE_THRESHOLD = 80_000 * 1e6; // 80,000 virtual dollars    

    constructor() Ownable(msg.sender) {
        inactivePoolThreshold = 24 hours;
        minPurchaseAmount = 1 * 1e6;  // 1 SUSD (1,000,000 base units)
        creationFee = 1_000; // 0.001 SUSD in base units        
    }

    function setAddresses(
        address _platformWallet,
        address _susdToken,
        address _boomToken,
        address _feeManager,
        address _migratorManager,
        address _rewardManager,
        address _router
    ) public onlyOwner {
        platformWallet = _platformWallet;
        susdToken = _susdToken;
        boomToken = _boomToken;
        feeManager = _feeManager;
        migratorManager = _migratorManager;
        rewardManager = _rewardManager;
        router = _router;
    }

    function setParameters(
        uint256 _minPurchaseAmount,
        uint256 _inactivePoolThreshold,
        uint256 _creationFee
    ) public onlyOwner {
        minPurchaseAmount = _minPurchaseAmount;
        inactivePoolThreshold = _inactivePoolThreshold;
        creationFee = _creationFee;
    }

}

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

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./RewardManager.sol";
import "./Router.sol";
import "./ConfigManager.sol";

contract FeeManager is Ownable {

    ConfigManager public configManager;

    // Fee rates for each tier (in basis points, 1% = 100)
    uint256 public constant START_FEE_RATE = 2000;   // 20%
    uint256 public constant FAMOUS_FEE_RATE = 1500;  // 15%
    uint256 public constant VIRAL_FEE_RATE = 1000;   // 10%

    // Platform shares for each tier (in basis points)
    uint256 public constant START_PLATFORM_SHARE = 8000;   // 80%
    uint256 public constant FAMOUS_PLATFORM_SHARE = 8000;  // 80%
    uint256 public constant VIRAL_PLATFORM_SHARE = 9200;   // 92%

    // Creator shares for each tier (in basis points)
    uint256 public constant START_CREATOR_SHARE = 1200;   // 12%
    uint256 public constant FAMOUS_CREATOR_SHARE = 1200;  // 12%
    uint256 public constant VIRAL_CREATOR_SHARE = 800;    // 8%

    // Deployer shares for each tier (in basis points)
    uint256 public constant START_DEPLOYER_SHARE = 800;   // 8%
    uint256 public constant FAMOUS_DEPLOYER_SHARE = 800;  // 8%
    uint256 public constant VIRAL_DEPLOYER_SHARE = 0;     // 0%

    // Graduate DEX distribution (in basis points)
    uint256 public constant GRADUATE_PLATFORM_SHARE = 9000;  // 90%
    uint256 public constant GRADUATE_CREATOR_SHARE = 750;    // 7.5%
    uint256 public constant GRADUATE_DEPLOYER_SHARE = 250;   // 2.5%

    // Graduate DEX configuration
    uint256 public constant GRADUATE_SUSD_AMOUNT = 35_000 * 1e6;  // 35,000 SUSD
    uint256 public constant GRADUATE_TOKEN_AMOUNT = 10_000 * 1e18; // 10,000 tokens

    struct FeeInfo {
        uint256 feeRate;
        uint256 creatorShare;
        uint256 deployerShare;
        uint256 platformShare;
    }

    constructor(address _configManager) Ownable(msg.sender) {
        configManager = ConfigManager(_configManager);
    }

    function getFeeInfo(uint256 virtualDollarAmount) public view returns (uint256, uint256, uint256, uint256) {
        if (virtualDollarAmount >= configManager.GRADUATE_THRESHOLD()) {
            return (0, GRADUATE_CREATOR_SHARE, GRADUATE_DEPLOYER_SHARE, GRADUATE_PLATFORM_SHARE);
        }
        else if (virtualDollarAmount >= configManager.VIRAL_THRESHOLD()) {
            return (
                VIRAL_FEE_RATE,
                VIRAL_CREATOR_SHARE,
                VIRAL_DEPLOYER_SHARE,
                VIRAL_PLATFORM_SHARE
            );
        }
        else if (virtualDollarAmount >= configManager.FAMOUS_THRESHOLD()) {
            return (
                FAMOUS_FEE_RATE,
                FAMOUS_CREATOR_SHARE,
                FAMOUS_DEPLOYER_SHARE,
                FAMOUS_PLATFORM_SHARE
            );
        }
        else if (virtualDollarAmount >= configManager.START_THRESHOLD()) {
            return (
                START_FEE_RATE,
                START_CREATOR_SHARE,
                START_DEPLOYER_SHARE,
                START_PLATFORM_SHARE
            );
        }
        return (0, 0, 0, 0); // Below start threshold
    }

}

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

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./TweetToken.sol";
import "./TweetPool.sol";
import "./RewardManager.sol";
import "./MigratorManager.sol";
import "./FeeManager.sol";
import "./ConfigManager.sol";

interface IUniswapV2Factory {
    function getPair(address tokenA, address tokenB) external view returns (address pair);
}

interface IUniswapV2Router02 {
    function addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity);
}

contract MigratorManager is Ownable {
    // State Variables
    RewardManager public rewardManager;
    FeeManager public feeManager;    
    ConfigManager public configManager;
    address public routingContract;

    // Events
    event MigratedToShadowChain(address indexed virtualPool, address indexed tweetToken);

    constructor(
        address _rewardManager,
        address _feeManager,
        address _configManager,
        address _routingContract
    ) Ownable(msg.sender) {
        rewardManager = RewardManager(_rewardManager);
        feeManager = FeeManager(_feeManager);
        configManager = ConfigManager(_configManager);
        routingContract = _routingContract;
    }

    // Modifiers
    modifier onlyRoutingContract() {
        require(msg.sender == routingContract, "Reward: caller is not the routing contract");
        _;
    }    

    function checkAndHandleGraduation(
        address _virtualPoolAddress
    ) external onlyRoutingContract {

        TweetPool virtualPool = TweetPool(_virtualPoolAddress);
        TweetToken tweetToken = TweetToken(virtualPool.tweetToken());

        // Check if pool should graduate
        if (virtualPool.virtualDollarReserve() >= virtualPool.GRADUATE_THRESHOLD() && !virtualPool.isGraduated()) {
            _migrateToShadowChain(virtualPool, tweetToken);
        }
    }

    function _migrateToShadowChain(
        TweetPool tweetPool,
        TweetToken tweetToken
    ) internal {
        require(configManager.graduateFactory() != address(0), "Migrator: Graduate factory not set");
        require(configManager.graduateRouter() != address(0), "Migrator: Graduate router not set");

        // Get current reserves
        uint256 virtualDollarReserve = tweetPool.virtualDollarReserve();

        (uint256 graduatePoolDollar, uint256 creatorShare, uint256 deployerShare, uint256 platformShare) = feeManager.getFeeInfo(virtualDollarReserve);


        // Calculate amounts for profit, creator, and deployer
        uint256 totalRewardAmount = virtualDollarReserve - graduatePoolDollar - (10_000 * 10**6);  // All virtual dollars become fees
        uint256 creatorAmount = totalRewardAmount * creatorShare / 10_000;
        uint256 deployerAmount = totalRewardAmount * deployerShare / 10_000;
        uint256 platformAmount = totalRewardAmount * platformShare / 10_000;

        IERC20(configManager.susdToken()).transfer(configManager.platformWallet(), platformAmount);
        rewardManager.depositRewards(tweetToken.tweetUserId(), creatorAmount, tweetPool.deployerAddress(), deployerAmount);


        // Create pair on Shadow chain DEX
        bytes memory createPairData = abi.encodeWithSignature(
            "createPair(address,address)",
            address(tweetToken),
            configManager.susdToken()
        );
        (bool success1,) = configManager.graduateFactory().call(createPairData);
        require(success1, "Migrator: Failed to create pair on Shadow chain");

        // Burn remaining Tweet Tokens
        tweetToken.burn(tweetToken.balanceOf(address(this)));

        // Add liquidity to the pair
        bytes memory addLiquidityData = abi.encodeWithSignature(
            "addLiquidity(address,address,uint256,uint256,uint256,uint256,address,uint256)",
            address(tweetToken),
            configManager.susdToken(),
            tweetToken.balanceOf(address(this)),
            graduatePoolDollar,
            0,
            0,
            address(this),
            block.timestamp + 1800
        );
        (bool success2,) = configManager.graduateRouter().call(addLiquidityData);
        require(success2, "Migrator: Failed to add liquidity on Shadow chain");

        emit MigratedToShadowChain(address(tweetPool), address(tweetToken));
    }

    // Admin functions
    function setConfigManager(address _configManager) external onlyOwner {
        require(_configManager != address(0), "Migrator: invalid config manager");
        configManager = ConfigManager(_configManager);
    }

}

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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./ConfigManager.sol";

contract RewardManager is Ownable {
    using ECDSA for bytes32;

    // State variables
    IERC20 public susdToken;
    address public signerAddress;
    mapping(string => uint256) public creatorRewards;
    mapping(address => uint256) public deployerRewards;
    address public routingContract;
    ConfigManager public configManager;

    // Events
    event CreatorRewardClaimed(string indexed tweetUserId, address indexed recipient, uint256 amount);
    event DeployerRewardClaimed(address indexed deployerAddress, address indexed recipient, uint256 amount);

    // Modifiers
    modifier onlyRoutingContract() {
        require(msg.sender == routingContract, "Reward: caller is not the routing contract");
        _;
    }

    constructor(address _routingContract, address _configManager, address _signerAddress, address _susdToken) Ownable(msg.sender) {
        routingContract = _routingContract;
        require(_signerAddress != address(0), "Reward: invalid signer address");
        require(_susdToken != address(0), "Reward: invalid SUSD token");
        signerAddress = _signerAddress;
        susdToken = IERC20(_susdToken);
        configManager = ConfigManager(_configManager);
    }

    function depositRewards(string calldata tweetUserId, uint256 amount, address deployerAddress, uint256 deployerAmount) external onlyRoutingContract {
        creatorRewards[tweetUserId] += amount;
        deployerRewards[deployerAddress] += deployerAmount;
    }


    function claimCreatorReward(string calldata tweetUserId, address payable to, bytes calldata signature) external {
        uint256 amount = creatorRewards[tweetUserId];
        require(amount > 0, "Reward: no reward available");

        // Verify signature
        bytes32 messageHash = keccak256(
            abi.encodePacked(
                "\x19Ethereum Signed Message:\n32",
                keccak256(
                    abi.encode(
                        address(this),    // Contract address for replay protection
                        tweetUserId,      // Tweet user ID
                        to,              // Recipient address
                        amount          // Amount being claimed
                    )
                )
            )
        );
        address recoveredSigner = ECDSA.recover(messageHash, signature);
        require(recoveredSigner == signerAddress, "RewardManager: invalid signature");

        creatorRewards[tweetUserId] = 0;
        require(susdToken.transfer(to, amount), "Reward: transfer failed");
        
        emit CreatorRewardClaimed(tweetUserId, to, amount);
    }

    function claimDeployerReward(address payable to) external {
        uint256 amount = deployerRewards[msg.sender];
        require(amount > 0, "Reward: no reward available");

        deployerRewards[msg.sender] = 0;
        require(susdToken.transfer(to, amount), "Reward: transfer failed");
        
        emit DeployerRewardClaimed(msg.sender, to, amount);
    }

    function setRoutingContract(address _routingContract) external onlyOwner {
        require(_routingContract != address(0), "Reward: invalid routing contract");
        routingContract = _routingContract;
    }

    function setSignerAddress(address newSigner) external onlyOwner {
        require(newSigner != address(0), "Reward: invalid signer address");
        signerAddress = newSigner;
    }

    function setConfigManager(address _configManager) external onlyOwner {
        require(_configManager != address(0), "Reward: invalid config manager");
        configManager = ConfigManager(_configManager);
    }

    function getCreatorReward(string calldata tweetUserId) external view returns (uint256) {
        return creatorRewards[tweetUserId];
    }

    function getDeployerReward(address deployerAddress) external view returns (uint256) {
        return deployerRewards[deployerAddress];
    }
}

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

import "./TweetToken.sol";
import "./TweetPool.sol"; 
import "./RewardManager.sol";
import "./MigratorManager.sol";
import "./FeeManager.sol";
import "./ConfigManager.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract Router is Ownable {
    using SafeERC20 for IERC20;

    // State Variables
    ConfigManager public configManager;
    FeeManager public feeManager;
    RewardManager public rewardManager;

    IERC20 public susdToken;
    uint256 public poolId;

    // Events
    event TweetPoolCreated(uint256 poolId);
    event TweetPoolBought(uint256 indexed poolId, address buyer, uint256 tokenAmount);
    event TweetPoolSold(uint256 indexed poolId, address seller, uint256 dollarAmount);
    event TweetPoolGraduated(uint256 indexed poolId);

    // Mappings
    mapping(uint256 => address) public poolIdToTweetPool;
    mapping(string => mapping(string => uint256)) public tweetPoolId;
    mapping(string => mapping(string => bool)) public tweetPoolExists;

    constructor(address _configManager) Ownable(msg.sender) {
        poolId = 0;
        configManager = ConfigManager(_configManager);
    }

    // View functions
    function viewBuyOrder(uint256 _poolId, uint256 _dollarIn) public view returns(uint256 fee_, uint256 tokenOut_, uint256 newDollarReserve_, uint256 newTokenReserve_) { 
        require(_dollarIn >= configManager.minPurchaseAmount(), "Router: Amount too small");
        TweetPool tweetPool = TweetPool(poolIdToTweetPool[_poolId]);        
        (uint256 feeRate,,, ) = feeManager.getFeeInfo(tweetPool.virtualDollarReserve());
        
        // Calculate fee and net input
        uint256 totalFee = (_dollarIn * feeRate) / 10000;
        uint256 netDollarIn = _dollarIn - totalFee;

        // Calculate AMM swap
        uint256 k = tweetPool.virtualDollarReserve() * tweetPool.tweetTokenReserve();
        uint256 newDollarReserve = tweetPool.virtualDollarReserve() + netDollarIn;
        uint256 newTokenReserve = k / newDollarReserve;
        uint256 tokenOut = tweetPool.tweetTokenReserve() - newTokenReserve;

        return (totalFee, tokenOut, newDollarReserve, newTokenReserve);
    }

    function viewSellOrder(uint256 _poolId, uint256 _tokenIn) public view returns(uint256 fee_, uint256 dollarOut_, uint256 newDollarReserve_, uint256 newTokenReserve_) {
        TweetPool tweetPool = TweetPool(poolIdToTweetPool[_poolId]);
        (uint256 feeRate,,, ) = feeManager.getFeeInfo(tweetPool.virtualDollarReserve());
        
        // Calculate AMM swap
        uint256 k = tweetPool.virtualDollarReserve() * tweetPool.tweetTokenReserve();
        uint256 newTokenReserve = tweetPool.tweetTokenReserve() + _tokenIn;
        uint256 newDollarReserve = k / newTokenReserve;
        uint256 grossDollarOut = tweetPool.virtualDollarReserve() - newDollarReserve;
        
        // Calculate fee and net output
        uint256 fee = (grossDollarOut * feeRate) / 10000;
        uint256 netDollarOut = grossDollarOut - fee;

        return (fee, netDollarOut, newDollarReserve, newTokenReserve);
    }

    // State-changing functions
    function createTweetToken(
        string calldata tweetId,
        string calldata tweetUserId) external returns(uint256) {

        require(!tweetPoolExists[tweetId][tweetUserId], "Router: TweetToken already exists");

        // Check allowance and transfer 0.001 SUSD to platform
        require(IERC20(configManager.susdToken()).allowance(msg.sender, address(this)) >= configManager.creationFee(), "Router: insufficient allowance for platform fee");
        require(IERC20(configManager.susdToken()).transferFrom(msg.sender, configManager.platformWallet(), configManager.creationFee()), "Router: platform fee transfer failed");

        // Deploy TweetToken
        TweetToken tweetToken = new TweetToken(tweetId, tweetUserId);
        
        // Deploy TweetPool        
        TweetPool tweetPool = new TweetPool(
            tweetId,
            address(tweetToken),
            address(this),
            tweetUserId,
            msg.sender,
            configManager.inactivePoolThreshold(),
            address(configManager)
        );

        poolId++;
        
        poolIdToTweetPool[poolId] = address(tweetPool);
        tweetPoolId[tweetId][tweetUserId] = poolId;
        tweetPoolExists[tweetId][tweetUserId] = true;

        emit TweetPoolCreated(poolId);
        return poolId;
    }

    function buyTweetToken(
        uint256 _poolId, 
        uint256 _dollarIn
    ) external {
        require(_dollarIn >= configManager.minPurchaseAmount(), "Router: Amount too small");
        require(IERC20(configManager.susdToken()).transferFrom(msg.sender, address(this), _dollarIn), "Router: SUSD transfer failed");
        require(0 < _poolId && _poolId <= poolId, "Router: Pool does not exist");        
        
        TweetPool tweetPool = TweetPool(poolIdToTweetPool[_poolId]);
        require(tweetPool.isGraduated() == false, "Router: Pool is not graduated yet");

        // Calculate fees and rewards
        _handleBuyFees(tweetPool, _dollarIn);

        // Calculate token amount out using AMM strategy and update balances
        (uint256 tokenAmountOut, uint256 newDollarReserve) = _handleBuyAMM(tweetPool, _poolId, _dollarIn);

        // Transfer tokens to buyer
        require(TweetToken(tweetPool.tweetToken()).transfer(msg.sender, tokenAmountOut), "Router: token transfer failed");
        
        // Update timestamp and emit event
        tweetPool.updateLastBuyTimestamp();
        emit TweetPoolBought(_poolId, msg.sender, tokenAmountOut);

        // Check for graduation
        if (newDollarReserve >= configManager.GRADUATE_THRESHOLD()) {
            _handleGraduation(tweetPool, newDollarReserve);
        }
    }

    function _handleBuyFees(TweetPool tweetPool, uint256 dollarAmount) private {
        (uint256 tradeFee, uint256 creatorShare, uint256 deployerShare, uint256 platformShare) = feeManager.getFeeInfo(tweetPool.virtualDollarReserve());
        uint256 fees = dollarAmount * tradeFee / 10_000;

        uint256 platformFeeAmount = fees * platformShare / 10_000;
        uint256 creatorFeeAmount = fees * creatorShare / 10_000;
        uint256 deployerFeeAmount = fees * deployerShare / 10_000;
        uint256 rewardFeeAmount = creatorFeeAmount + deployerFeeAmount;

        IERC20(configManager.susdToken()).transfer(configManager.platformWallet(), platformFeeAmount);   
        IERC20(configManager.susdToken()).transfer(address(rewardManager), rewardFeeAmount);
        rewardManager.depositRewards(
            tweetPool.tweetUserId(),
            creatorFeeAmount,
            tweetPool.deployerAddress(),
            deployerFeeAmount
        );
    }

    function _handleBuyAMM(TweetPool tweetPool, uint256 _poolId, uint256 dollarAmount) private returns (uint256 tokenAmountOut, uint256 newVirtualDollarReserve) {
        (uint256 fees, uint256 _tokenAmountOut, uint256 _newVirtualDollarReserve, uint256 newTweetTokenReserve) = viewBuyOrder(_poolId, dollarAmount);
        tweetPool.updateBalances(_newVirtualDollarReserve, newTweetTokenReserve);
        return (_tokenAmountOut, _newVirtualDollarReserve);
    }

    function _handleGraduation(TweetPool tweetPool, uint256 newVirtualDollarReserve) private {
        tweetPool.setGraduated();
        uint256 graduatePoolDollar = newVirtualDollarReserve - 10_000 * 1e6;
        uint256 graduatePoolToken = tweetPool.tweetTokenReserve();
        MigratorManager migratorManager = MigratorManager(configManager.migratorManager());
        IERC20(tweetPool.tweetToken()).transfer(address(migratorManager), graduatePoolToken);
        IERC20(configManager.susdToken()).transfer(address(migratorManager), graduatePoolDollar);
        emit TweetPoolGraduated(poolId);
    }

    function sellTweetToken(
        uint256 _poolId, 
        uint256 _amountIn
    ) external {
        address tweetPoolAddress = poolIdToTweetPool[_poolId];
        TweetPool tweetPool = TweetPool(tweetPoolAddress);
        TweetToken tweetToken = TweetToken(tweetPool.tweetToken());

        require(tweetToken.balanceOf(msg.sender) >= _amountIn, "Router: insufficient tweet token balance");
        require(tweetToken.allowance(msg.sender, address(this)) >= _amountIn, "Router: insufficient allowance");
        require(tweetPool.isGraduated() == false, "Router: Pool is not graduated yet");        

        // Handle fees and AMM calculations
        (uint256 dollarAmountOut, uint256 newVirtualDollarReserve) = _handleSellAMM(tweetPool, _poolId, _amountIn);
        require(dollarAmountOut >= configManager.minPurchaseAmount(), "Router: Insufficient output amount");
        
        // Transfer tokens from seller
        require(tweetToken.transferFrom(msg.sender, address(this), _amountIn), "Router: tweet token transfer failed");

        // Handle fees and rewards
        _handleSellFees(tweetPool, dollarAmountOut);

        // Transfer SUSD to seller
        require(susdToken.transfer(msg.sender, dollarAmountOut), "Router: SUSD transfer failed");

        // Update timestamp and emit event
        tweetPool.updateLastSellTimestamp();        
        emit TweetPoolSold(_poolId, msg.sender, dollarAmountOut);
    }

    function _handleSellFees(TweetPool tweetPool, uint256 dollarAmount) private {
        (uint256 tradeFee, uint256 creatorShare, uint256 deployerShare, uint256 platformShare) = feeManager.getFeeInfo(tweetPool.virtualDollarReserve());
        uint256 fees = dollarAmount * tradeFee / 10_000;

        uint256 platformFeeAmount = fees * platformShare / 10_000;
        uint256 creatorFeeAmount = fees * creatorShare / 10_000;
        uint256 deployerFeeAmount = fees * deployerShare / 10_000;
        uint256 rewardFeeAmount = creatorFeeAmount + deployerFeeAmount;

        IERC20(configManager.susdToken()).transfer(configManager.platformWallet(), platformFeeAmount);   
        IERC20(configManager.susdToken()).transfer(address(rewardManager), rewardFeeAmount);
        rewardManager.depositRewards(
            tweetPool.tweetUserId(),
            creatorFeeAmount,
            tweetPool.deployerAddress(),
            deployerFeeAmount
        );        
    }

    function _handleSellAMM(TweetPool tweetPool, uint256 _poolId, uint256 tokenAmount) private returns (uint256 dollarAmountOut, uint256 newVirtualDollarReserve) {
        (uint256 fees, uint256 _dollarAmountOut, uint256 _newVirtualDollarReserve, uint256 newTweetTokenReserve) = viewSellOrder(_poolId, tokenAmount);
        tweetPool.updateBalances(_newVirtualDollarReserve, newTweetTokenReserve);
        return (_dollarAmountOut, _newVirtualDollarReserve);
    }

    // Admin functions
    function setConfigAddress(address _configAddress) external onlyOwner {
        configManager = ConfigManager(_configAddress);
    }
}

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

import "@openzeppelin/contracts/access/Ownable.sol";
import "./ConfigManager.sol";

contract TweetPool is Ownable {

    // State variables
    uint256 public virtualDollarReserve;
    uint256 public tweetTokenReserve;
    
    uint256 public createdTimestamp;
    uint256 public lastBuyTimestamp;
    uint256 public lastSellTimestamp;
    
    bool public isGraduated;
    
    string public tweetId;    
    string public tweetUserId;

    address public tweetToken;
    address public routingContract;    
    address public deployerAddress;

    uint256 public inactivityThreshold;

    ConfigManager public configManager;

    uint256 public constant START_THRESHOLD = 10_000 * 1e6;    // 10,000 SUSD
    uint256 public constant FAMOUS_THRESHOLD = 20_000 * 1e6;  // 20,000 SUSD
    uint256 public constant VIRAL_THRESHOLD = 40_000 * 1e6;   // 40,000 SUSD
    uint256 public constant GRADUATE_THRESHOLD = 80_000 * 1e6; // 80,000 SUSD
    uint256 public constant K = (10_0000 * 1e6) * (1_000_000 * 1e18);

    // Events
    event BalancesUpdated(uint256 virtualDollarReserve, uint256 tweetTokenReserve);
    event InactivityThresholdUpdated(uint256 inactivityThreshold);
    event GraduationStatusUpdated(bool isGraduated);

    // Modifiers
    modifier onlyRoutingContract() {
        require(msg.sender == routingContract, "VirtualPool: caller is not the routing contract");
        _;
    }

    constructor(
        string memory _tweetId,
        address _tweetToken,
        address _routingContract,
        string memory _tweetUserId,
        address _deployerAddress,
        uint256 _inactivityThreshold,
        address _configAddress
    ) Ownable(msg.sender) {
        tweetId = _tweetId;
        tweetToken = _tweetToken;
        routingContract = _routingContract;
        tweetUserId = _tweetUserId;
        deployerAddress = _deployerAddress;

        configManager = ConfigManager(_configAddress);

        inactivityThreshold = _inactivityThreshold * 1 hours;
        lastBuyTimestamp = 0;
        lastSellTimestamp = 0;
        createdTimestamp = block.timestamp;
        isGraduated = false;
    }

    function updateBalances(uint256 _newSUSD, uint256 _newTweetToken) external onlyRoutingContract {
        virtualDollarReserve = _newSUSD;
        tweetTokenReserve = _newTweetToken;        
        emit BalancesUpdated(virtualDollarReserve, tweetTokenReserve);
    }

    function getBalances() external view returns (uint256, uint256) {
        return (virtualDollarReserve, tweetTokenReserve);
    }

    function getTier() external view returns (uint256) {
        if (tweetTokenReserve >= GRADUATE_THRESHOLD) return 3; // Graduate
        if (tweetTokenReserve >= VIRAL_THRESHOLD) return 2;    // Viral
        if (tweetTokenReserve >= FAMOUS_THRESHOLD) return 1;   // Famous
        return 0; // Start
    }

    function updateLastBuyTimestamp() external onlyRoutingContract {
        lastBuyTimestamp = block.timestamp;
    }

    function updateLastSellTimestamp() external onlyRoutingContract {
        lastSellTimestamp = block.timestamp;
    }    

    function isJackpotEligible() external view returns (bool) {
        // Pool is eligible if it's inactive (no buys for inactivityThreshold)
        // or if it's in the Start tier
        return (block.timestamp - lastBuyTimestamp >= inactivityThreshold) || 
               (tweetTokenReserve < START_THRESHOLD);
    }

    function setGraduated() external onlyRoutingContract {        
        isGraduated = true;
    }

}

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

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";

contract TweetToken is ERC20, ERC20Burnable {
    // State variables
    string public tweetId;
    string public tweetUserId;

    constructor(string memory _tweetId, string memory _tweetUserId) ERC20(string.concat("Tweet Token -", _tweetId), string.concat("TT-", _tweetId)) {
        tweetId = _tweetId;
        tweetUserId = _tweetUserId;
        _mint(msg.sender, 1_000_000 * 10**18); // Initial supply of 1M tokens
    }
}

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

Context size (optional):