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": []
}
}}
Submitted on: 2025-10-28 09:57:31
Comments
Log in to comment.
No comments yet.