
convex.fungible.cvx Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of convex-core Show documentation
Show all versions of convex-core Show documentation
Convex core libraries and common utilities
'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