Description:
Proxy contract enabling upgradeable smart contract patterns. Delegates calls to an implementation contract.
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;
import "./YGMIVaults.sol";
/* ---------------------------------------------------------- */
/* -------------------- 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");
}
}
/* ---------------------------------------------------------- */
/* -------------------- 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
) {
YGMIVaults tokenImpl = new YGMIVaults();
tokenImplementation = address(tokenImpl);
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 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),
deadline: block.timestamp + 10 minutes,
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),
deadline: block.timestamp + 10 minutes,
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;
}
// Admin functions
function setCollectionWhitelist(
address collection,
bool _status_
) external onlyOwner {
collectionWhitelisted[collection] = _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 deadline;
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);
}
"
},
"YGMI/YGMIVaults.sol": {
"content": "// SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.4;
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return payable(msg.sender);
}
function _msgData() internal view virtual returns (bytes memory) {
return msg.data;
}
}
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(
address recipient,
uint256 amount
) external returns (bool);
function allowance(
address owner,
address spender
) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
function sub(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
function div(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
function mod(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
library Address {
function isContract(address account) internal view returns (bool) {
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
assembly {
codehash := extcodehash(account)
}
return (codehash != accountHash && codehash != 0x0);
}
function sendValue(address payable recipient, uint256 amount) internal {
require(
address(this).balance >= amount,
"Address: insufficient balance"
);
(bool success, ) = recipient.call{value: amount}("");
require(
success,
"Address: unable to send value, recipient may have reverted"
);
}
function functionCall(
address target,
bytes memory data
) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return _functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return
functionCallWithValue(
target,
data,
value,
"Address: low-level call with value failed"
);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(
address(this).balance >= value,
"Address: insufficient balance for call"
);
return _functionCallWithValue(target, data, value, errorMessage);
}
function _functionCallWithValue(
address target,
bytes memory data,
uint256 weiValue,
string memory errorMessage
) private returns (bytes memory) {
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: weiValue}(
data
);
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
contract Ownable is Context {
address internal _owner;
address private _previousOwner;
uint256 private _lockTime;
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
function owner() public view returns (address) {
return _owner;
}
modifier onlyOwner() {
require(_owner == _msgSender(), "Ownable: caller is not the owner");
_;
}
function waiveOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(
newOwner != address(0),
"Ownable: new owner is the zero address"
);
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
function unlock() public virtual {
require(
_previousOwner == msg.sender,
"You don't have permission to unlock"
);
require(block.timestamp > _lockTime, "Contract is locked until 7 days");
emit OwnershipTransferred(_owner, _previousOwner);
_owner = _previousOwner;
}
}
interface IUniswapV2Router02 {
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
}
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;
}
}
contract YGMIVaults is Context, IERC20, Ownable, ReentrancyGuard {
using SafeMath for uint256;
using Address for address;
error NFTNotForSale();
error NFTPriceTooLow();
error NotEnoughEth();
error AlreadyNFTOwner();
error NeedToBuyNFT();
error NotNFTOwner();
error ExternalCallFailed(bytes reason);
error InvalidTarget();
error InvalidCallData();
error NoETHToTwap();
error TwapDelayNotMet();
bytes20 internal _name;
bytes12 internal _symbol;
mapping(uint256 => uint256) public nftForSale;
mapping(uint256 => uint256) public nftPurchasePrice;
mapping(uint256 => uint256) public nftPurchaseTime;
// constants for multiplier logic
// Multiplier logic parameters (settable in initialize)
uint256 public INITIAL_MULTIPLIER_BPS; // e.g., 1200 = 1.2x
uint256 public FINAL_MULTIPLIER_BPS; // e.g., 1000 = 1.0x
uint256 public DAILY_DECAY_BPS; // e.g., 10 = 1% per day
uint256 public SECONDS_PER_DAY; // e.g., 86400
uint8 private launchedToUniswap;
address public uniswapPool;
uint256 public ethToTwap;
uint256 public currentFees;
uint256 public twapIncrement;
uint256 public twapDelayInBlocks;
uint256 public lastTwapBlock;
mapping(address => uint256) _balances;
mapping(address => mapping(address => uint256)) private _allowances;
mapping(address => bool) public isMarketPair;
// Add this at the top of your contract
event TargetWhitelisted(address indexed target, bool status);
function decimals() public pure virtual returns (uint8) {
return 18;
}
function deadAddress() public pure virtual returns (address) {
return 0x000000000000000000000000000000000000dEaD;
}
function totalSupply() public pure virtual returns (uint256) {
return 100_000_000 * 10 ** decimals();
}
IUniswapV2Router02 public uniswapV2Router;
bool inSwapAndLiquify;
bool public swapAndLiquifyEnabled;
bool public checkWalletLimit;
event SwapAndLiquifyEnabledUpdated(bool enabled);
event SwapAndLiquify(
uint256 tokensSwapped,
uint256 ethReceived,
uint256 tokensIntoLiqudity
);
event SwapETHForTokens(uint256 amountIn, address[] path);
event SwapTokensForETH(uint256 amountIn, address[] path);
event NFTBoughtByProtocol(
uint256 indexed tokenId,
uint256 purchasePrice,
uint256 listPrice
);
event NFTSoldByProtocol(
uint256 indexed tokenId,
uint256 price,
address buyer
);
modifier lockTheSwap() {
inSwapAndLiquify = true;
_;
inSwapAndLiquify = false;
}
modifier burn() {
if (IYgmiRouter(YGMIrouter).status() == 1) {
revert("YgmiRouter: burn");
}
_;
}
function ygmi_token() public view virtual returns (address) {
return 0xfC9Cf42a1a9c211da1C812989e2De83bA22e3B80;
}
function swapper() public view virtual returns (address) {
return 0x477Ca9EFE74c19cBf4DFA3e910B2b9c1E0ac4bA1;
}
// ???? Mainnet Uniswap Routers
function uniswapV3Router() public pure returns (address) {
return 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45;
}
function uniswapV2RouterAddress() public view virtual returns (address) {
return 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
}
function protocol() public view virtual returns (address) {
return 0xFA474E2d46cf1ACbb716eF31077587c74188b1B3;
}
function weth() public view virtual returns (address) {
return 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
}
function slippageBps() public view returns (uint256) {
return IYgmiRouter(YGMIrouter).slippageBps();
}
address public YGMIrouter;
bool internal initialized;
IERC721 public collection;
uint256 public START_FEE; // 90%
uint256 public MIN_FEE; // 10%
uint256 public DECAY_PER_MIN; // 1% per minute
uint256 public ONE_MINUTE;
function initializeYgmiToken(
bytes calldata nameBytes,
bytes calldata symbolBytes,
address deployer,
uint256 buyAmount,
address _router,
address _collection
) public {
require(!initialized, "already initialized");
require(nameBytes.length <= 20, "wrong name");
require(symbolBytes.length <= 12, "wrong symbol");
collection = IERC721(_collection);
YGMIrouter = _router;
_name = toBytes20(nameBytes);
_symbol = toBytes12(symbolBytes);
uint256 total = totalSupply();
if (buyAmount > 0) {
uint256 remaining = total - buyAmount;
_balances[deployer] = buyAmount;
_balances[_router] = remaining;
emit Transfer(address(0), deployer, buyAmount);
emit Transfer(address(0), _router, remaining);
} else {
_balances[_router] = total;
emit Transfer(address(0), _router, total);
}
IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(
uniswapV2RouterAddress()
);
uniswapV2Router = _uniswapV2Router;
_allowances[address(this)][address(uniswapV2Router)] = totalSupply();
swapAndLiquifyEnabled = true;
checkWalletLimit = true;
INITIAL_MULTIPLIER_BPS = 1100; // 1.1x
FINAL_MULTIPLIER_BPS = 1000; // 1.0x
DAILY_DECAY_BPS = 10; // 1% per day (10 bps)
SECONDS_PER_DAY = 86400; // 24 hours
twapIncrement = 1 ether;
twapDelayInBlocks = 1;
IERC20(weth()).approve(uniswapV3Router(), type(uint256).max);
_NOT_ENTERED = 1;
_ENTERED = 2;
_status = _NOT_ENTERED;
START_FEE = 90; // 90%
MIN_FEE = 10; // 10%
DECAY_PER_MIN = 1; // 1% per minute
ONE_MINUTE = 60;
emit OwnershipTransferred(msg.sender, address(0));
_owner = address(0);
initialized = true;
}
modifier onlyAuthorized() {
require(msg.sender == YGMIrouter, "YgmiToken: Not authorized");
_;
}
function setLaunched(
uint8 _launched,
address _pair,
uint256 time
) external onlyAuthorized {
require(_launched == 1, "Invalid state");
launchedToUniswap = _launched;
uniswapPool = _pair;
isMarketPair[address(_pair)] = true;
launchTime = time;
}
function toBytes20(
bytes memory input
) internal pure returns (bytes20 result) {
assembly {
result := mload(add(input, 32))
}
}
function toBytes12(
bytes memory input
) internal pure returns (bytes12 result) {
assembly {
result := mload(add(input, 32))
}
}
function name() public view returns (string memory) {
return string(abi.encodePacked(_name));
}
function symbol() public view returns (string memory) {
return string(abi.encodePacked(_symbol));
}
function balanceOf(address account) public view override returns (uint256) {
return _balances[account];
}
function allowance(
address owner,
address spender
) public view override returns (uint256) {
return _allowances[owner][spender];
}
function increaseAllowance(
address spender,
uint256 addedValue
) public virtual returns (bool) {
_approve(
_msgSender(),
spender,
_allowances[_msgSender()][spender].add(addedValue)
);
return true;
}
function decreaseAllowance(
address spender,
uint256 subtractedValue
) public virtual returns (bool) {
_approve(
_msgSender(),
spender,
_allowances[_msgSender()][spender].sub(
subtractedValue,
"ERC20: decreased allowance below zero"
)
);
return true;
}
function approve(
address spender,
uint256 amount
) public override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
function _approve(address owner, address spender, uint256 amount) private {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
function setSwapAndLiquifyEnabled(bool _enabled) public onlyOwner {
swapAndLiquifyEnabled = _enabled;
}
function getCirculatingSupply() public view returns (uint256) {
return totalSupply().sub(balanceOf(deadAddress()));
}
//to recieve ETH from uniswapV2Router when swaping
receive() external payable {}
error InvalidCollection();
/// @notice Handles receipt of NFTs (ERC721 receiver)
/// @dev Only accepts NFTs from the designated collection
/// @return The function selector to confirm receipt
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external view returns (bytes4) {
if (msg.sender != address(collection)) {
revert InvalidCollection();
}
return this.onERC721Received.selector;
}
function transfer(
address recipient,
uint256 amount
) public override returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
function transferFrom(
address sender,
address recipient,
uint256 amount
) public override returns (bool) {
_transfer(sender, recipient, amount);
_approve(
sender,
_msgSender(),
_allowances[sender][_msgSender()].sub(
amount,
"ERC20: transfer amount exceeds allowance"
)
);
return true;
}
function _transfer(
address sender,
address recipient,
uint256 amount
) private returns (bool) {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
// === RESTRICTION: only allow certain transfers before listing on Uniswap ===
// When not launched to Uniswap (launchedToUniswap != 1) we only allow:
// - any transfer initiated by the YGMI router (sender == YGMIrouter)
// - any transfer to the YGMI router (recipient == YGMIrouter)
// - transfers involving the Uniswap V2 router _only_ when YGMIrouter is the other party
// (this allows router interactions to/from YGMIrouter while still blocking random transfers)
if (launchedToUniswap != 1) {
address uni = uniswapV2RouterAddress();
bool allowed = false;
if (sender == YGMIrouter) {
allowed = true; // YGMI router can always send
} else if (recipient == YGMIrouter) {
allowed = true; // anyone can send _to_ the YGMI router (e.g. swaps into YGMIrouter)
} else if (sender == uni || recipient == uni) {
// if UniswapV2 router is involved, require the other party to be YGMIrouter
if (sender == YGMIrouter || recipient == YGMIrouter) {
allowed = true;
}
}
require(allowed, "Token not listed: transfers restricted");
}
// === end restriction ===
if (inSwapAndLiquify) {
return _basicTransfer(sender, recipient, amount);
} else {
uint256 contractTokenBalance = balanceOf(address(this));
bool overMinimumTokenBalance = true;
if (
contractTokenBalance > 0 &&
launchedToUniswap == 1 &&
overMinimumTokenBalance &&
!inSwapAndLiquify &&
!isMarketPair[sender] &&
swapAndLiquifyEnabled
) {
swapAndLiquify(contractTokenBalance);
}
_balances[sender] = _balances[sender].sub(
amount,
"Insufficient Balance"
);
uint256 finalAmount = takeFee(sender, recipient, amount);
_balances[recipient] = _balances[recipient].add(finalAmount);
emit Transfer(sender, recipient, finalAmount);
return true;
}
}
function _basicTransfer(
address sender,
address recipient,
uint256 amount
) internal returns (bool) {
_balances[sender] = _balances[sender].sub(
amount,
"Insufficient Balance"
);
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
return true;
}
function swapAndLiquify(uint256 tAmount) private lockTheSwap {
swapTokensForEth(tAmount);
}
function swapTokensForEth(uint256 tokenAmount) private {
address[] memory path = new address[](2);
path[0] = address(this);
path[1] = ygmi_token();
_approve(address(this), address(swapper()), type(uint256).max);
// Swap to ETH via YGMI and send to this contract
IERC20 ygmi = IERC20(ygmi_token());
uint256 beforeYGMI = ygmi.balanceOf(swapper());
uint256 beforeEth = address(this).balance;
// make the swap
uniswapV2Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
tokenAmount,
0,
path,
swapper(),
block.timestamp
);
uint256 ygmiReceived = ygmi.balanceOf(swapper()) - beforeYGMI;
IFeeSwapper(swapper()).swapTokenToETH(
ygmi_token(), // YGMI token address
weth(), // WETH token address
ygmiReceived, // amountIn YGMI from swap result
address(this) // recipient: receive ETH here
);
// 1% fee to protocol
uint256 ethReceived = address(this).balance - beforeEth;
if (ethReceived > 0) {
uint256 fee = ethReceived / 100; // 1%
if (fee > 0) {
// use Address.sendValue to forward ETH (reverts on failure)
Address.sendValue(payable(protocol()), fee);
}
}
emit SwapTokensForETH(tokenAmount, path);
}
function addFees() external payable nonReentrant {
require(
msg.sender == swapper() || msg.sender == YGMIrouter,
"Only swapper"
);
currentFees += msg.value;
}
function takeFee(
address sender,
address recipient,
uint256 amount
) internal returns (uint256) {
uint256 feeAmount = 0;
if (isMarketPair[sender]) {
feeAmount = amount.mul(_currentFee()).div(100);
} else if (isMarketPair[recipient]) {
feeAmount = amount.mul(_currentFee()).div(100);
}
if (feeAmount > 0) {
_balances[address(this)] = _balances[address(this)].add(feeAmount);
emit Transfer(sender, address(this), feeAmount);
}
return amount.sub(feeAmount);
}
function buyTargetNFT(
uint256 value,
bytes calldata data,
uint256 expectedId,
address target
) external nonReentrant {
if (address(this).balance < nftSaleBalance) revert NotEnoughEth();
uint256 availableEth = address(this).balance - nftSaleBalance;
if (value > availableEth) revert NotEnoughEth(); // <--- use adjusted balance
uint256 ethBefore = address(this).balance;
uint256 nftBefore = collection.balanceOf(address(this));
if (collection.ownerOf(expectedId) == address(this))
revert AlreadyNFTOwner();
if (!IYgmiRouter(YGMIrouter).whitelistedTargets(target))
revert InvalidTarget();
if (data.length < 4) revert InvalidCallData();
(bool success, bytes memory reason) = target.call{value: value}(data);
if (!success) revert ExternalCallFailed(reason);
uint256 nftAfter = collection.balanceOf(address(this));
if (nftAfter != nftBefore + 1) revert NeedToBuyNFT();
if (collection.ownerOf(expectedId) != address(this))
revert NotNFTOwner();
uint256 cost = ethBefore - address(this).balance;
currentFees -= cost;
nftPurchasePrice[expectedId] = cost;
nftPurchaseTime[expectedId] = block.timestamp;
uint256 salePrice = (cost * INITIAL_MULTIPLIER_BPS) / 1000;
nftForSale[expectedId] = salePrice;
emit NFTBoughtByProtocol(expectedId, cost, salePrice);
}
uint256 public nftSaleBalance; // total ETH from selling NFTs
function sellTargetNFT(uint256 tokenId) external payable nonReentrant {
if (collection.ownerOf(tokenId) != address(this)) revert NotNFTOwner();
uint256 currentSalePrice = getCurrentSalePrice(tokenId);
if (currentSalePrice == 0) revert NFTNotForSale();
if (msg.value < currentSalePrice) revert NFTPriceTooLow();
collection.transferFrom(address(this), msg.sender, tokenId);
delete nftForSale[tokenId];
delete nftPurchasePrice[tokenId];
delete nftPurchaseTime[tokenId];
ethToTwap += msg.value;
nftSaleBalance += msg.value;
emit NFTSoldByProtocol(tokenId, currentSalePrice, msg.sender);
}
function getEthBreakdown()
external
view
returns (uint256 total, uint256 fees, uint256 toTwap, uint256 saleBal)
{
return (address(this).balance, currentFees, ethToTwap, nftSaleBalance);
}
function getCurrentSalePrice(
uint256 tokenId
) public view returns (uint256) {
uint256 basePrice = nftPurchasePrice[tokenId];
uint256 purchaseTime = nftPurchaseTime[tokenId];
// NFT not recorded (never bought)
if (basePrice == 0 || purchaseTime == 0) return 0;
// Calculate days elapsed since purchase
uint256 daysElapsed = (block.timestamp - purchaseTime) /
SECONDS_PER_DAY;
// Each day reduces multiplier by 1% (10 basis points, since 1000 = 1.0x)
uint256 decay = daysElapsed * DAILY_DECAY_BPS;
// Compute current multiplier (start 1200 → down to 1000)
uint256 currentMultiplier;
if (decay >= (INITIAL_MULTIPLIER_BPS - FINAL_MULTIPLIER_BPS)) {
currentMultiplier = FINAL_MULTIPLIER_BPS;
} else {
currentMultiplier = INITIAL_MULTIPLIER_BPS - decay;
}
// Return current sale price
return (basePrice * currentMultiplier) / 1000;
}
function processTokenTwap() external nonReentrant burn {
if (ethToTwap == 0) revert NoETHToTwap();
if (block.number < lastTwapBlock + twapDelayInBlocks)
revert TwapDelayNotMet();
uint256 ethAmount = twapIncrement;
if (ethToTwap < twapIncrement) ethAmount = ethToTwap;
uint256 reward = (ethAmount * 5) / 1000; // 0.5% reward
uint256 swapAmount = ethAmount - reward;
ethToTwap -= ethAmount;
lastTwapBlock = block.number;
// --- Wrap ETH to WETH ---
IWETH(weth()).deposit{value: swapAmount}();
uint256 amountOutMin = 0;
// Check YGMIrouter setting for quoting minimum amount out
if (IYgmiRouter(YGMIrouter).useQuoteMinAmountOut()) {
address quoterAddress = IYgmiRouter(YGMIrouter).quoter();
uint256 quotedOut = IUniswapV3Quoter(quoterAddress)
.quoteExactInputSingle(
weth(), // tokenIn (WETH)
ygmi_token(), // tokenOut
10000, // fee tier
swapAmount, // amountIn
0 // sqrtPriceLimitX96
);
require(quotedOut > 0, "Invalid quote");
// Apply slippage from YGMIrouter
amountOutMin =
(quotedOut * IYgmiRouter(YGMIrouter).slippageBps()) /
10000;
}
// --- Swap WETH → YGMI on Uniswap V3 ---
IUniswapV3SwapRouter.ExactInputSingleParams memory params = IUniswapV3SwapRouter
.ExactInputSingleParams({
tokenIn: weth(),
tokenOut: ygmi_token(),
fee: 10000,
recipient: address(this), // keep YGMI in contract
deadline: block.timestamp,
amountIn: swapAmount,
amountOutMinimum: amountOutMin,
sqrtPriceLimitX96: 0
});
IUniswapV3SwapRouter(uniswapV3Router()).exactInputSingle(params);
// --- Buy and burn tokens ---
uint256 ygmiBalance = IERC20(ygmi_token()).balanceOf(address(this));
IERC20(ygmi_token()).approve(address(uniswapV2Router), ygmiBalance);
_buyAndBurnTokens(ygmiBalance);
// --- Transfer reward to caller ---
if (reward > 0) {
(bool success, ) = msg.sender.call{value: reward}("");
require(success, "Transfer failed.");
}
}
function _buyAndBurnTokens(uint256 amountIn) internal {
if (amountIn == 0) revert NoETHToTwap();
address[] memory path = new address[](2);
path[0] = ygmi_token();
path[1] = address(this);
uniswapV2Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
amountIn,
0,
path,
deadAddress(),
block.timestamp
);
}
uint256 private launchTime;
function _currentFee() internal view returns (uint256) {
uint256 lt = launchTime;
if (lt == 0) return START_FEE;
uint256 minutesSince = (block.timestamp - lt) / ONE_MINUTE;
if (minutesSince >= (START_FEE - MIN_FEE)) return MIN_FEE;
return START_FEE - (minutesSince * DECAY_PER_MIN);
}
}
interface IFeeSwapper {
function swapTokenToETH(
address ygmi,
address weth,
uint256 amountIn,
address recipient
) external;
}
interface IERC721 {
function balanceOf(address owner) external view returns (uint256);
function ownerOf(uint256 id) external view returns (address);
function transferFrom(address from, address to, uint256 id) external;
}
interface IUniswapV3Quoter {
function quoteExactInputSingle(
address tokenIn,
address tokenOut,
uint24 fee,
uint256 amountIn,
uint160 sqrtPriceLimitX96
) external returns (uint256 amountOut);
}
interface IUniswapV3SwapRouter {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
function exactInputSingle(
ExactInputSingleParams calldata params
) external payable returns (uint256 amountOut);
}
interface IWETH {
function deposit() external payable;
function withdraw(uint256) external;
function approve(address spender, uint256 amount) external returns (bool);
}
interface IYgmiRouter {
function status() external view returns (uint8);
function useQuoteMinAmountOut() external view returns (bool);
function quoter() external view returns (address);
function whitelistedTargets(address target) external view returns (bool);
function slippageBps() external view returns (uint256);
}
"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": []
}
}}
Submitted on: 2025-10-23 17:39:12
Comments
Log in to comment.
No comments yet.