Source Code

;; boom-payment-hub.clar
;;
;; A payment hub contract that:
;; 1. Receives STX or SIP-010 tokens with a memo (payment intent ID)
;; 2. Forwards payment to the specified recipient (merchant)
;; 3. Emits a payment-received event for chainhook detection
;;
;; This enables scalable payment detection with a single chainhook
;; watching this contract instead of N merchant addresses.

;; SIP-010 trait for fungible tokens
(use-trait sip010-trait 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait)

;; Error codes
(define-constant ERR-INVALID-AMOUNT (err u100))
(define-constant ERR-TRANSFER-FAILED (err u101))
(define-constant ERR-EMPTY-MEMO (err u102))
(define-constant ERR-TOKEN-NOT-APPROVED (err u103))
(define-constant ERR-NOT-AUTHORIZED (err u104))

;; Contract owner for admin functions
(define-constant CONTRACT-OWNER tx-sender)

;; Approved tokens whitelist
;; Only sBTC and USDCx are allowed
(define-map approved-tokens principal bool)

;; Initialize approved tokens (sBTC and USDCx on mainnet)
;; sBTC: SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token
;; USDCx: (add actual contract address)
;; Mainnet Address: SP120SBRBQJ00MCWS7TM5R8WJNTTKD5K0HFRC2CNE.usdcx
;; Testnet Address: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usdcx
(map-set approved-tokens 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token true)
(map-set approved-tokens 'SP120SBRBQJ00MCWS7TM5R8WJNTTKD5K0HFRC2CNE.usdcx true)


;; Read-only: Check if a token is approved
(define-read-only (is-token-approved (token-contract principal))
  (default-to false (map-get? approved-tokens token-contract))
)

;; Read-only: Get contract info
(define-read-only (get-info)
  (ok {
    name: "Boom Payment Hub",
    version: u2
  })
)

;; Admin: Add approved token
(define-public (add-approved-token (token-contract principal))
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (ok (map-set approved-tokens token-contract true))
  )
)

;; Admin: Remove approved token
(define-public (remove-approved-token (token-contract principal))
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (ok (map-delete approved-tokens token-contract))
  )
)

;; Pay with STX
;; @param recipient - The merchant/seller address to receive payment
;; @param memo - The payment intent ID (format: p:{id}{hmac_tag})
(define-public (pay-stx (recipient principal) (memo (buff 34)))
  (let (
    (amount (stx-get-balance tx-sender))
  )
    ;; Validate
    (asserts! (> amount u0) ERR-INVALID-AMOUNT)
    (asserts! (> (len memo) u0) ERR-EMPTY-MEMO)

    ;; Transfer STX to recipient
    (try! (stx-transfer-memo? amount tx-sender recipient memo))

    ;; Emit payment event for chainhook
    (print {
      event: "payment-received",
      token: "STX",
      sender: tx-sender,
      recipient: recipient,
      amount: amount,
      memo: memo
    })

    (ok amount)
  )
)

;; Pay with STX - specify amount explicitly
;; @param recipient - The merchant/seller address to receive payment
;; @param amount - Amount of microSTX to send
;; @param memo - The payment intent ID
(define-public (pay-stx-amount (recipient principal) (amount uint) (memo (buff 34)))
  (begin
    ;; Validate
    (asserts! (> amount u0) ERR-INVALID-AMOUNT)
    (asserts! (> (len memo) u0) ERR-EMPTY-MEMO)

    ;; Transfer STX to recipient
    (try! (stx-transfer-memo? amount tx-sender recipient memo))

    ;; Emit payment event for chainhook
    (print {
      event: "payment-received",
      token: "STX",
      sender: tx-sender,
      recipient: recipient,
      amount: amount,
      memo: memo
    })

    (ok amount)
  )
)

;; Pay with SIP-010 token (sBTC, USDCx only)
;; @param token - The SIP-010 token contract (must be whitelisted)
;; @param recipient - The merchant/seller address to receive payment
;; @param amount - Amount of tokens to send (in base units)
;; @param memo - The payment intent ID
(define-public (pay-sip010
  (token <sip010-trait>)
  (recipient principal)
  (amount uint)
  (memo (buff 34))
)
  (let (
    (token-contract (contract-of token))
  )
    ;; Validate token is whitelisted
    (asserts! (is-token-approved token-contract) ERR-TOKEN-NOT-APPROVED)

    ;; Validate amount and memo
    (asserts! (> amount u0) ERR-INVALID-AMOUNT)
    (asserts! (> (len memo) u0) ERR-EMPTY-MEMO)

    ;; Transfer tokens to recipient (with memo in token transfer)
    (try! (contract-call? token transfer amount tx-sender recipient (some memo)))

    ;; Emit payment event for chainhook
    (print {
      event: "payment-received",
      token: token-contract,
      sender: tx-sender,
      recipient: recipient,
      amount: amount,
      memo: memo
    })

    (ok amount)
  )
)

Functions (7)

FunctionAccessArgs
is-token-approvedread-onlytoken-contract: principal
get-inforead-only
add-approved-tokenpublictoken-contract: principal
remove-approved-tokenpublictoken-contract: principal
pay-stxpublicrecipient: principal, memo: (buff 34
pay-stx-amountpublicrecipient: principal, amount: uint, memo: (buff 34
pay-sip010publictoken: <sip010-trait>, recipient: principal, amount: uint, memo: (buff 34