// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import "./e2XUsernames.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract TCPointContract is Ownable { e2XUsernames public userDataContract; IERC20 public e2xToken; IERC20 public e2wToken; uint256 public pointsPerToken; uint256 public defaultDeductionPoints = 1; // Default deduction points address public tcPointBurntAccount; address public modifyAdmin; address public withdrawAdmin; address public e2WContract; string public contractVersion; uint8 public constant TOKEN_DECIMALS = 18; // Decimal scale of the token struct TCPointBalance { uint128 balance; } modifier onlyAdminOrAuthorizedContract() { require(msg.sender == modifyAdmin || authorizedContracts[msg.sender] || msg.sender == owner(), "Not authorized"); _; } modifier onlyWithdrawAdmin() { require(msg.sender == withdrawAdmin || msg.sender == owner(), "Not authorized"); _; } mapping(address => TCPointBalance) public tcBalance; mapping(address => uint256) public totalTCBalance; mapping(address => bool) public tcPointsAdmins; mapping(address => bool) public authorizedContracts; event PointsRatioUpdated(uint256 newPointsPerToken); event TCPUpdated(address user, uint256 amount, bool added); event AdminAdded(address indexed admin); event AdminRemoved(address indexed admin); event ContractAuthorized(address indexed contractAddress); event ContractUnauthorized(address indexed contractAddress); event TCPointDeducted(address indexed from, address indexed to, uint256 amount); event WithdrawAdminChanged(address indexed previousAdmin, address indexed newAdmin); event ModifyAdminChanged(address indexed previousAdmin, address indexed newAdmin); event E2XWithdrawn(uint256 amount); event TCPointsTransferred(address indexed to, uint256 amount); constructor(address _initialOwner, address _userDataContract, address _e2xToken, address _withdrawAdmin, address _e2WContract, address _e2wToken) Ownable(_initialOwner) { userDataContract = e2XUsernames(_userDataContract); e2xToken = IERC20(_e2xToken); e2wToken = IERC20(_e2wToken); pointsPerToken = 100; withdrawAdmin = _withdrawAdmin; e2WContract = _e2WContract; contractVersion = "0.16"; } function setModifyAdmin(address newAdmin) external onlyOwner { require(newAdmin != address(0), "Invalid address"); modifyAdmin = newAdmin; emit ModifyAdminChanged(modifyAdmin, newAdmin); } function setPointsPerToken(uint256 _newPointsPerToken) external onlyAdminOrAuthorizedContract { pointsPerToken = _newPointsPerToken; emit PointsRatioUpdated(_newPointsPerToken); } function addAdmin(address _admin) external onlyOwner { require(_admin != address(0), "Invalid admin address"); tcPointsAdmins[_admin] = true; emit AdminAdded(_admin); } function removeAdmin(address _admin) external onlyOwner { require(tcPointsAdmins[_admin], "Address is not an admin"); tcPointsAdmins[_admin] = false; emit AdminRemoved(_admin); } function authorizeContract(address _contractAddress) external onlyOwner { require(_contractAddress != address(0), "Invalid contract address"); authorizedContracts[_contractAddress] = true; emit ContractAuthorized(_contractAddress); } function unauthorizeContract(address _contractAddress) external onlyOwner { require(authorizedContracts[_contractAddress], "Contract is not authorized"); authorizedContracts[_contractAddress] = false; emit ContractUnauthorized(_contractAddress); } function transferE2XToken(uint256 _amount) external { require(_amount > 0, "Amount must be greater than 0"); require(e2xToken.transferFrom(msg.sender, address(this), _amount), "E2X token transfer failed"); address userAddress = msg.sender; uint256 tcPointsToMint = _amount * pointsPerToken; tcBalance[userAddress].balance += uint128(tcPointsToMint); totalTCBalance[userAddress] += tcPointsToMint; emit TCPUpdated(userAddress, tcPointsToMint, true); } function transferTCP(address _toUser, uint256 _amount) external { address senderAddress = msg.sender; uint128 amount = uint128(_amount); deductDefaultTCPoints(senderAddress); // Deduct default TCPoints before transfer uint256 senderBalanceDecimal = uint256(tcBalance[senderAddress].balance) * (10**TOKEN_DECIMALS); uint256 amountDecimal = uint256(amount) * (10**TOKEN_DECIMALS); require(senderBalanceDecimal >= amountDecimal, "Insufficient TCP balance"); uint256 newSenderBalanceDecimal = senderBalanceDecimal - amountDecimal; uint256 newRecipientBalanceDecimal = uint256(tcBalance[_toUser].balance) * (10**TOKEN_DECIMALS) + amountDecimal; tcBalance[senderAddress].balance = uint128(newSenderBalanceDecimal / (10**TOKEN_DECIMALS)); tcBalance[_toUser].balance = uint128(newRecipientBalanceDecimal / (10**TOKEN_DECIMALS)); emit TCPointDeducted(senderAddress, e2WContract, _amount); } function transferTCPBetweenMembers(string calldata _fromUser, string calldata _toUser, uint256 _amount) external { address senderAddress = msg.sender; address fromUserAddress = userDataContract.getAddressByUser(_fromUser); address toUserAddress = userDataContract.getAddressByUser(_toUser); require(fromUserAddress == senderAddress, "Sender is not the owner of the e2xdata address"); require(fromUserAddress != address(0) && toUserAddress != address(0), "Invalid e2xdata addresses"); deductDefaultTCPoints(fromUserAddress); // Deduct default TCPoints before transfer uint128 amount = uint128(_amount); uint256 fromUserBalanceDecimal = uint256(tcBalance[fromUserAddress].balance) * (10**TOKEN_DECIMALS); uint256 amountDecimal = uint256(amount) * (10**TOKEN_DECIMALS); require(fromUserBalanceDecimal >= amountDecimal, "Insufficient TCP balance"); uint256 newSenderBalanceDecimal = fromUserBalanceDecimal - amountDecimal; uint256 newRecipientBalanceDecimal = uint256(tcBalance[toUserAddress].balance) * (10**TOKEN_DECIMALS) + amountDecimal; tcBalance[fromUserAddress].balance = uint128(newSenderBalanceDecimal / (10**TOKEN_DECIMALS)); tcBalance[toUserAddress].balance = uint128(newRecipientBalanceDecimal / (10**TOKEN_DECIMALS)); emit TCPointDeducted(fromUserAddress, e2WContract, _amount); } function withdrawE2XToken(uint256 amount) external onlyWithdrawAdmin { require(amount > 0, "Withdrawal amount must be greater than 0"); uint256 balance = e2xToken.balanceOf(address(this)); require(amount <= balance, "Insufficient balance in the contract"); e2xToken.transfer(msg.sender, amount); emit E2XWithdrawn(amount); } function transferTCPointsToE2W(uint256 _amount) external onlyAdminOrAuthorizedContract { require(_amount > 0, "Amount must be greater than 0"); require(tcBalance[address(this)].balance >= _amount, "Insufficient TCPoints balance"); tcBalance[address(this)].balance -= uint128(_amount); emit TCPointsTransferred(e2WContract, _amount); } function fraction(uint256 numerator, uint256 denominator) internal pure returns (uint256) { require(denominator > 0, 'TCPointContract::fraction: division by zero'); if (numerator == 0) return 0; uint256 result = (numerator * (1 ether)) / denominator; require(result <= type(uint224).max, 'TCPointContract::fraction: overflow'); return result; } function deductDefaultTCPoints(address _fromUser) internal { require(tcBalance[_fromUser].balance >= defaultDeductionPoints, "Insufficient TCP balance"); // Convert 1 e2W token to ETH uint256 oneE2WTokenInEth = 1 * (10 ** TOKEN_DECIMALS); // Check if the sender holds 1 token of e2W (in ETH) if (e2wToken.balanceOf(_fromUser) >= oneE2WTokenInEth) { tcBalance[_fromUser].balance -= uint128(defaultDeductionPoints); tcBalance[e2WContract].balance += uint128(defaultDeductionPoints); emit TCPointDeducted(_fromUser, e2WContract, defaultDeductionPoints); } else { // If not, deduct 2 times of setDefaultDeductionPoints uint256 doubleDeduction = 2 * defaultDeductionPoints; require(tcBalance[_fromUser].balance >= doubleDeduction, "Insufficient TCP balance for double deduction"); tcBalance[_fromUser].balance -= uint128(doubleDeduction); tcBalance[e2WContract].balance += uint128(doubleDeduction); emit TCPointDeducted(_fromUser, e2WContract, doubleDeduction); } } function setDefaultDeductionPoints(uint256 _newDefaultDeductionPoints) external onlyOwner { defaultDeductionPoints = _newDefaultDeductionPoints; } }