// SPDX-License-Identifier: MIT pragma solidity 0.8.4; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol"; contract SimpleFund { using SafeERC20 for IERC20; address public manager; address[] public investedTokens; address private constant UNISWAP_V3_ROUTER = 0x5615CDAb10dc425a742d643d949a7F474C01abc4; address private constant WETH_ADDRESS = 0x122013fd7dF1C6F636a5bb8f03108E876548b455; event TokenTraded(address indexed tokenIn, uint256 amountIn, address indexed tokenOut, uint256 amountOut, uint24 fee); constructor(address _manager) { manager = _manager; } // Allow anyone to send CELO to the contract receive() external payable {} function withdraw() public { uint256 wethBalance = IERC20(WETH_ADDRESS).balanceOf(address(this)); require(wethBalance > 0, "No WETH to withdraw"); IERC20(WETH_ADDRESS).safeTransfer(msg.sender, wethBalance); } function trade( address _tokenIn, uint256 _amountIn, address _tokenOut, uint256 _amountOutMin, uint256 _deadline, uint24 _fee ) external { require(msg.sender == manager, "Only the fund manager can execute trades"); require(_deadline >= block.timestamp, "Deadline must be in the future"); address tokenIn = _tokenIn; if (tokenIn == address(0)) { tokenIn = WETH_ADDRESS; } uint24 fee = _fee; if (fee == 0) { fee = 3000; // Default to 0.3% fee tier } // Approve Uniswap router to spend the input token IERC20(tokenIn).safeApprove(UNISWAP_V3_ROUTER, _amountIn); ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({ tokenIn: tokenIn, tokenOut: _tokenOut, fee: fee, recipient: address(this), deadline: _deadline, amountIn: _amountIn, amountOutMinimum: _amountOutMin, sqrtPriceLimitX96: 0 }); uint256 amountOut = ISwapRouter(UNISWAP_V3_ROUTER).exactInputSingle(params); updateInvestedTokens(_tokenOut); emit TokenTraded(tokenIn, _amountIn, _tokenOut, amountOut, fee); } function updateInvestedTokens(address _newToken) private { for (uint256 i = 0; i < investedTokens.length; i++) { address token = investedTokens[i]; uint256 balance = IERC20(token).balanceOf(address(this)); if (balance == 0) { // Remove the token from the array if the balance is zero investedTokens[i] = investedTokens[investedTokens.length - 1]; investedTokens.pop(); i--; } } // Add the newly swapped token to the array if not already present if (_newToken != address(0) && !isTokenInvested(_newToken)) { investedTokens.push(_newToken); } } function isTokenInvested(address _token) private view returns (bool) { for (uint256 i = 0; i < investedTokens.length; i++) { if (investedTokens[i] == _token) { return true; } } return false; } }