编译器
0.8.20+commit.a1b79de6
文件 1 的 6:Context.sol
pragma solidity ^0.8.20;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
文件 2 的 6:IERC20.sol
pragma solidity ^0.8.20;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
文件 3 的 6:IPreSale.sol
pragma solidity ^0.8.20;
interface IpreSale{
function getUserToken(address userAddress) external view returns (uint);
function transferToStaking(uint amount) external returns(bool);
function getEndTime() external view returns(uint);
}
文件 4 的 6:Ownable.sol
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 5 的 6:ReentrancyGuard.sol
pragma solidity ^0.8.20;
abstract contract ReentrancyGuard {
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
_status = ENTERED;
}
function _nonReentrantAfter() private {
_status = NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
文件 6 的 6:stakingTest.sol
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./IPreSale.sol";
contract DoggyAiStake is Ownable, ReentrancyGuard {
IERC20 public stakingToken;
uint public stakingEndDate;
uint256 public minStake = 10 * 10 ** 18;
uint256 public stakePeriod;
uint256 public totalStaked;
uint public totalFund;
uint public rewardRate;
IpreSale preSaleData;
event Staked(address indexed, uint256 amount);
event Claimed(address indexed, uint256 amount);
event Withdrawn(address indexed, uint256 amount);
struct Stake {
uint256 amount;
uint256 startTime;
uint256 lastClaimTimestamp;
}
struct RewardRateChange {
uint256 timestamp;
uint256 rewardRate;
uint256 totalStaked;
}
RewardRateChange[]public rewardRateHistory;
mapping(address => Stake[]) public stakes;
mapping(address => uint256) public userTotalStaked;
function _checkRewardRate() internal {
if (totalStaked == 0) {
rewardRate = (totalFund / stakePeriod);
rewardRateHistory.push(RewardRateChange({timestamp: block.timestamp + 1, rewardRate: rewardRate,totalStaked: minStake }));
} else {
rewardRate = (totalFund / stakePeriod);
rewardRateHistory.push(RewardRateChange({timestamp: block.timestamp + 1, rewardRate: rewardRate,totalStaked: totalStaked}));
}
}
constructor(
address DoggyAi,
address initialOwner,
uint256 _stakePeriod,
uint amountToFill,
uint _timestamp,
address _preSaleData
) Ownable(initialOwner) {
stakingToken = IERC20(DoggyAi);
stakePeriod = _stakePeriod * 1 days;
totalFund = amountToFill;
stakingEndDate = _timestamp + stakePeriod;
rewardRate = (totalFund / stakePeriod);
preSaleData = IpreSale(_preSaleData);
rewardRateHistory.push(RewardRateChange({timestamp: _timestamp, rewardRate: rewardRate, totalStaked: minStake }));
}
function getStakes(
address user,
uint stakesIndex
) public view returns (Stake memory) {
return stakes[user][stakesIndex];
}
function setPresale(address _preSaleData) public onlyOwner{
preSaleData = IpreSale(_preSaleData);
}
function calculateReward(
address userAddress,
uint256 stakeIndex
) public view returns (uint256) {
Stake storage userStake = stakes[userAddress][stakeIndex];
uint rewardSum;
uint8 triger;
uint secSum;
for(uint i; i<rewardRateHistory.length; i++){
unchecked{
uint elapsedSeconds;
if(rewardRateHistory[i].timestamp >= userStake.lastClaimTimestamp){
triger++;
if(triger == 1){
uint ternarResult = i == rewardRateHistory.length - 1? block.timestamp + 1: rewardRateHistory[i + 1].timestamp;
elapsedSeconds = ternarResult - rewardRateHistory[i].timestamp;
secSum += elapsedSeconds;
uint rewardRateAct = rewardRateHistory[i].rewardRate * ((userStake.amount / (rewardRateHistory[i].totalStaked / 1e18)));
rewardSum += (rewardRateAct * elapsedSeconds) / 10**18;
} else if(i == rewardRateHistory.length - 1){
elapsedSeconds = block.timestamp + 1 - rewardRateHistory[i].timestamp;
secSum += elapsedSeconds;
uint rewardRateAct = rewardRateHistory[i].rewardRate * ((userStake.amount / (rewardRateHistory[i].totalStaked / 1e18)));
rewardSum += (rewardRateAct * elapsedSeconds) / 10**18;
} else {
elapsedSeconds = rewardRateHistory[i+1].timestamp - rewardRateHistory[i].timestamp;
secSum += elapsedSeconds;
uint rewardRateAct =rewardRateHistory[i].rewardRate * ((userStake.amount / (rewardRateHistory[i].totalStaked / 1e18)));
rewardSum += (rewardRateAct * elapsedSeconds) / 10**18;
}
}
}
}
if(secSum > stakingEndDate - rewardRateHistory[0].timestamp){
uint procentage = (secSum * 1e18) / (stakingEndDate - rewardRateHistory[0].timestamp);
return rewardSum * 1e18 / procentage;
}
return rewardSum;
}
function getProcentageOfStake(address _user) public view returns(uint){
return ((userTotalStaked[_user] / (totalStaked / 1e18))) * 100;
}
function getUserStakes(
address userAddress
) external view returns (Stake[] memory userStakes) {
return stakes[userAddress];
}
function setNewRewardToken(address newTokenAddress) external onlyOwner{
stakingToken = IERC20(newTokenAddress);
}
function setStakers(address[] memory _stakerAddress, uint[] memory _amount, uint[] memory timestamp) public onlyOwner{
require(_stakerAddress.length == _amount.length && _amount.length == timestamp.length, "All arrays length should be equal");
for(uint i; i<_stakerAddress.length; i++){
stakes[_stakerAddress[i]].push(Stake(_amount[i], timestamp[i], timestamp[i]));
userTotalStaked[_stakerAddress[i]] += _amount[i];
totalStaked += _amount[i];
rewardRateHistory.push(RewardRateChange({
timestamp: timestamp[i],
rewardRate: rewardRate,
totalStaked: totalStaked
}));
}
}
function transferPool() public onlyOwner{
stakingToken.transfer(owner(), totalFund);
}
function stake(uint256 _amount) public {
require(block.timestamp < stakingEndDate, "Staking had already end");
require(
_amount >= minStake,
"The amount must be greater than minimum 10 tokens"
);
require(
preSaleData.transferToStaking(_amount),
"Token transfer failed"
);
stakes[msg.sender].push(
Stake(_amount, block.timestamp, block.timestamp)
);
totalStaked += _amount;
userTotalStaked[msg.sender] += _amount;
emit Staked(msg.sender, _amount);
_checkRewardRate();
}
function claimReward(uint256 stakeIndex) public {
Stake storage userStake = stakes[msg.sender][stakeIndex];
uint256 rewardAmount = calculateReward(msg.sender, stakeIndex);
require(rewardAmount > 0, "No reward available");
userStake.lastClaimTimestamp = block.timestamp;
require(
stakingToken.transfer(msg.sender, rewardAmount),
"Token transfer failed"
);
totalFund -= rewardAmount;
_checkRewardRate();
emit Claimed(msg.sender, rewardAmount);
}
function claimAllRewards() external nonReentrant{
require(block.timestamp >= preSaleData.getEndTime(), "Early claim is not allowed");
Stake[] memory userStakes = stakes[msg.sender];
for (uint256 i; i < userStakes.length; i++) {
claimReward(i);
}
}
function claimAndStake() public nonReentrant {
require(block.timestamp >= preSaleData.getEndTime(), "Early claim is not allowed");
require(block.timestamp < stakingEndDate, "Staking had already end");
uint reward;
for (uint256 i; i < stakes[msg.sender].length; i++) {
reward += calculateReward(msg.sender, i);
require(reward >= minStake, "Reward is too less to stake");
require(reward > 0, "No reward available");
}
stakes[msg.sender].push(
Stake(reward, block.timestamp, block.timestamp)
);
totalFund -= reward;
totalStaked += reward;
userTotalStaked[msg.sender] += reward;
_checkRewardRate();
}
function withdraw() public nonReentrant {
require(block.timestamp >= preSaleData.getEndTime(), "Early claim is not allowed");
Stake[] storage userStake = stakes[msg.sender];
for (uint i; i < userStake.length; i++) {
uint256 rewardAmount = calculateReward(msg.sender, i);
require(
stakingToken.transfer(
msg.sender,
userStake[i].amount + rewardAmount
),
"Token transfer failed"
);
totalStaked -= userStake[i].amount;
userTotalStaked[msg.sender] -= userStake[i].amount;
totalFund -= rewardAmount;
emit Withdrawn(msg.sender, userStake[i].amount + rewardAmount);
delete stakes[msg.sender];
}
_checkRewardRate();
}
function updatePoolWith(uint _poolValue, uint _newStakePeriod) public onlyOwner{
unchecked{
for(uint i; i<rewardRateHistory.length; i++){
rewardRateHistory[i].rewardRate = (_poolValue/(_newStakePeriod * 1 days));
}
rewardRate = (_poolValue/(_newStakePeriod * 1 days));
stakingEndDate = rewardRateHistory[0].timestamp + (_newStakePeriod * 1 days);
}
}
function setMinStake(uint256 newMinStake) public onlyOwner {
minStake = newMinStake;
}
}
{
"compilationTarget": {
"contracts/stakingTest.sol": "DoggyAiStake"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"details": {
"constantOptimizer": true,
"cse": true,
"deduplicate": true,
"inliner": true,
"jumpdestRemover": true,
"orderLiterals": true,
"peephole": true,
"yul": true,
"yulDetails": {
"optimizerSteps": "u:fDnTOc",
"stackAllocation": true
}
},
"runs": 200
},
"remappings": [],
"viaIR": true
}
[{"inputs":[{"internalType":"address","name":"DoggyAi","type":"address"},{"internalType":"address","name":"initialOwner","type":"address"},{"internalType":"uint256","name":"_stakePeriod","type":"uint256"},{"internalType":"uint256","name":"amountToFill","type":"uint256"},{"internalType":"uint256","name":"_timestamp","type":"uint256"},{"internalType":"address","name":"_preSaleData","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Claimed","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":true,"internalType":"address","name":"","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdrawn","type":"event"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"},{"internalType":"uint256","name":"stakeIndex","type":"uint256"}],"name":"calculateReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimAllRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimAndStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"stakeIndex","type":"uint256"}],"name":"claimReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getProcentageOfStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"stakesIndex","type":"uint256"}],"name":"getStakes","outputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"lastClaimTimestamp","type":"uint256"}],"internalType":"struct DoggyAiStake.Stake","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"}],"name":"getUserStakes","outputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"lastClaimTimestamp","type":"uint256"}],"internalType":"struct DoggyAiStake.Stake[]","name":"userStakes","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardRateHistory","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"rewardRate","type":"uint256"},{"internalType":"uint256","name":"totalStaked","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMinStake","type":"uint256"}],"name":"setMinStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newTokenAddress","type":"address"}],"name":"setNewRewardToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_preSaleData","type":"address"}],"name":"setPresale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_stakerAddress","type":"address[]"},{"internalType":"uint256[]","name":"_amount","type":"uint256[]"},{"internalType":"uint256[]","name":"timestamp","type":"uint256[]"}],"name":"setStakers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakePeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakes","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"lastClaimTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingEndDate","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":"totalFund","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalStaked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"transferPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_poolValue","type":"uint256"},{"internalType":"uint256","name":"_newStakePeriod","type":"uint256"}],"name":"updatePoolWith","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userTotalStaked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]