Contract - Code

Optimism Mainnet (10): https://optimistic.etherscan.io/address/0xBBBcad6EdBCBE647FBe273fb588cb5e8E1B385da#code BNB SmartChain (56): https://bscscan.com/address/0xBBBcad6EdBCBE647FBe273fb588cb5e8E1B385da#code Polygon Mainnet (137): https://polygonscan.com/address/0xBBBcad6EdBCBE647FBe273fb588cb5e8E1B385da#code

Arbitrum One (42161): https://arbiscan.io/address/0xBBBcad6EdBCBE647FBe273fb588cb5e8E1B385da#code ====== Revamp Contract =====

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

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/**
 * @title Revamp
 * @notice Decentralized protocol for token listing, revamp (burn), referral rewards, and native value redistribution.
 *         Includes a global per-wallet native contribution cap (owner adjustable) and initial participant migration logic.
 */
contract Revamp is ReentrancyGuard, Ownable {
    using SafeERC20 for IERC20;

    // ────────────── Constants ──────────────
    uint256 public constant PRECISION = 1e18;

    // ────────────── Structs ──────────────
    struct UserInfo {
        uint256 totalContributed;    // Native sent by user (principal)
        uint256 rewardDebt;          // Used for reward calculations
        uint256 claimedSoFar;        // Total claimed rewards
    }

    struct TokenInfo {
        uint256 rate;                // Fixed revamp rate (token per native)
        address lister;              // Who listed this token
        string logoUrl;
        uint8 decimals;
        string name;
        string symbol;
    }

    struct TokenData {
        address token;
        uint256 rate;
        address lister;
        string logoUrl;
        uint8 decimals;
        string name;
        string symbol;
    }

    // ────────────── State Variables ──────────────

    // Contribution cap
    uint256 public maxContributionPerWallet;

    // Referral system
    mapping(address => address) public referrerOf;
    address public genesisAddress;
    uint256 public referralFeePercent;

    // Listed tokens and info
    mapping(address => TokenInfo) public tokenInfos;
    address[] private listedTokens;

    // Fee parameters
    uint256 public listingFee;
    uint256 public claimFee;
    uint256 public delistFee;
    address public feeRecipient;
    uint256 public totalListingFees;

    // Revenue splits
    uint256 public nativeFeePercent;
    address public nativeFeeRecipient;
    uint256 public shareholdingFeePercent;
    address public shareholdingFeeRecipient;
    uint256 public fastLineFeePercent;
    address public fastLineFeeRecipient;

    // ⭐️ NEW: Cumulative sum of all Fast Line fees forwarded
    uint256 public totalFastLineFees;

    // Reward/accounting variables
    uint256 public totalNativeContributed;
    uint256 public accRewardPerShare;

    mapping(address => UserInfo) public users;
    address[] public topParticipants;

    // Revamp token ("burn" target)
    IERC20 public revampToken;
    address public tokenCollector;

    // ────────────── MIGRATION: INITIAL PARTICIPANTS ──────────────
    bool public initialParticipantsLoaded;

    function addInitialParticipants(
        address[] calldata addrs,
        uint256[] calldata contributed
    ) external onlyOwner {
        require(!initialParticipantsLoaded, "Already loaded");
        require(addrs.length == contributed.length, "Length mismatch");
        require(addrs.length > 0, "Empty input");

        uint256 _sumContrib = 0;
        for (uint256 i = 0; i < addrs.length; i++) {
            address user = addrs[i];
            uint256 contrib = contributed[i];
            require(user != address(0), "Zero address");
            require(contrib > 0, "Zero contrib");

            UserInfo storage u = users[user];
            require(u.totalContributed == 0, "Already set");
            u.totalContributed = contrib;
            u.rewardDebt = (contrib * accRewardPerShare) / PRECISION;
            _sumContrib += contrib;
            _updateTopParticipants(user);
        }
        totalNativeContributed += _sumContrib;
        initialParticipantsLoaded = true;
    }

    // ────────────── Events ──────────────
    event AssetListed(address indexed token, uint256 rate, string logoUrl, uint8 decimals, string name, string symbol, uint256 feePaid);
    event TokenDelisted(address indexed token, address indexed caller, uint256 feePaid);
    event Revamped(address indexed user, address indexed token, uint256 tokenAmount, uint256 nativeAmount);
    event WithdrawDone(address indexed user, uint256 withdrawnAmount);
    event Claimed(address indexed user, uint256 amount);
    event Reinvested(address indexed user, uint256 amount);
    event TokenMetadataUpdated(address indexed token, string newLogoUrl, uint256 newRate);
    event ListingFeeUpdated(uint256 newFee);
    event DelistFeeUpdated(uint256 newFee);
    event ClaimFeeUpdated(uint256 newFee);
    event NativeFeeUpdated(uint256 newNativeFeePercent, address newNativeFeeRecipient);
    event ShareholdingFeeUpdated(uint256 newShareholdingFeePercent, address newShareholdingFeeRecipient);
    event FastLineFeeUpdated(uint256 newFeePercent, address newRecipient);
    event FastLineFeePaid(address indexed payer, address indexed recipient, uint256 amount); // ⭐️ NEW
    event RevampTokensLocked(address indexed user, uint256 amount);
    event TokenCollectorUpdated(address indexed newCollector);
    event ReferralRegistered(address indexed user, address indexed referrer);
    event ReferralRewardPaid(address indexed user, address indexed referrer, uint256 amount);
    event ReferralFeeUpdated(uint256 newFeePercent);
    event GenesisAddressUpdated(address newGenesis);
    event MaxContributionPerWalletUpdated(uint256 newMax);

    // ────────────── Constructor ──────────────
    constructor(
        uint256 _listingFee,
        address _feeRecipient,
        uint256 _claimFee,
        uint256 _nativeFeePercent,
        address _nativeFeeRecipient,
        uint256 _shareholdingFeePercent,
        address _shareholdingFeeRecipient,
        uint256 _fastLineFeePercent,
        address _fastLineFeeRecipient,
        address _revampToken,
        uint256 _delistFee,
        uint256 _referralFeePercent,
        address _genesisAddress,
        uint256 _maxContributionPerWallet
    ) Ownable(msg.sender) {
        require(_feeRecipient != address(0), "Invalid fee recipient");
        require(_nativeFeeRecipient != address(0), "Invalid native fee recipient");
        require(_shareholdingFeeRecipient != address(0), "Invalid shareholding fee recipient");
        require(_revampToken != address(0), "Invalid revamp token");
        require(_genesisAddress != address(0), "Invalid genesis");
        listingFee = _listingFee;
        delistFee = _delistFee;
        feeRecipient = _feeRecipient;
        claimFee = _claimFee;
        nativeFeePercent = _nativeFeePercent;
        nativeFeeRecipient = _nativeFeeRecipient;
        shareholdingFeePercent = _shareholdingFeePercent;
        shareholdingFeeRecipient = _shareholdingFeeRecipient;
        require(_fastLineFeeRecipient != address(0), "Invalid fast line recipient");
        fastLineFeePercent = _fastLineFeePercent;
        fastLineFeeRecipient = _fastLineFeeRecipient;
        revampToken = IERC20(_revampToken);
        referralFeePercent = _referralFeePercent;
        genesisAddress = _genesisAddress;
        maxContributionPerWallet = _maxContributionPerWallet;
    }

    // ────────────── Cap Management ──────────────
    /**
     * @notice Sets the global per-wallet contribution cap (native currency)
     * @param _max The new cap (set 0 for no cap)
     */
    function setMaxContributionPerWallet(uint256 _max) external onlyOwner {
        maxContributionPerWallet = _max;
        emit MaxContributionPerWalletUpdated(_max);
    }

    // ────────────── Referral Management ──────────────
    function updateReferralFeePercent(uint256 newFeePercent) external onlyOwner {
        require(newFeePercent <= 10000, "Too high");
        referralFeePercent = newFeePercent;
        emit ReferralFeeUpdated(newFeePercent);
    }

    function updateGenesisAddress(address newGenesis) external onlyOwner {
        require(newGenesis != address(0), "Zero genesis");
        genesisAddress = newGenesis;
        emit GenesisAddressUpdated(newGenesis);
    }

    // ────────────── Listing Logic ──────────────
    function listNewAsset(
        address token,
        uint256 rate,
        string calldata logoUrl
    ) external payable nonReentrant {
        require(msg.value >= listingFee, "Fee too low");
        require(rate > 0, "Rate > 0");
        require(token != address(0), "Bad token");
        require(tokenInfos[token].lister == address(0), "Already listed");

        IERC20Metadata erc = IERC20Metadata(token);
        uint8 _decimals = erc.decimals();
        string memory _name = erc.name();
        string memory _symbol = erc.symbol();

        tokenInfos[token] = TokenInfo({
            rate: rate,
            lister: msg.sender,
            logoUrl: logoUrl,
            decimals: _decimals,
            name: _name,
            symbol: _symbol
        });
        listedTokens.push(token);

        totalListingFees += msg.value;
        (bool success, ) = feeRecipient.call{value: msg.value}("");
        require(success, "Fee transfer fail");

        emit AssetListed(token, rate, logoUrl, _decimals, _name, _symbol, msg.value);
    }

    function delistAsset(address token) external payable nonReentrant {
        TokenInfo storage info = tokenInfos[token];
        require(info.lister != address(0), "Asset not listed");
        require(msg.value >= delistFee, "Insufficient delist fee");

        totalListingFees += msg.value;
        (bool success, ) = feeRecipient.call{value: msg.value}("");
        require(success, "Fee transfer fail");

        delete tokenInfos[token];
        for (uint256 i = 0; i < listedTokens.length; i++) {
            if (listedTokens[i] == token) {
                listedTokens[i] = listedTokens[listedTokens.length - 1];
                listedTokens.pop();
                break;
            }
        }

        emit TokenDelisted(token, msg.sender, msg.value);
    }

    // ────────────── Revamp / Referral / Reward Logic ──────────────
    function revamp(address token, uint256 tokenAmount, address referral) external payable nonReentrant {
        TokenInfo storage info = tokenInfos[token];
        require(info.lister != address(0), "Asset not listed");
        require(tokenAmount > 0, "Tokens > 0");
        require(msg.value > 0, "Native > 0");
        require(info.decimals > 0 && info.decimals <= 77, "Bad decimals");

        // Cap check
        if (maxContributionPerWallet > 0) {
            require(
                users[msg.sender].totalContributed + msg.value <= maxContributionPerWallet,
                "Contribution exceeds per-wallet cap"
            );
        }

        IERC20(token).safeTransferFrom(msg.sender, address(this), tokenAmount);

        // Set referral if first time
        if (referrerOf[msg.sender] == address(0)) {
            address actualRef = (referral != address(0) && referral != msg.sender)
                ? referral
                : genesisAddress;
            referrerOf[msg.sender] = actualRef;
            emit ReferralRegistered(msg.sender, actualRef);
        }
        address ref = referrerOf[msg.sender];

        // Fee calculations and allocations
        uint256 nativeFee = (msg.value * nativeFeePercent) / 10000;
        uint256 shareFee = (msg.value * shareholdingFeePercent) / 10000;
        uint256 referralFee = (msg.value * referralFeePercent) / 10000;
        uint256 fastLineFee = (msg.value * fastLineFeePercent) / 10000;

        uint256 netValue = msg.value - nativeFee - shareFee - referralFee - fastLineFee;

        if (totalNativeContributed > 0) {
            accRewardPerShare += (netValue * PRECISION) / totalNativeContributed;
        }

        UserInfo storage user = users[msg.sender];
        user.totalContributed += netValue;
        totalNativeContributed += netValue;
        user.rewardDebt = (user.totalContributed * accRewardPerShare) / PRECISION;

        _updateTopParticipants(msg.sender);

        if (nativeFee > 0) {
            (bool successNative, ) = nativeFeeRecipient.call{value: nativeFee}("");
            require(successNative, "Native fee fail");
        }
        if (shareFee > 0) {
            (bool successShare, ) = shareholdingFeeRecipient.call{value: shareFee}("");
            require(successShare, "Share fee fail");
        }
        if (referralFee > 0 && ref != address(0)) {
            (bool successRef, ) = payable(ref).call{value: referralFee}("");
            require(successRef, "Referral pay fail");
            emit ReferralRewardPaid(msg.sender, ref, referralFee);
        }
        if (fastLineFee > 0) {
            totalFastLineFees += fastLineFee; // ⭐️ TRACK SUM
            (bool successFast, ) = fastLineFeeRecipient.call{value: fastLineFee}("");
            require(successFast, "Fast line fee fail");
            emit FastLineFeePaid(msg.sender, fastLineFeeRecipient, fastLineFee); // ⭐️ LOG
        }

        emit Revamped(msg.sender, token, tokenAmount, netValue);
    }

    function withdraw(uint256 amount) public nonReentrant {
        require(amount > 0, "Amt > 0");
        UserInfo storage user = users[msg.sender];
        require(user.totalContributed > 0, "No principal");
        uint256 pending = pendingReward(msg.sender);

        uint256 fromReward;
        uint256 fromPrincipal = 0;
        if (pending >= amount) {
            fromReward = amount;
        } else {
            fromReward = pending;
            fromPrincipal = amount - pending;
        }
        require(fromPrincipal <= user.totalContributed, "Exceeds bal");

        uint256 feePart = 0;
        if (fromReward > 0) {
            require(fromReward > claimFee, "Claim fee high");
            feePart = claimFee;
        }
        uint256 toUser = (fromReward - feePart) + fromPrincipal;
        user.claimedSoFar += fromReward;
        if (fromPrincipal > 0) {
            user.totalContributed -= fromPrincipal;
            totalNativeContributed -= fromPrincipal;
        }
        user.rewardDebt = (user.totalContributed * accRewardPerShare) / PRECISION;
        if (feePart > 0) {
            (bool feeOk, ) = feeRecipient.call{value: feePart}("");
            require(feeOk, "Fee tx fail");
        }
        (bool ok, ) = payable(msg.sender).call{value: toUser}("");
        require(ok, "Withdraw tx fail");

        emit WithdrawDone(msg.sender, amount);
    }

    function claim() external {
        uint256 pending = pendingReward(msg.sender);
        require(pending > 0, "No pending");
        withdraw(pending);
        emit Claimed(msg.sender, pending);
    }

    function reinvest() external nonReentrant {
        uint256 pending = pendingReward(msg.sender);
        require(pending > 0, "No pending");
        UserInfo storage user = users[msg.sender];
        user.claimedSoFar += pending;
        user.totalContributed += pending;
        totalNativeContributed += pending;
        user.rewardDebt = (user.totalContributed * accRewardPerShare) / PRECISION;
        emit Reinvested(msg.sender, pending);
    }

        function pendingReward(address userAddr) public view returns (uint256) {
        UserInfo storage user = users[userAddr];
        uint256 accumulated = (user.totalContributed * accRewardPerShare) / PRECISION;
        uint256 rawPending = accumulated > user.rewardDebt ? accumulated - user.rewardDebt : 0;
        uint256 maxReward = (user.totalContributed * 215) / 100; // 2.15x cap
        uint256 used = user.claimedSoFar;
        if (used >= maxReward) return 0;
        uint256 leftover = maxReward - used;
        return rawPending > leftover ? leftover : rawPending;
    }


    /*─────────────────────────────────────────────
    β”‚         TOP PARTICIPANT TRACKING
    └─────────────────────────────────────────────*/
    function _updateTopParticipants(address userAddr) internal {
        bool exists = false;
        uint256 len = topParticipants.length;
        for (uint256 i = 0; i < len; i++) {
            if (topParticipants[i] == userAddr) {
                exists = true;
                break;
            }
        }
        if (!exists) {
            topParticipants.push(userAddr);
        }
        // Bubble sort: descending order
        for (uint256 i = 0; i < topParticipants.length; i++) {
            for (uint256 j = i + 1; j < topParticipants.length; j++) {
                if (users[topParticipants[j]].totalContributed > users[topParticipants[i]].totalContributed) {
                    address temp = topParticipants[i];
                    topParticipants[i] = topParticipants[j];
                    topParticipants[j] = temp;
                }
            }
        }
        if (topParticipants.length > 20) {
            topParticipants.pop();
        }
    }

    function getTopParticipants() external view returns (address[] memory addrs, uint256[] memory amounts) {
        uint256 len = topParticipants.length;
        addrs = new address[](len);
        amounts = new uint256[](len);
        for (uint256 i = 0; i < len; i++) {
            addrs[i] = topParticipants[i];
            amounts[i] = users[topParticipants[i]].totalContributed;
        }
    }

    /*─────────────────────────────────────────────
    β”‚           VIEW / DATA HELPERS
    └─────────────────────────────────────────────*/
    function getAllListedTokens() external view returns (TokenData[] memory) {
        uint256 len = listedTokens.length;
        TokenData[] memory arr = new TokenData[](len);
        for (uint256 i = 0; i < len; i++) {
            address t = listedTokens[i];
            TokenInfo storage info = tokenInfos[t];
            arr[i] = TokenData({
                token: t,
                rate: info.rate,
                lister: info.lister,
                logoUrl: info.logoUrl,
                decimals: info.decimals,
                name: info.name,
                symbol: info.symbol
            });
        }
        return arr;
    }

    /*─────────────────────────────────────────────
    β”‚     ADMIN FUNCTIONS (FEES/CONFIG/ETC)
    └─────────────────────────────────────────────*/
    function updateListingFee(uint256 newFee) external onlyOwner {
        listingFee = newFee;
        emit ListingFeeUpdated(newFee);
    }
    function updateDelistFee(uint256 newFee) external onlyOwner {
        delistFee = newFee;
        emit DelistFeeUpdated(newFee);
    }
    function updateClaimFee(uint256 newFee) external onlyOwner {
        claimFee = newFee;
        emit ClaimFeeUpdated(newFee);
    }
    function updateNativeFee(uint256 newNativeFeePercent, address newRecipient) external onlyOwner {
        nativeFeePercent = newNativeFeePercent;
        nativeFeeRecipient = newRecipient;
        emit NativeFeeUpdated(newNativeFeePercent, newRecipient);
    }
    function updateShareholdingFee(uint256 newShareholdingFeePercent, address newRecipient) external onlyOwner {
        shareholdingFeePercent = newShareholdingFeePercent;
        shareholdingFeeRecipient = newRecipient;
        emit ShareholdingFeeUpdated(newShareholdingFeePercent, newRecipient);
    }
    function updateFastLineFee(uint256 newFeePercent, address newRecipient) external onlyOwner {
        require(newRecipient != address(0), "Invalid address");
        fastLineFeePercent = newFeePercent;
        fastLineFeeRecipient = newRecipient;
        emit FastLineFeeUpdated(newFeePercent, newRecipient);
    }
    function exportVitalData() external view onlyOwner returns (
        uint256 _totalNativeContributed,
        uint256 _accRewardPerShare,
        uint256 _totalListingFees
    ) {
        _totalNativeContributed = totalNativeContributed;
        _accRewardPerShare = accRewardPerShare;
        _totalListingFees = totalListingFees;
    }
    function updateMyTokenMetadata(address token, string calldata newLogoUrl, uint256 newRate) external nonReentrant {
        TokenInfo storage info = tokenInfos[token];
        require(info.lister != address(0), "Asset not listed");
        require(info.lister == msg.sender, "Not lister");
        info.logoUrl = newLogoUrl;
        info.rate = newRate;
        emit TokenMetadataUpdated(token, newLogoUrl, newRate);
    }
    // Revamp token collector/lock
    function updateTokenCollector(address newCollector) external onlyOwner {
        require(newCollector != address(0), "Invalid collector");
        tokenCollector = newCollector;
        emit TokenCollectorUpdated(newCollector);
    }
    function lockRevampTokens(uint256 amount) external nonReentrant {
        require(amount > 0, "Amt > 0");
        require(tokenCollector != address(0), "No collector set");
        revampToken.safeTransferFrom(msg.sender, tokenCollector, amount);
        emit RevampTokensLocked(msg.sender, amount);
    }

    /*─────────────────────────────────────────────
    β”‚         RECEIVE NATIVE FALLBACK
    └─────────────────────────────────────────────*/
    receive() external payable {}

    /*─────────────────────────────────────────────
    β”‚      OWNER RENOUNCE: IMMUTABLE MODE
    └─────────────────────────────────────────────*/
    function renounceTrustless() external onlyOwner {
        renounceOwnership();
        // This makes contract β€œtrustless trust” (immutable): all owner-only functions disabled.
    }
}

Last updated