YgmiRouter

Description:

Decentralized Finance (DeFi) protocol contract providing Swap, Liquidity, Factory functionality.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "YGMI/YgmiRouter.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

/* ---------------------------------------------------------- */
/* -------------------- Clones Library ---------------------- */
/* ---------------------------------------------------------- */
library Clones {
    function cloneToken(
        address implementation,
        bytes32 salt
    ) internal returns (address result) {
        bytes20 targetBytes = bytes20(implementation);
        assembly {
            let clone := mload(0x40)
            mstore(
                clone,
                0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000
            )
            mstore(add(clone, 0x14), targetBytes)
            mstore(
                add(clone, 0x28),
                0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000
            )
            result := create2(0, clone, 0x37, salt)
        }
        require(result != address(0), "CREATE2 failed");
    }
}

abstract contract ReentrancyGuard {
    uint256 internal _NOT_ENTERED = 1;
    uint256 internal _ENTERED = 2;
    uint256 internal _status = _NOT_ENTERED;

    modifier nonReentrant() {
        require(_status == _NOT_ENTERED, "REENTRANCY");
        _status = _ENTERED;
        _;
        _status = _NOT_ENTERED;
    }
}

/* ---------------------------------------------------------- */
/* -------------------- Ygmi Router ------------------------ */
/* ---------------------------------------------------------- */
contract YgmiRouter is ReentrancyGuard {
    using Clones for address;

    address public ownerEOA;
    address private launcher;
    address public tokenImplementation;
    address private YgmiPoolQuoter;

    address public paymentToken; // YGMI

    address public uniswapV2Router = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
    address public uniswapV3Router = 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45;
    address private uniswapQuoter = 0x5e55C9e631FAE526cd4B0526C4818D6e0a9eF0e3;

    mapping(address => uint256) public deployerNonces;
    mapping(address => uint256) public tokenLaunchTime;

    bool private paused = false;
    uint8 private burn_status = 0;

    mapping(address => bool) public collectionWhitelisted; // whitelist collections
    mapping(address => bool) public collectionHasLaunched; // tracks if a collection already has a token
    uint256 public tokenLaunchCooldown = 15 minutes; // configurable cooldown
    uint256 public lastTokenLaunchTime;

    uint256 public tokenLiquidityAmount = 18_000_000 * 1e18; // min liquidity amount
    uint256 public paymentLiquidityAmount = 25_000 * 1e18;
    uint256 public launchFee = 1000 * 1e18;
    mapping(address => bool) private _whitelistedTargets;

    uint256 public constant START_FEE = 90; // 90%
    uint256 public constant MIN_FEE = 10; // 10%
    uint256 public constant DECAY_PER_MIN = 1; // 1% per minute
    uint256 public constant ONE_MINUTE = 60;

    event TokenSwapped(
        address indexed token,
        address indexed user,
        uint256 feePercent
    );
    event FeeConvertedToETH(uint256 paymentTokenAmount, uint256 ethReceived);

    struct LaunchVars {
        uint256 feeAmount;
        uint256 swapAmount;
        uint256 tokensToBuy;
    }

    constructor(
        address _quoter,
        address _owner,
        address _paymentToken,
        address _launcher
    ) {
        ownerEOA = _owner;
        YgmiPoolQuoter = _quoter;
        paymentToken = _paymentToken;
        launcher = _launcher;
    }

    /* -------------------- Modifiers -------------------- */
    modifier isPaused() {
        require(!paused, "Security pause");
        _;
    }
    modifier onlyOwner() {
        require(msg.sender == ownerEOA, "Caller not allowed");
        _;
    }
    modifier onlyDeployer() {
        require(msg.sender == launcher, "Caller not allowed");
        _;
    }
    modifier ensure(uint256 deadline) {
        require(deadline >= block.timestamp, "ROUTER: EXPIRED");
        _;
    }

    /* -------------------- Admin -------------------- */
    function pause(bool _status_) external onlyOwner {
        paused = _status_;
    }

    function setTokenImplementation(
        address _implementation
    ) external onlyOwner {
        require(_implementation != address(0), "Invalid address");
        tokenImplementation = _implementation;
    }

    function setPaymentToken(address _paymentToken) external onlyOwner {
        paymentToken = _paymentToken;
    }

    function setLiquidityAmounts(
        uint256 _paymentAmount,
        uint256 _tokenAmount
    ) external onlyOwner {
        require(_paymentAmount > 0 && _tokenAmount > 0, "invalid amount");
        paymentLiquidityAmount = _paymentAmount;
        tokenLiquidityAmount = _tokenAmount;
    }

    function setLaunchFee(uint256 newFee) external onlyOwner {
        launchFee = newFee;
    }

    /* -------------------- Dynamic Fee -------------------- */
    function _currentFee(address token) internal view returns (uint256) {
        uint256 launchTime = tokenLaunchTime[token];
        if (launchTime == 0) return START_FEE;
        uint256 minutesSince = (block.timestamp - launchTime) / ONE_MINUTE;
        if (minutesSince >= (START_FEE - MIN_FEE)) return MIN_FEE;
        return START_FEE - (minutesSince * DECAY_PER_MIN);
    }

    /* -------------------- Token Launch -------------------- */

    function launchToken(
        bytes[] calldata tokenData,
        address collection
    ) external payable isPaused nonReentrant {
        require(tokenData.length == 2, "Invalid tokenData");
        require(
            collectionWhitelisted[collection],
            "Collection not whitelisted"
        );
        require(
            !collectionHasLaunched[collection],
            "Collection already has a token"
        );
        require(
            block.timestamp >= lastTokenLaunchTime + tokenLaunchCooldown,
            "Token launch cooldown active"
        );

        if (launchFee > 0) {
            require(
                IERC20token(paymentToken).transferFrom(
                    msg.sender,
                    0x000000000000000000000000000000000000dEaD,
                    launchFee
                ),
                "PAY: transferFrom failed"
            );
        }

        uint256 nonce = deployerNonces[msg.sender]++;
        bytes32 salt = keccak256(abi.encode(msg.sender, nonce));

        string memory nameStr = string(tokenData[0]);
        string memory symbolStr = string(tokenData[1]);

        address token = _deployAndInitializeToken(
            tokenData[0],
            tokenData[1],
            collection,
            salt
        );

        tokenLaunchTime[token] = block.timestamp;
        lastTokenLaunchTime = block.timestamp;
        collectionHasLaunched[collection] = true;

        IPoolQuoter(YgmiPoolQuoter).initializeYgmiPool(
            token,
            0,
            0,
            msg.sender,
            collection,
            nameStr,
            symbolStr
        );
    }

    function _deployAndInitializeToken(
        bytes calldata nameBytes,
        bytes calldata symbolBytes,
        address collection,
        bytes32 salt
    ) internal returns (address token) {
        token = tokenImplementation.cloneToken(salt);

        TokenInterface(token).initializeYgmiToken(
            nameBytes,
            symbolBytes,
            msg.sender,
            10000000000000000000000, // TODO: initial supply 10,000 tokens with 18 decimals
            address(this),
            collection
        );
    }

    /* -------------------- Swaps -------------------- */
    function swapExactPAYForTokens(
        address tokenToBuy,
        uint256 paymentAmountIn,
        uint256 deadline,
        uint256 minTokenAmountOut
    ) external isPaused ensure(deadline) nonReentrant returns (bool) {
        require(paymentAmountIn > 0, "Need payment");
        require(
            IERC20token(paymentToken).transferFrom(
                msg.sender,
                address(this),
                paymentAmountIn
            ),
            "PAY: transferFrom failed"
        );

        uint256 feePercent = _currentFee(tokenToBuy);
        uint256 feeAmount = (paymentAmountIn * feePercent) / 100;
        uint256 net = paymentAmountIn - feeAmount;

        _swapPaymentTokenToETH(feeAmount, tokenToBuy);

        uint256 tokenAmountOut = IPoolQuoter(YgmiPoolQuoter).quoteTokenAmount(
            net,
            tokenToBuy
        );
        IPoolQuoter(YgmiPoolQuoter).swap(
            tokenToBuy,
            0,
            tokenAmountOut,
            0,
            msg.sender,
            msg.sender,
            net,
            minTokenAmountOut
        );

        emit TokenSwapped(tokenToBuy, msg.sender, feePercent);
        return true;
    }

    function swapExactTokensForPAY(
        uint256 amountIn,
        address tokenToSell,
        uint256 deadline,
        uint256 minPayAmountOut
    ) external isPaused ensure(deadline) nonReentrant returns (bool) {
        require(amountIn > 0, "Need sell amount");

        uint256 payAmountOut = IPoolQuoter(YgmiPoolQuoter).quoteETHAmount(
            amountIn,
            tokenToSell
        );

        IPoolQuoter(YgmiPoolQuoter).swap(
            tokenToSell,
            amountIn,
            0,
            payAmountOut,
            msg.sender,
            msg.sender,
            0,
            minPayAmountOut
        );

        return true;
    }

    /* -------------------- Quoter callback -------------------- */
    function swapNow(
        address from,
        address tokenToSend,
        address to,
        uint256 value,
        uint256 minAmountOut,
        uint256 refund,
        address tokenTraded
    ) public returns (bool) {
        require(msg.sender == YgmiPoolQuoter, "ROUTER: NOT_ALLOWED");

        if (tokenToSend == paymentToken) {
            //sell
            uint256 feePercent = _currentFee(tokenTraded);
            uint256 feeAmount = (value * feePercent) / 100;
            uint256 finalValue = value - feeAmount;

            _swapPaymentTokenToETH(feeAmount, tokenTraded);
            require(
                IERC20token(paymentToken).transfer(to, finalValue),
                "pay xfer"
            );
        } else {
            //buy
            require(value >= minAmountOut, "ROUTER: MinAmountOut");
            if (from == address(this)) {
                require(
                    IERC20token(tokenToSend).transfer(to, value),
                    "token xfer"
                );
            } else {
                require(
                    IERC20token(tokenToSend).transferFrom(from, to, value),
                    "token xferFrom"
                );
            }
        }
        return true;
    }

    // -------------------- Swap fees to ETH (V3) --------------------
    // Splits paymentTokenAmount: 1% -> owner, 10% -> burn, 89% -> swap to ETH
    function _swapPaymentTokenToETH(
        uint256 paymentTokenAmount,
        address tokenTraded
    ) internal {
        if (paymentTokenAmount == 0) return;
        IUniswapV3Router swapRouter = IUniswapV3Router(uniswapV3Router);
        uint24 poolFee = 10000;
        address wethAddr = swapRouter.WETH9();
        uint256 ownerFee = (paymentTokenAmount * 1) / 100;
        uint256 burnAmount = (paymentTokenAmount * 10) / 100;
        uint256 swapAmount = paymentTokenAmount - ownerFee - burnAmount;
        if (ownerFee > 0) {
            require(
                IERC20token(paymentToken).approve(
                    address(swapRouter),
                    ownerFee
                ),
                "approve failed"
            );
            IUniswapV3Router.ExactInputSingleParams
                memory params0 = IUniswapV3Router.ExactInputSingleParams({
                    tokenIn: paymentToken,
                    tokenOut: swapRouter.WETH9(),
                    fee: poolFee,
                    recipient: address(this),
                    amountIn: ownerFee,
                    amountOutMinimum: 0,
                    sqrtPriceLimitX96: 0
                });

            uint256 amountOutWETH0 = swapRouter.exactInputSingle(params0);
            IWETH9(wethAddr).withdraw(amountOutWETH0);
            (bool success, ) = ownerEOA.call{value: amountOutWETH0}("");
            require(success, "Transfer to owner failed");
        }
        if (burnAmount > 0) {
            require(
                IERC20token(paymentToken).transfer(
                    0x000000000000000000000000000000000000dEaD,
                    burnAmount
                ),
                "burn transfer failed"
            );
        }

        if (swapAmount == 0) {
            emit FeeConvertedToETH(burnAmount, 0);
            return;
        }
        require(
            IERC20token(paymentToken).approve(address(swapRouter), swapAmount),
            "approve failed"
        );
        IUniswapV3Router.ExactInputSingleParams memory params = IUniswapV3Router
            .ExactInputSingleParams({
                tokenIn: paymentToken,
                tokenOut: swapRouter.WETH9(),
                fee: poolFee,
                recipient: address(this),
                amountIn: swapAmount,
                amountOutMinimum: 0,
                sqrtPriceLimitX96: 0
            });
        uint256 amountOutWETH = swapRouter.exactInputSingle(params);
        IWETH9(wethAddr).withdraw(amountOutWETH);
        IERC20token(tokenTraded).addFees{value: amountOutWETH}();
        emit FeeConvertedToETH(burnAmount, swapAmount);
    }

    /* -------------------- Listing helpers -------------------- */
    function listTokenOnUniswapV2(
        address _tokenToGraduate
    ) external isPaused nonReentrant onlyDeployer returns (address, address) {
        (uint256 collected, bool alreadyAdded) = IPoolQuoter(YgmiPoolQuoter)
            .poolInfo(_tokenToGraduate);
        require(!alreadyAdded, "liq already added");
        uint256 liqPayment = paymentLiquidityAmount;
        uint256 liqTokens = IERC20token(_tokenToGraduate).balanceOf(
            address(this)
        );
        require(liqTokens >= tokenLiquidityAmount, "liq tokens low");
        require(
            IERC20token(paymentToken).balanceOf(address(this)) >= liqPayment,
            "insufficient paymentToken"
        );
        require(
            IERC20token(_tokenToGraduate).balanceOf(address(this)) >= liqTokens,
            "insufficient token"
        );
        IUniswapV2Router router = IUniswapV2Router(uniswapV2Router);
        address pair = IUniswapV2Factory(router.factory()).getPair(
            paymentToken,
            _tokenToGraduate
        );
        if (pair == address(0)) {
            pair = IUniswapV2Factory(router.factory()).createPair(
                paymentToken,
                _tokenToGraduate
            );
        } else {
            revert("pool already exist");
        }
        IERC20token(paymentToken).approve(address(router), liqPayment);
        IERC20token(_tokenToGraduate).approve(address(router), liqTokens);
        router.addLiquidity(
            paymentToken,
            _tokenToGraduate,
            liqPayment,
            liqTokens,
            0,
            0,
            address(0x000000000000000000000000000000000000dEaD),
            block.timestamp + 1 minutes
        );
        IPoolQuoter(YgmiPoolQuoter).setLiquidityAddedToUniswap(
            _tokenToGraduate
        );
        IERC20token(_tokenToGraduate).setLaunched(
            1,
            pair,
            tokenLaunchTime[_tokenToGraduate]
        );
        if (collected > liqPayment) {
            uint256 leftover = collected - liqPayment;
            uint256 bal = IERC20token(paymentToken).balanceOf(address(this));
            if (bal >= leftover) {
                IERC20token(paymentToken).transfer(ownerEOA, leftover);
            } else {
                IERC20token(paymentToken).transfer(ownerEOA, bal);
            }
        }
        return (_tokenToGraduate, pair);
    }

    function isTokenTradable(address token) external view returns (bool) {
        (, bool added) = IPoolQuoter(YgmiPoolQuoter).poolInfo(token);
        return added;
    }

    function setStatus(uint8 _status_) external onlyOwner {
        require(_status_ == 0 || _status_ == 1, "Invalid status");
        burn_status = _status_;
    }

    function status() external view returns (uint8) {
        return burn_status;
    }

    function setCollectionWhitelist(
        address collection,
        bool _status_
    ) external onlyOwner {
        collectionWhitelisted[collection] = _status_;
    }

    // NEW: batch setter
    function setCollectionWhitelist(
        address[] calldata collections,
        bool _status_
    ) external onlyOwner {
        uint256 len = collections.length;
        for (uint256 i = 0; i < len; i++) {
            address c = collections[i];
            collectionWhitelisted[c] = _status_;
        }
    }

    function setTokenLaunchCooldown(uint256 _seconds) external onlyOwner {
        tokenLaunchCooldown = _seconds;
    }

    uint256 public slippageBps = 9000; // default 3% (300 basis points)

    function setSlippageBps(uint256 _bps) external onlyOwner {
        require(_bps <= 10_000, "Invalid slippage"); // max 100%
        slippageBps = _bps;
    }

    // Set a single target
    function setWhitelistedTarget(
        address target,
        bool _status_
    ) external onlyOwner {
        _whitelistedTargets[target] = _status_;
    }

    // Batch whitelist multiple targets
    function setWhitelistedTargets(
        address[] calldata targets,
        bool _status_
    ) external onlyOwner {
        for (uint256 i = 0; i < targets.length; i++) {
            _whitelistedTargets[targets[i]] = _status_;
        }
    }

    // Quoter address getter/setter
    function quoter() external view returns (address) {
        return uniswapQuoter;
    }

    function setQuoter(address newQuoter) external onlyOwner {
        require(newQuoter != address(0), "Invalid address");
        uniswapQuoter = newQuoter;
    }

    // View whitelist status
    function whitelistedTargets(address target) external view returns (bool) {
        return _whitelistedTargets[target];
    }

    bool private _useQuoteMinAmountOut = false;

    // TWAP quote min flag
    function useQuoteMinAmountOut() external view returns (bool) {
        return _useQuoteMinAmountOut;
    }

    function setUseQuoteMinAmountOut(bool _status_) external onlyOwner {
        _useQuoteMinAmountOut = _status_;
    }

    receive() external payable {}

    fallback() external payable {}
}

