LCOV - code coverage report
Current view: top level - contracts/extensions - bme022-0-market-gating.clar (source / functions) Coverage Total Hit
Test: lcov.info Lines: 58.3 % 72 42
Test Date: 2025-11-05 10:54:00 Functions: 66.7 % 12 8
Branches: 50.0 % 12 6

             Branch data     Line data    Source code
       1                 :             : ;; Title: BME021 Market Gating
       2                 :             : ;; Synopsis:
       3                 :             : ;; Efficient access control using merkle proofs.
       4                 :             : ;; Description:
       5                 :             : ;; Provides gating functionality based on account (can-access-by-account) and
       6                 :             : ;; ownership (can-access-by-ownership). The map of keys / roots are DAO managed.
       7                 :             : ;; Keys can by any data hash or a specific contract id hash. For ownership the user
       8                 :             : ;; must pass either an NFT or FT token and a merkle proof of ownership. For access
       9                 :             : ;; by account the account principal is passed along with the proof.
      10                 :             : 
      11                 :             : ;; Define the SIP-009 and SIP-010 traits
      12                 :             : (use-trait nft-trait 'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait.nft-trait)
      13                 :             : (use-trait ft-trait 'SP2AKWJYC7BNY18W1XXKPGP0YVEK63QJG4793Z2D4.sip-010-trait-ft-standard.sip-010-trait)
      14                 :             : (impl-trait 'SP3JP0N1ZXGASRJ0F7QAHWFPGTVK9T2XNXDB908Z.extension-trait.extension-trait)
      15                 :             : 
      16                 :             : (define-constant err-unauthorised (err u2200))
      17                 :             : (define-constant err-either-sip9-or-sip10-required (err u2201))
      18                 :             : (define-constant err-token-contract-invalid (err u2202))
      19                 :             : (define-constant err-token-ownership-invalid (err u2203))
      20                 :             : (define-constant err-expecting-nft-contract (err u2204))
      21                 :             : (define-constant err-expecting-ft-contract (err u2205))
      22                 :             : (define-constant err-expecting-token-id (err u2206))
      23                 :             : (define-constant err-not-nft-owner (err u2207))
      24                 :             : (define-constant err-not-ft-owner (err u2208))
      25                 :             : (define-constant err-expecting-nft-buffer (err u2209))
      26                 :             : (define-constant err-expecting-ft-buffer (err u2210))
      27                 :             : (define-constant err-expecting-valid-merkle-proof (err u2211))
      28                 :             : (define-constant err-expecting-merkle-root-for-poll (err u2212))
      29                 :             : (define-constant err-expecting-an-owner (err u2213))
      30                 :             : (define-constant err-account-proof-invalid (err u2214))
      31                 :             : (define-constant err-ownership-proof-invalid (err u2215))
      32                 :             : 
      33                 :             : ;; Merkle roots for gated data
      34                 :             : (define-map merkle-roots
      35                 :             :   (buff 32)
      36                 :             :   {
      37                 :             :                 merkle-root: (buff 32)
      38                 :             :   }
      39                 :             : )
      40                 :             : 
      41                 :         289 : (define-public (is-dao-or-extension)
      42    [ + ][ +  + ]:         878 :         (ok (asserts! (or (is-eq tx-sender .bigmarket-dao) (contract-call? .bigmarket-dao is-extension contract-caller)) err-unauthorised))
      43                 :             : )
      44                 :             : 
      45                 :           1 : (define-public (set-merkle-root (hashed-id (buff 32)) (root (buff 32)))
      46                 :           1 :   (begin
      47                 :             :     ;; Ensure only dao can set the root
      48                 :           1 :     (try! (is-dao-or-extension))
      49                 :           0 :     (map-set merkle-roots hashed-id { merkle-root: root})
      50                 :           0 :     (print {event: "merkle-root", hashed-id: hashed-id, merkle-root: (map-get? merkle-roots hashed-id)})
      51                 :           0 :     (ok true)
      52                 :             :   )
      53                 :             : )
      54                 :             : 
      55                 :         289 : (define-public (set-merkle-root-by-principal (contract-id principal) (root (buff 32)))
      56                 :         877 :   (let
      57                 :             :       (
      58                 :             :         ;; construct the key from the contract-id
      59                 :         877 :         (principal-contract (unwrap! (principal-destruct? contract-id) (err u1001)))
      60                 :         877 :         (contract-bytes (get hash-bytes principal-contract))
      61                 :             :         ;; panics if not contract principal
      62                 :         877 :         (contract-name (unwrap! (to-consensus-buff? (unwrap! (get name principal-contract) err-expecting-an-owner)) err-expecting-an-owner))
      63                 :         877 :         (contract-key (sha256 (concat contract-bytes contract-name )))
      64                 :             :     )
      65                 :             :       ;; Ensure only dao can set the root
      66                 :         877 :       (try! (is-dao-or-extension))
      67                 :         877 :       (map-set merkle-roots contract-key { merkle-root: root})
      68                 :         877 :       (print {event: "set-merkle-root-by-principal", contract-id: contract-id, contract-name: contract-name, contract-key: contract-key, merkle-root: (map-get? merkle-roots contract-key)})
      69                 :         877 :       (ok true)
      70                 :             : ))
      71                 :             : 
      72                 :           3 : (define-read-only (get-merkle-root (hashed-id (buff 32)))
      73                 :           3 :     (map-get? merkle-roots hashed-id)
      74                 :             : )
      75                 :             : 
      76                 :             : ;; Verify a Merkle proof
      77                 :           6 : (define-private (calculate-hash (hash1 (buff 32)) (hash2 (buff 32)) (position bool))
      78                 :          18 :   (if position
      79            [ + ]:           6 :       (sha256 (concat hash2 hash1))
      80            [ + ]:          12 :       (sha256 (concat hash1 hash2))
      81                 :             :   )
      82                 :             : )
      83                 :             : 
      84                 :           6 :  (define-private (process-proof-step (proof-step (tuple (position bool) (hash (buff 32)))) (current (buff 32)))
      85                 :          18 :   (let ((position (get position proof-step))
      86                 :          18 :         (hash (get hash proof-step)))
      87                 :          18 :     (calculate-hash current hash position)
      88                 :             :   )
      89                 :             : )
      90                 :             : 
      91                 :         230 : (define-private (verify-merkle-proof
      92                 :             :     (leaf (buff 32))               ;; The leaf hash (token hash)
      93                 :             :     (proof (list 10 (tuple (position bool) (hash (buff 32))))) 
      94                 :             :     (root (buff 32)) 
      95                 :             :   )
      96                 :         348 :   (let
      97                 :             :       (
      98                 :           0 :         (calculated-root
      99                 :         348 :           (fold process-proof-step proof leaf)
     100                 :             :         )
     101                 :             :       )
     102                 :         348 :     (ok (is-eq calculated-root root))
     103                 :             :   )
     104                 :             : )
     105                 :             : 
     106                 :             : 
     107                 :           0 : (define-private (verify-nft-ownership
     108                 :             :     (nft-contract <nft-trait>) ;; NFT contract
     109                 :             :     (voter principal)          ;; Voter's principal
     110                 :             :     (token-id uint)            ;; Token ID
     111                 :             :   )
     112                 :           0 :   (let
     113                 :             :       (
     114                 :           0 :         (owner (unwrap! (contract-call? nft-contract get-owner token-id) err-not-nft-owner))
     115                 :             :       )
     116                 :           0 :     (ok (is-eq (unwrap! owner err-expecting-an-owner) voter))
     117                 :             :   ))
     118                 :             : 
     119                 :           0 : (define-private (verify-ft-balance
     120                 :             :     (ft-contract <ft-trait>) ;; FT contract
     121                 :             :     (voter principal)        ;; Voter's principal
     122                 :             :     (quantity uint)          ;; Required token quantity
     123                 :             :   )
     124                 :           0 :   (let
     125                 :             :       (
     126                 :           0 :         (balance (unwrap! (contract-call? ft-contract get-balance voter) err-not-ft-owner))
     127                 :             :       )
     128                 :           0 :     (ok (>= balance quantity))
     129                 :             :   ))
     130                 :             : 
     131                 :             : 
     132                 :             : ;; Validate proof of access
     133                 :           0 : (define-public (can-access-by-ownership
     134                 :             :     (market-data-hash (buff 32))                ;; The hashed ID
     135                 :             :     (nft-contract (optional <nft-trait>)) ;; Optional NFT contract
     136                 :             :     (ft-contract (optional <ft-trait>))   ;; Optional FT contract
     137                 :             :     (token-id (optional uint))         ;; Token ID for NFTs
     138                 :             :     (proof (list 10 (tuple (position bool) (hash (buff 32)))))       ;; The Merkle proof
     139                 :             :     (quantity uint)                    ;; Required token quantity
     140                 :             :   )
     141                 :           0 :   (let
     142                 :             :       (
     143                 :             :         ;; Determine if this is an NFT or FT contract
     144                 :           0 :         (is-nft-contract (is-some nft-contract))
     145                 :             : 
     146                 :             :         ;; Fetch the Merkle root for the poll
     147                 :           0 :         (root (unwrap! (map-get? merkle-roots market-data-hash) err-expecting-merkle-root-for-poll))
     148                 :             : 
     149                 :             :         ;; Compute the Merkle proof leaf
     150                 :           0 :         (contract-id (if is-nft-contract
     151            [ - ]:           0 :                          (unwrap! (to-consensus-buff? (as-contract (unwrap! nft-contract err-expecting-nft-contract))) err-expecting-nft-buffer)
     152            [ - ]:           0 :                          (unwrap! (to-consensus-buff? (as-contract (unwrap! ft-contract err-expecting-ft-contract))) err-expecting-ft-buffer)))
     153                 :           0 :         (leaf (sha256 contract-id))
     154                 :             : 
     155                 :             :         ;; Verify the Merkle proof
     156                 :           0 :         (proof-valid (unwrap! (verify-merkle-proof leaf proof (get merkle-root root)) err-expecting-valid-merkle-proof))
     157                 :             : 
     158                 :             :         ;; Verify ownership or balance
     159                 :           0 :         (ownership-valid
     160                 :           0 :           (if is-nft-contract
     161            [ - ]:           0 :               (unwrap! (verify-nft-ownership (unwrap! nft-contract err-expecting-nft-contract) tx-sender (unwrap! token-id err-expecting-token-id)) err-not-nft-owner)
     162            [ - ]:           0 :               (unwrap! (verify-ft-balance (unwrap! ft-contract err-expecting-ft-contract) tx-sender quantity) err-not-ft-owner)))
     163                 :             :       )
     164                 :             :     ;; Ensure both conditions are satisfied
     165            [ - ]:           0 :     (asserts! proof-valid err-ownership-proof-invalid)
     166            [ - ]:           0 :     (asserts! ownership-valid err-token-ownership-invalid)
     167                 :           0 :     (ok true)
     168                 :             :   ))
     169                 :             : 
     170                 :         230 : (define-public (can-access-by-account
     171                 :             :     (sender principal)
     172                 :             :     (proof (list 10 (tuple (position bool) (hash (buff 32)))))       ;; The Merkle proof
     173                 :             :   )
     174                 :         348 :   (let
     175                 :             :       (
     176                 :             :         ;; Fetch the Merkle root for the poll
     177                 :         348 :         (principal-contract (unwrap! (principal-destruct? tx-sender) (err u1001)))
     178                 :         348 :         (contract-bytes (get hash-bytes principal-contract))
     179                 :         348 :         (contract-name (unwrap! (to-consensus-buff? (unwrap! (get name principal-contract) err-expecting-an-owner)) err-expecting-an-owner))
     180                 :         348 :         (contract-key (sha256 (concat contract-bytes contract-name )))
     181                 :         348 :         (root (unwrap! (map-get? merkle-roots contract-key) err-expecting-merkle-root-for-poll))
     182                 :             :     
     183                 :             :         ;; Compute the Merkle proof leaf
     184                 :         348 :         (principal-data (unwrap! (principal-destruct? sender) (err u1001)))
     185                 :         348 :         (leaf (sha256 (get hash-bytes principal-data)))
     186                 :             : 
     187                 :             :         ;; Verify the Merkle proof
     188                 :         348 :         (proof-valid (unwrap! (verify-merkle-proof leaf proof (get merkle-root root)) err-expecting-valid-merkle-proof))
     189                 :             :     )
     190                 :             :     ;; Ensure both conditions are satisfied
     191            [ + ]:         348 :     (asserts! proof-valid err-account-proof-invalid)
     192                 :         345 :     (print {event: "can-access-by-account", contract-key: contract-key, contract-name: contract-name, root: root, leaf: leaf, sender: sender, txsender: tx-sender, proof-valid: proof-valid})
     193                 :         345 :     (ok true)
     194                 :             :   ))
     195                 :             : 
     196                 :             : 
     197                 :             :   ;; --- Extension callback
     198                 :           0 : (define-public (callback (sender principal) (memo (buff 34)))
     199                 :           0 :         (ok true)
     200                 :             : )
     201                 :             : 
        

Generated by: LCOV version 2.3.2-1