Contract Name:
MarketPredictions
Contract Source Code:
File 1 of 1 : MarketPredictions
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract MarketPredictions {
// Inlined ReentrancyGuard from OpenZeppelin (simplified version)
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
struct Market {
string question;
uint256 totalPredictions;
uint256 totalYes;
uint256 totalNo;
bool resolved;
bool outcome;
mapping(address => PredictionInfo) userPredictions;
}
struct PredictionInfo {
uint256 amount;
bool choice;
bool claimed;
}
Market[] public markets;
uint256 public platformFee; // In basis points (e.g., 200 = 2%)
uint256 public platformBalance;
address public owner;
mapping(address => bool) public admins;
event MarketCreated(uint256 marketId, string question);
event PredictionPlaced(uint256 marketId, address predictor, bool choice, uint256 amount);
event MarketResolved(uint256 marketId, bool outcome);
event RewardClaimed(uint256 marketId, address winner, uint256 amount);
event FeeWithdrawn(uint256 amount);
event AdminAdded(address admin);
event AdminRemoved(address admin);
event UnclaimedFundsWithdrawn(uint256 marketId, uint256 amount);
modifier onlyOwner() {
require(msg.sender == owner, "Only owner");
_;
}
modifier onlyAdmin() {
require(admins[msg.sender] || msg.sender == owner, "Only admin");
_;
}
modifier nonReentrant() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
constructor() {
owner = msg.sender;
admins[msg.sender] = true;
platformFee = 200; // 2% fee in basis points
_status = _NOT_ENTERED; // Initialize reentrancy guard
}
function addAdmin(address _admin) external onlyOwner {
require(!admins[_admin], "Already admin");
admins[_admin] = true;
emit AdminAdded(_admin);
}
function removeAdmin(address _admin) external onlyOwner {
require(admins[_admin], "Not an admin");
require(_admin != owner, "Cannot remove owner");
admins[_admin] = false;
emit AdminRemoved(_admin);
}
function createMarket(string memory _question) external onlyAdmin {
Market storage newMarket = markets.push();
newMarket.question = _question;
newMarket.totalPredictions = 0;
newMarket.totalYes = 0;
newMarket.totalNo = 0;
newMarket.resolved = false;
emit MarketCreated(markets.length - 1, _question);
}
function makePrediction(uint256 _marketId, bool _choice) external payable {
require(_marketId < markets.length, "Invalid market ID");
require(!markets[_marketId].resolved, "Market resolved");
require(msg.value > 0, "Amount must be > 0");
uint256 fee = (msg.value * platformFee) / 10000;
uint256 predictionAmount = msg.value - fee;
platformBalance += fee;
Market storage market = markets[_marketId];
PredictionInfo storage prediction = market.userPredictions[msg.sender];
prediction.amount += predictionAmount;
prediction.choice = _choice;
market.totalPredictions += predictionAmount;
if (_choice) {
market.totalYes += predictionAmount;
} else {
market.totalNo += predictionAmount;
}
emit PredictionPlaced(_marketId, msg.sender, _choice, predictionAmount);
}
function resolveMarket(uint256 _marketId, bool _outcome) external onlyAdmin {
require(_marketId < markets.length, "Invalid market ID");
require(!markets[_marketId].resolved, "Already resolved");
Market storage market = markets[_marketId];
market.resolved = true;
market.outcome = _outcome;
emit MarketResolved(_marketId, _outcome);
}
function claimReward(uint256 _marketId) external nonReentrant {
require(_marketId < markets.length, "Invalid market ID");
Market storage market = markets[_marketId];
require(market.resolved, "Not resolved");
PredictionInfo storage prediction = market.userPredictions[msg.sender];
require(!prediction.claimed, "Already claimed");
require(prediction.amount > 0, "No prediction");
require(prediction.choice == market.outcome, "Incorrect prediction");
uint256 totalWinningPool = market.outcome ? market.totalYes : market.totalNo;
uint256 totalPool = market.totalPredictions;
uint256 reward = (prediction.amount * totalPool) / totalWinningPool;
prediction.claimed = true;
payable(msg.sender).transfer(reward);
emit RewardClaimed(_marketId, msg.sender, reward);
}
function withdrawFees() external onlyOwner {
uint256 amount = platformBalance;
require(amount > 0, "No fees to withdraw");
platformBalance = 0;
payable(owner).transfer(amount);
emit FeeWithdrawn(amount);
}
function withdrawUnclaimedFunds(uint256 _marketId) external onlyOwner {
require(_marketId < markets.length, "Invalid market ID");
Market storage market = markets[_marketId];
require(market.resolved, "Not resolved");
uint256 totalWinningPool = market.outcome ? market.totalYes : market.totalNo;
uint256 unclaimedAmount = market.totalPredictions - totalWinningPool;
require(unclaimedAmount > 0, "No unclaimed funds");
payable(owner).transfer(unclaimedAmount);
emit UnclaimedFundsWithdrawn(_marketId, unclaimedAmount);
}
function setPlatformFee(uint256 _newFee) external onlyOwner {
require(_newFee <= 1000, "Fee max 10%");
platformFee = _newFee;
}
function getMarket(uint256 _marketId) external view returns (
string memory question,
uint256 totalPredictions,
uint256 totalYes,
uint256 totalNo,
bool resolved,
bool outcome
) {
require(_marketId < markets.length, "Invalid market ID");
Market storage market = markets[_marketId];
return (
market.question,
market.totalPredictions,
market.totalYes,
market.totalNo,
market.resolved,
market.outcome
);
}
function getUserPrediction(uint256 _marketId, address _user) external view returns (
uint256 amount,
bool choice,
bool claimed
) {
require(_marketId < markets.length, "Invalid market ID");
Market storage market = markets[_marketId];
PredictionInfo storage prediction = market.userPredictions[_user];
return (
prediction.amount,
prediction.choice,
prediction.claimed
);
}
}