/* -------------------- Interfaces -------------------- */

interface IUniswapV2Router {
    function factory() external pure returns (address);

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

interface IERC20token {
    function balanceOf(address account) external view returns (uint256);

    function addFees() external payable;

    function approve(address spender, uint256 amount) external returns (bool);

    function transferFrom(
        address from,
        address to,
        uint value
    ) external returns (bool);

    function transfer(address to, uint value) external returns (bool);

    function setLaunched(uint8 _launched, address pair, uint256 time) external;

    function setUniswapPool(address _pool) external;
}

interface IPoolQuoter {
    function swap(
        address token,
        uint256 amountOftokensToSell,
        uint256 amountTokenOut,
        uint256 amountETHOut,
        address from,
        address to,
        uint256 ethValue,
        uint256 minAmountOut
    ) external payable;

    function quoteTokenAmount(
        uint256 ethAmount,
        address tokenAddress
    ) external view returns (uint256 tokenAmountOut);

    function quoteETHAmount(
        uint256 tokensToSell,
        address tokenAddress
    ) external view returns (uint256 ethAmountOut);

    function initializeYgmiPool(
        address token,
        uint256 tokenAmount,
        uint256 ethAmount,
        address caller,
        address collection,
        string calldata name,
        string calldata symbol
    ) external;

