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 {}
}
Submitted on: 2025-11-05 13:22:26
Comments
Log in to comment.
No comments yet.