Fungible helper functions
- Contract / Module:
free.util-fungible
- Dependencies:
free.util-chain-data
This modules contains many helpers to create standard fungible modules (aka Tokens).
All theses functions are inspired by the coin
contract and the venerable util.fungible-util
module which no longer seems to be maintained. Many functions have been sanitized and modernized to use the
last released features of Pact:
Return type checking
Principals handling functions
Lazy evaluation of enforcements.
Most of theses function below are enforcements:
Return true in case everything is OK
Throw an error and revert the transaction is case something is wrong.
Accounts / Amounts verification
enforce-precision
precision integer
amount decimal
→ bool
Enforce that an amount satisfies a given precision.
ie: that the amount has not more decimals than precision.
pact> (enforce-precision 4 3.1415)
true
pact> (enforce-precision 4 3.14159)
util-fungible.pact:36:4:Error: Amount 3.14159 violates the required precision 4
Can be used to conveniently implement the (enforce-unit)
function of fungible-v2
:
Example:
(defun enforce-unit:bool (amount:decimal)
(enforce-precision (precision) amount))
enforce-valid-amount
precision integer
amount decimal
→ bool
Enforce that an amount is valid for a transfer:
Satisfies precision
Is strictly positive
pact> (enforce-valid-amount 4 3.1415)
true
pact> (enforce-valid-amount 4 3.14159)
util-fungible.pact:36:4:Error: Amount 3.14159 violates the required precision 4
pact> (enforce-valid-amount 4 -3.1415)
util-fungible.pact:44:4:Error: Amount must be positive
enforce-valid-account
account string
→ bool
Enforce that an account meets the requirements to be used for a fungible.
Only ASCII and Latin1 characters
Length between 3 and 256
pact> (enforce-valid-account "alice")
true
pact> (enforce-valid-account "ST")
util-fungible.pact:55:4:Error: Account name does not conform to the length rquirements [3-256]
pact> (enforce-valid-account "你好世界")
util-fungible.pact:52:4:Error: Account does not conform to the charset LATIN1
enforce-valid-transfer
sender string
receiver string
precision integer
amount decimal
→ bool
Enforce that everything is OK for a transfer:
Both sender and receiver meets the accounts requirements
sender is not the same as receiver
amount meets the amount requirements (precision and sign)
pact> (enforce-valid-transfer "alice" "bob" 4 3.1415)
true
pact> (enforce-valid-transfer "alice" "alice" 4 3.1415)
util-fungible.pact:64:6:Error: Sender and Receiver must be different: alice
pact> (enforce-valid-transfer "alice" "bob" 4 3.14159)
util-fungible.pact:36:4:Error: Amount 3.14159 violates the required precision 4
pact> (enforce-valid-transfer "alice" "ST" 4 3.1415)
util-fungible.pact:55:4:Error: Account name does not conform to the length rquirements [3-256]
Can be used to conveniently implement the (transfer)
, (transfer-create)
functions of fungible-v2
, and
checks everything all at once
Example:
(defun transfer:string (sender:string receiver:string amount:decimal)
(enforce-valid-transfer sender receiver (precision) amount)
(with-capability (TRANSFER sender receiver amount)
...
...
)
enforce-valid-transfer-xchain
sender string
receiver string
precision integer
amount decimal
→ bool
Enforce that everything is OK for a X-chain transfer.
The only difference with the previous one enforce-valid-transfer
, is the condition sender != receiver
which is a non-sense in context of an X-chain transfer. Indeed transfer between same account name is allowed X-chain.
pact> (enforce-valid-transfer-xchain "alice" "bob" 4 3.1415)
true
pact> (enforce-valid-transfer-xchain "alice" "alice" 4 3.1415)
true
pact> (enforce-valid-transfer-xchain "alice" "ST" 4 3.1415)
util-fungible.pact:55:4:Error: Account name does not conform to the length rquirements [3-256]
Can be used to conveniently implement the (transfer-crosschain)
pact of fungible-v2
, and
checks everything all at once
Example:
(defpact transfer-crosschain:string (sender:string receiver:string receiver-guard:guard target-chain:string amount:decimal)
(step
(with-capability (TRANSFER_XCHAIN sender receiver amount target-chain)
(enforce-valid-transfer-xchain sender receiver (precision) amount)
...
...
)
Principals verification
enforce-reserved
account string
guard guard
→ bool
Improved version of the enforce-reserved
function from the coin
contract.
If account is a principal (starts with x:
), the guard must match to the principal
account name.
pact> (env-data {'ks:["7f04bc04a9cf96701a806110242aee08a1692437413bead299fffb4a5b2e4bb6"]})
"Setting transaction data"
; Keyset matches
pact> (enforce-reserved "k:7f04bc04a9cf96701a806110242aee08a1692437413bead299fffb4a5b2e4bb6" (read-keyset 'ks))
true
; Keyset doesn't match
pact> (enforce-reserved "k:2e04bc04a9cf96701a806110242aee08a1692437413bead299fffb4a5b2e4bb6" (read-keyset 'ks))
util-fungible.pact:82:8:Error: Reserved protocol guard violation: k:
; Ref guard matches
pact> (enforce-reserved "r:user.alice" (keyset-ref-guard "user.alice"))
true
; Ref guard doesn't match
pact> (enforce-reserved "r:user.alice" (read-keyset 'ks))
kda-env/pact-util-lib/util-fungible.pact:82:8:Error: Reserved protocol guard violation: r
; Non principal account accepted
pact> (enforce-reserved "alice" (read-keyset 'ks))
true
Can be used to conveniently implement the (create-account)
,``(transfer-create) `` functions of fungible-v2
, to manage
accounts creation.
enforce-reserved*
account string
guard guard
→ bool
Slightly different version of the above function.
Only principals account are accepted. Vanilla account (without any prefix) are disallowed
This can be used to replace (enforce-reserved)
in any fungible contract, to make the token principal only.
pact> (env-data {'ks:["7f04bc04a9cf96701a806110242aee08a1692437413bead299fffb4a5b2e4bb6"]})
"Setting transaction data"
; Keyset matches
pact> (enforce-reserved "k:7f04bc04a9cf96701a806110242aee08a1692437413bead299fffb4a5b2e4bb6" (read-keyset 'ks))
true
; Keyset doesn't match
pact> (enforce-reserved "k:2e04bc04a9cf96701a806110242aee08a1692437413bead299fffb4a5b2e4bb6" (read-keyset 'ks))
util-fungible.pact:91:4:Error: Reserved protocol guard violation: k:
; Non principal account not allowed
pact> (enforce-reserved* "alice" (read-keyset 'ks))
kda-env/pact-util-lib/util-fungible.pact:90:4:Error: Only principal accounts can be used
Cross-chain helpers
enforce-valid-chain-id
chain-id string
→ bool
Enforce that chain-id is valid chain denomination.
pact> (enforce-valid-chain-id "1")
true
pact> (enforce-valid-chain-id "45")
kda-env/pact-util-lib/util-fungible.pact:97:4:Error: Target chain is not a valid Chainweb chainID
pact> (enforce-valid-chain-id "foo")
kda-env/pact-util-lib/util-fungible.pact:97:4:Error: Target chain is not a valid Chainweb chainID
enforce-not-same-chain
chain-id string
→ bool
Enforce that the code is not executing on the same chain, as chain-id.
This is useful in a cross-chain transfer context to verify that we really send tokens to a different chain.
; Simulate execution on chain 0
pact> (env-chain-data {'chain-id:"0"})
"Updated public metadata"
pact> (enforce-not-same-chain "1")
true
pact> (enforce-not-same-chain "0")
kda-env/pact-util-lib/util-fungible.pact:102:4:Error: Target chain 0 cannot be the current chain
fungible-xchain-sch
Predefined schema to be used as a yielded value for X-chains pacts.
It has the following definition:
(defschema fungible-xchain-sch
receiver:string
receiver-guard:guard
amount:decimal
source-chain:string
)