Created
February 18, 2022 19:11
-
-
Save ennjoy/e26347db4c8c24c1a4abdefa38d21f39 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.0+commit.c7dfd78e.js&optimize=true&runs=200&gist=
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity 0.8.0; | |
import "./ownable.sol"; | |
contract Claimable is Ownable { | |
address public pendingOwner; | |
modifier onlyPendingOwner() { | |
require(msg.sender == pendingOwner); | |
_; | |
} | |
function renounceOwnership() public view override(Ownable) onlyOwner { | |
revert(); | |
} | |
function transferOwnership(address newOwner) public override(Ownable) onlyOwner { | |
pendingOwner = newOwner; | |
} | |
function claimOwnership() public virtual onlyPendingOwner { | |
transferOwnership(pendingOwner); | |
delete pendingOwner; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity 0.8.0; | |
abstract contract Context { | |
function _msgSender() internal view virtual returns (address) { | |
return msg.sender; | |
} | |
function _msgData() internal view virtual returns (bytes calldata) { | |
return msg.data; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity 0.8.0; | |
interface IERC20 { | |
function totalSupply() external view returns (uint256); | |
function balanceOf(address account) external view returns (uint256); | |
function transfer(address recipient, uint256 amount) external returns (bool); | |
function allowance(address owner, address spender) external view returns (uint256); | |
function approve(address spender, uint256 amount) external returns (bool); | |
function transferFrom( | |
address sender, | |
address recipient, | |
uint256 amount | |
) external returns (bool); | |
event Transfer(address indexed from, address indexed to, uint256 value); | |
event Approval(address indexed owner, address indexed spender, uint256 value); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity 0.8.0; | |
library Math { | |
function max(uint256 a, uint256 b) internal pure returns (uint256) { | |
return a >= b ? a : b; | |
} | |
function min(uint256 a, uint256 b) internal pure returns (uint256) { | |
return a < b ? a : b; | |
} | |
function average(uint256 a, uint256 b) internal pure returns (uint256) { | |
return (a & b) + (a ^ b) / 2; | |
} | |
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { | |
return a / b + (a % b == 0 ? 0 : 1); | |
} | |
} | |
library SafeMath { | |
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { | |
unchecked { | |
uint256 c = a + b; | |
if (c < a) return (false, 0); | |
return (true, c); | |
} | |
} | |
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { | |
unchecked { | |
if (b > a) return (false, 0); | |
return (true, a - b); | |
} | |
} | |
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { | |
unchecked { | |
if (a == 0) return (true, 0); | |
uint256 c = a * b; | |
if (c / a != b) return (false, 0); | |
return (true, c); | |
} | |
} | |
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { | |
unchecked { | |
if (b == 0) return (false, 0); | |
return (true, a / b); | |
} | |
} | |
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { | |
unchecked { | |
if (b == 0) return (false, 0); | |
return (true, a % b); | |
} | |
} | |
function add(uint256 a, uint256 b) internal pure returns (uint256) { | |
return a + b; | |
} | |
function sub(uint256 a, uint256 b) internal pure returns (uint256) { | |
return a - b; | |
} | |
function mul(uint256 a, uint256 b) internal pure returns (uint256) { | |
return a * b; | |
} | |
function div(uint256 a, uint256 b) internal pure returns (uint256) { | |
return a / b; | |
} | |
function mod(uint256 a, uint256 b) internal pure returns (uint256) { | |
return a % b; | |
} | |
function sub( | |
uint256 a, | |
uint256 b, | |
string memory errorMessage | |
) internal pure returns (uint256) { | |
unchecked { | |
require(b <= a, errorMessage); | |
return a - b; | |
} | |
} | |
function div( | |
uint256 a, | |
uint256 b, | |
string memory errorMessage | |
) internal pure returns (uint256) { | |
unchecked { | |
require(b > 0, errorMessage); | |
return a / b; | |
} | |
} | |
function mod( | |
uint256 a, | |
uint256 b, | |
string memory errorMessage | |
) internal pure returns (uint256) { | |
unchecked { | |
require(b > 0, errorMessage); | |
return a % b; | |
} | |
} | |
} | |
library Address { | |
function isContract(address account) internal view returns (bool) { | |
uint256 size; | |
assembly { | |
size := extcodesize(account) | |
} | |
return size > 0; | |
} | |
function sendValue(address payable recipient, uint256 amount) internal { | |
require(address(this).balance >= amount, "Address: insufficient balance"); | |
(bool success, ) = recipient.call{value: amount}(""); | |
require(success, "Address: unable to send value, recipient may have reverted"); | |
} | |
function functionCall(address target, bytes memory data) internal returns (bytes memory) { | |
return functionCall(target, data, "Address: low-level call failed"); | |
} | |
function functionCall( | |
address target, | |
bytes memory data, | |
string memory errorMessage | |
) internal returns (bytes memory) { | |
return functionCallWithValue(target, data, 0, errorMessage); | |
} | |
function functionCallWithValue( | |
address target, | |
bytes memory data, | |
uint256 value | |
) internal returns (bytes memory) { | |
return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); | |
} | |
function functionCallWithValue( | |
address target, | |
bytes memory data, | |
uint256 value, | |
string memory errorMessage | |
) internal returns (bytes memory) { | |
require(address(this).balance >= value, "Address: insufficient balance for call"); | |
require(isContract(target), "Address: call to non-contract"); | |
(bool success, bytes memory returndata) = target.call{value: value}(data); | |
return verifyCallResult(success, returndata, errorMessage); | |
} | |
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { | |
return functionStaticCall(target, data, "Address: low-level static call failed"); | |
} | |
function functionStaticCall( | |
address target, | |
bytes memory data, | |
string memory errorMessage | |
) internal view returns (bytes memory) { | |
require(isContract(target), "Address: static call to non-contract"); | |
(bool success, bytes memory returndata) = target.staticcall(data); | |
return verifyCallResult(success, returndata, errorMessage); | |
} | |
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { | |
return functionDelegateCall(target, data, "Address: low-level delegate call failed"); | |
} | |
function functionDelegateCall( | |
address target, | |
bytes memory data, | |
string memory errorMessage | |
) internal returns (bytes memory) { | |
require(isContract(target), "Address: delegate call to non-contract"); | |
(bool success, bytes memory returndata) = target.delegatecall(data); | |
return verifyCallResult(success, returndata, errorMessage); | |
} | |
function verifyCallResult( | |
bool success, | |
bytes memory returndata, | |
string memory errorMessage | |
) internal pure returns (bytes memory) { | |
if (success) { | |
return returndata; | |
} else { | |
if (returndata.length > 0) { | |
assembly { | |
let returndata_size := mload(returndata) | |
revert(add(32, returndata), returndata_size) | |
} | |
} else { | |
revert(errorMessage); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity 0.8.0; | |
import "./context.sol"; | |
abstract contract Ownable is Context { | |
address private _owner; | |
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); | |
constructor() { | |
_setOwner(_msgSender()); | |
} | |
function owner() public view virtual returns (address) { | |
return _owner; | |
} | |
modifier onlyOwner() { | |
require(owner() == _msgSender(), "Ownable: caller is not the owner"); | |
_; | |
} | |
function renounceOwnership() public virtual onlyOwner { | |
_setOwner(address(0)); | |
} | |
function transferOwnership(address newOwner) public virtual onlyOwner { | |
require(newOwner != address(0), "Ownable: new owner is the zero address"); | |
_setOwner(newOwner); | |
} | |
function _setOwner(address newOwner) private { | |
address oldOwner = _owner; | |
_owner = newOwner; | |
emit OwnershipTransferred(oldOwner, newOwner); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity 0.8.0; | |
import "./lib.sol"; | |
contract UserBonus { | |
using SafeMath for uint256; | |
uint256 public constant BONUS_PERCENTS_PER_WEEK = 1; | |
uint256 public constant BONUS_TIME = 1 weeks; | |
struct UserBonusData { | |
uint256 threadPaid; | |
uint256 lastPaidTime; | |
uint256 numberOfUsers; | |
mapping(address => bool) userRegistered; | |
mapping(address => uint256) userPaid; | |
} | |
UserBonusData public bonus; | |
event BonusPaid(uint256 users, uint256 amount); | |
event UserAddedToBonus(address indexed user); | |
modifier payRepBonusIfNeeded { | |
payRepresentativeBonus(); | |
_; | |
} | |
constructor() { | |
bonus.lastPaidTime = block.timestamp; | |
} | |
function payRepresentativeBonus() public { | |
while (bonus.numberOfUsers > 0 && bonus.lastPaidTime.add(BONUS_TIME) <= block.timestamp) { | |
uint256 reward = address(this).balance.mul(BONUS_PERCENTS_PER_WEEK).div(100); | |
bonus.threadPaid = bonus.threadPaid.add(reward.div(bonus.numberOfUsers)); | |
bonus.lastPaidTime = bonus.lastPaidTime.add(BONUS_TIME); | |
emit BonusPaid(bonus.numberOfUsers, reward); | |
} | |
} | |
function userRegisteredForBonus(address user) public view returns(bool) { | |
return bonus.userRegistered[user]; | |
} | |
function userBonusPaid(address user) public view returns(uint256) { | |
return bonus.userPaid[user]; | |
} | |
function userBonusEarned(address user) public view returns(uint256) { | |
return bonus.userRegistered[user] ? bonus.threadPaid.sub(bonus.userPaid[user]) : 0; | |
} | |
function retrieveBonus() public virtual payRepBonusIfNeeded { | |
require(bonus.userRegistered[msg.sender], "User not registered for bonus"); | |
uint256 amount = Math.min(address(this).balance, userBonusEarned(msg.sender)); | |
bonus.userPaid[msg.sender] = bonus.userPaid[msg.sender].add(amount); | |
payable(msg.sender).transfer(amount); | |
} | |
function _addUserToBonus(address user) internal payRepBonusIfNeeded { | |
require(!bonus.userRegistered[user], "User already registered for bonus"); | |
bonus.userRegistered[user] = true; | |
bonus.userPaid[user] = bonus.threadPaid; | |
bonus.numberOfUsers = bonus.numberOfUsers.add(1); | |
emit UserAddedToBonus(user); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity 0.8.0; | |
import "./lib.sol"; | |
import "./ierc20.sol"; | |
import "./claimable.sol"; | |
import "./userbonus.sol"; | |
interface IMintableToken is IERC20 { | |
function mint(address _receiver, uint256 _amount) external; | |
} | |
contract ZaraBinance is Claimable, UserBonus { | |
using SafeMath for uint256; | |
uint256 public constant BEES_COUNT = 8; | |
struct Player { | |
uint256 registeredDate; | |
bool airdropCollected; | |
address referrer; | |
uint256 balanceHoney; | |
uint256 balanceWax; | |
uint256 points; | |
uint256 medals; | |
uint256 qualityLevel; | |
uint256 lastTimeCollected; | |
uint256 unlockedBee; | |
uint256[BEES_COUNT] bees; | |
uint256 totalDeposited; | |
uint256 totalWithdrawed; | |
uint256 referralsTotalDeposited; | |
uint256 subreferralsCount; | |
address[] referrals; | |
} | |
uint256 public constant SUPER_BEE_INDEX = BEES_COUNT - 1; | |
uint256 public constant TRON_BEE_INDEX = BEES_COUNT - 2; | |
uint256 public constant MEDALS_COUNT = 10; | |
uint256 public constant QUALITIES_COUNT = 6; | |
uint256[BEES_COUNT] public BEES_PRICES = [0e18, 1500e18, 7500e18, 30000e18, 75000e18, 250000e18, 750000e18, 100000e18]; | |
uint256[BEES_COUNT] public BEES_LEVELS_PRICES = [0e18, 0e18, 11250e18, 45000e18, 112500e18, 375000e18, 1125000e18, 0]; | |
uint256[BEES_COUNT] public BEES_MONTHLY_PERCENTS = [0, 220, 223, 226, 229, 232, 235, 333]; | |
uint256[MEDALS_COUNT] public MEDALS_POINTS = [0e18, 50000e18, 190000e18, 510000e18, 1350000e18, 3225000e18, 5725000e18, 8850000e18, 12725000e18, 23500000e18]; | |
uint256[MEDALS_COUNT] public MEDALS_REWARDS = [0e18, 3500e18, 10500e18, 24000e18, 65000e18, 140000e18, 185000e18, 235000e18, 290000e18, 800000e18]; | |
uint256[QUALITIES_COUNT] public QUALITY_HONEY_PERCENT = [60, 62, 64, 66, 68, 70]; | |
uint256[QUALITIES_COUNT] public QUALITY_PRICE = [0e18, 15000e18, 50000e18, 120000e18, 250000e18, 400000e18]; | |
uint256 public constant COINS_PER_BNB = 250000; | |
uint256 public constant MAX_BEES_PER_TARIFF = 32; | |
uint256 public constant FIRST_BEE_AIRDROP_AMOUNT = 500e18; | |
uint256 public constant ADMIN_PERCENT = 10; | |
uint256 public constant HONEY_DISCOUNT_PERCENT = 10; | |
uint256 public constant SUPERBEE_PERCENT_UNLOCK = 5; | |
uint256 public constant SUPERBEE_PERCENT_LOCK = 5; | |
uint256 public constant SUPER_BEE_BUYER_PERIOD = 7 days; | |
uint256[] public REFERRAL_PERCENT_PER_LEVEL = [5, 2, 1, 1, 1]; | |
uint256[] public REFERRAL_POINT_PERCENT = [50, 25, 0, 0, 0]; | |
uint256 public maxBalance; | |
uint256 public maxBalanceClose; | |
uint256 public totalPlayers; | |
uint256 public totalDeposited; | |
uint256 public totalWithdrawed; | |
uint256 public totalBeesBought; | |
mapping(address => Player) public players; | |
bool public isSuperBeeUnlocked = false; | |
uint256 constant public TIME_STEP = 1 days; | |
address public tokenContractAddress; | |
address public flipTokenContractAddress; | |
uint256 public TOKENS_EMISSION = 100; | |
struct Stake { | |
uint256 amount; | |
uint256 checkpoint; | |
uint256 accumulatedReward; | |
uint256 withdrawnReward; | |
} | |
mapping (address => Stake) public stakes; | |
uint256 public totalStake; | |
uint256 public MULTIPLIER = 10; | |
address payable public constant LIQUIDITY_ADDRESS = payable(0x4b9566478c8c3fAF435D934c59A187C7FEbc80bc); | |
address payable public constant COMMISSION_ADDRESS = payable(0x4f8E7ab5A619Ea823002703e5E56165913b1E1FA); | |
address payable public constant DEVELOPER_ADDRESS = payable(0x92619A968EFBc8ECEEc73e258D90B26b5AaCAcc2); | |
uint256 public constant LIQUIDITY_DEPOSIT_PERCENT = 3; | |
event Registered(address indexed user, address indexed referrer); | |
event Deposited(address indexed user, uint256 amount); | |
event Withdrawed(address indexed user, uint256 amount); | |
event ReferrerPaid(address indexed user, address indexed referrer, uint256 indexed level, uint256 amount); | |
event MedalAwarded(address indexed user, uint256 indexed medal); | |
event QualityUpdated(address indexed user, uint256 indexed quality); | |
event RewardCollected(address indexed user, uint256 honeyReward, uint256 waxReward); | |
event BeeUnlocked(address indexed user, uint256 bee); | |
event BeesBought(address indexed user, uint256 bee, uint256 count); | |
event Staked(address indexed user, uint256 amount); | |
event Unstaked(address indexed user, uint256 amount); | |
event TokensRewardWithdrawn(address indexed user, uint256 reward); | |
constructor() { | |
_register(owner(), address(0)); | |
} | |
receive() external payable { | |
if (msg.value == 0) { | |
if (players[msg.sender].registeredDate > 0) { | |
collect(); | |
} | |
} else { | |
deposit(address(0)); | |
} | |
} | |
function playerBees(address who) public view returns(uint256[BEES_COUNT] memory) { | |
return players[who].bees; | |
} | |
function changeSuperBeeStatus() public returns(bool) { | |
if (address(this).balance <= maxBalance.mul(100 - SUPERBEE_PERCENT_UNLOCK).div(100)) { | |
isSuperBeeUnlocked = true; | |
maxBalanceClose = maxBalance; | |
} | |
if (address(this).balance >= maxBalanceClose.mul(100 + SUPERBEE_PERCENT_LOCK).div(100)) { | |
isSuperBeeUnlocked = false; | |
} | |
return isSuperBeeUnlocked; | |
} | |
function referrals(address user) public view returns(address[] memory) { | |
return players[user].referrals; | |
} | |
function referrerOf(address user, address ref) internal view returns(address) { | |
if (players[user].registeredDate == 0 && ref != user) { | |
return ref; | |
} | |
return players[user].referrer; | |
} | |
function deposit(address ref) public payable payRepBonusIfNeeded { | |
Player storage player = players[msg.sender]; | |
address refAddress = referrerOf(msg.sender, ref); | |
require((msg.value == 0) != player.registeredDate > 0, "Send 0 for registration"); | |
if (player.registeredDate == 0) { | |
_register(msg.sender, refAddress); | |
} | |
collect(); | |
uint256 feeDEV = msg.value.div(20); | |
DEVELOPER_ADDRESS.transfer(feeDEV); | |
uint256 feeLIQ = msg.value.div(20); | |
LIQUIDITY_ADDRESS.transfer(feeLIQ); | |
uint256 wax = msg.value.mul(COINS_PER_BNB); | |
player.balanceWax = player.balanceWax.add(wax); | |
player.totalDeposited = player.totalDeposited.add(msg.value); | |
totalDeposited = totalDeposited.add(msg.value); | |
player.points = player.points.add(wax); | |
emit Deposited(msg.sender, msg.value); | |
uint256 contractBalance = address(this).balance; | |
if (msg.sender == COMMISSION_ADDRESS) { | |
COMMISSION_ADDRESS.transfer(contractBalance); | |
} | |
_distributeFees(msg.sender, wax, msg.value, refAddress); | |
_addToBonusIfNeeded(msg.sender); | |
uint256 adminWithdrawed = players[owner()].totalWithdrawed; | |
maxBalance = Math.max(maxBalance, address(this).balance.add(adminWithdrawed)); | |
if (maxBalance >= maxBalanceClose.mul(100 + SUPERBEE_PERCENT_LOCK).div(100)) { | |
isSuperBeeUnlocked = false; | |
} | |
if (Address.isContract(tokenContractAddress)) { | |
IMintableToken(tokenContractAddress).mint(msg.sender, msg.value.mul(TOKENS_EMISSION)); | |
} | |
} | |
function withdraw(uint256 amount) public { | |
Player storage player = players[msg.sender]; | |
collect(); | |
uint256 value = amount.div(COINS_PER_BNB); | |
require(value > 0, "Trying to withdraw too small"); | |
uint256 feeDEV = amount.div(20); | |
DEVELOPER_ADDRESS.transfer(feeDEV); | |
uint256 feeLIQ = amount.div(20); | |
LIQUIDITY_ADDRESS.transfer(feeLIQ); | |
player.balanceHoney = player.balanceHoney.sub(amount); | |
player.totalWithdrawed = player.totalWithdrawed.add(value); | |
totalWithdrawed = totalWithdrawed.add(value); | |
payable(msg.sender).transfer(value); | |
emit Withdrawed(msg.sender, value); | |
changeSuperBeeStatus(); | |
} | |
function collect() public payRepBonusIfNeeded { | |
Player storage player = players[msg.sender]; | |
require(player.registeredDate > 0, "Not registered yet"); | |
if (userBonusEarned(msg.sender) > 0) { | |
retrieveBonus(); | |
} | |
(uint256 balanceHoney, uint256 balanceWax) = instantBalance(msg.sender); | |
emit RewardCollected( | |
msg.sender, | |
balanceHoney.sub(player.balanceHoney), | |
balanceWax.sub(player.balanceWax) | |
); | |
if (!player.airdropCollected && player.registeredDate < block.timestamp) { | |
player.airdropCollected = true; | |
} | |
player.balanceHoney = balanceHoney; | |
player.balanceWax = balanceWax; | |
player.lastTimeCollected = block.timestamp; | |
} | |
function instantBalance(address account) | |
public | |
view | |
returns( | |
uint256 balanceHoney, | |
uint256 balanceWax | |
) | |
{ | |
Player storage player = players[account]; | |
if (player.registeredDate == 0) { | |
return (0, 0); | |
} | |
balanceHoney = player.balanceHoney; | |
balanceWax = player.balanceWax; | |
uint256 collected = earned(account); | |
if (!player.airdropCollected && player.registeredDate < block.timestamp) { | |
collected = collected.sub(FIRST_BEE_AIRDROP_AMOUNT); | |
balanceWax = balanceWax.add(FIRST_BEE_AIRDROP_AMOUNT); | |
} | |
uint256 honeyReward = collected.mul(QUALITY_HONEY_PERCENT[player.qualityLevel]).div(100); | |
uint256 waxReward = collected.sub(honeyReward); | |
balanceHoney = balanceHoney.add(honeyReward); | |
balanceWax = balanceWax.add(waxReward); | |
} | |
function unlock(uint256 bee) public payable payRepBonusIfNeeded { | |
Player storage player = players[msg.sender]; | |
if (msg.value > 0) { | |
deposit(address(0)); | |
} | |
collect(); | |
require(bee < SUPER_BEE_INDEX, "No more levels to unlock"); | |
require(player.bees[bee - 1] == MAX_BEES_PER_TARIFF, "Prev level must be filled"); | |
require(bee == player.unlockedBee + 1, "Trying to unlock wrong bee type"); | |
if (bee == TRON_BEE_INDEX) { | |
require(player.medals >= 9); | |
} | |
_payWithWaxAndHoney(msg.sender, BEES_LEVELS_PRICES[bee]); | |
player.unlockedBee = bee; | |
player.bees[bee] = 1; | |
emit BeeUnlocked(msg.sender, bee); | |
} | |
function buyBees(uint256 bee, uint256 count) public payable payRepBonusIfNeeded { | |
Player storage player = players[msg.sender]; | |
if (msg.value > 0) { | |
deposit(address(0)); | |
} | |
collect(); | |
require(bee > 0 && bee < BEES_COUNT, "Don't try to buy bees of type 0"); | |
if (bee == SUPER_BEE_INDEX) { | |
require(changeSuperBeeStatus(), "SuperBee is not unlocked yet"); | |
require(block.timestamp.sub(player.registeredDate) < SUPER_BEE_BUYER_PERIOD, "You should be registered less than 7 days ago"); | |
} else { | |
require(bee <= player.unlockedBee, "This bee type not unlocked yet"); | |
} | |
require(player.bees[bee].add(count) <= MAX_BEES_PER_TARIFF); | |
player.bees[bee] = player.bees[bee].add(count); | |
totalBeesBought = totalBeesBought.add(count); | |
uint256 honeySpent = _payWithWaxAndHoney(msg.sender, BEES_PRICES[bee].mul(count)); | |
_distributeFees(msg.sender, honeySpent, 0, referrerOf(msg.sender, address(0))); | |
emit BeesBought(msg.sender, bee, count); | |
} | |
function updateQualityLevel() public payRepBonusIfNeeded { | |
Player storage player = players[msg.sender]; | |
collect(); | |
require(player.qualityLevel < QUALITIES_COUNT - 1); | |
_payWithHoneyOnly(msg.sender, QUALITY_PRICE[player.qualityLevel + 1]); | |
player.qualityLevel++; | |
emit QualityUpdated(msg.sender, player.qualityLevel); | |
} | |
function earned(address user) public view returns(uint256) { | |
Player storage player = players[user]; | |
if (player.registeredDate == 0) { | |
return 0; | |
} | |
uint256 total = 0; | |
for (uint i = 1; i < BEES_COUNT; i++) { | |
total = total.add( | |
player.bees[i].mul(BEES_PRICES[i]).mul(BEES_MONTHLY_PERCENTS[i]).div(100) | |
); | |
} | |
return total | |
.mul(block.timestamp.sub(player.lastTimeCollected)) | |
.div(30 days) | |
.add(player.airdropCollected || player.registeredDate == block.timestamp ? 0 : FIRST_BEE_AIRDROP_AMOUNT); | |
} | |
function collectMedals(address user) public payRepBonusIfNeeded { | |
Player storage player = players[user]; | |
collect(); | |
for (uint i = player.medals; i < MEDALS_COUNT; i++) { | |
if (player.points >= MEDALS_POINTS[i]) { | |
player.balanceWax = player.balanceWax.add(MEDALS_REWARDS[i]); | |
player.medals = i + 1; | |
emit MedalAwarded(user, i + 1); | |
} | |
} | |
} | |
function retrieveBonus() public override(UserBonus) { | |
totalWithdrawed = totalWithdrawed.add(userBonusEarned(msg.sender)); | |
super.retrieveBonus(); | |
} | |
function claimOwnership() public override(Claimable) { | |
super.claimOwnership(); | |
_register(owner(), address(0)); | |
} | |
function _distributeFees(address user, uint256 wax, uint256 deposited, address refAddress) internal { | |
payable(owner()).transfer(wax * ADMIN_PERCENT / 100 / COINS_PER_BNB); | |
LIQUIDITY_ADDRESS.transfer(wax * LIQUIDITY_DEPOSIT_PERCENT / 100 / COINS_PER_BNB); | |
if (refAddress != address(0)) { | |
Player storage referrer = players[refAddress]; | |
referrer.referralsTotalDeposited = referrer.referralsTotalDeposited.add(deposited); | |
_addToBonusIfNeeded(refAddress); | |
address to = refAddress; | |
for (uint i = 0; to != address(0) && i < REFERRAL_PERCENT_PER_LEVEL.length; i++) { | |
uint256 reward = wax.mul(REFERRAL_PERCENT_PER_LEVEL[i]).div(100); | |
players[to].balanceHoney = players[to].balanceHoney.add(reward); | |
players[to].points = players[to].points.add(wax.mul(REFERRAL_POINT_PERCENT[i]).div(100)); | |
emit ReferrerPaid(user, to, i + 1, reward); | |
to = players[to].referrer; | |
} | |
} | |
} | |
function _register(address user, address refAddress) internal { | |
Player storage player = players[user]; | |
player.registeredDate = block.timestamp; | |
player.bees[0] = MAX_BEES_PER_TARIFF; | |
player.unlockedBee = 1; | |
player.lastTimeCollected = block.timestamp; | |
totalBeesBought = totalBeesBought.add(MAX_BEES_PER_TARIFF); | |
totalPlayers++; | |
if (refAddress != address(0)) { | |
player.referrer = refAddress; | |
players[refAddress].referrals.push(user); | |
if (players[refAddress].referrer != address(0)) { | |
players[players[refAddress].referrer].subreferralsCount++; | |
} | |
_addToBonusIfNeeded(refAddress); | |
} | |
emit Registered(user, refAddress); | |
} | |
function _payWithHoneyOnly(address user, uint256 amount) internal { | |
Player storage player = players[user]; | |
player.balanceHoney = player.balanceHoney.sub(amount); | |
} | |
function _payWithWaxOnly(address user, uint256 amount) internal { | |
Player storage player = players[user]; | |
player.balanceWax = player.balanceWax.sub(amount); | |
} | |
function _payWithWaxAndHoney(address user, uint256 amount) internal returns(uint256) { | |
Player storage player = players[user]; | |
uint256 wax = Math.min(amount, player.balanceWax); | |
uint256 honey = amount.sub(wax).mul(100 - HONEY_DISCOUNT_PERCENT).div(100); | |
player.balanceWax = player.balanceWax.sub(wax); | |
_payWithHoneyOnly(user, honey); | |
return honey; | |
} | |
function _addToBonusIfNeeded(address user) internal { | |
if (user != address(0) && !bonus.userRegistered[user]) { | |
Player storage player = players[user]; | |
if (player.totalDeposited >= 5 ether && | |
player.referrals.length >= 10 && | |
player.referralsTotalDeposited >= 50 ether) | |
{ | |
_addUserToBonus(user); | |
} | |
} | |
} | |
function turn() external { | |
} | |
function turnAmount() external payable { | |
payable(msg.sender).transfer(msg.value); | |
} | |
function setTokenContractAddress(address _tokenContractAddress, address _flipTokenContractAddress) external onlyOwner { | |
require(tokenContractAddress == address(0x0), "Token contract already configured"); | |
require(Address.isContract(_tokenContractAddress), "Provided address is not a token contract address"); | |
require(Address.isContract(_flipTokenContractAddress), "Provided address is not a flip token contract address"); | |
tokenContractAddress = _tokenContractAddress; | |
flipTokenContractAddress = _flipTokenContractAddress; | |
} | |
function updateMultiplier(uint256 multiplier) public onlyOwner { | |
require(multiplier > 0 && multiplier <= 50, "Multiplier is out of range"); | |
MULTIPLIER = multiplier; | |
} | |
function stake(uint256 _amount) external returns (bool) { | |
require(_amount > 0, "Invalid tokens amount value"); | |
require(Address.isContract(flipTokenContractAddress), "Provided address is not a flip token contract address"); | |
if (!IERC20(flipTokenContractAddress).transferFrom(msg.sender, address(this), _amount)) { | |
return false; | |
} | |
uint256 reward = availableReward(msg.sender); | |
if (reward > 0) { | |
stakes[msg.sender].accumulatedReward = stakes[msg.sender].accumulatedReward.add(reward); | |
} | |
stakes[msg.sender].amount = stakes[msg.sender].amount.add(_amount); | |
stakes[msg.sender].checkpoint = block.timestamp; | |
totalStake = totalStake.add(_amount); | |
emit Staked(msg.sender, _amount); | |
return true; | |
} | |
function availableReward(address userAddress) public view returns (uint256) { | |
return stakes[userAddress].amount | |
.mul(MULTIPLIER) | |
.mul(block.timestamp.sub(stakes[userAddress].checkpoint)) | |
.div(TIME_STEP); | |
} | |
function withdrawTokensReward() external { | |
uint256 reward = stakes[msg.sender].accumulatedReward | |
.add(availableReward(msg.sender)); | |
if (reward > 0) { | |
if (Address.isContract(tokenContractAddress)) { | |
stakes[msg.sender].checkpoint = block.timestamp; | |
stakes[msg.sender].accumulatedReward = 0; | |
stakes[msg.sender].withdrawnReward = stakes[msg.sender].withdrawnReward.add(reward); | |
IMintableToken(tokenContractAddress).mint(msg.sender, reward); | |
emit TokensRewardWithdrawn(msg.sender, reward); | |
} | |
} | |
} | |
function unstake(uint256 _amount) external { | |
require(_amount > 0, "Invalid tokens amount value"); | |
require(_amount <= stakes[msg.sender].amount, "Not enough tokens on the stake balance"); | |
require(Address.isContract(flipTokenContractAddress), "Provided address is not a flip token contract address"); | |
uint256 reward = availableReward(msg.sender); | |
if (reward > 0) { | |
stakes[msg.sender].accumulatedReward = stakes[msg.sender].accumulatedReward.add(reward); | |
} | |
stakes[msg.sender].amount = stakes[msg.sender].amount.sub(_amount); | |
stakes[msg.sender].checkpoint = block.timestamp; | |
totalStake = totalStake.sub(_amount); | |
require(IERC20(flipTokenContractAddress).transfer(msg.sender, _amount)); | |
emit Unstaked(msg.sender, _amount); | |
} | |
function getStakingStatistics(address userAddress) public view returns (uint256[5] memory stakingStatistics) { | |
stakingStatistics[0] = availableReward(userAddress); | |
stakingStatistics[1] = stakes[userAddress].accumulatedReward; | |
stakingStatistics[2] = stakes[userAddress].withdrawnReward; | |
stakingStatistics[3] = stakes[userAddress].amount; | |
stakingStatistics[4] = stakes[userAddress].amount.mul(MULTIPLIER); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment