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;
}
}