@helios-lang/contract-utils 0.2.71 → 0.2.73

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.71",
3
+ "version": "0.2.73",
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",
@@ -12,8 +12,8 @@
12
12
  "license": "BSD-3-Clause",
13
13
  "devDependencies": {
14
14
  "@types/node": "^20.11.24",
15
- "prettier": "^3.1.0",
16
- "typescript": "^5.3.2"
15
+ "prettier": "^3.3.3",
16
+ "typescript": "^5.6.2"
17
17
  },
18
18
  "prettier": {
19
19
  "trailingComma": "none",
@@ -25,12 +25,13 @@
25
25
  "@helios-lang/compiler": "*"
26
26
  },
27
27
  "dependencies": {
28
- "@helios-lang/cli-utils": "^0.1.6",
29
- "@helios-lang/codec-utils": "^0.1.35",
30
- "@helios-lang/compiler-utils": "^0.1.61",
31
- "@helios-lang/ledger": "^0.5.6",
32
- "@helios-lang/type-utils": "^0.1.23",
33
- "@helios-lang/uplc": "^0.4.13"
28
+ "@helios-lang/cli-utils": "^0.1.10",
29
+ "@helios-lang/codec-utils": "^0.2.1",
30
+ "@helios-lang/compiler-utils": "^0.2.1",
31
+ "@helios-lang/crypto": "0.1.17",
32
+ "@helios-lang/ledger": "^0.5.8",
33
+ "@helios-lang/type-utils": "^0.1.25",
34
+ "@helios-lang/uplc": "^0.6.1"
34
35
  },
