文件 1 的 9:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 2 的 9:ERC20.sol
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
function decimals() public view virtual override returns (uint8) {
return 18;
}
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 amount) public virtual override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
function transferFrom(
address sender,
address recipient,
uint256 amount
) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
uint256 currentAllowance = _allowances[sender][_msgSender()];
require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
unchecked {
_approve(sender, _msgSender(), currentAllowance - amount);
}
return true;
}
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
return true;
}
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
uint256 currentAllowance = _allowances[_msgSender()][spender];
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(_msgSender(), spender, currentAllowance - subtractedValue);
}
return true;
}
function _transfer(
address sender,
address recipient,
uint256 amount
) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
uint256 senderBalance = _balances[sender];
require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[sender] = senderBalance - amount;
}
_balances[recipient] += amount;
emit Transfer(sender, recipient, amount);
_afterTokenTransfer(sender, recipient, amount);
}
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
}
文件 3 的 9:IERC20.sol
pragma solidity ^0.8.0;
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);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 4 的 9:IERC20Metadata.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
interface IERC20Metadata is IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
}
文件 5 的 9:MetalSwapIronStakingPool.sol
pragma solidity ^0.8.7;
import'./StakingPoolPremium.sol';
contract MetalSwapIronStakingPool is StakingPoolPremium {
constructor(IERC20 stakingToken) StakingPool(stakingToken) {
}
}
文件 6 的 9:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
function owner() public view virtual returns (address) {
return _owner;
}
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 7 的 9:ReentrancyGuard.sol
pragma solidity ^0.8.0;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
}
文件 8 的 9:StakingPool.sol
pragma solidity ^0.8.7;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract StakingPool is ReentrancyGuard, Ownable {
IERC20 public stakingToken;
uint256 public startStaking;
uint256 public endStaking;
uint256 public poolWeightedAverage;
uint256 public rewardTokensAmount;
uint256 public stakedTokensTotal;
bool public paused;
bool public finalized;
address private _governance;
mapping(address => uint256) public tokensStakedPerUser;
mapping(address => uint256) public userWeightedAverage;
event RewardAdded(uint256 reward);
event Staked(address indexed user, uint256 amount);
event Exit(address indexed user, uint256 stakedTokens, uint256 reward);
constructor(
IERC20 _stakingToken
) {
stakingToken = _stakingToken;
_transferGovernance(_msgSender());
paused = true;
finalized = false;
}
function finalizePoolCreation (uint256 _startStaking, uint256 periodInSec, uint256 amountOfRewardsTokensToSend,uint16 _slotNumber, uint256 _forSlotAmount) public virtual onlyGovernance nonReentrant {
require(finalized == false, "Error: Staking Pool must be paused in order to finalize!");
require (amountOfRewardsTokensToSend > 0, "Error: The creator must send some reward tokens to the pool in order to create it");
require (stakingToken.transferFrom(msg.sender, address(this), amountOfRewardsTokensToSend), "Error: Reward tokens trasnfer error, cannot create pool");
startStaking = _startStaking;
endStaking = _startStaking + periodInSec;
rewardTokensAmount = amountOfRewardsTokensToSend;
paused = false;
finalized = true;
emit RewardAdded(amountOfRewardsTokensToSend);
}
function stake(uint256 userInput) public virtual nonReentrant checkPoolOpen checkStakingUnpaused{
require(userInput > 0, "Error: Cannot stake 0");
require(stakingToken.transferFrom(msg.sender, address(this), userInput), "Error during token transfer");
tokensStakedPerUser[msg.sender] += userInput;
uint256 weightedAverage = calcWeightedAverage(userInput);
userWeightedAverage[msg.sender] += weightedAverage;
poolWeightedAverage += weightedAverage;
stakedTokensTotal += userInput;
emit Staked(msg.sender, userInput);
}
function exit() public nonReentrant checkStakingFinished checkStakingUnpaused{
uint256 stakedTokens = tokensStakedPerUser[msg.sender];
uint256 reward = calcReward(msg.sender);
require(stakedTokens > 0, "Error: Cannot get reward if staked tokens = 0");
require(reward > 0, "Error: Cannot get reward = 0");
tokensStakedPerUser[msg.sender] = 0;
userWeightedAverage[msg.sender] = 0;
require(stakingToken.transfer(msg.sender, (stakedTokens+reward)), "Error during the withdrawal of user reward");
emit Exit(msg.sender, stakedTokens, reward);
}
function addRewardTokensAmount(uint256 amountToAdd) external onlyGovernance nonReentrant {
require(stakingToken.transferFrom(msg.sender, address(this), amountToAdd), "Error during the token reward increase transaction!");
rewardTokensAmount += amountToAdd;
emit RewardAdded(amountToAdd);
}
function setTime(uint256 initTimestamp, uint256 endTimestamp) public onlyGovernance {
if(endTimestamp > initTimestamp){
startStaking = initTimestamp;
endStaking = endTimestamp;
}
}
function closePool() public onlyGovernance nonReentrant{
paused = true;
require (stakingToken.transfer(msg.sender, stakingToken.balanceOf(address(this))), "Error during token transfer");
}
function pauseStaking () public onlyGovernance {
paused = true;
}
function unpauseStaking () public onlyGovernance {
paused = false;
}
function calcWeightedAverage(uint256 amount) public view returns (uint256 weightedAverage) {
if(endStaking > startStaking){
return amount * (endStaking - block.timestamp ) / (endStaking - startStaking );
}
return 0;
}
function calcReward(address user) public view returns (uint256 reward) {
if(poolWeightedAverage>0){
return userWeightedAverage[user] * rewardTokensAmount / poolWeightedAverage;
}
return 0;
}
function canIStakeNow() public view returns (bool poolReady) {
if((paused == false) && (block.timestamp >= startStaking) && (block.timestamp <= endStaking)){
return true;
}
return false;
}
function canIExitPoolNow() public view returns (bool poolReady) {
if(block.timestamp >= endStaking){
return true;
}
return false;
}
function getPoolUserData(address user) public view returns (uint256 userStakedTokens, uint256 userReward ){
return (tokensStakedPerUser[user],calcReward(user));
}
function getStakingPeriod() public view returns (uint256 returnStartStaking, uint256 returnEndStaking){
return (startStaking, endStaking);
}
function getPoolWeightedAverage() public view returns (uint256){
return poolWeightedAverage;
}
function getRewardTokensAmount() public view returns (uint256){
return rewardTokensAmount;
}
function getStakedTokensTotal() public view returns (uint256){
return stakedTokensTotal;
}
function getTokensStakedPerUser(address user) public view returns (uint256){
return tokensStakedPerUser[user];
}
function getUserWeightedAverage(address user) public view returns (uint256){
return userWeightedAverage[user];
}
function transferGovernance(address newGovernace) public virtual onlyOwner {
require(newGovernace != address(0), "Governace: new owner is the zero address");
_transferGovernance(newGovernace);
}
function _transferGovernance(address newGovernace) internal virtual {
_governance = newGovernace;
}
function governance() public view virtual returns (address) {
return _governance;
}
modifier checkPoolOpen() {
require(block.timestamp <= endStaking, "Error: Staking is finished");
require(block.timestamp >= startStaking, "Error: Staking has not yet begun");
_;
}
modifier checkStakingFinished() {
require(block.timestamp >= endStaking, "Error: Staking period is not finished");
_;
}
modifier checkStakingUnpaused() {
require(paused == false, "Error: Swap is paused, try again later");
_;
}
modifier onlyGovernance() {
require(owner() == _msgSender() || governance() == _msgSender() , "Ownable: caller is not the owner/governance");
_;
}
}
文件 9 的 9:StakingPoolPremium.sol
pragma solidity ^0.8.7;
import './StakingPool.sol';
abstract contract StakingPoolPremium is StakingPool {
uint16 public slotNumber;
uint256 public forSlotAmount;
uint16 public takenSlots;
function finalizePoolCreation(
uint256 _startStaking,
uint256 periodInSec,
uint256 amountOfRewardsTokensToSend,
uint16 _slotNumber,
uint256 _forSlotAmount
) public override onlyGovernance nonReentrant {
require(
finalized == false,
"Error: Staking Pool must be paused in order to finalize!"
);
require(
amountOfRewardsTokensToSend > 0,
"Error: The creator must send some reward tokens to the pool in order to create it"
);
require(
stakingToken.transferFrom(
msg.sender,
address(this),
amountOfRewardsTokensToSend
),
"Error: Reward tokens trasnfer error, cannot create pool"
);
startStaking = _startStaking;
endStaking = _startStaking + periodInSec;
slotNumber = _slotNumber;
forSlotAmount = _forSlotAmount;
rewardTokensAmount = amountOfRewardsTokensToSend;
paused = false;
finalized = true;
emit RewardAdded(amountOfRewardsTokensToSend);
}
function stake(uint256 userInput)
public override
nonReentrant
checkPoolOpen
checkStakingUnpaused
{
require(userInput > 0, "Error: Cannot stake 0");
require(
takenSlots + userInput <= slotNumber,
"Error: slot are full"
);
takenSlots = takenSlots + uint16(userInput);
uint256 amount = userInput * forSlotAmount;
require(
stakingToken.transferFrom(msg.sender, address(this), amount),
"Error during token transfer"
);
tokensStakedPerUser[msg.sender] += amount;
uint256 weightedAverage = calcWeightedAverage(amount);
userWeightedAverage[msg.sender] += weightedAverage;
poolWeightedAverage += weightedAverage;
stakedTokensTotal += amount;
emit Staked(msg.sender, amount);
}
function getTakenSlots() public view returns (uint16) {
return takenSlots;
}
function getSlotInfo() public view returns (uint256 forSlotAmountInfo,uint16 slotNumberInfo) {
return (forSlotAmount,slotNumber);
}
}
{
"compilationTarget": {
"contracts/MetalSwapIronStakingPool.sol": "MetalSwapIronStakingPool"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"contract IERC20","name":"stakingToken","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"stakedTokens","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"Exit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"RewardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Staked","type":"event"},{"inputs":[{"internalType":"uint256","name":"amountToAdd","type":"uint256"}],"name":"addRewardTokensAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"calcReward","outputs":[{"internalType":"uint256","name":"reward","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"calcWeightedAverage","outputs":[{"internalType":"uint256","name":"weightedAverage","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"canIExitPoolNow","outputs":[{"internalType":"bool","name":"poolReady","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"canIStakeNow","outputs":[{"internalType":"bool","name":"poolReady","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"closePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"endStaking","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_startStaking","type":"uint256"},{"internalType":"uint256","name":"periodInSec","type":"uint256"},{"internalType":"uint256","name":"amountOfRewardsTokensToSend","type":"uint256"},{"internalType":"uint16","name":"_slotNumber","type":"uint16"},{"internalType":"uint256","name":"_forSlotAmount","type":"uint256"}],"name":"finalizePoolCreation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"finalized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"forSlotAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getPoolUserData","outputs":[{"internalType":"uint256","name":"userStakedTokens","type":"uint256"},{"internalType":"uint256","name":"userReward","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPoolWeightedAverage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardTokensAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSlotInfo","outputs":[{"internalType":"uint256","name":"forSlotAmountInfo","type":"uint256"},{"internalType":"uint16","name":"slotNumberInfo","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStakedTokensTotal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStakingPeriod","outputs":[{"internalType":"uint256","name":"returnStartStaking","type":"uint256"},{"internalType":"uint256","name":"returnEndStaking","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTakenSlots","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getTokensStakedPerUser","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserWeightedAverage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pauseStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolWeightedAverage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardTokensAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"initTimestamp","type":"uint256"},{"internalType":"uint256","name":"endTimestamp","type":"uint256"}],"name":"setTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"slotNumber","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"userInput","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakedTokensTotal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startStaking","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"takenSlots","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokensStakedPerUser","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newGovernace","type":"address"}],"name":"transferGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpauseStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userWeightedAverage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]