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 =====

// 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 {}
}

Last updated