Source Code

;; Gift Card Contract
;; Digital gift card issuance and redemption
;; Halal - gift giving
;; Clarity 4 compatible

(define-constant CONTRACT-OWNER tx-sender)
(define-constant ERR-NOT-AUTHORIZED (err u401))
(define-constant ERR-NOT-FOUND (err u404))
(define-constant ERR-ALREADY-REDEEMED (err u405))
(define-constant ERR-EXPIRED (err u406))
(define-constant ERR-INSUFFICIENT (err u407))

(define-data-var card-count uint u0)
(define-data-var total-issued uint u0)

(define-map gift-cards uint {
  creator: principal, recipient: principal, balance: uint,
  message: (string-utf8 100), created: uint, expires: uint, redeemed: bool
})

(define-public (create-gift-card (recipient principal) (amount uint) (message (string-utf8 100)) (validity uint))
  (let ((id (+ (var-get card-count) u1)))
    (try! (stx-transfer? amount tx-sender CONTRACT-OWNER))
    (map-set gift-cards id {
      creator: tx-sender, recipient: recipient, balance: amount,
      message: message, created: stacks-block-height, expires: (+ stacks-block-height validity), redeemed: false
    })
    (var-set card-count id)
    (var-set total-issued (+ (var-get total-issued) amount))
    (print { event: "gift-card-created", id: id, to: recipient, amount: amount })
    (ok id)))

(define-public (redeem-gift-card (card-id uint))
  (let ((card (unwrap! (map-get? gift-cards card-id) ERR-NOT-FOUND)))
    (asserts! (is-eq tx-sender (get recipient card)) ERR-NOT-AUTHORIZED)
    (asserts! (not (get redeemed card)) ERR-ALREADY-REDEEMED)
    (asserts! (< stacks-block-height (get expires card)) ERR-EXPIRED)
    (try! (stx-transfer? (get balance card) CONTRACT-OWNER tx-sender))
    (map-set gift-cards card-id (merge card { redeemed: true, balance: u0 }))
    (ok (get balance card))))

(define-public (partial-redeem (card-id uint) (amount uint))
  (let ((card (unwrap! (map-get? gift-cards card-id) ERR-NOT-FOUND)))
    (asserts! (is-eq tx-sender (get recipient card)) ERR-NOT-AUTHORIZED)
    (asserts! (not (get redeemed card)) ERR-ALREADY-REDEEMED)
    (asserts! (< stacks-block-height (get expires card)) ERR-EXPIRED)
    (asserts! (<= amount (get balance card)) ERR-INSUFFICIENT)
    (try! (stx-transfer? amount CONTRACT-OWNER tx-sender))
    (map-set gift-cards card-id (merge card { balance: (- (get balance card) amount) }))
    (ok (- (get balance card) amount))))

(define-read-only (get-gift-card (id uint)) (map-get? gift-cards id))
(define-read-only (get-card-count) (ok (var-get card-count)))
(define-read-only (get-total-issued) (ok (var-get total-issued)))
(define-read-only (is-valid (id uint))
  (match (map-get? gift-cards id) c (ok (and (not (get redeemed c)) (< stacks-block-height (get expires c)))) (ok false)))

Functions (7)

FunctionAccessArgs
create-gift-cardpublicrecipient: principal, amount: uint, message: (string-utf8 100
redeem-gift-cardpubliccard-id: uint
partial-redeempubliccard-id: uint, amount: uint
get-gift-cardread-onlyid: uint
get-card-countread-only
get-total-issuedread-only
is-validread-onlyid: uint