1

Unable to deploy this code on any chain. I have done all i could, cleared my browser and restarted my pc and still could not deploy. what could be the issue please.

    // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";

contract P2PDonation is Ownable(msg.sender), ReentrancyGuard, Pausable {
IERC20 public usdtToken;

    struct User {
    uint64 balance;
    uint32 level;
    address referrer;
    uint64 totalDonated;
    uint64 referralRewards;
    uint64 totalReceived;
    uint64 totalWithdrawn;
    bool exists;

}

    struct Level {
        uint256 requiredBalance;
        uint256 totalPool;
        string name;
    }
    
    uint256 public constant DEPOSIT_AMOUNT = 10 * 1e6; // Assuming USDT has 6 decimals
    uint256 public constant MAX_LEVEL = 10;
    uint256 public constant REFERRAL_BONUS_PERCENT = 1;
    uint256 public constant TAX_PERCENT = 20;
    
    Level[MAX_LEVEL] public levels;
    mapping(address => User) public users;
    address[] public queue;
    mapping(address => uint256) public pendingWithdrawals;
    address[] private founders;
    uint8[] private founderShares;
    
    
    // Events
    event Deposited(address indexed user, uint256 amount);
    event Withdrawn(address indexed user, uint256 amount);
    event Upgraded(address indexed user, uint256 level);
    event ReferralRewarded(address indexed referrer, address indexed referee, uint256 amount);
    event EmergencyWithdrawal(address indexed owner, uint256 amount);
    event ReferralBonusWithdrawn(address indexed user, uint256 bonusAmount);
    event UserRecycled(address indexed user, uint32 level);
    
    
    constructor(address _usdtTokenAddress) {
        usdtToken = IERC20(_usdtTokenAddress);
        transferOwnership(msg.sender);
    
        initializeLevels();
        initializeFounders();
    }
    
    function initializeLevels() internal {
    levels[0] = Level({requiredBalance: 10 * 1e6, totalPool: 40 * 1e6, name: "1BABY"});
    levels[1] = Level({requiredBalance: 16 * 1e6, totalPool: 64 * 1e6, name: "2PILOT"});
    levels[2] = Level({requiredBalance: 25 * 1e6, totalPool: 104 * 1e6, name: "3GOLD"});
    levels[3] = Level({requiredBalance: 40 * 1e6, totalPool: 160 * 1e6, name: "4COMMANDER"});
    levels[4] = Level({requiredBalance: 65 * 1e6, totalPool: 260 * 1e6, name: "5SENATOR"});
    levels[5] = Level({requiredBalance: 104 * 1e6, totalPool: 416 * 1e6, name: "6DIRECTOR"});
    levels[6] = Level({requiredBalance: 167 * 1e6, totalPool: 668 * 1e6, name: "7EEXUTIVE"});
    levels[7] = Level({requiredBalance: 268 * 1e6, totalPool: 1072 * 1e6, name: "8PRIME_MINISTER"});
    levels[8] = Level({requiredBalance: 429 * 1e6, totalPool: 1716 * 1e6, name: "9BLUE_DIAMOND"});
    levels[9] = Level({requiredBalance: 1000 * 1e6, totalPool: 6300 * 1e6, name: "10PRESIDENT"});
    }
    
    
    function initializeFounders() internal {
    founders = [
        0xF8838532D1FD225E93520Edfbe19FA6aE6369320,
        0x13E209F184BCEFBea537A2e50DB554f60e0fd1a9,
        0xD24aF77C52701259FC404c5D64B0E55DC37f806C,
        0xE137ee359438064bA10b881497a4F30A75a346cd,
        0x9B7B2cBA5885A8B653a2aD8142C42138A4bCf523,
        0xEdf7192e99a192C9c1428121BA963B8346B07c8c
    ];
    founderShares = [7, 6, 4, 1, 1, 1];
    }
    
    // Deposit function
    function deposit(address _referrer) external whenNotPaused nonReentrant {
        validateDepositAmount(DEPOSIT_AMOUNT);
        User storage user = users[msg.sender];
        user.balance = uint64(DEPOSIT_AMOUNT);
        initializeUserOnFirstDeposit(user, _referrer);
        processUserDeposit(user);
        transferFundsToContract();
        emit Deposited(msg.sender, DEPOSIT_AMOUNT);
    }
    
    function validateDepositAmount(uint256 _amount) private pure {
    require(_amount == DEPOSIT_AMOUNT, "Invalid deposit amount");
    }
    
    function initializeUserOnFirstDeposit(User storage user, address _referrer) private {
        require(_referrer != address(0) && _referrer != msg.sender, "Invalid referrer");
        require(!user.exists, "User already exists");
        user.exists = true;
        user.referrer = _referrer;
        user.level = 1;
    }
    
    function processUserDeposit(User storage user) private {
        // Reset user's balance if required
        user.balance = uint64(DEPOSIT_AMOUNT);
    
        // Add user to the queue
        queue.push(msg.sender);
    }
    
    function transferFundsToContract() private {
        require(usdtToken.transferFrom(msg.sender, address(this), DEPOSIT_AMOUNT), "Transfer failed");
    }
    
    
    // Withdraw earnings function
    function withdrawEarnings() external whenNotPaused nonReentrant {
    User storage user = users[msg.sender];
    validateEnrollment(user);
    
    uint256 withdrawalAmount = calculateWithdrawalAmount(user);
    
    // Check if the withdrawal is possible given the user's current balance
    require(withdrawalAmount <= user.balance, "Withdrawal amount exceeds balance");
    
    performAutomaticUpgrade(user, withdrawalAmount);
    
    uint256 tax = calculateTax(withdrawalAmount);
    withdrawalAmount -= tax; // Apply tax
    distributeTax(tax);
    
    // Execute the withdrawal
    executeWithdrawal(msg.sender, withdrawalAmount);
    
    // Recycling logic for max level users
    if(user.level == MAX_LEVEL && withdrawalAmount > 0) {
        // After successful withdrawal, recycle the user back to level 1
        recycleUser(user);
    } else {
        // Update the total withdrawn for users not at max level
        user.totalWithdrawn += uint64(withdrawalAmount);
    }
    
    emit Withdrawn(msg.sender, withdrawalAmount);
    }
    
    function recycleUser(User storage user) private {
    user.level = 1;
    user.balance = uint64(levels[0].requiredBalance); // Set balance to required balance of level 1
    
    // Resetting other user properties
    user.totalDonated = 0;
    user.referralRewards = 0;
    user.totalReceived = 0;
    user.totalWithdrawn = 0;
    // You might want to reset other properties depending on your contract's logic
    
    // Emit an event if necessary, for example:
    emit UserRecycled(msg.sender, user.level);
    }
    
    
    function withdrawPendingAmount() external {
    uint256 amount = pendingWithdrawals[msg.sender];
    require(amount > 0, "No funds to withdraw");
    
    pendingWithdrawals[msg.sender] = 0;
    
    require(usdtToken.transfer(msg.sender, amount), "Transfer failed");
    
    }
    
    
    function validateEnrollment(User storage user) private view {
        require(user.exists, "User is not enrolled");
        require(user.balance > 0, "Insufficient balance");
    }
    
    function performAutomaticUpgrade(User storage user, uint256 withdrawalAmount) private {
    uint256 upgradeCost = calculateUpgradeCost(user.level);
    require(withdrawalAmount <= user.balance, "Withdrawal amount exceeds balance"); // Check
    if (withdrawalAmount / 2 >= upgradeCost) {
        user.balance -= uint64(upgradeCost); // Effects
        handleUpgradeFinances(upgradeCost); // External Interaction (handled carefully)
        user.level++;
        emit Upgraded(msg.sender, user.level);
    }

}

    function executeWithdrawal(address userAddress, uint256 withdrawalAmount) private {
        require(usdtToken.transfer(userAddress, withdrawalAmount), "Withdrawal transfer failed");
    }
    
    function calculateWithdrawalAmount(User storage user) private view returns (uint256) {
    Level storage userLevel = levels[user.level - 1];
    uint256 poolEarnings = userLevel.totalPool;
    require(user.totalReceived <= poolEarnings, "Maximum earnings exceeded");
    
    // Max withdrawal for max level users is the total pool minus the recycling fee
    if(user.level == MAX_LEVEL) {
        uint256 recyclingFee = 10 * 1e6; // Recycling fee of 10 USDT
        uint256 maxWithdrawal = poolEarnings - recyclingFee;
        // Ensure the user has not withdrawn more than they are allowed
        require(user.totalWithdrawn <= maxWithdrawal, "Withdrawal exceeds limit");
        return maxWithdrawal - user.totalWithdrawn; // Adjusted to deduct the recycling fee
    } else {
        // For users below max level, they can withdraw 50% of their balance
        uint256 maxWithdrawal = poolEarnings - user.totalWithdrawn;
        return maxWithdrawal / 2; // User can only withdraw 50% of their balance
    }

}

    function handleReferralBonusOnUpgrade(address referrer, uint256 upgradeCost) internal {
        uint256 taxAmount = calculateTax(upgradeCost);
        uint256 netUpgradeCost = upgradeCost - taxAmount;
    
        uint256 referralBonus = netUpgradeCost * REFERRAL_BONUS_PERCENT / 100;
    
        User storage referrerUser = users[referrer];
        referrerUser.referralRewards += uint64(referralBonus);
    
        emit ReferralRewarded(referrer, msg.sender, referralBonus);
        
        // Record the referral bonus for withdrawal
        recordPendingWithdrawal(referrer, referralBonus);
    }
    
    // Modified function to calculate tax
    function calculateTax(uint256 amount) private pure returns (uint256) {
        return amount * TAX_PERCENT / 100;
    }
    
    function distributeTax(uint256 taxAmount) private {
        for (uint256 i = 0; i < founders.length; i++) {
            uint256 share = taxAmount * founderShares[i] / 100;
            require(usdtToken.transfer(founders[i], share), "Tax distribution failed");
        }
    }
    
    function calculateUpgradeCost(uint256 currentLevel) private view returns (uint256) {
        if (currentLevel >= MAX_LEVEL) {
            // If the user is at the max level, return 0 as there's no upgrade cost.
            return 0;
        }
        // Calculate the upgrade cost based on the level structure.
        // 50% of the next level's pool is used as the upgrade cost.
        Level storage nextLevel = levels[currentLevel]; // currentLevel is 1-indexed, array is 0-indexed
        return nextLevel.totalPool / 2;
    }
    
    function recordPendingWithdrawal(address userAddress, uint256 withdrawalAmount) private {
    pendingWithdrawals[userAddress] += withdrawalAmount;
    }
    
    function handleUpgradeFinances(uint256 upgradeCost) private {
        if (queue.length > 0 && upgradeCost > 0) {
            address frontUserAddress = queue[0];
            User storage frontUser = users[frontUserAddress];
            uint256 paymentAmount = upgradeCost;
    
            uint256 contractBalance = usdtToken.balanceOf(address(this));
            require(contractBalance >= paymentAmount, "Insufficient contract balance");
    
            recordPendingWithdrawal(frontUserAddress, paymentAmount);
            frontUser.totalReceived += uint64(paymentAmount);
            emit Withdrawn(frontUserAddress, paymentAmount);
    
            // Remove the front user from the queue
            removeFromQueue(frontUserAddress);
    
            // Handle referral bonus on upgrade for front user
            if (frontUser.referrer != address(0)) {
                handleReferralBonusOnUpgrade(frontUser.referrer, paymentAmount);
            }
        }
    }
    
    // Automatic upgrade logic
    function performUpgrade() internal {
    User storage user = users[msg.sender];
    if (validateUpgradeEligibility(user)) {
        uint256 upgradeCost = calculateUpgradeCost(user.level);
        executeUpgrade(user, upgradeCost);
        handleUpgradeFinances(upgradeCost);
        settleFrontUser();
    }

}

    function validateUpgradeEligibility(User storage user) internal view returns (bool) {
        uint256 requiredEarnings = levels[user.level - 1].totalPool;
        return user.totalReceived >= requiredEarnings / 2;
    }
    
    function executeUpgrade(User storage user, uint256 upgradeCost) internal {
        user.level += 1;
        user.totalReceived -= uint64(upgradeCost);
        emit Upgraded(msg.sender, user.level);
    }
    
    // Queue management logic
    function addToQueue(address userAddress) internal {
        queue.push(userAddress);
    }
    
    function removeFromQueue(address userAddress) internal {
    // Ensure the queue is not empty
    require(queue.length > 0, "Queue is empty");
    
    // Check that the user to remove is at the front of the queue
    require(queue[0] == userAddress, "User is not at the front of the queue");
    
    // Remove the first element by shifting all elements to the left
    for (uint256 i = 0; i < queue.length - 1; i++) {
        queue[i] = queue[i + 1];
    }
    queue.pop(); // Remove the last element, which is now a duplicate of the second to last element

}

    function settleFrontUser() internal {
    require(queue.length > 0, "Queue is empty");
    
    address frontUserAddress = queue[0];
    User storage frontUser = users[frontUserAddress];
    
    uint256 paymentAmount = calculateSettlementAmount(frontUser);
    
    // Check if the contract has enough balance to make the payment
    uint256 contractBalance = usdtToken.balanceOf(address(this));
    require(contractBalance >= paymentAmount, "Insufficient contract balance");
    
    // Record the payment to the front user as a pending withdrawal
    recordPendingWithdrawal(frontUserAddress, paymentAmount);
    frontUser.totalReceived += uint64(paymentAmount);
    emit Withdrawn(frontUserAddress, paymentAmount);
    
    // Handle the referral bonus for the referrer if applicable
    if (frontUser.referrer != address(0)) {
        handleReferralBonusOnSettlement(frontUser.referrer);
    }
    
    // Remove the front user from the queue
    removeFromQueue(frontUserAddress);
    }
    
    
    
    function calculateSettlementAmount(User storage user) internal view returns (uint256) {
    // Since payments are only made to the front user, check if the user is at the front of the queue
    if (isUserFrontOfQueue(user)) {
        Level storage userLevel = levels[user.level - 1];
        return userLevel.totalPool; // Front user receives the full pool amount for their level
    } else {
        return 0; // Users not at the front of the queue receive no payment
    }
    }
    
    function isUserFrontOfQueue(User storage user) internal view returns (bool) {
    address userAddress = user.referrer;
    return queue.length > 0 && queue[0] == userAddress;
    }
    
    function findUserIndexInQueue(address userAddress) internal view returns (uint256) {
    for (uint256 i = 0; i < queue.length; i++) {
        if (queue[i] == userAddress) {
            return i;
        }
    }
    revert("User not in queue");
    }
    
     // Owner can pause the contract in case of an emergency
    function pause() external onlyOwner {
        _pause();
    }
    
    // Owner can unpause the contract once the emergency is resolved
    function unpause() external onlyOwner {
        _unpause();
    }
    
    // Owner can withdraw the contract's funds in case of an emergency
    function emergencyWithdraw() external onlyOwner {
        uint256 balance = usdtToken.balanceOf(address(this));
        require(balance > 0, "No funds to withdraw");
        require(usdtToken.transfer(owner(), balance), "Emergency withdrawal failed");
        emit EmergencyWithdrawal(owner(), balance);
    }
    // Function for users to withdraw their referral bonuses
    function withdrawRefBonus() external whenNotPaused nonReentrant {
    User storage user = users[msg.sender];
    uint256 bonusAmount = user.referralRewards;
    require(bonusAmount > 0, "No referral bonus to withdraw");
    
    // Reset the referral bonus balance before transferring to prevent re-entrancy attacks
    user.referralRewards = 0;
    
    // Transfer the referral bonus to the user
    require(usdtToken.transfer(msg.sender, bonusAmount), "Referral bonus transfer failed");
    
    // Emit an event for the withdrawal
    emit ReferralBonusWithdrawn(msg.sender, bonusAmount);
    }
    
    function handleReferralBonusOnSettlement(address referrer) internal {
    uint256 referralBonus = DEPOSIT_AMOUNT * REFERRAL_BONUS_PERCENT / 100;
    User storage referrerUser = users[referrer];
    referrerUser.referralRewards += uint64(referralBonus);
    emit ReferralRewarded(referrer, msg.sender, referralBonus);
    
    // Record the referral bonus as a pending withdrawal
    recordPendingWithdrawal(referrer, referralBonus);
    }

}