35
36
  "scripts": {
36
37
  "build": "pnpm run prettify && pnpm run build:types && pnpm run test:suite",
@@ -1,6 +1,6 @@
1
1
  import { bytesToHex } from "@helios-lang/codec-utils"
2
2
  import { None } from "@helios-lang/type-utils"
3
- import { UplcProgramV2, UplcSourceMap } from "@helios-lang/uplc"
3
+ import { UplcSourceMap } from "@helios-lang/uplc"
4
4
 
5
5
  /**
6
6
  * @typedef {import("@helios-lang/uplc").UplcData} UplcData
@@ -24,7 +24,7 @@ import { UplcProgramV2, UplcSourceMap } from "@helios-lang/uplc"
24
24
  * validatorIndices?: Record<string, number>
25
25
  * excludeUserFuncs?: Set<string>
26
26
  * onCompileUserFunc?: (name: string, uplc: UplcProgramV2I) => void
27
- * }): UplcProgramV2
27
+ * }): UplcProgramV2I
28
28
  * }} Program
29
29
  */
30
30
 
@@ -90,7 +90,7 @@ export class CompilerLib_v0_17 {
90
90
  const onCompileUserFunc = options.onCompileUserFunc
91
91
 
92
92
  /**
93
- * @type {UplcProgramV2}
93
+ * @type {UplcProgramV2I}
94
94
  */
95
95
  const uplc = program.compile({
96
96
  optimize: options.optimize,
@@ -0,0 +1,310 @@
1
+ import { bytesToHex } from "@helios-lang/codec-utils"
2
+ import {
3
+ MintingPolicyHash,
4
+ ScriptHash,
5
+ StakingValidatorHash,
6
+ ValidatorHash
7
+ } from "@helios-lang/ledger"
8
+ import { None, expectSome } from "@helios-lang/type-utils"
9
+ import { UplcProgramV1, UplcProgramV2, UplcSourceMap } from "@helios-lang/uplc"
10
+ import { Cast } from "../cast/Cast.js"
11
+
12
+ /**
13
+ * @typedef {import("@helios-lang/type-utils").TypeSchema} TypeSchema
14
+ * @typedef {import("@helios-lang/uplc").UplcData} UplcData
15
+ * @typedef {import("@helios-lang/uplc").PlutusVersion} PlutusVersion
16
+ * @typedef {import("@helios-lang/uplc").UplcProgram} UplcProgram
17
+ * @typedef {import("@helios-lang/uplc").UplcProgramV1I} UplcProgramV1I
18
+ * @typedef {import("@helios-lang/uplc").UplcProgramV2I} UplcProgramV2I
19
+ * @typedef {import("@helios-lang/uplc").UplcSourceMapJsonSafe} UplcSourceMapJsonSafe
20
+ * @typedef {import("../cast/index.js").CastConfig} CastConfig
21
+ * @typedef {import("../codegen/LoadedModule.js").LoadedModule} LoadedModule
22
+ * @typedef {import("../codegen/LoadedValidator.js").LoadedValidator} LoadedValidator
23
+ * @typedef {import("./ContractContext.js").AnyContractValidatorContext} AnyContractValidatorContext
24
+ */
25
+
26
+ /**
27
+ * @typedef {{
28
+ * debug?: boolean
29
+ * validators: {[name: string]: AnyContractValidatorContext}
30
+ * userFuncs: {[name: string]: UplcProgram}
31
+ * }} CacheEntry
32
+ */
33
+
34
+ /**
35
+ * @typedef {{[name: string]: {
36
+ * purpose: "minting" | "spending" | "staking" | "mixed"
37
+ * bytes: number[]
38
+ * unoptimizedCborHex?: string
39
+ * unoptimizedIr?: string
40
+ * unoptimizedSourceMap?: UplcSourceMapJsonSafe
41
+ * optimizedCborHex: string
42
+ * optimizedIr?: string
43
+ * optimizedSourceMap?: UplcSourceMapJsonSafe
44
+ * plutusVersion: PlutusVersion
45
+ * castConfig: CastConfig
46
+ * datum?: TypeSchema
47
+ * redeemer: TypeSchema
48
+ * }}} CacheEntryValidatorsJson
49
+ */
50
+
51
+ /**
52
+ * @typedef {{[name: string]: {
53
+ * unoptimizedCborHex?: string
54
+ * unoptimizedIr?: string
55
+ * unoptimizedSourceMap?: UplcSourceMapJsonSafe
56
+ * optimizedCborHex: string
57
+ * optimizedIr?: string
58
+ * optimizedSourceMap?: UplcSourceMapJsonSafe
59
+ * plutusVersion: PlutusVersion
60
+ * }}} CacheEntryUserFuncsJson
61
+ */
62
+
63
+ /**
64
+ * @typedef {{
65
+ * debug?: boolean
66
+ * validators: CacheEntryValidatorsJson
67
+ * userFuncs: CacheEntryUserFuncsJson
68
+ * }} CacheEntryJson
69
+ */
70
+
71
+ /**
72
+ * @param {CacheEntry} entry
73
+ * @returns {CacheEntryJson}
74
+ */
75
+ export function cacheEntryToJsonSafe(entry) {
76
+ /**
77
+ * @type {CacheEntryValidatorsJson}
78
+ */
79
+ const resValidators = {}
80
+
81
+ /**
82
+ * @type {CacheEntryUserFuncsJson}
83
+ */
84
+ const resUserFuncs = {}
85
+
86
+ for (let name in entry.validators) {
87
+ const hash = entry.validators[name]
88
+
89
+ const purpose =
90
+ hash instanceof ValidatorHash
91
+ ? "spending"
92
+ : hash instanceof MintingPolicyHash
93
+ ? "minting"
94
+ : hash instanceof StakingValidatorHash
95
+ ? "staking"
96
+ : hash instanceof ScriptHash
97
+ ? "mixed"
98
+ : "unknown"
99
+ if (purpose == "unknown") {
100
+ throw new Error("unhandled hash type")
101
+ }
102
+
103
+ const redeemer =
104
+ hash.context.redeemer instanceof Cast ? hash.context.redeemer : None
105
+
106
+ if (!redeemer) {
107
+ throw new Error("Redeemer is not a Cast type")
108
+ }
109
+
110
+ /**
111
+ * @type {Option<Cast>}
112
+ */
113
+ let datum = None
114
+
115
+ if (hash.context.datum && hash.context.datum instanceof Cast) {
116
+ datum = hash.context.datum
117
+ }
118
+
119
+ if (["spending", "mixed"].includes(purpose) && !datum) {
120
+ throw new Error("Datum is not a Cast type")
121
+ }
122
+
123
+ /**
124
+ * @type {UplcProgramV1I | UplcProgramV2I}
125
+ */
126
+ const program = hash.context.program
127
+
128
+ resValidators[name] = {
129
+ purpose: purpose,
130
+ bytes: hash.bytes,
131
+ optimizedCborHex: bytesToHex(program.toCbor()),
132
+ unoptimizedCborHex: program.alt
133
+ ? bytesToHex(program.alt.toCbor())
134
+ : undefined,
135
+ optimizedIr:
136
+ /** @type {UplcProgramV2I} */ (program).ir ?? undefined,
137
+ unoptimizedIr:
138
+ /** @type {UplcProgramV2I} */ (program).alt?.ir ?? undefined,
139
+ optimizedSourceMap: UplcSourceMap.fromUplcTerm(
140
+ program.root
141
+ ).toJsonSafe(),
142
+ unoptimizedSourceMap: program.alt
143
+ ? UplcSourceMap.fromUplcTerm(program.alt.root).toJsonSafe()
144
+ : undefined,
145
+ plutusVersion: hash.context.program.plutusVersion,
146
+ castConfig: redeemer.config,
147
+ redeemer: redeemer.schema,
148
+ datum: datum?.schema
149
+ }
150
+ }
151
+
152
+ for (let name in entry.userFuncs) {
153
+ const userFunc = entry.userFuncs[name]
154
+
155
+ resUserFuncs[name] = {
156
+ optimizedCborHex: bytesToHex(userFunc.toCbor()),
157
+ unoptimizedCborHex: /** @type {UplcProgramV2I} */ (userFunc).alt
158
+ ? bytesToHex(
159
+ /** @type {UplcProgramV2I} */ (userFunc).alt.toCbor()
160
+ )
161
+ : undefined,
162
+ optimizedIr: userFunc.ir ?? undefined,
163
+ unoptimizedIr: userFunc.alt?.ir ?? undefined,
164
+ optimizedSourceMap: UplcSourceMap.fromUplcTerm(
165
+ userFunc.root
166
+ ).toJsonSafe(),
167
+ unoptimizedSourceMap: userFunc.alt
168
+ ? UplcSourceMap.fromUplcTerm(userFunc.alt.root).toJsonSafe()
169
+ : undefined,
170
+ plutusVersion: userFunc.plutusVersion
171
+ }
172
+ }
173
+
174
+ return {
175
+ debug: entry.debug ?? false,
176
+ validators: resValidators,
177
+ userFuncs: resUserFuncs
178
+ }
179
+ }
180
+
181
+ /**
182
+ * @param {CacheEntryJson} entry
183
+ * @returns {CacheEntry}
184
+ */
185
+ export function cacheEntryFromJson(entry) {
186
+ /**
187
+ * @type {CacheEntry}
188
+ */
189
+ const res = {
190
+ debug: entry.debug ?? false,
191
+ validators: {},
192
+ userFuncs: {}
193
+ }
194
+
195
+ for (let name in entry.validators) {
196
+ const props = entry.validators[name]
197
+
198
+ /**
199
+ * @type {UplcProgram}
200
+ */
201
+ let program =
202
+ props.plutusVersion == "PlutusScriptV1"
203
+ ? UplcProgramV1.fromCbor(props.optimizedCborHex, {
204
+ ir: props.optimizedIr,
205
+ sourceMap: props.optimizedSourceMap
206
+ })
207
+ : UplcProgramV2.fromCbor(props.optimizedCborHex, {
208
+ ir: props.optimizedIr,
209
+ sourceMap: props.optimizedSourceMap
210
+ })
211
+
212
+ if (props.unoptimizedCborHex) {
213
+ if (program.plutusVersion == "PlutusScriptV1") {
214
+ program = program.withAlt(
215
+ UplcProgramV1.fromCbor(props.unoptimizedCborHex, {
216
+ ir: props.unoptimizedIr,
217
+ sourceMap: props.unoptimizedSourceMap
218
+ })
219
+ )
220
+ } else if (program.plutusVersion == "PlutusScriptV2") {
221
+ program = program.withAlt(
222
+ UplcProgramV2.fromCbor(props.unoptimizedCborHex, {
223
+ ir: props.unoptimizedIr,
224
+ sourceMap: props.unoptimizedSourceMap
225
+ })
226
+ )
227
+ } else {
228
+ throw new Error("unhandled Plutus version")
229
+ }
230
+ }
231
+
232
+ let redeemer = new Cast(props.redeemer, props.castConfig)
233
+
234
+ switch (props.purpose) {
235
+ case "spending": {
236
+ res.validators[name] = new ValidatorHash(props.bytes, {
237
+ datum: new Cast(expectSome(props.datum), props.castConfig),
238
+ redeemer: redeemer,
239
+ program: program
240
+ })
241
+ break
242
+ }
243
+ case "minting": {
244
+ res.validators[name] = new MintingPolicyHash(props.bytes, {
245
+ redeemer: redeemer,
246
+ program: program
247
+ })
248
+ break
249
+ }
250
+ case "staking": {
251
+ res.validators[name] = new StakingValidatorHash(props.bytes, {
252
+ redeemer: redeemer,
253
+ program: program
254
+ })
255
+ break
256
+ }
257
+ case "mixed": {
258
+ res.validators[name] = new ScriptHash(props.bytes, {
259
+ datum: new Cast(expectSome(props.datum), props.castConfig),
260
+ redeemer: redeemer,
261
+ program: program
262
+ })
263
+ break
264
+ }
265
+ default:
266
+ throw new Error("unhandled purpose")
267
+ }
268
+ }
269
+
270
+ for (let name in entry.userFuncs) {
271
+ const props = entry.userFuncs[name]
272
+
273
+ /**
274
+ * @type {UplcProgram}
275
+ */
276
+ let program =
277
+ props.plutusVersion == "PlutusScriptV1"
278
+ ? UplcProgramV1.fromCbor(props.optimizedCborHex, {
279
+ ir: props.optimizedIr,
280
+ sourceMap: props.optimizedSourceMap
281
+ })
282
+ : UplcProgramV2.fromCbor(props.optimizedCborHex, {
283
+ ir: props.optimizedIr,
284
+ sourceMap: props.optimizedSourceMap
285
+ })
286
+
287
+ if (props.unoptimizedCborHex) {
288
+ if (program.plutusVersion == "PlutusScriptV1") {
289
+ program = program.withAlt(
290
+ UplcProgramV1.fromCbor(props.unoptimizedCborHex, {
291
+ ir: props.unoptimizedIr,
292
+ sourceMap: props.unoptimizedSourceMap
293
+ })
294
+ )
295
+ } else if (program.plutusVersion == "PlutusScriptV2") {
296
+ program = program.withAlt(
297
+ UplcProgramV2.fromCbor(props.unoptimizedCborHex, {
298
+ ir: props.unoptimizedIr,
299
+ sourceMap: props.unoptimizedSourceMap
300
+ })
301
+ )
302
+ } else {
303
+ throw new Error("unhandled Plutus version")
304
+ }
305
+ }
306
+ res.userFuncs[name] = program
307
+ }
308
+
309
+ return res
310
+ }
@@ -47,6 +47,7 @@ export class ContractContextBuilder {
47
47
  validators
48
48
 
49
49
  /**
50
+ * @private
50
51
  * @readonly
51
52
  * @type {Ms}
52
53
  */
@@ -118,12 +119,22 @@ export class ContractContextBuilder {
118
119
  const castConfig = { isMainnet: props.isMainnet }
119
120
  const lib = loadCompilerLib()
120
121
 
122
+ // generate a cache key
123
+ const key = contractContextCache.genCacheKey({
124
+ version: lib.version,
125
+ debug: !!props.debug,
126
+ isMainnet: props.isMainnet,
127
+ validators: this.validators,
128
+ modules: this.modules,
129
+ parameters: props.parameters ?? {}
130
+ })
131
+
121
132
  const dagCompiler = new DagCompiler(lib, {
122
133
  debug: props.debug ?? false,
123
134
  ...castConfig
124
135
  })
125
136
 
126
- const cached = contractContextCache.shift()
137
+ const cached = contractContextCache.get(key)
127
138
 
128
139
  const compiled =
129
140
  cached ||
@@ -134,8 +145,7 @@ export class ContractContextBuilder {
134
145
  props.expectedHashes
135
146
  )
136
147
 
137
- // TODO: adapt to use generic cache interface
138
- contractContextCache.push(compiled)
148
+ contractContextCache.set(key, compiled)
139
149
 
140
150
  if (props.dumpHashes) {
141
151
  for (let name in compiled.validators) {