编译器
0.8.11+commit.d7f03943
文件 1 的 5:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address who) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(
address from,
address to,
uint256 value
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 2 的 5:IOwnable.sol
pragma solidity ^0.8.0;
interface IOwnable {
function owner() external view returns (address);
function setOwner(address _newOwner) external;
event OwnerUpdated(address indexed prevOwner, address indexed newOwner);
}
文件 3 的 5:MatchStakingPoolV3.sol
pragma solidity ^0.8.11;
import "@thirdweb-dev/contracts/eip/interface/IERC20.sol";
import "@thirdweb-dev/contracts/extension/Ownable.sol";
contract MatchStakingPoolV3 is Ownable {
enum Winner { Pending, Home, Away, Draw }
struct Stake {
uint256 amount;
bool claimed;
}
struct TeamClaim {
uint256 amount;
bool claimed;
}
struct Claim {
TeamClaim home;
TeamClaim away;
}
IERC20 public token;
uint256 public startDate;
uint256 public endDate;
uint256 public totalRewards;
uint8 public feePercentage;
Winner public winner;
address admin;
mapping(address => mapping(Winner => Stake)) public stakes;
mapping(Winner => uint256) public totalStaked;
event Deposited(uint256 amount);
event Withdrawn(uint256 amount);
event Staked(address indexed user, uint256 amount, Winner prediction);
event Unstaked(address indexed user, uint256 amount, Winner prediction);
event WinnerDeclared(Winner winner);
event Claimed(address indexed user, uint256 amount);
constructor(
uint256 _startDate,
uint256 _endDate,
uint8 _feePercentage,
address _token,
address _admin
) {
require(_startDate < _endDate, "Start date must be before end date");
require(_feePercentage <= 100, "Invalid fee percentage");
startDate = _startDate;
endDate = _endDate;
feePercentage = _feePercentage;
token = IERC20(_token);
winner = Winner.Pending;
admin = _admin;
_setupOwner(msg.sender);
}
function getClaim(address user) public view returns (Claim memory) {
Claim memory userClaim = Claim(TeamClaim(0, false), TeamClaim(0, false));
if (winner == Winner.Pending) {
return userClaim;
}
Stake storage homeStake = stakes[user][Winner.Home];
userClaim.home.claimed = homeStake.claimed;
if (homeStake.amount > 0) {
uint256 fee = (homeStake.amount * feePercentage) / 100;
uint256 reward = 0;
if (winner == Winner.Home) {
uint256 winnerTotalStaked = totalStaked[winner];
reward = (totalRewards * homeStake.amount) / winnerTotalStaked;
}
userClaim.home.amount = homeStake.amount - fee + reward;
}
Stake storage awayStake = stakes[user][Winner.Away];
userClaim.away.claimed = awayStake.claimed;
if (awayStake.amount > 0) {
uint256 fee = (awayStake.amount * feePercentage) / 100;
uint256 reward = 0;
if (winner == Winner.Away) {
uint256 winnerTotalStaked = totalStaked[winner];
reward = (totalRewards * awayStake.amount) / winnerTotalStaked;
}
userClaim.away.amount = awayStake.amount - fee + reward;
}
return userClaim;
}
function deposit(uint256 amount) external onlyOwner {
require(block.timestamp < startDate, "Deposit period is over");
totalRewards += amount;
require(token.transferFrom(admin, address(this), amount), "Deposit failed");
emit Deposited(amount);
}
function withdraw(uint256 amount) external onlyOwner {
require(block.timestamp < startDate, "Withdraw period is over");
totalRewards -= amount;
require(token.transfer(admin, amount), "Withdraw failed");
emit Withdrawn(amount);
}
function declareWinner(Winner _winner) external onlyOwner {
require(block.timestamp > endDate, "Match not yet ended");
require(_winner == Winner.Home || _winner == Winner.Away || _winner == Winner.Draw, "Invalid winner");
winner = _winner;
emit WinnerDeclared(_winner);
}
function claimRewards() external onlyOwner {
require(block.timestamp > endDate, "Claim period not yet started");
require(winner == Winner.Draw, "Invalid winner for claiming rewards");
require(token.transfer(admin, totalRewards), "Claim transfer failed");
emit Claimed(admin, totalRewards);
}
function setStake(address user, uint256 desiredHomeAmount, uint256 desiredAwayAmount) external onlyOwner {
require(block.timestamp < startDate, "Staking period is over");
Stake storage homeStake = stakes[user][Winner.Home];
Stake storage awayStake = stakes[user][Winner.Away];
int256 homeDifference = int256(desiredHomeAmount) - int256(homeStake.amount);
int256 awayDifference = int256(desiredAwayAmount) - int256(awayStake.amount);
int256 netDifference = homeDifference + awayDifference;
if (homeDifference != 0) {
if (homeDifference > 0) {
homeStake.amount += uint256(homeDifference);
totalStaked[Winner.Home] += uint256(homeDifference);
totalRewards += (uint256(homeDifference) * feePercentage) / 100;
emit Staked(user, uint256(homeDifference), Winner.Home);
} else {
uint256 reduceAmount = uint256(-homeDifference);
require(homeStake.amount >= reduceAmount, "Insufficient staked amount for home");
homeStake.amount -= reduceAmount;
totalStaked[Winner.Home] -= reduceAmount;
totalRewards -= (reduceAmount * feePercentage) / 100;
emit Unstaked(user, reduceAmount, Winner.Home);
}
}
if (awayDifference != 0) {
if (awayDifference > 0) {
awayStake.amount += uint256(awayDifference);
totalStaked[Winner.Away] += uint256(awayDifference);
totalRewards += (uint256(awayDifference) * feePercentage) / 100;
emit Staked(user, uint256(awayDifference), Winner.Away);
} else {
uint256 reduceAmount = uint256(-awayDifference);
require(awayStake.amount >= reduceAmount, "Insufficient staked amount for away");
awayStake.amount -= reduceAmount;
totalStaked[Winner.Away] -= reduceAmount;
totalRewards -= (reduceAmount * feePercentage) / 100;
emit Unstaked(user, reduceAmount, Winner.Away);
}
}
if (netDifference > 0) {
require(token.transferFrom(user, address(this), uint256(netDifference)),
"Stake transfer failed");
} else if (netDifference < 0) {
require(token.transfer(user, uint256(-netDifference)),
"Unstake transfer failed");
}
}
function claim(address user) external onlyOwner {
require(block.timestamp > endDate, "Claim period not yet started");
require(winner != Winner.Pending, "Winner not declared yet");
uint256 totalClaimAmount = 0;
Claim memory userClaim = getClaim(user);
if (!userClaim.home.claimed) {
totalClaimAmount += userClaim.home.amount;
stakes[user][Winner.Home].claimed = true;
}
if (!userClaim.away.claimed) {
totalClaimAmount += userClaim.away.amount;
stakes[user][Winner.Away].claimed = true;
}
require(totalClaimAmount > 0, "No valid claims to process");
require(token.transfer(user, totalClaimAmount), "Claim transfer failed");
emit Claimed(user, totalClaimAmount);
}
function _canSetOwner() internal view virtual override returns (bool) {
return msg.sender == owner();
}
}
文件 4 的 5:MatchStakingPoolV3Factory.sol
pragma solidity ^0.8.11;
import "./MatchStakingPoolV3.sol";
import "@thirdweb-dev/contracts/extension/Ownable.sol";
contract MatchStakingPoolV3Factory is Ownable {
event PoolCreated(address indexed poolAddress, uint256 startDate, uint256 endDate);
mapping(address => bool) public pools;
constructor() {
_setupOwner(msg.sender);
}
function deploy(
uint256 _startDate,
uint256 _endDate,
uint8 _feePercentage,
address _token
) external onlyOwner returns (address) {
MatchStakingPoolV3 pool = new MatchStakingPoolV3(
_startDate,
_endDate,
_feePercentage,
_token,
msg.sender
);
pools[address(pool)] = true;
emit PoolCreated(address(pool), _startDate, _endDate);
return address(pool);
}
function deposit(address poolAddress, uint256 amount) external onlyOwner {
require(pools[poolAddress], "Invalid pool");
MatchStakingPoolV3(poolAddress).deposit(amount);
}
function withdraw(address poolAddress, uint256 amount) external onlyOwner {
require(pools[poolAddress], "Invalid pool");
MatchStakingPoolV3(poolAddress).withdraw(amount);
}
function declareWinner(address poolAddress, MatchStakingPoolV3.Winner winner) external onlyOwner {
require(pools[poolAddress], "Invalid pool");
MatchStakingPoolV3(poolAddress).declareWinner(winner);
}
function claimRewards(address poolAddress) external onlyOwner {
require(pools[poolAddress], "Invalid pool");
MatchStakingPoolV3(poolAddress).claimRewards();
}
function setStake(address poolAddress, uint256 desiredHomeAmount, uint256 desiredAwayAmount) external {
require(pools[poolAddress], "Invalid pool");
MatchStakingPoolV3(poolAddress).setStake(msg.sender, desiredHomeAmount, desiredAwayAmount);
}
function claim(address poolAddress) external {
require(pools[poolAddress], "Invalid pool");
MatchStakingPoolV3(poolAddress).claim(msg.sender);
}
function _canSetOwner() internal view virtual override returns (bool) {
return msg.sender == owner();
}
}
文件 5 的 5:Ownable.sol
pragma solidity ^0.8.0;
import "./interface/IOwnable.sol";
abstract contract Ownable is IOwnable {
address private _owner;
modifier onlyOwner() {
if (msg.sender != _owner) {
revert("Not authorized");
}
_;
}
function owner() public view override returns (address) {
return _owner;
}
function setOwner(address _newOwner) external override {
if (!_canSetOwner()) {
revert("Not authorized");
}
_setupOwner(_newOwner);
}
function _setupOwner(address _newOwner) internal {
address _prevOwner = _owner;
_owner = _newOwner;
emit OwnerUpdated(_prevOwner, _newOwner);
}
function _canSetOwner() internal view virtual returns (bool);
}
{
"compilationTarget": {
"contracts/MatchStakingPoolV3Factory.sol": "MatchStakingPoolV3Factory"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"prevOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"poolAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"startDate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endDate","type":"uint256"}],"name":"PoolCreated","type":"event"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"}],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"enum MatchStakingPoolV3.Winner","name":"winner","type":"uint8"}],"name":"declareWinner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_startDate","type":"uint256"},{"internalType":"uint256","name":"_endDate","type":"uint256"},{"internalType":"uint8","name":"_feePercentage","type":"uint8"},{"internalType":"address","name":"_token","type":"address"}],"name":"deploy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"pools","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"uint256","name":"desiredHomeAmount","type":"uint256"},{"internalType":"uint256","name":"desiredAwayAmount","type":"uint256"}],"name":"setStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]