@odatano/core 0.3.1

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.
Files changed (98) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +212 -0
  3. package/cds-plugin.js +5 -0
  4. package/config/preview/cardano-node/alonzo-genesis.json +196 -0
  5. package/config/preview/cardano-node/byron-genesis.json +117 -0
  6. package/config/preview/cardano-node/config.json +118 -0
  7. package/config/preview/cardano-node/conway-genesis.json +297 -0
  8. package/config/preview/cardano-node/shelley-genesis.json +68 -0
  9. package/config/preview/cardano-node/topology.json +19 -0
  10. package/db/schema.cds +1318 -0
  11. package/package.json +125 -0
  12. package/src/index.d.ts.map +1 -0
  13. package/src/index.js +96 -0
  14. package/src/index.js.map +1 -0
  15. package/src/plugin.d.ts.map +1 -0
  16. package/src/plugin.js +92 -0
  17. package/src/plugin.js.map +1 -0
  18. package/srv/blockchain/backends/blockfrost-backend.d.ts.map +1 -0
  19. package/srv/blockchain/backends/blockfrost-backend.js +398 -0
  20. package/srv/blockchain/backends/blockfrost-backend.js.map +1 -0
  21. package/srv/blockchain/backends/cardano-backend.d.ts.map +1 -0
  22. package/srv/blockchain/backends/cardano-backend.js +12 -0
  23. package/srv/blockchain/backends/cardano-backend.js.map +1 -0
  24. package/srv/blockchain/backends/koios-backend.d.ts.map +1 -0
  25. package/srv/blockchain/backends/koios-backend.js +537 -0
  26. package/srv/blockchain/backends/koios-backend.js.map +1 -0
  27. package/srv/blockchain/backends/ogmios-backend.d.ts.map +1 -0
  28. package/srv/blockchain/backends/ogmios-backend.js +516 -0
  29. package/srv/blockchain/backends/ogmios-backend.js.map +1 -0
  30. package/srv/blockchain/cardano-client.d.ts.map +1 -0
  31. package/srv/blockchain/cardano-client.js +377 -0
  32. package/srv/blockchain/cardano-client.js.map +1 -0
  33. package/srv/blockchain/cardano-indexer.d.ts.map +1 -0
  34. package/srv/blockchain/cardano-indexer.js +542 -0
  35. package/srv/blockchain/cardano-indexer.js.map +1 -0
  36. package/srv/blockchain/cardano-tx-builder.d.ts.map +1 -0
  37. package/srv/blockchain/cardano-tx-builder.js +232 -0
  38. package/srv/blockchain/cardano-tx-builder.js.map +1 -0
  39. package/srv/blockchain/circuit-breaker.d.ts.map +1 -0
  40. package/srv/blockchain/circuit-breaker.js +110 -0
  41. package/srv/blockchain/circuit-breaker.js.map +1 -0
  42. package/srv/blockchain/signing/external-signer.d.ts.map +1 -0
  43. package/srv/blockchain/signing/external-signer.js +302 -0
  44. package/srv/blockchain/signing/external-signer.js.map +1 -0
  45. package/srv/blockchain/signing/signature-verifier.d.ts.map +1 -0
  46. package/srv/blockchain/signing/signature-verifier.js +249 -0
  47. package/srv/blockchain/signing/signature-verifier.js.map +1 -0
  48. package/srv/blockchain/transaction-building/buildooor-tx.d.ts.map +1 -0
  49. package/srv/blockchain/transaction-building/buildooor-tx.js +636 -0
  50. package/srv/blockchain/transaction-building/buildooor-tx.js.map +1 -0
  51. package/srv/blockchain/transaction-building/cardano-tx.d.ts.map +1 -0
  52. package/srv/blockchain/transaction-building/cardano-tx.js +3 -0
  53. package/srv/blockchain/transaction-building/cardano-tx.js.map +1 -0
  54. package/srv/blockchain/transaction-building/csl-tx.d.ts.map +1 -0
  55. package/srv/blockchain/transaction-building/csl-tx.js +766 -0
  56. package/srv/blockchain/transaction-building/csl-tx.js.map +1 -0
  57. package/srv/blockchain/transaction-building/tx-builder-registry.d.ts.map +1 -0
  58. package/srv/blockchain/transaction-building/tx-builder-registry.js +67 -0
  59. package/srv/blockchain/transaction-building/tx-builder-registry.js.map +1 -0
  60. package/srv/cardano-service.cds +179 -0
  61. package/srv/cardano-service.d.ts.map +1 -0
  62. package/srv/cardano-service.js +227 -0
  63. package/srv/cardano-service.js.map +1 -0
  64. package/srv/cardano-tx-service.cds +298 -0
  65. package/srv/cardano-tx-service.d.ts.map +1 -0
  66. package/srv/cardano-tx-service.js +646 -0
  67. package/srv/cardano-tx-service.js.map +1 -0
  68. package/srv/cardano-ui.cds +2949 -0
  69. package/srv/server.d.ts.map +1 -0
  70. package/srv/server.js +212 -0
  71. package/srv/server.js.map +1 -0
  72. package/srv/utils/backend-request-handler.d.ts.map +1 -0
  73. package/srv/utils/backend-request-handler.js +47 -0
  74. package/srv/utils/backend-request-handler.js.map +1 -0
  75. package/srv/utils/const.d.ts.map +1 -0
  76. package/srv/utils/const.js +86 -0
  77. package/srv/utils/const.js.map +1 -0
  78. package/srv/utils/error-codes.d.ts.map +1 -0
  79. package/srv/utils/error-codes.js +49 -0
  80. package/srv/utils/error-codes.js.map +1 -0
  81. package/srv/utils/errors.d.ts.map +1 -0
  82. package/srv/utils/errors.js +389 -0
  83. package/srv/utils/errors.js.map +1 -0
  84. package/srv/utils/mappers.d.ts.map +1 -0
  85. package/srv/utils/mappers.js +723 -0
  86. package/srv/utils/mappers.js.map +1 -0
  87. package/srv/utils/signing-helper.d.ts.map +1 -0
  88. package/srv/utils/signing-helper.js +128 -0
  89. package/srv/utils/signing-helper.js.map +1 -0
  90. package/srv/utils/tx-build-helper.d.ts.map +1 -0
  91. package/srv/utils/tx-build-helper.js +135 -0
  92. package/srv/utils/tx-build-helper.js.map +1 -0
  93. package/srv/utils/types.d.ts.map +1 -0
  94. package/srv/utils/types.js +36 -0
  95. package/srv/utils/types.js.map +1 -0
  96. package/srv/utils/validators.d.ts.map +1 -0
  97. package/srv/utils/validators.js +382 -0
  98. package/srv/utils/validators.js.map +1 -0
