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