-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTransaction.juvix
175 lines (152 loc) · 7.3 KB
/
Transaction.juvix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
module Token.Transaction;
import Stdlib.Prelude open;
import Stdlib.Debug.Fail open using {failwith};
import Stdlib.Data.Nat.Ord open using {compare};
import Data.Map open;
import Anoma open;
import AnomaHelpers open;
import Token.Resource open;
import Token.Label open;
import Token.Logic open;
import Token.Error open;
import Authorization.Owner open;
--- Mints a token ;Resource; owned by an receiver ;PublicKey;.
--- This requires an ephemeral resource to be consumed.
--- The function returns a ;TokenError; if the calling ;KeyPair; is not the owner.
mint (self : KeyPair) (label : Label) (amount : Nat) (receiver : PublicKey) : Either TokenError Transaction :=
let
myself : PublicKey := KeyPair.pubKey self;
mySecret : PrivateKey := KeyPair.privKey self;
ephToken : Resource := mkToken true amount label mkOwner@{owner := myself};
newToken : Resource := mkToken false amount label mkOwner@{owner := receiver};
consumedRs : List Resource := [ephToken];
createdRs : List Resource := [newToken];
extraData : Map Bytes32 Bytes :=
mkAuthExtraData mySecret consumedRs createdRs;
in
case Label.supply label of
| Unbound := pass (mkTransaction mySecret consumedRs createdRs extraData)
| Capped := throw mkError@{msg := "Tokens with capped supply are not supported yet."}
| Fixed := throw mkError@{msg := "Tokens with fixed supply are not supported yet."};
--- Burns a token ;Resource;, if the calling ;KeyPair; is the owner.
--- This requires an ephemeral resource to be created.
--- The function returns a ;TokenError; if the calling ;KeyPair; is not the owner.
burn (self : KeyPair) (token : Resource) : Either TokenError Transaction :=
let
myself : PublicKey := KeyPair.pubKey self;
mySecret : PrivateKey := KeyPair.privKey self;
owner : PublicKey := getOwner token;
-- This ephemeral resource is needed for the total balance check.
ephToken := mkToken true (Resource.quantity token) (getLabel token) mkOwner@{owner := 0};
consumedRs : List Resource := [token];
createdRs : List Resource := [ephToken];
extraData : Map Bytes32 Bytes :=
mkAuthExtraData mySecret consumedRs createdRs;
in if
| owner /= myself := throw mkUnauthorizedError@{expected := myself; actual := owner}
| else :=
case getSupply token of
| Unbound := pass (mkTransaction mySecret consumedRs createdRs extraData)
| Capped := throw mkError@{msg := "Tokens with capped supply are not supported yet."}
| Fixed := throw mkError@{msg := "Tokens with fixed supply are not supported yet."};
--- Transfers the token ;Resource; to a receiver, if the calling ;KeyPair; is the owner.
--- The function returns a ;TokenError; if the calling ;KeyPair; is not the owner.
transfer
(self : KeyPair)
(token : Resource)
(receiver : PublicKey)
: Either TokenError Transaction :=
let
myself : PublicKey := KeyPair.pubKey self;
mySecret : PrivateKey := KeyPair.privKey self;
owner : PublicKey := getOwner token;
newToken := mkToken false (Resource.quantity token) (getLabel token) mkOwner@{owner := receiver};
consumedRs : List Resource := [token];
createdRs : List Resource := [newToken];
extraData : Map Bytes32 Bytes :=
mkAuthExtraData mySecret consumedRs createdRs;
in if
| owner /= myself := throw mkUnauthorizedError@{expected := myself; actual := owner}
| else := pass (mkTransaction mySecret consumedRs createdRs extraData);
--- Splits a token ;Resource;, if the calling ;KeyPair; is the owner.
--- The function returns a ;TokenError; if
--- - the calling ;KeyPair; is not the owner or
--- - the amounts do not balance,
--- the function returns a ;TokenError;.
split
(self : KeyPair)
(token : Resource)
(amountsAndReceivers : List (Pair Nat PublicKey))
: Either TokenError Transaction :=
let
myself : PublicKey := KeyPair.pubKey self;
mySecret : PrivateKey := KeyPair.privKey self;
owner : PublicKey := getOwner token;
label : Label := anomaDecode (Resource.label token);
sum : Nat := for (acc := 0) ((amount, _) in amountsAndReceivers) {amount + acc};
balance := Resource.quantity token;
consumedRs : List Resource := [token];
createdRs : List Resource := map ((amount, receiver) in amountsAndReceivers) {mkToken false amount label mkOwner@{owner := receiver;}};
extraData : Map Bytes32 Bytes := mkAuthExtraData mySecret consumedRs createdRs;
in if
| owner /= myself := throw mkUnauthorizedError@{expected := myself; actual := owner}
| balance /= sum := throw mkInsufficientQuantityError@{limit := balance; actual := sum}
| else := pass (mkTransaction mySecret consumedRs createdRs extraData);
--- Sends an amount of token ;Resource; to a receiver, if the calling ;KeyPair; is the owner.
--- If the amount is lower than of the quantity of the consumed ;Resource;, the function creates
--- a second ;Resource; with the remainder being owned by the caller.
--- The function returns a ;TokenError; if
--- - the calling ;KeyPair; is not the owner or
--- - if the amounts do or the amount exceeds the available quantity of the resource.
send
(self : KeyPair)
(token : Resource)
(amount : Nat)
(receiver : PublicKey)
: Either TokenError Transaction :=
let
myself : PublicKey := KeyPair.pubKey self;
availableAmount : Nat := Resource.quantity token;
in
case (compare availableAmount amount) of
| LT := throw mkInsufficientQuantityError@{limit := availableAmount; actual := amount}
| EQ := transfer self token receiver
| GT :=
let
remainder : Nat := toNat (intSubNat availableAmount amount);
in
split self token ((amount, receiver) :: (remainder, myself) :: nil);
--- Merges token ;Resource;s, if the calling ;KeyPair; is the owner of all
--- of them and the ;Label;s match.
--- The function returns a ;TokenError; if
--- - the calling ;KeyPair; is not the owner of the tokens or
--- - one of the token ;Label;s differs or
--- - one of the token ;Resource; logics differs.
merge
(self : KeyPair)
(tokens : List Resource)
(receiver : PublicKey)
: Either TokenError Transaction :=
case tokens of
| nil := throw mkInsufficientElementsError@{limit := 1; actual := 0}
| (t :: ts) :=
let
myself : PublicKey := KeyPair.pubKey self;
mySecret : PrivateKey := KeyPair.privKey self;
label : Label := getLabel t;
logic : Nat := anomaEncode(Resource.logic t);
owner : PublicKey := getOwner t;
sum : Nat := for (acc := 0) (t in tokens) {Resource.quantity t + acc};
merged : Resource := mkToken false sum label mkOwner@{owner := myself};
consumedRs : List Resource := tokens;
createdRs : List Resource := [merged];
extraData : Map Bytes32 Bytes := mkAuthExtraData mySecret consumedRs createdRs;
in case findMismatch owner (map getOwner tokens) of
| just wrongOwner := throw mkUnauthorizedError@{expected := myself; actual := wrongOwner}
| nothing :=
case findMismatch label (map getLabel tokens) of
| just wrongLabel := throw mkInvalidLabelError@{expected := label; actual := wrongLabel}
| nothing :=
case findMismatch logic (map getLogic tokens) of
| just wrongLogic := throw mkInvalidLogicError@{expected := logic; actual := wrongLogic}
| nothing := pass (mkTransaction mySecret consumedRs createdRs extraData);