Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions predict/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

**/settings/Mainnet.toml
**/settings/Testnet.toml
.requirements/
history.txt
5 changes: 5 additions & 0 deletions predict/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

{
"deno.enable": true,
"files.eol": "\n"
}
18 changes: 18 additions & 0 deletions predict/.vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

{
"version": "2.0.0",
"tasks": [
{
"label": "check contracts",
"group": "test",
"type": "shell",
"command": "clarinet check"
},
{
"label": "test contracts",
"group": "test",
"type": "shell",
"command": "clarinet test"
}
]
}
23 changes: 23 additions & 0 deletions predict/Clarinet.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[project]
name = "predict"
authors = []
description = ""
telemetry = true
requirements = []
cache_dir = "/home/runner/hadassah/High-level/predict/./.requirements"
boot_contracts = ["pox", "costs-v2", "bns"]
[contracts.prediction]
path = "contracts/prediction.clar"

[repl]
costs_version = 2
parser_version = 2

[repl.analysis]
passes = ["check_checker"]

[repl.analysis.check_checker]
strict = false
trusted_sender = false
trusted_caller = false
callee_filter = false
128 changes: 128 additions & 0 deletions predict/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Prediction Market Smart Contract

## ABOUT

This smart contract implements a decentralized prediction market on the Stacks blockchain. Users can create markets, place bets, resolve markets, and claim payouts. The contract is designed to be fair, transparent, and efficient.

## Features

- Create prediction markets
- Place bets on market outcomes (Yes/No)
- Resolve markets
- Claim payouts for winning bets
- Fee system for contract sustainability

## Contract Details

- *Language*: Clarity
- *Blockchain*: Stacks
- *Minimum Bet*: 1 STX (1,000,000 microSTX)
- *Fee*: 0.5% of winnings

## Functions

### Public Functions

1. create-market: Create a new prediction market
2. place-bet: Place a bet on a market outcome
3. resolve-market: Resolve a market (only by market creator)
4. claim-payout: Claim winnings from a resolved market
5. withdraw-fees: Withdraw accumulated fees (only by contract owner)

### Read-only Functions

1. get-market-info: Get information about a specific market
2. get-user-bets: Get a user's bets for a specific market
3. get-accumulated-fees: Get the total accumulated fees

## Usage

### Creating a Market

To create a new prediction market:

clarity
(contract-call? .prediction-market create-market "Will it rain tomorrow?" u12345678)


- Parameters:
- Description (string-utf8 256): Market question or description
- Resolution timestamp (uint): Block height when the market will be resolved

### Placing a Bet

To place a bet on a market outcome:

clarity
(contract-call? .prediction-market place-bet u1 true u1000000)


- Parameters:
- Market ID (uint): The ID of the market
- Bet outcome (bool): true for "Yes", false for "No"
- Amount (uint): Bet amount in microSTX (minimum 1,000,000)

### Resolving a Market

To resolve a market (only by the market creator):

clarity
(contract-call? .prediction-market resolve-market u1 true)


- Parameters:
- Market ID (uint): The ID of the market
- Outcome (bool): true for "Yes", false for "No"

### Claiming a Payout

To claim a payout for a winning bet:

clarity
(contract-call? .prediction-market claim-payout u1)


- Parameters:
- Market ID (uint): The ID of the resolved market

### Withdrawing Fees

To withdraw accumulated fees (only by contract owner):

clarity
(contract-call? .prediction-market withdraw-fees)


## Error Codes

- ERR-UNAUTHORIZED (u100): Unauthorized action
- ERR-ALREADY-INITIALIZED (u101): Market already initialized
- ERR-NOT-INITIALIZED (u102): Market not initialized
- ERR-INVALID-BET (u103): Invalid bet parameters
- ERR-MARKET-CLOSED (u104): Market is closed for betting
- ERR-ALREADY-RESOLVED (u105): Market already resolved
- ERR-INSUFFICIENT-BALANCE (u106): Insufficient balance for bet
- ERR-NO-PAYOUT (u107): No payout available

