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.17;
/**
* @title ModernTetherToken
* @dev Simplified implementation of the Tether token with modern security features
*/
contract ModernTetherToken {
// Events
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
event Paused(address account);
event Unpaused(address account);
event BlacklistUpdated(address indexed account, bool isBlacklisted);
event TokensIssued(address indexed to, uint256 amount);
event TokensRedeemed(address indexed from, uint256 amount);
event FeeParametersChanged(uint256 basisPointsRate, uint256 maximumFee);
// Token metadata
string private _name;
string private _symbol;
uint8 private constant _decimals = 6;
uint256 private _totalSupply;
// Role definitions
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
bytes32 public constant CONTROLLER_ROLE = keccak256("CONTROLLER_ROLE"); // Combined role for multiple functions
// Role storage
mapping(bytes32 => mapping(address => bool)) private _roles;
// Fee structure
uint256 public basisPointsRate;
uint256 public maximumFee;
// Blacklist functionality
mapping(address => bool) public isBlackListed;
// Pause state
bool private _paused;
// Balances and allowances
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
/**
* @dev Modifier to restrict function to role holders.
*/
modifier onlyRole(bytes32 role) {
require(hasRole(role, msg.sender), "Caller doesn't have required role");
_;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*/
modifier whenNotPaused() {
require(!_paused, "Contract is paused");
_;
}
/**
* @dev Constructor
* @param name_ The name of the token
* @param symbol_ The symbol of the token
* @param initialSupply The initial supply of tokens
* @param admin The address that will have admin role
*/
constructor(
string memory name_,
string memory symbol_,
uint256 initialSupply,
address admin
) {
_name = name_;
_symbol = symbol_;
// Setup roles
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(CONTROLLER_ROLE, admin);
// Mint initial supply
_mint(admin, initialSupply);
// Set default fee parameters
basisPointsRate = 0;
maximumFee = 0;
}
/**
* @dev Returns basic token information and state
*/
function getTokenInfo() public view returns (
string memory name,
string memory symbol,
uint8 decimals,
uint256 totalSupply,
bool isPaused,
uint256 feeRate,
uint256 maxFee
) {
return (
_name,
_symbol,
_decimals,
_totalSupply,
_paused,
basisPointsRate,
maximumFee
);
}
/**
* @dev Returns the balance of a specific account.
*/
function balanceOf(address account) public view returns (uint256) {
return _balances[account];
}
/**
* @dev Returns the remaining allowance of spender over owner's tokens.
*/
function allowance(address owner, address spender) public view returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev Checks if an account has a specific role.
*/
function hasRole(bytes32 role, address account) public view returns (bool) {
return _roles[role][account];
}
/**
* @dev Admin function to manage roles, pause state, and blacklist
* @param action 1=grantRole, 2=revokeRole, 3=pause, 4=unpause, 5=blacklist, 6=unblacklist
* @param target Address to affect (for role and blacklist actions)
* @param data Role bytes32 for role actions, ignored for other actions
*/
function adminControl(uint8 action, address target, bytes32 data) public onlyRole(DEFAULT_ADMIN_ROLE) {
if (action == 1) {
// Grant role
_grantRole(data, target);
} else if (action == 2) {
// Revoke role
_revokeRole(data, target);
} else if (action == 3) {
// Pause
require(!_paused, "Already paused");
_paused = true;
emit Paused(msg.sender);
} else if (action == 4) {
// Unpause
require(_paused, "Not paused");
_paused = false;
emit Unpaused(msg.sender);
} else if (action == 5) {
// Blacklist
require(!isBlackListed[target], "Already blacklisted");
isBlackListed[target] = true;
emit BlacklistUpdated(target, true);
} else if (action == 6) {
// Unblacklist
require(isBlackListed[target], "Not blacklisted");
isBlackListed[target] = false;
emit BlacklistUpdated(target, false);
} else {
revert("Invalid action");
}
}
/**
* @dev Combined token management function for controllers
* @param action 1=issue, 2=redeem, 3=destroyBlackFunds, 4=setFeeParameters
* @param target Address to affect
* @param amount Amount for token operations or basis points for fee setting
* @param maxFee Maximum fee (only used for action 4)
*/
function controllerAction(uint8 action, address target, uint256 amount, uint256 maxFee) public onlyRole(CONTROLLER_ROLE) {
if (action == 1) {
// Issue tokens
_mint(target, amount);
emit TokensIssued(target, amount);
} else if (action == 2) {
// Redeem tokens
if (target != msg.sender) {
uint256 currentAllowance = _allowances[target][msg.sender];
require(currentAllowance >= amount, "Exceeds allowance");
unchecked {
_approve(target, msg.sender, currentAllowance - amount);
}
}
_burn(target, amount);
emit TokensRedeemed(target, amount);
} else if (action == 3) {
// Destroy blacklisted funds
require(isBlackListed[target], "Not blacklisted");
uint256 balance = _balances[target];
_burn(target, balance);
emit TokensRedeemed(target, balance);
} else if (action == 4) {
// Set fee parameters
require(amount < 20, "Basis points too high");
require(maxFee < 50 * (10 ** _decimals), "Maximum fee too high");
basisPointsRate = amount;
maximumFee = maxFee;
emit FeeParametersChanged(amount, maxFee);
} else {
revert("Invalid action");
}
}
/**
* @dev Transfers tokens to a specified address
* @param to The recipient address
* @param amount The amount to transfer
* @return A boolean indicating if the operation was successful
*/
function transfer(address to, uint256 amount) public whenNotPaused returns (bool) {
_transfer(msg.sender, to, amount);
return true;
}
/**
* @dev Approves a spender to spend tokens
* @param spender The spender address
* @param amount The amount to approve
* @return A boolean indicating if the operation was successful
*/
function approve(address spender, uint256 amount) public returns (bool) {
_approve(msg.sender, spender, amount);
return true;
}
/**
* @dev Transfers tokens from one address to another using allowance
* @param from The sender address
* @param to The recipient address
* @param amount The amount to transfer
* @return A boolean indicating if the operation was successful
*/
function transferFrom(address from, address to, uint256 amount) public whenNotPaused returns (bool) {
_spendAllowance(from, msg.sender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Internal function to grant a role
*/
function _grantRole(bytes32 role, address account) internal {
if (!_roles[role][account]) {
_roles[role][account] = true;
emit RoleGranted(role, account, msg.sender);
}
}
/**
* @dev Internal function to revoke a role
*/
function _revokeRole(bytes32 role, address account) internal {
if (_roles[role][account]) {
_roles[role][account] = false;
emit RoleRevoked(role, account, msg.sender);
}
}
/**
* @dev Internal transfer function with fee handling
*/
function _transfer(address from, address to, uint256 amount) internal {
require(from != address(0), "Transfer from zero address");
require(to != address(0), "Transfer to zero address");
require(!isBlackListed[from], "Sender is blacklisted");
require(!isBlackListed[to], "Recipient is blacklisted");
uint256 fee = _calculateFee(amount);
uint256 sendAmount = amount - fee;
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "Exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
}
_balances[to] += sendAmount;
emit Transfer(from, to, sendAmount);
if (fee > 0) {
// Use first controller as fee recipient
address feeReceiver = msg.sender;
for (uint256 i = 0; i < 3; i++) { // Limit iterations for gas efficiency
if (hasRole(CONTROLLER_ROLE, feeReceiver)) {
break;
}
feeReceiver = msg.sender; // Fallback to sender if no controller found
}
_balances[feeReceiver] += fee;
emit Transfer(from, feeReceiver, fee);
}
}
/**
* @dev Calculates the fee for a given amount
*/
function _calculateFee(uint256 amount) internal view returns (uint256) {
uint256 fee = (amount * basisPointsRate) / 10000;
if (fee > maximumFee) {
fee = maximumFee;
}
return fee;
}
/**
* @dev Internal approval function
*/
function _approve(address owner, address spender, uint256 amount) internal {
require(owner != address(0), "Approve from zero address");
require(spender != address(0), "Approve to zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Internal function to spend allowance
*/
function _spendAllowance(address owner, address spender, uint256 amount) internal {
uint256 currentAllowance = _allowances[owner][spender];
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "Insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Internal mint function
*/
function _mint(address account, uint256 amount) internal {
require(account != address(0), "Mint to zero address");
_totalSupply += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
}
/**
* @dev Internal burn function
*/
function _burn(address account, uint256 amount) internal {
require(account != address(0), "Burn from zero address");
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "Burn exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
}
}
Submitted on: 2025-10-18 09:06:42
Comments
Log in to comment.
No comments yet.