//SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import "@openzeppelin/contracts/access/AccessControl.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "./Treasury.sol"; /* * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR*+++++*@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@++++++++*RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR+++++++++@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR*++++++++*RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR*+++++++++RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR+++++++++RRRRRRRRRRRRRRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR+R++++++++++RRRRRRRRRRRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR*+RR*+++++++++RRRRRRRRRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR+++++++RRRRRR++RRR*+++++++++RRRRRRRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR++++++++RRRR+++RRRR******+++*RRRRRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR++++++RRRR**+RRRRRR*******++RRRRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR***+++RRR****RRRRRR********+*RRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR****++@RR****RRRRRRR********+*RRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR****++R##***RRRRRRRRR*********RRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR#****+R###**RRRRRRRRRR*********@RRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR##****R###**RRRRRRRRRRR**********RRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR###***####**RRRRRRRRRRRR**********RRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR#####**####**RRRRRRRRRRRRR**********RRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR######*#####**RRRRRRRRRRRRRR*********RRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@######*#####*RRRRRRRRRRRRRRRR*********RRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@##############RRRRRRRRRRRRRRRRR*********RRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR###############RRRRRRRRRRRRRRRRRRR*********RRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@+++++RRRR*RR################RRRRRRRRRRRRRRRRRRR**********RRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRR*++++++++++++*RR####################RRRRRRRRRRRRRRRRR*********RRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRR+++++++++++++**RRR#######################RRRRRRRRRRRRRRRR*********RRRRRR * RRRRRRRRRRRRRRRRRRRRRR+++++++++++++**RRRR#######**################RRRRRRRRRRRRRRR**********RRRRR * RRRRRRRRRRRRRRRRRRRR+++++++++++++***RRRR######*********############*RRRRRRRRRRRRRR*********RRRRR * RRRRRRRRRRRRRRRRRRR++++++++++++****RRRR**####R++***+***###########***RRRRRRRRRRRRR**********RRRR * RRRRRRRRRRRRRRRRR+++++++++++++***RRRRRR***##R++++++++***##*#######**++RRRRRRRRRRRRR*********RRRR * RRRRRRRRRRRRRRRR+++*************RRRRRR*****RR+++*****++++++++++++++++++RRRRRRRRRRRRR*********RRR * RRRRRRRRRRRRRR@****************RRRRRRR++**RRR+********+++*********++++++RRRRRRRRRRRR*********RRR * RRRRRRRRRRRRR******************RRRRRRR+++RRR#####******+************++++*RRRRRRRRRRR*********RRR * RRRRRRRRRRRR******************RRRRRRRR++*R#########*******************++RRRRRRRRRRRRR*********RR * RRRRRRRRRRR******************RRRRRRRRR++*############******************+RR+RRRRRRRRRR*********RR * RRRRRRRRRRR*****************RRRRRRRRRR+*##############**********##*******R@@RRRRRRRRR#********RR * RRRR*RRRRR**********#*******RRRRRRRRRR*###############***********##*****+RR+RRRRRRRRRR*********R * RRRR*RRRR********####******RRRRRRRRRR*#####******######**********###*****RR+RRRRRRRRRR*********R * RRR*RRRR******######*******RRRRRRRRR**###****##****####***********###*****RR+RRRRRRRRR#********R * RRR+RRRR*****######*******RRRRRRRRRR**##**#RRRR###**###************###****RR+RRRRRRRRR#********R * RR*+RRR*****#######*******RRRRRRRRRR**#**#RRRRRRR##**##*************##****RR+*RRRRRRRRR#*******R * RR++RRR****#######********RRRRRRRRRR**#**RRRRRRRRRR#**#***#*********###***RR++RRRRRRRRR#******** * RR++RR****#######********RRRRRRRRRRR****RRRRRRRRRRR##*****##***#****###***RR++RRRRRRRRR#******** * R*++RR***########********RRRRRRRRRRR***RRRRRRRRRRRRR#*****##***#*****##***RR*+RRRRRRRRR##******* * R+++R****#######******+**RRRRRRRRRRR***RRRRRRRRRRRRRR****####**#*****##***RRR++RRRRRRRR#******** * R+++R***########******+**RRRRRRRRRRRR*RRRRRRRRRRRRRRR****####***#****##***RRR++RRRRRRRR#******** * R+++R**#########*****#***RRRRRRRRRRRR*RRRRRRRRRRRRRRRR***####***#*****#***RRR++RRRRRRRR##******* * @+++***########*****#****RRRRRRRRRRRRRRRRRRRRRRRRRRRRR***####***#********RRRR++RRRRRRRR##******* * ++++**#########*****#****RRRRRRRRRRRRRRRRRRRRRRRRRRRRR**#####***#********RRR*++RRRRRRRR##******* * ++++**#########*****#****RRRRRRRRRRRRRRRRRRRRRRRRRRRRR*+#####***#********RRR*++RRRRRRRR##******* * ++++**########*****##*****RRRRRRRRRRRRRRRRRRRRRRRRRRR*++#####**##*******RRRR*+++RRRRRRR##******* * ++++**########*****###****RRRRRRRRRRRRRRRRRRRRRRRRRRR++R#####**##*******RRRR*+++RRRRRRR##******* * ++++*#########*****###****RRRRRRRRRRRRRRRRRRRRRRRRRR+++R####***##*******RRRR*+++RRRRRRR##******* * ++++*#########*****###*****RRRRRRRRRRRRRRRRRRRRRRRRR++RR####***#*******RRRR**+++RRRRRRR##******* * ++++*#########*****###*****RRRRRRRRRRRRRRRRRRRRRRRR*+RR####***##*******RRRR**+++RRRRRRR#******** * ++++*#########*****###*****RRRRRRRRRRRRRRRRRRRRRRRRRRRR###****#*******RRRR@**+++RRRRRRR#******** * ++++*#########*****####*****RRRRRRRRRRRRRRRRRRRRRRRRRRR##***********+@RRRR***++RRRRRRR##******** * ++++*#########******###******RRRRRRRRRRRRRRRRRRRRRRRRR##*****#*****++RRRRR**+++RRRRRRR##******** * +++++*########******###******RRRRRRRRRRRRRRRRRRRRRRRRR************++RRRRR***+++RRRRRRR#********R * @++++*########******####******RRRRRRRRRRRRRRRRRRRRRRR*************+RRRRR****+++RRRRRRR#********R * @++++*#########*****####*******RRRRRRRRRRRRRRRRRRRRR*************+RRRRRR***++++RRRRRRR#********R * R++++**########******####*******RRRRRRRRRRRRRRRRRRR*************+RRRRRR****++++RRRRRR#*********R * R+++++*########******####********RRRRRRRRRRRRRRRR*****++*******+RRRRRR****++++*RRRRRR#*********R * R+++++*#########*****#####*******#RRR++RRRRRRRR+*+++++*******++RRRRRR*****++++RRRRRRR#********RR * RR++++**########******#####*******#RRR@+++++++++++++*******+++RRRRRR******++++RRRRRR#*********RR * RR++++***#######******#####********#RRRRR++++++++******+++++RRRRRR*******+++++RRRRRR#*********RR * RR++++****#######******#####********#RRRRRR@++++++++++++++RRRRRRR*******+++++@RRRRRR*********RRR * RRR+++*****######*******#####*********#RRRRRRRRR*++*++RRRRRRRRR********++++++RRRRRR**********RRR * RRR+++*******#####******######*********##RRRRRRRRRRRRRRRRRRRR**********++++++RRRRRR**********RRR * RRRR++*********###********#####**********###RRRRRRRRRRRRRR************++++++RRRRRR**********RRRR * RRRR++*********************#####************######RR####*************+++++++RRRRRR**********RRRR * RRRR@+**********************#####***************#*******************+++++++@RRRRR**********RRRRR * RRRRR+***********************######********************************++++++++RRRRRR**********RRRRR * RRRRRR+************************#####*****************************+++**++++RRRRRR***********RRRRR * RRRRRR**********#****************####***************************+++**+++++RRRRRR**********RRRRRR * RRRRRRR*********##*******************##***********************++++***++++RRRRRR**********RRRRRRR * RRRRRRRR*********###**+++***********************************++++****+++++RRRRR***********RRRRRRR * RRRRRRRRR*********####*+++*******************************++++++*****++++RRRRRR**********RRRRRRRR * RRRRRRRRR*********######*++*******************************++++******+++RRRRRR***********RRRRRRRR * RRRRRRRRRR*********#######*++****************************+++*******+++RRRRRR***********RRRRRRRRR * RRRRRRRRRRR********##########++***********************++++*********++@RRRRR***********RRRRRRRRRR * RRRRRRRRRRRR********###########*********************++++**********++RRRRRR***********@RRRRRRRRRR * RRRRRRRRRRRRR********#############***************++++************++@RRRRRR***********RRRRRRRRRRR * RRRRRRRRRRRRRR*********###############********+++**##***********++RRRRRRR***********RRRRRRRRRRRR * RRRRRRRRRRRRRRR*********###########################**************RRRRRRR**********+RRRRRRRRRRRRR * RRRRRRRRRRRRRRRRR********########################***************RRRRRRR*********++RRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRR*********#####################**************RRRRRRRR*********++RRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRR*********################****************RRRRRRR*********+++RRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRR*************########*****************RRRRRRRR*********+++@RRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRR**********************************RRRRRRRRR*******+++++RRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRR****************************RRRRRRRRRR*******++++++RRRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRR************************RRRRRRRRRRR*****+++++++*RRRRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR*****************RRRRRRRRRRRRR*++*+++++++++*RRRRRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR*+++++++++++++RRRRRRRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR*++++++++++++++RRRRRRRRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR+++++++++++++++RRRRRRRRRRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRR**RRRRRRRRRRRRRRRRRRRRRRRRRRR*+++++++++++++++RRRRRRRRRRRRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRR+++@RRRRRRRRRRRRRRRRR@++++++++++++++++++RRRRRRRRRRRRRRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRR@++++++++++*++++++++++++++++++++++++RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR++++++++++++++++++++++++++++*RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR * RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@++++++++++++++++++++*RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR * KODEX EVM-TICKET */ /** * @title EvmTicket * @dev A contract that allows users to purchase access to an application using Ether. * This contract is an extension of the Treasury contract, which is used to manage the contract's funds. * It uses the AccessControl contract to manage role-based access control. */ abstract contract EvmTicket is Treasury, AccessControl { using EnumerableSet for EnumerableSet.AddressSet; EnumerableSet.AddressSet private _clientAddresses; /** * @dev Emitted when a user buys access to the application. * @param buyer The address of the user who bought access. * @param duration The duration (in seconds) of the access period. * @param clientExpireAt The timestamp when the user's access will expire. * @param price The price (in wei) paid for the access. */ event BuyTicket( address buyer, uint256 duration, uint256 clientExpireAt, uint256 price ); /** * @dev Emitted when the price of access changes. * @param oldPrice The old price (in wei) of access. * @param newPrice The new price (in wei) of access. * @param timestamp The timestamp of the price change. */ event PriceChange(uint256 oldPrice, uint256 newPrice, uint timestamp); /** * @dev Emitted when the duration of access changes. * @param oldDuration The old duration (in seconds) of access. * @param newDuration The new duration (in seconds) of access. * @param timestamp The timestamp of the duration change. */ event DurationChange( uint256 oldDuration, uint256 newDuration, uint timestamp ); bytes32 public constant ROLE_MEMBER = keccak256("ROLE_MEMBER"); uint256 public price = 1 ether / 10; uint public duration = 86400; mapping(address => uint) _clients; /** * @dev Changes the price of access. * @param _price The new price (in wei) of access. */ function changePrice(uint256 _price) public onlyRole(DEFAULT_ADMIN_ROLE) { emit PriceChange(price, _price, block.timestamp); price = _price; } /** * @dev Removes a client's access to the application. * @param _client The address of the client whose access is being removed. */ function removeClient(address _client) public onlyRole(DEFAULT_ADMIN_ROLE) { if (!isClient(_client)) return; uint256 contractBalance = address(this).balance; uint256 valueLeft = etherLeft(_client); if (valueLeft > 0 && contractBalance >= valueLeft) { payable(_client).transfer(valueLeft); } _clientAddresses.remove(_client); delete _clients[_client]; } /** * @dev Changes the duration of access. * @param _duration The new duration (in seconds) of access. */ function changeDuration( uint256 _duration ) public onlyRole(DEFAULT_ADMIN_ROLE) { emit DurationChange(duration, _duration, block.timestamp); duration = _duration; } /** * @dev Returns the remaining ether balance that a client has left before their access expires. * @param _client The address of the client to check. * @return remainingBalance The amount of ether that the client has left before their access expires. */ function etherLeft( address _client ) public view returns (uint256 remainingBalance) { if ( !_clientAddresses.contains(_client) || (_clients[_client] - block.timestamp) <= 0 ) { return 0; } return ((_clients[_client] - block.timestamp) * price) / duration; } /** * @notice Get the expiration timestamp of a client's access. * @param _client Address of the client. * @return expiresAt The expiration timestamp of the client's access, or zero if the client does not have access. */ function clientExpiresAt( address _client ) public view returns (uint256 expiresAt) { if (!_clientAddresses.contains(_client)) return 0; return _clients[_client]; } /** * @dev Returns an array of all client addresses. * @return clientAddresses An array of client addresses. */ function getClients() public view returns (address[] memory clientAddresses) { return _clientAddresses.values(); } /** * @dev Checks if the given address is registered as a client. * @param _client The address to check. * @return bool Returns true if the address is registered as a client, false otherwise. */ function isClient(address _client) public view returns (bool) { return _clientAddresses.contains(_client); } /** * @notice Allows a user to purchase access to the system by paying the current ticket price * @dev If the user has already purchased access, the access duration is extended * @return expireIn The expiration time for the user's access * @dev Emits a `BuyTicket` event on success * @dev Reverts if the sent ether value does not match the current ticket price * @dev Uses the `nonReentrant` modifier from OpenZeppelin's `ReentrancyGuard` to prevent reentrancy attacks */ function buyAccess() public payable nonReentrant returns (uint expireIn) { address client = _msgSender(); if (msg.value != price) { revert("EvmTicket: no ether sent"); } if (_clients[client] <= 0) { _clients[client] = block.timestamp + duration; } else { _clients[client] = _clients[client] + duration; } if (!_clientAddresses.contains(client)) { _clientAddresses.add(client); } emit BuyTicket(msg.sender, duration, _clients[client], msg.value); return _clients[client] - block.timestamp; } /** * @dev Returns the balance available for withdrawal. * @return balance The balance available for withdrawal. */ function getWithdrawBalance() public view returns (uint256 balance) { return address(this).balance; } }