## Security Considerations

- Only the market creator can resolve a market
- Markets can only be resolved after the specified resolution timestamp
- Minimum bet amount and description length to prevent spam
- Fee system to incentivize contract maintenance and prevent abuse

## Limitations

- Binary outcomes only (Yes/No)
- No partial withdrawals or bet cancellations
- Fixed fee percentage

## Future Improvements

- Multiple outcome support
- Dynamic fee adjustment
- Partial bet withdrawal
- Integration with external data sources for automated resolution

## AUTHOR
ESTHER OJO
188 changes: 188 additions & 0 deletions predict/contracts/prediction.clar
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
;; Prediction Market Smart Contract

;; Constants
(define-constant CONTRACT-OWNER tx-sender)
(define-constant ERR-UNAUTHORIZED (err u100))
(define-constant ERR-ALREADY-INITIALIZED (err u101))
(define-constant ERR-NOT-INITIALIZED (err u102))
(define-constant ERR-INVALID-BET (err u103))
(define-constant ERR-MARKET-CLOSED (err u104))
(define-constant ERR-ALREADY-RESOLVED (err u105))
(define-constant ERR-INSUFFICIENT-BALANCE (err u106))
(define-constant ERR-NO-PAYOUT (err u107))
(define-constant MINIMUM-BET-AMOUNT u1000000) ;; 1 STX
(define-constant FEE-PERCENTAGE u5) ;; 0.5%
(define-constant MIN-DESCRIPTION-LENGTH u10)

;; Data Variables
(define-data-var market-state (string-ascii 20) "uninitialized")
(define-data-var market-id uint u0)
(define-data-var market-creator principal CONTRACT-OWNER)
(define-data-var market-description (string-utf8 256) u"")
(define-data-var resolution-timestamp uint u0)
(define-data-var total-yes-amount uint u0)
(define-data-var total-no-amount uint u0)
(define-data-var market-outcome (optional bool) none)
(define-data-var accumulated-fees uint u0)

;; Data Maps
(define-map markets uint {
creator: principal,
description: (string-utf8 256),
resolution-timestamp: uint,
total-yes-amount: uint,
total-no-amount: uint,
outcome: (optional bool),
state: (string-ascii 20)
})

(define-map user-bets { market-id: uint, user: principal } { yes-amount: uint, no-amount: uint })

;; Private Functions
(define-private (is-valid-description (description (string-utf8 256)))
(>= (len description) MIN-DESCRIPTION-LENGTH)
)

(define-private (is-market-open (market-id-param uint))
(let ((market (unwrap! (map-get? markets market-id-param) false)))
(and
(is-eq (get state market) "active")
(< block-height (get resolution-timestamp market))
)
)
)

(define-private (calculate-payout (bet-amount uint) (winning-pool uint) (losing-pool uint))
(let (
(total-pool (+ winning-pool losing-pool))
(payout-ratio (/ (* bet-amount u100000000) winning-pool))
(gross-payout (/ (* total-pool payout-ratio) u100000000))
(fee (/ (* gross-payout FEE-PERCENTAGE) u1000))
)
(- gross-payout fee)
)
)

(define-private (transfer-token (amount uint) (sender principal) (recipient principal))
(begin
(try! (stx-transfer? amount sender recipient))
(ok true)
)
)

;; Public Functions
(define-public (create-market (description (string-utf8 256)) (resolution-time uint))
(let ((new-market-id (+ (var-get market-id) u1)))
(asserts! (> resolution-time block-height) ERR-INVALID-BET)
(asserts! (is-valid-description description) ERR-INVALID-BET)
(map-set markets new-market-id {
creator: tx-sender,
description: description,
resolution-timestamp: resolution-time,
total-yes-amount: u0,
total-no-amount: u0,
outcome: none,
state: "active"
})
(var-set market-id new-market-id)
(ok new-market-id)
)
)

