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 draw/.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 draw/.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 draw/.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 draw/Clarinet.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[project]
name = "draw"
authors = []
description = ""
telemetry = true
requirements = []
cache_dir = "/home/runner/Toyin/Great-Height/draw/./.requirements"
boot_contracts = ["pox", "costs-v2", "bns"]
[contracts.raffle]
path = "contracts/raffle.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
79 changes: 79 additions & 0 deletions draw/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Raffle Smart Contract

## About

This smart contract implements a decentralized raffle system on the Stacks blockchain. It allows for the creation and management of raffles, ticket purchases, winner selection, and prize distribution.

## Features

•⁠ ⁠Raffle initialization with customizable parameters
•⁠ ⁠Ticket purchasing with limits
•⁠ ⁠Random winner selection
•⁠ ⁠Prize claiming
•⁠ ⁠Fee collection for the contract owner
•⁠ ⁠Raffle cancellation and refund functionality

## Contract Details

### Constants

•⁠ ⁠⁠ CONTRACT-OWNER ⁠: The owner of the contract (set to the contract deployer)
•⁠ ⁠Error codes for various scenarios (e.g., unauthorized access, insufficient balance)

### Data Variables

•⁠ ⁠⁠ raffle-state ⁠: Boolean indicating if a raffle is active
•⁠ ⁠⁠ ticket-cost ⁠: Cost of a single ticket (in microSTX)
•⁠ ⁠⁠ raffle-end-block ⁠: Block height at which the raffle ends
•⁠ ⁠⁠ winning-participant ⁠: The address of the winning participant
•⁠ ⁠⁠ prize-claim-status ⁠: Boolean indicating if the prize has been claimed
•⁠ ⁠⁠ minimum-participants ⁠: Minimum number of participants required for a valid raffle
•⁠ ⁠⁠ max-tickets-per-participant ⁠: Maximum number of tickets a single participant can purchase
•⁠ ⁠⁠ raffle-fee-rate ⁠: Percentage of the total pool taken as a fee (e.g., 5%)

### Maps

•⁠ ⁠⁠ participant-tickets ⁠: Tracks the number of tickets purchased by each participant
•⁠ ⁠⁠ participant-registry ⁠: Maps participant indices to their addresses
•⁠ ⁠⁠ participant-indices ⁠: Maps participant addresses to their indices

### Main Functions

1.⁠ ⁠⁠ initialize-raffle ⁠: Starts a new raffle with specified parameters
2.⁠ ⁠⁠ purchase-tickets ⁠: Allows participants to buy raffle tickets
3.⁠ ⁠⁠ conclude-raffle ⁠: Ends the raffle and selects a winner
4.⁠ ⁠⁠ claim-raffle-prize ⁠: Allows the winner to claim their prize
5.⁠ ⁠⁠ withdraw-raffle-fees ⁠: Enables the contract owner to withdraw collected fees
6.⁠ ⁠⁠ cancel-active-raffle ⁠: Cancels an active raffle if minimum participants aren't met
7.⁠ ⁠⁠ refund-participant-tickets ⁠: Refunds tickets if a raffle is cancelled

### Read-Only Functions

•⁠ ⁠⁠ get-ticket-price ⁠: Returns the current ticket price
•⁠ ⁠⁠ get-raffle-info ⁠: Provides information about the current raffle state
•⁠ ⁠⁠ get-participant-tickets ⁠: Returns the number of tickets owned by a participant
•⁠ ⁠⁠ get-winning-participant ⁠: Returns the address of the winning participant
•⁠ ⁠⁠ get-prize-info ⁠: Provides information about the prize status and total pool

## Usage

1.⁠ ⁠Deploy the contract to the Stacks blockchain.
2.⁠ ⁠The contract owner initializes a raffle using ⁠ initialize-raffle ⁠.
3.⁠ ⁠Participants purchase tickets using ⁠ purchase-tickets ⁠.
4.⁠ ⁠Once the raffle end block is reached, anyone can call ⁠ conclude-raffle ⁠ to select a winner.
5.⁠ ⁠The winner can claim their prize using ⁠ claim-raffle-prize ⁠.
6.⁠ ⁠The contract owner can withdraw fees using ⁠ withdraw-raffle-fees ⁠.

## Security Considerations

•⁠ ⁠The contract uses block height for randomness, which isn't cryptographically secure. In a production environment, consider using a more robust randomness source.
•⁠ ⁠There's no mechanism to update the contract owner. Consider implementing an ownership transfer function if needed.
•⁠ ⁠Ensure proper testing and auditing before deploying to mainnet.

