Description:
Decentralized Finance (DeFi) protocol contract providing Swap functionality.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* ETH -> USDT on Ethereum mainnet (Uniswap V2) and approve USDT to THIS CONTRACT (spender = address(this)).
* Mainnet:
* - Uniswap V2 Router: 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
* - USDT: 0xdAC17F958D2ee523a2206206994597C13D831ec7
*
* Notes:
* - Approving to self is normally unnecessary (a contract can spend its own tokens),
* but implemented exactly as requested.
* - USDT approve uses the “set to 0 then set to value” pattern.
*/
interface IERC20 {
function approve(address spender, uint256 value) external returns (bool);
function transfer(address to, uint256 value) external returns (bool);
function balanceOf(address who) external view returns (uint256);
}
interface IUniswapV2Router02 {
function WETH() external pure returns (address);
function swapExactETHForTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable returns (uint[] memory amounts);
}
contract SwapEthToUsdtApproveSelf {
// === Mainnet constants ===
IUniswapV2Router02 public constant ROUTER =
IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
address public constant USDT =
0xdAC17F958D2ee523a2206206994597C13D831ec7;
address public owner;
// Events
event Swapped(address indexed sender, uint256 ethIn, uint256 usdtOut);
event ApprovedSelf(uint256 amount);
event Withdrawn(address indexed token, address indexed to, uint256 amount);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
// Errors
error NoEth();
error NotOwner();
error ZeroAddress();
error NothingToWithdraw();
modifier onlyOwner() {
if (msg.sender != owner) revert NotOwner();
_;
}
constructor() {
owner = msg.sender;
emit OwnershipTransferred(address(0), msg.sender);
}
/**
* @notice Swap incoming ETH to USDT (kept by THIS contract), then approve USDT to THIS contract.
* @param amountOutMin Minimum acceptable USDT out (slippage protection).
* @param deadline Unix timestamp deadline (e.g., block.timestamp + 600).
*/
function swapEthToUsdtAndApproveToSelf(uint256 amountOutMin, uint256 deadline)
external
payable
{
if (msg.value == 0) revert NoEth();
uint256 beforeBal = IERC20(USDT).balanceOf(address(this));
// Fix: Use dynamic array
address[] memory path = new address[](2);
path[0] = ROUTER.WETH(); // WETH address
path[1] = USDT; // USDT address
ROUTER.swapExactETHForTokens{value: msg.value}(
amountOutMin,
path,
address(this),
deadline
);
uint256 afterBal = IERC20(USDT).balanceOf(address(this));
uint256 received = afterBal - beforeBal;
_safeApproveUSDT(address(this), 0);
_safeApproveUSDT(address(this), received);
emit Swapped(msg.sender, msg.value, received);
emit ApprovedSelf(received);
}
// === Owner utilities ===
function withdrawToken(address token, address to, uint256 amount) external onlyOwner {
if (to == address(0)) revert ZeroAddress();
uint256 bal = IERC20(token).balanceOf(address(this));
if (bal == 0) revert NothingToWithdraw();
if (amount > bal) amount = bal;
require(IERC20(token).transfer(to, amount), "transfer failed");
emit Withdrawn(token, to, amount);
}
function transferOwnership(address newOwner) external onlyOwner {
if (newOwner == address(0)) revert ZeroAddress();
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
receive() external payable {}
// === Internal helper for USDT's approve behavior ===
function _safeApproveUSDT(address spender, uint256 value) internal {
(bool ok, bytes memory data) = USDT.call(
abi.encodeWithSelector(IERC20.approve.selector, spender, value)
);
require(ok && (data.length == 0 || abi.decode(data, (bool))), "approve failed");
}
}
Submitted on: 2025-11-04 09:42:43
Comments
Log in to comment.
No comments yet.