pragma solidity ^0.7.1; interface IPancakeRouter01 { function factory() external pure returns (address); function WETH() 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); function addLiquidityETH( address token, uint amountTokenDesired, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) external payable returns (uint amountToken, uint amountETH, uint liquidity); function removeLiquidity( address tokenA, address tokenB, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline ) external returns (uint amountA, uint amountB); function removeLiquidityETH( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) external returns (uint amountToken, uint amountETH); function removeLiquidityWithPermit( address tokenA, address tokenB, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint amountA, uint amountB); function removeLiquidityETHWithPermit( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint amountToken, uint amountETH); function swapExactTokensForTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external returns (uint[] memory amounts); function swapTokensForExactTokens( uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline ) external returns (uint[] memory amounts); function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts); function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts); function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts); function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts); function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB); function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut); function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn); function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts); function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts); } interface IPancakeRouter02 is IPancakeRouter01 { function removeLiquidityETHSupportingFeeOnTransferTokens( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) external returns (uint amountETH); function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint amountETH); function swapExactTokensForTokensSupportingFeeOnTransferTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external; function swapExactETHForTokensSupportingFeeOnTransferTokens( uint amountOutMin, address[] calldata path, address to, uint deadline ) external payable; function swapExactTokensForETHSupportingFeeOnTransferTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external; } 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 mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a / b; return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a); return a - b; } function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a); return c; } } contract LiquidityLock { address public owner; address public deployer; uint256 public unlockTime; uint256 public lockupDays; modifier onlyDeployer() { require(deployer == msg.sender, "Caller is not the deployer contract"); _; } constructor(address _owner, uint256 _lockupDays) { lockupDays = _lockupDays; owner = _owner; } function initializeAssosiation(address _deployer) public { deployer = _deployer; // The assosiated sale contract } function initiateLockup() public onlyDeployer { require(unlockTime == 0, "Tokens already locked"); unlockTime = block.timestamp + lockupDays * 1 days; } function releaseLPTokens(address _tokenAddress) public { // The token address of the LP tokens from UNISWAP require(block.timestamp >= unlockTime, "Tokens not unlocked yet"); IERC20 _token = IERC20(_tokenAddress); uint256 amount = _token.balanceOf(address(this)); require(amount > 0, "No tokens to release"); _token.transfer(owner, amount); } } contract Sale { using SafeMath for uint256; //Address needs to be changed for main-net address internal constant PANCAKESWAP_ROUTER_ADDRESS = 0x05fF2B0DB69458A0750badebc4f9e13aDd608C7F ; IPancakeRouter02 public uniswapRouter; address public owner; address public deployer; address public nativeToken; uint256 public nativeTokenRequired; address public tokenAddress; uint256 public tokenDecimal; uint256 public availableTokens; uint256 public saleRatio; uint256 public saleLimit; uint256 public saleEndDate; uint256 public perAccountLimit; uint256 public etherLimit; uint256 public etherRaised; uint256 public liquidityPercent; uint256 public swapRatio; uint256 public liquidityTokens; address public lockupContract; bool public saleActive = true; bool public liquidityAdded = false; bool public ownerWithdrawn = false; bool public saleAbruptlyEnded = false; mapping(address => uint256) public balanceOf; modifier onlyOwner() { require(owner == msg.sender, "Caller is not the Owner"); _; } modifier activeSale() { require(saleActive == true, "Sale has ended"); _; } modifier tokensUnlocked() { // Liquidity added and tokens unlocked require(liquidityAdded == true, "Tokens not unlocked yet"); _; } modifier emergencyState() { if(saleAbruptlyEnded == true) _; else { require((saleEndDate + 7 days) <= block.timestamp, "Not enough time has past since sale ended"); require(liquidityAdded == false, "Liquidity was added, cannot withdraw ether"); _; } } constructor( address _owner, address _deployer, address _token, uint256 _tokenDecimal, uint256 _availableTokens, uint256 _saleRatio, uint256 _saleLimit, uint256 _perAccountLimit, uint256 _saleDuration, uint256 _liquidityPercent, uint256 _swapRatio, address _lockupContract, address _nativeToken, uint256 _nativeTokenRequired) { // Standard variables uniswapRouter = IPancakeRouter02(PANCAKESWAP_ROUTER_ADDRESS); owner = _owner; deployer = _deployer; nativeToken = _nativeToken; tokenDecimal = _tokenDecimal; nativeTokenRequired = _nativeTokenRequired; // Zero if there is no restriction tokenAddress = _token; availableTokens = _availableTokens; // Sale related variables saleRatio = _saleRatio; saleLimit = _saleLimit; etherLimit = (_saleLimit.div(_saleRatio.mul(10 ** tokenDecimal))).mul(10 ** 18); perAccountLimit = _perAccountLimit; saleEndDate = block.timestamp + _saleDuration; // Lock up and liquidity variables liquidityPercent = _liquidityPercent; swapRatio = _swapRatio; lockupContract = _lockupContract; // Associating this Sale contract with its corresponding lock up contract LiquidityLock(lockupContract).initializeAssosiation(address(this)); } // The sale can be ended early by the owner if it reaches atleast 98% of the cap function endSaleEarly() public onlyOwner activeSale { uint256 minEth = (etherLimit.mul(98)).div(100); require(minEth <= etherRaised, "Not enough ether raised"); saleActive = false; } function getApproval() public onlyOwner { IERC20 _token = IERC20(tokenAddress); _token.approve(PANCAKESWAP_ROUTER_ADDRESS, availableTokens); } // Once the Sale has ended the owner can move liqudity to UNISWAP // This unlocks the tokens for the buyers to withdraw function addLiquidity() public onlyOwner { require((saleActive == false) || (saleEndDate < block.timestamp), "Sale not ended"); require((saleAbruptlyEnded == false), "Sale was ended Abruptly"); require(liquidityAdded == false, "Liquidity already added"); uint256 ethAmountAfterFee = (etherRaised.mul(99)).div(100); // 1% protocol fee payable(deployer).transfer(etherRaised.sub(ethAmountAfterFee)); // Fee transfered to deployer contract uint256 liqudityEth = (etherRaised.mul(liquidityPercent)).div(100); uint256 scaledSwapRatio = swapRatio.mul(10 ** tokenDecimal); liquidityTokens = liqudityEth.mul(scaledSwapRatio).div(10 ** 18); uint256 deadline = block.timestamp + 30; LiquidityLock(lockupContract).initiateLockup(); uniswapRouter.addLiquidityETH{ value: liqudityEth }( tokenAddress, liquidityTokens, liquidityTokens, liqudityEth , lockupContract, deadline); liquidityAdded = true; } // Buyers can deposit ether corresponding to how much they wish to buy // Tokens can be withdrawl only after liqudity has been added to UNISWAP function deposit() payable public activeSale { require(block.timestamp < saleEndDate, "Sale has ended"); require(saleAbruptlyEnded == false, "Sale ended Abruptly"); uint256 amount = msg.value; IERC20 _nativeToken = IERC20(nativeToken); uint256 buyerNativeBalance = _nativeToken.balanceOf(msg.sender); require(nativeTokenRequired <= buyerNativeBalance, "Not enough Native token to participate"); if(perAccountLimit != 0) // Zero means No limit per Account require(balanceOf[msg.sender] + amount <= perAccountLimit, "Higher than account limit"); require(etherRaised + amount <= etherLimit, "Higher than total sale limit"); balanceOf[msg.sender] += amount; etherRaised += amount; } // Buyers can withdraw tokens once liquidity has been added to UNISWAP function withdrawTokens() public tokensUnlocked { IERC20 _token = IERC20(tokenAddress); uint256 scaledSaleRatio = saleRatio.mul(10 ** tokenDecimal); uint256 tokensOwed = balanceOf[msg.sender].mul(scaledSaleRatio).div(10 ** 18); _token.transfer(msg.sender, tokensOwed); } // Owner can withdraw leftover unsold tokens once the liquidity has been added function withdrawLeftoverTokens() public onlyOwner tokensUnlocked { require(ownerWithdrawn == false, "Withdrawl already completed"); uint256 scaledSaleRatio = saleRatio.mul(10 ** tokenDecimal); uint256 tokensSold = etherRaised.mul(scaledSaleRatio).div(10 ** 18); uint256 movedTokens = tokensSold.add(liquidityTokens); uint256 leftoverTokens = availableTokens.sub(movedTokens); ownerWithdrawn = true; IERC20 _token = IERC20(tokenAddress); _token.transfer(owner, leftoverTokens); } // The owner can withdraw the sold ether once the liquidity has been added. function withdrawSaleEther() public onlyOwner tokensUnlocked { uint256 etherBalance = address(this).balance; payable(owner).transfer(etherBalance); } // The owner can stop the sale at anytime but won't have able to add liquidity or withdraw function abruptlyEndSale() public onlyOwner activeSale { saleAbruptlyEnded = true; saleActive = false; } // Buyers can withdraw their ether back if liquidity has not been added for 7 days // since the end of the sale or if the owner has abruptly ended the same function emergencyWithdrawEther() public emergencyState { require(balanceOf[msg.sender] != 0, "You have no ether left to withdraw"); uint256 amount = balanceOf[msg.sender]; balanceOf[msg.sender] -= amount; etherRaised -= amount; payable(msg.sender).transfer(amount); } } contract SalesFactory { using SafeMath for uint256; mapping(uint256 => Sale) private sales; mapping(uint256 => uint256) private saleDurations; mapping(uint256 => LiquidityLock) private lockups; uint256 private index; address public owner; address public nativeToken; event ContractsDeployed (address sale, address lockup); modifier onlyOwner() { require(owner == msg.sender, "Caller is not the Owner"); _; } constructor(address _nativeToken) { owner = msg.sender; nativeToken = _nativeToken; } function changeNativeToken(address _token) public onlyOwner { nativeToken = _token; } function deployContract( address token, uint256 tokenDecimal, uint256 totalTokens, uint256 saleLimit, uint256 saleRatio, uint256 perAccountLimit, uint256 saleDays, uint256 saleHours, uint256 liquidityPercent, uint256 swapRatio, uint256 liquidityLockDays, uint256 nativeTokenAmount ) external { require(liquidityPercent <= 98, "Liquidity cannot be more than 98% of sale"); index += 1; saleDurations[index] = saleDays * 1 days + saleHours * 1 hours; lockups[index] = new LiquidityLock(msg.sender, liquidityLockDays); sales[index] = new Sale(msg.sender, address(this), token, tokenDecimal, totalTokens, saleRatio, saleLimit, perAccountLimit, saleDurations[index], liquidityPercent, swapRatio, address(lockups[index]), nativeToken, nativeTokenAmount); IERC20(token).transferFrom(msg.sender, address(sales[index]), totalTokens); emit ContractsDeployed(address(sales[index]), address(lockups[index])); } function withdrawEther() public onlyOwner { payable(owner).transfer(address(this).balance); } receive() payable external {} }