    function initialTokensForEth(
        uint256 ethAmount
    ) external pure returns (uint256 tokens);

    function poolInfo(
        address _token
    ) external view returns (uint256 ethAmount, bool uniswapAdded);

    function setLiquidityAddedToUniswap(address _token) external;
}

interface TokenInterface {
    function initializeYgmiToken(
        bytes calldata nameBytes,
        bytes calldata symbolBytes,
        address deployer,
        uint256 buyAmount,
        address router,
        address _collection
    ) external;
}

interface IUniswapV3Router {
    struct ExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 amountIn;
        uint256 amountOutMinimum;
        uint160 sqrtPriceLimitX96;
    }

    function exactInputSingle(
        ExactInputSingleParams calldata params
    ) external payable returns (uint256 amountOut);

    function WETH9() external pure returns (address);
}

interface IUniswapV2Factory {
    function createPair(
        address tokenA,
        address tokenB
    ) external returns (address pair);

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

interface IWETH9 {
    function deposit() external payable;

    function withdraw(uint256) external;

    function approve(address guy, uint256 wad) external returns (bool);

    function balanceOf(address) external view returns (uint256);
}"
    }
  },
  "settings": {
    "optimizer": {
      "enabled": true,
      "runs": 200
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "remappings": []
  }
}}

Tags:
DeFi, Swap, Liquidity, Factory|addr:0x904033f6708fa4e76c3d2064cf5bbaeeaae360c0|verified:true|block:23671109|tx:0x1b879d8b0f6500a4568a18c5a59ee9e4809e76408aa80d43738fb6ea663305fe|first_check:1761641849

Submitted on: 2025-10-28 09:57:31

Comments

Log in to comment.

No comments yet.