Skip to main content
PulseScanner.io

Address

0xd8fbf204c729a693b5441fc0287a00ea9fb474aa
Current Holdings
$0.00
TXs sent
not counted
First Active
2025-11-07
block 24,960,782
Last Active
315 days ago
block 24,960,782
Funded By
not identified

Net worth historyi

No net-worth snapshots recorded yet
exact matchPulseRaceRewardsolc 0.8.29+commit.ab55807cruntime exact · creation exact
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.29;

/**
 * @title PulseRaceReward - Ready for Remix Deployment
 * @notice First-come-first-served competition system for PulseChain
 * @dev Users send PLS (min 10K), contract swaps to PX402, then gives 1.5x of swapped amount (50% profit)
 * 
 * DEPLOYMENT INSTRUCTIONS:
 * 1. Open Remix IDE (remix.ethereum.org)
 * 2. Create new file: PulseRaceReward.sol
 * 3. Paste this entire contract code
 * 4. Compile with Solidity 0.8.29
 * 5. Deploy to PulseChain mainnet (Chain ID: 369)
 * 6. After deployment, owner must:
 *    - Approve PX402 tokens to contract
 *    - Call depositPX402() to fund reward pool
 * 
 * OWNER FUNCTIONS:
 * - depositPLS() - Add PLS to pool (payable)
 * - depositPX402(amount) - Add PX402 to reward pool (must approve first)
 * - withdraw() - Withdraw all PLS and PX402 from contract
 * - emergencyWithdrawToken(tokenAddress) - Emergency withdraw any token
 * 
 * CONTRACT ADDRESSES (PulseChain Mainnet):
 * - PX402: 0x675aC865AEBcfc1D22F819bA0Fe7a60Bf17Cb60d
 * - WPLS: 0xA1077a294dDE1B09bB078844df40758a5D0f9a27
 * - PulseX Router: 0x98bf93ebf5c380C0e6Ae8e192A7e2AE08edAcc02
 * - PulseX Factory: 0x1715a3E4A142d8b698131108995174F37aEBA10D
 */

// OpenZeppelin Contracts - Ownable
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }
}

abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    function owner() public view virtual returns (address) {
        return _owner;
    }

    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }

    error OwnableUnauthorizedAccount(address account);
    error OwnableInvalidOwner(address owner);
}

// OpenZeppelin Contracts - ReentrancyGuard
abstract contract ReentrancyGuard {
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;
    uint256 private _status;

    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        _status = NOT_ENTERED;
    }

    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}

// Minimal ERC20 Interface
interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 value) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 value) external returns (bool);
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

// PulseX V2 Router Interface
interface IPulseXRouter {
    function swapExactETHForTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable returns (uint[] memory amounts);
}

// PulseX V2 Factory Interface
interface IPulseXFactory {
    function getPair(address tokenA, address tokenB) external view returns (address pair);
}

// PulseX V2 Pair Interface
interface IPulseXPair {
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    function token0() external view returns (address);
    function token1() external view returns (address);
}

/**
 * @title PulseRaceReward
 * @notice Competition contract that swaps PLS to PX402 and rewards 1.5x
 */
