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;
// 簡化 ERC20 介面
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);
// 注意:有的代幣 transfer/transferFrom 不回傳 bool,所以我們改用低階 call 來相容
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;
// 可選:為了查詢目前有哪些 operator,維護一份清單
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");
_;
}
// owner 或被授權的人都可執行
modifier onlyOperatorOrOwner() {
require(msg.sender == owner || operators[msg.sender], "Not authorized");
_;
}
// ===== 權限管理 =====
// 新增可操作人(只有 owner 能呼叫)
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);
}
// 撤銷可操作人(只有 owner 能呼叫)
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);
}
// 轉移 owner(只有 owner 能呼叫)
function transferOwnership(address newOwner) external onlyOwner {
require(newOwner != address(0), "Zero address");
address old = owner;
owner = newOwner;
emit OwnerTransferred(old, newOwner);
}
// 查詢某地址是否為 operator
function isOperator(address op) external view returns (bool) {
return operators[op];
}
// 列出目前所有 operator(for 介面查詢)
function getOperators() external view returns (address[] memory) {
return operatorList;
}
// ===== 業務邏輯 =====
// 安全版 transferFrom:相容不回傳 bool 的代幣
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) {
// 有的代幣會回傳 bool,有的什麼都不回
require(abi.decode(data, (bool)), "transferFrom returned false");
}
}
// 只有 owner 或任一被授權的 operator 能呼叫
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-19 12:47:48
Comments
Log in to comment.
No comments yet.