@@ -0,0 +1,766 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.CSLTxBuilder = void 0;
40
+ const cds_1 = __importDefault(require("@sap/cds"));
41
+ const CSL = __importStar(require("@emurgo/cardano-serialization-lib-nodejs"));
42
+ const blake2b_1 = __importDefault(require("blake2b"));
43
+ const tx_build_helper_1 = require("../../utils/tx-build-helper");
44
+ const const_1 = require("../../utils/const");
45
+ const logger = cds_1.default.log('CSLTxBuilder');
46
+ /**
47
+ * CSLTxBuilder - Implementation of CardanoTxBuilder using cardano-serialization-lib (CSL)
48
+ */
49
+ class CSLTxBuilder {
50
+ name = "CslTxBuilder";
51
+ txBuilderConfig;
52
+ protocolParameters; // Store for cost models
53
+ cardanoClient;
54
+ /**
55
+ * Initialize the builder
56
+ * @param cardanoClient - The CardanoClient instance
57
+ * @param protocolParams - Optional protocol parameters (if not provided, fetched from backend)
58
+ */
59
+ async init(cardanoClient, protocolParams) {
60
+ this.protocolParameters = protocolParams ?? await cardanoClient.getProtocolParameters();
61
+ this.txBuilderConfig = this._createTxBuilderConfig(this.protocolParameters);
62
+ this.cardanoClient = cardanoClient;
63
+ logger.info(`Initialized with protocol parameters.`);
64
+ }
65
+ /**
66
+ * Build unsigned ADA transfer transaction
67
+ * @param req transaction build request
68
+ * @param ctx transaction build context
69
+ * @returns {Promise<TxBuildResult>} transaction build result
70
+ */
71
+ async buildUnsignedAdaTransfer(req, ctx) {
72
+ try {
73
+ // prepare addresses
74
+ const recipientAddress = CSL.Address.from_bech32(req.recipientAddress);
75
+ const changeAddress = CSL.Address.from_bech32(req.changeAddress ?? req.senderAddress);
76
+ // map ODATANO UTxOs -> CSL TransactionUnspentOutputs (with multi-asset support)
77
+ // This allows spending UTxOs that contain native assets - they will be returned in the change output
78
+ const cslUtxos = this._mapMultiAssetUtxosToCslUtxos(ctx.utxos);
79
+ // create Transaction Builder from stored config
80
+ const txb = CSL.TransactionBuilder.new(this.txBuilderConfig);
81
+ // add recipient & output (lovelace), with optional inline datum
82
+ const amount = CSL.BigNum.from_str(String(req.lovelaceAmount));
83
+ const outValue = CSL.Value.new(amount);
84
+ const out = CSL.TransactionOutput.new(recipientAddress, outValue);
85
+ if (req.outputDatum) {
86
+ const plutusData = CSL.PlutusData.from_json(JSON.stringify(req.outputDatum), CSL.PlutusDatumSchema.DetailedSchema);
87
+ out.set_plutus_data(plutusData);
88
+ }
89
+ txb.add_output(out);
90
+ // add inputs via coin selection + add change
91
+ txb.add_inputs_from(cslUtxos, CSL.CoinSelectionStrategyCIP2.LargestFirstMultiAsset);
92
+ txb.add_change_if_needed(changeAddress);
93
+ // build unsigned tx
94
+ const unsignedTx = txb.build_tx();
95
+ // Export the complete transaction (with empty witness set) for cardano-cli
96
+ const unsignedTxCbor = Buffer.from(unsignedTx.to_bytes()).toString("hex");
97
+ // hash + fee + outputs
98
+ const body = unsignedTx.body();
99
+ const bodyBytes = body.to_bytes();
100
+ const hash = (0, blake2b_1.default)(32).update(bodyBytes).digest('hex');
101
+ const txBodyHash = hash;
102
+ const feeLovelace = body.fee().to_str();
103
+ const outputs = [];
104
+ const txOuts = body.outputs();
105
+ for (let i = 0; i < txOuts.len(); i++) {
106
+ const o = txOuts.get(i);
107
+ outputs.push({
108
+ address: o.address().to_bech32(),
109
+ lovelace: o.amount().coin().to_str(),
110
+ });
111
+ }
112
+ logger.info(`Built unsigned transaction successfully.`);
113
+ return {
114
+ unsignedTxCbor,
115
+ txBodyHash,
116
+ senderAddress: req.senderAddress,
117
+ network: this.cardanoClient.network,
118
+ sizeBytes: unsignedTxCbor.length / 2, // hex to bytes
119
+ builderEngine: this.name,
120
+ feeLovelace: feeLovelace,
121
+ inputs: ctx.utxos.map(u => ({
122
+ txHash: u.txHash,
123
+ index: u.outputIndex,
124
+ lovelace: (0, tx_build_helper_1.getLovelace)(u).toString(),
125
+ })),
126
+ outputs,
127
+ warnings: [],
128
+ };
129
+ }
130
+ catch (err) {
131
+ (0, tx_build_helper_1.mapBuilderError)(err, 'lovelace');
132
+ }
133
+ }
134
+ async buildUnsignedTransactionWithMetadata(req, ctx) {
135
+ try {
136
+ // prepare addresses
137
+ const recipientAddress = CSL.Address.from_bech32(req.recipientAddress);
138
+ const changeAddress = CSL.Address.from_bech32(req.changeAddress ?? req.senderAddress);
139
+ // map ODATANO UTxOs -> CSL TransactionUnspentOutputs (with multi-asset support)
140
+ // This allows spending UTxOs that contain native assets - they will be returned in the change output
141
+ const cslUtxos = this._mapMultiAssetUtxosToCslUtxos(ctx.utxos);
142
+ // create Transaction Builder from stored config
143
+ const txb = CSL.TransactionBuilder.new(this.txBuilderConfig);
144
+ // add recipient & output (lovelace)
145
+ const amount = CSL.BigNum.from_str(String(req.lovelaceAmount));
146
+ const outValue = CSL.Value.new(amount);
147
+ const out = CSL.TransactionOutput.new(recipientAddress, outValue);
148
+ txb.add_output(out);
149
+ // add metadata if provided
150
+ if (req.metadataJson) {
151
+ const metadata = this._mapOdatanoMetadataToCSLMetadata(req.metadataJson);
152
+ txb.set_metadata(metadata);
153
+ }
154
+ // add inputs via coin selection + add change
155
+ txb.add_inputs_from(cslUtxos, CSL.CoinSelectionStrategyCIP2.LargestFirstMultiAsset);
156
+ txb.add_change_if_needed(changeAddress);
157
+ // build unsigned tx
158
+ const unsignedTx = txb.build_tx();
159
+ // Export the complete transaction (with empty witness set) for cardano-cli
160
+ const unsignedTxCbor = Buffer.from(unsignedTx.to_bytes()).toString("hex");
161
+ // hash + fee + outputs
162
+ const body = unsignedTx.body();
163
+ const bodyBytes = body.to_bytes();
164
+ const hash = (0, blake2b_1.default)(32).update(bodyBytes).digest('hex');
165
+ const txBodyHash = hash;
166
+ const feeLovelace = body.fee().to_str();
167
+ const outputs = [];
168
+ const txOuts = body.outputs();
169
+ for (let i = 0; i < txOuts.len(); i++) {
170
+ const o = txOuts.get(i);
171
+ outputs.push({
172
+ address: o.address().to_bech32(),
173
+ lovelace: o.amount().coin().to_str(),
174
+ });
175
+ }
176
+ logger.info(`Built unsigned transaction with metadata successfully.`);
177
+ return {
178
+ unsignedTxCbor,
179
+ txBodyHash,
180
+ senderAddress: req.senderAddress,
181
+ network: this.cardanoClient.network,
182
+ sizeBytes: unsignedTxCbor.length / 2, // hex to bytes
183
+ builderEngine: this.name,
184
+ feeLovelace,
185
+ inputs: ctx.utxos.map(u => ({
186
+ txHash: u.txHash,
187
+ index: u.outputIndex,
188
+ lovelace: (0, tx_build_helper_1.getLovelace)(u).toString(),
189
+ })),
190
+ outputs,
191
+ warnings: [],
192
+ };
193
+ }
194
+ catch (err) {
195
+ (0, tx_build_helper_1.mapBuilderError)(err, 'lovelace');
196
+ }
197
+ }
198
+ async buildUnsignedMultiAssetTransaction(req, ctx) {
199
+ if (!req.assets || req.assets.length === 0) {
200
+ throw new Error('[CSLTxBuilder] buildUnsignedMultiAssetTransaction requires assets to be specified');
201
+ }
202
+ try {
203
+ // prepare addresses
204
+ const recipientAddress = CSL.Address.from_bech32(req.recipientAddress);
205
+ const changeAddress = CSL.Address.from_bech32(req.changeAddress ?? req.senderAddress);
206
+ // map ODATANO UTxOs -> CSL TransactionUnspentOutputs (with multi-asset support)
207
+ const cslUtxos = this._mapMultiAssetUtxosToCslUtxos(ctx.utxos);
208
+ // create Transaction Builder from stored config
209
+ const txb = CSL.TransactionBuilder.new(this.txBuilderConfig);
210
+ // Build output value with ADA + multi-assets
211
+ const lovelace = CSL.BigNum.from_str(String(req.lovelaceAmount));
212
+ const outputValue = CSL.Value.new(lovelace);
213
+ // Create MultiAsset structure for native tokens
214
+ const multiAsset = CSL.MultiAsset.new();
215
+ for (const asset of req.assets) {
216
+ // Skip 'lovelace' unit (already handled above)
217
+ if (asset.unit === 'lovelace')
218
+ continue;
219
+ const { policyId, assetName } = (0, tx_build_helper_1.parseAssetUnit)(asset.unit);
220
+ // Get or create Assets for this policy
221
+ const policyHash = CSL.ScriptHash.from_bytes(Buffer.from(policyId, 'hex'));
222
+ let assets = multiAsset.get(policyHash);
223
+ if (!assets) {
224
+ assets = CSL.Assets.new();
225
+ }
226
+ // Add asset with quantity
227
+ const assetNameBytes = Buffer.from(assetName, 'hex');
228
+ const cslAssetName = CSL.AssetName.new(assetNameBytes);
229
+ const quantity = CSL.BigNum.from_str(asset.quantity);
230
+ assets.insert(cslAssetName, quantity);
231
+ multiAsset.insert(policyHash, assets);
232
+ }
233
+ // Set multi-asset on output value
234
+ outputValue.set_multiasset(multiAsset);
235
+ // Create output with all assets
236
+ const recipientOutput = CSL.TransactionOutput.new(recipientAddress, outputValue);
237
+ txb.add_output(recipientOutput);
238
+ // add inputs via coin selection + add change
239
+ txb.add_inputs_from(cslUtxos, CSL.CoinSelectionStrategyCIP2.LargestFirstMultiAsset);
240
+ txb.add_change_if_needed(changeAddress);
241
+ // build unsigned tx
242
+ const unsignedTx = txb.build_tx();
243
+ // Export the complete transaction (with empty witness set) for cardano-cli
244
+ const unsignedTxCbor = Buffer.from(unsignedTx.to_bytes()).toString("hex");
245
+ // hash + fee + outputs
246
+ const body = unsignedTx.body();
247
+ const bodyBytes = body.to_bytes();
248
+ const hash = (0, blake2b_1.default)(32).update(bodyBytes).digest('hex');
249
+ const txBodyHash = hash;
250
+ const feeLovelace = body.fee().to_str();
251
+ const outputs = [];
252
+ const txOuts = body.outputs();
253
+ for (let i = 0; i < txOuts.len(); i++) {
254
+ const o = txOuts.get(i);
255
+ outputs.push({
256
+ address: o.address().to_bech32(),
257
+ lovelace: o.amount().coin().to_str(),
258
+ });
259
+ }
260
+ logger.info(`Built unsigned multi-asset transaction successfully.`);
261
+ return {
262
+ unsignedTxCbor,
263
+ txBodyHash,
264
+ senderAddress: req.senderAddress,
265
+ network: this.cardanoClient.network,
266
+ sizeBytes: unsignedTxCbor.length / 2, // hex to bytes
267
+ builderEngine: this.name,
268
+ feeLovelace,
269
+ inputs: ctx.utxos.map(u => ({
270
+ txHash: u.txHash,
271
+ index: u.outputIndex,
272
+ lovelace: (0, tx_build_helper_1.getLovelace)(u).toString(),
273
+ })),
274
+ outputs,
275
+ warnings: [],
276
+ };
277
+ }
278
+ catch (err) {
279
+ // Extract asset unit from error message if possible
280
+ const assetMatch = err?.message?.match(/not enough\s+([a-f0-9.]+)/i);
281
+ const assetUnit = assetMatch?.[1] || 'assets';
282
+ (0, tx_build_helper_1.mapBuilderError)(err, assetUnit);
283
+ }
284
+ }
285
+ async buildUnsignedMintTransaction(req, ctx) {
286
+ try {
287
+ // Determine execution units based on evaluator availability
288
+ let finalExUnits = {
289
+ mem: String(const_1.DEFAULT_EXECUTION_UNITS.mem),
290
+ cpu: String(const_1.DEFAULT_EXECUTION_UNITS.cpu)
291
+ };
292
+ if (ctx.evaluateTransaction) {
293
+ // Build first pass with high execution units for evaluation
294
+ logger.debug(`[CSLTxBuilder] Building evaluation pass with high execution units`);
295
+ const evalTx = this._buildMintTx(req, ctx, {
296
+ mem: String(const_1.HIGH_EXECUTION_UNITS.mem),
297
+ cpu: String(const_1.HIGH_EXECUTION_UNITS.cpu)
298
+ });
299
+ const evalTxCbor = Buffer.from(evalTx.to_bytes()).toString('hex');
300
+ try {
301
+ // Evaluate to get exact execution units
302
+ const evalResults = await ctx.evaluateTransaction(evalTxCbor);
303
+ logger.debug(`[CSLTxBuilder] Evaluation results: ${JSON.stringify(evalResults)}`);
304
+ if (evalResults && evalResults.length > 0) {
305
+ // Use the evaluated budget (take first result for single script)
306
+ const budget = evalResults[0].budget;
307
+ // Add safety margin to evaluated units
308
+ finalExUnits = {
309
+ mem: Math.ceil(budget.memory * const_1.EXECUTION_UNIT_BUFFER).toString(),
310
+ cpu: Math.ceil(budget.cpu * const_1.EXECUTION_UNIT_BUFFER).toString()
311
+ };
312
+ logger.info(`[CSLTxBuilder] Using evaluated execution units: mem=${finalExUnits.mem}, cpu=${finalExUnits.cpu}`);
313
+ }
314
+ }
315
+ catch (evalError) {
316
+ logger.warn(`[CSLTxBuilder] Evaluation failed, using default units: ${evalError.message}`);
317
+ // Fall back to defaults on evaluation failure
318
+ }
319
+ }
320
+ else {
321
+ logger.debug(`[CSLTxBuilder] No evaluator available, using default execution units`);
322
+ }
323
+ // Build final transaction with determined execution units
324
+ const unsignedTx = this._buildMintTx(req, ctx, finalExUnits);
325
+ logger.debug(`[CSLTxBuilder] Transaction built successfully`);
326
+ // Export as CBOR hex
327
+ const unsignedTxCbor = Buffer.from(unsignedTx.to_bytes()).toString('hex');
328
+ // Extract transaction details from the transaction
329
+ const body = unsignedTx.body();
330
+ const bodyBytes = body.to_bytes();
331
+ const hash = (0, blake2b_1.default)(32).update(bodyBytes).digest('hex');
332
+ const txBodyHash = hash;
333
+ const feeLovelace = body.fee().to_str();
334
+ // Extract outputs
335
+ const outputs = [];
336
+ const txOuts = body.outputs();
337
+ for (let i = 0; i < txOuts.len(); i++) {
338
+ const o = txOuts.get(i);
339
+ outputs.push({
340
+ address: o.address().to_bech32(),
341
+ lovelace: o.amount().coin().to_str(),
342
+ });
343
+ }
344
+ logger.info(`[CSLTxBuilder] Built unsigned minting transaction successfully. Fee: ${feeLovelace}`);
345
+ return {
346
+ unsignedTxCbor,
347
+ txBodyHash,
348
+ senderAddress: req.senderAddress,
349
+ network: this.cardanoClient.network,
350
+ sizeBytes: unsignedTxCbor.length / 2, // hex to bytes
351
+ builderEngine: this.name,
352
+ feeLovelace,
353
+ inputs: ctx.utxos.map(u => ({
354
+ txHash: u.txHash,
355
+ index: u.outputIndex,
356
+ lovelace: (0, tx_build_helper_1.getLovelace)(u).toString(),
357
+ })),
358
+ outputs,
359
+ warnings: [],
360
+ };
361
+ }
362
+ catch (error) {
363
+ logger.error(`[CSLTxBuilder] buildUnsignedMintTransaction error: ${error?.message || error}`);
364
+ (0, tx_build_helper_1.mapBuilderError)(error, 'lovelace');
365
+ }
366
+ }
367
+ /**
368
+ * Helper to build mint transaction with specified execution units
369
+ */
370
+ _buildMintTx(req, ctx, exUnits) {
371
+ // Prepare addresses
372
+ const recipientAddress = CSL.Address.from_bech32(req.recipientAddress);
373
+ const changeAddress = CSL.Address.from_bech32(req.changeAddress ?? req.senderAddress);
374
+ // Map ODATANO UTxOs -> CSL TransactionUnspentOutputs (with multi-asset support for burn transactions)
375
+ const cslUtxos = this._mapMultiAssetUtxosToCslUtxos(ctx.utxos);
376
+ // Create Transaction Builder
377
+ const txb = CSL.TransactionBuilder.new(this.txBuilderConfig);
378
+ // Parse the Plutus script from CBOR hex (CBOR-wrapped flat UPLC bytecode)
379
+ const scriptBytes = Buffer.from(req.mintingPolicyScript, 'hex');
380
+ const plutusScript = CSL.PlutusScript.new_v3(scriptBytes);
381
+ const scriptHash = plutusScript.hash();
382
+ // Create PlutusScriptSource - wrapper for the script
383
+ const scriptSource = CSL.PlutusScriptSource.new(plutusScript);
384
+ // Create MintBuilder
385
+ const mintBuilder = CSL.MintBuilder.new();
386
+ // Track total minted value for output
387
+ let totalMintedValue = CSL.Value.new(CSL.BigNum.from_str('0'));
388
+ // Process each mint action
389
+ for (const mintAction of req.mintActions) {
390
+ const { assetName } = (0, tx_build_helper_1.parseAssetUnit)(mintAction.assetUnit);
391
+ // Create asset name
392
+ const assetNameBytes = Buffer.from(assetName, 'hex');
393
+ const cslAssetName = CSL.AssetName.new(assetNameBytes);
394
+ // Create mint quantity (can be negative for burning)
395
+ const mintQuantity = CSL.Int.new_i32(Number(mintAction.quantity));
396
+ // Create redeemer with specified execution units
397
+ const redeemerData = CSL.PlutusData.new_integer(CSL.BigInt.from_str(String(mintAction.redeemer ?? 0)));
398
+ const redeemer = CSL.Redeemer.new(CSL.RedeemerTag.new_mint(), CSL.BigNum.from_str('0'), redeemerData, CSL.ExUnits.new(CSL.BigNum.from_str(exUnits.mem), CSL.BigNum.from_str(exUnits.cpu)));
399
+ // Create MintWitness directly from PlutusScriptSource + Redeemer
400
+ const mintWitness = CSL.MintWitness.new_plutus_script(scriptSource, redeemer);
401
+ // Add to mint builder
402
+ mintBuilder.add_asset(mintWitness, cslAssetName, mintQuantity);
403
+ // Add to total minted value for output construction (only if quantity > 0)
404
+ if (Number(mintAction.quantity) > 0) {
405
+ const assetValue = CSL.Value.new(CSL.BigNum.from_str('0'));
406
+ const multiAsset = CSL.MultiAsset.new();
407
+ const assets = CSL.Assets.new();
408
+ assets.insert(cslAssetName, CSL.BigNum.from_str(String(mintAction.quantity)));
409
+ multiAsset.insert(scriptHash, assets);
410
+ assetValue.set_multiasset(multiAsset);
411
+ totalMintedValue = totalMintedValue.checked_add(assetValue);
412
+ }
413
+ }
414
+ // Set the mint builder on transaction
415
+ txb.set_mint_builder(mintBuilder);
416
+ // Create output with minted assets + minimum lovelace
417
+ const minLovelace = CSL.BigNum.from_str(String(req.lovelaceAmount));
418
+ const outputValue = CSL.Value.new(minLovelace);
419
+ const finalOutputValue = outputValue.checked_add(totalMintedValue);
420
+ const recipientOutput = CSL.TransactionOutput.new(recipientAddress, finalOutputValue);
421
+ txb.add_output(recipientOutput);
422
+ // Add collateral for Plutus script execution
423
+ if (ctx.utxos.length > 0) {
424
+ const collateralUtxo = ctx.utxos[0];
425
+ const collateralBuilder = CSL.TxInputsBuilder.new();
426
+ const txHash = CSL.TransactionHash.from_bytes(Buffer.from(collateralUtxo.txHash, 'hex'));
427
+ const input = CSL.TransactionInput.new(txHash, collateralUtxo.outputIndex);
428
+ const address = CSL.Address.from_bech32(collateralUtxo.address);
429
+ const value = CSL.Value.new(CSL.BigNum.from_str((0, tx_build_helper_1.getLovelace)(collateralUtxo).toString()));
430
+ collateralBuilder.add_regular_input(address, input, value);
431
+ txb.set_collateral(collateralBuilder);
432
+ }
433
+ // Add inputs via coin selection
434
+ txb.add_inputs_from(cslUtxos, CSL.CoinSelectionStrategyCIP2.LargestFirstMultiAsset);
435
+ // Calculate script data hash BEFORE add_change_if_needed
436
+ // Always call calc_script_data_hash for Plutus transactions - CSL requires it
437
+ const costModels = this._createCostModels('v3');
438
+ txb.calc_script_data_hash(costModels);
439
+ // Now add change
440
+ txb.add_change_if_needed(changeAddress);
441
+ // Build and return transaction
442
+ return txb.build_tx();
443
+ }
444
+ async buildUnsignedPlutusSpendTransaction(req, ctx) {
445
+ try {
446
+ // Determine execution units - default or from evaluator
447
+ let finalExUnits = {
448
+ mem: const_1.DEFAULT_EXECUTION_UNITS.mem.toString(),
449
+ cpu: const_1.DEFAULT_EXECUTION_UNITS.cpu.toString()
450
+ };
451
+ if (ctx.evaluateTransaction) {
452
+ // Build evaluation pass with high execution units
453
+ logger.debug(`[CSLTxBuilder] Building evaluation pass for Plutus spending`);
454
+ const highExUnits = {
455
+ mem: const_1.HIGH_EXECUTION_UNITS.mem.toString(),
456
+ cpu: const_1.HIGH_EXECUTION_UNITS.cpu.toString()
457
+ };
458
+ const evalTx = this._buildPlutusSpendTx(req, ctx, highExUnits);
459
+ const evalTxCbor = Buffer.from(evalTx.to_bytes()).toString('hex');
460
+ try {
461
+ const evalResults = await ctx.evaluateTransaction(evalTxCbor);
462
+ logger.debug(`[CSLTxBuilder] Evaluation results: ${JSON.stringify(evalResults)}`);
463
+ if (evalResults && evalResults.length > 0) {
464
+ const budget = evalResults[0].budget;
465
+ finalExUnits = {
466
+ mem: Math.ceil(budget.memory * const_1.EXECUTION_UNIT_BUFFER).toString(),
467
+ cpu: Math.ceil(budget.cpu * const_1.EXECUTION_UNIT_BUFFER).toString()
468
+ };
469
+ logger.info(`[CSLTxBuilder] Using evaluated execution units: mem=${finalExUnits.mem}, cpu=${finalExUnits.cpu}`);
470
+ }
471
+ }
472
+ catch (evalError) {
473
+ logger.warn(`[CSLTxBuilder] Evaluation failed, using default units: ${evalError.message}`);
474
+ }
475
+ }
476
+ else {
477
+ logger.debug(`[CSLTxBuilder] No evaluator available, using default execution units`);
478
+ }
479
+ // Build final transaction with determined execution units
480
+ const unsignedTx = this._buildPlutusSpendTx(req, ctx, finalExUnits);
481
+ // Export as CBOR hex
482
+ const unsignedTxCbor = Buffer.from(unsignedTx.to_bytes()).toString('hex');
483
+ // Extract transaction details
484
+ const body = unsignedTx.body();
485
+ const bodyBytes = body.to_bytes();
486
+ const hash = (0, blake2b_1.default)(32).update(bodyBytes).digest('hex');
487
+ const txBodyHash = hash;
488
+ const feeLovelace = body.fee().to_str();
489
+ // Extract outputs
490
+ const outputs = [];
491
+ const txOuts = body.outputs();
492
+ for (let i = 0; i < txOuts.len(); i++) {
493
+ const o = txOuts.get(i);
494
+ outputs.push({
495
+ address: o.address().to_bech32(),
496
+ lovelace: o.amount().coin().to_str(),
497
+ });
498
+ }
499
+ logger.info(`[CSLTxBuilder] Built unsigned Plutus spending transaction. Fee: ${feeLovelace}`);
500
+ return {
501
+ unsignedTxCbor,
502
+ txBodyHash,
503
+ senderAddress: req.senderAddress,
504
+ network: this.cardanoClient.network,
505
+ sizeBytes: unsignedTxCbor.length / 2,
506
+ builderEngine: this.name,
507
+ feeLovelace,
508
+ inputs: ctx.utxos.map(u => ({
509
+ txHash: u.txHash,
510
+ index: u.outputIndex,
511
+ lovelace: (0, tx_build_helper_1.getLovelace)(u).toString(),
512
+ })),
513
+ outputs,
514
+ warnings: [],
515
+ };
516
+ }
517
+ catch (error) {
518
+ logger.error(`[CSLTxBuilder] buildUnsignedPlutusSpendTransaction error: ${error?.message || error}`);
519
+ (0, tx_build_helper_1.mapBuilderError)(error, 'lovelace');
520
+ }
521
+ }
522
+ /**
523
+ * Helper to build Plutus spending transaction with specified execution units
524
+ */
525
+ _buildPlutusSpendTx(req, ctx, exUnits) {
526
+ const { plutusScriptExecution } = req;
527
+ const scriptUtxoRef = plutusScriptExecution.scriptUtxo;
528
+ // Prepare addresses
529
+ const recipientAddress = CSL.Address.from_bech32(req.recipientAddress);
530
+ const changeAddress = CSL.Address.from_bech32(req.changeAddress ?? req.senderAddress);
531
+ // Create Transaction Builder
532
+ const txb = CSL.TransactionBuilder.new(this.txBuilderConfig);
533
+ // Parse the Plutus validator script from CBOR hex (CBOR-wrapped flat UPLC bytecode)
534
+ const scriptBytes = Buffer.from(plutusScriptExecution.validatorScript, 'hex');
535
+ const plutusScript = CSL.PlutusScript.new_v3(scriptBytes);
536
+ const scriptSource = CSL.PlutusScriptSource.new(plutusScript);
537
+ // Build redeemer (using DetailedSchema: { "constructor": 0, "fields": [] } format)
538
+ const redeemerPlutusData = CSL.PlutusData.from_json(JSON.stringify(plutusScriptExecution.redeemer), CSL.PlutusDatumSchema.DetailedSchema);
539
+ const redeemer = CSL.Redeemer.new(CSL.RedeemerTag.new_spend(), CSL.BigNum.from_str('0'), // index will be corrected by CSL
540
+ redeemerPlutusData, CSL.ExUnits.new(CSL.BigNum.from_str(exUnits.mem), CSL.BigNum.from_str(exUnits.cpu)));
541
+ // Build datum source
542
+ let datumSource;
543
+ if (plutusScriptExecution.datum) {
544
+ // Datum provided explicitly (hash-based datum)
545
+ const datumPlutusData = CSL.PlutusData.from_json(JSON.stringify(plutusScriptExecution.datum), CSL.PlutusDatumSchema.DetailedSchema);
546
+ datumSource = CSL.DatumSource.new(datumPlutusData);
547
+ }
548
+ else {
549
+ // No datum provided - for inline datums, pass a unit datum placeholder.
550
+ // The node will validate against the actual inline datum on-chain.
551
+ const unitDatum = CSL.PlutusData.new_constr_plutus_data(CSL.ConstrPlutusData.new(CSL.BigNum.from_str('0'), CSL.PlutusList.new()));
552
+ datumSource = CSL.DatumSource.new(unitDatum);
553
+ }
554
+ // Add the script input using TxInputsBuilder
555
+ const scriptInputsBuilder = CSL.TxInputsBuilder.new();
556
+ const scriptTxHash = CSL.TransactionHash.from_bytes(Buffer.from(scriptUtxoRef.txHash, 'hex'));
557
+ const scriptInput = CSL.TransactionInput.new(scriptTxHash, scriptUtxoRef.outputIndex);
558
+ // Find the script UTxO to get its value
559
+ const scriptOdatanoUtxo = ctx.utxos.find(u => u.txHash === scriptUtxoRef.txHash && u.outputIndex === scriptUtxoRef.outputIndex);
560
+ if (!scriptOdatanoUtxo) {
561
+ throw new Error(`[CSLTxBuilder] Script UTxO ${scriptUtxoRef.txHash}#${scriptUtxoRef.outputIndex} not found in provided UTxOs`);
562
+ }
563
+ const scriptUtxoValue = CSL.Value.new(CSL.BigNum.from_str((0, tx_build_helper_1.getLovelace)(scriptOdatanoUtxo).toString()));
564
+ // Add Plutus script input
565
+ scriptInputsBuilder.add_plutus_script_input(CSL.PlutusWitness.new_with_ref(scriptSource, datumSource, redeemer), scriptInput, scriptUtxoValue);
566
+ txb.set_inputs(scriptInputsBuilder);
567
+ // Add sender UTxOs for fee payment via coin selection
568
+ const senderUtxos = ctx.utxos.filter(u => !(u.txHash === scriptUtxoRef.txHash && u.outputIndex === scriptUtxoRef.outputIndex));
569
+ const cslSenderUtxos = this._mapMultiAssetUtxosToCslUtxos(senderUtxos);
570
+ txb.add_inputs_from(cslSenderUtxos, CSL.CoinSelectionStrategyCIP2.LargestFirstMultiAsset);
571
+ // Create output for recipient
572
+ const outputValue = CSL.Value.new(CSL.BigNum.from_str(String(req.lovelaceAmount || 2_000_000)));
573
+ const recipientOutput = CSL.TransactionOutput.new(recipientAddress, outputValue);
574
+ txb.add_output(recipientOutput);
575
+ // Add collateral (from sender UTxOs)
576
+ if (senderUtxos.length > 0) {
577
+ const collateralUtxo = senderUtxos[0];
578
+ const collateralBuilder = CSL.TxInputsBuilder.new();
579
+ const colTxHash = CSL.TransactionHash.from_bytes(Buffer.from(collateralUtxo.txHash, 'hex'));
580
+ const colInput = CSL.TransactionInput.new(colTxHash, collateralUtxo.outputIndex);
581
+ const colAddress = CSL.Address.from_bech32(collateralUtxo.address);
582
+ const colValue = CSL.Value.new(CSL.BigNum.from_str((0, tx_build_helper_1.getLovelace)(collateralUtxo).toString()));
583
+ collateralBuilder.add_regular_input(colAddress, colInput, colValue);
584
+ txb.set_collateral(collateralBuilder);
585
+ }
586
+ // Calculate script data hash
587
+ const costModels = this._createCostModels('v3');
588
+ txb.calc_script_data_hash(costModels);
589
+ // Add change
590
+ txb.add_change_if_needed(changeAddress);
591
+ // Build and return transaction
592
+ return txb.build_tx();
593
+ }
594
+ //---------------------------------------------------------------------------
595
+ // Private Helper Methods
596
+ //---------------------------------------------------------------------------
597
+ /**
598
+ * Map ODATANO UTxOs to CSL TransactionUnspentOutputs (with multi-asset support)
599
+ * @param utxos ODATANO UTxO array
600
+ * @returns CSL TransactionUnspentOutputs
601
+ */
602
+ _mapMultiAssetUtxosToCslUtxos(utxos) {
603
+ const outs = CSL.TransactionUnspentOutputs.new();
604
+ logger.debug(`Mapping ${utxos.length} UTxOs to CSL format`);
605
+ for (const u of utxos) {
606
+ logger.debug(`UTxO ${u.txHash}:${u.outputIndex} has ${u.amount.length} amounts: ${JSON.stringify(u.amount)}`);
607
+ const txHashBytes = Buffer.from(u.txHash, "hex");
608
+ const txHash = CSL.TransactionHash.from_bytes(txHashBytes);
609
+ const input = CSL.TransactionInput.new(txHash, u.outputIndex);
610
+ const addr = CSL.Address.from_bech32(u.address);
611
+ // Build value with ADA + all native assets
612
+ const lovelace = CSL.BigNum.from_str((0, tx_build_helper_1.getLovelace)(u).toString());
613
+ const value = CSL.Value.new(lovelace);
614
+ // Add native assets if any
615
+ const nonAdaAssets = u.amount.filter(a => a.unit.toLowerCase() !== 'lovelace' && BigInt(a.quantity) > 0n);
616
+ if (nonAdaAssets.length > 0) {
617
+ const multiAsset = CSL.MultiAsset.new();
618
+ for (const asset of nonAdaAssets) {
619
+ const { policyId, assetName } = (0, tx_build_helper_1.parseAssetUnit)(asset.unit);
620
+ const policyHash = CSL.ScriptHash.from_bytes(Buffer.from(policyId, 'hex'));
621
+ let assets = multiAsset.get(policyHash);
622
+ if (!assets) {
623
+ assets = CSL.Assets.new();
624
+ }
625
+ const assetNameBytes = Buffer.from(assetName, 'hex');
626
+ const assetNameObj = CSL.AssetName.new(assetNameBytes);
627
+ assets.insert(assetNameObj, CSL.BigNum.from_str(asset.quantity));
628
+ multiAsset.insert(policyHash, assets);
629
+ }
630
+ value.set_multiasset(multiAsset);
631
+ }
632
+ const output = CSL.TransactionOutput.new(addr, value);
633
+ outs.add(CSL.TransactionUnspentOutput.new(input, output));
634
+ }
635
+ return outs;
636
+ }
637
+ /**
638
+ * Create a CSL TransactionBuilderConfig from protocol parameters
639
+ * This config is created once and reused for all transactions
640
+ * @param protocolParams LedgerProtocolParameter
641
+ * @returns CSL.TransactionBuilderConfig
642
+ */
643
+ _createTxBuilderConfig(protocolParams) {
644
+ // required values for CSL config
645
+ const minFeeA = protocolParams.minFeeA;
646
+ const minFeeB = protocolParams.minFeeB;
647
+ const poolDeposit = protocolParams.poolDeposit;
648
+ const keyDeposit = protocolParams.keyDeposit;
649
+ const maxTxSize = protocolParams.maxTxSize;
650
+ const maxValueSize = protocolParams.maxValSize;
651
+ const coinsPerUtxoByte = protocolParams.coinsPerUtxoSize;
652
+ const feeAlgo = CSL.LinearFee.new(CSL.BigNum.from_str(String(minFeeA)), CSL.BigNum.from_str(String(minFeeB)));
653
+ // Plutus execution unit prices for script fee calculation
654
+ // Protocol params may contain decimal prices (e.g., 0.0577) - convert to integer numerators
655
+ const priceMemValue = protocolParams.priceMem;
656
+ const priceStepValue = protocolParams.priceStep;
657
+ // Convert decimal prices to integer numerators (or use defaults if already integers or missing)
658
+ const priceMemNumerator = (typeof priceMemValue === 'number' && priceMemValue < 1)
659
+ ? Math.round(priceMemValue * 10000) // 0.0577 -> 577
660
+ : (priceMemValue || 577);
661
+ const priceStepNumerator = (typeof priceStepValue === 'number' && priceStepValue < 1)
662
+ ? Math.round(priceStepValue * 10000000) // 0.0000721 -> 721
663
+ : (priceStepValue || 721);
664
+ const exUnitPrices = CSL.ExUnitPrices.new(CSL.UnitInterval.new(CSL.BigNum.from_str(String(priceMemNumerator)), // numerator
665
+ CSL.BigNum.from_str('10000') // denominator (0.0577 per memory unit)
666
+ ), CSL.UnitInterval.new(CSL.BigNum.from_str(String(priceStepNumerator)), // numerator
667
+ CSL.BigNum.from_str('10000000') // denominator (0.0000721 per CPU step)
668
+ ));
669
+ const cfg = CSL.TransactionBuilderConfigBuilder.new()
670
+ .fee_algo(feeAlgo)
671
+ .pool_deposit(CSL.BigNum.from_str(String(poolDeposit)))
672
+ .key_deposit(CSL.BigNum.from_str(String(keyDeposit)))
673
+ .max_tx_size(Number(maxTxSize))
674
+ .max_value_size(Number(maxValueSize))
675
+ .coins_per_utxo_byte(CSL.BigNum.from_str(String(coinsPerUtxoByte)))
676
+ .ex_unit_prices(exUnitPrices) // Critical for Plutus fee calculation
677
+ .build();
678
+ logger.info(`TransactionBuilderConfig created with Plutus execution unit prices.`);
679
+ return cfg;
680
+ }
681
+ /**
682
+ * Map ODATANO metadata JSON to CSL GeneralTransactionMetadata
683
+ * @param metadataJson JSON metadata object
684
+ * @returns CSL GeneralTransactionMetadata
685
+ */
686
+ _mapOdatanoMetadataToCSLMetadata(metadataJson) {
687
+ // Metadata must be an object with labels as keys
688
+ if (typeof metadataJson !== 'object' || Array.isArray(metadataJson) || metadataJson === null) {
689
+ throw new Error(`[CSLTxBuilder] Invalid metadata format. Expected object, got ${typeof metadataJson}`);
690
+ }
691
+ const metadata = CSL.GeneralTransactionMetadata.new();
692
+ for (const [label, value] of Object.entries(metadataJson)) {
693
+ // Convert label to BigNum
694
+ const numericLabel = parseInt(label, 10);
695
+ // Convert JSON Value to CSL TransactionMetadatum
696
+ const txMetadatum = this._jsonToCSLMetadatum(value);
697
+ logger.debug(`Created TransactionMetadatum for label ${numericLabel}`);
698
+ metadata.insert(CSL.BigNum.from_str(String(numericLabel)), txMetadatum);
699
+ }
700
+ logger.debug(`Created metadata with ${metadata.len()} labels`);
701
+ return metadata;
702
+ }
703
+ /**
704
+ * Create Costmdls from protocol parameters for specific Plutus version
705
+ * Required for correct script integrity hash calculation
706
+ * @param version - Optional: specific Plutus version ('v1', 'v2', 'v3'). If not provided, adds all versions.
707
+ * @returns CSL.Costmdls
708
+ */
709
+ _createCostModels(version) {
710
+ const costModels = CSL.Costmdls.new();
711
+ try {
712
+ // Parse cost models JSON from protocol parameters
713
+ // Format: { "plutus:v1": [array of 166 numbers], "plutus:v2": [array of 175 numbers], ... }
714
+ const costModelsJson = JSON.parse(this.protocolParameters.costModels || '{}');
715
+ // PlutusV3 cost model (check both "plutus:v3" and "PlutusV3" formats)
716
+ if (!version || version === 'v3') {
717
+ const plutusV3Costs = costModelsJson['plutus:v3'] || costModelsJson['PlutusV3'];
718
+ if (plutusV3Costs && Array.isArray(plutusV3Costs)) {
719
+ const plutusV3CostModel = CSL.CostModel.new();
720
+ for (let i = 0; i < plutusV3Costs.length; i++) {
721
+ plutusV3CostModel.set(i, CSL.Int.new_i32(plutusV3Costs[i]));
722
+ }
723
+ costModels.insert(CSL.Language.new_plutus_v3(), plutusV3CostModel);
724
+ logger.debug(`[CSLTxBuilder] Added PlutusV3 cost model with ${plutusV3Costs.length} parameters`);
725
+ }
726
+ }
727
+ }
728
+ catch (error) {
729
+ logger.warn(`[CSLTxBuilder] Failed to parse cost models: ${error}. Using empty cost models.`);
730
+ }
731
+ return costModels;
732
+ }
733
+ /**
734
+ * Convert JSON value to CSL TransactionMetadatum
735
+ * @param value JSON value
736
+ * @returns CSL TransactionMetadatum
737
+ */
738
+ _jsonToCSLMetadatum(value) {
739
+ if (typeof value === 'number' || typeof value === 'bigint') {
740
+ const intValue = CSL.Int.new_i32(Number(value));
741
+ return CSL.TransactionMetadatum.new_int(intValue);
742
+ }
743
+ if (typeof value === 'string') {
744
+ return CSL.TransactionMetadatum.new_text(value);
745
+ }
746
+ if (Array.isArray(value)) {
747
+ const list = CSL.MetadataList.new();
748
+ for (const item of value) {
749
+ list.add(this._jsonToCSLMetadatum(item));
750
+ }
751
+ return CSL.TransactionMetadatum.new_list(list);
752
+ }
753
+ if (typeof value === 'object' && value !== null) {
754
+ const map = CSL.MetadataMap.new();
755
+ for (const [k, v] of Object.entries(value)) {
756
+ const key = CSL.TransactionMetadatum.new_text(k);
757
+ const val = this._jsonToCSLMetadatum(v);
758
+ map.insert(key, val);
759
+ }
760
+ return CSL.TransactionMetadatum.new_map(map);
761
+ }
762
+ throw new Error(`[CSLTxBuilder] Unsupported metadata value type: ${typeof value}`);
763
+ }
764
+ }
765
+ exports.CSLTxBuilder = CSLTxBuilder;
766
+ //# sourceMappingURL=csl-tx.js.map