contract PulseRaceReward is Ownable, ReentrancyGuard {
    // Constants
    uint256 public constant MIN_PLS_AMOUNT = 10_000 ether; // Minimum 10,000 PLS
    uint256 public constant REWARD_MULTIPLIER = 150; // 1.5x = 150%
    uint256 public constant MULTIPLIER_DENOMINATOR = 100;
    uint256 public constant SLIPPAGE_TOLERANCE = 50; // 50% slippage tolerance
    uint256 public constant SLIPPAGE_DENOMINATOR = 100;
    
    // PulseChain Mainnet Addresses
    address public constant WPLS = 0xA1077a294dDE1B09bB078844df40758a5D0f9a27;
    address public constant PULSEX_ROUTER_ADDRESS = 0x98bf93ebf5c380C0e6Ae8e192A7e2AE08edAcc02;
    address public constant PULSEX_FACTORY_ADDRESS = 0x1715a3E4A142d8b698131108995174F37aEBA10D;
    
    // Contracts
    IERC20 public immutable PX402;
    IPulseXRouter public immutable PULSEX_ROUTER;
    IPulseXFactory public immutable PULSEX_FACTORY;
    
    // Events
    event RewardDistributed(address indexed user, uint256 plsAmount, uint256 px402Reward);
    event Deposited(address indexed owner, uint256 amount);
    event Withdrawn(address indexed owner, uint256 plsAmount, uint256 px402Amount);
    event EmergencyWithdraw(address indexed owner, address indexed token, uint256 amount);
    
    /**
     * @notice Constructor
     * @param _px402Address PX402 token address
     */
    constructor(address _px402Address) Ownable(msg.sender) {
        require(_px402Address != address(0), "Invalid PX402 address");
        PX402 = IERC20(_px402Address);
        PULSEX_ROUTER = IPulseXRouter(PULSEX_ROUTER_ADDRESS);
        PULSEX_FACTORY = IPulseXFactory(PULSEX_FACTORY_ADDRESS);
    }
    
    /**
     * @notice Receive function - accepts PLS and distributes PX402 rewards
     * @dev Swaps PLS to PX402, then sends 1.5x of swapped amount to user
     */
    receive() external payable nonReentrant {
        require(msg.value >= MIN_PLS_AMOUNT, "Below minimum PLS amount");
        
        // Swap PLS to PX402
        uint256 px402FromSwap = _swapPLSToPX402(msg.value);
        require(px402FromSwap > 0, "Swap failed");
        
        // Calculate reward: 1.5x of swapped amount
        uint256 rewardAmount = (px402FromSwap * REWARD_MULTIPLIER) / MULTIPLIER_DENOMINATOR;
        
        // Check if contract has enough PX402
        uint256 contractBalance = PX402.balanceOf(address(this));
        require(contractBalance >= rewardAmount, "Insufficient PX402 in pool");
        
        // Transfer reward to user
        require(PX402.transfer(msg.sender, rewardAmount), "Reward transfer failed");
        
        emit RewardDistributed(msg.sender, msg.value, rewardAmount);
    }
    
    /**
     * @notice Owner deposits PLS to fill the reward pool
     */
    function depositPLS() external payable onlyOwner {
        require(msg.value > 0, "Cannot deposit 0");
        emit Deposited(msg.sender, msg.value);
    }
    
    /**
     * @notice Owner deposits PX402 tokens to fill the reward pool
     * @param amount Amount of PX402 to deposit
     */
    function depositPX402(uint256 amount) external onlyOwner {
        require(amount > 0, "Cannot deposit 0");
        require(
            PX402.transferFrom(msg.sender, address(this), amount),
            "Transfer failed"
        );
        emit Deposited(msg.sender, amount);
    }
    
    /**
     * @notice Owner withdraws remaining PLS and PX402
     */
    function withdraw() external onlyOwner nonReentrant {
        uint256 plsBalance = address(this).balance;
        uint256 px402Balance = PX402.balanceOf(address(this));
        
        if (plsBalance > 0) {
            (bool success, ) = payable(owner()).call{value: plsBalance}("");
            require(success, "PLS transfer failed");
        }
        
        if (px402Balance > 0) {
            require(PX402.transfer(owner(), px402Balance), "PX402 transfer failed");
        }
        
        emit Withdrawn(owner(), plsBalance, px402Balance);
    }
    
    /**
     * @notice Emergency withdraw any ERC20 token
     * @param token Token address to withdraw
     */
    function emergencyWithdrawToken(address token) external onlyOwner {
        require(token != address(0), "Invalid token");
        IERC20 tokenContract = IERC20(token);
        uint256 balance = tokenContract.balanceOf(address(this));
        require(balance > 0, "No balance");
        require(tokenContract.transfer(owner(), balance), "Transfer failed");
        emit EmergencyWithdraw(owner(), token, balance);
    }
    
    /**
     * @notice Get remaining PLS pool balance
     */
    function getRemainingPLSPool() external view returns (uint256) {
        return address(this).balance;
    }
    
    /**
     * @notice Get remaining PX402 pool balance
     */
    function getRemainingPX402Pool() external view returns (uint256) {
        return PX402.balanceOf(address(this));
    }
    
    /**
     * @notice Debug function to check pair and reserves
     */
    function debugPairInfo() external view returns (
        address pairAddress,
        uint256 reserve0,
        uint256 reserve1,
        address token0,
        address token1
    ) {
        pairAddress = PULSEX_FACTORY.getPair(address(PX402), WPLS);
        if (pairAddress == address(0)) return (address(0), 0, 0, address(0), address(0));
        
        IPulseXPair pair = IPulseXPair(pairAddress);
        (uint112 r0, uint112 r1,) = pair.getReserves();
        reserve0 = uint256(r0);
        reserve1 = uint256(r1);
        token0 = pair.token0();
        token1 = pair.token1();
    }
    
    /**
     * @notice Estimate how many more participants can get rewards
     * @param plsAmount PLS amount per participant
     * @return Number of participants that can still get rewards
     */
    function estimateRemainingParticipants(uint256 plsAmount) 
        external 
        view 
        returns (uint256) 
    {
        if (plsAmount < MIN_PLS_AMOUNT) return 0;
        
        uint256 px402FromSwap = _estimateSwapOutput(plsAmount);
        if (px402FromSwap == 0) return 0;
        
        uint256 rewardPerParticipant = (px402FromSwap * REWARD_MULTIPLIER) / MULTIPLIER_DENOMINATOR;
        if (rewardPerParticipant == 0) return 0;
        
        uint256 px402Balance = PX402.balanceOf(address(this));
        return px402Balance / rewardPerParticipant;
    }
    
    /**
     * @notice Estimate PX402 reward for a given PLS amount
     * @param plsAmount Amount of PLS to send
     * @return Expected PX402 reward amount
     */
    function estimateReward(uint256 plsAmount) external view returns (uint256) {
        if (plsAmount < MIN_PLS_AMOUNT) return 0;
        
        uint256 px402FromSwap = _estimateSwapOutput(plsAmount);
        if (px402FromSwap == 0) return 0;
        
        return (px402FromSwap * REWARD_MULTIPLIER) / MULTIPLIER_DENOMINATOR;
    }
    
    /**
     * @notice Internal function to swap PLS to PX402
     * @param plsAmount Amount of PLS to swap
     * @return Amount of PX402 received
     */
    function _swapPLSToPX402(uint256 plsAmount) internal virtual returns (uint256) {
        address[] memory path = new address[](2);
        path[0] = WPLS;
        path[1] = address(PX402);
        
        // Calculate minimum output with slippage tolerance
        uint256 expectedOutput = _estimateSwapOutput(plsAmount);
        require(expectedOutput > 0, "Cannot estimate swap output");
        
        uint256 minOutput = (expectedOutput * (SLIPPAGE_DENOMINATOR - SLIPPAGE_TOLERANCE)) / SLIPPAGE_DENOMINATOR;
        
        uint256[] memory amounts = PULSEX_ROUTER.swapExactETHForTokens{value: plsAmount}(
            minOutput,
            path,
            address(this),
            block.timestamp + 3600 // 1 hour deadline
        );
        
        return amounts[1]; // Return PX402 amount received
    }
    
    /**
     * @notice Internal function to estimate swap output using pair reserves
     * @param plsAmount Amount of PLS to swap
     * @return Estimated PX402 output
     */
    function _estimateSwapOutput(uint256 plsAmount) internal view virtual returns (uint256) {
        // Get pair address - try PX402/WPLS order (not WPLS/PX402)
        address pairAddress = PULSEX_FACTORY.getPair(address(PX402), WPLS);
        if (pairAddress == address(0)) return 0;
        
        IPulseXPair pair = IPulseXPair(pairAddress);
        
        // Get reserves
        (uint112 reserve0, uint112 reserve1,) = pair.getReserves();
        
        // Determine which reserve is WPLS and which is PX402
        address token0 = pair.token0();
        
        uint256 reserveIn;
        uint256 reserveOut;
        
        if (token0 == WPLS) {
            reserveIn = uint256(reserve0);
            reserveOut = uint256(reserve1);
        } else {
            reserveIn = uint256(reserve1);
            reserveOut = uint256(reserve0);
        }
        
        // Calculate output using UniswapV2 formula: amountOut = (amountIn * 997 * reserveOut) / (reserveIn * 1000 + amountIn * 997)
        uint256 amountInWithFee = plsAmount * 997;
        uint256 numerator = amountInWithFee * reserveOut;
        uint256 denominator = (reserveIn * 1000) + amountInWithFee;
        
        return numerator / denominator;
    }
}