@helios-lang/effect 0.1.0
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/LICENSE +28 -0
- package/README.md +3 -0
- package/dist/Address.js +13 -0
- package/dist/Address.js.map +1 -0
- package/dist/Bech32.js +153 -0
- package/dist/Bech32.js.map +1 -0
- package/dist/Cbor.js +1171 -0
- package/dist/Cbor.js.map +1 -0
- package/dist/Uplc/Cek.js +3 -0
- package/dist/Uplc/Cek.js.map +1 -0
- package/dist/Uplc/Data.js +171 -0
- package/dist/Uplc/Data.js.map +1 -0
- package/dist/Uplc/DataSchema.js +118 -0
- package/dist/Uplc/DataSchema.js.map +1 -0
- package/dist/Uplc/Primitive.js +23 -0
- package/dist/Uplc/Primitive.js.map +1 -0
- package/dist/Uplc/index.js +4 -0
- package/dist/Uplc/index.js.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/Base32.js +201 -0
- package/dist/internal/Base32.js.map +1 -0
- package/dist/internal/BigEndian.js +56 -0
- package/dist/internal/BigEndian.js.map +1 -0
- package/dist/internal/Bits.js +300 -0
- package/dist/internal/Bits.js.map +1 -0
- package/dist/internal/Bytes.js +293 -0
- package/dist/internal/Bytes.js.map +1 -0
- package/dist/internal/Flat.js +298 -0
- package/dist/internal/Flat.js.map +1 -0
- package/dist/internal/Float.js +154 -0
- package/dist/internal/Float.js.map +1 -0
- package/dist/internal/Utf8.js +44 -0
- package/dist/internal/Utf8.js.map +1 -0
- package/eslint.config.mjs +30 -0
- package/package.json +36 -0
- package/src/Address.ts +20 -0
- package/src/Bech32.test.ts +117 -0
- package/src/Bech32.ts +198 -0
- package/src/Cbor.test.ts +1610 -0
- package/src/Cbor.ts +1704 -0
- package/src/Uplc/Cek.ts +92 -0
- package/src/Uplc/Data.ts +259 -0
- package/src/Uplc/DataSchema.test.ts +207 -0
- package/src/Uplc/DataSchema.ts +181 -0
- package/src/Uplc/Primitive.ts +56 -0
- package/src/Uplc/index.ts +3 -0
- package/src/index.ts +4 -0
- package/src/internal/Base32.test.ts +219 -0
- package/src/internal/Base32.ts +341 -0
- package/src/internal/BigEndian.test.ts +79 -0
- package/src/internal/BigEndian.ts +67 -0
- package/src/internal/Bits.test.ts +300 -0
- package/src/internal/Bits.ts +398 -0
- package/src/internal/Bytes.test.ts +369 -0
- package/src/internal/Bytes.ts +343 -0
- package/src/internal/Flat.test.ts +29 -0
- package/src/internal/Flat.ts +387 -0
- package/src/internal/Float.test.ts +51 -0
- package/src/internal/Float.ts +190 -0
- package/src/internal/Utf8.test.ts +69 -0
- package/src/internal/Utf8.ts +58 -0
- package/tsconfig.build.json +14 -0
- package/tsconfig.json +38 -0
package/src/Uplc/Cek.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
//import { Either } from "effect"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The context that terms and frames need to operate.
|
|
5
|
+
*/
|
|
6
|
+
export interface Context {
|
|
7
|
+
readonly cost: CostTracker
|
|
8
|
+
//getBuiltin(id: number): Builtin | undefined
|
|
9
|
+
print(message: string, site?: Site): void
|
|
10
|
+
popLastMessage(): string | undefined
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface Cost {
|
|
14
|
+
readonly cpu: bigint
|
|
15
|
+
readonly mem: bigint
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type CostBreakdown = {
|
|
19
|
+
[name: string]: Cost & { count: number }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface CostModel {
|
|
23
|
+
readonly applyTerm: Cost
|
|
24
|
+
readonly builtinTerm: Cost
|
|
25
|
+
readonly caseTerm: Cost
|
|
26
|
+
readonly constTerm: Cost
|
|
27
|
+
readonly constrTerm: Cost
|
|
28
|
+
readonly delayTerm: Cost
|
|
29
|
+
readonly forceTerm: Cost
|
|
30
|
+
readonly lambdaTerm: Cost
|
|
31
|
+
readonly startupCost: Cost
|
|
32
|
+
readonly varTerm: Cost
|
|
33
|
+
readonly builtins: Record<string, (argSizes: number[]) => Cost>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface CostTracker {
|
|
37
|
+
readonly cost: Cost
|
|
38
|
+
readonly costModel: CostModel
|
|
39
|
+
readonly breakdown: CostBreakdown
|
|
40
|
+
incrApplyCost(): void
|
|
41
|
+
incrBuiltinCost(): void
|
|
42
|
+
incrCaseCost(): void
|
|
43
|
+
incrConstCost(): void
|
|
44
|
+
incrConstrCost(): void
|
|
45
|
+
incrDelayCost(): void
|
|
46
|
+
incrForceCost(): void
|
|
47
|
+
incrLambdaCost(): void
|
|
48
|
+
incrStartupCost(): void
|
|
49
|
+
incrVarCost(): void
|
|
50
|
+
incrArgSizesCost(name: string, argSizes: bigint[]): void
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Instantiate a `Machine` with {@link makeCekMachine}.
|
|
55
|
+
*/
|
|
56
|
+
//export interface Machine extends Context {
|
|
57
|
+
// readonly builtins: Builtin[]
|
|
58
|
+
// readonly logger: Logger | undefined
|
|
59
|
+
// readonly state: State
|
|
60
|
+
// readonly trace: { message: string; site?: Site }[]
|
|
61
|
+
// eval(): Result
|
|
62
|
+
//}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* TODO: rename to CEKResult
|
|
66
|
+
* @typedef {{
|
|
67
|
+
* result: Either<
|
|
68
|
+
* {
|
|
69
|
+
* error: string
|
|
70
|
+
* callSites: CallSiteInfo[]
|
|
71
|
+
* },
|
|
72
|
+
* string | UplcValue
|
|
73
|
+
* >
|
|
74
|
+
* cost: Cost
|
|
75
|
+
* logs: {message: string, site?: Site}[]
|
|
76
|
+
* breakdown: CostBreakdown
|
|
77
|
+
* }} CekResult
|
|
78
|
+
* Return value is optional and can be omitted if the UplcValue doesn't suffice to contain it (eg. lambda functions).
|
|
79
|
+
*/
|
|
80
|
+
//export interface Result {
|
|
81
|
+
// result: Either.Either<
|
|
82
|
+
// string | Value,
|
|
83
|
+
// { error: string; callSites: CallSiteInfo[] }
|
|
84
|
+
// >
|
|
85
|
+
//}
|
|
86
|
+
|
|
87
|
+
export interface Site {
|
|
88
|
+
readonly file: string
|
|
89
|
+
readonly line: number
|
|
90
|
+
readonly column: number
|
|
91
|
+
readonly description: string | undefined
|
|
92
|
+
}
|
package/src/Uplc/Data.ts
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect"
|
|
2
|
+
import * as Bytes from "../internal/Bytes.js"
|
|
3
|
+
import * as Cbor from "../Cbor.js"
|
|
4
|
+
|
|
5
|
+
const SuspendedData = Schema.suspend(
|
|
6
|
+
(): Schema.Schema<Data, DataEncoded> => Data
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
export const ByteArray = Schema.Struct({
|
|
10
|
+
bytes: Schema.String
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
export type ByteArray = Schema.Schema.Type<typeof ByteArray>
|
|
14
|
+
|
|
15
|
+
export type ByteArrayEncoded = Schema.Schema.Encoded<typeof ByteArray>
|
|
16
|
+
|
|
17
|
+
export function makeByteArray(
|
|
18
|
+
bytes: string | number[] | Uint8Array
|
|
19
|
+
): ByteArray {
|
|
20
|
+
return { bytes: Bytes.toHex(bytes) }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* No need to serialize
|
|
25
|
+
*
|
|
26
|
+
* Cannot used Branded types due Schema issues.
|
|
27
|
+
*/
|
|
28
|
+
export const Int = Schema.Struct({
|
|
29
|
+
int: Schema.BigIntFromNumber
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
export type Int = Schema.Schema.Type<typeof Int>
|
|
33
|
+
|
|
34
|
+
export type IntEncoded = Schema.Schema.Encoded<typeof Int>
|
|
35
|
+
|
|
36
|
+
export function makeInt(value: number | bigint): Int {
|
|
37
|
+
return { int: BigInt(value) }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const List = Schema.Struct({
|
|
41
|
+
list: Schema.Array(SuspendedData)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Must be defined explicitly to avoid circular reference problems
|
|
46
|
+
*/
|
|
47
|
+
export type List = {
|
|
48
|
+
readonly list: ReadonlyArray<Data>
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type ListEncoded = {
|
|
52
|
+
readonly list: ReadonlyArray<DataEncoded>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function makeList(items: Data[]): List {
|
|
56
|
+
return {
|
|
57
|
+
list: items
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const Map = Schema.Struct({
|
|
62
|
+
map: Schema.Array(
|
|
63
|
+
Schema.Struct({
|
|
64
|
+
k: SuspendedData,
|
|
65
|
+
v: SuspendedData
|
|
66
|
+
})
|
|
67
|
+
)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Must be defined explicitly to avoid circular reference problems
|
|
72
|
+
*/
|
|
73
|
+
export type Map = {
|
|
74
|
+
readonly map: ReadonlyArray<{
|
|
75
|
+
readonly k: Data
|
|
76
|
+
readonly v: Data
|
|
77
|
+
}>
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type MapEncoded = {
|
|
81
|
+
readonly map: ReadonlyArray<{
|
|
82
|
+
readonly k: DataEncoded
|
|
83
|
+
readonly v: DataEncoded
|
|
84
|
+
}>
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function makeMap(entries: [Data, Data][]): Map {
|
|
88
|
+
return {
|
|
89
|
+
map: entries.map(([k, v]) => ({ k, v }))
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const Constr = Schema.Struct({
|
|
94
|
+
constructor: Schema.Number,
|
|
95
|
+
fields: Schema.Array(SuspendedData)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Must be defined explicitly to avoid circular reference problems
|
|
100
|
+
*/
|
|
101
|
+
export type Constr = {
|
|
102
|
+
readonly constructor: number
|
|
103
|
+
readonly fields: ReadonlyArray<Data>
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export type ConstrEncoded = {
|
|
107
|
+
readonly constructor: number
|
|
108
|
+
readonly fields: ReadonlyArray<DataEncoded>
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function makeConstr(tag: bigint | number, fields: Data[]): Constr {
|
|
112
|
+
return {
|
|
113
|
+
constructor: Number(tag),
|
|
114
|
+
fields
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export const Data = Schema.Union(ByteArray, Constr, Int, List, Map, Constr)
|
|
119
|
+
|
|
120
|
+
export const DataUnencoded = Schema.typeSchema(Data)
|
|
121
|
+
|
|
122
|
+
export type DataUnencoded = Data
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Must be defined explicitly to avoid circular reference problems
|
|
126
|
+
*/
|
|
127
|
+
export type Data = ByteArray | Constr | Int | List | Map
|
|
128
|
+
|
|
129
|
+
export type DataEncoded =
|
|
130
|
+
| ByteArrayEncoded
|
|
131
|
+
| ConstrEncoded
|
|
132
|
+
| IntEncoded
|
|
133
|
+
| ListEncoded
|
|
134
|
+
| MapEncoded
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Simple recursive CBOR decoder
|
|
138
|
+
* @param bytes
|
|
139
|
+
* @returns
|
|
140
|
+
*/
|
|
141
|
+
export const decode = (bytes: Bytes.BytesLike): Cbor.DecodeEffect<Data> =>
|
|
142
|
+
Effect.gen(function* () {
|
|
143
|
+
const stream = Bytes.makeStream(bytes)
|
|
144
|
+
|
|
145
|
+
if (yield* Cbor.isList(stream)) {
|
|
146
|
+
const items = yield* Cbor.decodeList(decode)(stream)
|
|
147
|
+
|
|
148
|
+
return makeList(items)
|
|
149
|
+
} else if (yield* Cbor.isBytes(stream)) {
|
|
150
|
+
return makeByteArray(yield* Cbor.decodeBytes(stream))
|
|
151
|
+
} else if (yield* Cbor.isMap(stream)) {
|
|
152
|
+
const entries = yield* Cbor.decodeMap(decode, decode)(stream)
|
|
153
|
+
|
|
154
|
+
return makeMap(entries)
|
|
155
|
+
} else if (yield* Cbor.isConstr(stream)) {
|
|
156
|
+
const [tag, fields] = yield* Cbor.decodeConstr(decode)(stream)
|
|
157
|
+
return makeConstr(tag, fields)
|
|
158
|
+
} else {
|
|
159
|
+
return makeInt(yield* Cbor.decodeInt(stream))
|
|
160
|
+
}
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Simple recursive CBOR encoder
|
|
165
|
+
* @param data
|
|
166
|
+
* @returns
|
|
167
|
+
*/
|
|
168
|
+
export function encode(data: Data): number[] {
|
|
169
|
+
if ("bytes" in data) {
|
|
170
|
+
return Cbor.encodeBytes(data.bytes.slice(), true)
|
|
171
|
+
} else if ("fields" in data) {
|
|
172
|
+
return Cbor.encodeConstr(data.constructor, data.fields.map(encode))
|
|
173
|
+
} else if ("int" in data) {
|
|
174
|
+
return Cbor.encodeInt(data.int)
|
|
175
|
+
} else if ("list" in data) {
|
|
176
|
+
return Cbor.encodeList(data.list.map(encode))
|
|
177
|
+
} else if ("map" in data) {
|
|
178
|
+
return Cbor.encodeMap(data.map.map(({ k, v }) => [encode(k), encode(v)]))
|
|
179
|
+
} else {
|
|
180
|
+
throw new Error("Unrecognized Uplc.Data type")
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export const NODE_MEM_SIZE = 4
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Simple recursive algorithm
|
|
188
|
+
* @param data
|
|
189
|
+
* @returns
|
|
190
|
+
*/
|
|
191
|
+
export function memSize(data: Data): number {
|
|
192
|
+
if ("bytes" in data) {
|
|
193
|
+
return NODE_MEM_SIZE + calcBytesMemSize(data.bytes)
|
|
194
|
+
} else if ("fields" in data) {
|
|
195
|
+
return data.fields.reduce(
|
|
196
|
+
(prev, field) => prev + memSize(field),
|
|
197
|
+
NODE_MEM_SIZE
|
|
198
|
+
)
|
|
199
|
+
} else if ("int" in data) {
|
|
200
|
+
return NODE_MEM_SIZE + calcIntMemSize(data.int)
|
|
201
|
+
} else if ("list" in data) {
|
|
202
|
+
return data.list.reduce((prev, item) => prev + memSize(item), NODE_MEM_SIZE)
|
|
203
|
+
} else if ("map" in data) {
|
|
204
|
+
return data.map.reduce(
|
|
205
|
+
(prev, { k, v }) => prev + memSize(k) + memSize(v),
|
|
206
|
+
NODE_MEM_SIZE
|
|
207
|
+
)
|
|
208
|
+
} else {
|
|
209
|
+
throw new Error("Unrecognized Uplc.Data type")
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Calculates the mem size of a byte array without the DATA_NODE overhead.
|
|
215
|
+
* @param bytes
|
|
216
|
+
* @returns
|
|
217
|
+
*/
|
|
218
|
+
export function calcBytesMemSize(
|
|
219
|
+
bytes: string | readonly number[] | Uint8Array
|
|
220
|
+
): number {
|
|
221
|
+
const n = Bytes.toArray(bytes).length
|
|
222
|
+
|
|
223
|
+
if (n === 0) {
|
|
224
|
+
return 1 // this is so annoying: haskell reference implementation says it should be 0, but current (20220925) testnet and mainnet settings say it's 1
|
|
225
|
+
} else {
|
|
226
|
+
return Math.floor((n - 1) / 8) + 1
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Calculate the mem size of a integer (without the DATA_NODE overhead)
|
|
232
|
+
* @param value
|
|
233
|
+
* @returns
|
|
234
|
+
*/
|
|
235
|
+
export function calcIntMemSize(value: bigint) {
|
|
236
|
+
if (value == 0n) {
|
|
237
|
+
return 1
|
|
238
|
+
} else {
|
|
239
|
+
const abs = value > 0n ? value : -value
|
|
240
|
+
|
|
241
|
+
return Math.floor(log2i(abs) / 64) + 1
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Math.log2 truncates, but we need a rounding down version
|
|
247
|
+
* @param x positive number
|
|
248
|
+
* @returns
|
|
249
|
+
*/
|
|
250
|
+
export function log2i(x: bigint): number {
|
|
251
|
+
let p = 0
|
|
252
|
+
|
|
253
|
+
while (x > 1n) {
|
|
254
|
+
x >>= 1n
|
|
255
|
+
p++
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return p
|
|
259
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test"
|
|
2
|
+
import { Schema } from "effect"
|
|
3
|
+
import * as DataSchema from "./DataSchema.js"
|
|
4
|
+
|
|
5
|
+
describe("Uplc.DataSchema.BigInt", () => {
|
|
6
|
+
it("succeeds for {int: 0n}", () => {
|
|
7
|
+
expect(Schema.decodeSync(DataSchema.BigInt)({ int: 0n })).toBe(0n)
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it("fails for {int: '0'}", () => {
|
|
11
|
+
expect(() =>
|
|
12
|
+
Schema.decodeUnknownSync(DataSchema.BigInt)({ int: "0" })
|
|
13
|
+
).toThrow()
|
|
14
|
+
})
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
describe("Uplc.DataSchema.Int", () => {
|
|
18
|
+
it("succeeds for {int: 0n}", () => {
|
|
19
|
+
expect(Schema.decodeSync(DataSchema.Int)({ int: 0n })).toBe(0)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it("fails for {int: '0'}", () => {
|
|
23
|
+
expect(() =>
|
|
24
|
+
Schema.decodeUnknownSync(DataSchema.Int)({ int: "0" })
|
|
25
|
+
).toThrow()
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
describe("Uplc.DataSchema.String", () => {
|
|
30
|
+
it("succeeds for {bytes: ''}", () => {
|
|
31
|
+
expect(Schema.decodeSync(DataSchema.String)({ bytes: "" })).toBe("")
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it("fails for {bytes: 'ff'}", () => {
|
|
35
|
+
expect(() =>
|
|
36
|
+
Schema.decodeSync(DataSchema.String)({ bytes: "ff" })
|
|
37
|
+
).toThrow()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it("succeeds for {bytes: '48656C6C6F20576F726C64'}", () => {
|
|
41
|
+
expect(
|
|
42
|
+
Schema.decodeSync(DataSchema.String)({ bytes: "48656C6C6F20576F726C64" })
|
|
43
|
+
).toBe("Hello World")
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
describe("Uplc.DataSchema.Array", () => {
|
|
48
|
+
it("succeeds for empty ListData", () => {
|
|
49
|
+
expect(
|
|
50
|
+
Schema.decodeSync(DataSchema.Array(DataSchema.String))({ list: [] })
|
|
51
|
+
).toEqual([])
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it("succeeds for ListData containg single 'Hello World' string", () => {
|
|
55
|
+
expect(
|
|
56
|
+
Schema.decodeSync(DataSchema.Array(DataSchema.String))({
|
|
57
|
+
list: [{ bytes: "48656C6C6F20576F726C64" }]
|
|
58
|
+
})
|
|
59
|
+
).toEqual(["Hello World"])
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it("fails if ListData items are heterogenous", () => {
|
|
63
|
+
expect(() =>
|
|
64
|
+
Schema.decodeSync(DataSchema.Array(DataSchema.String))({
|
|
65
|
+
list: [{ bytes: "48656C6C6F20576F726C64" }, { int: 0n }]
|
|
66
|
+
})
|
|
67
|
+
).toThrow()
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
describe("Uplc.DataSchema.Struct", () => {
|
|
72
|
+
it("succeeds for empty ListData for empty Struct", () => {
|
|
73
|
+
expect(Schema.decodeSync(DataSchema.Struct({}))({ list: [] })).toEqual({})
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it("fails for empty ListData if one field is defined", () => {
|
|
77
|
+
expect(() =>
|
|
78
|
+
Schema.decodeSync(DataSchema.Struct({ foo: DataSchema.String }))({
|
|
79
|
+
list: []
|
|
80
|
+
})
|
|
81
|
+
).toThrow()
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it("succeeds for ListData with single entry if one field is defined", () => {
|
|
85
|
+
expect(
|
|
86
|
+
Schema.decodeSync(DataSchema.Struct({ foo: DataSchema.String }))({
|
|
87
|
+
list: [{ bytes: "48656C6C6F20576F726C64" }]
|
|
88
|
+
})
|
|
89
|
+
).toEqual({ foo: "Hello World" })
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it("fails for ListData with wrong entry in first place with one field is defined", () => {
|
|
93
|
+
expect(() =>
|
|
94
|
+
Schema.decodeSync(DataSchema.Struct({ foo: DataSchema.String }))({
|
|
95
|
+
list: [{ int: 0n }]
|
|
96
|
+
})
|
|
97
|
+
).toThrow()
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it("succeeds for ListData with spurious entries at end with one field is defined", () => {
|
|
101
|
+
expect(
|
|
102
|
+
Schema.decodeSync(DataSchema.Struct({ foo: DataSchema.String }))({
|
|
103
|
+
list: [{ bytes: "48656C6C6F20576F726C64" }, { int: 0n }]
|
|
104
|
+
})
|
|
105
|
+
).toEqual({ foo: "Hello World" })
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it("succeeds for ListData with two entries with two fields", () => {
|
|
109
|
+
expect(
|
|
110
|
+
Schema.decodeSync(
|
|
111
|
+
DataSchema.Struct({ foo: DataSchema.String, bar: DataSchema.Int })
|
|
112
|
+
)({ list: [{ bytes: "48656C6C6F20576F726C64" }, { int: 0n }] })
|
|
113
|
+
).toEqual({ foo: "Hello World", bar: 0 })
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it("fails for ListData with two entries in wrong order with two fields", () => {
|
|
117
|
+
expect(() =>
|
|
118
|
+
Schema.decodeSync(
|
|
119
|
+
DataSchema.Struct({ bar: DataSchema.Int, foo: DataSchema.String })
|
|
120
|
+
)({ list: [{ bytes: "48656C6C6F20576F726C64" }, { int: 0n }] })
|
|
121
|
+
).toThrow()
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
describe("Uplc.DataSchema.EnumVariant", () => {
|
|
126
|
+
it("succeeds for empty ConstrData for empty EnumVariant", () => {
|
|
127
|
+
expect(
|
|
128
|
+
Schema.decodeSync(DataSchema.EnumVariant(0, {}))({
|
|
129
|
+
constructor: 0,
|
|
130
|
+
fields: []
|
|
131
|
+
})
|
|
132
|
+
).toEqual({})
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it("fields for ConstrData with wrong tag", () => {
|
|
136
|
+
expect(() =>
|
|
137
|
+
Schema.decodeSync(DataSchema.EnumVariant(0, {}))({
|
|
138
|
+
constructor: 1,
|
|
139
|
+
fields: []
|
|
140
|
+
})
|
|
141
|
+
).toThrow()
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it("fails for empty ConstrData if one field is defined", () => {
|
|
145
|
+
expect(() =>
|
|
146
|
+
Schema.decodeSync(DataSchema.EnumVariant(0, { foo: DataSchema.String }))({
|
|
147
|
+
constructor: 0,
|
|
148
|
+
fields: []
|
|
149
|
+
})
|
|
150
|
+
).toThrow()
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it("succeeds for ConstrData with single entry if one field is defined", () => {
|
|
154
|
+
expect(
|
|
155
|
+
Schema.decodeSync(DataSchema.EnumVariant(0, { foo: DataSchema.String }))({
|
|
156
|
+
constructor: 0,
|
|
157
|
+
fields: [{ bytes: "48656C6C6F20576F726C64" }]
|
|
158
|
+
})
|
|
159
|
+
).toEqual({ foo: "Hello World" })
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it("fails for ConstrData with wrong entry in first place with one field is defined", () => {
|
|
163
|
+
expect(() =>
|
|
164
|
+
Schema.decodeSync(DataSchema.EnumVariant(0, { foo: DataSchema.String }))({
|
|
165
|
+
constructor: 0,
|
|
166
|
+
fields: [{ int: 0n }]
|
|
167
|
+
})
|
|
168
|
+
).toThrow()
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it("succeeds for EnumVariant with spurious entries at end with one field is defined", () => {
|
|
172
|
+
expect(
|
|
173
|
+
Schema.decodeSync(DataSchema.EnumVariant(0, { foo: DataSchema.String }))({
|
|
174
|
+
constructor: 0,
|
|
175
|
+
fields: [{ bytes: "48656C6C6F20576F726C64" }, { int: 0n }]
|
|
176
|
+
})
|
|
177
|
+
).toEqual({ foo: "Hello World" })
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it("succeeds for EnumVariant with two entries with two fields", () => {
|
|
181
|
+
expect(
|
|
182
|
+
Schema.decodeSync(
|
|
183
|
+
DataSchema.EnumVariant(0, {
|
|
184
|
+
foo: DataSchema.String,
|
|
185
|
+
bar: DataSchema.Int
|
|
186
|
+
})
|
|
187
|
+
)({
|
|
188
|
+
constructor: 0,
|
|
189
|
+
fields: [{ bytes: "48656C6C6F20576F726C64" }, { int: 0n }]
|
|
190
|
+
})
|
|
191
|
+
).toEqual({ foo: "Hello World", bar: 0 })
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
it("fails for EnumVariant with two entries in wrong order with two fields", () => {
|
|
195
|
+
expect(() =>
|
|
196
|
+
Schema.decodeSync(
|
|
197
|
+
DataSchema.EnumVariant(0, {
|
|
198
|
+
bar: DataSchema.Int,
|
|
199
|
+
foo: DataSchema.String
|
|
200
|
+
})
|
|
201
|
+
)({
|
|
202
|
+
constructor: 0,
|
|
203
|
+
fields: [{ bytes: "48656C6C6F20576F726C64" }, { int: 0n }]
|
|
204
|
+
})
|
|
205
|
+
).toThrow()
|
|
206
|
+
})
|
|
207
|
+
})
|