-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathliteral.ts
270 lines (246 loc) · 9.07 KB
/
literal.ts
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
import type { IRecordVariant } from './type';
import type { MichelsonJSON, MichelsonMicheline, PairsOfKeys, PrimValue, IValue } from './typings';
import {
TOr,
//
buildRecordVariantType,
} from './type';
import { Prim } from './enums/prim';
import Utils, { composeRightCombLayout, curlyBrackets, parenthesis } from './misc/utils';
import Converter from './converter';
export class Michelson_Literal implements IValue {
private prim: PrimValue;
private value: number | string | boolean | Prim;
constructor(prim: PrimValue, value?: number | string | boolean) {
this.prim = prim;
switch (prim) {
case Prim.Unit:
case Prim.None:
case Prim.False:
case Prim.True:
this.value = prim;
break;
default:
if (typeof value === 'undefined') {
throw new Error('Expected a value!');
}
this.value = value;
}
}
toMicheline(): MichelsonMicheline {
switch (this.prim) {
case Prim.None:
case Prim.False:
case Prim.True:
case Prim.Unit:
case Prim.int:
case Prim.bytes:
return `${this.value}`;
case Prim.string:
return `"${this.value}"`;
}
throw new Error(`Cannot produce michelson for literal of type: ${this.prim}`);
}
toJSON(): MichelsonJSON {
switch (this.prim) {
case Prim.None:
case Prim.Unit:
case Prim.False:
case Prim.True:
return {
prim: this.value as Prim,
};
case Prim.int:
return {
[Prim.int]: `${this.value}`,
};
case Prim.string:
return {
[Prim.string]: this.value as string,
};
case Prim.bytes:
return {
// Same behaviour as in "tezos-client"
[Prim.bytes]: Utils.compressHexString(`${this.value}`),
};
}
throw new Error(`Cannot produce michelson JSON for literal of type: ${this.prim}`);
}
}
export class Michelson_Literal_C1 implements IValue {
#prim: PrimValue;
#elements: IValue[];
constructor(prim: PrimValue, elements: IValue[]) {
this.#prim = prim;
this.#elements = elements;
}
toMicheline(wrap = true): MichelsonMicheline {
switch (this.#prim) {
case Prim.Some:
case Prim.Pair:
case Prim.Left:
case Prim.Right:
const prim = `${this.#prim} ${this.#elements.map((v) => v.toMicheline()).join(' ')}`;
return wrap ? parenthesis(prim) : prim;
case Prim.list:
return curlyBrackets(this.#elements.map((v) => v.toMicheline(false)).join(' ; '));
}
throw new Error(`Cannot produce michelson for literal of type: ${this.#prim}`);
}
toJSON(): MichelsonJSON {
switch (this.#prim) {
case Prim.Some:
case Prim.Pair:
case Prim.Left:
case Prim.Right:
return {
prim: this.#prim,
args: this.#elements.map((v) => v.toJSON()),
};
case Prim.list:
return this.#elements.map((v) => v.toJSON());
}
throw new Error(`Cannot produce michelson JSON for literal of type: ${this.#prim}`);
}
}
export class Michelson_Map implements IValue {
#elements: IValue[][];
constructor(elements: IValue[][]) {
this.#elements = elements;
}
private buildMichelineElt = (key: IValue, value: IValue) => {
return `${Prim.Elt} ${key.toMicheline()} ${value.toMicheline()}`;
};
toMicheline(): MichelsonMicheline {
return curlyBrackets(this.#elements.map(([key, value]) => this.buildMichelineElt(key, value)).join(' ; '));
}
toJSON(): MichelsonJSON {
return this.#elements.map(([key, value]) => ({
prim: Prim.Elt,
args: [key.toJSON(), value.toJSON()],
}));
}
}
/**
* @description Build record literal
* @param fields record dictionary
* @param layout record layout
* @returns {Michelson_LiteralUnion}
*/
const buildRecord = (fields: Record<string, IValue>, layout?: PairsOfKeys<string>): IValue => {
const buildBranch = (branch: string | PairsOfKeys<string>): IValue => {
if (typeof branch === 'string') {
return fields[branch];
}
const [left, right] = branch;
return Pair(buildBranch(left), buildBranch(right));
};
return buildBranch(layout || composeRightCombLayout(Object.keys(fields)));
};
/**
* @description Build variant literal
* @param branch branch name
* @param value branch value
* @param type variant type
* @returns {Michelson_LiteralUnion}
*/
const buildVariant = (target: string, value: IValue, type: IRecordVariant): IValue => {
const [left, right] = type.layout;
if (left === target) {
return Left(value);
}
if (right === target) {
return Right(value);
}
if (Array.isArray(left) && left.flat().includes(target)) {
return Left(buildVariant(target, value, buildRecordVariantType(type.fields, left, TOr)));
}
if (Array.isArray(right) && right.flat().includes(target)) {
return Right(buildVariant(target, value, buildRecordVariantType(type.fields, right, TOr)));
}
throw new Error(`Variant (${target}) is invalid.`);
};
const buildLambda = (michelson: MichelsonMicheline | MichelsonJSON): IValue => {
if (typeof michelson === 'string') {
return {
toMicheline: () => michelson,
toJSON: () => {
throw new Error('Convertion from Micheline to JSON is not implemented.');
},
};
}
return {
toMicheline: () => Converter.michelineOfJSON(michelson),
toJSON: () => michelson,
};
};
// Singletons
export const Nat = (value: number | string) => new Michelson_Literal(Prim.int, value);
export const Int = (value: number | string) => new Michelson_Literal(Prim.int, value);
export const Mutez = (value: number | string) => new Michelson_Literal(Prim.int, value);
export const Timestamp = (value: number | string) =>
new Michelson_Literal(typeof value === 'string' ? Prim.string : Prim.int, value);
export const String = (value: string) => new Michelson_Literal(Prim.string, value);
export const Address = (address: string) => new Michelson_Literal(Prim.string, address);
export const Contract = (address: string, entry_point: string) =>
new Michelson_Literal(Prim.string, `${address}%${entry_point}`);
export const Bytes = (value: string) => new Michelson_Literal(Prim.bytes, value);
export const Chain_id = (value: string) => {
return new Michelson_Literal(`${value}`.slice(0, 2) === '0x' ? Prim.bytes : Prim.string, value);
};
export const Bls12_381_fr = (value: string | number) =>
new Michelson_Literal(typeof value === 'string' ? Prim.bytes : Prim.int, value);
export const Bls12_381_g1 = (value: string) => new Michelson_Literal(Prim.bytes, value);
export const Bls12_381_g2 = (value: string) => new Michelson_Literal(Prim.bytes, value);
export const Key = (key: string) => new Michelson_Literal(Prim.string, key);
export const Key_hash = (key_hash: string) => new Michelson_Literal(Prim.string, key_hash);
export const Signature = (signature: string) => new Michelson_Literal(Prim.string, signature);
export const Bool = (bool: boolean) => new Michelson_Literal(bool ? Prim.True : Prim.False);
export const Unit = () => new Michelson_Literal(Prim.Unit);
// Containers
export const List = (elements: IValue[]) => new Michelson_Literal_C1(Prim.list, elements);
export const Set = List;
export const None = () => new Michelson_Literal(Prim.None);
export const Some = (element: IValue) => new Michelson_Literal_C1(Prim.Some, [element]);
export const Pair = (left: IValue, right: IValue) => new Michelson_Literal_C1(Prim.Pair, [left, right]);
export const Map = (elements: IValue[][] = []) => new Michelson_Map(elements);
export const Big_map = Map;
export const Lambda = (code: MichelsonMicheline | MichelsonJSON) => buildLambda(code);
export const Left = (value: IValue) => new Michelson_Literal_C1(Prim.Left, [value]);
export const Right = (value: IValue) => new Michelson_Literal_C1(Prim.Right, [value]);
// Artificial containers
export const Record = (fields: Record<string, IValue>, layout?: PairsOfKeys<string>) => buildRecord(fields, layout);
export const Variant = (branch: string, value: IValue, type: IRecordVariant) => buildVariant(branch, value, type);
const Literals = {
// Singletons
Unit,
Nat,
Int,
Mutez,
String,
Bool,
Address,
Contract,
Timestamp,
Chain_id,
Bytes,
Bls12_381_fr,
Bls12_381_g1,
Bls12_381_g2,
Key,
Key_hash,
Signature,
// Containers
List,
Set,
None,
Some,
Pair,
Map,
Big_map,
Lambda,
// Artificial containers
Record,
Variant,
};
export default Literals;