Source Code

;; VoteStack - Simplified Voting Platform
;; 20 predefined polls, unlimited voting

;; Constants
(define-constant contract-owner tx-sender)
(define-constant err-not-found (err u404))
(define-constant err-invalid-option (err u400))

;; Data Maps
(define-map polls
  { poll-id: uint }
  {
    title: (string-ascii 200),
    option-a: (string-ascii 100),
    option-b: (string-ascii 100),
    option-a-image: (string-ascii 500),
    option-b-image: (string-ascii 500),
    votes-a: uint,
    votes-b: uint,
    total-votes: uint,
    category: (string-ascii 50)
  }
)

;; Track individual votes (for leaderboard)
(define-map user-vote-count
  { voter: principal }
  { total-votes: uint }
)

;; Track votes per poll per user (to show what they voted for)
(define-map user-poll-votes
  { poll-id: uint, voter: principal }
  { option: uint, vote-count: uint }
)

;; Read-only functions
(define-read-only (get-poll (poll-id uint))
  (ok (map-get? polls { poll-id: poll-id }))
)

(define-read-only (get-user-vote-count (voter principal))
  (default-to { total-votes: u0 } (map-get? user-vote-count { voter: voter }))
)

(define-read-only (get-user-poll-vote (poll-id uint) (voter principal))
  (ok (map-get? user-poll-votes { poll-id: poll-id, voter: voter }))
)

;; Public function - Vote (can vote multiple times)
(define-public (vote (poll-id uint) (option uint))
  (let
    (
      (voter tx-sender)
      (poll-data (unwrap! (map-get? polls { poll-id: poll-id }) err-not-found))
      (current-user-votes (get total-votes (get-user-vote-count voter)))
    )
    ;; Check valid option (1 or 2)
    (asserts! (or (is-eq option u1) (is-eq option u2)) err-invalid-option)
    
    ;; Update poll vote counts
    (if (is-eq option u1)
      (map-set polls
        { poll-id: poll-id }
        (merge poll-data {
          votes-a: (+ (get votes-a poll-data) u1),
          total-votes: (+ (get total-votes poll-data) u1)
        })
      )
      (map-set polls
        { poll-id: poll-id }
        (merge poll-data {
          votes-b: (+ (get votes-b poll-data) u1),
          total-votes: (+ (get total-votes poll-data) u1)
        })
      )
    )
    
    ;; Update user total vote count
    (map-set user-vote-count
      { voter: voter }
      { total-votes: (+ current-user-votes u1) }
    )
    
    ;; Track what user voted for in this poll
    (match (map-get? user-poll-votes { poll-id: poll-id, voter: voter })
      existing
        (map-set user-poll-votes
          { poll-id: poll-id, voter: voter }
          {
            option: option,
            vote-count: (+ (get vote-count existing) u1)
          }
        )
      (map-set user-poll-votes
        { poll-id: poll-id, voter: voter }
        { option: option, vote-count: u1 }
      )
    )
    
    (ok true)
  )
)

