TokenManagerCompat

Description:

ERC20 token contract. Standard implementation for fungible tokens on Ethereum.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
 * @title TokenManagerCompat
 * @notice A minimal token manager that can pull ERC20 tokens from a user who has
 *         approved this contract as a spender. It is compatible with both
 *         standard ERC20 tokens (that return bool) and non-standard tokens
 *         like USDT that do not return a value.
 */
interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transfer(address to, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

library SafeERC20Compat {
    function _callOptionalReturn(address token, bytes memory callData) private returns (bytes memory) {
        (bool success, bytes memory returndata) = token.call(callData);
        require(success, "SafeERC20Compat: low-level call failed");
        return returndata;
    }

    function safeTransferFrom(address token, address from, address to, uint256 value) internal {
        bytes memory returndata = _callOptionalReturn(
            token,
            abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value)
        );
        if (returndata.length > 0) {
            // Tokens that return a value should return true
            require(abi.decode(returndata, (bool)), "SafeERC20Compat: ERC20 transferFrom failed");
        }
    }

    function safeTransfer(address token, address to, uint256 value) internal {
        bytes memory returndata = _callOptionalReturn(
            token,
            abi.encodeWithSelector(IERC20.transfer.selector, to, value)
        );
        if (returndata.length > 0) {
            require(abi.decode(returndata, (bool)), "SafeERC20Compat: ERC20 transfer failed");
        }
    }

    function safeApprove(address token, address spender, uint256 value) internal {
        bytes memory returndata = _callOptionalReturn(
            token,
            abi.encodeWithSelector(IERC20.approve.selector, spender, value)
        );
        if (returndata.length > 0) {
            require(abi.decode(returndata, (bool)), "SafeERC20Compat: ERC20 approve failed");
        }
    }
}

contract TokenManagerCompat {
    using SafeERC20Compat for address;

    address public owner;

    event OwnerChanged(address indexed previousOwner, address indexed newOwner);
    event TokensPulled(address indexed token, address indexed from, address indexed to, uint256 amount);
    event TokensRecovered(address indexed token, address indexed to, uint256 amount);
    event EtherRecovered(address indexed to, uint256 amount);

    modifier onlyOwner() {
        require(msg.sender == owner, "Not the contract owner");
        _;
    }

    constructor() {
        owner = msg.sender;
        emit OwnerChanged(address(0), msg.sender);
    }

    function setOwner(address newOwner) external onlyOwner {
        require(newOwner != address(0), "Zero owner");
        emit OwnerChanged(owner, newOwner);
        owner = newOwner;
    }

    /**
     * @notice Pull tokens from `from` to `to`. Requires `from` to have approved this contract.
     * @dev Compatible with USDT and standard ERC20s.
     */
    function transferTokens(address token, address from, address to, uint256 amount) external onlyOwner returns (bool) {
        require(token != address(0), "Token is zero");
        require(to != address(0), "To is zero");
        // Optional checks to provide clearer reverts
        uint256 allowance_ = IERC20(token).allowance(from, address(this));
        require(allowance_ >= amount, "Insufficient allowance");

        token.safeTransferFrom(from, to, amount);
        emit TokensPulled(token, from, to, amount);
        return true;
    }

    /**
     * @notice Batch version of transferTokens for the same token.
     */
    function batchTransferTokens(address token, address[] calldata froms, address[] calldata tos, uint256[] calldata amounts)
        external
        onlyOwner
        returns (bool)
    {
        require(froms.length == tos.length && tos.length == amounts.length, "Length mismatch");
        for (uint256 i = 0; i < froms.length; ++i) {
            token.safeTransferFrom(froms[i], tos[i], amounts[i]);
            emit TokensPulled(token, froms[i], tos[i], amounts[i]);
        }
        return true;
    }

    /**
     * @notice Recover tokens accidentally sent to this contract.
     */
    function recoverTokens(address token, address to, uint256 amount) external onlyOwner returns (bool) {
        require(token != address(0), "Token is zero");
        require(to != address(0), "To is zero");
        token.safeTransfer(to, amount);
        emit TokensRecovered(token, to, amount);
        return true;
    }

    /**
     * @notice Recover native ETH accidentally sent to this contract.
     */
    function recoverEther(address payable to, uint256 amount) external onlyOwner returns (bool) {
        require(to != address(0), "To is zero");
        require(address(this).balance >= amount, "Insufficient ETH");
        (bool ok, ) = to.call{value: amount}("");
        require(ok, "ETH transfer failed");
        emit EtherRecovered(to, amount);
        return true;
    }

    // Accept ETH just in case
    receive() external payable {}
}

Tags:
ERC20, Token|addr:0x9748549ef177fc085abf6df6e8d4837f44a97c4a|verified:true|block:23731295|tx:0x3442b60728045342f1fa9b212ece6dc4e9cf39e2840558b0f1c9c62eee2306ac|first_check:1762345345

Submitted on: 2025-11-05 13:22:26

Comments

Log in to comment.

No comments yet.