编译器
0.8.27+commit.40a35a09
文件 1 的 6: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 的 6:IERC20.sol
pragma solidity ^0.8.20;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
文件 3 的 6:IInfiniteGame.sol
pragma solidity ^0.8.27;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IInfiniteGame {
event Played(address indexed player, uint256 paid, uint256 round);
event RoundCompleted(
address indexed winner,
uint256 winnerPrize,
uint256 miscAmount,
uint256 retainedAmount
);
event ERC20Withdrawn(address indexed token, uint256 amount);
event SharesSet(uint256 winnerShare, uint256 miscShare);
event InitialCountdownSet(uint256 newDuration);
event PlaysPerHourTargetSet(uint16 newTarget);
event FeeUpdated(uint256 newFee);
event CooldownTimeSet(uint256 newCooldownTime);
error GameNotStarted();
error GameIsStarted();
error NextRoundPending();
error NotAllowedDuringRound();
error AwaitingCooldown();
error DurationTooLow();
error InvalidAdjustment();
error SharesAlreadyUpdatedThisRound();
error CannotUpdateYet();
error NotAuthorized();
function play() external;
function completeRound() external;
function setInitialCountdown(uint16 newDuration) external;
function setPlaysPerHourTarget(uint16 newTarget) external;
function setShares(uint8 newWinnerShare, uint8 newMiscShare) external;
function setCooldownTime(uint16 newCooldownTime) external;
function setMiscRecipient(address newRecipient) external;
function setPlayFee(uint256 newFee) external;
function withdrawERC20(address tokenAddress) external;
function setNewAdmin(address newAdmin) external;
function MINIMUM_RESET_TIME() external pure returns (uint16);
function TIME_SUBTRACTION() external pure returns (uint256);
function ADMIN_PERIOD() external pure returns (uint256);
function currentPrize() external view returns (uint256);
function roundDeadline() external view returns (uint256);
function startTime() external view returns (uint256);
function roundStartTime() external view returns (uint256);
function lastPlayedAt() external view returns (uint256);
function cooldownUntil() external view returns (uint256);
function lastFeeUpdate() external view returns (uint256);
function playFee() external view returns (uint256);
function initialCountdown() external view returns (uint256);
function countDown() external view returns (uint256);
function roundPlayCount() external view returns (uint256);
function totalPlayCount() external view returns (uint256);
function finalizedRounds() external view returns (uint256);
function playsPerHourTarget() external view returns (uint16);
function lastPlaysPerHourUpdate() external view returns (uint256);
function lastShareUpdateRound() external view returns (uint256);
function cooldownTime() external view returns (uint256);
function winnerShare() external view returns (uint8);
function miscShare() external view returns (uint8);
function latestPlayer() external view returns (address);
function miscRecipient() external view returns (address);
function gameToken() external view returns (IERC20);
function playerPlayCount(address player) external view returns (uint256);
function admin() external view returns (address);
}
文件 4 的 6:InfiniteGame.sol
pragma solidity ^0.8.27;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./interfaces/IInfiniteGame.sol";
contract InfiniteGame is ReentrancyGuard, Ownable, IInfiniteGame {
uint16 public constant MINIMUM_RESET_TIME = 1 minutes;
uint256 public constant TIME_SUBTRACTION = 1 seconds;
uint256 public constant ADMIN_PERIOD = 60 days;
address internal _miscRecipient;
address internal _latestPlayer;
address internal _admin;
uint256 internal _launchTime;
uint256 internal _startTime;
uint256 internal _roundStartTime;
uint256 internal _lastPlayedAt;
uint256 internal _cooldownUntil;
uint256 internal _lastFeeUpdate;
uint256 internal _playFee;
uint256 internal _countDown;
uint256 internal _roundPlayCount;
uint256 internal _totalPlayCount;
uint256 internal _finalizedRounds;
uint256 internal _lastOverride;
uint256 internal _lastShareUpdateRound;
uint16 internal _initialCountdown = 1 hours;
uint16 internal _cooldownTime = 10 minutes;
uint16 internal _playsPerHourTarget = 100;
uint8 internal _winnerShare = 50;
uint8 internal _miscShare = 0;
IERC20 internal immutable _gameToken;
mapping(address => uint256) internal _playCounts;
constructor(
address gameToken_,
uint256 startTime_,
address admin_
) Ownable(msg.sender) {
_gameToken = IERC20(gameToken_);
_launchTime = block.timestamp;
_countDown = _initialCountdown;
_startTime = startTime_;
_roundStartTime = startTime_;
_lastFeeUpdate = startTime_;
_playFee = _gameToken.totalSupply() / 200_000;
_admin = admin_;
}
modifier onlyDuringCooldown() {
uint256 timeSinceCooldownStart = _cooldownUntil - _cooldownTime;
if (
block.timestamp <= timeSinceCooldownStart ||
block.timestamp >= _cooldownUntil
) {
revert NotAllowedDuringRound();
}
_;
}
modifier onlyOncePerDay() {
if (block.timestamp < _lastOverride + 1 days) {
revert CannotUpdateYet();
}
_;
}
modifier onlyOwnerOrAdmin() {
bool isOwner = msg.sender == owner();
bool isValidAdmin = msg.sender == _admin &&
block.timestamp < _launchTime + ADMIN_PERIOD;
if (!isOwner && !isValidAdmin) {
revert NotAuthorized();
}
_;
}
function play() external nonReentrant {
if (block.timestamp < _startTime) {
revert GameNotStarted();
}
if (block.timestamp < _cooldownUntil) {
revert AwaitingCooldown();
}
if (block.timestamp >= roundDeadline()) {
revert NextRoundPending();
}
require(
_gameToken.transferFrom(msg.sender, address(this), _playFee),
"Transfer failed"
);
if (_roundPlayCount == 0) {
_roundStartTime = block.timestamp;
}
_lastPlayedAt = block.timestamp;
_latestPlayer = msg.sender;
unchecked {
_roundPlayCount++;
_totalPlayCount++;
_playCounts[msg.sender]++;
}
if (_countDown > MINIMUM_RESET_TIME) {
unchecked {
_countDown = _countDown > MINIMUM_RESET_TIME + TIME_SUBTRACTION
? _countDown - TIME_SUBTRACTION
: MINIMUM_RESET_TIME;
}
}
if (block.timestamp >= _lastFeeUpdate + 1 hours) {
_updateFee();
}
emit Played(msg.sender, _playFee, _finalizedRounds + 1);
}
function completeRound() external nonReentrant {
uint256 deadline = roundDeadline();
if (block.timestamp < deadline) {
revert NotAllowedDuringRound();
}
if (_latestPlayer != address(0)) {
uint256 currentBalance = _gameToken.balanceOf(address(this));
uint256 winnerPrize = (currentBalance * _winnerShare) / 100;
uint256 miscAmount = (currentBalance * _miscShare) / 100;
uint256 retainedAmount = currentBalance - winnerPrize - miscAmount;
_cooldownUntil = block.timestamp + _cooldownTime;
_lastPlayedAt = _cooldownUntil;
_countDown = _initialCountdown;
_roundPlayCount = 0;
_finalizedRounds++;
require(
_gameToken.transfer(_latestPlayer, winnerPrize),
"Prize transfer failed"
);
if (_miscRecipient != address(0) && miscAmount > 0) {
require(
_gameToken.transfer(_miscRecipient, miscAmount),
"Misc transfer failed"
);
}
emit RoundCompleted(
_latestPlayer,
winnerPrize,
miscAmount,
retainedAmount
);
_latestPlayer = address(0);
}
}
function setInitialCountdown(uint16 newDuration) external onlyOwner {
if (newDuration < MINIMUM_RESET_TIME) revert DurationTooLow();
_initialCountdown = newDuration;
emit InitialCountdownSet(newDuration);
}
function setPlaysPerHourTarget(
uint16 newTarget
) external onlyOwnerOrAdmin onlyOncePerDay {
if (
newTarget < _playsPerHourTarget / 4 ||
newTarget > _playsPerHourTarget * 4
) {
revert InvalidAdjustment();
}
_playsPerHourTarget = newTarget;
_lastOverride = block.timestamp;
emit PlaysPerHourTargetSet(newTarget);
}
function setShares(
uint8 newWinnerShare,
uint8 newMiscShare
) external onlyOwner onlyDuringCooldown {
if (_lastShareUpdateRound == _finalizedRounds) {
revert SharesAlreadyUpdatedThisRound();
}
if (
newWinnerShare + newMiscShare == 0 ||
newWinnerShare + newMiscShare > 100
) {
revert InvalidAdjustment();
}
_winnerShare = newWinnerShare;
_miscShare = newMiscShare;
_lastShareUpdateRound = _finalizedRounds;
emit SharesSet(newWinnerShare, newMiscShare);
}
function setCooldownTime(uint16 newCooldownTime) external onlyOwner {
if (newCooldownTime < 1 minutes || newCooldownTime > 12 hours)
revert InvalidAdjustment();
_cooldownTime = newCooldownTime;
emit CooldownTimeSet(newCooldownTime);
}
function setMiscRecipient(address newRecipient) external onlyOwner {
_miscRecipient = newRecipient;
}
function setPlayFee(
uint256 newFee
) external onlyOwnerOrAdmin onlyOncePerDay {
if (newFee < _playFee / 10 || newFee > _playFee * 10) {
revert InvalidAdjustment();
}
_playFee = newFee;
_lastOverride = block.timestamp;
emit FeeUpdated(_playFee);
}
function withdrawERC20(address tokenAddress) external onlyOwner {
require(
tokenAddress != address(_gameToken),
"Cannot withdraw game token"
);
IERC20 token = IERC20(tokenAddress);
uint256 balance = token.balanceOf(address(this));
require(balance > 0, "No tokens to withdraw");
require(token.transfer(msg.sender, balance), "Transfer failed");
emit ERC20Withdrawn(tokenAddress, balance);
}
function setNewAdmin(address newAdmin) external {
if (
msg.sender != _admin ||
block.timestamp >= _launchTime + ADMIN_PERIOD
) revert NotAuthorized();
_admin = newAdmin;
}
function _abs(int256 x) internal pure returns (uint256 z) {
unchecked {
z = (uint256(x) + uint256(x >> 255)) ^ uint256(x >> 255);
}
}
function _updateFee() private {
uint256 secsSinceRoundStart = block.timestamp - _roundStartTime;
if (secsSinceRoundStart == 0) {
return;
}
uint256 playsPerHour = (_roundPlayCount * 3600) / secsSinceRoundStart;
if (playsPerHour < _playsPerHourTarget) {
_playFee = (_playFee * 80) / 100;
} else {
_playFee = (_playFee * 120) / 100;
}
_lastFeeUpdate = block.timestamp;
emit FeeUpdated(_playFee);
}
function currentPrize() public view returns (uint256) {
return (_gameToken.balanceOf(address(this)) * _winnerShare) / 100;
}
function roundDeadline() public view returns (uint256) {
if (_cooldownUntil > block.timestamp) {
return _cooldownUntil;
}
uint256 startingPoint = _roundStartTime < _lastPlayedAt
? _lastPlayedAt
: _roundStartTime;
return startingPoint + _countDown;
}
function startTime() external view returns (uint256) {
return _startTime;
}
function roundStartTime() external view returns (uint256) {
return _roundStartTime;
}
function lastPlayedAt() external view returns (uint256) {
return _lastPlayedAt;
}
function cooldownUntil() external view returns (uint256) {
return _cooldownUntil;
}
function lastFeeUpdate() external view returns (uint256) {
return _lastFeeUpdate;
}
function playFee() external view returns (uint256) {
return _playFee;
}
function initialCountdown() external view returns (uint256) {
return _initialCountdown;
}
function countDown() external view returns (uint256) {
return _countDown;
}
function roundPlayCount() external view returns (uint256) {
return _roundPlayCount;
}
function totalPlayCount() external view returns (uint256) {
return _totalPlayCount;
}
function finalizedRounds() external view returns (uint256) {
return _finalizedRounds;
}
function playsPerHourTarget() external view returns (uint16) {
return _playsPerHourTarget;
}
function lastPlaysPerHourUpdate() external view returns (uint256) {
return _lastOverride;
}
function lastShareUpdateRound() external view returns (uint256) {
return _lastShareUpdateRound;
}
function cooldownTime() external view returns (uint256) {
return _cooldownTime;
}
function winnerShare() external view returns (uint8) {
return _winnerShare;
}
function miscShare() external view returns (uint8) {
return _miscShare;
}
function latestPlayer() external view returns (address) {
return _latestPlayer;
}
function miscRecipient() external view returns (address) {
return _miscRecipient;
}
function gameToken() external view returns (IERC20) {
return _gameToken;
}
function playerPlayCount(address player) external view returns (uint256) {
return _playCounts[player];
}
function admin() external view returns (address) {
return _admin;
}
}
文件 5 的 6: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);
}
}
文件 6 的 6:ReentrancyGuard.sol
pragma solidity ^0.8.20;
abstract contract ReentrancyGuard {
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
_status = ENTERED;
}
function _nonReentrantAfter() private {
_status = NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
{
"compilationTarget": {
"src/InfiniteGame.sol": "InfiniteGame"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
":@uniswap/v3-core/=lib/v3-core/",
":@uniswap/v3-periphery/=lib/v3-periphery/",
":ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
":forge-std/=lib/forge-std/src/",
":halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":v3-core/=lib/v3-core/",
":v3-periphery/=lib/v3-periphery/contracts/"
]
}
[{"inputs":[{"internalType":"address","name":"gameToken_","type":"address"},{"internalType":"uint256","name":"startTime_","type":"uint256"},{"internalType":"address","name":"admin_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AwaitingCooldown","type":"error"},{"inputs":[],"name":"CannotUpdateYet","type":"error"},{"inputs":[],"name":"DurationTooLow","type":"error"},{"inputs":[],"name":"GameIsStarted","type":"error"},{"inputs":[],"name":"GameNotStarted","type":"error"},{"inputs":[],"name":"InvalidAdjustment","type":"error"},{"inputs":[],"name":"NextRoundPending","type":"error"},{"inputs":[],"name":"NotAllowedDuringRound","type":"error"},{"inputs":[],"name":"NotAuthorized","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"SharesAlreadyUpdatedThisRound","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newCooldownTime","type":"uint256"}],"name":"CooldownTimeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ERC20Withdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newFee","type":"uint256"}],"name":"FeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newDuration","type":"uint256"}],"name":"InitialCountdownSet","type":"event"},{"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":"address","name":"player","type":"address"},{"indexed":false,"internalType":"uint256","name":"paid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"round","type":"uint256"}],"name":"Played","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"newTarget","type":"uint16"}],"name":"PlaysPerHourTargetSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"winner","type":"address"},{"indexed":false,"internalType":"uint256","name":"winnerPrize","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"miscAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"retainedAmount","type":"uint256"}],"name":"RoundCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"winnerShare","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"miscShare","type":"uint256"}],"name":"SharesSet","type":"event"},{"inputs":[],"name":"ADMIN_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINIMUM_RESET_TIME","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TIME_SUBTRACTION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"completeRound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cooldownTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cooldownUntil","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"countDown","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentPrize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"finalizedRounds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gameToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialCountdown","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastFeeUpdate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastPlayedAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastPlaysPerHourUpdate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastShareUpdateRound","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestPlayer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"miscRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"miscShare","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"play","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"playFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"}],"name":"playerPlayCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"playsPerHourTarget","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"roundDeadline","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"roundPlayCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"roundStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"newCooldownTime","type":"uint16"}],"name":"setCooldownTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"newDuration","type":"uint16"}],"name":"setInitialCountdown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newRecipient","type":"address"}],"name":"setMiscRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"setNewAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newFee","type":"uint256"}],"name":"setPlayFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"newTarget","type":"uint16"}],"name":"setPlaysPerHourTarget","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"newWinnerShare","type":"uint8"},{"internalType":"uint8","name":"newMiscShare","type":"uint8"}],"name":"setShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalPlayCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"winnerShare","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"withdrawERC20","outputs":[],"stateMutability":"nonpayable","type":"function"}]