pragma solidity >=0.8.0;
// SPDX-License-Identifier: BSD-3-Clause
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a * b;
assert(a == 0 || c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256`
* (`UintSet`) are supported.
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping (bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) { // Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
// When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
// so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
bytes32 lastvalue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastvalue;
// Update the index for the moved value
set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
require(set._values.length > index, "EnumerableSet: index out of bounds");
return set._values[index];
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint(_at(set._inner, index))));
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
}
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor() {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) onlyOwner public {
require(newOwner != address(0));
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
interface Token {
function transferFrom(address, address, uint) external returns (bool);
function transfer(address, uint) external returns (bool);
}
contract EU21_Italy is Ownable {
using SafeMath for uint;
using EnumerableSet for EnumerableSet.AddressSet;
event RewardsTransferred(address holder, uint amount);
// EU21 token contract address
address public constant tokenAddress = 0x87ea1F06d7293161B9ff080662c1b0DF775122D3;
// amount disbursed per victory
uint public amountToDisburse = 1000000000000000000000; // 1000 EU21 PER VICTORY
// total games rewards for each pool
uint public totalReward = 7000000000000000000000; // 7000 EU21 TOTAL GAMES REWARDS (EXCLUDING THE GRAND PRIZE)
// unstaking possible after ...
uint public constant unstakeTime = 37 days;
// claiming possible after ...
uint public constant claimTime = 37 days;
uint public totalClaimedRewards = 0;
uint public totalDeposited = 0;
uint public totalDisbursed = 0;
bool public ended ;
uint public startTime = block.timestamp;
EnumerableSet.AddressSet private holders;
mapping (address => uint) public depositedTokens;
mapping (address => uint) public pending;
mapping (address => uint) public totalEarnedTokens;
mapping (address => uint) public rewardEnded;
function disburse () public onlyOwner returns (bool){
require(!ended, "Staking already ended");
address _hold;
uint _add;
for(uint i = 0; i < holders.length(); i = i.add(1)){
_hold = holders.at(i);
_add = depositedTokens[_hold].mul(amountToDisburse).div(totalDeposited);
pending[_hold] = pending[_hold].add(_add);
}
totalDisbursed = totalDisbursed.add(amountToDisburse);
return true;
}
//Disburse and End the staking pool
function disburseAndEnd(uint _finalDisburseAmount) public onlyOwner returns (bool){
require(!ended, "Staking already ended");
require(_finalDisburseAmount > 0);
address _hold;
uint _add;
for(uint i = 0; i < holders.length(); i = i.add(1)){
_hold = holders.at(i);
_add = depositedTokens[_hold].mul(_finalDisburseAmount).div(totalDeposited);
pending[_hold] = pending[_hold].add(_add);
}
totalDisbursed = totalDisbursed.add(_finalDisburseAmount);
ended = true;
return true;
}
//End the staking pool
function end() public onlyOwner returns (bool){
require(!ended, "Staking already ended");
ended = true;
return true;
}
function updateAccount(address account) private {
uint pendingDivs = getPendingDivs(account);
if (pendingDivs > 0) {
pending[account] = 0;
depositedTokens[account] = depositedTokens[account].add(pendingDivs);
totalDeposited = totalDeposited.add(pendingDivs);
totalEarnedTokens[account] = totalEarnedTokens[account].add(pendingDivs);
totalClaimedRewards = totalClaimedRewards.add(pendingDivs);
}
}
function getPendingDivs(address _holder) public view returns (uint) {
if (!holders.contains(_holder)) return 0;
uint pendingDivs = pending[_holder];
return pendingDivs;
}
function getNumberOfHolders() public view returns (uint) {
return holders.length();
}
function deposit(uint amountToStake) public {
require(!ended, "Staking has ended");
require(amountToStake > 0, "Cannot deposit 0 Tokens");
require(Token(tokenAddress).transferFrom(msg.sender, address(this), amountToStake), "Insufficient Token Allowance");
updateAccount(msg.sender);
depositedTokens[msg.sender] = depositedTokens[msg.sender].add(amountToStake);
totalDeposited = totalDeposited.add(amountToStake);
if (!holders.contains(msg.sender)) {
holders.add(msg.sender);
}
}
function claim() public{
require(holders.contains(msg.sender));
require(block.timestamp.sub(startTime) > claimTime || ended, "Not yet.");
require(pending[msg.sender] > 0);
uint _reward = pending[msg.sender];
pending[msg.sender] = 0;
require(Token(tokenAddress).transfer(msg.sender, _reward), "Could not transfer tokens.");
totalClaimedRewards = totalClaimedRewards.add(_reward);
totalEarnedTokens[msg.sender] = totalEarnedTokens[msg.sender].add(_reward);
if(depositedTokens[msg.sender] == 0){
holders.remove(msg.sender);
}
}
function withdraw(uint _amount) public{
require(block.timestamp.sub(startTime) > unstakeTime || ended, "Not yet.");
require(depositedTokens[msg.sender] >= _amount);
require(_amount > 0);
depositedTokens[msg.sender] = depositedTokens[msg.sender].sub(_amount);
totalDeposited = totalDeposited.sub(_amount);
require(Token(tokenAddress).transfer(msg.sender, _amount), "Could not transfer tokens.");
if(depositedTokens[msg.sender] == 0 && pending[msg.sender] == 0){
holders.remove(msg.sender);
}
}
/*
function withdrawAllAfterEnd() public {
require(ended, "Staking has not ended");
uint _pend = pending[msg.sender];
uint amountToWithdraw = _pend.add(depositedTokens[msg.sender]);
require(amountToWithdraw >= 0, "Invalid amount to withdraw");
pending[msg.sender] = 0;
depositedTokens[msg.sender] = 0;
totalDeposited = totalDeposited.sub(depositedTokens[msg.sender]);
require(Token(tokenAddress).transfer(msg.sender, amountToWithdraw), "Could not transfer tokens.");
totalClaimedRewards = totalClaimedRewards.add(_pend);
totalEarnedTokens[msg.sender] = totalEarnedTokens[msg.sender].add(_pend);
holders.remove(msg.sender);
}*/
function getStakersList(uint startIndex, uint endIndex)
public
view
returns (address[] memory stakers,
uint[] memory stakingTimestamps,
uint[] memory lastClaimedTimeStamps,
uint[] memory stakedTokens) {
require (startIndex < endIndex);
uint length = endIndex.sub(startIndex);
address[] memory _stakers = new address[](length);
uint[] memory _stakingTimestamps = new uint[](length);
uint[] memory _lastClaimedTimeStamps = new uint[](length);
uint[] memory _stakedTokens = new uint[](length);
for (uint i = startIndex; i < endIndex; i = i.add(1)) {
address staker = holders.at(i);
uint listIndex = i.sub(startIndex);
_stakers[listIndex] = staker;
_stakedTokens[listIndex] = depositedTokens[staker];
}
return (_stakers, _stakingTimestamps, _lastClaimedTimeStamps, _stakedTokens);
}
// function to allow admin to claim *other* ERC20 tokens sent to this contract (by mistake)
function transferAnyERC20Tokens(address _tokenAddr, address _to, uint _amount) public onlyOwner {
require (_tokenAddr != tokenAddress , "Cannot Transfer Out this token");
Token(_tokenAddr).transfer(_to, _amount);
}
}
{
"compilationTarget": {
"EU21_Italy.sol": "EU21_Italy"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"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":false,"internalType":"address","name":"holder","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardsTransferred","type":"event"},{"inputs":[],"name":"amountToDisburse","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountToStake","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"depositedTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"disburse","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_finalDisburseAmount","type":"uint256"}],"name":"disburseAndEnd","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"end","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ended","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNumberOfHolders","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_holder","type":"address"}],"name":"getPendingDivs","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"endIndex","type":"uint256"}],"name":"getStakersList","outputs":[{"internalType":"address[]","name":"stakers","type":"address[]"},{"internalType":"uint256[]","name":"stakingTimestamps","type":"uint256[]"},{"internalType":"uint256[]","name":"lastClaimedTimeStamps","type":"uint256[]"},{"internalType":"uint256[]","name":"stakedTokens","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"pending","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardEnded","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalClaimedRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDeposited","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDisbursed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"totalEarnedTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddr","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"transferAnyERC20Tokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unstakeTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]