2 Answers 2

0

There may be other issues, but the first thing that pops out to me is an array out-of-bounds issue.

If you try to access (read or write) to an index in an array that is larger than the arrays length, then it will revert because you're going outside of its bounds. Arrays, unless specified during creation if its a memory array, start with a length of 0. You do this in initializeLevels 10 times. You should use the push function of arrays if you want to add an element. If you change the contents of that function to the below, it should fix it.

    levels.push(Level({requiredBalance: 10 * 1e6, totalPool: 40 * 1e6, name: "1BABY"})));
    levels.push(Level({requiredBalance: 16 * 1e6, totalPool: 64 * 1e6, name: "2PILOT"})));
    levels.push(Level({requiredBalance: 25 * 1e6, totalPool: 104 * 1e6, name: "3GOLD"})));
    levels.push(Level({requiredBalance: 40 * 1e6, totalPool: 160 * 1e6, name: "4COMMANDER"})));
    levels.push(Level({requiredBalance: 65 * 1e6, totalPool: 260 * 1e6, name: "5SENATOR"})));
    levels.push(Level({requiredBalance: 104 * 1e6, totalPool: 416 * 1e6, name: "6DIRECTOR"})));
    levels.push(Level({requiredBalance: 167 * 1e6, totalPool: 668 * 1e6, name: "7EEXUTIVE"})));
    levels.push(Level({requiredBalance: 268 * 1e6, totalPool: 1072 * 1e6, name: "8PRIME_MINISTER"})));
    levels.push(Level({requiredBalance: 429 * 1e6, totalPool: 1716 * 1e6, name: "9BLUE_DIAMOND"})));
    levels.push(Level({requiredBalance: 1000 * 1e6, totalPool: 6300 * 1e6, name: "10PRESIDENT"})));

