All Downloads are FREE. Search and download functionalities are using the official Maven repository.

convex.fungible.cvx Maven / Gradle / Ivy

There is a newer version: 0.7.15
Show newest version
'convex.fungible

(call *registry*
      (register {:description ["Provides library functions for building and managing standard fungible assets."
                               "Quantity is expressed as a long representing the amount of an asset."
                               "The `build-token` function creates deployable code that follows the interface described in `convex.asset`."]
                 :name        "Fungible token creation and management"}))

(declare balance transfer)

;;;;;;;;;; API for Building fungible token actors

(defn add-mint
  ^{:doc {:description ["Creates deployable code that, when added to actor code from `build-token`, allows priviledged accounts to mint and burn tokens."
                        "Configuration map contains:"
                        "- `:max-supply`, a long designating the maximum mintable supply (optional, defaults to 1000000000000000000, the allowed maximum)"
                        "- `:minter`, a single address or a Trust Monitor from `convex.trust` (mandatory)"]
          :examples    [{:code "(deploy [(build-token {}) (add-mint {:minter *address* :max-supply 1000000000})])"}]
          :signature   [{:params [config]}]}}
  [config]
  (let [max-supply (int (or (:max-supply config)
                             1000000000000000000))
        minter     (address (or (:minter config)
                                *address*))]
    (assert (<= 0
                max-supply
                1000000000000000000))
    `(do
       (import convex.trust :as trust)
       (declare balance transfer)

       ;; Who is allowed to mint tokens?
       ;;
       (def minter
            ~minter)

       ;; Maximum supply (limit after minting)
       ;;
       (def max-supply
            ~max-supply)

       (defn burn
         ^{:callable? true}
         [amount]
         (when-not (trust/trusted? minter *caller*)
           (fail :TRUST "No rights to burn"))
         (let [amount (int amount)
               bal    (balance *caller*)]
           ;; Burn amount must be less than or equal to caller's balance.
           ;;
           (assert (<= 0
                       amount
                       bal))
           (set-holding *caller*
                        (- bal
                           amount))
           (def supply (- supply
                          amount))))

       (defn mint
         ^{:callable? true}
         [amount]
         (when-not (trust/trusted? minter *caller*)
           (fail :TRUST "No rights to mint"))
         (let [amount     (int amount)
               new-supply (+ supply amount)
               bal        (balance *caller*)
               new-bal    (+ bal amount)]
           ;; Mint amount.
           ;;
           (assert (<= 0
                       new-bal
                       max-supply))
           ;; New supply must be in valid range.
           ;;
           (assert (<= 0
                       new-supply
                       max-supply))
           (set-holding *caller*
                        new-bal)
           (def supply
                new-supply))))))

(defn build-token 
  ^{:doc {:description ["Creates deployable code for a new fungible token which follows the interface described in `convex.asset`."
                        "An optional config map can be provided:"
                        "- `:initial-holder`, address which will hold the initial supply (defaults to `*address*`)"
                        "- `:supply`, supply created and attributed to `:initial-holder` (Integer, defaults to 1000000)"]
          :examples    [{:code "(deploy (build-token {:supply 1000000 :initial-holder *address*}))"}]
          :signature   [{:params [config]}]}}
  [config]
  (let [supply         (or (:supply config)
                           1000000)
        initial-holder (or (:initial-holder config)
                           *address*)]
    `(do

       (def supply
            ~supply)

       (set-holding ~initial-holder
                    ~supply)

       ;; Map of holder-address -> {offeree-address -> positive integer amount}
       ;; Must enforce valid positive offers
       ;;
       (def offers {})

       ;; Functions of the interface described in the `convex.asset` library
       (defn accept 
         ^{:callable? true}
         [sender quantity]
         (let [sender   (address sender)
               quantity (if quantity
                          (int quantity)
                          0)
               om       (or (get offers
                                 sender)
                            0)
               sendbal  (or (get-holding sender)
                            0)
               offer    (or (get om
                                 *caller*)
                            0)]
             (cond 
               (< quantity
                  0)
               (fail "Can't accept a negative quantity of fungible tokens.")

               (< offer
                  quantity)
               (fail "Offer is insufficient")

               (< sendbal
                  quantity)
               (fail "Sender token balance is insufficient")

               (let [new-offer (- offer
                                  quantity)] 
                 (def offers
                      (assoc offers
                             sender
                             (if (> new-offer
                                    0)
                               (assoc om
                                      *caller*
                                      new-offer)
                               (dissoc om *caller*))))
                 (set-holding sender
                              (- sendbal
                                 quantity))
                 (set-holding *caller*
                              (+ (or (get-holding *caller*)
                                     0)
                                 quantity))
                 quantity))))

       (defn balance
         ^{:callable? true}
         [addr]
         (or (get-holding addr) 0))

       ;; No restrictions on transfer by default.
       ;;
       (defn check-transfer
         ^{:callable? true}
         [_sender _receiver _quantity]
         nil)

       (defn direct-transfer
         ^{:callable? true}
         [addr amount]
         (let [addr   (address addr)
               amount (if amount
                        (int amount)
                        0)
               bal    (or (get-holding *caller*)
                          0)
               tbal   (or (get-holding addr)
                          0)]
           ;; Amount must be in valid range.
           ;;
           (assert (<= 0
                       amount
                       bal)) 
           ;; Need this check in case of self-transfers.
           (when (= *caller*
                    addr)
             (return amount))
           (set-holding *caller*
                        (- bal
                           amount))
           (set-holding addr
                        (+ tbal
                           amount))))

       (defn get-offer
         ^{:callable? true}
         [sender receiver]
         (or (get-in offers
                     [sender
                      receiver])
             0))

       (defn offer
         ^{:callable? true}
         [receiver quantity]
         (let [receiver (address receiver)
               quantity (if quantity
                          (int quantity)
                          0)
               om       (get offers
                             *caller*)]
           (if (<= quantity
                   0)
             (when (get om
                        receiver)
               (def offers
                    (assoc offers
                           *caller*
                           (dissoc om
                                   receiver))))
             (def offers
                  (assoc-in offers
                            [*caller*
                             receiver]
                            quantity)))
           quantity))


       ;; TODO. Shouldn't also implement `owns?`


       (defn quantity-add

         ^{:callable? true}

         [a b]

         (let [a (if a
                   (int a)
                   0)
               b (if b
                   (int b)
                   0)]
           (+ a b)))


       (defn quantity-sub

         ^{:callable? true}

         [a b]

         (let [a (if a
                   (int a)
                   0)
               b (if b
                   (int b)
                   0)] 
           (if (> a b)
             (- a
                b)
             0)))


       (defn quantity-subset?

         ^{:callable? true}

         [a b]

         (<= (if a
               (int a)
               0)
             (if b
               (int b)
               0))))))