## Development and Testing

To interact with and test this contract:

1.⁠ ⁠Use the [Clarinet](https://github.com/hirosystems/clarinet) development tool for local testing.
2.⁠ ⁠Deploy to testnet for further testing before mainnet deployment.
3.⁠ ⁠Use [Stacks.js](https://github.com/hirosystems/stacks.js) or other Stacks libraries to interact with the deployed contract.
225 changes: 225 additions & 0 deletions draw/contracts/raffle.clar
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
;; Enhanced Raffle Smart Contract with Improved Variable Names and Updated Constants

;; Constants
(define-constant CONTRACT-OWNER tx-sender)
(define-constant ERR-UNAUTHORIZED (err u100))
(define-constant ERR-RAFFLE-INACTIVE (err u101))
(define-constant ERR-RAFFLE-IN-PROGRESS (err u102))
(define-constant ERR-INSUFFICIENT-BALANCE (err u103))
(define-constant ERR-NO-PARTICIPANTS (err u104))
(define-constant ERR-RAFFLE-ENDED (err u105))
(define-constant ERR-INVALID-INPUT (err u106))
(define-constant ERR-NOT-WINNER (err u107))
(define-constant ERR-PRIZE-ALREADY-CLAIMED (err u108))

;; Data Variables
(define-data-var raffle-state bool false)
(define-data-var ticket-cost uint u1000000) ;; 1 STX
(define-data-var raffle-end-block uint u0)
(define-data-var winning-participant (optional principal) none)
(define-data-var prize-claim-status bool false)
(define-data-var minimum-participants uint u5)
(define-data-var max-tickets-per-participant uint u10)
(define-data-var raffle-fee-rate uint u5) ;; 5% fee

;; Maps
(define-map participant-tickets principal uint)
(define-map participant-registry {index: uint} {address: principal})
(define-map participant-indices principal uint)

;; Variables
(define-data-var participant-count uint u0)
(define-data-var total-tickets-sold uint u0)

;; Private Functions
(define-private (is-owner)
(is-eq tx-sender CONTRACT-OWNER)
)

(define-private (is-raffle-active)
(var-get raffle-state)
)

(define-private (register-new-participant (address principal))
(let (
(current-count (var-get participant-count))
(new-count (+ current-count u1))
)
(map-insert participant-registry {index: new-count} {address: address})
(map-insert participant-indices address new-count)
(var-set participant-count new-count)
)
)

(define-private (generate-random-number (seed uint))
(let (
(combined-value (+ (var-get participant-count) seed block-height))
(random-value (mod combined-value (var-get participant-count)))
)
(if (is-eq random-value u0)
u1
(+ random-value u1)
)
)
)

(define-private (calculate-raffle-fee (amount uint))
(/ (* amount (var-get raffle-fee-rate)) u100)
)

;; Public Functions
(define-public (initialize-raffle (duration uint) (price uint) (min-participants uint) (max-tickets uint))
(begin
(asserts! (is-owner) ERR-UNAUTHORIZED)
(asserts! (not (is-raffle-active)) ERR-RAFFLE-IN-PROGRESS)
(asserts! (> duration u0) ERR-INVALID-INPUT)
(asserts! (> price u0) ERR-INVALID-INPUT)
(asserts! (>= min-participants u2) ERR-INVALID-INPUT)
(asserts! (> max-tickets u0) ERR-INVALID-INPUT)
(var-set raffle-state true)
(var-set ticket-cost price)
(var-set raffle-end-block (+ block-height duration))
(var-set minimum-participants min-participants)
(var-set max-tickets-per-participant max-tickets)
(var-set prize-claim-status false)
(var-set participant-count u0)
(var-set total-tickets-sold u0)
(ok true)
)
)

(define-public (purchase-tickets (quantity uint))
(let (
(buyer tx-sender)
(price (var-get ticket-cost))
(total-purchase-cost (* price quantity))
(current-ticket-count (default-to u0 (map-get? participant-tickets buyer)))
(new-ticket-count (+ current-ticket-count quantity))
)
(asserts! (is-raffle-active) ERR-RAFFLE-INACTIVE)
(asserts! (<= block-height (var-get raffle-end-block)) ERR-RAFFLE-ENDED)
(asserts! (>= (stx-get-balance buyer) total-purchase-cost) ERR-INSUFFICIENT-BALANCE)
(asserts! (<= new-ticket-count (var-get max-tickets-per-participant)) ERR-INVALID-INPUT)
(try! (stx-transfer? total-purchase-cost buyer (as-contract tx-sender)))
(map-set participant-tickets buyer new-ticket-count)
(match (map-get? participant-indices buyer)
index true
(register-new-participant buyer)
)
(var-set total-tickets-sold (+ (var-get total-tickets-sold) quantity))
(ok new-ticket-count)
)
)

(define-public (conclude-raffle)
(begin
(asserts! (is-owner) ERR-UNAUTHORIZED)
(asserts! (is-raffle-active) ERR-RAFFLE-INACTIVE)
(asserts! (>= block-height (var-get raffle-end-block)) ERR-RAFFLE-INACTIVE)
(let (
(total-participants (var-get participant-count))
(random-seed block-height)
)
(asserts! (>= total-participants (var-get minimum-participants)) ERR-NO-PARTICIPANTS)
(let (
(winner-index (generate-random-number random-seed))
(winner-info (unwrap! (map-get? participant-registry {index: winner-index}) ERR-NO-PARTICIPANTS))
)
(var-set winning-participant (some (get address winner-info)))
(var-set raffle-state false)
(ok (get address winner-info))
)
)
)
)

(define-public (claim-raffle-prize)
(let (
(claimer tx-sender)
(winner (unwrap! (var-get winning-participant) ERR-NO-PARTICIPANTS))
)
(asserts! (is-eq claimer winner) ERR-NOT-WINNER)
(asserts! (not (var-get prize-claim-status)) ERR-PRIZE-ALREADY-CLAIMED)
(let (
(total-prize-pool (var-get total-tickets-sold))
(raffle-fee (calculate-raffle-fee total-prize-pool))
(winner-prize (- total-prize-pool raffle-fee))
)
(try! (as-contract (stx-transfer? winner-prize tx-sender winner)))
(var-set prize-claim-status true)
(ok winner-prize)
)
)
)

(define-read-only (get-ticket-price)
(ok (var-get ticket-cost))
)

(define-read-only (get-raffle-info)
(ok {
active: (var-get raffle-state),
end-block: (var-get raffle-end-block),
current-block: block-height,
total-tickets: (var-get total-tickets-sold),
participant-count: (var-get participant-count),
minimum-participants: (var-get minimum-participants),
max-tickets-per-participant: (var-get max-tickets-per-participant)
})
)

(define-read-only (get-participant-tickets (address principal))
(ok (default-to u0 (map-get? participant-tickets address)))
)

(define-read-only (get-winning-participant)
(ok (var-get winning-participant))
)

(define-read-only (get-prize-info)
(ok {
claimed: (var-get prize-claim-status),
total-prize-pool: (var-get total-tickets-sold)
})
)

(define-public (withdraw-raffle-fees)
(begin
(asserts! (is-owner) ERR-UNAUTHORIZED)
(asserts! (not (is-raffle-active)) ERR-RAFFLE-INACTIVE)
(asserts! (var-get prize-claim-status) ERR-PRIZE-ALREADY-CLAIMED)
(let (
(total-sales (var-get total-tickets-sold))
(fee (calculate-raffle-fee total-sales))
)
(try! (as-contract (stx-transfer? fee tx-sender CONTRACT-OWNER)))
(ok fee)
)
)
)

(define-public (cancel-active-raffle)
(begin
(asserts! (is-owner) ERR-UNAUTHORIZED)
(asserts! (is-raffle-active) ERR-RAFFLE-INACTIVE)
(asserts! (< (var-get participant-count) (var-get minimum-participants)) ERR-INVALID-INPUT)
(var-set raffle-state false)
(ok true)
)
)

(define-public (refund-participant-tickets)
(let (
(participant tx-sender)
(ticket-count (default-to u0 (map-get? participant-tickets participant)))
(refund-amount (* ticket-count (var-get ticket-cost)))
)
(asserts! (not (is-raffle-active)) ERR-RAFFLE-IN-PROGRESS)
(asserts! (> ticket-count u0) ERR-INVALID-INPUT)
(try! (as-contract (stx-transfer? refund-amount tx-sender participant)))
(map-delete participant-tickets participant)
(map-delete participant-indices participant)
(var-set total-tickets-sold (- (var-get total-tickets-sold) ticket-count))
(ok refund-amount)
)
)
Loading