// SPDX-License-Identifier: MIT
pragma solidity ^0.6.6;
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);
}