;; Initialize function - creates 20 polls (call once after deployment)
(define-public (initialize)
  (begin
    (asserts! (is-eq tx-sender contract-owner) (err u401))
    
    ;; Poll 1: iPhone vs Android
    (map-set polls { poll-id: u1 } {
      title: "Which smartphone is better?",
      option-a: "iPhone",
      option-b: "Android",
      option-a-image: "https://images.unsplash.com/photo-1592286927505-2fd5ba000e35?w=800",
      option-b-image: "https://images.unsplash.com/photo-1607252650355-f7fd0460ccdb?w=800",
      votes-a: u0, votes-b: u0, total-votes: u0,
      category: "Technology"
    })
    
    ;; Poll 2: Coffee vs Tea
    (map-set polls { poll-id: u2 } {
      title: "Your morning drink?",
      option-a: "Coffee",
      option-b: "Tea",
      option-a-image: "https://images.unsplash.com/photo-1495474472287-4d71bcdd2085?w=800",
      option-b-image: "https://images.unsplash.com/photo-1564890369478-c89ca6d9cde9?w=800",
      votes-a: u0, votes-b: u0, total-votes: u0,
      category: "Food & Drink"
    })
    
    ;; Poll 3: Dogs vs Cats
    (map-set polls { poll-id: u3 } {
      title: "Better pet?",
      option-a: "Dogs",
      option-b: "Cats",
      option-a-image: "https://images.unsplash.com/photo-1587300003388-59208cc962cb?w=800",
      option-b-image: "https://images.unsplash.com/photo-1514888286974-6c03e2ca1dba?w=800",
      votes-a: u0, votes-b: u0, total-votes: u0,
      category: "Pets"
    })
    
    ;; Poll 4: Pizza vs Burger
    (map-set polls { poll-id: u4 } {
      title: "Ultimate comfort food?",
      option-a: "Pizza",
      option-b: "Burger",
      option-a-image: "https://images.unsplash.com/photo-1513104890138-7c749659a591?w=800",
      option-b-image: "https://images.unsplash.com/photo-1568901346375-23c9450c58cd?w=800",
      votes-a: u0, votes-b: u0, total-votes: u0,
      category: "Food & Drink"
    })
    
    ;; Poll 5: Bitcoin vs Ethereum
    (map-set polls { poll-id: u5 } {
      title: "Top cryptocurrency?",
      option-a: "Bitcoin",
      option-b: "Ethereum",
      option-a-image: "https://images.unsplash.com/photo-1518546305927-5a555bb7020d?w=800",
      option-b-image: "https://images.unsplash.com/photo-1622630998477-20aa696ecb05?w=800",
      votes-a: u0, votes-b: u0, total-votes: u0,
      category: "Crypto"
    })
    
    ;; Poll 6: Netflix vs YouTube
    (map-set polls { poll-id: u6 } {
      title: "Best streaming platform?",
      option-a: "Netflix",
      option-b: "YouTube",
      option-a-image: "https://images.unsplash.com/photo-1574375927938-d5a98e8ffe85?w=800",
      option-b-image: "https://images.unsplash.com/photo-1611162616475-46b635cb6868?w=800",
      votes-a: u0, votes-b: u0, total-votes: u0,
      category: "Entertainment"
    })
    
    ;; Poll 7: PlayStation vs Xbox
    (map-set polls { poll-id: u7 } {
      title: "Gaming console of choice?",
      option-a: "PlayStation",
      option-b: "Xbox",
      option-a-image: "https://images.unsplash.com/photo-1606144042614-b2417e99c4e3?w=800",
      option-b-image: "https://images.unsplash.com/photo-1621259182978-fbf93132d53d?w=800",
      votes-a: u0, votes-b: u0, total-votes: u0,
      category: "Gaming"
    })
    
    ;; Poll 8: Summer vs Winter
    (map-set polls { poll-id: u8 } {
      title: "Favorite season?",
      option-a: "Summer",
      option-b: "Winter",
      option-a-image: "https://images.unsplash.com/photo-1473496169904-658ba7c44d8a?w=800",
      option-b-image: "https://images.unsplash.com/photo-1491002052546-bf38f186af56?w=800",
      votes-a: u0, votes-b: u0, total-votes: u0,
      category: "Lifestyle"
    })
    
    ;; Poll 9: Nike vs Adidas
    (map-set polls { poll-id: u9 } {
      title: "Better sports brand?",
      option-a: "Nike",
      option-b: "Adidas",
      option-a-image: "https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=800",
      option-b-image: "https://images.unsplash.com/photo-1556906781-9a412961c28c?w=800",
      votes-a: u0, votes-b: u0, total-votes: u0,
      category: "Fashion"
    })
    
    ;; Poll 10: Beach vs Mountains
    (map-set polls { poll-id: u10 } {
      title: "Perfect vacation spot?",
      option-a: "Beach",
      option-b: "Mountains",
      option-a-image: "https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=800",
      option-b-image: "https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800",
      votes-a: u0, votes-b: u0, total-votes: u0,
      category: "Travel"
    })
    
    ;; Poll 11: Mac vs Windows
    (map-set polls { poll-id: u11 } {
      title: "Best operating system?",
      option-a: "Mac",
      option-b: "Windows",
      option-a-image: "https://images.unsplash.com/photo-1517694712202-14dd9538aa97?w=800",
      option-b-image: "https://images.unsplash.com/photo-1587831990711-23ca6441447b?w=800",
      votes-a: u0, votes-b: u0, total-votes: u0,
      category: "Technology"
    })
    
    ;; Poll 12: Coke vs Pepsi
    (map-set polls { poll-id: u12 } {
      title: "Cola preference?",
      option-a: "Coke",
      option-b: "Pepsi",
      option-a-image: "https://images.unsplash.com/photo-1554866585-cd94860890b7?w=800",
      option-b-image: "https://images.unsplash.com/photo-1629203851122-3726ecdf080e?w=800",
      votes-a: u0, votes-b: u0, total-votes: u0,
      category: "Food & Drink"
    })
    
    ;; Poll 13: Morning vs Night
    (map-set polls { poll-id: u13 } {
      title: "When are you most productive?",
      option-a: "Morning Person",
      option-b: "Night Owl",
      option-a-image: "https://images.unsplash.com/photo-1470252649378-9c29740c9fa8?w=800",
      option-b-image: "https://images.unsplash.com/photo-1519681393784-d120267933ba?w=800",
      votes-a: u0, votes-b: u0, total-votes: u0,
      category: "Lifestyle"
    })
    
    ;; Poll 14: Tesla vs BMW
    (map-set polls { poll-id: u14 } {
      title: "Dream car brand?",
      option-a: "Tesla",
      option-b: "BMW",
      option-a-image: "https://images.unsplash.com/photo-1560958089-b8a1929cea89?w=800",
      option-b-image: "https://images.unsplash.com/photo-1555215695-3004980ad54e?w=800",
      votes-a: u0, votes-b: u0, total-votes: u0,
      category: "Automotive"
    })
    
    ;; Poll 15: Instagram vs TikTok
    (map-set polls { poll-id: u15 } {
      title: "Social media platform?",
      option-a: "Instagram",
      option-b: "TikTok",
      option-a-image: "https://images.unsplash.com/photo-1611162617474-5b21e879e113?w=800",
      option-b-image: "https://images.unsplash.com/photo-1611605698335-8b1569810432?w=800",
      votes-a: u0, votes-b: u0, total-votes: u0,
      category: "Social Media"
    })
    
    ;; Poll 16: Football vs Basketball
    (map-set polls { poll-id: u16 } {
      title: "Favorite sport to watch?",
      option-a: "Football",
      option-b: "Basketball",
      option-a-image: "https://images.unsplash.com/photo-1579952363873-27f3bade9f55?w=800",
      option-b-image: "https://images.unsplash.com/photo-1546519638-68e109498ffc?w=800",
      votes-a: u0, votes-b: u0, total-votes: u0,
      category: "Sports"
    })
    
    ;; Poll 17: Gym vs Running
    (map-set polls { poll-id: u17 } {
      title: "Preferred workout?",
      option-a: "Gym",
      option-b: "Running",
      option-a-image: "https://images.unsplash.com/photo-1534438327276-14e5300c3a48?w=800",
      option-b-image: "https://images.unsplash.com/photo-1552674605-db6ffd4facb5?w=800",
      votes-a: u0, votes-b: u0, total-votes: u0,
      category: "Fitness"
    })
    
    ;; Poll 18: Rock vs Hip Hop
    (map-set polls { poll-id: u18 } {
      title: "Music genre?",
      option-a: "Rock",
      option-b: "Hip Hop",
      option-a-image: "https://images.unsplash.com/photo-1498038432885-c6f3f1b912ee?w=800",
      option-b-image: "https://images.unsplash.com/photo-1571330735066-03aaa9429d89?w=800",
      votes-a: u0, votes-b: u0, total-votes: u0,
      category: "Music"
    })
    
    ;; Poll 19: New York vs LA
    (map-set polls { poll-id: u19 } {
      title: "Dream city to live in?",
      option-a: "New York",
      option-b: "Los Angeles",
      option-a-image: "https://images.unsplash.com/photo-1496442226666-8d4d0e62e6e9?w=800",
      option-b-image: "https://images.unsplash.com/photo-1580655653885-65763b2597d0?w=800",
      votes-a: u0, votes-b: u0, total-votes: u0,
      category: "Cities"
    })
    
    ;; Poll 20: Car vs Motorcycle
    (map-set polls { poll-id: u20 } {
      title: "Transportation preference?",
      option-a: "Car",
      option-b: "Motorcycle",
      option-a-image: "https://images.unsplash.com/photo-1492144534655-ae79c964c9d7?w=800",
      option-b-image: "https://images.unsplash.com/photo-1558981408-db0ecd8a1ee4?w=800",
      votes-a: u0, votes-b: u0, total-votes: u0,
      category: "Transport"
    })
    
    (ok true)
  )
)

Functions (5)

FunctionAccessArgs
get-pollread-onlypoll-id: uint
get-user-vote-countread-onlyvoter: principal
get-user-poll-voteread-onlypoll-id: uint, voter: principal
votepublicpoll-id: uint, option: uint
initializepublic