I have been learning how to use Uniswap V2 recently and am trying to add logic on top of the original code. I want to have a fee of 1% on each swap transaction using the input token. I was successfully implemented the code for exact input (e.g., swap exact tokens for tokens), but I am struggling to implement the code for exact output (e.g., swap tokens for exact tokens), because the error that was shown is only execution reverted
from the remix IDE. Here is a snippet of my code.
note: you have to have some amount of the input token in this smart contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';
/*
Goerli network:
WETH: 0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6
*/
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.6;
interface IWETH {
function deposit() external payable;
function withdraw(uint) external;
function totalSupply() external view returns (uint);
function balanceOf(address account) external view returns (uint);
function transfer(address recipient, uint amount) external returns (bool);
function allowance(address owner, address spender)
external
view
returns (uint);
function approve(address spender, uint amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}
interface ISwapV2Router {
function factory() external pure returns (address);
function WETH() external pure returns (address);
//token exact amount in, token -> token 1
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
//token exact amount out, token -> token 2
function swapTokensForExactTokens(
uint amountOut,
uint amountInMax,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
//ETH exact amount in, exact ETH -> token 3
function swapExactETHForTokens
(uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable returns (uint[] memory amounts);
//ETH exact amount out, token -> exact ETH 4
function swapTokensForExactETH(
uint amountOut,
uint amountInMax,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
// token exact amount in, exact token -> ETH 5
function swapExactTokensForETH(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
)
external
returns (uint[] memory amounts);
// token exact amout out, ETH -> exact token 6
function swapETHForExactTokens(
uint amountOut,
address[] calldata path,
address to,
uint deadline)
external
payable
returns (uint[] memory amounts);
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;
// calculate maximum output token, useful for exact token -> token
function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
// calculate minimum input token, useful for token -> exact token
function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}
contract SwapCentral {
address public owner;
address payable public dev;
uint public exchangeFee = 1000; //1% = 0.01 = 1000/denomFee
uint public constant DENOM_FEE = 1e5;
address public immutable WETH;
address private constant UNISWAP_V2_ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
ISwapV2Router private constant V2_ROUTER = ISwapV2Router(UNISWAP_V2_ROUTER);
mapping (address => bool) public isExcludedFromFee;
constructor(address _WETH){
require(_WETH != address(0), "WETH can't be address zero");
WETH = _WETH;
owner = msg.sender;
isExcludedFromFee[msg.sender] = true;
dev = payable(msg.sender);
}
modifier onlyOwner(){
require(msg.sender == owner, "you're not the owner");
_;
}
//---------------Uniswap V2 Starts Here----------------------//
function v2GetEstimatedIn(uint amountOutDesired, address tokenIn, address tokenOut) public view returns (uint){
address[] memory path = createPath(tokenIn, tokenOut);
uint[] memory amount = V2_ROUTER.getAmountsIn(amountOutDesired, path);
return amount[0];
}
//1. token exact amount in, exact token -> token
function v2SwapExactTokensForTokens(
address caller,
address tokenIn,
address tokenOut,
uint amountIn,
uint amountOutMin,
uint deadlineInSeconds
) external returns (uint[] memory amounts)
{
uint[] memory dividedAmount = splitTokenValue(msg.sender, amountIn); //split the token
TransferHelper.safeTransferFrom(tokenIn, msg.sender, address(this), amountIn);
TransferHelper.safeApprove(tokenIn, UNISWAP_V2_ROUTER, dividedAmount[1]);
TransferHelper.safeTransfer(tokenIn, dev, dividedAmount[0]); //transfer to developer
address[] memory path = createPath(tokenIn, tokenOut);
amounts = V2_ROUTER.swapExactTokensForTokens(
dividedAmount[1],
amountOutMin,
path,
caller,
block.timestamp + deadlineInSeconds
);
}
//2. token exact amount out, token -> exact token
function v2SwapTokensForExactTokens(
address caller,
address tokenIn,
address tokenOut,
uint amountOutDesired,
uint amountInMax,
uint deadlineInSeconds
) public returns (uint[] memory amounts, bool success)
{
TransferHelper.safeTransferFrom(tokenIn, msg.sender, address(this), amountInMax);
TransferHelper.safeApprove(tokenIn, UNISWAP_V2_ROUTER, amountInMax); //approval for swapping
uint estAmountIn = v2GetEstimatedIn(amountOutDesired, tokenIn, tokenOut); //calculate estimation
uint[] memory dividedAmount = splitTokenValue(msg.sender, amountInMax); //split the token
require((estAmountIn + dividedAmount[0] <= amountInMax), "amountInMax is not sufficient");
// swapping with the amount of amountToSwap
address[] memory path = createPath(tokenIn, tokenOut);
amounts = V2_ROUTER.swapTokensForExactTokens(
amountOutDesired,
dividedAmount[1],
path,
caller,
block.timestamp + deadlineInSeconds
);
//transfer to dev
TransferHelper.safeTransfer(tokenIn, dev, dividedAmount[0]);
// refund the change to caller
if(amounts[0] < amountInMax){
TransferHelper.safeApprove(tokenIn, UNISWAP_V2_ROUTER, 0);
TransferHelper.safeTransfer(tokenIn, msg.sender, amountInMax - amounts[0] - dividedAmount[0]);
}
success = true;
}
//---------------Helper Functions----------------------//
function splitTokenValue(address userAddress, uint amount) private view returns (uint[] memory dividedValue){
uint fee;
isExcludedFromFee[userAddress] ? fee = 0: fee = exchangeFee;
dividedValue[0] = (amount * fee) / DENOM_FEE; //amountToDev
dividedValue[1] = amount - dividedValue[0]; //amountToSwap
require(amount >= dividedValue[1], "calculation error");
}
function createPath(address tokenIn, address tokenOut) private pure returns (address[] memory){
address[] memory path = new address[](2);
path[0] = tokenIn; //input token
path[1] = tokenOut; //output token
return path;
}
receive() external payable {}
fallback() external payable {}
}
Is there anything I missed? Any feedback is welcomed. Thank you very much!