Skip to content

Instantly share code, notes, and snippets.

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

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

Select an option

Save zulnabil/55aecc3eee151033826767dcbb7469c3 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 tokens to safely interact with ERC20 tokens.
using SafeERC20 for IERC20;
contract PredictionMarket {
// Enum to define the type of event: Single (yes/no) or Multiple (multiple outcomes).
enum EventType { Single, Multiple }
// Struct to represent a Bet in the market.
struct Bet {
uint256 eventId; // The ID of the event the bet is placed on.
uint256 outcome; // The chosen outcome of the bet.
uint256 amount; // The amount of tokens bet.
address payable user; // The address of the user who placed the bet.
address tokenAddress; // The address of the ERC20 token used for the bet.
bool isResolved; // Whether the bet has been resolved.
bool won; // Whether the bet was a winning bet.
}
// Struct to represent an Outcome within an event.
struct Outcome {
uint256 id; // The ID of the outcome.
string description; // Description of the outcome.
uint256 totalAmount; // Total amount bet on this outcome.
uint256 multiplier; // Multiplier for payout calculation.
string imageUrl; // Image URL representing the outcome.
}
// Struct to represent an Event in the prediction market.
struct Event {
string description; // Description of the event.
EventType eventType; // Type of the event (Single or Multiple).
bool isResolved; // Whether the event has been resolved.
uint256 winningOutcome; // The ID of the winning outcome.
Outcome[] outcomes; // List of outcomes for the event.
Bet[] bets; // List of bets placed on the event.
string imageUrl; // Image URL representing the event.
}
mapping(uint256 => Event) public events; // Mapping of event IDs to events.
uint256 public eventCount; // Total number of events created.
uint256 public betCount; // Total number of bets placed.
event EventCreated(uint256 indexed eventId, string description, string imageUrl, EventType eventType); // Event emitted when a new event is created.
event OutcomeAdded(uint256 indexed eventId, uint256 outcomeId, string description, string imageUrl); // Event emitted when a new outcome is added.
event BetPlaced(uint256 indexed eventId, uint256 indexed betId, uint256 outcome, uint256 amount, address user, address tokenAddress); // Event emitted when a bet is placed.
event EventResolved(uint256 indexed eventId, uint256 winningOutcome); // Event emitted when an event is resolved.
// Function to create a new event.
function createEvent(string memory description, string memory imageUrl, EventType eventType) public {
eventCount++; // Increment event count.
Event storage newEvent = events[eventCount]; // Create a new event.
newEvent.description = description;
newEvent.eventType = eventType;
newEvent.isResolved = false;
newEvent.winningOutcome = 0;
newEvent.imageUrl = imageUrl;
// If the event type is Single, add default outcomes "Yes" and "No".
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); // Emit event created event.
}
// Function to add an outcome to an existing event.
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; // Get the new outcome ID.
eventToUpdate.outcomes.push(Outcome({
id: outcomeId,
description: description,
totalAmount: 0,
multiplier: 0,
imageUrl: imageUrl
}));
emit OutcomeAdded(eventId, outcomeId, description, imageUrl); // Emit outcome added event.
}
// Function to place a bet on an event.
function placeBet(uint256 eventId, uint256 outcome, uint256 amount, address tokenAddress) public {
require(amount > 0, "Bet amount must be greater than zero"); // Ensure bet amount is greater than zero.
Event storage eventToBet = events[eventId];
require(!eventToBet.isResolved, "Event already resolved"); // Ensure event is not resolved.
require(outcome < eventToBet.outcomes.length, "Invalid outcome"); // Ensure outcome is valid.
IERC20 token = IERC20(tokenAddress); // Get the token contract.
uint256 allowance = token.allowance(msg.sender, address(this));
require(allowance >= amount, "Check the token allowance"); // Ensure sufficient allowance.
uint256 balance = token.balanceOf(msg.sender);
require(balance >= amount, "Insufficient token balance"); // Ensure sufficient token balance.
token.safeTransferFrom(msg.sender, address(this), amount); // Transfer tokens from the user to the contract.
// Create a new bet.
Bet memory newBet = Bet({
eventId: eventId,
outcome: outcome,
amount: amount,
user: payable(msg.sender),
tokenAddress: tokenAddress,
isResolved: false,
won: false
});
eventToBet.bets.push(newBet); // Add the bet to the event.
eventToBet.outcomes[outcome].totalAmount += amount; // Update the total amount for the outcome.
betCount++; // Increment the bet count.
_updateMultipliers(eventId); // Update the multipliers for the event.
emit BetPlaced(eventId, betCount, outcome, amount, msg.sender, tokenAddress); // Emit bet placed event.
}
// Internal function to update the multipliers for an event.
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 to resolve an event.
function resolveEvent(uint256 eventId, uint256 winningOutcome) public {
Event storage eventToResolve = events[eventId];
require(!eventToResolve.isResolved, "Event already resolved"); // Ensure event is not resolved.
require(winningOutcome < eventToResolve.outcomes.length, "Invalid outcome"); // Ensure winning outcome is valid.
eventToResolve.isResolved = true; // Mark event as resolved.
eventToResolve.winningOutcome = winningOutcome; // Set the winning outcome.
// Iterate through all bets and transfer rewards to winners.
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); // Emit event resolved event.
}
// Function to get all events.
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 to get a specific event by its ID.
function getEventById(uint256 eventId) public view returns (Event memory) {
require(eventId > 0 && eventId <= eventCount, "Event ID is out of range"); // Ensure event ID is valid.
return events[eventId];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment