# 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 =====\ <br>

```remix-solidity
// 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.
    }
}

```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.waveswaps.com/smart-contracts/contract-code.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