;;;;;;;;;; API for handling fungible actors

(defn balance
  ^{:doc {:description "Gets the balance from a fungible token. Checks the balance for the specified holder, or the current *address* if not specified."
          :examples    [{:code "(balance my-token-address)"}]
          :signature   [{:params [token holder]}]}}
  [token holder]
  (call token
        (balance holder))) 

(defn burn 
  ^{:doc {:description "Burns an amount of tokens for the given token, if allowed by the implementation. Amount must be non-negative and no greater than the caller's balance."
          :examples    [{:code "(mint my-token-address 1000)"}]
          :signature   [{:params [token amount]}]}}
  [token amount]
  (call token
        (burn amount)))

(defn mint 
  ^{:doc {:description "Mints an amount of tokens for the given token. User must have minting privileges. Amount may be negative to burn fungible tokens."
          :examples    [{:code "(mint my-token-address 1000)"}]
          :signature   [{:params [token amount]}]}}
  [token amount]
  (call token
        (mint amount)))

(defn transfer 
  ^{:doc {:description "Transfers balance of a fungible token."
          :examples    [{:code "(transfer my-token-address my-friend 100)"}]
          :signature   [{:params [token target amount]}]}}
  [token target amount]
  ;; TODO should take account of actors as recipients, similar to convex.asset?
  (call token
        (direct-transfer target
                         amount))) 




© 2015 - 2025 Weber Informatics LLC | Privacy Policy