编译器
0.8.24+commit.e11b9ed9
文件 1 的 5:Context.sol
pragma solidity ^0.8.20;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
文件 2 的 5:IManager.sol
pragma solidity ^0.8.24;
interface IManager {
event WithdrawalRequested(uint256 indexed id, address indexed recipient, uint256 amount);
event WithdrawalApproved(uint256 indexed id, address indexed approver);
event WithdrawalExecuted(uint256 indexed id, address indexed recipient, uint256 amount);
function setBuyFeePercentage(uint256 _newBuyFeePercentage) external;
function setSellFeePercentage(uint256 _newSellFeePercentage) external;
function addToWhitelist(address _account) external;
function removeFromWhitelist(address _account) external;
function requestWithdrawal(uint256 _amount, address payable _recipient) external;
function approveWithdrawal(uint256 _requestId) external;
}
文件 3 的 5:ITokenERC20.sol
pragma solidity ^0.8.24;
interface ITokenERC20 {
event ManagerChanged(address indexed _manager);
event BuyFeePercentageChanged(uint256 _previousBuyFee, uint256 _newBuyFee);
event SellFeePercentageChanged(uint256 _previousSellFee, uint256 _newSellFee);
event Whitelisted(address indexed _account);
event RemovedFromWhitelist(address indexed _account);
function isWhitelisted(address _account) external view returns (bool);
function setManager(address payable _management) external;
function renounceOwnership() external;
function addToWhitelist(address _account) external;
function removeFromWhitelist(address _account) external;
function setBuyFeePercentage(uint256 _newBuyFeePercentage) external;
function setSellFeePercentage(uint256 _newSellFeePercentage) external;
}
文件 4 的 5:Manager.sol
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/access/Ownable.sol";
import "./ITokenERC20.sol";
import {IManager} from "./IManager.sol";
contract Manager is IManager, Ownable {
struct WithdrawalRequest {
uint256 id;
uint256 amount;
address payable recipient;
bool executed;
uint256 approvals;
}
ITokenERC20 public tokenContract;
uint256 public requestCount = 0;
uint256 public constant requiredApprovals = 2;
mapping(uint256 => WithdrawalRequest) public withdrawalRequests;
mapping(uint256 => mapping(address => bool)) public approvals;
mapping(address => bool) public approvers;
constructor(address _tokenContract) Ownable(msg.sender) {
require(_tokenContract != address(0), "Invalid token contract address");
tokenContract = ITokenERC20(_tokenContract);
}
modifier onlyApprover() {
require(approvers[msg.sender], "Not an approver");
_;
}
function setApprover(address _approver, bool _status) external onlyOwner {
approvers[_approver] = _status;
}
function setBuyFeePercentage(uint256 _newBuyFeePercentage) external onlyOwner {
tokenContract.setBuyFeePercentage(_newBuyFeePercentage);
}
function setSellFeePercentage(uint256 _newSellFeePercentage) external onlyOwner {
tokenContract.setSellFeePercentage(_newSellFeePercentage);
}
function addToWhitelist(address _account) external onlyOwner {
tokenContract.addToWhitelist(_account);
}
function removeFromWhitelist(address _account) external onlyOwner {
tokenContract.removeFromWhitelist(_account);
}
function requestWithdrawal(uint256 _amount, address payable _recipient) external onlyOwner {
require(_recipient != address(0), "Invalid recipient address");
(bool success, bytes memory result) = address(tokenContract).staticcall(
abi.encodeWithSelector(bytes4(keccak256("balanceOf(address)")), address(this))
);
require(success, "Failed to get contract balance");
require(_amount <= abi.decode(result, (uint256)), "Insufficient balance");
uint256 requestId = ++requestCount;
withdrawalRequests[requestId] = WithdrawalRequest({
id: requestId,
amount: _amount,
recipient: _recipient,
executed: false,
approvals: 0
});
emit WithdrawalRequested(requestId, _recipient, _amount);
}
function approveWithdrawal(uint256 _requestId) external onlyApprover {
WithdrawalRequest storage request = withdrawalRequests[_requestId];
require(request.id == _requestId, "Invalid request id");
require(!request.executed, "Request already executed");
require(!approvals[_requestId][msg.sender], "Already approved");
approvals[_requestId][msg.sender] = true;
request.approvals++;
emit WithdrawalApproved(_requestId, msg.sender);
if (request.approvals >= requiredApprovals) {
executeWithdrawal(_requestId);
}
}
function executeWithdrawal(uint256 _requestId) internal {
WithdrawalRequest storage request = withdrawalRequests[_requestId];
require(request.id == _requestId, "Invalid request id");
require(!request.executed, "Request already executed");
require(request.approvals >= requiredApprovals, "Not enough approvals");
request.executed = true;
bytes memory data = abi.encodeWithSignature("transfer(address,uint256)", request.recipient, request.amount);
(bool success,) = address(tokenContract).call(data);
require(success, "Failed to transfer tokens");
emit WithdrawalExecuted(_requestId, request.recipient, request.amount);
}
}
文件 5 的 5:Ownable.sol
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
{
"compilationTarget": {
"contracts/Manager.sol": "Manager"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_tokenContract","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"approver","type":"address"}],"name":"WithdrawalApproved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawalExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawalRequested","type":"event"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"addToWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"approvals","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_requestId","type":"uint256"}],"name":"approveWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"approvers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"removeFromWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requestCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address payable","name":"_recipient","type":"address"}],"name":"requestWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requiredApprovals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_approver","type":"address"},{"internalType":"bool","name":"_status","type":"bool"}],"name":"setApprover","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newBuyFeePercentage","type":"uint256"}],"name":"setBuyFeePercentage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newSellFeePercentage","type":"uint256"}],"name":"setSellFeePercentage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tokenContract","outputs":[{"internalType":"contract ITokenERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"withdrawalRequests","outputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"recipient","type":"address"},{"internalType":"bool","name":"executed","type":"bool"},{"internalType":"uint256","name":"approvals","type":"uint256"}],"stateMutability":"view","type":"function"}]