;; pg-mdomains-v1-1
;;
;; Decentralized domain names manager for Paradigma
;; To facilitate acquisition of Stacks decentralized domain names
(use-trait sip-010-trait 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait )
(use-trait token-trait 'SP3YK7KWMYRCDMV5M4792T0T7DERQXHJJGGEPV1N8.paradigma-token-trait-v1.paradigma-token-trait)

;; constants
(define-constant ERR_INSUFFICIENT_FUNDS 101)
(define-constant ERR_UNAUTHORIZED 109)
(define-constant ERR_NAME_PREORDER_FUNDS_INSUFFICIENT 203)
(define-constant ERR_BNS_PREORDER_ISSUE 205)
(define-constant ERR_DOMAINNAME_MANAGER_NOT_FOUND 501)

;; set constant for contract owner, used for updating token-uri
(define-constant CONTRACT_OWNER tx-sender)

;; initial value for domain wallet, set to this contract until initialized
(define-data-var domainWallet principal 'SP3YK7KWMYRCDMV5M4792T0T7DERQXHJJGGEPV1N8)

(define-data-var platformDomainWallet principal 'SPRK2JVQ988PYT19JSAJNR3K9YZAZGVY04XMC2Z7)  ;; Wallet where to transfer share fee services

;; Manage domain name service fees
;;  by accepted tokens
(define-map DomainServiceFeeIndex
   {
     serviceId: uint
   }
   {
     tokenSymbol: (string-ascii 32),
   }  
)

(define-read-only (get-domain-service-fee-index (id uint))
     (map-get? DomainServiceFeeIndex
        {
            serviceId: id
        }
     ) 
)

(define-map DomainServiceFee
   {
     tokenSymbol: (string-ascii 32),
   }
   {
     fee: uint
   }
)
(define-read-only (get-domain-service-fee (tokenSymbol (string-ascii 32)))
  (unwrap-panic (get fee 
                  (map-get? DomainServiceFee
                     {tokenSymbol: tokenSymbol}
                  )
                )
  )
)
(define-data-var domainServiceFeeCount uint u0)
(define-read-only (get-domain-service-fee-count)
  (var-get domainServiceFeeCount)
)

;; Set reference info for domain service fee
;; protected function to update domain service fee variable
(define-public (create-domain-service-fee 
                            (tokenSymbol (string-ascii 32))
                            (fee uint) 
                )
  (begin
    (if (is-authorized-domain) 
      (if
        (is-none 
          (map-get? DomainServiceFee
             {
                tokenSymbol: tokenSymbol
             }
          )       
        )
        (begin
          (var-set domainServiceFeeCount (+ (var-get domainServiceFeeCount) u1))
          (map-insert DomainServiceFeeIndex
          { 
            serviceId: (var-get domainServiceFeeCount)
          }
           {
            tokenSymbol: tokenSymbol
           } 
          )
          (map-insert DomainServiceFee 
           {
             tokenSymbol: tokenSymbol
           } 
           {
             fee: fee
           }
          ) 
         (ok true)
        )
        (begin
         (ok 
          (map-set DomainServiceFee 
           {
            tokenSymbol: tokenSymbol
           } 
           {
             fee: fee
           }
          )
         )
        )
      )
      (err ERR_UNAUTHORIZED)
    )
  )
)

;; check if contract caller is contract owner
(define-private (is-authorized-owner)
  (is-eq contract-caller CONTRACT_OWNER)
)

;; Token flow management

;; Stores participants DomainName service sell

;; (define-data-var domainNameManagerCount -list (list 2000 uint) (list))

(define-data-var domainNameManagerCount uint u0)

(define-read-only (get-domain-name-manager-count)
  (var-get domainNameManagerCount)
)
(define-map DomainNameManagersIndex
  { domainNMId: uint }
  {
   nameSpace: (buff 48),                  ;; domain namespace defined in Blockchain Name Service (BNS) like .app
   domainName: (buff 48)                  ;; domain name under a namespace like xck in xck.app
  }
)

(define-read-only (get-domain-name-managers-index (id uint))
     (map-get? DomainNameManagersIndex
        {
            domainNMId: id
        }
     ) 
)

(define-map DomainNameManagers
  {
   nameSpace: (buff 48),                  ;; domain namespace defined in Blockchain Name Service (BNS) like .app
   domainName: (buff 48)                  ;; domain name under a namespace like xck in xck.app
  }
  {
    domainNameWallet: principal,           ;; DomainName manager account - branding and domainName token
    domainNameFeePerc: uint,               ;; DomainName share percentage of fee (ie u10)
    domainNameFeeTokenMint: uint,          ;; Tokens considered reciprocity to domainName token
    domainNameTokenSymbol: (string-utf8 5), ;; Token Symbol used to mint domainName token
    sponsoredWallet: principal,            ;; Sponsored institution account
    sponsoredFeePerc: uint,                ;; Sponsored share percentage of fee (ie u10)
    sponsoredDID: (string-utf8 256),       ;; Sponsored Stacks ID
    sponsoredUri: (string-utf8 256),       ;; Sponsored website Uri
    referencerFeeTokenMint: uint           ;; Tokens for promoters references as reciprocity 
  }
)

;; returns set domain wallet principal
(define-read-only (get-domain-wallet)
  (var-get domainWallet)
)

;; checks if caller is Auth contract
(define-private (is-authorized-auth)   
  (is-eq contract-caller 'SP3YK7KWMYRCDMV5M4792T0T7DERQXHJJGGEPV1N8)
) 

;; protected function to update domain wallet variable
(define-public (set-domain-wallet (newDomainWallet principal))
  (begin
    (asserts! (is-authorized-auth) (err ERR_UNAUTHORIZED))  
    (ok (var-set domainWallet newDomainWallet))
  )
)

;; check if contract caller is domain wallet
(define-private (is-authorized-domain)
    (is-eq contract-caller (var-get domainWallet))
)

;; Set reference info for domainName managers
(define-public (create-domainname-manager 
                            (nameSpace (buff 48))
                            (domainName (buff 48)) 
                            (domainNameWallet principal) 
                            (domainNameFeePerc uint) 
                            (domainNameFeeTokenMint uint) 
                            (tokenSymbol (string-utf8 5))
                            (sponsoredWallet principal) 
                            (sponsoredFeePerc uint)
                            (sponsoredDID (string-utf8 256))
                            (sponsoredUri (string-utf8 256))
                            (referencerFeeTokenMint uint)
                )
  (begin
    (if (is-authorized-domain) 
      (if
        (is-none 
           (map-get? DomainNameManagers 
             {
                nameSpace: nameSpace,
                domainName: domainName
             }
           )       
        )
        (begin
          (var-set domainNameManagerCount (+ (var-get domainNameManagerCount) u1))
          (map-insert DomainNameManagersIndex
          { 
            domainNMId: (var-get domainNameManagerCount)
          }
           {
            nameSpace: nameSpace,
            domainName: domainName
           } 
          )
          (map-insert DomainNameManagers 
           {
            nameSpace: nameSpace,
            domainName: domainName
           } 
           {
            domainNameWallet:  domainNameWallet,
            domainNameFeePerc: domainNameFeePerc,
            domainNameFeeTokenMint: domainNameFeeTokenMint,
            domainNameTokenSymbol: tokenSymbol,
            sponsoredWallet: sponsoredWallet,
            sponsoredFeePerc: sponsoredFeePerc,
            sponsoredDID: sponsoredDID,
            sponsoredUri: sponsoredUri,
            referencerFeeTokenMint: referencerFeeTokenMint
           }
          ) 
         (ok true)
        )
        (begin
         (ok 
          (map-set DomainNameManagers 
           {
            nameSpace: nameSpace,
            domainName: domainName
           } 
           {
            domainNameWallet:  domainNameWallet,
            domainNameFeePerc: domainNameFeePerc,
            domainNameFeeTokenMint: domainNameFeeTokenMint,
            domainNameTokenSymbol: tokenSymbol,
            sponsoredWallet: sponsoredWallet,
            sponsoredFeePerc: sponsoredFeePerc,
            sponsoredDID: sponsoredDID,
            sponsoredUri: sponsoredUri,
            referencerFeeTokenMint: referencerFeeTokenMint
           }
          )
         )
        )
      )
      (err ERR_UNAUTHORIZED)
    )
  )
)

;; Gets the principal for domainName managers
(define-read-only (get-ref-domainname-manager (nameSpace (buff 48)) (domainName (buff 48)))
   (ok (unwrap! (map-get? DomainNameManagers 
                        {
                         nameSpace: nameSpace,
                         domainName: domainName
                        }
               )
               (err ERR_DOMAINNAME_MANAGER_NOT_FOUND)
      )
   )
)


;; Makes the name-preorder
(define-public (bns-name-preorder (hashedSaltedFqn (buff 20)) (stxToBurn uint) (paymentSIP010Trait <sip-010-trait>) (reciprocityTokenTrait <token-trait>) (referencerWallet principal))
  (begin
    (asserts! (> (stx-get-balance tx-sender) stxToBurn) (err ERR_NAME_PREORDER_FUNDS_INSUFFICIENT))
    (let 
        (
          (symbol (unwrap-panic (contract-call? paymentSIP010Trait get-symbol)))
          (fee (get-domain-service-fee symbol))
          (toBurn (- stxToBurn fee))
          (expires (try! (contract-call? 'SP000000000000000000002Q6VF78.bns name-preorder hashedSaltedFqn toBurn)))
          (done (unwrap-panic (order-to-register-domain tx-sender fee 0x616c6c 0x616c6c 0x737461636b73 paymentSIP010Trait reciprocityTokenTrait referencerWallet)))  ;; Includes subdomain:all namespace:all name:stacks as domainnames
        )
        (ok expires) 
    )     
  )
)

;; Gives the order to register a domain and subdomain associated to a domainName and transfers to the domain managers
(define-public (order-to-register-domain (sender principal) (fee uint) (nameSpace (buff 48)) (domainName (buff 48)) (subDomain (buff 48)) 
                                         (paymentSIP010Trait <sip-010-trait>) (reciprocityTokenTrait <token-trait>) (referencerWallet principal))
   (begin
    (asserts! (is-eq tx-sender sender) (err ERR_UNAUTHORIZED))
    (asserts! (> (unwrap-panic (contract-call? paymentSIP010Trait get-balance tx-sender)) fee) (err ERR_INSUFFICIENT_FUNDS))
    (let 
    (
       (domainNameRef  
             (unwrap-panic (map-get? DomainNameManagers 
                        {
                         nameSpace: nameSpace,
                         domainName: domainName
                        }
               )
             )
       )
       (sponsoredFeePerc 
             (get sponsoredFeePerc domainNameRef)
       )
       (sponsoredWallet 
            (get sponsoredWallet domainNameRef)
       )
       (domainNameFeePerc 
          (get domainNameFeePerc domainNameRef)
       )    
      (domainNameWallet 
             (get domainNameWallet domainNameRef)
       )
      (domainNameFeeTokenMint 
              (get domainNameFeeTokenMint domainNameRef)
       )
      (referencerFeeTokenMint
               (get referencerFeeTokenMint domainNameRef))
       (transferToSponsored (/ (* sponsoredFeePerc  fee) u100) )
       (transferToDomainManager (/ (* domainNameFeePerc  fee) u100))
       (transferToPlatform (/ (* (- u100 (+ domainNameFeePerc sponsoredFeePerc ) ) fee) u100))
       (platformDWallet (get-platform-domain-wallet))
     )  
       ;; transfer to sponsored  
              ;; transfer to sponsored  
     (if (> transferToSponsored u0)
        (unwrap-panic (contract-call? paymentSIP010Trait transfer 
                             transferToSponsored 
                             sender 
                             sponsoredWallet
                             none
                      )
        )
        true
     )
         ;; transfer to domain name manager
      (if (> transferToDomainManager u0)
        (unwrap-panic (contract-call? paymentSIP010Trait transfer
                             transferToDomainManager
                             sender
                             domainNameWallet
                             none
                     )
        )
        true
      )
        ;; transfer to platform manager
      (if (> transferToPlatform u0)
         (unwrap-panic (contract-call? paymentSIP010Trait transfer
                              transferToPlatform
                              sender 
                              platformDWallet
                              none
                )
         )
          true
      )
         ;; mint token to sender as reciprocity
      (if (> domainNameFeeTokenMint u0)
        (unwrap-panic (as-contract (contract-call? reciprocityTokenTrait 
                            mint 
                            domainNameFeeTokenMint
                            sender
                                   )
                      )
        )
        true
      )
         ;; mint token for referencer (if there is) as reciprocity
      (if (> referencerFeeTokenMint u0)
        (unwrap-panic (as-contract (contract-call? reciprocityTokenTrait 
                            mint 
                            referencerFeeTokenMint
                            referencerWallet
                                   )
                      )
        )
        true
      )
    )
   (ok true)
  )
)

;; returns set domain wallet principal
(define-read-only (get-platform-domain-wallet)
  (var-get platformDomainWallet)
)
;; protected function to update domain wallet variable
(define-public (set-platform-domain-wallet (newPDomainWallet principal))
  (begin
    (asserts! (is-authorized-auth) (err ERR_UNAUTHORIZED))  
    (ok (var-set platformDomainWallet newPDomainWallet))
  )
)