Created
June 26, 2024 18:02
-
-
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=
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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