文件 1 的 3:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 2 的 3:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
function owner() public view virtual returns (address) {
return _owner;
}
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 3 的 3:Tuition.sol
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
contract Tuition is Ownable {
address public TREASURY;
bool public locked;
mapping(address => bool) public isStaff;
mapping(address => bool) public alreadyPaid;
mapping(address => uint256) public balance;
constructor(
address newOwner,
address treasury,
address[] memory initialStaff
) {
for (uint256 i = 0; i < initialStaff.length; i++) {
isStaff[initialStaff[i]] = true;
}
TREASURY = treasury;
transferOwnership(newOwner);
}
modifier onlyStaff() {
require(isStaff[msg.sender] || msg.sender == owner(), "STAFF_ONLY");
_;
}
modifier contractNotLocked() {
require(!locked, "NOT_TAKING_PAYMENTS");
_;
}
function contribute() external payable contractNotLocked {
require(!alreadyPaid[msg.sender], "ALREADY_PAID");
require(msg.value == 1 ether, "WRONG_AMOUNT");
alreadyPaid[msg.sender] = true;
balance[msg.sender] = msg.value;
}
function refundUser(address account) external onlyStaff contractNotLocked {
require(alreadyPaid[account], "STUDENT_DIDNT_PAY");
require(balance[account] > 0, "NOTHING_TO_REFUND");
uint256 amountToRefund = balance[account];
balance[account] = 0;
alreadyPaid[account] = false;
(bool success, ) = account.call{value: amountToRefund}("");
require(success, "TRANSFER_FAILED");
}
function moveStudentFundsToTreasury(address account)
external
onlyStaff
contractNotLocked
{
require(alreadyPaid[account], "STUDENT_DIDNT_PAY");
require(balance[account] > 0, "NO_FUNDS_AVAILABLE");
uint256 amountToMove = balance[account];
balance[account] = 0;
alreadyPaid[account] = false;
(bool success, ) = TREASURY.call{value: amountToMove}("");
require(success, "TRANSFER_FAILED");
}
function manageStaff(address account, bool isAddingStaff)
external
onlyOwner
{
isStaff[account] = isAddingStaff;
}
function changeTreasuryAddress(address newTreasury) external onlyOwner {
TREASURY = newTreasury;
}
function permanentlyMoveAllFundsToTreasuryAndLockContract()
external
onlyOwner
{
locked = true;
(bool success, ) = TREASURY.call{value: address(this).balance}("");
require(success, "TRANSFER_FAILED");
}
}
{
"compilationTarget": {
"contracts/Tuition.sol": "Tuition"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"newOwner","type":"address"},{"internalType":"address","name":"treasury","type":"address"},{"internalType":"address[]","name":"initialStaff","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"TREASURY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"alreadyPaid","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newTreasury","type":"address"}],"name":"changeTreasuryAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"contribute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isStaff","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"locked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"isAddingStaff","type":"bool"}],"name":"manageStaff","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"moveStudentFundsToTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"permanentlyMoveAllFundsToTreasuryAndLockContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"refundUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]