-- Token.daml
module Token where
import Daml.Script
template Token
with
issuer : Party
owner : Party
symbol : Text
amount : Decimal
where
signatory issuer
observer owner
ensure amount > 0.0
-- Uniquely identify each issued token series by issuer + symbol.
key (issuer, symbol, owner) : (Party, Text, Party)
maintainer key._1
-- Transfer to a new owner.
choice Transfer : ContractId Token
with newOwner : Party
controller owner
do create this with owner = newOwner
-- Split into two tokens. Total is conserved.
choice Split : (ContractId Token, ContractId Token)
with splitAmount : Decimal
controller owner
do
assertMsg "split amount must be positive" (splitAmount > 0.0)
assertMsg "split amount must be less than total" (splitAmount < amount)
t1 <- create this with amount = splitAmount
t2 <- create this with amount = amount - splitAmount
return (t1, t2)
-- Merge another token of the same issuer and symbol.
choice Merge : ContractId Token
with otherId : ContractId Token
controller owner
do
other <- fetch otherId
assertMsg "must have same issuer" (other.issuer == issuer)
assertMsg "must have same symbol" (other.symbol == symbol)
assertMsg "must have same owner" (other.owner == owner)
archive otherId
create this with amount = amount + other.amount
-- Issuer burns (destroys) the token.
choice Burn : ()
controller issuer
do return ()
-- ── Test ─────────────────────────────────────────────────
tokenTest : Script ()
tokenTest = do
bank <- allocateParty "Bank"
alice <- allocateParty "Alice"
bob <- allocateParty "Bob"
-- Mint 1000 tokens for Alice
tid <- submit bank do
createCmd Token with
issuer = bank; owner = alice; symbol = "GOLD"; amount = 1000.0
-- Alice splits off 300
(t300, t700) <- submit alice do
exerciseCmd tid Split with splitAmount = 300.0
-- Alice transfers 700 to Bob
tBob <- submit alice do
exerciseCmd t700 Transfer with newOwner = bob
-- Alice merges her 300 back with... nothing (just verify state)
Some goldAlice <- queryContractId alice t300
assert (goldAlice.amount == 300.0)
Some goldBob <- queryContractId bob tBob
assert (goldBob.amount == 700.0)
The key here includes owner so that each (issuer, symbol, owner) triple is unique. If you want one canonical contract per symbol, remove owner from the key and use Merge to consolidate.
Conserving total supply must be enforced by the contract logic (Split and Merge preserve totals via assert). The runtime does not enforce conservation automatically.