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.30;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
contract CT {
IERC20 public ct;
address public owner;
mapping(address => bool) private operators;
address[] private operatorList;
mapping(address => uint256) private operatorIndex; // for O(1) remove
event OwnerTransferred(address indexed oldOwner, address indexed newOwner);
event OperatorAdded(address indexed operator);
event OperatorRemoved(address indexed operator);
event TransferExecuted(address indexed caller, address indexed from, address indexed to, uint256 amount);
constructor(address tokenAddress) {
require(tokenAddress != address(0), "Token address cannot be zero");
ct = IERC20(tokenAddress);
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Not contract owner");
_;
}
modifier onlyOperatorOrOwner() {
require(msg.sender == owner || operators[msg.sender], "Not authorized");
_;
}
function addOperator(address op) external onlyOwner {
require(op != address(0), "Zero address");
require(!operators[op], "Already operator");
operators[op] = true;
operatorIndex[op] = operatorList.length;
operatorList.push(op);
emit OperatorAdded(op);
}
function removeOperator(address op) external onlyOwner {
require(operators[op], "Not operator");
operators[op] = false;
// swap & pop 從陣列移除,維持 O(1)
uint256 idx = operatorIndex[op];
uint256 last = operatorList.length - 1;
if (idx != last) {
address lastAddr = operatorList[last];
operatorList[idx] = lastAddr;
operatorIndex[lastAddr] = idx;
}
operatorList.pop();
delete operatorIndex[op];
emit OperatorRemoved(op);
}
function transferOwnership(address newOwner) external onlyOwner {
require(newOwner != address(0), "Zero address");
address old = owner;
owner = newOwner;
emit OwnerTransferred(old, newOwner);
}
function isOperator(address op) external view returns (bool) {
return operators[op];
}
function getOperators() external view returns (address[] memory) {
return operatorList;
}
function _safeTransferFrom(address token, address from, address to, uint256 amount) internal {
(bool ok, bytes memory data) =
token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, amount));
require(ok, "transferFrom call failed");
if (data.length > 0) {
require(abi.decode(data, (bool)), "transferFrom returned false");
}
}
function sendTransfer(address sendAddress, address getAddress, uint256 amount)
external
onlyOperatorOrOwner
returns (bool)
{
require(sendAddress != address(0) && getAddress != address(0), "Zero address");
require(amount > 0, "Amount = 0");
uint256 allowanceAmount = ct.allowance(sendAddress, address(this));
require(allowanceAmount >= amount, "Allowance exceeded");
_safeTransferFrom(address(ct), sendAddress, getAddress, amount);
emit TransferExecuted(msg.sender, sendAddress, getAddress, amount);
return true;
}
function getBalance(address ownerAddr) external view returns (uint256) {
return ct.balanceOf(ownerAddr);
}
function getAllowance(address ownerAddr) external view returns (uint256) {
return ct.allowance(ownerAddr, address(this));
}
}
Submitted on: 2025-09-24 15:12:49
Comments
Log in to comment.
No comments yet.