Source Code

;; GUSLAND v.1.0
;; guscoinstx.xyz & gusland.xyz

;; Storage
(define-map chosen-ids { number: uint } { round-id: uint, pick: uint, ticket: uint })
(define-map participants { round-id: uint, participant-ticket-id: uint } { ticket-id: uint, wallet: principal, block: uint })
(define-map winners { round-id: uint, winner-id: uint } { ticket-id: uint, winner-wallet: principal, block: uint })
(define-map players { round-id: uint, wallet: principal } { ticket-ids: (list 1000 uint) })

;; Constants and Errors
(define-constant CONTRACT-OWNER tx-sender)
(define-constant FEES-WALLET 'SP1CQ629JWQP67FA6A905NHFCV4ZDE9FT983CV3PZ)
(define-constant BURN-WALLET 'SP000000000000000000002Q6VF78)
(define-constant ERR-NOT-AUTHORIZED (err u101))
(define-constant ERR-SALE-NOT-ACTIVE (err u102))
(define-constant ERR-SALE-ACTIVE (err u103))
(define-constant REACHED-BLOCK-PICK-LIMIT (err u104))
(define-constant REACHED-WINNER-TICKETS-LIMIT (err u105))
(define-constant REACHED-LIMIT-TICKETS (err u106))
(define-constant ERR-SOLD-OUT (err u107))

;; Variables
(define-data-var sale-active bool false)
(define-data-var limit-tickets uint u0)
(define-data-var sold-tickets uint u0)
(define-data-var winners-limit uint u5)
(define-data-var winner-id uint u1)
(define-data-var round uint u1)
(define-data-var participant-ticket-id uint u0)
(define-data-var price uint u0)
(define-data-var mint-limit uint u0)
(define-data-var win-ticket uint u0)
(define-data-var last-block uint u0)
(define-data-var last-vrf (buff 64) 0x00)
(define-data-var b-idx uint u0)
(define-data-var ticket-id uint u0)
(define-data-var pick uint u1)

;; Get the mint limit
(define-read-only (get-mint-limit)
    (ok (var-get mint-limit)))

;; Check sales active
(define-read-only (sale-enabled)
    (ok (var-get sale-active)))

;; Get a price for ticket
(define-read-only (get-price-in-gus)
    (ok (var-get price)))

;; Get a round info
(define-read-only (get-round-info)
    (ok (var-get round)))

;; Get a number sold tickets
(define-read-only (get-sold-tickets)
    (ok (var-get sold-tickets)))

;; Get a prize pool
(define-read-only (get-prize-pool)
    (ok (* (var-get price) (var-get sold-tickets))))

;; Get a winner limit
(define-read-only (get-winners-limit)
    (ok (var-get winners-limit)))

;; Get participant by round-id & ticket-id
(define-read-only (get-participant-info (id-round uint) (id uint))
    (ok (map-get? participants (tuple (round-id id-round) (participant-ticket-id id))))
)

;; Get winner by round-id & id
(define-read-only (get-winner-info (id-round uint) (id uint))
    (ok (map-get? winners (tuple (round-id id-round) (winner-id id))))
)

;; Get player tickets by round-id
(define-read-only (get-player-tickets (id-round uint) (player-wallet principal))
    (ok (unwrap-panic (get ticket-ids (map-get? players (tuple (round-id id-round) (wallet player-wallet))))))
)

;; Get winners by round-id
(define-read-only (get-winners (id-round uint))
    (begin
        (print (map-get? winners (tuple (round-id id-round) (winner-id u1))))
        (print (map-get? winners (tuple (round-id id-round) (winner-id u2))))
        (print (map-get? winners (tuple (round-id id-round) (winner-id u3))))
        (print (map-get? winners (tuple (round-id id-round) (winner-id u4))))
        (print (map-get? winners (tuple (round-id id-round) (winner-id u5))))
        (ok true)
))

;; Set sale flag (only contract owner)
(define-public (flip-sale)
    (begin
        (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
        (var-set sale-active (not (var-get sale-active)))
    (ok (var-get sale-active))))

;; Set price (only contract owner)
(define-public (set-price-in-gus (new-price uint))
    (begin
        (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
        (var-set price new-price)
    (ok true)))

;; Set mint limit (only contract owner)
(define-public (set-mint-limit (limit uint))
    (begin
        (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
        (var-set mint-limit limit)
    (ok true)))

;; Claim ticket
(define-public (claim-ticket)
    (begin
        (asserts! (var-get sale-active) ERR-SALE-NOT-ACTIVE)
        (asserts! (< (var-get sold-tickets) (var-get mint-limit)) ERR-SOLD-OUT)
        (try! (contract-call? 'SP1JFFSYTSH7VBM54K29ZFS9H4SVB67EA8VT2MYJ9.gus-token transfer (var-get price) tx-sender (as-contract tx-sender) none))
        (map-set participants { round-id: (var-get round), participant-ticket-id: (var-get participant-ticket-id) } { ticket-id: (var-get participant-ticket-id), wallet: tx-sender, block: block-height })
            (if (is-none (get ticket-ids (map-get? players (tuple (round-id (var-get round)) (wallet tx-sender)))))
                (map-set players { round-id: (var-get round), wallet: tx-sender } { ticket-ids: (list (var-get participant-ticket-id)) })
                (let ((new-ticket-ids (unwrap-panic (as-max-len? (append (unwrap-panic (get ticket-ids (map-get? players (tuple (round-id (var-get round)) (wallet tx-sender))))) (var-get participant-ticket-id)) u1000))))
                (map-set players { round-id: (var-get round), wallet: tx-sender } { ticket-ids: new-ticket-ids }))
        )
        (var-set limit-tickets (+ (var-get limit-tickets) u1))
        (var-set sold-tickets (+ (var-get sold-tickets) u1))
        (print (map-get? participants (tuple (round-id (var-get round)) (participant-ticket-id (var-get participant-ticket-id)))))
        (var-set participant-ticket-id (+ (var-get participant-ticket-id) u1))
    (ok true))
)

;; Claim 5 tickets
(define-public (claim-five-tickets)
    (begin
        (try! (claim-ticket))
        (try! (claim-ticket))
        (try! (claim-ticket))
        (try! (claim-ticket))
        (try! (claim-ticket))
    (ok true)))

;; Claim 10 tickets
(define-public (claim-ten-tickets)
    (begin
        (try! (claim-ticket))
        (try! (claim-ticket))
        (try! (claim-ticket))
        (try! (claim-ticket))
        (try! (claim-ticket))
        (try! (claim-ticket))
        (try! (claim-ticket))
        (try! (claim-ticket))
        (try! (claim-ticket))
        (try! (claim-ticket))
    (ok true)))

;; Make a payment (only contract owner)
(define-public (finalize)
    (let
        ((prize-pool (* (var-get price) (var-get sold-tickets)))
        (winner-pool (/ (* u15 prize-pool) u100))
        (burn-pool (/ (* u15 prize-pool) u100))
        (fees (/ (* u10 prize-pool) u100)))
            (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
            (asserts! (not (var-get sale-active)) ERR-SALE-ACTIVE)
                (begin
                    (print "Winner #1")
                    (try! (as-contract (contract-call? 'SP1JFFSYTSH7VBM54K29ZFS9H4SVB67EA8VT2MYJ9.gus-token transfer winner-pool tx-sender (unwrap-panic (get winner-wallet (map-get? winners (tuple (round-id (var-get round)) (winner-id u1))))) none)))
                    (print "Winner #2")
                    (try! (as-contract (contract-call? 'SP1JFFSYTSH7VBM54K29ZFS9H4SVB67EA8VT2MYJ9.gus-token transfer winner-pool tx-sender (unwrap-panic (get winner-wallet (map-get? winners (tuple (round-id (var-get round)) (winner-id u2))))) none)))
                    (print "Winner #3")
                    (try! (as-contract (contract-call? 'SP1JFFSYTSH7VBM54K29ZFS9H4SVB67EA8VT2MYJ9.gus-token transfer winner-pool tx-sender (unwrap-panic (get winner-wallet (map-get? winners (tuple (round-id (var-get round)) (winner-id u3))))) none)))
                    (print "Winner #4")
                    (try! (as-contract (contract-call? 'SP1JFFSYTSH7VBM54K29ZFS9H4SVB67EA8VT2MYJ9.gus-token transfer winner-pool tx-sender (unwrap-panic (get winner-wallet (map-get? winners (tuple (round-id (var-get round)) (winner-id u4))))) none)))
                    (print "Winner #5")
                    (try! (as-contract (contract-call? 'SP1JFFSYTSH7VBM54K29ZFS9H4SVB67EA8VT2MYJ9.gus-token transfer winner-pool tx-sender (unwrap-panic (get winner-wallet (map-get? winners (tuple (round-id (var-get round)) (winner-id u5))))) none)))
                    (print "Burn")
                    (try! (as-contract (contract-call? 'SP1JFFSYTSH7VBM54K29ZFS9H4SVB67EA8VT2MYJ9.gus-token transfer burn-pool tx-sender BURN-WALLET none)))
                    (print "Service fees")
                    (try! (as-contract (contract-call? 'SP1JFFSYTSH7VBM54K29ZFS9H4SVB67EA8VT2MYJ9.gus-token transfer fees tx-sender FEES-WALLET none)))
                (ok true))))

;; Update for next round (only contract owner)
(define-public (update)
    (begin
        (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
        (asserts! (not (var-get sale-active)) ERR-SALE-ACTIVE)
        (var-set limit-tickets u0)
        (var-set sold-tickets u0)
        (var-set winner-id u1)
        (var-set participant-ticket-id u0)
        (var-set price u0)
        (var-set mint-limit u0)
        (var-set win-ticket u0)
        (var-set last-block u0)
        (var-set last-vrf 0x00)
        (var-set b-idx u0)
        (var-set ticket-id u0)
        (var-set winners-limit u5)
        (var-set pick u1)
        (var-set round (+ (var-get round) u1))
    (ok true)))

;; RNG MAGIC
;; Pick winners (only contract owner)
(define-public (pick-five-tickets)
    (begin
        (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
        (asserts! (not (var-get sale-active)) ERR-SALE-ACTIVE)
            (try! (pick-win-ticket))
            (try! (pick-win-ticket))
            (try! (pick-win-ticket))
            (try! (pick-win-ticket))
            (try! (pick-win-ticket))
    (ok true)))

(define-private (pick-win-ticket)
    (let ((limit-tickets-ids (var-get limit-tickets)))
        (asserts! (> limit-tickets-ids u0) REACHED-LIMIT-TICKETS)
        (asserts! (> (var-get winners-limit) u0) REACHED-WINNER-TICKETS-LIMIT)
        (let ((random-id (try! (cycle-random-id limit-tickets-ids))))
    (ok random-id))))

(define-private (swap-container (id uint) (idx uint) (ids-limit-tickets uint))
    (let ((top (- ids-limit-tickets u1))
          (top-id (default-to top (get ticket (map-get? chosen-ids { number: top })))))
        (map-set chosen-ids { number: top } { round-id: (var-get round), pick: (var-get pick), ticket: id })
        (map-set chosen-ids { number: idx } { round-id: (var-get round), pick: (var-get pick), ticket: top-id })
        (var-set limit-tickets top)))
      
(define-private (cycle-random-id (limit-tickets-ids uint))
     (let ((byte-idx (var-get b-idx)))
          (if (is-eq (var-get last-block) block-height)
              (begin
                  (asserts! (< byte-idx u62) REACHED-BLOCK-PICK-LIMIT)
                  (let ((picked-idx (mod (rand byte-idx) limit-tickets-ids))
                        (picked-id (default-to picked-idx (get ticket (map-get? chosen-ids { number: picked-idx })))))
                      (swap-container picked-id picked-idx limit-tickets-ids)
                      (var-set b-idx (+ byte-idx u2))
                      (var-set win-ticket picked-id)
                      (var-set pick (+ (var-get pick) u1))
                      (var-set winners-limit (- (var-get winners-limit) u1))
                      (map-set winners { round-id: (var-get round), winner-id: (var-get winner-id) } { ticket-id: (var-get win-ticket), winner-wallet: (unwrap-panic (get wallet (map-get? participants (tuple (round-id (var-get round)) (participant-ticket-id (var-get win-ticket)))))), block: block-height})
                      (print (map-get? winners (tuple (round-id (var-get round)) (winner-id (var-get winner-id)))))
                      (var-set winner-id (+ (var-get winner-id) u1))
                      (ok picked-id)))
              (begin
                  (set-vrf)
                  (let ((picked-idx (mod (rand byte-idx) limit-tickets-ids))
                        (picked-id (default-to picked-idx (get ticket (map-get? chosen-ids { number: picked-idx })))))
                      (var-set last-block block-height)
                      (swap-container picked-id picked-idx limit-tickets-ids)
                      (var-set b-idx u2)
                      (var-set win-ticket picked-id)
                      (var-set pick (+ (var-get pick) u1))
                      (var-set winners-limit (- (var-get winners-limit) u1))
                      (map-set winners { round-id: (var-get round), winner-id: (var-get winner-id) } { ticket-id: (var-get win-ticket), winner-wallet: (unwrap-panic (get wallet (map-get? participants (tuple (round-id (var-get round)) (participant-ticket-id (var-get win-ticket)))))), block: block-height})
                      (print (map-get? winners (tuple (round-id (var-get round)) (winner-id (var-get winner-id)))))
                      (var-set winner-id (+ (var-get winner-id) u1))
                      (ok picked-id))))))

(define-private (set-vrf)
    (var-set last-vrf (sha512 (unwrap-panic (get-block-info? vrf-seed (- block-height u1))))))

(define-private (rand (byte-idx uint))
    (let ((vrf (var-get last-vrf)) )
        (+ 
            (* (buff-to-uint-be (unwrap-panic (element-at vrf byte-idx))) u256)
            (buff-to-uint-be (unwrap-panic (element-at vrf (+ byte-idx u1))))
        )
    )
)

Functions (25)

FunctionAccessArgs
get-mint-limitread-only
sale-enabledread-only
get-price-in-gusread-only
get-round-inforead-only
get-sold-ticketsread-only
get-prize-poolread-only
get-winners-limitread-only
get-participant-inforead-onlyid-round: uint, id: uint
get-winner-inforead-onlyid-round: uint, id: uint
get-player-ticketsread-onlyid-round: uint, player-wallet: principal
get-winnersread-onlyid-round: uint
flip-salepublic
set-price-in-guspublicnew-price: uint
set-mint-limitpubliclimit: uint
claim-ticketpublic
claim-five-ticketspublic
claim-ten-ticketspublic
finalizepublic
updatepublic
pick-five-ticketspublic
pick-win-ticketprivate
swap-containerprivateid: uint, idx: uint, ids-limit-tickets: uint
cycle-random-idprivatelimit-tickets-ids: uint
set-vrfprivate
randprivatebyte-idx: uint