Address
0x097bf9da4bb375823ac844c5dd272231dab2a2efCurrent 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);
}
}