Skip to content

Instantly share code, notes, and snippets.

@zulnabil
Created June 26, 2024 18:02
Show Gist options
  • Select an option

  • Save zulnabil/eb3ec4dd25401bab0635ca731b800cf7 to your computer and use it in GitHub Desktop.

Select an option

Save zulnabil/eb3ec4dd25401bab0635ca731b800cf7 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.26+commit.8a97fa7a.js&optimize=false&runs=200&gist=
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
using SafeERC20 for IERC20;
contract PredictionMarket {
enum EventType { Single, Multiple }
struct Bet {
uint256 eventId;
uint256 outcome;
uint256 amount;
address payable user;
address tokenAddress;
bool isResolved;
bool won;
}
struct Outcome {
uint256 id;
string description;
uint256 totalAmount;
uint256 multiplier;
string imageUrl;
}
struct Event {
string description;
EventType eventType;
bool isResolved;
uint256 winningOutcome;
Outcome[] outcomes;
Bet[] bets;
string imageUrl;
}
mapping(uint256 => Event) public events;
uint256 public eventCount;
uint256 public betCount;
event EventCreated(uint256 indexed eventId, string description, string imageUrl, EventType eventType);
event OutcomeAdded(uint256 indexed eventId, uint256 outcomeId, string description, string imageUrl);
event BetPlaced(uint256 indexed eventId, uint256 indexed betId, uint256 outcome, uint256 amount, address user, address tokenAddress);
event EventResolved(uint256 indexed eventId, uint256 winningOutcome);
function createEvent(string memory description, string memory imageUrl, EventType eventType) public {
eventCount++;
Event storage newEvent = events[eventCount];
newEvent.description = description;
newEvent.eventType = eventType;
newEvent.isResolved = false;
newEvent.winningOutcome = 0;
newEvent.imageUrl = imageUrl;
if (eventType == EventType.Single) {
newEvent.outcomes.push(Outcome({ id: 0, description: "Yes", totalAmount: 0, multiplier: 0, imageUrl: "" }));
newEvent.outcomes.push(Outcome({ id: 1, description: "No", totalAmount: 0, multiplier: 0, imageUrl: "" }));
}
emit EventCreated(eventCount, description, imageUrl, eventType);
}
function addOutcome(uint256 eventId, string memory description, string memory imageUrl) public {
Event storage eventToUpdate = events[eventId];
require(eventToUpdate.eventType == EventType.Multiple, "Only multiple option events can add outcomes");
uint256 outcomeId = eventToUpdate.outcomes.length;
eventToUpdate.outcomes.push(Outcome({
id: outcomeId,
description: description,
totalAmount: 0,
multiplier: 0,
imageUrl: imageUrl
}));
emit OutcomeAdded(eventId, outcomeId, description, imageUrl);
}
function placeBet(uint256 eventId, uint256 outcome, uint256 amount, address tokenAddress) public {
require(amount > 0, "Bet amount must be greater than zero");
Event storage eventToBet = events[eventId];
require(!eventToBet.isResolved, "Event already resolved");
require(outcome < eventToBet.outcomes.length, "Invalid outcome");
IERC20 token = IERC20(tokenAddress);
uint256 allowance = token.allowance(msg.sender, address(this));
require(allowance >= amount, "Check the token allowance");
uint256 balance = token.balanceOf(msg.sender);
require(balance >= amount, "Insufficient token balance");
token.safeTransferFrom(msg.sender, address(this), amount);
Bet memory newBet = Bet({
eventId: eventId,
outcome: outcome,
amount: amount,
user: payable(msg.sender),
tokenAddress: tokenAddress,
isResolved: false,
won: false
});
eventToBet.bets.push(newBet);
eventToBet.outcomes[outcome].totalAmount += amount;
betCount++;
_updateMultipliers(eventId);
emit BetPlaced(eventId, betCount, outcome, amount, msg.sender, tokenAddress);
}
function _updateMultipliers(uint256 eventId) internal {
Event storage eventToUpdate = events[eventId];
uint256 totalPool = 0;
// Calculate total pool amount
for (uint256 i = 0; i < eventToUpdate.outcomes.length; i++) {
totalPool += eventToUpdate.outcomes[i].totalAmount;
}
// Calculate multipliers
for (uint256 i = 0; i < eventToUpdate.outcomes.length; i++) {
if (eventToUpdate.outcomes[i].totalAmount > 0) {
eventToUpdate.outcomes[i].multiplier = totalPool * 1e18 / eventToUpdate.outcomes[i].totalAmount;
} else {
eventToUpdate.outcomes[i].multiplier = 0;
}
}
}
function resolveEvent(uint256 eventId, uint256 winningOutcome) public {
Event storage eventToResolve = events[eventId];
require(!eventToResolve.isResolved, "Event already resolved");
require(winningOutcome < eventToResolve.outcomes.length, "Invalid outcome");
eventToResolve.isResolved = true;
eventToResolve.winningOutcome = winningOutcome;
for (uint256 i = 0; i < eventToResolve.bets.length; i++) {
if (eventToResolve.bets[i].outcome == winningOutcome) {
eventToResolve.bets[i].won = true;
uint256 reward = (eventToResolve.bets[i].amount * eventToResolve.outcomes[winningOutcome].multiplier) / 1e18;
IERC20 token = IERC20(eventToResolve.bets[i].tokenAddress);
token.safeTransfer(eventToResolve.bets[i].user, reward);
}
eventToResolve.bets[i].isResolved = true;
}
emit EventResolved(eventId, winningOutcome);
}
function getEvents() public view returns (Event[] memory) {
Event[] memory result = new Event[](eventCount);
for (uint256 i = 1; i <= eventCount; i++) {
result[i - 1] = events[i];
}
return result;
}
function getEventById(uint256 eventId) public view returns (Event memory) {
require(eventId > 0 && eventId <= eventCount, "Event ID is out of range");
return events[eventId];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment