Contract Source Code:
File 1 of 1 : VRMatrix
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;
// Ownable contract to manage contract ownership
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(msg.sender);
}
function owner() public view returns (address) {
return _owner;
}
modifier onlyOwner() {
require(_owner == msg.sender, "Ownable: caller is not the owner");
_;
}
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// ReentrancyGuard contract to prevent reentrancy attacks
contract ReentrancyGuard {
uint256 private _status;
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
constructor() {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
}
// Interface for the registration contract
interface IREG {
function NumberOfUsers() external view returns (uint256);
function AddressToCountId(address user) external view returns (uint256);
function countIdToAddress(uint id) external view returns (address);
function isRegistered(address user) external view returns (bool);
function userUpline(address user) external view returns (address);
}
contract VRMatrix is Ownable , ReentrancyGuard {
// public variables-
uint public numberOfActiveUsers;
uint public totalEarned ;
IREG public regContract;
address public lastUser;
uint public TOTAL_LEVELS = 10;
constructor() {
regContract = IREG(0xbbcd14B04924F33c303627578708fd4f51d39383);
levelPrice[1] = 5 ether ;
for (uint i = 2; i < TOTAL_LEVELS ; i++ ) {
levelPrice[i] = levelPrice[i - 1] * 2; // each price from 1 is 2X the last price
}
initOwner(); // initialize the constante variables and public variables with the values from the registry
}
function initOwner() internal {
address zeroAddress = address(0);
lastUser = getFirstID();
virtualUplineOf[lastUser] = zeroAddress ;
users[lastUser] = User ({
userAddress : lastUser,
team_Id : 1 ,
totalIncome : 0 ,
totalVirtualIncome : 0 ,
transactionCount : 0 ,
totalDirect : 0 ,
lastUpdate : block.timestamp ,
currentUserLevel : TOTAL_LEVELS ,
firstActivationDate : block.timestamp
}) ;
userLevel[lastUser] = TOTAL_LEVELS ;
virtualIds[lastUser] = numberOfActiveUsers;
isActive[lastUser] = true ;
numberOfActiveUsers = 1;
emit ownerInit(lastUser);
}
mapping (address => address ) public virtualUplineOf;
mapping (uint => uint ) public levelPrice;
mapping (address => uint ) public userLevel;
mapping (address => uint ) public totalVirtualDirect;
mapping (address => address[] ) public virtualDirectsOf;
mapping (uint => address ) public virtualAddresses;
mapping (address => uint ) public virtualIds;
mapping (address => bool ) public isActive;
mapping (address => User ) public users;
mapping (address => UserHistory [] ) public history;
// user structure to save total informations
struct User {
address userAddress;
uint team_Id ;
uint totalIncome;
uint totalVirtualIncome;
uint transactionCount;
uint totalDirect;
uint lastUpdate;
uint currentUserLevel ;
uint firstActivationDate ;
}
// userHistory to track system activities
struct UserHistory {
string actionName;
uint actionAmount;
address actionFrom;
uint actionDate;
}
// events to track contract activities
event TransferSent (address to, uint amount , uint timestamp);
event virtualGainSent (address to , uint amount , uint timestamp);
event RoyaltyGainSent(address to, uint amount , uint timestamp);
event LevelPurchased (address from, uint amount , uint timestamp);
event ownerInit (address owner) ;
// get the fiirst id registered
function getFirstID () internal view returns(address) {
return regContract.countIdToAddress(1);
}
function purchase (uint level ) public nonReentrant payable {
// the user must enter the correct level
require(level > 0 && level < 11, "Level is out of range");
// the user can't be the 0 address
require(msg.sender != address(0), "Zero address is not allowed");
// the user must be regsitered
require(regContract.isRegistered(msg.sender), "User is not registered");
// the value to send must be equal to the price of the level
require(msg.value == levelPrice[level], "not enougth opBNB");
if (level > 1 ) {
// the user must have the previus level activated
require(userLevel[msg.sender] == level - 1, "Previous level not activated");
}
// handling user purchase
if (level == 1) {
handlePurchase(level) ;
} else {
handlePurchaseFromVrData (level) ;
}
// after distribution handle updates
address uplineOfUser = regContract.userUpline(msg.sender);
uint totalDirect = totalVirtualDirect[uplineOfUser] ;
if (users[msg.sender].userAddress == address(0) ) {
// it's the user first time to buy the level ...
users[msg.sender] = User ({
userAddress : msg.sender,
team_Id : totalDirect + 1 ,
totalIncome : 0 ,
totalVirtualIncome : 0 ,
transactionCount : 0 ,
totalDirect : 0 ,
lastUpdate : block.timestamp ,
currentUserLevel : level ,
firstActivationDate : block.timestamp
}) ;
totalVirtualDirect[uplineOfUser] ++ ;
virtualDirectsOf[uplineOfUser].push(msg.sender) ;
numberOfActiveUsers ++ ;
virtualIds[msg.sender] = numberOfActiveUsers;
isActive[msg.sender] = true ;
virtualUplineOf[msg.sender] = uplineOfUser;
}
userLevel[msg.sender] = level ;
lastUser = msg.sender ;
totalEarned += msg.value ;
emit LevelPurchased(msg.sender , levelPrice[level], block.timestamp) ;
}
function purchase_sec_case (address user , uint level ) public nonReentrant onlyOwner {
// the user must enter the correct level
require(level > 0 && level < 11, "Level is out of range");
// the user can't be the 0 address
require(user != address(0), "Zero address is not allowed");
// the user must be regsitered
require(regContract.isRegistered(user), "User is not registered");
// after distribution handle updates
address uplineOfUser = regContract.userUpline(user);
uint totalDirect = totalVirtualDirect[uplineOfUser] ;
if (users[user].userAddress == address(0) ) {
// it's the user first time to buy the level ...
users[user] = User ({
userAddress : user,
team_Id : totalDirect + 1 ,
totalIncome : 0 ,
totalVirtualIncome : 0 ,
transactionCount : 0 ,
totalDirect : 0 ,
lastUpdate : block.timestamp ,
currentUserLevel : level ,
firstActivationDate : block.timestamp
}) ;
totalVirtualDirect[uplineOfUser] ++ ;
virtualDirectsOf[uplineOfUser].push(user) ;
numberOfActiveUsers ++ ;
virtualIds[user] = numberOfActiveUsers;
isActive[user] = true ;
virtualUplineOf[user] = uplineOfUser;
}
userLevel[user] = level ;
emit LevelPurchased(user , levelPrice[level], block.timestamp) ;
}
function handlePurchase (uint level) internal {
address uplineOfUser = regContract.userUpline(msg.sender);
address [4] memory uplines = getUPlinesArray(uplineOfUser);
if (uplineOfUser != address(0)) {
// we get the number of virtual downlines of the upline of this adress
// and add 1 to get the position of the upcoming user
uint directNumber = totalVirtualDirect[uplineOfUser] + 1;
// if the upcoming user is odd
if (directNumber % 2 != 0) {
// the direct is odd
// this function will distribute 90% to the direct upline
// and 10% to the last user has Free income
distributeDirectIncome ( level , uplines);
} else {
distributeToUplines (level , uplines) ;
}
}
}
function getUPlinesArray (address uplineOne) internal view returns (address [4] memory ) {
address [4] memory uplines ;
uplines[0] = uplineOne;
// we already have one upline , this function will return 4 uplines
for (uint i = 1 ; i < 4 ; i++ ) {
uplines[i] = regContract.userUpline(uplines[i - 1]);
}
return uplines;
}
function distributeDirectIncome ( uint level , address [4] memory uplines) internal returns(uint ) {
address firstUpline = uplines[0];
address currentUpline ;
address firstId = getFirstID();
if (firstUpline == address(0)) {
currentUpline = firstId;
distributeFunds(level , currentUpline) ;
return 0 ;
}
for (uint i = 0 ; i < uplines.length; i ++) {
if (hasLevel(level, uplines[i])) {
currentUpline = uplines[i] ;
break ;
}
if (i == 3) {
currentUpline = firstId;
}
}
distributeFunds(level , currentUpline) ;
return 0 ;
}
function hasLevel (uint level ,address user) internal view returns (bool){
return userLevel[user] >= level;
}
// this function transfer the amount to the sponsor who is detecet by the function callert
function distributeFunds(uint level, address user) internal {
uint amount = levelPrice[level] ;
if (user != address(0)) {
(bool success , ) = user.call{value : amount}("") ;
require(success, "Transfer to the user failed") ;
UserHistory memory newHistory = UserHistory ({
actionName : "Direct Gain" ,
actionAmount : amount ,
actionFrom : msg.sender ,
actionDate : block.timestamp
}) ;
history[user].push(newHistory) ;
// upldate user state
users[user].totalIncome += amount ;
users[user].transactionCount += 1 ;
users[user].lastUpdate = block.timestamp ;
} else {
address firstSponsor = getFirstID();
(bool success2 , ) = firstSponsor.call{value : amount}("");
require(success2, "An error occured during direct transfer");
}
emit TransferSent(user, amount, block.timestamp);
}
// this function send at the same time to 4 sponsor 25% of the investment if they are available
function distributeToUplines (uint level , address [4] memory uplines) internal {
uint staticAmount = levelPrice[level] * 25 / 100;
address initialUpline = getFirstID();
for (uint i = 0 ; i < uplines.length ; i ++) {
address currentUpline = uplines[i] ;
address availableUpline;
if (currentUpline == address(0) || !hasLevel(level, currentUpline)) {
availableUpline = initialUpline ;
} else {
availableUpline = currentUpline ;
}
(bool success , ) = availableUpline.call{value :staticAmount }("");
require(success, "the transfer to the current upline amoung 4 of them failed");
UserHistory memory newHistory = UserHistory ({
actionName : "Sponsor Gain" ,
actionAmount : staticAmount ,
actionFrom : msg.sender ,
actionDate : block.timestamp
}) ;
history[availableUpline].push(newHistory);
// upldate user state
users[availableUpline].totalIncome += staticAmount;
users[availableUpline].transactionCount += 1 ;
users[availableUpline].lastUpdate = block.timestamp ;
users[availableUpline].totalVirtualIncome += staticAmount;
emit TransferSent(availableUpline, staticAmount, block.timestamp);
}
}
function handlePurchaseFromVrData (uint level) internal {
uint team_idOfUser = users[msg.sender].team_Id ;
address uplineOfUser = regContract.userUpline(msg.sender);
address [4] memory uplines = getUPlinesArray(uplineOfUser);
if (team_idOfUser % 2 != 0) {
distributeDirectIncome (level , uplines) ;
} else {
distributeToUplines(level , uplines) ;
}
}
function getvirtualTeamSize(address user) public view returns (uint256) {
require(regContract.isRegistered(user), "User not registered");
uint256 totalTeamSize = _getTotalTeamSize(user);
return totalTeamSize;
}
function _getTotalTeamSize(address user) internal view returns (uint256) {
uint256 teamSize = virtualDirectsOf[user].length;
// Parcours récursif pour compter les downlines des downlines
for (uint256 i = 0; i < virtualDirectsOf[user].length; i++) {
teamSize += _getTotalTeamSize(virtualDirectsOf[user][i]);
}
return teamSize;
}
function getStories (address user) external view returns (UserHistory[] memory) {
return history[user];
}
}