@midnight-ntwrk/midnight-js-protocol 5.0.0-beta.7 → 5.0.0-beta.8

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/dist/index.js CHANGED
@@ -1,11 +1,796 @@
1
+ import { StateDecodeFailedError, ComposeFailedError, ComposeOptionError, NO_CIRCUIT, DownConvertFailedError, UnknownLedgerVersionError, Ledger8RuntimeInvalidError, Ledger8RuntimeMissingError, Ledger8InstanceMismatchError } from './errors.js';
2
+ export { MerkleNotRehashedError, PROTOCOL_ERROR_CODES, PayloadNotATransactionError, TRANSACTION_TAG_PREFIX, UnknownLedger8AxisError, UnknownProtocolVersionError } from './errors.js';
3
+ import * as ledgerV9 from '@midnightntwrk/ledger-v9';
4
+ import { ContractState } from '@midnightntwrk/ledger-v9';
5
+ export { ledgerV9 as ledger };
6
+ import { b as entryPointName, c as assertComposeEnvelope, a as assembleCallPrototype, d as composeV8DeployTx, r as resolveVerifierKeyRegistrations, p as partitionCallTranscript } from './shared/deploy-D6yyn9P7.js';
7
+ export { I as INITIAL_LEDGER_PARAMETERS } from './shared/deploy-D6yyn9P7.js';
8
+ import { hashVerifierKey } from '@midnight-ntwrk/compact-js';
1
9
  import * as compactJs from '@midnight-ntwrk/compact-js';
2
10
  export { compactJs };
11
+ import { l as loadLedger8 } from './shared/load-C0TZnEd2.js';
12
+ export { CURRENT_LEDGER_VERSION, LEDGER_VERSIONS, RETAINED_LEDGER_VERSIONS, networkHeadVersion, protocolVersionToLedger, versionOfRecord } from './version.js';
3
13
  import * as compactRuntime from '@midnight-ntwrk/compact-runtime';
4
14
  export { compactRuntime };
5
15
  import * as platformJs from '@midnight-ntwrk/platform-js';
6
16
  export { platformJs as platform };
7
- import * as ledgerV9 from '@midnightntwrk/ledger-v9';
8
- export { ledgerV9 as ledger };
9
17
  import * as onchainRuntimeV4 from '@midnightntwrk/onchain-runtime-v4';
10
18
  export { onchainRuntimeV4 as onchainRuntime };
