Source Code

;; Bridge Rate Limiter Contract
;; Protects sBTC bridge from large withdrawals and potential exploits
;; Implements per-user and global rate limits

(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u400))
(define-constant err-rate-limit-exceeded (err u401))
(define-constant err-not-authorized (err u402))
(define-constant err-invalid-limit (err u403))
(define-constant err-blacklisted (err u404))

;; Default limits
(define-constant DEFAULT-USER-DAILY-LIMIT u1000000000)   ;; 10 BTC in sats
(define-constant DEFAULT-GLOBAL-DAILY-LIMIT u100000000000) ;; 1000 BTC in sats
(define-constant RESET-PERIOD u144)  ;; ~1 day in blocks

(define-data-var global-daily-limit uint DEFAULT-GLOBAL-DAILY-LIMIT)
(define-data-var global-used-today uint u0)
(define-data-var last-global-reset uint u0)
(define-data-var limiter-active bool true)

(define-map user-limits principal uint)
(define-map user-usage
  { user: principal, period: uint }
  uint
)
(define-map blacklisted-users principal bool)
(define-map whitelisted-users principal bool)

;; Read-only
(define-read-only (get-user-limit (user principal))
  (default-to DEFAULT-USER-DAILY-LIMIT (map-get? user-limits user))
)

(define-read-only (get-current-period)
  (/ stacks-block-height RESET-PERIOD)
)

(define-read-only (get-user-usage-current-period (user principal))
  (default-to u0 (map-get? user-usage { user: user, period: (get-current-period) }))
)

(define-read-only (get-global-remaining)
  (let ((reset-needed (> (- stacks-block-height (var-get last-global-reset)) RESET-PERIOD)))
    (if reset-needed
      (var-get global-daily-limit)
      (- (var-get global-daily-limit) (var-get global-used-today))
    )
  )
)

(define-read-only (can-bridge (user principal) (amount uint))
  (let (
    (user-used (get-user-usage-current-period user))
    (user-limit (get-user-limit user))
    (is-whitelisted (default-to false (map-get? whitelisted-users user)))
    (is-blacklisted (default-to false (map-get? blacklisted-users user)))
  )
    (and
      (not is-blacklisted)
      (or is-whitelisted (<= (+ user-used amount) user-limit))
      (<= amount (get-global-remaining))
    )
  )
)

(define-read-only (is-blacklisted-user (user principal))
  (default-to false (map-get? blacklisted-users user))
)

;; Public functions
(define-public (set-user-limit (user principal) (limit uint))
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (asserts! (> limit u0) err-invalid-limit)
    (map-set user-limits user limit)
    (ok { user: user, limit: limit })
  )
)

(define-public (set-global-limit (limit uint))
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (var-set global-daily-limit limit)
    (ok limit)
  )
)

(define-public (blacklist-user (user principal))
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (map-set blacklisted-users user true)
    (ok user)
  )
)

(define-public (remove-blacklist (user principal))
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (map-set blacklisted-users user false)
    (ok user)
  )
)

(define-public (whitelist-user (user principal))
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (map-set whitelisted-users user true)
    (ok user)
  )
)

(define-public (record-bridge-usage (user principal) (amount uint))
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (asserts! (var-get limiter-active) err-not-authorized)
    (asserts! (not (is-blacklisted-user user)) err-blacklisted)

    ;; Reset global if needed
    (if (> (- stacks-block-height (var-get last-global-reset)) RESET-PERIOD)
      (begin
        (var-set global-used-today u0)
        (var-set last-global-reset stacks-block-height)
      )
      false
    )

    (asserts! (can-bridge user amount) err-rate-limit-exceeded)

    ;; Update usage
    (let ((period (get-current-period)))
      (map-set user-usage
        { user: user, period: period }
        (+ (get-user-usage-current-period user) amount)
      )
    )
    (var-set global-used-today (+ (var-get global-used-today) amount))

    (ok { user: user, amount: amount, remaining: (- (get-user-limit user) (get-user-usage-current-period user)) })
  )
)

(define-public (toggle-limiter (active bool))
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (var-set limiter-active active)
    (ok active)
  )
)

Functions (13)

FunctionAccessArgs
get-user-limitread-onlyuser: principal
get-current-periodread-only
get-user-usage-current-periodread-onlyuser: principal
get-global-remainingread-only
can-bridgeread-onlyuser: principal, amount: uint
is-blacklisted-userread-onlyuser: principal
set-user-limitpublicuser: principal, limit: uint
set-global-limitpubliclimit: uint
blacklist-userpublicuser: principal
remove-blacklistpublicuser: principal
whitelist-userpublicuser: principal
record-bridge-usagepublicuser: principal, amount: uint
toggle-limiterpublicactive: bool