(define-public (place-bet (market-id-param uint) (bet-yes bool) (amount uint))
(let (
(market (unwrap! (map-get? markets market-id-param) ERR-NOT-INITIALIZED))
(user-bet (default-to { yes-amount: u0, no-amount: u0 }
(map-get? user-bets { market-id: market-id-param, user: tx-sender })))
(user-balance (stx-get-balance tx-sender))
)
(asserts! (is-market-open market-id-param) ERR-MARKET-CLOSED)
(asserts! (>= amount MINIMUM-BET-AMOUNT) ERR-INVALID-BET)
(asserts! (>= user-balance amount) ERR-INSUFFICIENT-BALANCE)

(if bet-yes
(map-set markets market-id-param
(merge market { total-yes-amount: (+ (get total-yes-amount market) amount) }))
(map-set markets market-id-param
(merge market { total-no-amount: (+ (get total-no-amount market) amount) }))
)

(map-set user-bets { market-id: market-id-param, user: tx-sender }
(merge user-bet {
yes-amount: (if bet-yes (+ (get yes-amount user-bet) amount) (get yes-amount user-bet)),
no-amount: (if bet-yes (get no-amount user-bet) (+ (get no-amount user-bet) amount))
})
)

(try! (transfer-token amount tx-sender (as-contract tx-sender)))
(ok true)
)
)

(define-public (resolve-market (market-id-param uint) (outcome bool))
(let ((market (unwrap! (map-get? markets market-id-param) ERR-NOT-INITIALIZED)))
(asserts! (is-eq tx-sender (get creator market)) ERR-UNAUTHORIZED)
(asserts! (is-eq (get state market) "active") ERR-ALREADY-RESOLVED)
(asserts! (>= block-height (get resolution-timestamp market)) ERR-MARKET-CLOSED)

(map-set markets market-id-param
(merge market {
outcome: (some outcome),
state: "resolved"
})
)
(ok true)
)
)

(define-public (claim-payout (market-id-param uint))
(let (
(market (unwrap! (map-get? markets market-id-param) ERR-NOT-INITIALIZED))
(user-bet (unwrap! (map-get? user-bets { market-id: market-id-param, user: tx-sender }) ERR-INVALID-BET))
(outcome (unwrap! (get outcome market) ERR-NOT-INITIALIZED))
)
(asserts! (is-eq (get state market) "resolved") ERR-NOT-INITIALIZED)

(let (
(winning-amount (if outcome (get yes-amount user-bet) (get no-amount user-bet)))
(winning-pool (if outcome (get total-yes-amount market) (get total-no-amount market)))
(losing-pool (if outcome (get total-no-amount market) (get total-yes-amount market)))
)
(asserts! (> winning-amount u0) ERR-NO-PAYOUT)

(let (
(payout (calculate-payout winning-amount winning-pool losing-pool))
)
(map-delete user-bets { market-id: market-id-param, user: tx-sender })
(var-set accumulated-fees (+ (var-get accumulated-fees) (- (+ winning-pool losing-pool) payout)))
(try! (as-contract (transfer-token payout tx-sender tx-sender)))
(ok payout)
)
)
)
)

(define-public (withdraw-fees)
(begin
(asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-UNAUTHORIZED)
(let ((fees (var-get accumulated-fees)))
(var-set accumulated-fees u0)
(try! (as-contract (transfer-token fees tx-sender CONTRACT-OWNER)))
(ok fees)
)
)
)

;; Read-only Functions
(define-read-only (get-market-info (market-id-param uint))
(ok (unwrap! (map-get? markets market-id-param) ERR-NOT-INITIALIZED))
)

(define-read-only (get-user-bets (market-id-param uint) (user principal))
(ok (default-to { yes-amount: u0, no-amount: u0 }
(map-get? user-bets { market-id: market-id-param, user: user })))
)

(define-read-only (get-accumulated-fees)
(ok (var-get accumulated-fees))
)
Loading