SwapEthToUsdtApproveSelf

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");
    }
}

Tags:
DeFi, Swap|addr:0x4c269c24c945cfeee1aa619144220a5e85b5ac2b|verified:true|block:23720966|tx:0x116aecdc4a4fa556d5128dc25e27fbba77a725633dedd01d1e0a1cb9d45134b8|first_check:1762245761

Submitted on: 2025-11-04 09:42:43

Comments

Log in to comment.

No comments yet.