Description:
Decentralized Finance (DeFi) protocol contract providing Mintable, Burnable, Swap, Liquidity, Factory, Oracle functionality.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/Router.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import "./interfaces/IWETH.sol";
import "./interfaces/IRouter.sol";
import "./interfaces/IFactory.sol";
import "./interfaces/IPair.sol";
/// @title Router is used to swap on Antfarm Protocol without having to own ATF tokens
/// @notice This Version only supports ETH swaps and relies on the ATF/ETH Oracle
contract Router is IRouter {
address public immutable factory;
address public immutable protocolToken;
address public immutable weth;
address public immutable oraclePair;
mapping(address => uint256) public userDeposits;
uint256 public totalDeposits;
constructor(address _factory, address _weth) {
factory = _factory;
protocolToken = IFactory(factory).protocolToken();
weth = _weth;
oraclePair = IFactory(factory).getPair(protocolToken, weth, uint16(10));
}
modifier ensure(uint256 deadline) {
if (deadline < block.timestamp) revert Router__Expired();
_;
}
receive() external payable {}
// Helper function to avoid stack too deep
function _transferToOracle(uint256 amount, uint256 feeToPay) internal {
IERC20(weth).transfer(oraclePair, amount);
IPair(oraclePair).swap(feeToPay, 0, address(this));
}
// Helper function to execute swap logic
function _executeSwapLogic(
uint256[] memory amounts,
address pair,
address _to,
uint256 _amountIn,
address _output
) internal returns (uint256) {
(uint256 amount0Out, uint256 amount1Out, uint256 finalFeeToPay) =
_getFeeToPay(amounts, weth, _output, pair);
IERC20(weth).transfer(pair, amounts[0]);
IERC20(protocolToken).transfer(pair, finalFeeToPay);
IPair(pair).swap(amount0Out, amount1Out, _to);
_transferToOracle(_amountIn - amounts[0], finalFeeToPay);
emit SwapExecuted(
_to, weth, _output, _amountIn, amount0Out + amount1Out
);
return amount0Out + amount1Out;
}
// INTERNAL WETH SWAP FUNCTIONS
/// @notice Internal function to swap exact WETH for tokens
/// @param _amountIn The amount of WETH to swap
/// @param _output The token address to receive
/// @param _fee The fee tier of the pool
/// @param _to The recipient address for the output tokens
/// @param _amountOutMin The minimum amount of output tokens to receive
/// @param _fromUser Whether to transfer WETH from user (true) or use contract's WETH (false)
/// @return _amountOut The actual amount of output tokens received
function _swapExactWETHForTokens(
uint256 _amountIn,
address _output,
uint16 _fee,
address _to,
uint256 _amountOutMin,
bool _fromUser
) internal returns (uint256 _amountOut) {
// Transfer WETH from user to this contract (only if _fromUser is true)
if (_fromUser) {
IERC20(weth).transferFrom(msg.sender, address(this), _amountIn);
}
uint256[] memory amounts = getAmountsOut(_amountIn, weth, _output, _fee);
address pair = IFactory(factory).getPair(weth, _output, _fee);
(,, uint256 feeToPay) = _getFeeToPay(amounts, weth, _output, pair);
// Use 'amounts' to store the required WETH to swap for fee ATF tokens
amounts = _getAmountsIn(feeToPay, weth, protocolToken, uint16(10));
uint256 adjustedAmount =
(_amountIn * _amountIn) / (_amountIn + amounts[0]);
amounts = getAmountsOut(adjustedAmount, weth, _output, _fee);
// Checks that the intermediary calculation is already enough
if (amounts[1] < _amountOutMin) {
revert Router__InsufficientOutputAmount();
}
return _executeSwapLogic(amounts, pair, _to, _amountIn, _output);
}
/// @notice Internal function to swap exact tokens for WETH
/// @param _input The token address to spend
/// @param _fee The fee tier of the pool
/// @param _to The recipient address for the WETH
/// @param _amountIn The amount of input tokens to spend
/// @param _amountOutMin The minimum amount of WETH to receive
/// @return _amountOut The actual amount of WETH received
function _swapExactTokensForWETH(
address _input,
uint16 _fee,
address _to,
uint256 _amountIn,
uint256 _amountOutMin
) internal returns (uint256 _amountOut) {
uint256[] memory amounts = getAmountsOut(_amountIn, _input, weth, _fee);
uint256 standardAmountOut = amounts[1];
address pair = IFactory(factory).getPair(_input, weth, _fee);
(uint256 amount0Out, uint256 amount1Out, uint256 feeToPay) =
_getFeeToPay(amounts, _input, weth, pair);
if (feeToPay > totalDeposits) {
revert Router__InsufficientATFBalance();
}
amounts = _getAmountsIn(feeToPay, weth, protocolToken, uint16(10));
if (standardAmountOut - amounts[0] < _amountOutMin) {
revert Router__InsufficientOutputAmount();
}
IERC20(protocolToken).transfer(pair, feeToPay);
IERC20(_input).transferFrom(msg.sender, pair, _amountIn);
IPair(pair).swap(amount0Out, amount1Out, _to);
_amountOut = standardAmountOut - amounts[0];
_transferToOracle(amounts[0], feeToPay);
emit SwapExecuted(_to, _input, weth, _amountIn, _amountOut);
}
/// @notice Internal function to swap WETH for exact tokens
/// @param _amountInMax The maximum amount of WETH to spend
/// @param _output The token address to receive
/// @param _fee The fee tier of the pool
/// @param _to The recipient address for the tokens
/// @param _amountOut The exact amount of output tokens to receive
/// @param _fromUser Whether to transfer WETH from user (true) or use contract's WETH (false)
/// @return _amountIn The actual amount of WETH spent
function _swapWETHForExactTokens(
uint256 _amountInMax,
address _output,
uint16 _fee,
address _to,
uint256 _amountOut,
bool _fromUser
) internal returns (uint256 _amountIn) {
uint256[] memory amounts =
_getAmountsIn(_amountOut, weth, _output, _fee);
address pair = IFactory(factory).getPair(weth, _output, _fee);
(uint256 amount0Out, uint256 amount1Out, uint256 feeToPay) =
_getFeeToPay(amounts, weth, _output, pair);
_amountIn = amounts[0]; // Amount of WETH needed for main swap
amounts = _getAmountsIn(feeToPay, weth, protocolToken, uint16(10));
uint256 totalWETHNeeded = _amountIn + amounts[0];
if (totalWETHNeeded > _amountInMax) {
revert Router__InsufficientInputAmount();
}
// Transfer WETH from user to this contract (only if _fromUser is true)
if (_fromUser) {
IERC20(weth).transferFrom(
msg.sender, address(this), totalWETHNeeded
);
}
IERC20(weth).transfer(pair, _amountIn);
IERC20(protocolToken).transfer(pair, feeToPay);
IPair(pair).swap(amount0Out, amount1Out, _to);
_transferToOracle(amounts[0], feeToPay);
_amountIn = totalWETHNeeded;
emit SwapExecuted(_to, weth, _output, _amountIn, _amountOut);
}
// MAGIC ROUTER SWAP FUNCTIONS
/// @inheritdoc IRouter
function swapExactETHForTokens(
address _output,
uint16 _fee,
address _to,
uint256 _deadline,
uint256 _amountOutMin
) external payable ensure(_deadline) returns (uint256 _amountOut) {
// Wrap ETH to WETH
IWETH(weth).deposit{value: msg.value}();
// Call internal WETH function
_amountOut = _swapExactWETHForTokens(
msg.value, _output, _fee, _to, _amountOutMin, false
);
}
/// @inheritdoc IRouter
function swapExactTokensForETH(
address _input,
uint16 _fee,
address _to,
uint256 _deadline,
uint256 _amountIn,
uint256 _amountOutMin
) external ensure(_deadline) returns (uint256 _amountOut) {
// Call internal WETH function with this contract as recipient
_amountOut = _swapExactTokensForWETH(
_input, _fee, address(this), _amountIn, _amountOutMin
);
// Unwrap WETH to ETH
IWETH(weth).withdraw(_amountOut);
// Transfer ETH to recipient
_safeTransferETH(_to, _amountOut);
}
/// @inheritdoc IRouter
function swapETHForExactTokens(
address _output,
uint16 _fee,
address _to,
uint256 _deadline,
uint256 _amountOut
) external payable ensure(_deadline) returns (uint256 _amountIn) {
// Wrap all received ETH to WETH
IWETH(weth).deposit{value: msg.value}();
// Call internal WETH function
_amountIn = _swapWETHForExactTokens(
msg.value, _output, _fee, _to, _amountOut, false
);
// Calculate refund amount
uint256 refund = msg.value - _amountIn;
if (refund > 0) {
// Unwrap refunded WETH to ETH
IWETH(weth).withdraw(refund);
_safeTransferETH(msg.sender, refund);
}
}
// PUBLIC WETH SWAP FUNCTIONS
/// @notice Swaps an exact amount of WETH for tokens
/// @param _amountIn The amount of WETH to swap
/// @param _output The token address to receive
/// @param _fee The fee tier of the pool
/// @param _to The recipient address for the output tokens
/// @param _deadline The timestamp by which the transaction must be executed
/// @param _amountOutMin The minimum amount of output tokens to receive
/// @return _amountOut The actual amount of output tokens received
function swapExactWETHForTokens(
uint256 _amountIn,
address _output,
uint16 _fee,
address _to,
uint256 _deadline,
uint256 _amountOutMin
) external ensure(_deadline) returns (uint256 _amountOut) {
return _swapExactWETHForTokens(
_amountIn, _output, _fee, _to, _amountOutMin, true
);
}
/// @notice Swaps an exact amount of tokens for WETH
/// @param _input The token address to spend
/// @param _fee The fee tier of the pool
/// @param _to The recipient address for the WETH
/// @param _deadline The timestamp by which the transaction must be executed
/// @param _amountIn The amount of input tokens to spend
/// @param _amountOutMin The minimum amount of WETH to receive
/// @return _amountOut The actual amount of WETH received
function swapExactTokensForWETH(
address _input,
uint16 _fee,
address _to,
uint256 _deadline,
uint256 _amountIn,
uint256 _amountOutMin
) external ensure(_deadline) returns (uint256 _amountOut) {
uint256 out = _swapExactTokensForWETH(
_input, _fee, address(this), _amountIn, _amountOutMin
);
// router now holds the full WETH, oracle leg already settled inside
IERC20(weth).transfer(_to, out);
return out;
}
/// @notice Swaps WETH for an exact amount of tokens
/// @param _amountInMax The maximum amount of WETH to spend
/// @param _output The token address to receive
/// @param _fee The fee tier of the pool
/// @param _to The recipient address for the tokens
/// @param _deadline The timestamp by which the transaction must be executed
/// @param _amountOut The exact amount of output tokens to receive
/// @return _amountIn The actual amount of WETH spent
function swapWETHForExactTokens(
uint256 _amountInMax,
address _output,
uint16 _fee,
address _to,
uint256 _deadline,
uint256 _amountOut
) external ensure(_deadline) returns (uint256 _amountIn) {
return _swapWETHForExactTokens(
_amountInMax, _output, _fee, _to, _amountOut, true
);
}
/// @inheritdoc IRouter
function estimateExactETHForTokens(
address _output,
uint16 _fee,
uint256 _amountIn
) external view returns (uint256 estimatedOut) {
uint256[] memory amounts = getAmountsOut(_amountIn, weth, _output, _fee);
address pair = IFactory(factory).getPair(weth, _output, _fee);
(,, uint256 feeToPay) = _getFeeToPay(amounts, weth, _output, pair);
uint256[] memory feeInAmounts =
_getAmountsIn(feeToPay, weth, protocolToken, uint16(10));
feeInAmounts = getAmountsOut(
(_amountIn * _amountIn) / (_amountIn + feeInAmounts[0]),
weth,
_output,
_fee
);
return feeInAmounts[1];
}
/// @inheritdoc IRouter
function estimateExactTokensForETH(
address _input,
uint16 _fee,
uint256 _amountIn
) external view returns (uint256 estimatedOut) {
uint256[] memory amounts = getAmountsOut(_amountIn, _input, weth, _fee);
uint256 standardAmountOut = amounts[1];
address pair = IFactory(factory).getPair(_input, weth, _fee);
(,, uint256 feeToPay) = _getFeeToPay(amounts, _input, weth, pair);
amounts = _getAmountsIn(feeToPay, weth, protocolToken, uint16(10));
return standardAmountOut - amounts[0];
}
/// @inheritdoc IRouter
function estimateETHForExactTokens(
address _output,
uint16 _fee,
uint256 _amountOut
) external view returns (uint256 estimatedIn) {
uint256[] memory amounts =
_getAmountsIn(_amountOut, weth, _output, _fee);
address pair = IFactory(factory).getPair(weth, _output, _fee);
(,, uint256 feeToPay) = _getFeeToPay(amounts, weth, _output, pair);
// Calculate WETH needed to pay for ATF fees
uint256[] memory feeInAmounts =
_getAmountsIn(feeToPay, weth, protocolToken, uint16(10));
// Total ETH needed is the amount for the swap plus the amount needed for fees
return amounts[0] + feeInAmounts[0];
}
/// CLASSIC ROUTER SWAP FUNCTIONS
/// @notice Swaps an exact amount of input tokens for as many output tokens as possible
/// @param params The parameters necessary for the swap, encoded as `swapExactTokensForTokensParams` in calldata
// @param amountIn The amount of input tokens to send
// @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert
// @param maxFee Maximum fees to be paid
// @param path An array of token addresses
// @param fees Associated fee for each two token addresses within the path
// @param to Recipient of the output tokens
// @param deadline Unix timestamp after which the transaction will revert
/// @return amounts The input token amount and all subsequent output token amounts
function swapExactTokensForTokens(
swapExactTokensForTokensParams calldata params
)
external
virtual
ensure(params.deadline)
returns (uint256[] memory amounts)
{
uint256 amountIn = params.amountIn;
amounts = getAmountsOut(amountIn, params.path, params.fees);
if (amounts[amounts.length - 1] < params.amountOutMin) {
revert Router__InsufficientOutputAmount();
}
_safeTransferFrom(
params.path[0],
msg.sender,
pairFor(params.path[0], params.path[1], params.fees[0]),
amounts[0]
);
if (_swap(amounts, params.path, params.fees, params.to) > params.maxFee)
{
revert Router__InsufficientMaxFee();
}
}
/// @notice Receive an exact amount of output tokens for as few input tokens as possible
/// @param params The parameters necessary for the swap, encoded as `swapTokensForExactTokensParams` in calldata
// @param amountOut The amount of output tokens to receive
// @param amountInMax The maximum amount of input tokens that can be required before the transaction reverts
// @param maxFee Maximum fees to be paid
// @param path An array of token addresses
// @param fees Associated fee for each two token addresses within the path
// @param to Recipient of the output tokens
// @param deadline Unix timestamp after which the transaction will revert
/// @return amounts The input token amount and all subsequent output token amounts
function swapTokensForExactTokens(
swapTokensForExactTokensParams calldata params
)
external
virtual
ensure(params.deadline)
returns (uint256[] memory amounts)
{
uint256 amountInMax = params.amountInMax;
amounts = getAmountsIn(params.amountOut, params.path, params.fees);
if (amounts[0] > amountInMax) revert Router__ExcessiveInputAmount();
_safeTransferFrom(
params.path[0],
msg.sender,
pairFor(params.path[0], params.path[1], params.fees[0]),
amounts[0]
);
if (_swap(amounts, params.path, params.fees, params.to) > params.maxFee)
{
revert Router__InsufficientMaxFee();
}
}
/// @notice Swaps an exact amount of ETH for as many output tokens as possible
/// @param params The parameters necessary for the swap, encoded as `swapExactETHForTokensParams` in calldata
// @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert
// @param maxFee Maximum fees to be paid
// @param path An array of token addresses
// @param fees Associated fee for each two token addresses within the path
// @param to Recipient of the output tokens
// @param deadline Unix timestamp after which the transaction will revert
/// @return amounts The input token amount and all subsequent output token amounts
function swapExactETHForTokens(swapExactETHForTokensParams calldata params)
external
payable
virtual
ensure(params.deadline)
returns (uint256[] memory amounts)
{
if (params.path[0] != weth) revert Router__InvalidPath();
amounts = getAmountsOut(msg.value, params.path, params.fees);
if (amounts[amounts.length - 1] < params.amountOutMin) {
revert Router__InsufficientOutputAmount();
}
IWETH(weth).deposit{value: amounts[0]}();
assert(
IWETH(weth).transfer(
pairFor(params.path[0], params.path[1], params.fees[0]),
amounts[0]
)
);
if (_swap(amounts, params.path, params.fees, params.to) > params.maxFee)
{
revert Router__InsufficientMaxFee();
}
}
/// @notice Receive an exact amount of ETH for as few input tokens as possible
/// @param params The parameters necessary for the swap, encoded as `swapTokensForExactETHParams` in calldata
// @param amountOut The amount of ETH to receive
// @param amountInMax The maximum amount of input tokens that can be required before the transaction reverts
// @param maxFee Maximum fees to be paid
// @param path An array of token addresses
// @param fees Associated fee for each two token addresses within the path
// @param to Recipient of the output tokens
// @param deadline Unix timestamp after which the transaction will revert
/// @return amounts The input token amount and all subsequent output token amounts
function swapTokensForExactETH(swapTokensForExactETHParams calldata params)
external
virtual
ensure(params.deadline)
returns (uint256[] memory amounts)
{
if (params.path[params.path.length - 1] != weth) {
revert Router__InvalidPath();
}
uint256 amountInMax = params.amountInMax;
amounts = getAmountsIn(params.amountOut, params.path, params.fees);
if (amounts[0] > amountInMax) revert Router__ExcessiveInputAmount();
_safeTransferFrom(
params.path[0],
msg.sender,
pairFor(params.path[0], params.path[1], params.fees[0]),
amounts[0]
);
if (
_swap(amounts, params.path, params.fees, address(this))
> params.maxFee
) {
revert Router__InsufficientMaxFee();
}
IWETH(weth).withdraw(amounts[amounts.length - 1]);
_safeTransferETH(params.to, amounts[amounts.length - 1]);
}
/// @notice Swaps an exact amount of tokens for as much ETH as possible
/// @param params The parameters necessary for the swap, encoded as `swapExactTokensForETHParams` in calldata
// @param amountIn The amount of input tokens to send
// @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert
// @param maxFee Maximum fees to be paid
// @param path An array of token addresses
// @param fees Associated fee for each two token addresses within the path
// @param to Recipient of the output tokens
// @param deadline Unix timestamp after which the transaction will revert
/// @return amounts The input token amount and all subsequent output token amounts
function swapExactTokensForETH(swapExactTokensForETHParams calldata params)
external
virtual
ensure(params.deadline)
returns (uint256[] memory amounts)
{
uint256 amountIn = params.amountIn;
if (params.path[params.path.length - 1] != weth) {
revert Router__InvalidPath();
}
amounts = getAmountsOut(amountIn, params.path, params.fees);
if (amounts[amounts.length - 1] < params.amountOutMin) {
revert Router__InsufficientOutputAmount();
}
_safeTransferFrom(
params.path[0],
msg.sender,
pairFor(params.path[0], params.path[1], params.fees[0]),
amounts[0]
);
if (
_swap(amounts, params.path, params.fees, address(this))
> params.maxFee
) {
revert Router__InsufficientMaxFee();
}
IWETH(weth).withdraw(amounts[amounts.length - 1]);
_safeTransferETH(params.to, amounts[amounts.length - 1]);
}
/// @notice Receive an exact amount of tokens for as little ETH as possible
/// @param params The parameters necessary for the swap, encoded as `swapETHForExactTokensParams` in calldata
// @param amountOut The amount of tokens to receive
// @param maxFee Maximum fees to be paid
// @param path An array of token addresses
// @param fees Associated fee for each two token addresses within the path
// @param to Recipient of the output tokens
// @param deadline Unix timestamp after which the transaction will revert
/// @return amounts The input token amount and all subsequent output token amounts
function swapETHForExactTokens(swapETHForExactTokensParams calldata params)
external
payable
virtual
ensure(params.deadline)
returns (uint256[] memory amounts)
{
if (params.path[0] != weth) revert Router__InvalidPath();
amounts = getAmountsIn(params.amountOut, params.path, params.fees);
if (amounts[0] > msg.value) revert Router__ExcessiveInputAmount();
IWETH(weth).deposit{value: amounts[0]}();
assert(
IWETH(weth).transfer(
pairFor(params.path[0], params.path[1], params.fees[0]),
amounts[0]
)
);
if (_swap(amounts, params.path, params.fees, params.to) > params.maxFee)
{
revert Router__InsufficientMaxFee();
}
// refund dust ETH if any
if (msg.value > amounts[0]) {
_safeTransferETH(msg.sender, msg.value - amounts[0]);
}
}
// fetches and sorts the reserves for a pair
function getReserves(address tokenA, address tokenB, uint16 fee)
public
view
returns (uint256 reserveA, uint256 reserveB)
{
(address token0,) = _sortTokens(tokenA, tokenB);
(uint256 reserve0, uint256 reserve1,) =
IPair(pairFor(tokenA, tokenB, fee)).getReserves();
(reserveA, reserveB) =
tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
}
/// @notice Calculates the CREATE2 address for a pair without making any external calls
/// @param tokenA Token0 from the Pair
/// @param tokenB Token1 from the Pair
/// @param fee Associated fee to the Pair
/// @return pair The CREATE2 address for the desired Pair
function pairFor(address tokenA, address tokenB, uint16 fee)
public
view
returns (address pair)
{
(address token0, address token1) = _sortTokens(tokenA, tokenB);
pair = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
hex"ff",
factory,
keccak256(
abi.encodePacked(
token0, token1, fee, protocolToken
)
),
token0 == protocolToken
?
hex"e482076c47481b6652287a3b6ef16bc8047ff83dde52d08962662fff8ab4d471" // OraclePair init code hash
:
hex"d7737950b58d71c23c1376e989549cd028b62f935397f508b68602b01e6a4cdf" // Pair init code hash
)
)
)
)
);
}
// performs chained getAmountOut calculations on any number of pairs
function getAmountsOut(
uint256 amountIn,
address[] memory path,
uint16[] memory fees
) public view returns (uint256[] memory) {
if (path.length < 2) revert Router__InvalidPath();
uint256[] memory amounts = new uint256[](path.length);
amounts[0] = amountIn;
for (uint256 i; i < path.length - 1; i++) {
(uint256 reserveIn, uint256 reserveOut) =
getReserves(path[i], path[i + 1], fees[i]);
if (path[i] == protocolToken) {
amounts[i + 1] = getAmountOut(
(amounts[i] * 1000) / (1000 + fees[i]),
reserveIn,
reserveOut
);
} else if (path[i + 1] == protocolToken) {
amounts[i + 1] = (
getAmountOut(amounts[i], reserveIn, reserveOut) * 1000
) / (1000 + fees[i]);
} else {
amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
}
}
return amounts;
}
// performs chained getAmountIn calculations on any number of pairs
function getAmountsIn(
uint256 amountOut,
address[] memory path,
uint16[] memory fees
) public view returns (uint256[] memory) {
if (path.length < 2) revert Router__InvalidPath();
uint256[] memory amounts = new uint256[](path.length);
amounts[path.length - 1] = amountOut;
for (uint256 i = path.length - 1; i > 0; i--) {
(uint256 reserveIn, uint256 reserveOut) =
getReserves(path[i - 1], path[i], fees[i - 1]);
if (path[i - 1] == protocolToken) {
amounts[i - 1] = (
getAmountIn(amounts[i], reserveIn, reserveOut)
* (1000 + fees[i - 1])
) / 1000;
} else if (path[i] == protocolToken) {
amounts[i - 1] = getAmountIn(
(amounts[i] * (1000 + fees[i - 1])) / 1000,
reserveIn,
reserveOut
);
} else {
amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
}
}
return amounts;
}
function getAmountsOut(
uint256 amountIn,
address tokenIn,
address tokenOut,
uint16 fee
) internal view returns (uint256[] memory) {
uint256[] memory amounts = new uint256[](2);
amounts[0] = amountIn;
(uint256 reserveIn, uint256 reserveOut) =
_getReserves(tokenIn, tokenOut, fee);
if (tokenIn == protocolToken) {
amounts[1] = _getAmountOut(
(amountIn * 1000) / (1000 + 10), reserveIn, reserveOut
);
} else if (tokenOut == protocolToken) {
amounts[1] = (_getAmountOut(amountIn, reserveIn, reserveOut) * 1000)
/ (1000 + 10);
} else {
amounts[1] = _getAmountOut(amountIn, reserveIn, reserveOut);
}
return amounts;
}
function _getAmountOut(
uint256 amountIn,
uint256 reserveIn,
uint256 reserveOut
) internal pure returns (uint256) {
if (amountIn == 0) revert Router__InsufficientInputAmount();
if (reserveIn == 0 || reserveOut == 0) {
revert Router__InsufficientLiquidity();
}
uint256 numerator = amountIn * reserveOut;
uint256 denominator = reserveIn + amountIn;
return numerator / denominator;
}
// given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
function getAmountOut(
uint256 amountIn,
uint256 reserveIn,
uint256 reserveOut
) internal pure returns (uint256) {
if (amountIn == 0) revert Router__InsufficientInputAmount();
if (reserveIn == 0 || reserveOut == 0) {
revert Router__InsufficientLiquidity();
}
uint256 numerator = amountIn * reserveOut;
uint256 denominator = reserveIn + amountIn;
return numerator / denominator;
}
function _getAmountsIn(
uint256 amountOut,
address tokenIn,
address tokenOut,
uint16 fee
) internal view returns (uint256[] memory) {
uint256[] memory amounts = new uint256[](2);
amounts[1] = amountOut;
(uint256 reserveIn, uint256 reserveOut) =
_getReserves(tokenIn, tokenOut, fee);
if (tokenIn == protocolToken) {
amounts[0] = (
_getAmountIn(amountOut, reserveIn, reserveOut) * (1000 + 10)
) / 1000;
} else if (tokenOut == protocolToken) {
amounts[0] = _getAmountIn(
(amountOut * (1000 + 10)) / 1000, reserveIn, reserveOut
);
} else {
amounts[0] = _getAmountIn(amountOut, reserveIn, reserveOut);
}
return amounts;
}
function _getAmountIn(
uint256 amountOut,
uint256 reserveIn,
uint256 reserveOut
) internal pure returns (uint256) {
if (amountOut == 0) revert Router__InsufficientOutputAmount();
if (reserveIn == 0 || reserveOut == 0 || amountOut >= reserveOut) {
revert Router__InsufficientLiquidity();
}
uint256 numerator = reserveIn * amountOut;
uint256 denominator = reserveOut - amountOut;
return (numerator / denominator) + 1;
}
// given an output amount of an asset and pair reserves, returns a required input amount of the other asset
function getAmountIn(
uint256 amountOut,
uint256 reserveIn,
uint256 reserveOut
) internal pure returns (uint256) {
if (amountOut == 0) revert Router__InsufficientOutputAmount();
if (reserveIn == 0 || reserveOut == 0) {
revert Router__InsufficientLiquidity();
}
uint256 numerator = reserveIn * amountOut;
uint256 denominator = reserveOut - amountOut;
return (numerator / denominator) + 1;
}
function _getFeeToPay(
uint256[] memory amounts,
address tokenIn,
address tokenOut,
address pair
)
internal
view
returns (uint256 amount0Out, uint256 amount1Out, uint256 feeToPay)
{
// Original fee calculation for non-ATF pairs
(address token0,) = _sortTokens(tokenIn, tokenOut);
(amount0Out, amount1Out) = tokenIn == token0
? (uint256(0), amounts[1])
: (amounts[1], uint256(0));
uint256 amountIn = amounts[0];
feeToPay = IPair(pair).getFees(
amount0Out,
tokenIn == token0 ? amountIn : uint256(0),
amount1Out,
tokenIn == token0 ? uint256(0) : amountIn
);
}
function _getReserves(address tokenA, address tokenB, uint16 fee)
internal
view
returns (uint256 reserveA, uint256 reserveB)
{
(address token0,) = _sortTokens(tokenA, tokenB);
(uint256 reserve0, uint256 reserve1,) =
IPair(IFactory(factory).getPair(tokenA, tokenB, fee)).getReserves();
(reserveA, reserveB) =
tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
}
function _sortTokens(address tokenA, address tokenB)
internal
view
returns (address token0, address token1)
{
if (tokenA == tokenB) revert Router__IdenticalAddresses();
if (tokenA == protocolToken || tokenB == protocolToken) {
(token0, token1) = tokenA == protocolToken
? (protocolToken, tokenB)
: (protocolToken, tokenA);
if (token1 == address(0)) revert Router__AddressZero();
} else {
(token0, token1) =
tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
if (token0 == address(0)) revert Router__AddressZero();
}
}
function _safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
(bool success, bytes memory data) =
token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"TransferHelper::transferFrom: transferFrom failed"
);
}
function _safeTransferETH(address to, uint256 value) internal {
(bool success,) = to.call{value: value}("");
if (!success) revert Router__ETHTransferFailed();
}
function depositATF(uint256 amount) external {
if (amount == 0) revert Router__ZeroAmount();
userDeposits[msg.sender] += amount;
totalDeposits += amount;
if (
!IERC20(protocolToken).transferFrom(
msg.sender, address(this), amount
)
) {
revert Router__TransferFailed();
}
}
function withdrawATF(uint256 amount) external {
if (amount == 0) revert Router__ZeroAmount();
if (amount > userDeposits[msg.sender]) {
revert Router__InsufficientBalance();
}
userDeposits[msg.sender] -= amount;
totalDeposits -= amount;
if (!IERC20(protocolToken).transfer(msg.sender, amount)) {
revert Router__TransferFailed();
}
}
function getDeposit(address user) external view returns (uint256) {
return userDeposits[user];
}
// SWAP
// requires the initial amount to have already been sent to the first pair
function _swap(
uint256[] memory amounts,
address[] memory path,
uint16[] memory fees,
address _to
) internal virtual returns (uint256 totalFee) {
for (uint256 i; i < path.length - 1; i++) {
(address input, address output) = (path[i], path[i + 1]);
uint16 fee = fees[i];
IPair pair = IPair(pairFor(input, output, fee));
(address token0,) = _sortTokens(input, output);
(uint256 amount0Out, uint256 amount1Out) = input == token0
? (uint256(0), amounts[i + 1])
: (amounts[i + 1], uint256(0));
{
uint256 amountIn = amounts[i];
if (input == protocolToken) {
totalFee = totalFee + ((amountIn * fee) / (1000 + fee));
} else if (output == protocolToken) {
totalFee = totalFee + ((amounts[i + 1] * fee) / 1000);
} else {
uint256 feeToPay = pair.getFees(
amount0Out,
input == token0 ? amountIn : uint256(0),
amount1Out,
input == token0 ? uint256(0) : amountIn
);
_safeTransferFrom(
protocolToken, msg.sender, address(pair), feeToPay
);
totalFee = totalFee + feeToPay;
}
}
address to = i < path.length - 2
? pairFor(output, path[i + 2], fees[i + 1])
: _to;
pair.swap(amount0Out, amount1Out, to);
}
}
}
"
},
"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
},
"src/interfaces/IWETH.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IWETH {
// ERC20-like functions
function deposit() external payable;
function withdraw(uint256) external;
// Standard ERC20 functions
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);
// Events
event Deposit(address indexed dst, uint256 wad);
event Withdrawal(address indexed src, uint256 wad);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(
address indexed owner, address indexed spender, uint256 value
);
}
"
},
"src/interfaces/IRouter.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @title Router Interface for Antfarm Protocol
/// @notice Interface for handling token swaps with ETH on Antfarm Protocol
/// @dev Implements core swap functionality and estimation methods
interface IRouter {
error Router__Expired();
error Router__IdenticalAddresses();
error Router__AddressZero();
error Router__ZeroAmount();
error Router__InsufficientLiquidity();
error Router__InsufficientInputAmount();
error Router__InsufficientOutputAmount();
error Router__ETHTransferFailed();
error Router__InsufficientBalance();
error Router__InsufficientATFBalance();
error Router__TransferFailed();
error Router__InsufficientMaxFee();
error Router__ExcessiveInputAmount();
error Router__InvalidPath();
event SwapExecuted(
address indexed sender,
address indexed tokenIn,
address indexed tokenOut,
uint256 amountIn,
uint256 amountOut
);
/// @notice Returns the factory contract address
/// @return Address of the factory contract
function factory() external view returns (address);
/// @notice Returns the Antfarm token address
/// @return Address of the Antfarm token
function protocolToken() external view returns (address);
/// @notice Returns the WETH token address
/// @return Address of the WETH token
function weth() external view returns (address);
/// @notice Returns the oracle pair address
/// @return Address of the oracle pair
function oraclePair() external view returns (address);
/// @notice Allows the contract to receive ETH
receive() external payable;
/// @notice Swaps an exact amount of ETH for tokens
/// @param _output The token address to receive
/// @param _fee The fee tier of the pool
/// @param _to The recipient address for the output tokens
/// @param _deadline The timestamp by which the transaction must be executed
/// @param _amountOutMin The minimum amount of output tokens to receive
/// @return _amountOut The actual amount of output tokens received
function swapExactETHForTokens(
address _output,
uint16 _fee,
address _to,
uint256 _deadline,
uint256 _amountOutMin
) external payable returns (uint256 _amountOut);
/// @notice Swaps an exact amount of tokens for ETH
/// @param _input The token address to spend
/// @param _fee The fee tier of the pool
/// @param _to The recipient address for the ETH
/// @param _deadline The timestamp by which the transaction must be executed
/// @param _amountIn The amount of input tokens to spend
/// @param _amountOutMin The minimum amount of ETH to receive
/// @return _amountOut The actual amount of ETH received
function swapExactTokensForETH(
address _input,
uint16 _fee,
address _to,
uint256 _deadline,
uint256 _amountIn,
uint256 _amountOutMin
) external returns (uint256 _amountOut);
/// @notice Swaps ETH for an exact amount of tokens
/// @dev Refunds excess ETH if too much is sent
/// @param _output The token address to receive
/// @param _fee The fee tier of the pool
/// @param _to The recipient address for the tokens
/// @param _deadline The timestamp by which the transaction must be executed
/// @param _amountOut The exact amount of output tokens to receive
/// @return _amountIn The amount of ETH spent
function swapETHForExactTokens(
address _output,
uint16 _fee,
address _to,
uint256 _deadline,
uint256 _amountOut
) external payable returns (uint256 _amountIn);
/// @notice Estimates the output amount of tokens for an exact ETH input
/// @param _output The token address to receive
/// @param _fee The fee tier of the pool
/// @param _amountIn The amount of ETH to swap
/// @return estimatedOut The estimated amount of output tokens
function estimateExactETHForTokens(
address _output,
uint16 _fee,
uint256 _amountIn
) external view returns (uint256 estimatedOut);
/// @notice Estimates the output amount of ETH for an exact token input
/// @param _input The token address to spend
/// @param _fee The fee tier of the pool
/// @param _amountIn The amount of input tokens
/// @return estimatedOut The estimated amount of ETH
function estimateExactTokensForETH(
address _input,
uint16 _fee,
uint256 _amountIn
) external view returns (uint256 estimatedOut);
/// @notice Estimates the input amount of ETH needed for an exact token output
/// @param _output The token address to receive
/// @param _fee The fee tier of the pool
/// @param _amountOut The exact amount of output tokens desired
/// @return estimatedIn The estimated amount of ETH needed
function estimateETHForExactTokens(
address _output,
uint16 _fee,
uint256 _amountOut
) external view returns (uint256 estimatedIn);
/// @notice Swaps an exact amount of WETH for tokens
/// @param _amountIn The amount of WETH to swap
/// @param _output The token address to receive
/// @param _fee The fee tier of the pool
/// @param _to The recipient address for the output tokens
/// @param _deadline The timestamp by which the transaction must be executed
/// @param _amountOutMin The minimum amount of output tokens to receive
/// @return _amountOut The actual amount of output tokens received
function swapExactWETHForTokens(
uint256 _amountIn,
address _output,
uint16 _fee,
address _to,
uint256 _deadline,
uint256 _amountOutMin
) external returns (uint256 _amountOut);
/// @notice Swaps an exact amount of tokens for WETH
/// @param _input The token address to spend
/// @param _fee The fee tier of the pool
/// @param _to The recipient address for the WETH
/// @param _deadline The timestamp by which the transaction must be executed
/// @param _amountIn The amount of input tokens to spend
/// @param _amountOutMin The minimum amount of WETH to receive
/// @return _amountOut The actual amount of WETH received
function swapExactTokensForWETH(
address _input,
uint16 _fee,
address _to,
uint256 _deadline,
uint256 _amountIn,
uint256 _amountOutMin
) external returns (uint256 _amountOut);
/// @notice Swaps WETH for an exact amount of tokens
/// @param _amountInMax The maximum amount of WETH to spend
/// @param _output The token address to receive
/// @param _fee The fee tier of the pool
/// @param _to The recipient address for the tokens
/// @param _deadline The timestamp by which the transaction must be executed
/// @param _amountOut The exact amount of output tokens to receive
/// @return _amountIn The actual amount of WETH spent
function swapWETHForExactTokens(
uint256 _amountInMax,
address _output,
uint16 _fee,
address _to,
uint256 _deadline,
uint256 _amountOut
) external returns (uint256 _amountIn);
struct swapParams {
address[] path;
uint16[] fees;
address to;
}
struct swapExactTokensForTokensParams {
uint256 amountIn;
uint256 amountOutMin;
uint256 maxFee;
address[] path;
uint16[] fees;
address to;
uint256 deadline;
}
function swapExactTokensForTokens(
swapExactTokensForTokensParams calldata params
) external returns (uint256[] memory amounts);
struct swapTokensForExactTokensParams {
uint256 amountOut;
uint256 amountInMax;
uint256 maxFee;
address[] path;
uint16[] fees;
address to;
uint256 deadline;
}
function swapTokensForExactTokens(
swapTokensForExactTokensParams calldata params
) external returns (uint256[] memory amounts);
struct swapExactETHForTokensParams {
uint256 amountOutMin;
uint256 maxFee;
address[] path;
uint16[] fees;
address to;
uint256 deadline;
}
function swapExactETHForTokens(swapExactETHForTokensParams calldata params)
external
payable
returns (uint256[] memory amounts);
struct swapTokensForExactETHParams {
uint256 amountOut;
uint256 amountInMax;
uint256 maxFee;
address[] path;
uint16[] fees;
address to;
uint256 deadline;
}
function swapTokensForExactETH(swapTokensForExactETHParams calldata params)
external
returns (uint256[] memory amounts);
struct swapExactTokensForETHParams {
uint256 amountIn;
uint256 amountOutMin;
uint256 maxFee;
address[] path;
uint16[] fees;
address to;
uint256 deadline;
}
function swapExactTokensForETH(swapExactTokensForETHParams calldata params)
external
returns (uint256[] memory amounts);
struct swapETHForExactTokensParams {
uint256 amountOut;
uint256 maxFee;
address[] path;
uint16[] fees;
address to;
uint256 deadline;
}
function swapETHForExactTokens(swapETHForExactTokensParams calldata params)
external
payable
returns (uint256[] memory amounts);
/// @notice Deposits ATF tokens
/// @param amount The amount of ATF tokens to deposit
function depositATF(uint256 amount) external;
/// @notice Withdraws ATF tokens
/// @param amount The amount of ATF tokens to withdraw
function withdrawATF(uint256 amount) external;
/// @notice Gets the deposit amount for a user
/// @param user The address of the user
/// @return The deposit amount for the user
function getDeposit(address user) external view returns (uint256);
/// @notice Returns the deposit amount for a user
/// @param user The address of the user
/// @return The deposit amount for the user
function userDeposits(address user) external view returns (uint256);
// fetches and sorts the reserves for a pair
function getReserves(address tokenA, address tokenB, uint16 fee)
external
view
returns (uint256 reserveA, uint256 reserveB);
}
"
},
"src/interfaces/IFactory.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IFactory {
event PairCreated(
address indexed token0,
address indexed token1,
address pair,
uint16 fee,
uint256 allPairsLength
);
function possibleFees(uint256) external view returns (uint16);
function allPairs(uint256) external view returns (address);
function protocolToken() external view returns (address);
function getPairs(uint256 startIndex, uint256 numOfPairs)
external
view
returns (address[] memory, uint256);
function getPair(address tokenA, address tokenB, uint16 fee)
external
view
returns (address pair);
function feesForPair(address tokenA, address tokenB, uint256)
external
view
returns (uint16);
function getFeesForPair(address tokenA, address tokenB)
external
view
returns (uint16[8] memory fees);
function allPairsLength() external view returns (uint256);
function createPair(address tokenA, address tokenB, uint16 fee)
external
returns (address pair);
}
"
},
"src/interfaces/IPair.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IPair {
error BalanceOverflow();
error InsufficientFee();
error InsufficientInputAmount();
error InsufficientLiquidity();
error InsufficientLiquidityBurned();
error InsufficientLiquidityMinted();
error InsufficientOutputAmount();
error InvalidReceiver();
error K();
error NoBetterOracle();
error NoOracleFound();
error SenderNotFactory();
error SwapAmountTooLow();
event Burn(
address indexed sender,
uint256 amount0,
uint256 amount1,
address indexed to
);
event Mint(address indexed sender, uint256 amount0, uint256 amount1);
event Swap(
address indexed sender,
uint256 amount0In,
uint256 amount1In,
uint256 amount0Out,
uint256 amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
function antfarmOracle() external view returns (address);
function antfarmToken() external view returns (address);
function antfarmTokenReserve() external view returns (uint256);
function burn(address to, uint256 positionId, uint256 liquidity)
external
returns (uint256, uint256);
function claimDividend(address to, uint256 positionId)
external
returns (uint256 claimAmount);
function claimableDividends(address operator, uint256 positionId)
external
view
returns (uint256 amount);
function factory() external view returns (address);
function fee() external view returns (uint16);
function getFees(
uint256 amount0Out,
uint256 amount0In,
uint256 amount1Out,
uint256 amount1In
) external view returns (uint256 feeToPay);
function getPositionLP(address operator, uint256 positionId)
external
view
returns (uint128);
function getReserves()
external
view
returns (
uint112 _reserve0,
uint112 _reserve1,
uint32 _blockTimestampLast
);
function initialize(
address _token0,
address _token1,
uint16 _fee,
address _antfarmToken
) external;
function mint(address to, uint256 positionId) external returns (uint256);
function positions(address, uint256)
external
view
returns (uint128 lp, uint256 dividend, uint256 lastDividendPoints);
function scanOracles(uint112 maxReserve)
external
view
returns (address bestOracle);
function skim(address to) external;
function swap(uint256 amount0Out, uint256 amount1Out, address to)
external;
function sync() external;
function token0() external view returns (address);
function token1() external view returns (address);
function totalSupply() external view returns (uint256);
function updateOracle() external;
}
"
}
},
"settings": {
"remappings": [
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/"
],
"optimizer": {
"enabled": true,
"runs": 1000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "shanghai",
"viaIR": false
}
}}
Submitted on: 2025-10-20 16:17:14
Comments
Log in to comment.
No comments yet.