// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Interface for interacting with AMM pairs like UniswapV2 or similar
interface IAMMPair {
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
function getReserves() external view returns (uint reserve0, uint reserve1, uint32 blockTimestampLast);
function token0() external view returns (address);
function token1() external view returns (address);
}
// Standard ERC20 interface for token interactions
interface IERC20 {
function balanceOf(address account) external view returns (uint);
function transferFrom(address sender, address recipient, uint amount) external returns (bool);
function transfer(address recipient, uint amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint);
}
// TokenSwapper contract that interacts with AMM pairs and ERC20 tokens
contract TokenSwapperV2 {
address public owner;
address constant TOKEN1 = 0x4200000000000000000000000000000000000006;
address constant TOKEN2 = 0x0b3e328455c4059EEb9e3f84b5543F74E24e7E1b;
event SwapExecuted(address indexed tokenIn, uint amountIn, uint amountOutMin, uint amountOut, address indexed recipient, bool success);
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Caller is not the owner");
_;
}
// Function to calculate the output amount based on input, reserves, and an optional tax percentage
function getAmountOut(
uint amountIn,
uint reserveIn,
uint reserveOut,
uint taxPercentIn,
uint taxPercentOut
) public pure returns (uint amountOut) {
require(amountIn > 0, "TokenSwapper: INSUFFICIENT_INPUT_AMOUNT");
require(reserveIn > 0 && reserveOut > 0, "TokenSwapper: INSUFFICIENT_LIQUIDITY");
// Calculate the amount in after applying input tax
uint amountInAfterTax = amountIn * (100 - taxPercentIn) / 100;
// Standard Uniswap V2 formula with fee
uint amountInWithFee = amountInAfterTax * 997;
uint numerator = amountInWithFee * reserveOut;
uint denominator = reserveIn * 1000 + amountInWithFee;
uint amountOutBeforeTax = numerator / denominator;
// Apply output tax
amountOut = amountOutBeforeTax * (100 - taxPercentOut) / 100;
}
// Function to get reserves for the swap
function getReserves(IAMMPair ammPair, bool isToken0In) internal view returns (uint reserveIn, uint reserveOut) {
(uint reserve0, uint reserve1,) = ammPair.getReserves();
return isToken0In ? (reserve0, reserve1) : (reserve1, reserve0);
}
// Function to determine the input token based on the swap direction
function getTokens(IAMMPair ammPair, bool isToken0In) internal view returns (address tokenIn) {
return isToken0In ? ammPair.token0() : ammPair.token1();
}
// Function to execute the swap on the AMM pair
function swap(
address pair,
uint amountIn,
uint amountOutMin,
bool isToken0In,
uint taxPercent
) external {
IAMMPair ammPair = IAMMPair(pair);
// Determine the input and output tokens
address tokenIn = getTokens(ammPair, isToken0In);
uint taxPercentIn = 0;
uint taxPercentOut = 0;
if (tokenIn == TOKEN1 || tokenIn == TOKEN2) {
// Apply tax on output token
taxPercentOut = taxPercent;
} else {
// Apply tax on input token
taxPercentIn = taxPercent;
}
// Get reserves
(uint reserveIn, uint reserveOut) = getReserves(ammPair, isToken0In);
// Calculate the expected output amount with the tax applied
uint amountOut = getAmountOut(amountIn, reserveIn, reserveOut, taxPercentIn, taxPercentOut);
require(amountOut >= amountOutMin, "TokenSwapper: INSUFFICIENT_OUTPUT_AMOUNT");
// Transfer input tokens directly from the sender to the AMM pair
require(IERC20(tokenIn).transferFrom(msg.sender, pair, amountIn), "TokenSwapper: TRANSFER_FAILED");
// Execute the swap on the AMM pair
ammPair.swap(
isToken0In ? 0 : amountOut,
isToken0In ? amountOut : 0,
msg.sender, // Send output tokens directly to the user
""
);
// Emit the swap event
emit SwapExecuted(tokenIn, amountIn, amountOutMin, amountOut, msg.sender, true);
}
// Function to rescue tokens accidentally sent to the contract
function rescueTokens(address token, uint amount, address to) external onlyOwner {
require(IERC20(token).transfer(to, amount), "TokenSwapper: TRANSFER_FAILED");
}
// Function to transfer ownership of the contract
function transferOwnership(address newOwner) external onlyOwner {
require(newOwner != address(0), "New owner cannot be the zero address");
owner = newOwner;
}
}
{
"compilationTarget": {
"contracts/IAMM_swapper.sol": "TokenSwapperV2"
},
"evmVersion": "cancun",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"bool","name":"success","type":"bool"}],"name":"SwapExecuted","type":"event"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"reserveIn","type":"uint256"},{"internalType":"uint256","name":"reserveOut","type":"uint256"},{"internalType":"uint256","name":"taxPercentIn","type":"uint256"},{"internalType":"uint256","name":"taxPercentOut","type":"uint256"}],"name":"getAmountOut","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"rescueTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pair","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"bool","name":"isToken0In","type":"bool"},{"internalType":"uint256","name":"taxPercent","type":"uint256"}],"name":"swap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]