19
+
20
+ /*
21
+ * This file is part of midnight-js.
22
+ * Copyright (C) Midnight Foundation
23
+ * SPDX-License-Identifier: Apache-2.0
24
+ * Licensed under the Apache License, Version 2.0 (the "License");
25
+ * You may not use this file except in compliance with the License.
26
+ * You may obtain a copy of the License at
27
+ * http://www.apache.org/licenses/LICENSE-2.0
28
+ * Unless required by applicable law or agreed to in writing, software
29
+ * distributed under the License is distributed on an "AS IS" BASIS,
30
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31
+ * See the License for the specific language governing permissions and
32
+ * limitations under the License.
33
+ */
34
+ /**
35
+ * Reads the primary state out of a raw envelope with the given era's extractor,
36
+ * reporting every failure as {@link StateDecodeFailedError} naming `version`.
37
+ *
38
+ * @param raw The serialized contract-state envelope.
39
+ * @param version The era whose extractor is used, and which every failure
40
+ * raised here names.
41
+ * @param extract The era's own envelope extractor.
42
+ * @returns The primary state read out of the envelope.
43
+ * @throws StateDecodeFailedError for every failure, carrying the extractor's
44
+ * own diagnosis on `cause`.
45
+ * @see {@link FailClosedDecoding}
46
+ */
47
+ const extractStateWith = (raw, version, extract) => {
48
+ try {
49
+ return extract(raw);
50
+ }
51
+ catch (cause) {
52
+ throw new StateDecodeFailedError(version, cause);
53
+ }
54
+ };
55
+ /**
56
+ * Reads a raw, serialized contract-state envelope into a
57
+ * {@link ContractStatePojo} using the given era's own `ContractState`.
58
+ *
59
+ * Nothing that crosses back is a live WASM handle: the primary state leaves as
60
+ * an `EncodedStateValue` (plain objects, arrays, `Map`s, `Uint8Array`s and
61
+ * primitives) and each entry point as a plain record.
62
+ *
63
+ * @param raw The serialized contract-state envelope.
64
+ * @param version The era whose decoder is used, and which every failure raised
65
+ * here names.
66
+ * @param ledger The era module slice carrying its own `ContractState`.
67
+ * @returns The state and the entry points it declares, as plain data.
68
+ * @throws StateDecodeFailedError for every failure — the whole read is
69
+ * covered, not just the deserialization — with the decoder's own diagnosis on
70
+ * `cause`.
71
+ * @see {@link FailClosedDecoding}
72
+ * @see {@link EraSeam}
73
+ */
74
+ const decodeContractStateWith = (raw, version, ledger) => {
75
+ try {
76
+ const decoded = ledger.ContractState.deserialize(raw);
77
+ const entryPoints = decoded.operations().map((entryPoint) => {
78
+ // Deliberately not optional-chained: an unresolvable entry point is an
79
+ // inconsistent state, not a blank slot -- see FailClosedDecoding.
80
+ const operation = decoded.operation(entryPoint);
81
+ if (operation === undefined) {
82
+ throw new Error(`contract state declares entry point '${entryPointName(entryPoint)}' but resolves no operation for it.`);
83
+ }
84
+ const { verifierKey } = operation;
85
+ return {
86
+ circuitId: entryPointName(entryPoint),
87
+ verifierKey,
88
+ verifierKeyHash: verifierKey === undefined ? undefined : hashVerifierKey(verifierKey)
89
+ };
90
+ });
91
+ return { state: decoded.data.state.encode(), entryPoints };
92
+ }
93
+ catch (cause) {
94
+ throw new StateDecodeFailedError(version, cause);
95
+ }
96
+ };
97
+
98
+ /*
99
+ * This file is part of midnight-js.
100
+ * Copyright (C) Midnight Foundation
101
+ * SPDX-License-Identifier: Apache-2.0
102
+ * Licensed under the Apache License, Version 2.0 (the "License");
103
+ * You may not use this file except in compliance with the License.
104
+ * You may obtain a copy of the License at
105
+ * http://www.apache.org/licenses/LICENSE-2.0
106
+ * Unless required by applicable law or agreed to in writing, software
107
+ * distributed under the License is distributed on an "AS IS" BASIS,
108
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
109
+ * See the License for the specific language governing permissions and
110
+ * limitations under the License.
111
+ */
112
+ /**
113
+ * Reads the UTXO outputs a call's transcript claims on behalf of USERS.
114
+ *
115
+ * @param transcript One half of a call's transcript pair. Absent is the normal
116
+ * shape of a call with no fallible half, and yields no outputs.
117
+ * @param version The era every failure raised here names.
118
+ * @param circuitId The entry point every failure raised here names.
119
+ * @returns One `UtxoOutput` per user-addressed unshielded spend the transcript
120
+ * claims. Contract-addressed spends are skipped.
121
+ * @throws ComposeFailedError at stage `'call-dust-payout'` for a user-addressed
122
+ * dust spend.
123
+ * @throws ComposeFailedError at stage `'call-unsupported-payout'` for a
124
+ * user-addressed spend of any token type other than unshielded.
125
+ * @see {@link ComposeRefusalOrder}
126
+ */
127
+ const extractUserAddressedOutputs = (transcript, version, circuitId) => {
128
+ if (transcript === undefined) {
129
+ return [];
130
+ }
131
+ const outputs = [];
132
+ for (const [[tokenType, publicAddress], value] of transcript.effects.claimedUnshieldedSpends) {
133
+ if (publicAddress.tag !== 'user') {
134
+ continue;
135
+ }
136
+ if (tokenType.tag === 'dust') {
137
+ throw new ComposeFailedError(version, 'call-dust-payout', circuitId);
138
+ }
139
+ if (tokenType.tag !== 'unshielded') {
140
+ throw new ComposeFailedError(version, 'call-unsupported-payout', circuitId);
141
+ }
142
+ outputs.push({ value, owner: publicAddress.address, type: tokenType.raw });
143
+ }
144
+ return outputs;
145
+ };
146
+ /**
147
+ * Builds the one unshielded offer each segment of a transaction carries, from
148
+ * EVERY call in the tree.
149
+ *
150
+ * @typeParam TOffer The offer type `ledger.UnshieldedOffer.new` returns.
151
+ * @param calls Every call in the transaction's tree — cross-contract callees
152
+ * as well as the root call, since any of them can produce a payout.
153
+ * @param ledger The era module slice carrying `UnshieldedOffer`.
154
+ * @param version The era every failure raised here names.
155
+ * @returns The guaranteed and fallible offers. A segment with nothing to pay
156
+ * out gets no offer at all rather than an empty one.
157
+ * @throws ComposeFailedError from {@link extractUserAddressedOutputs} for a
158
+ * user-addressed spend this seam cannot pay out.
159
+ * @see {@link ComposeRefusalOrder}
160
+ */
161
+ const aggregateUnshieldedOffers = (calls, ledger, version) => {
162
+ const guaranteedOutputs = calls.flatMap((call) => extractUserAddressedOutputs(call.guaranteed, version, call.circuitId));
163
+ const fallibleOutputs = calls.flatMap((call) => extractUserAddressedOutputs(call.fallible, version, call.circuitId));
164
+ return {
165
+ guaranteed: guaranteedOutputs.length > 0 ? ledger.UnshieldedOffer.new([], guaranteedOutputs, []) : undefined,
166
+ fallible: fallibleOutputs.length > 0 ? ledger.UnshieldedOffer.new([], fallibleOutputs, []) : undefined
167
+ };
168
+ };
169
+
170
+ /*
171
+ * This file is part of midnight-js.
172
+ * Copyright (C) Midnight Foundation
173
+ * SPDX-License-Identifier: Apache-2.0
174
+ * Licensed under the Apache License, Version 2.0 (the "License");
175
+ * You may not use this file except in compliance with the License.
176
+ * You may obtain a copy of the License at
177
+ * http://www.apache.org/licenses/LICENSE-2.0
178
+ * Unless required by applicable law or agreed to in writing, software
179
+ * distributed under the License is distributed on an "AS IS" BASIS,
180
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
181
+ * See the License for the specific language governing permissions and
182
+ * limitations under the License.
183
+ */
184
+ /**
185
+ * Composes a v8-native call transaction from one call's inputs and immediately
186
+ * serializes it. The call prototype comes from {@link assembleCallPrototype}
187
+ * against the injected v8 module.
188
+ *
189
+ * Never proves the transaction: the returned bytes are an UNPROVEN,
190
+ * tag-prefixed serialization, exactly what `Transaction.serialize()` produces
191
+ * before `.prove()` is ever called.
192
+ *
193
+ * @param options The call's inputs, offers and envelope options.
194
+ * @param v8 The v8 ledger module, as handed over by `loadLedger8`
195
+ * (`./load.ts`).
196
+ * @returns The UNPROVEN, serialized call transaction.
197
+ * @throws ComposeOptionError If `networkId` is empty or `ttl` is not a valid
198
+ * instant.
199
+ * @throws ComposeFailedError If `contractState` has no registered operation for
200
+ * `circuitId` (stage `'call-operation'`), or the operation it does have
201
+ * carries no verifier key (stage `'call-verifier-key'`), plus every stage
202
+ * {@link assembleCallPrototype} and {@link aggregateUnshieldedOffers} raise.
203
+ * @see {@link ComposeRefusalOrder}
204
+ */
205
+ const composeV8CallTx = (options, v8) => {
206
+ const { contractAddress, contractState, networkId, ttl, guaranteedZswapOffer, fallibleZswapOffer } = options;
207
+ assertComposeEnvelope(options, 'v8');
208
+ const prototype = assembleCallPrototype(v8, {
209
+ circuitId: options.circuitId,
210
+ contractAddress,
211
+ transcript: options.transcript,
212
+ privateTranscriptOutputs: options.privateTranscriptOutputs,
213
+ input: options.input,
214
+ output: options.output,
215
+ communicationCommitmentRandomness: options.communicationCommitmentRandomness,
216
+ ledgerParameters: options.ledgerParameters,
217
+ operations: contractState,
218
+ stage: 'call-operation',
219
+ version: 'v8'
220
+ });
221
+ const intent = v8.Intent.new(ttl).addCall(prototype);
222
+ // Read the partitioned pair back off the intent rather than re-deriving it,
223
+ // and attach the payout on this era too -- see ComposeRefusalOrder.
224
+ const unshielded = aggregateUnshieldedOffers(intent.actions
225
+ .filter((action) => action instanceof v8.ContractCall)
226
+ .map((call) => ({
227
+ circuitId: entryPointName(call.entryPoint),
228
+ guaranteed: call.guaranteedTranscript,
229
+ fallible: call.fallibleTranscript
230
+ })), v8, 'v8');
231
+ if (unshielded.guaranteed !== undefined) {
232
+ intent.guaranteedUnshieldedOffer = unshielded.guaranteed;
233
+ }
234
+ if (unshielded.fallible !== undefined) {
235
+ intent.fallibleUnshieldedOffer = unshielded.fallible;
236
+ }
237
+ return v8.Transaction.fromPartsRandomized(networkId, guaranteedZswapOffer, fallibleZswapOffer, intent).serialize();
238
+ };
239
+
240
+ /*
241
+ * This file is part of midnight-js.
242
+ * Copyright (C) Midnight Foundation
243
+ * SPDX-License-Identifier: Apache-2.0
244
+ * Licensed under the Apache License, Version 2.0 (the "License");
245
+ * You may not use this file except in compliance with the License.
246
+ * You may obtain a copy of the License at
247
+ * http://www.apache.org/licenses/LICENSE-2.0
248
+ * Unless required by applicable law or agreed to in writing, software
249
+ * distributed under the License is distributed on an "AS IS" BASIS,
250
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
251
+ * See the License for the specific language governing permissions and
252
+ * limitations under the License.
253
+ */
254
+ /**
255
+ * Bridges a raw, serialized contract state into the v8 era, reporting a
256
+ * rejected envelope as {@link ComposeOptionError} rather than letting a raw
257
+ * decoder failure escape.
258
+ */
259
+ const readContractState$1 = (raw, v8) => {
260
+ try {
261
+ return v8.ContractState.deserialize(raw);
262
+ }
263
+ catch (cause) {
264
+ throw new ComposeOptionError('v8', 'contractState', cause);
265
+ }
266
+ };
267
+ /**
268
+ * Reads a serialized Zswap offer into the v8 era, reporting bytes this era
269
+ * cannot decode as {@link ComposeOptionError}. An absent offer is the normal
270
+ * shape of a call that moved no shielded coins, and stays absent.
271
+ *
272
+ * @see {@link ComposeRefusalOrder}
273
+ */
274
+ const readZswapOffer$1 = (raw, v8) => {
275
+ if (raw === undefined) {
276
+ return undefined;
277
+ }
278
+ try {
279
+ return v8.ZswapOffer.deserialize('pre-proof', raw);
280
+ }
281
+ catch (cause) {
282
+ throw new ComposeOptionError('v8', 'zswapOffer', cause);
283
+ }
284
+ };
285
+ /**
286
+ * Maps the era-facade's call options onto the v8-native composition leg
287
+ * (`./compose.ts`).
288
+ *
289
+ * This era composes exactly one call: a `calls` tree with more than one entry
290
+ * is refused rather than silently narrowed. A Zswap offer is NOT refused.
291
+ *
292
+ * @param options The era-facade call options.
293
+ * @param v8 The v8 ledger module, as handed over by `loadLedger8`
294
+ * (`./load.ts`).
295
+ * @returns The UNPROVEN, serialized call transaction.
296
+ * @throws ComposeOptionError If an option cannot be used: a malformed envelope
297
+ * option, unreadable offer or contract-state bytes, or more than one entry in
298
+ * `calls`.
299
+ * @throws ComposeFailedError If `calls` is empty (stage `'call-empty'`), and
300
+ * for every stage the composition leg this delegates to can raise.
301
+ * @see {@link ComposeRefusalOrder}
302
+ * @see {@link EraSeam}
303
+ */
304
+ const composeEraV8CallTx = (options, v8) => {
305
+ const { calls, networkId, ttl, guaranteedZswapOffer, fallibleZswapOffer } = options;
306
+ // Checked here, not left to the inner leg, so both arms refuse in the same
307
+ // order; the re-check in the leg is idempotent -- see ComposeRefusalOrder.
308
+ assertComposeEnvelope(options, 'v8');
309
+ if (calls.length === 0) {
310
+ throw new ComposeFailedError('v8', 'call-empty', NO_CIRCUIT);
311
+ }
312
+ // Both offers are read before anything is composed -- see
313
+ // ComposeRefusalOrder.
314
+ const guaranteedOffer = readZswapOffer$1(guaranteedZswapOffer, v8);
315
+ const fallibleOffer = readZswapOffer$1(fallibleZswapOffer, v8);
316
+ if (calls.length > 1) {
317
+ throw new ComposeOptionError('v8', 'calls');
318
+ }
319
+ const [call] = calls;
320
+ return composeV8CallTx({
321
+ circuitId: call.circuitId,
322
+ contractAddress: call.contractAddress,
323
+ contractState: readContractState$1(call.contractState, v8),
324
+ transcript: call.transcript,
325
+ privateTranscriptOutputs: call.privateTranscriptOutputs,
326
+ input: call.input,
327
+ output: call.output,
328
+ communicationCommitmentRandomness: call.communicationCommitmentRandomness,
329
+ // Carried across the adapter, not dropped. Without this the retained-native arm silently
330
+ // partitions against this era's INITIAL parameters while the caller believed it had supplied
331
+ // the chain's -- the same defect this option exists to close, one layer down.
332
+ ledgerParameters: call.ledgerParameters,
333
+ networkId,
334
+ ttl,
335
+ guaranteedZswapOffer: guaranteedOffer,
336
+ fallibleZswapOffer: fallibleOffer
337
+ }, v8);
338
+ };
339
+ /**
340
+ * Maps the era-facade's deploy options onto the v8-native deploy leg
341
+ * (`./deploy.ts`).
342
+ *
343
+ * `verifierKeys` is optional on the facade but required here: this era's deploy
344
+ * leg registers the compiled contract's keys onto the initial state itself, so
345
+ * the omission is reported as {@link ComposeOptionError}.
346
+ *
347
+ * The contract state crosses into the leg by BYTES, which is what that leg
348
+ * takes: it deserializes into its own era rather than accepting a handle.
349
+ *
350
+ * @param options The era-facade deploy options.
351
+ * @param v8 The v8 ledger module, as handed over by `loadLedger8`
352
+ * (`./load.ts`).
353
+ * @returns The UNPROVEN, serialized deploy transaction, the address it deploys
354
+ * at, and the registered initial state.
355
+ * @throws ComposeOptionError If an option cannot be used: a malformed envelope
356
+ * option, unreadable offer bytes, or an omitted `verifierKeys` map.
357
+ * @throws ComposeFailedError For every stage the deploy leg this delegates to
358
+ * can raise.
359
+ * @see {@link VerifierKeys}
360
+ * @see {@link ComposeRefusalOrder}
361
+ * @see {@link EraSeam}
362
+ */
363
+ const composeEraV8DeployTx = (options, v8) => {
364
+ const { contractState, verifierKeys, networkId, ttl, guaranteedZswapOffer } = options;
365
+ // See `composeEraV8CallTx` — hoisted so both arms refuse the envelope first.
366
+ assertComposeEnvelope(options, 'v8');
367
+ const guaranteedOffer = readZswapOffer$1(guaranteedZswapOffer, v8);
368
+ if (verifierKeys === undefined) {
369
+ throw new ComposeOptionError('v8', 'verifierKeys');
370
+ }
371
+ return composeV8DeployTx({ contractState, verifierKeys, networkId, ttl, guaranteedZswapOffer: guaranteedOffer }, v8);
372
+ };
373
+
374
+ /*
375
+ * This file is part of midnight-js.
376
+ * Copyright (C) Midnight Foundation
377
+ * SPDX-License-Identifier: Apache-2.0
378
+ * Licensed under the Apache License, Version 2.0 (the "License");
379
+ * You may not use this file except in compliance with the License.
380
+ * You may obtain a copy of the License at
381
+ * http://www.apache.org/licenses/LICENSE-2.0
382
+ * Unless required by applicable law or agreed to in writing, software
383
+ * distributed under the License is distributed on an "AS IS" BASIS,
384
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
385
+ * See the License for the specific language governing permissions and
386
+ * limitations under the License.
387
+ */
388
+ /**
389
+ * Bridges a raw, serialized contract state into the v9 era, reporting a
390
+ * rejected envelope as {@link ComposeOptionError} -- see ComposeRefusalOrder.
391
+ */
392
+ const readContractState = (raw) => {
393
+ try {
394
+ return ledgerV9.ContractState.deserialize(raw);
395
+ }
396
+ catch (cause) {
397
+ throw new ComposeOptionError('v9', 'contractState', cause);
398
+ }
399
+ };
400
+ /**
401
+ * Reads a serialized Zswap offer, reporting bytes this era cannot decode as
402
+ * {@link ComposeOptionError}. An absent offer is the normal shape of a call
403
+ * that moved no shielded coins, and stays absent.
404
+ */
405
+ const readZswapOffer = (raw) => {
406
+ if (raw === undefined) {
407
+ return undefined;
408
+ }
409
+ try {
410
+ return ledgerV9.ZswapOffer.deserialize('pre-proof', raw);
411
+ }
412
+ catch (cause) {
413
+ throw new ComposeOptionError('v9', 'zswapOffer', cause);
414
+ }
415
+ };
416
+ /**
417
+ * Composes a v9-native call transaction from one or more contract calls and
418
+ * immediately serializes it.
419
+ *
420
+ * Never proves the transaction: the returned bytes are an UNPROVEN,
421
+ * tag-prefixed serialization, exactly what `Transaction.serialize()` produces
422
+ * before `.prove()` is ever called.
423
+ *
424
+ * @param options The calls to compose and the transaction-wide envelope.
425
+ * @returns The serialized UNPROVEN transaction.
426
+ * @throws ComposeFailedError if the call list is empty (stage `'call-empty'`)
427
+ * or a call cannot be assembled; `stage` names which step refused it.
428
+ * @throws ComposeOptionError if the network id, the ttl, a Zswap offer or a
429
+ * call's contract state is unusable.
430
+ * @see {@link ComposeRefusalOrder}
431
+ */
432
+ const composeV9CallTx = (options) => {
433
+ const { calls, networkId, ttl, guaranteedZswapOffer, fallibleZswapOffer } = options;
434
+ assertComposeEnvelope(options, 'v9');
435
+ if (calls.length === 0) {
436
+ throw new ComposeFailedError('v9', 'call-empty', NO_CIRCUIT);
437
+ }
438
+ // Read both offers before composing anything -- see ComposeRefusalOrder.
439
+ const guaranteedOffer = readZswapOffer(guaranteedZswapOffer);
440
+ const fallibleOffer = readZswapOffer(fallibleZswapOffer);
441
+ let intent = ledgerV9.Intent.new(ttl);
442
+ for (const call of calls) {
443
+ intent = intent.addCall(assembleCallPrototype(ledgerV9, {
444
+ circuitId: call.circuitId,
445
+ contractAddress: call.contractAddress,
446
+ transcript: call.transcript,
447
+ privateTranscriptOutputs: call.privateTranscriptOutputs,
448
+ input: call.input,
449
+ output: call.output,
450
+ communicationCommitmentRandomness: call.communicationCommitmentRandomness,
451
+ ledgerParameters: call.ledgerParameters,
452
+ operations: readContractState(call.contractState),
453
+ stage: 'call-operation',
454
+ version: 'v9'
455
+ }));
456
+ }
457
+ // Read the partitioned pairs back off the intent rather than re-deriving
458
+ // them -- see ComposeRefusalOrder.
459
+ const unshielded = aggregateUnshieldedOffers(intent.actions
460
+ .filter((action) => action instanceof ledgerV9.ContractCall)
461
+ .map((call) => ({
462
+ circuitId: entryPointName(call.entryPoint),
463
+ guaranteed: call.guaranteedTranscript,
464
+ fallible: call.fallibleTranscript
465
+ })), ledgerV9, 'v9');
466
+ if (unshielded.guaranteed !== undefined) {
467
+ intent.guaranteedUnshieldedOffer = unshielded.guaranteed;
468
+ }
469
+ if (unshielded.fallible !== undefined) {
470
+ intent.fallibleUnshieldedOffer = unshielded.fallible;
471
+ }
472
+ return ledgerV9.Transaction.fromPartsRandomized(networkId, guaranteedOffer, fallibleOffer, intent).serialize();
473
+ };
474
+ /**
475
+ * Registers a verifier key for every entry point the state declares, against
476
+ * the map validated by {@link resolveVerifierKeyRegistrations} -- see
477
+ * VerifierKeys.
478
+ */
479
+ const registerVerifierKeys = (contractState, verifierKeys) => {
480
+ for (const { entryPoint, circuitId, verifierKey } of resolveVerifierKeyRegistrations(contractState.operations(), verifierKeys, 'v9')) {
481
+ const operation = new ledgerV9.ContractOperation();
482
+ try {
483
+ operation.verifierKey = verifierKey;
484
+ }
485
+ catch (cause) {
486
+ throw new ComposeFailedError('v9', 'deploy-verifier-key-blob', circuitId, cause);
487
+ }
488
+ contractState.setOperation(entryPoint, operation);
489
+ }
490
+ };
491
+ /**
492
+ * Refuses a state that still declares an entry point with a blank verifier key
493
+ * when no key map was supplied -- see VerifierKeys.
494
+ */
495
+ const assertStateCarriesKeys = (contractState) => {
496
+ for (const entryPoint of contractState.operations()) {
497
+ // `?.` is deliberate here, unlike in `../shared/contract-state.ts`, and so
498
+ // is raising the OPTION error rather than the `'deploy-verifier-key'` stage
499
+ // -- see VerifierKeys.
500
+ if (contractState.operation(entryPoint)?.verifierKey === undefined) {
501
+ throw new ComposeOptionError('v9', 'verifierKeys');
502
+ }
503
+ }
504
+ };
505
+ /**
506
+ * Composes a v9-native deploy transaction from a serialized initial contract
507
+ * state and immediately serializes it, returning the transaction together with
508
+ * the address the deployment will have and the initial state that address is
509
+ * derived from.
510
+ *
511
+ * Never proves the transaction: the returned bytes are an UNPROVEN,
512
+ * tag-prefixed serialization, exactly what `Transaction.serialize()` produces
513
+ * before `.prove()` is ever called.
514
+ *
515
+ * With `verifierKeys` supplied, every declared entry point is registered before
516
+ * the address is derived. Without it, the state is deployed exactly as given —
517
+ * which is checked rather than assumed, see {@link assertStateCarriesKeys}.
518
+ *
519
+ * @param options The serialized initial state, its verifier keys, and the
520
+ * transaction-wide envelope.
521
+ * @returns The serialized UNPROVEN transaction, the address the deployment
522
+ * will have, and the initial state that address was derived from.
523
+ * @throws ComposeFailedError if the supplied keys do not match the state's
524
+ * declared entry points, or if the ledger rejects a key blob; `stage` names
525
+ * which check refused it.
526
+ * @throws ComposeOptionError if the network id, the ttl, the Zswap offer or
527
+ * the state bytes are unusable, or if `verifierKeys` was omitted for a state
528
+ * that still declares a blank key.
529
+ * @see {@link VerifierKeys}
530
+ * @see {@link ComposeRefusalOrder}
531
+ */
532
+ const composeV9DeployTx = (options) => {
533
+ const { contractState, verifierKeys, networkId, ttl, guaranteedZswapOffer } = options;
534
+ assertComposeEnvelope(options, 'v9');
535
+ const guaranteedOffer = readZswapOffer(guaranteedZswapOffer);
536
+ const state = readContractState(contractState);
537
+ if (verifierKeys === undefined) {
538
+ assertStateCarriesKeys(state);
539
+ }
540
+ else {
541
+ registerVerifierKeys(state, verifierKeys);
542
+ }
543
+ const deploy = new ledgerV9.ContractDeploy(state);
544
+ const intent = ledgerV9.Intent.new(ttl).addDeploy(deploy);
545
+ return {
546
+ transaction: ledgerV9.Transaction.fromParts(networkId, guaranteedOffer, undefined, intent).serialize(),
547
+ contractAddress: deploy.address,
548
+ initialState: deploy.initialState.serialize()
549
+ };
550
+ };
551
+
552
+ /*
553
+ * This file is part of midnight-js.
554
+ * Copyright (C) Midnight Foundation
555
+ * SPDX-License-Identifier: Apache-2.0
556
+ * Licensed under the Apache License, Version 2.0 (the "License");
557
+ * You may not use this file except in compliance with the License.
558
+ * You may obtain a copy of the License at
559
+ * http://www.apache.org/licenses/LICENSE-2.0
560
+ * Unless required by applicable law or agreed to in writing, software
561
+ * distributed under the License is distributed on an "AS IS" BASIS,
562
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
563
+ * See the License for the specific language governing permissions and
564
+ * limitations under the License.
565
+ */
566
+ /**
567
+ * Reads the primary {@link EncodedStateValue} out of a raw, serialized
568
+ * post-fork `contract-state[v8]` envelope.
569
+ *
570
+ * Reachable without a pre-fork runtime, unlike {@link extractEncodedStateValue},
571
+ * which requires one for every era.
572
+ *
573
+ * @param raw The serialized `contract-state[v8]` envelope.
574
+ * @returns The primary state read out of the envelope.
575
+ * @throws DownConvertFailedError at stage `'v9 envelope extraction'` if the
576
+ * envelope is malformed, truncated, over-long, or tagged for the other era,
577
+ * carrying the runtime's own diagnosis on `cause`.
578
+ * @see {@link FailClosedDecoding}
579
+ */
580
+ const extractV9EncodedStateValue = (raw) => {
581
+ try {
582
+ return ContractState.deserialize(raw).data.state.encode();
583
+ }
584
+ catch (cause) {
585
+ throw new DownConvertFailedError('v9 envelope extraction', cause);
586
+ }
587
+ };
588
+ /**
589
+ * One decoder per {@link LedgerVersion}:
590
+ *
591
+ * - `v9` — a post-fork `contract-state[v8]`-tagged envelope, read via
592
+ * `@midnightntwrk/ledger-v9`.
593
+ * - `v8` — a pre-fork `contract-state[v6]`-tagged envelope, read via
594
+ * onchain-runtime-v3, the same codec that produced it.
595
+ *
596
+ * A total `Record`, built on a null prototype and frozen -- see the
597
+ * SharedTableDiscipline document.
598
+ */
599
+ const ENVELOPE_DECODERS = Object.freeze(Object.assign(Object.create(null), {
600
+ v8: (raw, ledger8ContractState) => ledger8ContractState.deserialize(raw).data.state.encode(),
601
+ // A reference to the standalone decoder, not a second copy of the same
602
+ // read -- see FailClosedDecoding.
603
+ v9: extractV9EncodedStateValue
604
+ }));
605
+ /**
606
+ * Extracts the primary {@link EncodedStateValue} out of a raw, serialized
607
+ * `ContractState` envelope, using the decoder that matches `version`.
608
+ *
609
+ * @param raw The serialized envelope.
610
+ * @param version The era whose decoder reads `raw`. Validated at runtime, not
611
+ * merely type-checked.
612
+ * @param ledger8ContractState The pre-fork `ContractState` statics. Required
613
+ * for every `version`, not just `'v8'`, and checked before any decoding
614
+ * happens.
615
+ * @returns The primary state read out of the envelope — never a silently
616
+ * empty or partial one.
617
+ * @throws UnknownLedgerVersionError if `version` is not a member of
618
+ * `LEDGER_VERSIONS`.
619
+ * @throws Ledger8RuntimeInvalidError if `ledger8ContractState` does not carry
620
+ * `deserialize`.
621
+ * @throws DownConvertFailedError at stage `'v8 envelope extraction'` or
622
+ * `'v9 envelope extraction'` if the envelope cannot be read, carrying the
623
+ * runtime's own diagnosis on `cause`.
624
+ * @see {@link FailClosedDecoding}
625
+ * @see {@link SharedTableDiscipline}
626
+ */
627
+ const extractEncodedStateValue = (raw, version, ledger8ContractState) => {
628
+ const decoder = ENVELOPE_DECODERS[version];
629
+ if (typeof decoder !== 'function') {
630
+ throw new UnknownLedgerVersionError(String(version));
631
+ }
632
+ if (typeof ledger8ContractState?.deserialize !== 'function') {
633
+ throw new Ledger8RuntimeInvalidError('ContractState.deserialize');
634
+ }
635
+ try {
636
+ return decoder(raw, ledger8ContractState);
637
+ }
638
+ catch (cause) {
639
+ // THE STAGE IS CHECKED, not just the class: a decoder is injectable, so one
640
+ // that wrapped at a different stage must not pass through -- see
641
+ // FailClosedDecoding.
642
+ const stage = `${version} envelope extraction`;
643
+ throw cause instanceof DownConvertFailedError && cause.stage === stage
644
+ ? cause
645
+ : new DownConvertFailedError(stage, cause);
646
+ }
647
+ };
648
+
649
+ /*
650
+ * This file is part of midnight-js.
651
+ * Copyright (C) Midnight Foundation
652
+ * SPDX-License-Identifier: Apache-2.0
653
+ * Licensed under the Apache License, Version 2.0 (the "License");
654
+ * You may not use this file except in compliance with the License.
655
+ * You may obtain a copy of the License at
656
+ * http://www.apache.org/licenses/LICENSE-2.0
657
+ * Unless required by applicable law or agreed to in writing, software
658
+ * distributed under the License is distributed on an "AS IS" BASIS,
659
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
660
+ * See the License for the specific language governing permissions and
661
+ * limitations under the License.
662
+ */
663
+ // One memo slot per era, never one shared slot, and both arms are frozen --
664
+ // see SharedTableDiscipline and EraSeam.
665
+ let v8EraPromise;
666
+ let v9EraPromise;
667
+ /** The v9 arm. Wholly synchronous -- see the EraSeam document. */
668
+ const createV9Era = () => {
669
+ const era = {
670
+ version: 'v9',
671
+ extractState: (raw) => extractStateWith(raw, 'v9', extractV9EncodedStateValue),
672
+ decodeContractState: (raw) => decodeContractStateWith(raw, 'v9', ledgerV9),
673
+ composeCallTx: composeV9CallTx,
674
+ composeDeployTx: composeV9DeployTx,
675
+ partitionCallTranscript: (options) => {
676
+ // See the v8 arm: the assembler's tuple is readonly, the ledger's is not.
677
+ const [guaranteed, fallible] = partitionCallTranscript(ledgerV9, { ...options, version: 'v9' });
678
+ return [guaranteed, fallible];
679
+ }
680
+ };
681
+ return Object.freeze(era);
682
+ };
683
+ /**
684
+ * The v8 arm. Acquires the retained pre-fork ledger and binds it into closure,
685
+ * so the era's own methods stay synchronous -- see the EraSeam document for why
686
+ * the acquisition is hoisted here.
687
+ */
688
+ const createV8Era = async () => {
689
+ const v8 = await loadLedger8();
690
+ const era = {
691
+ version: 'v8',
692
+ extractState: (raw) => extractStateWith(raw, 'v8', (bytes) => extractEncodedStateValue(bytes, 'v8', v8.ContractState)),
693
+ decodeContractState: (raw) => decodeContractStateWith(raw, 'v8', v8),
694
+ composeCallTx: (options) => composeEraV8CallTx(options, v8),
695
+ composeDeployTx: (options) => composeEraV8DeployTx(options, v8),
696
+ partitionCallTranscript: (options) => {
697
+ // Copied into a mutable pair rather than handed on: the assembler answers
698
+ // with a readonly tuple, and the ledger's own `PartitionedTranscript` --
699
+ // which is what every consumer of this pair is typed against -- is not.
700
+ const [guaranteed, fallible] = partitionCallTranscript(v8, { ...options, version: 'v8' });
701
+ return [guaranteed, fallible];
702
+ }
703
+ };
704
+ return Object.freeze(era);
705
+ };
706
+ /**
707
+ * Resolves one ledger era to a {@link LedgerEra} bound to it.
708
+ *
709
+ * This is the only sanctioned way to reach either era's operations. Pass the
710
+ * version resolved from a record or from the network head (see
711
+ * `protocolVersionToLedger` in `../../version.ts`) rather than a string chosen
712
+ * by hand.
713
+ *
714
+ * Memoised per era, so the retained pre-fork WASM is instantiated at most once
715
+ * per process. A FAILED v8 acquisition is not memoised: the next call retries.
716
+ *
717
+ * @param version The era to resolve.
718
+ * @returns The era facade bound to `version`. The same object on every call
719
+ * for that era, and frozen.
720
+ * @throws UnknownLedgerVersionError — as a rejection — if `version` is not a
721
+ * member of `LEDGER_VERSIONS`.
722
+ * @throws Ledger8RuntimeMissingError — as a rejection — if the retained
723
+ * pre-fork runtime cannot be acquired. It propagates unchanged, carrying the
724
+ * underlying cause.
725
+ * @see {@link EraSeam}
726
+ * @see {@link SharedTableDiscipline}
727
+ */
728
+ const loadLedgerEra = (version) => {
729
+ switch (version) {
730
+ case 'v9':
731
+ return (v9EraPromise ??= Promise.resolve(createV9Era()));
732
+ case 'v8':
733
+ return (v8EraPromise ??= createV8Era().catch((error) => {
734
+ v8EraPromise = undefined;
735
+ throw error;
736
+ }));
737
+ default: {
738
+ // `const unhandled: never` is a compile-time exhaustiveness gate, and the
739
+ // runtime rejection is not redundant with it -- see SharedTableDiscipline.
740
+ const unhandled = version;
741
+ return Promise.reject(new UnknownLedgerVersionError(String(unhandled)));
742
+ }
743
+ }
744
+ };
745
+
746
+ /*
747
+ * This file is part of midnight-js.
748
+ * Copyright (C) Midnight Foundation
749
+ * SPDX-License-Identifier: Apache-2.0
750
+ * Licensed under the Apache License, Version 2.0 (the "License");
751
+ * You may not use this file except in compliance with the License.
752
+ * You may obtain a copy of the License at
753
+ * http://www.apache.org/licenses/LICENSE-2.0
754
+ * Unless required by applicable law or agreed to in writing, software
755
+ * distributed under the License is distributed on an "AS IS" BASIS,
756
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
757
+ * See the License for the specific language governing permissions and
758
+ * limitations under the License.
759
+ */
760
+ let enginePromise;
761
+ /**
762
+ * The only sanctioned runtime path to the engine's public surface.
763
+ *
764
+ * The retained `compact-runtime@0.16` glue and
765
+ * `@midnight-ntwrk/onchain-runtime-v3` WASM load only on the first call — never
766
+ * as a side effect of importing the package root.
767
+ *
768
+ * A failed load is not memoised: the next call retries the import. Exactly two
769
+ * rejections propagate unchanged — {@link Ledger8RuntimeMissingError} from the
770
+ * retained-runtime acquisition, and {@link Ledger8InstanceMismatchError} from
771
+ * the construction-time instance guard — keeping their class, code and
772
+ * discriminants intact for callers. Every other failure is wrapped in
773
+ * {@link Ledger8RuntimeMissingError}, including the coded
774
+ * `Ledger8RuntimeInvalidError` that same guard raises for an incomplete
775
+ * runtime, and a raw module-resolution error on the engine chunk itself.
776
+ *
777
+ * @returns The engine's public surface, memoised after the first successful
778
+ * load.
779
+ * @throws Ledger8RuntimeMissingError If the retained runtime, or the `./engine`
780
+ * chunk itself, cannot be acquired.
781
+ * @throws Ledger8InstanceMismatchError If the construction-time instance guard
782
+ * found `onchain-runtime-v3` resolved to two physically distinct copies.
783
+ * @see {@link ModuleGraphAndLazyLoading}
784
+ * @see {@link EraSeam}
785
+ */
786
+ const loadLedger8Engine = () => (enginePromise ??= import('./engine.js')
787
+ .then((engineModule) => engineModule.createLedger8Engine())
788
+ .catch((error) => {
789
+ enginePromise = undefined;
790
+ throw error instanceof Ledger8RuntimeMissingError || error instanceof Ledger8InstanceMismatchError
791
+ ? error
792
+ : new Ledger8RuntimeMissingError('/engine', error);
793
+ }));
794
+
795
+ export { ComposeFailedError, ComposeOptionError, DownConvertFailedError, Ledger8InstanceMismatchError, Ledger8RuntimeInvalidError, Ledger8RuntimeMissingError, NO_CIRCUIT, StateDecodeFailedError, UnknownLedgerVersionError, loadLedger8, loadLedger8Engine, loadLedgerEra };
11
796
  //# sourceMappingURL=index.js.map