Skip to main content
PulseScanner.io

Address

0x097bf9da4bb375823ac844c5dd272231dab2a2ef
Current Holdings
$0.00
TXs sent
not counted
First Active
2026-02-06
block 25,723,318
Last Active
223 days ago
block 25,723,318
Funded By
not identified

Net worth historyi

No net-worth snapshots recorded yet
exact matchStakingsolc 0.8.20+commit.a1b79de6runtime exact · creation exact
// SPDX-License-Identifier: Unlicensed

pragma solidity 0.8.20;

import "./SafeERC20.sol";
import "./ReentrancyGuard.sol";
import "./Ownable.sol";

interface IABCDEToken {
    function pairs(uint256 index) external view returns (address);
}

interface ILPToken {
    function balanceOf(address owner) external view returns (uint);
    function totalSupply() external view returns (uint);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
}

contract Staking is Ownable, ReentrancyGuard {
    using SafeERC20 for IERC20;
	
	IABCDEToken public ABCDEToken;
	
	uint256 public ABCDEStaked;
	uint256 public rewardPerShare;
	uint256 public precisionFactor;
	uint256 public undistributedReward;
	
	address[] public pairs;
	address[] public stakers;
	
	uint256 currentIndex;
	uint256 public autoDistributeIndex;
	uint256 public minAutoDistribute = 1 ether; // Minimum 1 PLS to auto-distribute
	
	struct StakingInfo {
	  uint256 stakedLP; 
	  uint256 stakedABCDE;
	  uint256 rewardExcluded;
	  uint256 pendingReward;
	  address pool;
    }
	
	mapping(address => mapping(address => StakingInfo)) public stakingInfo;
	mapping(address => uint256) public stakerIndexes;
	mapping(address => uint256) public claimedReward;
	mapping(address => bool) public isStaker;
	mapping(address => bool) public isliquidityPair;
	
    event LPStaked(address staker, address pool, uint256 amount);
    event LPUnstaked(address staker, address pool, uint256 amount);
	event RewardClaimed(address staker, uint256 reward);
	event RewardAutoDistributed(address indexed staker, uint256 reward);
	event PoolUpdated(uint256 amount);
	event PLSRescued(address indexed recipient, uint256 amount);
	event TokenRescued(address indexed token, address indexed recipient, uint256 amount);
	
    constructor() {
	    precisionFactor = 10**18;
	    ABCDEToken = IABCDEToken(0xD9c9A59A181390b83cd051746087E2d11A2fDBcf);
		
	    pairs.push(address(ABCDEToken.pairs(0)));
	    pairs.push(address(ABCDEToken.pairs(1)));
	    pairs.push(address(ABCDEToken.pairs(2)));
	    pairs.push(address(ABCDEToken.pairs(3)));
	    pairs.push(address(ABCDEToken.pairs(4)));
	    pairs.push(address(ABCDEToken.pairs(5)));
	    pairs.push(address(ABCDEToken.pairs(6)));
		
		isliquidityPair[address(ABCDEToken.pairs(0))] = true;
		isliquidityPair[address(ABCDEToken.pairs(1))] = true;
		isliquidityPair[address(ABCDEToken.pairs(2))] = true;
		isliquidityPair[address(ABCDEToken.pairs(3))] = true;
		isliquidityPair[address(ABCDEToken.pairs(4))] = true;
		isliquidityPair[address(ABCDEToken.pairs(5))] = true;
		isliquidityPair[address(ABCDEToken.pairs(6))] = true;
    }
	
	receive() external payable {}
	
	function stake(address pool, uint256 amount, address staker) external {
		require(address(msg.sender) == address(ABCDEToken), "Staker address is not allowed");
		require(isliquidityPair[address(pool)], "Pool address is not correct");
		require(address(staker) != address(0), "Pool address is not correct");
		
		address token0 = ILPToken(pool).token0();
		uint256 supply = ILPToken(pool).totalSupply();
	    (uint256 token0Balance, uint256 token1Balance, ) = ILPToken(pool).getReserves();
		
		uint256 ABCDEShare;
		if(address(token0) == address(ABCDEToken))
		{
		   ABCDEShare = (token0Balance * amount) / (supply);
		}
		else
		{
		   ABCDEShare = (token1Balance * amount) / (supply);
		}
		
		ABCDEStaked += ABCDEShare;
		
		if(stakingInfo[address(staker)][address(pool)].stakedLP > 0) 
		{
            uint256 pending = pendingReward(address(staker), address(pool));
            if(pending > 0) 
			{
			   stakingInfo[address(staker)][address(pool)].pendingReward += pending;
            }
        }
		
		if(!isStaker[address(staker)])
		{
		   isStaker[address(staker)] = true; 
		   addStaker(address(staker));
		}
		
		stakingInfo[address(staker)][address(pool)].stakedLP += amount;
		stakingInfo[address(staker)][address(pool)].stakedABCDE += ABCDEShare;
		stakingInfo[address(staker)][address(pool)].pool = address(pool);
		stakingInfo[address(staker)][address(pool)].rewardExcluded = (((stakingInfo[address(staker)][address(pool)].stakedABCDE) * rewardPerShare) / precisionFactor);
        emit LPStaked(address(staker), address(pool), amount);
    }
	
	function claimReward(address pool) external nonReentrant {
		require(isliquidityPair[address(pool)], "Pool address is not correct");
		
		uint256 pending = pendingReward(address(msg.sender), address(pool));
		        pending += stakingInfo[address(msg.sender)][address(pool)].pendingReward;
				
		if(pending > 0) 
		{
		    uint256 cBalance = IERC20(pool).balanceOf(address(msg.sender));
			uint256 sBalance = stakingInfo[address(msg.sender)][address(pool)].stakedLP;
			uint256 sABCDEBalance = stakingInfo[address(msg.sender)][address(pool)].stakedABCDE;
			
			if(sBalance > cBalance)
			{
			    uint256 newPending = (pending * cBalance) / (sBalance);
				uint256 newABCDEBalance = (sABCDEBalance * cBalance) / (sBalance);
				
				ABCDEStaked -= (sABCDEBalance - newABCDEBalance);
				
				_updatePool((pending - newPending));
			    payable(address(msg.sender)).transfer(newPending);
				
				claimedReward[address(msg.sender)] += newPending;
				stakingInfo[address(msg.sender)][address(pool)].stakedLP = cBalance;
				stakingInfo[address(msg.sender)][address(pool)].stakedABCDE = newABCDEBalance;
			    stakingInfo[address(msg.sender)][address(pool)].rewardExcluded = (((stakingInfo[address(msg.sender)][address(pool)].stakedABCDE) * rewardPerShare) / precisionFactor);
			    stakingInfo[address(msg.sender)][address(pool)].pendingReward = 0;
			    emit RewardClaimed(address(msg.sender), pending);
			}
			else
			{
			    claimedReward[address(msg.sender)] += pending;
			    payable(address(msg.sender)).transfer(pending);
			    stakingInfo[address(msg.sender)][address(pool)].rewardExcluded = (((stakingInfo[address(msg.sender)][address(pool)].stakedABCDE) * rewardPerShare) / precisionFactor);
			    stakingInfo[address(msg.sender)][address(pool)].pendingReward = 0;
			    emit RewardClaimed(address(msg.sender), pending);
			}
		}
    }
	
	function claimRewards() external nonReentrant {
	
		for (uint256 i = 0; i < pairs.length; i++) 
	    {
		    address pool = pairs[i];
		    uint256 pending = pendingReward(address(msg.sender), address(pool));
		            pending += stakingInfo[address(msg.sender)][address(pool)].pendingReward;
					
			if(pending > 0) 
			{
				uint256 cBalance = IERC20(pool).balanceOf(address(msg.sender));
				uint256 sBalance = stakingInfo[address(msg.sender)][address(pool)].stakedLP;
				uint256 sABCDEBalance = stakingInfo[address(msg.sender)][address(pool)].stakedABCDE;
				
				if(sBalance > cBalance)
				{
					uint256 newPending = (pending * cBalance) / (sBalance);
					uint256 newABCDEBalance = (sABCDEBalance * cBalance) / (sBalance);
					
					ABCDEStaked -= (sABCDEBalance - newABCDEBalance);
					
					_updatePool((pending - newPending));
					payable(address(msg.sender)).transfer(newPending);
					
					claimedReward[address(msg.sender)] += newPending;
					stakingInfo[address(msg.sender)][address(pool)].stakedLP = cBalance;
					stakingInfo[address(msg.sender)][address(pool)].stakedABCDE = newABCDEBalance;
					stakingInfo[address(msg.sender)][address(pool)].rewardExcluded = (((stakingInfo[address(msg.sender)][address(pool)].stakedABCDE) * rewardPerShare) / precisionFactor);
					stakingInfo[address(msg.sender)][address(pool)].pendingReward = 0;
					emit RewardClaimed(address(msg.sender), pending);
				}
				else
				{
				     claimedReward[address(msg.sender)] += pending;
					 payable(address(msg.sender)).transfer(pending);
					 stakingInfo[address(msg.sender)][address(pool)].rewardExcluded = (((stakingInfo[address(msg.sender)][address(pool)].stakedABCDE) * rewardPerShare) / precisionFactor);
					 stakingInfo[address(msg.sender)][address(pool)].pendingReward = 0;
					 emit RewardClaimed(address(msg.sender), pending);
				}
			}
		}
    }
	
	function process(uint256 gas) external {
	
		uint256 stakerCount = stakers.length;
        if(stakerCount == 0) { return; }

        uint256 gasUsed = 0;
        uint256 gasLeft = gasleft();

        uint256 iterations = 0;
		
        while(gasUsed < gas && iterations < stakerCount)
		{
            if(currentIndex >= stakerCount)
			{
               currentIndex = 0;
            }
			
			updateBalance(stakers[currentIndex]);
			stakerCount = stakers.length;
			
            gasUsed = gasUsed + gasLeft - gasleft();
            gasLeft = gasleft();
            currentIndex++;
            iterations++;
        }
    }
	
	function autoDistributeNext() external {
		require(msg.sender == address(ABCDEToken), "Only ABCDE contract");
		
		uint256 stakerCount = stakers.length;
		if(stakerCount == 0) return;
		
		// Try to find someone with rewards to distribute
		uint256 attempts = 0;
		while(attempts < stakerCount) {
			if(autoDistributeIndex >= stakerCount) {
				autoDistributeIndex = 0;
			}
			
			address staker = stakers[autoDistributeIndex];
			
			// Check all pools for this staker
			for(uint256 i = 0; i < pairs.length; i++) {
				address pool = pairs[i];
				uint256 pending = pendingReward(staker, pool);
				pending += stakingInfo[staker][pool].pendingReward;
				
				// If they have enough pending rewards, auto-send!
				if(pending >= minAutoDistribute) {
					uint256 cBalance = IERC20(pool).balanceOf(staker);
					uint256 sBalance = stakingInfo[staker][pool].stakedLP;
					uint256 sABCDEBalance = stakingInfo[staker][pool].stakedABCDE;
					
					if(sBalance > cBalance) {
						// Adjust for LP changes
						uint256 newPending = (pending * cBalance) / sBalance;
						uint256 newABCDEBalance = (sABCDEBalance * cBalance) / sBalance;
						
						ABCDEStaked -= (sABCDEBalance - newABCDEBalance);
						_updatePool(pending - newPending);
						
						payable(staker).transfer(newPending);
						
						claimedReward[staker] += newPending;
						stakingInfo[staker][pool].stakedLP = cBalance;
						stakingInfo[staker][pool].stakedABCDE = newABCDEBalance;
						stakingInfo[staker][pool].rewardExcluded = ((stakingInfo[staker][pool].stakedABCDE * rewardPerShare) / precisionFactor);
						stakingInfo[staker][pool].pendingReward = 0;
						
						emit RewardAutoDistributed(staker, newPending);
					} else {
						// Normal distribution
						payable(staker).transfer(pending);
						
						claimedReward[staker] += pending;
						stakingInfo[staker][pool].rewardExcluded = ((stakingInfo[staker][pool].stakedABCDE * rewardPerShare) / precisionFactor);
						stakingInfo[staker][pool].pendingReward = 0;
						
						emit RewardAutoDistributed(staker, pending);
					}
					
					autoDistributeIndex++;
					return; // Successfully distributed, done for this call
				}
			}
			
			autoDistributeIndex++;
			attempts++;
		}
	}
	
	function updateBalance(address staker) internal {
	   bool removeFromList = true;
	   
	   for (uint256 i = 0; i < pairs.length; i++) 
	   {
	       address pool = pairs[i];
		   uint256 sBalance = stakingInfo[address(staker)][address(pool)].stakedLP;
		   uint256 sABCDEBalance = stakingInfo[address(staker)][address(pool)].stakedABCDE;
		   
		   if(sBalance > 0)
		   {
		       uint256 cBalance = IERC20(pool).balanceOf(address(staker));
			   if(cBalance > 0)
			   {
			       removeFromList = false;
			   }
			   if(sBalance > cBalance)
			   {
			       uint256 pending = pendingReward(address(staker), address(pool));
				           pending += stakingInfo[address(staker)][address(pool)].pendingReward;
				   uint256 newPending = 0; 		   
				   uint256 newABCDEBalance = (sABCDEBalance * cBalance) / (sBalance);
				   
				   ABCDEStaked -= (sABCDEBalance - newABCDEBalance);
				   
				   if(pending > 0)
				   {
				       newPending = (pending * cBalance) / (sBalance);
				       _updatePool((pending - newPending));  
				   }
				   
				   stakingInfo[address(staker)][address(pool)].stakedLP = cBalance;
				   stakingInfo[address(staker)][address(pool)].stakedABCDE = newABCDEBalance;
				   stakingInfo[address(staker)][address(pool)].rewardExcluded = (((stakingInfo[address(staker)][address(pool)].stakedABCDE) * rewardPerShare) / precisionFactor);
				   stakingInfo[address(staker)][address(pool)].pendingReward = newPending;
			   }
		   }
       }
	   
	   if(removeFromList)
	   {
	      removeStaker(staker);
		  isStaker[address(staker)] = false; 
	   }
    }
	
	function _updatePool(uint256 amount) internal {
	    if(ABCDEStaked > 0)
	    {
	       uint256 totalAmount = undistributedReward + amount; 
	       rewardPerShare += ((totalAmount * precisionFactor) / ABCDEStaked);
		   undistributedReward = 0;
	    }
	    else
	    {
	       undistributedReward += amount;
	    }
	    emit PoolUpdated(amount);
    }
	
	function updatePool(uint256 amount) external {
	   require(msg.sender == address(ABCDEToken), "Sender not allowed");
	   
	   if(ABCDEStaked > 0)
	   {
	       uint256 totalAmount = undistributedReward + amount; 
	       rewardPerShare += ((totalAmount * precisionFactor) / ABCDEStaked);
		   undistributedReward = 0;
	   }
	   else
	   {
	      undistributedReward += amount;
	   }
	   emit PoolUpdated(amount);
    }
	
	function pendingReward(address staker, address pool) public view returns (uint256) {
	
	   if(stakingInfo[address(staker)][address(pool)].stakedLP > 0)
	   {
		    uint256 staked = stakingInfo[address(staker)][address(pool)].stakedABCDE;
		    uint256 claimed = stakingInfo[address(staker)][address(pool)].rewardExcluded;
		   
		    uint256 pending = ((staked * rewardPerShare) / precisionFactor) - (claimed);
		    return pending;
       } 
	   else 
	   {
		   return 0;
	   }
    }
	
	function setMinAutoDistribute(uint256 amount) external onlyOwner {
		minAutoDistribute = amount;
	}
	
	function addStaker(address staker) internal {
       stakerIndexes[staker] = stakers.length;
       stakers.push(staker);
    }
	
    function removeStaker(address staker) internal {
       stakers[stakerIndexes[staker]] = stakers[stakers.length-1];
       stakerIndexes[stakers[stakers.length-1]] = stakerIndexes[staker];
       stakers.pop();
    }

    function rescuePLS(address recipient) external onlyOwner nonReentrant {
		require(recipient != address(0), "Invalid recipient");
		require(ABCDEStaked == 0, "Cannot rescue while staking is active");
		
		uint256 amount = address(this).balance;
		payable(recipient).transfer(amount);
		emit PLSRescued(recipient, amount);
    }

    function rescueToken(address token, address recipient) external onlyOwner {
		require(token != address(0), "Invalid token");
		require(recipient != address(0), "Invalid recipient");
		require(token != address(ABCDEToken), "Cannot rescue ABCDE");
		
		uint256 amount = IERC20(token).balanceOf(address(this));
		bool success = IERC20(token).transfer(recipient, amount);
		require(success, "Transfer failed");
		emit TokenRescued(token, recipient, amount);
	}
}