Skip to content
Chain 4441 · LiteForge TestnetHead#0
LitVM·Scan
Hard Money Web3 Explorer
Verified Contract
0x28c7167ebF6112D5B01396eEeDFe8F990Fcb54bb
Pin to watchlist
scan to copy
zkLTC Balance
0
hard money native
Transactions
158,722
Token transfers
207,738
Validations
0
blocks produced
Hard Money Score
43
/100
MIXED
share of activity in native zkLTC vs. tokens
native
158,722
tokens
207,738
Contract name
MultiPoolFarm
Compiler
v0.8.30+commit.73712a01
Optimization
disabled
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

// Simple ERC20 interface (tanpa Oz)
interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
}

contract MultiPoolFarm {
    struct PoolInfo {
        IERC20 stakingToken;
        IERC20 rewardToken;
        uint256 lastRewardBlock;
        uint256 accRewardPerShare; // 1e12 scaler
        uint256 rewardPerBlock;
        uint256 totalStaked;
    }
    struct UserInfo {
        uint256 amount;
        uint256 rewardDebt;
    }
    PoolInfo[] public poolInfo;
    mapping(uint256 => mapping(address => UserInfo)) public userInfo;

    // Security: Ownable/manually implemented
    address public owner;
    // Security: Simple nonReentrant
    bool private _entered;

    event Deposit(address indexed user, uint256 indexed pid, uint256 amount);
    event Withdraw(address indexed user, uint256 indexed pid, uint256 amount);
    event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount);
    event RewardPaid(address indexed user, uint256 indexed pid, uint256 amount);
    event PoolAdded(uint256 indexed pid, address stakingToken, address rewardToken, uint256 rewardPerBlock);
    event RewardPerBlockUpdated(uint256 indexed pid, uint256 rewardPerBlock);
    event OwnershipTransferred(address indexed prevOwner, address indexed newOwner);

    modifier onlyOwner() {
        require(msg.sender == owner, "only owner");
        _;
    }
    modifier nonReentrant() {
        require(!_entered, "reentrancy");
        _entered = true;
        _;
        _entered = false;
    }

    constructor() {
        owner = msg.sender;
        _entered = false;
    }

    function transferOwnership(address newOwner) external onlyOwner {
        require(newOwner != address(0), "zero addr");
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
    }

    function addPool(
        IERC20 _stakingToken,
        IERC20 _rewardToken,
        uint256 _rewardPerBlock
    ) external onlyOwner {
        poolInfo.push(PoolInfo({
            stakingToken: _stakingToken,
            rewardToken: _rewardToken,
            lastRewardBlock: block.number,
            accRewardPerShare: 0,
            rewardPerBlock: _rewardPerBlock,
            totalStaked: 0
        }));
        emit PoolAdded(poolInfo.length - 1, address(_stakingToken), address(_rewardToken), _rewardPerBlock);
    }

    function updateRewardPerBlock(uint256 _pid, uint256 _rewardPerBlock) external onlyOwner {
        updatePool(_pid);
        poolInfo[_pid].rewardPerBlock = _rewardPerBlock;
        emit RewardPerBlockUpdated(_pid, _rewardPerBlock);
    }

    function massUpdatePools() public {
        for (uint256 pid = 0; pid < poolInfo.length; ++pid) {
            updatePool(pid);
        }
    }

    function updatePool(uint256 _pid) public {
        PoolInfo storage pool = poolInfo[_pid];
        if (block.number <= pool.lastRewardBlock) return;
        if (pool.totalStaked == 0) {
            pool.lastRewardBlock = block.number;
            return;
        }
        uint256 multiplier = block.number - pool.lastRewardBlock;
        uint256 reward = multiplier * pool.rewardPerBlock;
        pool.accRewardPerShare += reward * 1e12 / pool.totalStaked;
        pool.lastRewardBlock = block.number;
    }

    function deposit(uint256 _pid, uint256 _amount) external nonReentrant {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        updatePool(_pid);
        uint256 pending = user.amount * pool.accRewardPerShare / 1e12 - user.rewardDebt;
        if (pending > 0) {
            pool.rewardToken.transfer(msg.sender, pending);
            emit RewardPaid(msg.sender, _pid, pending);
        }
        if (_amount > 0) {
            require(
                pool.stakingToken.transferFrom(msg.sender, address(this), _amount),
                "transferFrom fail"
            );
            user.amount += _amount;
            pool.totalStaked += _amount;
        }
        user.rewardDebt = user.amount * pool.accRewardPerShare / 1e12;
        emit Deposit(msg.sender, _pid, _amount);
    }

    function withdraw(uint256 _pid, uint256 _amount) external nonReentrant {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        require(user.amount >= _amount, "withdraw > staked");
        updatePool(_pid);
        uint256 pending = user.amount * pool.accRewardPerShare / 1e12 - user.rewardDebt;
        if (pending > 0) {
            pool.rewardToken.transfer(msg.sender, pending);
            emit RewardPaid(msg.sender, _pid, pending);
        }
        if (_amount > 0) {
            user.amount -= _amount;
            pool.totalStaked -= _amount;
            require(pool.stakingToken.transfer(msg.sender, _amount), "stake transfer fail");
        }
        user.rewardDebt = user.amount * pool.accRewardPerShare / 1e12;
        emit Withdraw(msg.sender, _pid, _amount);
    }

    function emergencyWithdraw(uint256 _pid) external nonReentrant {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        uint256 amt = user.amount;
        require(pool.stakingToken.transfer(msg.sender, amt), "stake transfer fail");
        emit EmergencyWithdraw(msg.sender, _pid, amt);
        pool.totalStaked -= amt;
        user.amount = 0;
        user.rewardDebt = 0;
    }

    function pendingReward(uint256 _pid, address _user) external view returns (uint256) {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][_user];
        uint256 acc = pool.accRewardPerShare;
        if (block.number > pool.lastRewardBlock && pool.totalStaked != 0) {
            uint256 multiplier = block.number - pool.lastRewardBlock;
            uint256 reward = multiplier * pool.rewardPerBlock;
            acc += reward * 1e12 / pool.totalStaked;
        }
        return user.amount * acc / 1e12 - user.rewardDebt;
    }
}