Again, there may be other issues, but this popped out to me

3
  • @natweltch thanks for your fast response. Could you please help me with the error fix you are talking about. Much appreciated
    – Bamu Gift
    Nov 10, 2023 at 17:02
  • Thank you very much Mr. Nate for your prompt answer. could you please help me review the entire code snippet to fix any issue. I have been able to flatten the code and successfully deployed on remix. The issue I'm getting now is that, the contract ABI and address is not talking to my frontend when i connect. I will really appreciate if I get the correct code snippet from you. Thanks once more again Sir.
    – Bamu Gift
    Nov 11, 2023 at 4:03
  • @BamuGift if you're new issue seems to be unrelated to the issue in you're original post (it sounds like it), I would suggest creating a new question with as much info describing the new issue as possible. I can respond there if I think I can be of help (:
    – natewelch_
    Nov 13, 2023 at 14:31
0

You can only test in Goerli or Sepolia. In remix, importing the openzeppelin library requires your compiled version ^0.8.20; while most bsc or other Ethereum compatible chains are not yet compatible with push0, so an invalid opcode is prompted.

1
  • It still didn't work until I had to flatten the code before it was able to deploy. The challenge I'm getting now is that the functions are not working after USDT token is approved. Thanks for your time.
    – Bamu Gift
    Nov 11, 2023 at 20:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.