@helios-lang/contract-utils 0.2.8 → 0.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@helios-lang/contract-utils",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "description": "Convenience and type-safety utilities for using Helios validators from within Typescript",
5
5
  "main": "src/index.js",
6
6
  "types": "types/index.d.ts",
@@ -38,8 +38,8 @@
38
38
  "@helios-lang/cli-utils": "^0.1.6",
39
39
  "@helios-lang/codec-utils": "^0.1.30",
40
40
  "@helios-lang/compiler-utils": "^0.1.25",
41
- "@helios-lang/ledger": "^0.2.6",
42
- "@helios-lang/type-utils": "^0.1.18",
41
+ "@helios-lang/ledger": "^0.2.7",
42
+ "@helios-lang/type-utils": "^0.1.20",
43
43
  "@helios-lang/uplc": "^0.1.31"
44
44
  }
45
45
  }
package/src/cast/Cast.js CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  ValidatorHash,
18
18
  Value
19
19
  } from "@helios-lang/ledger"
20
- import { None } from "@helios-lang/type-utils"
20
+ import { None, expectSome } from "@helios-lang/type-utils"
21
21
  import {
22
22
  ByteArrayData,
23
23
  ConstrData,
@@ -102,120 +102,154 @@ export class Cast {
102
102
  /**
103
103
  * @param {TypeSchema} schema
104
104
  * @param {any} x
105
+ * @param {Record<string, TypeSchema>} defs
105
106
  * @returns {UplcData}
106
107
  */
107
- function schemaToUplc(schema, x) {
108
- if ("primitiveType" in schema) {
109
- switch (schema.primitiveType) {
110
- case "Address":
111
- return Address.new(x).toUplcData()
112
- case "Any":
113
- return new IntData(0n)
114
- case "AssetClass":
115
- return AssetClass.new(x).toUplcData()
116
- case "Bool":
117
- return encodeBoolData(x)
118
- case "ByteArray":
119
- return new ByteArrayData(x)
120
- case "Credential":
121
- case "PaymentCredential":
122
- return SpendingCredential.new(x).toUplcData()
123
- case "Data":
124
- return x
125
- case "DatumHash":
126
- return DatumHash.new(x).toUplcData()
127
- case "DCert":
128
- return x.toUplcData()
129
- case "Duration":
130
- case "Int":
131
- return new IntData(x)
132
- case "MintingPolicyHash":
133
- return MintingPolicyHash.new(x).toUplcData()
134
- case "PubKeyHash":
135
- return PubKeyHash.new(x).toUplcData()
136
- case "Real":
137
- return encodeRealData(x)
138
- case "ScriptHash":
139
- return ScriptHash.new(x).toUplcData()
140
- case "StakingCredential":
141
- return StakingCredential.new(x).toUplcData()
142
- case "StakingHash":
143
- return StakingHash.new(x).toUplcData()
144
- case "String":
145
- return new ByteArrayData(encodeUtf8(x))
146
- case "Time":
147
- return new IntData(x)
148
- case "TimeRange":
149
- return TimeRange.new(x).toUplcData()
150
- case "TxId":
151
- return TxId.new(x).toUplcData()
152
- case "TxOutputDatum":
153
- return x
154
- ? TxOutputDatum.new(x)?.toUplcData() ??
155
- new ConstrData(0, [])
156
- : new ConstrData(0, [])
157
- case "TxOutputId":
158
- return TxOutputId.new(x).toUplcData()
159
- case "ValidatorHash":
160
- return ValidatorHash.new(x).toUplcData()
161
- case "Value":
162
- return Value.new(x).toUplcData()
163
- default:
164
- throw new Error(
165
- `not yet implemented for ${schema.primitiveType}`
166
- )
108
+ function schemaToUplc(schema, x, defs = {}) {
109
+ const kind = schema.kind
110
+
111
+ switch (kind) {
112
+ case "reference": {
113
+ const def = expectSome(defs[schema.id])
114
+ return schemaToUplc(def, x, defs)
167
115
  }
168
- } else if ("listItemType" in schema) {
169
- return new ListData(x.map((x) => schemaToUplc(schema.listItemType, x)))
170
- } else if ("mapKeyType" in schema) {
171
- const entries = x instanceof Map ? x.entries() : x
172
- return new MapData(
173
- entries.map(([k, v]) => [
174
- schemaToUplc(schema.mapKeyType, k),
175
- schemaToUplc(schema.mapValueType, v)
176
- ])
177
- )
178
- } else if ("optionSomeType" in schema) {
179
- return encodeOptionData(
180
- x ? schemaToUplc(schema.optionSomeType, x) : None
181
- )
182
- } else if ("structFieldTypes" in schema) {
183
- // TODO: Cip-68 support
184
- if (schema.structFieldTypes.length == 1) {
185
- return schemaToUplc(
186
- schema.structFieldTypes[0].type,
187
- x[schema.structFieldTypes[0].name]
188
- )
189
- } else {
116
+ case "internal": {
117
+ const name = schema.name
118
+
119
+ switch (name) {
120
+ case "Address":
121
+ return Address.new(x).toUplcData()
122
+ case "Any":
123
+ return new IntData(0n)
124
+ case "AssetClass":
125
+ return AssetClass.new(x).toUplcData()
126
+ case "Bool":
127
+ return encodeBoolData(x)
128
+ case "ByteArray":
129
+ return new ByteArrayData(x)
130
+ case "Credential":
131
+ case "SpendingCredential":
132
+ return SpendingCredential.new(x).toUplcData()
133
+ case "Data":
134
+ return x
135
+ case "DatumHash":
136
+ return DatumHash.new(x).toUplcData()
137
+ case "DCert":
138
+ return x.toUplcData()
139
+ case "Duration":
140
+ case "Int":
141
+ return new IntData(x)
142
+ case "MintingPolicyHash":
143
+ return MintingPolicyHash.new(x).toUplcData()
144
+ case "PubKeyHash":
145
+ return PubKeyHash.new(x).toUplcData()
146
+ case "Real":
147
+ return encodeRealData(x)
148
+ case "ScriptHash":
149
+ return ScriptHash.new(x).toUplcData()
150
+ case "StakingCredential":
151
+ return StakingCredential.new(x).toUplcData()
152
+ case "StakingHash":
153
+ return StakingHash.new(x).toUplcData()
154
+ case "String":
155
+ return new ByteArrayData(encodeUtf8(x))
156
+ case "Time":
157
+ return new IntData(x)
158
+ case "TimeRange":
159
+ return TimeRange.new(x).toUplcData()
160
+ case "TxId":
161
+ return TxId.new(x).toUplcData()
162
+ case "TxOutputDatum":
163
+ return x
164
+ ? TxOutputDatum.new(x)?.toUplcData() ??
165
+ new ConstrData(0, [])
166
+ : new ConstrData(0, [])
167
+ case "TxOutputId":
168
+ return TxOutputId.new(x).toUplcData()
169
+ case "ValidatorHash":
170
+ return ValidatorHash.new(x).toUplcData()
171
+ case "Value":
172
+ return Value.new(x).toUplcData()
173
+ default:
174
+ throw new Error(`not yet implemented for ${name}`)
175
+ }
176
+ }
177
+ case "list":
190
178
  return new ListData(
191
- schema.structFieldTypes.map(({ name, type }) =>
192
- schemaToUplc(type, x[name])
193
- )
179
+ x.map((x) => schemaToUplc(schema.itemType, x, defs))
180
+ )
181
+ case "map": {
182
+ const entries = x instanceof Map ? x.entries() : x
183
+ return new MapData(
184
+ entries.map(([k, v]) => [
185
+ schemaToUplc(schema.keyType, k, defs),
186
+ schemaToUplc(schema.valueType, v, defs)
187
+ ])
194
188
  )
195
189
  }
196
- } else if ("enumVariantTypes") {
197
- const variantName = Object.keys(x)[0]
198
- const variantFields = Object.values(x)[0]
199
- const tag = schema.enumVariantTypes.findIndex(
200
- (v) => v.name == variantName
201
- )
202
-
203
- if (tag == -1) {
204
- throw new Error(
205
- `invalid variant ${variantName} (expected: ${schema.enumVariantTypes.map((v) => v.name).join(", ")})`
190
+ case "option":
191
+ return encodeOptionData(
192
+ x ? schemaToUplc(schema.someType, x, defs) : None
206
193
  )
194
+ case "struct": {
195
+ defs[schema.id] = schema
196
+ switch (schema.format) {
197
+ case "singleton":
198
+ return schemaToUplc(
199
+ schema.fieldTypes[0].type,
200
+ x[schema.fieldTypes[0].name],
201
+ defs
202
+ )
203
+ case "list":
204
+ return new ListData(
205
+ schema.fieldTypes.map(({ name, type }) =>
206
+ schemaToUplc(type, x[name])
207
+ )
208
+ )
209
+ case "map":
210
+ return new MapData(
211
+ schema.fieldTypes.map(({ name, type }) => [
212
+ new ByteArrayData(encodeUtf8(name)),
213
+ schemaToUplc(type, x[name], defs)
214
+ ])
215
+ )
216
+ default:
217
+ throw new Error(
218
+ `unhandled struct format '${schema.format}'`
219
+ )
220
+ }
207
221
  }
222
+ case "enum": {
223
+ defs[schema.id] = schema
224
+ const variantName = Object.keys(x)[0]
225
+ const variantFields = Object.values(x)[0]
226
+ const tag = schema.variantTypes.findIndex(
227
+ (v) => v.name == variantName
228
+ )
229
+
230
+ if (tag == -1) {
231
+ throw new Error(
232
+ `invalid variant ${variantName} (expected: ${schema.variantTypes.map((v) => v.name).join(", ")})`
233
+ )
234
+ }
208
235
 
209
- return new ConstrData(
210
- tag,
211
- schema.enumVariantTypes[tag].fieldTypes.map((f) =>
212
- schemaToUplc(f.type, variantFields[f.name])
236
+ return new ConstrData(
237
+ tag,
238
+ schema.variantTypes[tag].fieldTypes.map((f) =>
239
+ schemaToUplc(f.type, variantFields[f.name], defs)
240
+ )
213
241
  )
214
- )
215
- } else {
216
- throw new Error(
217
- `not yet implemented for ${JSON.stringify(schema, undefined, 4)}`
218
- )
242
+ }
243
+ case "variant":
244
+ defs[schema.id] = schema
245
+ return new ConstrData(
246
+ schema.tag,
247
+ schema.fieldTypes.map(({ name, type }) =>
248
+ schemaToUplc(type, x[name])
249
+ )
250
+ )
251
+ default:
252
+ throw new Error(`unhandled schema kind '${kind}'`)
219
253
  }
220
254
  }
221
255
 
@@ -224,137 +258,198 @@ function schemaToUplc(schema, x) {
224
258
  * @param {TypeSchema} schema
225
259
  * @param {UplcData} data
226
260
  * @param {CastConfig} config
261
+ * @param {Record<string, TypeSchema>} defs
227
262
  * @returns {any}
228
263
  */
229
- function uplcToSchema(schema, data, config) {
230
- if ("primitiveType" in schema) {
231
- switch (schema.primitiveType) {
232
- case "Address":
233
- return Address.fromUplcData(config.isMainnet, data)
234
- case "Any":
235
- // TODO: should this throw an error?
236
- return null
237
- case "Bool":
238
- return decodeBoolData(data, true)
239
- case "ByteArray":
240
- return ByteArrayData.expect(data).bytes
241
- case "SpendingCredential":
242
- case "Credential":
243
- return SpendingCredential.fromUplcData(data)
244
- case "Data":
245
- return data
246
- case "DCert":
247
- throw new Error(
248
- "can't convert UplcData back into DCert (significant loss of information)"
249
- )
250
- case "Duration":
251
- case "Int":
252
- return IntData.expect(data).value
253
- case "MintingPolicyHash":
254
- return MintingPolicyHash.fromUplcData(data)
255
- case "PubKeyHash":
256
- return PubKeyHash.fromUplcData(data)
257
- case "Real":
258
- return decodeRealData(data)
259
- case "ScriptHash":
260
- return ScriptHash.fromUplcData(data)
261
- case "StakingCredential":
262
- return StakingCredential.fromUplcData(data)
263
- case "StakingHash":
264
- return StakingHash.fromUplcData(data)
265
- case "StakingValidatorHash":
266
- return StakingValidatorHash.fromUplcData(data)
267
- case "String":
268
- return decodeUtf8(ByteArrayData.expect(data).bytes)
269
- case "Time":
270
- return Number(IntData.expect(data).value) // a number has enough precision to represent ms since 1970 for another 142000 years
271
- case "TimeRange":
272
- return TimeRange.fromUplcData(data)
273
- case "TxId":
274
- return TxId.fromUplcData(data)
275
- case "TxOutputId":
276
- return TxOutputId.fromUplcData(data)
277
- case "ValidatorHash":
278
- return ValidatorHash.fromUplcData(data)
279
- case "Value":
280
- return Value.fromUplcData(data)
281
- default:
282
- throw new Error(
283
- `not yet implemented for ${schema.primitiveType}`
284
- )
285
- }
286
- } else if ("listItemType" in schema) {
287
- return ListData.expect(data).items.map((x) =>
288
- uplcToSchema(schema.listItemType, x, config)
289
- )
290
- } else if ("mapKeyType" in schema) {
291
- return new Map(
292
- MapData.expect(data).items.map(([k, v]) => [
293
- uplcToSchema(schema.mapKeyType, k, config),
294
- uplcToSchema(schema.mapValueType, v, config)
295
- ])
296
- )
297
- } else if ("optionSomeType" in schema) {
298
- const optionData = decodeOptionData(data)
299
- return optionData
300
- ? uplcToSchema(schema.optionSomeType, optionData, config)
301
- : None
302
- } else if ("structFieldTypes" in schema) {
303
- const nExpected = schema.structFieldTypes.length
304
- // TODO: Cip-68 support
305
- if (nExpected == 1) {
306
- return {
307
- [schema.structFieldTypes[0].name]: uplcToSchema(
308
- schema.structFieldTypes[0].type,
309
- data,
310
- config
311
- )
312
- }
313
- } else {
314
- const fields = ListData.expect(data).items
264
+ function uplcToSchema(schema, data, config, defs = {}) {
265
+ const kind = schema.kind
315
266
 
316
- if (fields.length != nExpected) {
317
- throw new Error(
318
- `expected ${nExpected} fields in struct, got ${fields.length} fields`
319
- )
320
- }
267
+ switch (kind) {
268
+ case "internal": {
269
+ const name = schema.name
321
270
 
322
- return Object.fromEntries(
323
- fields.map((field, i) => [
324
- schema.structFieldTypes[i].name,
325
- uplcToSchema(schema.structFieldTypes[i].type, field, config)
271
+ switch (name) {
272
+ case "Address":
273
+ return Address.fromUplcData(config.isMainnet, data)
274
+ case "Any":
275
+ // TODO: should this throw an error?
276
+ return null
277
+ case "Bool":
278
+ return decodeBoolData(data, true)
279
+ case "ByteArray":
280
+ return ByteArrayData.expect(data).bytes
281
+ case "SpendingCredential":
282
+ case "Credential":
283
+ return SpendingCredential.fromUplcData(data)
284
+ case "Data":
285
+ return data
286
+ case "DCert":
287
+ throw new Error(
288
+ "can't convert UplcData back into DCert (significant loss of information)"
289
+ )
290
+ case "Duration":
291
+ case "Int":
292
+ return IntData.expect(data).value
293
+ case "MintingPolicyHash":
294
+ return MintingPolicyHash.fromUplcData(data)
295
+ case "PubKeyHash":
296
+ return PubKeyHash.fromUplcData(data)
297
+ case "Real":
298
+ return decodeRealData(data)
299
+ case "ScriptHash":
300
+ return ScriptHash.fromUplcData(data)
301
+ case "StakingCredential":
302
+ return StakingCredential.fromUplcData(data)
303
+ case "StakingHash":
304
+ return StakingHash.fromUplcData(data)
305
+ case "StakingValidatorHash":
306
+ return StakingValidatorHash.fromUplcData(data)
307
+ case "String":
308
+ return decodeUtf8(ByteArrayData.expect(data).bytes)
309
+ case "Time":
310
+ return Number(IntData.expect(data).value) // a number has enough precision to represent ms since 1970 for another 142000 years
311
+ case "TimeRange":
312
+ return TimeRange.fromUplcData(data)
313
+ case "TxId":
314
+ return TxId.fromUplcData(data)
315
+ case "TxOutputId":
316
+ return TxOutputId.fromUplcData(data)
317
+ case "ValidatorHash":
318
+ return ValidatorHash.fromUplcData(data)
319
+ case "Value":
320
+ return Value.fromUplcData(data)
321
+ default:
322
+ throw new Error(`not yet implemented for ${name}`)
323
+ }
324
+ }
325
+ case "list":
326
+ return ListData.expect(data).items.map((x) =>
327
+ uplcToSchema(schema.itemType, x, config, defs)
328
+ )
329
+ case "map":
330
+ return new Map(
331
+ MapData.expect(data).items.map(([k, v]) => [
332
+ uplcToSchema(schema.keyType, k, config, defs),
333
+ uplcToSchema(schema.valueType, v, config, defs)
326
334
  ])
327
335
  )
336
+ case "option": {
337
+ const optionData = decodeOptionData(data)
338
+ return optionData
339
+ ? uplcToSchema(schema.someType, optionData, config, defs)
340
+ : None
328
341
  }
329
- } else if ("enumVariantTypes" in schema) {
330
- const { tag, fields } = ConstrData.expect(data)
342
+ case "struct": {
343
+ defs[schema.id] = schema
344
+ switch (schema.format) {
345
+ case "singleton":
346
+ return {
347
+ [schema.fieldTypes[0].name]: uplcToSchema(
348
+ schema.fieldTypes[0].type,
349
+ data,
350
+ config,
351
+ defs
352
+ )
353
+ }
354
+ case "list": {
355
+ const fields = ListData.expect(data).items
356
+
357
+ if (fields.length != schema.fieldTypes.length) {
358
+ throw new Error(
359
+ `expected ${schema.fieldTypes.length} fields in struct, got ${fields.length} fields`
360
+ )
361
+ }
331
362
 
332
- const variantSchema = schema.enumVariantTypes[tag]
363
+ return Object.fromEntries(
364
+ fields.map((field, i) => [
365
+ schema.fieldTypes[i].name,
366
+ uplcToSchema(
367
+ schema.fieldTypes[i].type,
368
+ field,
369
+ config,
370
+ defs
371
+ )
372
+ ])
373
+ )
374
+ }
375
+ case "map": {
376
+ const entries = MapData.expect(data).items
333
377
 
334
- if (!variantSchema) {
335
- throw new Error(`tag ${tag} out of range`)
378
+ if (entries.length != schema.fieldTypes.length) {
379
+ throw new Error(
380
+ `expected ${schema.fieldTypes.length} fields in struct, got ${entries.length} fields`
381
+ )
382
+ }
383
+
384
+ return new Map(
385
+ schema.fieldTypes.map(({ name, type }) => {
386
+ const pair = expectSome(
387
+ entries.find(
388
+ ([key, _]) =>
389
+ decodeUtf8(
390
+ ByteArrayData.expect(key).bytes
391
+ ) == name
392
+ )
393
+ )
394
+
395
+ return [
396
+ name,
397
+ uplcToSchema(type, pair[1], config, defs)
398
+ ]
399
+ })
400
+ )
401
+ }
402
+ default:
403
+ throw new Error(
404
+ `unhandled struct format '${schema.format}'`
405
+ )
406
+ }
336
407
  }
408
+ case "enum": {
409
+ defs[schema.id] = schema
410
+ const { tag, fields } = ConstrData.expect(data)
337
411
 
338
- const nExpected = variantSchema.fieldTypes.length
412
+ const variantSchema = schema.variantTypes[tag]
339
413
 
340
- if (fields.length != nExpected) {
341
- throw new Error(
342
- `expected ${nExpected} fields for variant ${variantSchema.name} (tag ${tag}), got ${fields.length} fields`
343
- )
414
+ if (!variantSchema) {
415
+ throw new Error(`tag ${tag} out of range`)
416
+ }
417
+
418
+ const nExpected = variantSchema.fieldTypes.length
419
+
420
+ if (fields.length != nExpected) {
421
+ throw new Error(
422
+ `expected ${nExpected} fields for variant ${variantSchema.name} (tag ${tag}), got ${fields.length} fields`
423
+ )
424
+ }
425
+
426
+ return {
427
+ [variantSchema.name]: Object.fromEntries(
428
+ fields.map((f, i) => [
429
+ variantSchema.fieldTypes[i].name,
430
+ uplcToSchema(
431
+ variantSchema.fieldTypes[i].type,
432
+ f,
433
+ config,
434
+ defs
435
+ )
436
+ ])
437
+ )
438
+ }
344
439
  }
440
+ case "variant": {
441
+ defs[schema.id] = schema
442
+ const { fields } = ConstrData.expect(data)
345
443
 
346
- return {
347
- [variantSchema.name]: Object.fromEntries(
348
- fields.map((f, i) => [
349
- variantSchema.fieldTypes[i].name,
350
- uplcToSchema(variantSchema.fieldTypes[i].type, f, config)
444
+ return Object.fromEntries(
445
+ fields.map((field, i) => [
446
+ schema.fieldTypes[i].name,
447
+ uplcToSchema(schema.fieldTypes[i].type, field, config, defs)
351
448
  ])
352
449
  )
353
450
  }
354
- } else {
355
- throw new Error(
356
- `not yet implemented for ${JSON.stringify(schema, undefined, 4)}`
357
- )
451
+ default:
452
+ throw new Error(`unhandled schema kind '${kind}'`)
358
453
  }
359
454
  }
360
455