Source Code

;; sbc-token
;; Sustainable Bitcoin Certificate Token Definitions and Utilities

;; constants
;; The address for contract owner who deployed this contract.
(define-constant contract-owner tx-sender)
;; Errors
(define-constant ERR_CONTRACT_OWNER_ONLY u0)
(define-constant ERR_USER_ALREADY_REGISTERED u1)
(define-constant ERR_USER_NOT_REGISTERED u2)
(define-constant ERR_TOKEN_OWNER_ONLY u3)

;; data maps and vars
;; A map from a minted Bitcoin block to its owner address and Bitcoin amount.
(define-map minted-block-info
    {block: uint}
    {
        btc-address: (buff 40),
        btc-amount: uint,
        sbc-amount: uint
    }
)

;; A map from a Bitcoin address to the total sbc it has minted.
;; Only registered users are recorded.
(define-map minted-sbc-by-user
    {btc-address: (buff 40)}
    {total-minted-sbc: uint} 
)

;; URI for SBC token
(define-data-var token-uri (string-utf8 256) u"https://sbp-public.s3.amazonaws.com/json/sbc.json")

;; SIP-010 DEFINITION
;; (impl-trait .sip010-ft-trait.sip010-ft-trait)
;; Update to this definition when deploy on the mainnet
(impl-trait 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait)

(define-fungible-token sbc)
;; SIP-010 FUNCTIONS
;; Transfer from the caller to a new principal
(define-public (transfer (amount uint) (from principal) (to principal) (memo (optional (buff 34))))
  (begin
    (asserts! (is-eq from tx-sender) (err ERR_TOKEN_OWNER_ONLY))
    (try! (ft-transfer? sbc amount from to))
    (match memo to-print (print to-print) 0x)
    (ok true)    
  )
)

;; the human readable name of the token
(define-read-only (get-name)
  (ok "Sustainable Bitcoin Certificate")
)

;; the ticker symbol, or empty if none
(define-read-only (get-symbol)
  (ok "SBC")
)

;; the number of decimals used, e.g. 8 would mean 100_000_000 represents 1 token
;; SBC uses 8, which is the same as Bitcoin
(define-read-only (get-decimals)
  (ok u8)
)

;; the balance of the passed principal
(define-read-only (get-balance (user principal))
  (ok (ft-get-balance sbc user))
)

;; the current total supply (which does not need to be a constant)
(define-read-only (get-total-supply)
  (ok (ft-get-supply sbc))
)

;; an optional URI that represents metadata of this token
(define-read-only (get-token-uri)
  (ok (some (var-get token-uri)))
)

;; UTILITIES
;; Checks if caller is contract owner
(define-private (is-caller-contract-owner)
   (is-eq contract-caller contract-owner)
)

;; Checks if sender is contract owner
(define-private (is-sender-contract-owner)
   (is-eq tx-sender contract-owner)
)

;; Sets token URI to new value, only accessible by Auth
(define-public (set-token-uri (new-uri (string-utf8 256)))
  (begin
    (asserts! (is-sender-contract-owner) (err ERR_CONTRACT_OWNER_ONLY))
    (ok (var-set token-uri new-uri))
  )
)

;; Mints new tokens, only accessible by contract owner.
(define-public (mint (amount uint) (recipient principal))
  (begin
    (asserts! (is-caller-contract-owner) (err ERR_CONTRACT_OWNER_ONLY))
    (ft-mint? sbc amount recipient)
  )
)

;; Registers a new BTC address and initialize its total minted sbc.
(define-public (regist-sbc-user (btc-address (buff 40)))
  (begin
    (asserts! (is-caller-contract-owner) (err ERR_CONTRACT_OWNER_ONLY))
    (asserts! (is-eq (map-get? minted-sbc-by-user {btc-address: btc-address}) none) (err ERR_USER_ALREADY_REGISTERED))
    (map-insert minted-sbc-by-user {btc-address: btc-address} {total-minted-sbc: u0})
    (ok true)
  )
)

;; Mints new tokens and record data, only accessible by contract owner.
;; The recipient should already be registered.
(define-public (mint-and-record (amount uint) (recipient principal) (btc-block uint) (btc-address (buff 40)))
  (begin
    (asserts! (is-caller-contract-owner) (err ERR_CONTRACT_OWNER_ONLY))
    (asserts! (not (is-eq (map-get? minted-sbc-by-user {btc-address: btc-address}) none)) (err ERR_USER_NOT_REGISTERED))
    (try! (ft-mint? sbc amount recipient))
    (map-set minted-block-info {block: btc-block} {btc-address: btc-address, btc-amount: amount, sbc-amount: amount})
    (let ((current-btc 
      (get total-minted-sbc (unwrap! (map-get? minted-sbc-by-user {btc-address: btc-address}) (err ERR_USER_NOT_REGISTERED)))))
      (map-set minted-sbc-by-user {btc-address: btc-address} {total-minted-sbc: (+ current-btc amount)})                    
    )
    (ok true)
  )
)

;; SEND-MANY

(define-public (send-many (recipients (list 200 { to: principal, amount: uint, memo: (optional (buff 34)) })))
  (fold check-err
    (map send-token recipients)
    (ok true)
  )
)

(define-private (check-err (result (response bool uint)) (prior (response bool uint)))
  (match prior ok-value result
               err-value (err err-value)
  )
)

(define-private (send-token (recipient { to: principal, amount: uint, memo: (optional (buff 34)) }))
  (send-token-with-memo (get amount recipient) (get to recipient) (get memo recipient))
)

(define-private (send-token-with-memo (amount uint) (to principal) (memo (optional (buff 34))))
  (let
    (
      (transferOk (try! (transfer amount tx-sender to memo)))
    )
    (ok transferOk)
  )
)

Functions (17)

FunctionAccessArgs
transferpublicamount: uint, from: principal, to: principal, memo: (optional (buff 34
get-nameread-only
get-symbolread-only
get-decimalsread-only
get-balanceread-onlyuser: principal
get-total-supplyread-only
get-token-uriread-only
is-caller-contract-ownerprivate
is-sender-contract-ownerprivate
set-token-uripublicnew-uri: (string-utf8 256
mintpublicamount: uint, recipient: principal
regist-sbc-userpublicbtc-address: (buff 40
mint-and-recordpublicamount: uint, recipient: principal, btc-block: uint, btc-address: (buff 40
send-manypublicrecipients: (list 200 { to: principal, amount: uint, memo: (optional (buff 34
check-errprivateresult: (response bool uint
send-tokenprivaterecipient: { to: principal, amount: uint, memo: (optional (buff 34
send-token-with-memoprivateamount: uint, to: principal, memo: (optional (buff 34