Source Code

(define-constant ERR-NOT-AUTHORIZED (err u100))
(define-constant ERR-OTP-NOT-FOUND (err u101))
(define-constant ERR-OTP-EXPIRED (err u102))
(define-constant ERR-OTP-INVALID (err u103))
(define-constant ERR-ALREADY-VERIFIED (err u104))

(define-constant OTP-VALIDITY-PERIOD u10)

(define-map otps
  { phone-number: (string-ascii 20), request-id: uint }
  {
    otp-hash: (buff 32),
    created-at: uint,
    expires-at: uint,
    verified: bool,
    requester: principal
  }
)

(define-data-var request-nonce uint u0)

(define-public (generate-otp-request (phone-number (string-ascii 20)) (otp-hash (buff 32)))
  (let (
    (request-id (var-get request-nonce))
    (expiry (+ stacks-block-height OTP-VALIDITY-PERIOD))
  )
    (map-set otps
      { phone-number: phone-number, request-id: request-id }
      {
        otp-hash: otp-hash,
        created-at: stacks-block-height,
        expires-at: expiry,
        verified: false,
        requester: tx-sender
      }
    )
    (var-set request-nonce (+ request-id u1))
    (ok request-id)
  )
)

(define-public (verify-otp (phone-number (string-ascii 20)) (request-id uint) (otp-hash (buff 32)))
  (let (
    (otp-record (unwrap! (map-get? otps { phone-number: phone-number, request-id: request-id }) ERR-OTP-NOT-FOUND))
  )
    (asserts! (not (get verified otp-record)) ERR-ALREADY-VERIFIED)
    (asserts! (<= stacks-block-height (get expires-at otp-record)) ERR-OTP-EXPIRED)
    (asserts! (is-eq otp-hash (get otp-hash otp-record)) ERR-OTP-INVALID)
    (map-set otps
      { phone-number: phone-number, request-id: request-id }
      (merge otp-record { verified: true })
    )
    (ok true)
  )
)

(define-read-only (get-otp-status (phone-number (string-ascii 20)) (request-id uint))
  (map-get? otps { phone-number: phone-number, request-id: request-id })
)

(define-read-only (is-verified (phone-number (string-ascii 20)) (request-id uint))
  (match (map-get? otps { phone-number: phone-number, request-id: request-id })
    otp-record (ok (get verified otp-record))
    (err ERR-OTP-NOT-FOUND)
  )
)

(define-public (revoke-otp (phone-number (string-ascii 20)) (request-id uint))
  (let (
    (otp-record (unwrap! (map-get? otps { phone-number: phone-number, request-id: request-id }) ERR-OTP-NOT-FOUND))
  )
    (asserts! (is-eq tx-sender (get requester otp-record)) ERR-NOT-AUTHORIZED)
    (ok (map-delete otps { phone-number: phone-number, request-id: request-id }))
  )
)

(define-read-only (get-request-nonce)
  (ok (var-get request-nonce))
)

Functions (6)

FunctionAccessArgs
generate-otp-requestpublicphone-number: (string-ascii 20
verify-otppublicphone-number: (string-ascii 20
get-otp-statusread-onlyphone-number: (string-ascii 20
is-verifiedread-onlyphone-number: (string-ascii 20
revoke-otppublicphone-number: (string-ascii 20
get-request-nonceread-only