# Contract - Code

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

\
Arbitrum One (42161):\
<https://arbiscan.io/address/0xE4F637Cd6EaFBd33681ca6aB9694dB331e53901A#code>\
\
\
\
\====== **RevampGBLV3 Contract** =====\ <br>

```remix-solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";

contract RevampGBLV3 is ReentrancyGuard, Ownable(msg.sender) {
    using SafeERC20 for IERC20;

    // ======== Config ========
    IERC20        public revampToken;
    ISwapRouter   public swapRouterV3;
    address       public WETH;
    uint24        public poolFee;

    uint256 public dexPortionBps;
    uint256 public shareholdingFeeBps;
    uint256 public opsFeeBps;
    uint256 public claimFee;

    address public shareholdingAddress;
    address public opsFeeAddress;
    address public recyclePool;

    uint256 public minReserveToDistribute = 0.1 ether; // default, adjustable
    address[] public userList; // list of all users ever joined

    // ======== State ========
    struct User {
        uint256 contributed;
        uint256 rewardDebt;
        uint256 claimed;
        uint256 fastlineClaimable;
        uint256 fastlineClaimed;
        bool    exists;   // for userList uniqueness
    }
    mapping(address => User) public users;

    uint256 public totalContributed;
    uint256 public accRewardPerShare;
    uint256 private constant PRECISION = 1e18;

    // === Initial Participants Loader ===
    bool public initialParticipantsLoaded = false;
    event InitialParticipantsAdded(uint256 count, uint256 totalContributed);

    // ======== Events ========
    event Deposit(address indexed user, uint256 netAmount, uint256 dexAmount);
    event RewardClaimed(address indexed user, uint256 amount, uint256 fastline, uint256 net);
    event Reinvested(address indexed user, uint256 amount);
    event FastlineFeeDistributed(uint256 totalAmount);
    event BoostDistributed(uint256 amount);
    event MinReserveChanged(uint256 newMin);
    event ExternalRewardDistributed(uint256 amount, uint256 accRewardPerShare);

    // ======== Constructor ========
    constructor(
        address _revampToken,
        address _swapRouterV3,
        address _weth,
        uint24  _poolFee,
        address _shareholding,
        address _ops,
        address _recyclePool,
        uint256 _dexPortionBps,
        uint256 _shareholdingFeeBps,
        uint256 _opsFeeBps,
        uint256 _claimFee
    ) {
        revampToken          = IERC20(_revampToken);
        swapRouterV3         = ISwapRouter(_swapRouterV3);
        WETH                 = _weth;
        poolFee              = _poolFee;
        shareholdingAddress  = _shareholding;
        opsFeeAddress        = _ops;
        recyclePool          = _recyclePool;
        dexPortionBps        = _dexPortionBps;
        shareholdingFeeBps   = _shareholdingFeeBps;
        opsFeeBps            = _opsFeeBps;
        claimFee             = _claimFee;
    }

    // ======== Initial Participants Loader ========
    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");

            User storage u = users[user];
            require(u.contributed == 0, "Already set");
            u.contributed = contrib;
            u.rewardDebt = (contrib * accRewardPerShare) / PRECISION;
            if (!u.exists) {
                u.exists = true;
                userList.push(user);
            }
            _sumContrib += contrib;
        }
        totalContributed += _sumContrib;
        initialParticipantsLoaded = true;
        emit InitialParticipantsAdded(addrs.length, _sumContrib);
    }

    // ======== Views ========
    function pending(address user) public view returns (uint256) {
        User storage u = users[user];
        uint256 accumulated = (u.contributed * accRewardPerShare) / PRECISION;
        uint256 earned = accumulated > u.rewardDebt ? accumulated - u.rewardDebt : 0;
        uint256 cap = u.contributed * 2;
        if (u.claimed >= cap) return u.fastlineClaimable;
        uint256 left = cap - u.claimed;
        uint256 classicReward = earned > left ? left : earned;
        return classicReward + u.fastlineClaimable;
    }

    function fastlineClaimable(address user) external view returns (uint256) {
        return users[user].fastlineClaimable;
    }

    // ======== View: Boosting Reserve Calculation ========
    function getBoostingReserve() public view returns (uint256) {
        uint256 totalPending = 0;
        for (uint256 i = 0; i < userList.length; i++) {
            totalPending += pending(userList[i]);
        }
        uint256 bal = address(this).balance;
        if (bal > totalPending) return bal - totalPending;
        return 0;
    }

    // ======== Distribute Boosting Reserve ========
    function distributeBoostingReserve() external nonReentrant {
        uint256 boostingReserve = getBoostingReserve();
        require(boostingReserve >= minReserveToDistribute, "Reserve too low");
        require(totalContributed > 0, "No contributors");

        uint256 distributed = 0;
        for (uint256 i = 0; i < userList.length; i++) {
            address u = userList[i];
            // Only reward users who haven't capped out
            uint256 cap = users[u].contributed * 2;
            if (users[u].claimed >= cap) continue;
            uint256 share = (boostingReserve * users[u].contributed) / totalContributed;
            if (share > 0) {
                users[u].fastlineClaimable += share;
                distributed += share;
            }
        }
        emit BoostDistributed(distributed);
    }

    // ======== Fastline Fee Distribution (keep for DAO/ops if needed) ========
    function distributeFastlineFee(address[] calldata eligibleUsers, uint256[] calldata stakes) external payable onlyOwner {
        require(eligibleUsers.length == stakes.length && eligibleUsers.length > 0, "Length mismatch or empty");
        require(msg.value > 0, "No fee");

        uint256 totalStakes = 0;
        for (uint256 i = 0; i < stakes.length; i++) {
            totalStakes += stakes[i];
        }
        require(totalStakes > 0, "No eligible stakes");

        uint256 totalDistributed = 0;
        for (uint256 i = 0; i < eligibleUsers.length; i++) {
            address u = eligibleUsers[i];
            uint256 share = (msg.value * stakes[i]) / totalStakes;
            users[u].fastlineClaimable += share;
            totalDistributed += share;
        }
        emit FastlineFeeDistributed(totalDistributed);
    }

    // ======== Core: Contribute ========
    function contribute(uint256 minTokenOut, uint256 deadline)
        external
        payable
        nonReentrant
    {
        require(msg.value > 0, "Zero contribution");

        uint256 dexAmt       = (msg.value * dexPortionBps) / 10000;
        uint256 shareFee     = (msg.value * shareholdingFeeBps) / 10000;
        uint256 opsFee       = (msg.value * opsFeeBps) / 10000;
        uint256 netValue     = msg.value - dexAmt - shareFee - opsFee;

        if (dexAmt > 0) {
            _swapETHForRevampToken(dexAmt, minTokenOut, deadline);
        }
        if (shareFee > 0) {
            (bool ok,) = shareholdingAddress.call{value: shareFee}("");
            require(ok, "Shareholding fee failed");
        }
        if (opsFee > 0) {
            (bool ok,) = opsFeeAddress.call{value: opsFee}("");
            require(ok, "Ops fee failed");
        }

        // Standard proportional rewards for each join
        if (totalContributed > 0) {
            accRewardPerShare += (netValue * PRECISION) / totalContributed;
        }

        User storage u = users[msg.sender];
        u.contributed    += netValue;
        totalContributed += netValue;
        u.rewardDebt      = (u.contributed * accRewardPerShare) / PRECISION;

        if (!u.exists) {
            u.exists = true;
            userList.push(msg.sender);
        }

        emit Deposit(msg.sender, netValue, dexAmt);
    }

    // ======== Internal Swap Helper (Uniswap V3) ========
    function _swapETHForRevampToken(
        uint256 ethAmount,
        uint256 amountOutMin,
        uint256 deadline
    ) internal {
        require(deadline >= block.timestamp, "Deadline passed");

        ISwapRouter.ExactInputSingleParams memory params = ISwapRouter
            .ExactInputSingleParams({
                tokenIn: WETH,
                tokenOut: address(revampToken),
                fee: poolFee,
                recipient: recyclePool,
                deadline: deadline,
                amountIn: ethAmount,
                amountOutMinimum: amountOutMin,
                sqrtPriceLimitX96: 0
            });

        swapRouterV3.exactInputSingle{ value: ethAmount }(params);
    }

    // ======== Claim & Reinvest ========
    function claim() external nonReentrant {
        User storage u = users[msg.sender];
        uint256 amtClassic;
        {
            uint256 accumulated = (u.contributed * accRewardPerShare) / PRECISION;
            uint256 earned = accumulated > u.rewardDebt ? accumulated - u.rewardDebt : 0;
            uint256 cap = u.contributed * 2;
            if (u.claimed < cap) {
                uint256 left = cap - u.claimed;
                amtClassic = earned > left ? left : earned;
            }
        }
        uint256 amtFastline = u.fastlineClaimable;
        uint256 totalToClaim = amtClassic + amtFastline;
        require(totalToClaim > claimFee, "Nothing to claim");

        u.claimed += amtClassic;
        u.fastlineClaimable = 0;
        u.fastlineClaimed += amtFastline;
        u.rewardDebt = (u.contributed * accRewardPerShare) / PRECISION;

        (bool okFee,) = opsFeeAddress.call{value: claimFee}("");
        require(okFee, "Claim fee failed");

        (bool okSend,) = payable(msg.sender).call{value: totalToClaim - claimFee}("");
        require(okSend, "Reward transfer failed");

        emit RewardClaimed(msg.sender, amtClassic, amtFastline, totalToClaim - claimFee);
    }

    function reinvest() external nonReentrant {
        User storage u = users[msg.sender];
        uint256 amtClassic;
        {
            uint256 accumulated = (u.contributed * accRewardPerShare) / PRECISION;
            uint256 earned = accumulated > u.rewardDebt ? accumulated - u.rewardDebt : 0;
            uint256 cap = u.contributed * 2;
            if (u.claimed < cap) {
                uint256 left = cap - u.claimed;
                amtClassic = earned > left ? left : earned;
            }
        }
        uint256 amtFastline = u.fastlineClaimable;
        uint256 totalToReinvest = amtClassic + amtFastline;
        require(totalToReinvest > 0, "Nothing to reinvest");

        u.claimed += amtClassic;
        u.fastlineClaimable = 0;
        u.fastlineClaimed += amtFastline;
        u.contributed += totalToReinvest;
        totalContributed += totalToReinvest;
        u.rewardDebt = (u.contributed * accRewardPerShare) / PRECISION;

        emit Reinvested(msg.sender, totalToReinvest);
    }

    // ======== Admin Setters ========
    function setDexPortion(uint256 bps)           external onlyOwner { dexPortionBps        = bps; }
    function setShareholdingFee(uint256 bps)     external onlyOwner { shareholdingFeeBps   = bps; }
    function setOpsFee(uint256 bps)              external onlyOwner { opsFeeBps            = bps; }
    function setClaimFee(uint256 fee_)           external onlyOwner { claimFee             = fee_; }
    function setRouterAndPool(address r, uint24 f) external onlyOwner {
        swapRouterV3 = ISwapRouter(r);
        poolFee      = f;
    }
    function setRevampToken(address tok)         external onlyOwner { revampToken = IERC20(tok); }
    function setRecyclePool(address p)           external onlyOwner { recyclePool = p; }
    function setShareholdingAddress(address a)   external onlyOwner { shareholdingAddress = a; }
    function setOpsFeeAddress(address a)         external onlyOwner { opsFeeAddress = a; }
    function setMinReserveToDistribute(uint256 minAmt) external onlyOwner {
        minReserveToDistribute = minAmt;
        emit MinReserveChanged(minAmt);
    }

    // ======== Ownerless (Trustless) Mode ========
    function renounceTrustless() external onlyOwner {
        renounceOwnership();
        // No funds can be withdrawn by anyone, including the owner.
    }

    receive() external payable {}
}

```


---

# 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-1.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.
