Skip to main content
PulseScanner.io

Address

0x46bcb4bb405cd0a3bafc549fce33cd9044a1a20b
Current Holdings
$0.2531
TXs sent
not counted
First Active
2025-11-29
block 25,138,267
Last Active
292 days ago
block 25,150,707
Funded By
not identified

Net worth historyi

7 snapshots · to block 27,497,273coverage change 26 Augcoverage change 27 Augcoverage change 27 Augcoverage change 27 Augcoverage change 27 Aug
exact matchavstaking2solc 0.8.29+commit.ab55807cruntime exact · creation exact
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IERC20 {
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
    function transfer(address to, uint256 amount) external returns (bool);
    function balanceOf(address account) external view returns (uint256);
}

contract avstaking2 {
    IERC20 public immutable stakingToken;
    IERC20[] private _rewardTokens;           // ← private = no auto getter

    address public owner;

    uint256 public constant DAILY_BP = 2500;
    uint256 public constant BP_DIVISOR = 10000;
    uint256 public constant SECONDS_PER_DAY = 86400;
    uint256 public constant PRECISION = 1e24;

    struct RewardPool {
        uint256 rewardPerStakedToken;
        uint256 lastUpdate;
        uint256 leftoverRewards;
    }

    struct UserInfo {
        uint256 staked;
        uint256 rewardDebt;
        uint256 pending;
    }

    uint256 public totalStaked;
    mapping(uint256 => RewardPool) public rewardPools;
    mapping(address => mapping(uint256 => UserInfo)) private users;  // ← private = no auto getter

    event Staked(address indexed user, uint256 amount);
    event Unstaked(address indexed user, uint256 amount);
    event Claimed(address indexed user, uint256 amount, IERC20 indexed token);
    event RewardAdded(IERC20 indexed token, uint256 amount);

    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }

    constructor() {
        owner = msg.sender;
        stakingToken = IERC20(0xA6E768f384F70cfDe30DbbdA37ccD84734718c7d); // AVRL
        _addRewardToken(IERC20(0xA1077a294dDE1B09bB078844df40758a5D0f9a27)); // WPLS
    }

    function stake(uint256 amount) external {
        require(amount > 0, "Zero");
        for (uint256 i = 0; i < _rewardTokens.length; i++) _updateUser(msg.sender, i);
        require(stakingToken.transferFrom(msg.sender, address(this), amount), "TF");
        for (uint256 i = 0; i < _rewardTokens.length; i++) users[msg.sender][i].staked += amount;
        totalStaked += amount;
        emit Staked(msg.sender, amount);
    }

    function unstake(uint256 amount) external {
        require(amount > 0, "Zero");
        for (uint256 i = 0; i < _rewardTokens.length; i++) {
            UserInfo storage info = users[msg.sender][i];
            require(info.staked >= amount, "Low");
            _updateUser(msg.sender, i);
            info.staked -= amount;
        }
        totalStaked -= amount;
        require(stakingToken.transfer(msg.sender, amount), "TF");
        emit Unstaked(msg.sender, amount);
    }

    function claimAll() external {
        for (uint256 i = 0; i < _rewardTokens.length; i++) {
            _updateUser(msg.sender, i);
            uint256 reward = users[msg.sender][i].pending;
            if (reward > 0) {
                users[msg.sender][i].pending = 0;
                require(_rewardTokens[i].transfer(msg.sender, reward), "TF");
                emit Claimed(msg.sender, reward, _rewardTokens[i]);
            }
        }
    }

    function _updateReward(uint256 i) internal {
        RewardPool storage pool = rewardPools[i];
        if (block.timestamp <= pool.lastUpdate || totalStaked == 0 || pool.leftoverRewards == 0) return;
        uint256 elapsed = block.timestamp - pool.lastUpdate;
        pool.lastUpdate = block.timestamp;
        uint256 daily = (pool.leftoverRewards * DAILY_BP) / BP_DIVISOR;
        uint256 reward = (daily * elapsed) / SECONDS_PER_DAY;
        if (reward > pool.leftoverRewards) reward = pool.leftoverRewards;
        pool.leftoverRewards -= reward;
        pool.rewardPerStakedToken += (reward * PRECISION) / totalStaked;
    }

    function _updateUser(address user, uint256 i) internal {
        _updateReward(i);
        UserInfo storage info = users[user][i];
        RewardPool storage pool = rewardPools[i];
        uint256 acc = (info.staked * pool.rewardPerStakedToken) / PRECISION;
        uint256 owed = acc - info.rewardDebt;
        if (owed > 0) info.pending += owed;
        info.rewardDebt = acc;
    }

    // Owner
    function _addRewardToken(IERC20 token) internal {
        for (uint256 i = 0; i < _rewardTokens.length; i++) require(_rewardTokens[i] != token, "Exists");
        _rewardTokens.push(token);
        rewardPools[_rewardTokens.length - 1].lastUpdate = block.timestamp;
    }

    function addRewardToken(IERC20 token) external onlyOwner { _addRewardToken(token); }

    function depositRewards(IERC20 token, uint256 amount) external onlyOwner {
        uint256 index = type(uint256).max;
        for (uint256 i = 0; i < _rewardTokens.length; i++) {
            if (_rewardTokens[i] == token) { index = i; break; }
        }
        require(index != type(uint256).max, "Not reward");
        require(token.transferFrom(msg.sender, address(this), amount), "TF");
        rewardPools[index].leftoverRewards += amount;
        emit RewardAdded(token, amount);
    }

    // ==== VIEWS (manual getters — no clashes) ====
    function getUserInfo(address user, uint256 index) external view returns (uint256 staked, uint256 rewardDebt, uint256 pending) {
        UserInfo memory info = users[user][index];
        return (info.staked, info.rewardDebt, info.pending);
    }

    function rewardTokenCount() external view returns (uint256) { return _rewardTokens.length; }
    function rewardTokenAt(uint256 i) external view returns (address) { return address(_rewardTokens[i]); }

    function apr(address tokenAddr) external view returns (uint256) {
        for (uint256 i = 0; i < _rewardTokens.length; i++) {
            if (address(_rewardTokens[i]) == tokenAddr && totalStaked > 0) {
                uint256 daily = (rewardPools[i].leftoverRewards * DAILY_BP) / BP_DIVISOR;
                return (daily * 365 * 1e18) / totalStaked;
            }
        }
        return 0;
    }
}