// SPDX-License-Identifier: MIT
pragma solidity 0.8.6;
/**
* @title Claim
* @author gotbit
*/
interface IERC20 {
function balanceOf(address who) external view returns (uint balance);
function transfer(address to, uint value) external returns (bool trans1);
}
contract Ownable {
address public owner;
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call this function");
_;
}
function transferOwnership(address newOwner_) external onlyOwner {
require(
newOwner_ != address(0),
"You cant tranfer ownerships to address 0x0"
);
require(newOwner_ != owner, "You cant transfer ownerships to yourself");
emit OwnershipTransferred(owner, newOwner_);
owner = newOwner_;
}
}
contract Claim is Ownable {
struct Round {
uint cliff;
uint constReward;
uint linearPeriod;
}
struct Allocation {
uint seed;
uint strategic;
uint private_;
}
struct User {
uint claimed;
Allocation allocation;
uint claimTimestamp;
}
uint public constant MONTH = 30 days;
uint public constant MINUTE = 1 minutes;
uint public constant CONST_PERIOD = 2 * 24 hours;
uint public constant CONST_RELAX = MONTH;
IERC20 public token;
bool public isStarted;
uint public startTimestamp;
mapping(string => Round) rounds;
mapping(address => User) public users;
event Started(uint timestamp, address who);
event Claimed(address indexed to, uint value);
event SettedAllocation(address indexed to, uint seed, uint strategic, uint private_);
constructor(address owner_, address token_) {
owner = owner_;
token = IERC20(token_);
rounds["seed"] = Round(1, 10, 13);
rounds["strategic"] = Round(0, 15, 9);
rounds["private"] = Round(0, 20, 7);
}
function start() external onlyOwner returns (bool status) {
require(!isStarted, "The claim has already begun");
isStarted = true;
startTimestamp = block.timestamp;
emit Started(startTimestamp, msg.sender);
return true;
}
function claim() external returns (bool status) {
require(isStarted, "The claim has not started yet");
uint value_ = calculateUnclaimed(msg.sender);
require(value_ > 0, "You dont have DES to harvest");
require(
token.balanceOf(address(this)) >= value_,
"Not enough tokens on contract"
);
users[msg.sender].claimed += value_;
users[msg.sender].claimTimestamp = block.timestamp;
require(token.transfer(msg.sender, value_), 'Transfer issues');
emit Claimed(msg.sender, value_);
return true;
}
function getAllocation(address user_) external view returns (uint sum) {
return
(users[user_].allocation.seed +
users[user_].allocation.strategic +
users[user_].allocation.private_) / 2;
}
function calculateUnclaimed(address user_)
public
view
returns (uint unclaimed)
{
require(isStarted, "The claim has not started yet");
uint resultSeed_ = calculateRound(
"seed",
users[user_].allocation.seed
);
uint resultStrategic_ = calculateRound(
"strategic",
users[user_].allocation.strategic
);
uint resultPrivate_ = calculateRound(
"private",
users[user_].allocation.private_
);
return
(resultSeed_ + resultStrategic_ + resultPrivate_) /
2 -
users[user_].claimed;
}
function calculateRound(string memory roundName_, uint allocation_)
internal
view
returns (uint unclaimedFromRound)
{
require(isStarted, "The claim has not started yet");
Round memory round_ = rounds[roundName_];
uint timePassed_ = block.timestamp - startTimestamp;
uint bank_ = allocation_;
if (timePassed_ < (round_.cliff * MONTH)) return 0;
timePassed_ -= (round_.cliff * MONTH);
uint constReward_ = (bank_ * round_.constReward) / 100;
if (round_.cliff == 0) {
if (timePassed_ < CONST_PERIOD / 2) return constReward_ / 2;
}
if (timePassed_ < CONST_RELAX) return constReward_;
timePassed_ -= CONST_RELAX;
uint minutesPassed_ = timePassed_ / MINUTE;
uint leftInBank_ = bank_ - constReward_;
return
(leftInBank_ * MINUTE * minutesPassed_) /
(MONTH * round_.linearPeriod) +
constReward_;
}
function setAllocations(
address[] memory whos_,
uint[] memory seeds_,
uint[] memory strategics_,
uint[] memory privates_
)
public
onlyOwner {
uint len = whos_.length;
require(seeds_.length == len, 'Different length');
require(strategics_.length == len, 'Different length');
require(privates_.length == len, 'Different length');
for (uint i = 0; i < len; i++) {
address who_ = whos_[i];
if (users[who_].claimed == 0) {
uint seed_ = seeds_[i];
uint strategic_ = strategics_[i];
uint private_ = privates_[i];
users[who_] = User({
claimed: users[who_].claimed,
allocation: Allocation(seed_, strategic_, private_),
claimTimestamp: users[who_].claimTimestamp
});
emit SettedAllocation(who_, seed_, strategic_, private_);
}
}
}
}
{
"compilationTarget": {
"Claim.sol": "Claim"
},
"evmVersion": "berlin",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"token_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Claimed","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":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"seed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"strategic","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"private_","type":"uint256"}],"name":"SettedAllocation","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"},{"indexed":false,"internalType":"address","name":"who","type":"address"}],"name":"Started","type":"event"},{"inputs":[],"name":"CONST_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CONST_RELAX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINUTE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MONTH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user_","type":"address"}],"name":"calculateUnclaimed","outputs":[{"internalType":"uint256","name":"unclaimed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claim","outputs":[{"internalType":"bool","name":"status","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user_","type":"address"}],"name":"getAllocation","outputs":[{"internalType":"uint256","name":"sum","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isStarted","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":"whos_","type":"address[]"},{"internalType":"uint256[]","name":"seeds_","type":"uint256[]"},{"internalType":"uint256[]","name":"strategics_","type":"uint256[]"},{"internalType":"uint256[]","name":"privates_","type":"uint256[]"}],"name":"setAllocations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"start","outputs":[{"internalType":"bool","name":"status","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner_","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"users","outputs":[{"internalType":"uint256","name":"claimed","type":"uint256"},{"components":[{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"uint256","name":"strategic","type":"uint256"},{"internalType":"uint256","name":"private_","type":"uint256"}],"internalType":"struct Claim.Allocation","name":"allocation","type":"tuple"},{"internalType":"uint256","name":"claimTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"}]