// SPDX-License-Identifier: MIT pragma solidity ^0.6.6; import "https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/interfaces/IUniswapV2ERC20.sol"; import "https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/interfaces/IUniswapV2Factory.sol"; import "https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/interfaces/IUniswapV2Pair.sol"; contract DEXSlippageContract { using SafeMath for uint256; bool private locked; event SwapExecuted( address indexed user, address[] path, uint256 amountIn, uint256 amountOut, uint256 minAmountOut, uint256 timestamp ); /** * @dev Reentrancy Guard - Security Modifier * * This modifier protects against reentrancy attacks, one of the most common * vulnerabilities in smart contracts (e.g. the famous DAO hack). * * * How it works: * 1. Before executing the function, it checks if the contract is already locked. * 2. If not locked, it sets the lock to true and proceeds with the function. * 3. After the function finishes (including any external calls), it unlocks the contract. * * This ensures that no external contract can re-enter and manipulate state * during an ongoing transaction. * * This is a standard, widely-used security pattern in professional DeFi contracts. */ modifier nonReentrant() { require(!locked, "ReentrancyGuard: reentrant call"); locked = true; _; locked = false; } // Official Uniswap V2 Router (ALWAYS VERIFY ON ETHERSCAN.IO) address public constant DEX_ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D; // Official Wrapped Ether Address (ALWAYS VERIFY ON ETHERSCAN.IO) address public constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; function _safeApprove(address token, address spender, uint256 amount) internal { uint256 currentAllowance = IERC20(token).allowance(address(this), spender); if (currentAllowance > 0) { IERC20(token).approve(spender, 0); } require(IERC20(token).approve(spender, amount), "Safe approve failed"); } function swapTokensForTokensWithSlippage( address[] memory path, uint256 amountIn, uint256 minAmountOut, uint256 deadline ) external nonReentrant { require(path.length >= 2, "Path must have at least 2 tokens"); require(amountIn > 0 && minAmountOut > 0 && deadline >= block.timestamp, "Invalid params"); address tokenIn = path[0]; require( IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn), "transferFrom failed - approve this contract first" ); _safeApprove(tokenIn, DEX_ROUTER, amountIn); uint256[] memory amounts = IUniswapV2Router02(DEX_ROUTER) .swapExactTokensForTokens( amountIn, minAmountOut, path, msg.sender, deadline ); IERC20(tokenIn).approve(DEX_ROUTER, 0); emit SwapExecuted(msg.sender, path, amountIn, amounts[amounts.length - 1], minAmountOut, block.timestamp); } function swapETHForTokensWithSlippage( address[] memory path, uint256 minAmountOut, uint256 deadline ) external payable nonReentrant { require(path.length >= 2 && path[0] == WETH && msg.value > 0 && minAmountOut > 0 && deadline >= block.timestamp, "Invalid params"); uint256[] memory amounts = IUniswapV2Router02(DEX_ROUTER) .swapExactETHForTokens{value: msg.value}( minAmountOut, path, msg.sender, deadline ); emit SwapExecuted(msg.sender, path, msg.value, amounts[amounts.length - 1], minAmountOut, block.timestamp); } function swapTokensForETHWithSlippage( address[] memory path, uint256 amountIn, uint256 minAmountOut, uint256 deadline ) external nonReentrant { require(path.length >= 2 && path[path.length - 1] == WETH && amountIn > 0 && minAmountOut > 0 && deadline >= block.timestamp, "Invalid params"); address tokenIn = path[0]; require( IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn), "transferFrom failed" ); _safeApprove(tokenIn, DEX_ROUTER, amountIn); uint256[] memory amounts = IUniswapV2Router02(DEX_ROUTER) .swapExactTokensForETH( amountIn, minAmountOut, path, msg.sender, deadline ); IERC20(tokenIn).approve(DEX_ROUTER, 0); emit SwapExecuted(msg.sender, path, amountIn, amounts[amounts.length - 1], minAmountOut, block.timestamp); } function getExpectedOutput(address[] memory path, uint256 amountIn) external view returns (uint256 expectedAmountOut) { require(path.length >= 2, "Invalid path"); uint256[] memory amounts = IUniswapV2Router02(DEX_ROUTER).getAmountsOut(amountIn, path); return amounts[amounts.length - 1]; } function calculateMinAmountOut( address[] memory path, uint256 amountIn, uint256 slippageBps ) external view returns (uint256 minAmountOut) { require(slippageBps <= 1000, "Slippage too high (max 10%)"); uint256[] memory amounts = IUniswapV2Router02(DEX_ROUTER).getAmountsOut(amountIn, path); uint256 expected = amounts[amounts.length - 1]; minAmountOut = expected.mul(10000 - slippageBps).div(10000); } receive() external payable { revert("Direct ETH not accepted. Use swapETHForTokensWithSlippage()"); } fallback() external payable { revert("Fallback disabled"); } } 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) { require(b <= a, "SafeMath: subtraction overflow"); return a - b; } 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) { require(b > 0, "SafeMath: division by zero"); return a / b; } } interface IERC20 { function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); function approve(address spender, uint256 amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint256); } interface IUniswapV2Router02 { function swapExactTokensForTokens( uint amountIn, uint amountOutMin, 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 swapExactTokensForETH( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external returns (uint[] memory amounts); function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts); }