@odatano/core 0.3.16 → 0.3.18

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 (94) hide show
  1. package/@cds-models/CardanoODataService/index.d.ts +434 -427
  2. package/@cds-models/CardanoODataService/index.d.ts.map +1 -1
  3. package/@cds-models/CardanoODataService/index.js +7 -0
  4. package/@cds-models/CardanoODataService/index.js.map +1 -1
  5. package/@cds-models/CardanoODataService/index.ts +99 -92
  6. package/@cds-models/CardanoSignService/index.d.ts +1549 -0
  7. package/@cds-models/CardanoSignService/index.d.ts.map +1 -0
  8. package/@cds-models/CardanoSignService/index.js +291 -0
  9. package/@cds-models/CardanoSignService/index.js.map +1 -0
  10. package/@cds-models/CardanoSignService/index.ts +519 -0
  11. package/@cds-models/CardanoTransactionService/index.d.ts +338 -815
  12. package/@cds-models/CardanoTransactionService/index.d.ts.map +1 -1
  13. package/@cds-models/CardanoTransactionService/index.js +3 -63
  14. package/@cds-models/CardanoTransactionService/index.js.map +1 -1
  15. package/@cds-models/CardanoTransactionService/index.ts +71 -210
  16. package/@cds-models/index.d.ts +150 -1
  17. package/@cds-models/index.d.ts.map +1 -1
  18. package/@cds-models/index.js +51 -1
  19. package/@cds-models/index.js.map +1 -1
  20. package/@cds-models/index.ts +74 -2
  21. package/@cds-models/odatano/cardano/index.d.ts +553 -696
  22. package/@cds-models/odatano/cardano/index.d.ts.map +1 -1
  23. package/@cds-models/odatano/cardano/index.js +3 -52
  24. package/@cds-models/odatano/cardano/index.js.map +1 -1
  25. package/@cds-models/odatano/cardano/index.ts +93 -165
  26. package/README.md +78 -19
  27. package/db/schema.cds +6 -108
  28. package/db/types.cds +111 -0
  29. package/package.json +22 -6
  30. package/src/index.d.ts.map +1 -1
  31. package/src/index.js +8 -3
  32. package/src/index.js.map +1 -1
  33. package/src/plugin.d.ts.map +1 -1
  34. package/src/plugin.js +7 -2
  35. package/src/plugin.js.map +1 -1
  36. package/srv/blockchain/backends/koios-backend.d.ts.map +1 -1
  37. package/srv/blockchain/backends/koios-backend.js +6 -16
  38. package/srv/blockchain/backends/koios-backend.js.map +1 -1
  39. package/srv/blockchain/cardano-client.d.ts.map +1 -1
  40. package/srv/blockchain/cardano-client.js +13 -10
  41. package/srv/blockchain/cardano-client.js.map +1 -1
  42. package/srv/blockchain/cardano-indexer.d.ts.map +1 -1
  43. package/srv/blockchain/cardano-indexer.js +10 -8
  44. package/srv/blockchain/cardano-indexer.js.map +1 -1
  45. package/srv/blockchain/cardano-tx-builder.d.ts.map +1 -1
  46. package/srv/blockchain/cardano-tx-builder.js +16 -16
  47. package/srv/blockchain/cardano-tx-builder.js.map +1 -1
  48. package/srv/blockchain/signing/hsm-signer.d.ts.map +1 -0
  49. package/srv/blockchain/signing/hsm-signer.js +290 -0
  50. package/srv/blockchain/signing/hsm-signer.js.map +1 -0
  51. package/srv/blockchain/signing/signature-verifier.d.ts.map +1 -1
  52. package/srv/blockchain/signing/signature-verifier.js +7 -25
  53. package/srv/blockchain/signing/signature-verifier.js.map +1 -1
  54. package/srv/blockchain/transaction-building/buildooor-tx.d.ts.map +1 -1
  55. package/srv/blockchain/transaction-building/buildooor-tx.js +171 -437
  56. package/srv/blockchain/transaction-building/buildooor-tx.js.map +1 -1
  57. package/srv/blockchain/transaction-building/cardano-tx.d.ts.map +1 -1
  58. package/srv/blockchain/transaction-building/csl-tx.d.ts.map +1 -1
  59. package/srv/blockchain/transaction-building/csl-tx.js +230 -611
  60. package/srv/blockchain/transaction-building/csl-tx.js.map +1 -1
  61. package/srv/cardano-service.cds +17 -9
  62. package/srv/cardano-service.js +2 -14
  63. package/srv/cardano-service.js.map +1 -1
  64. package/srv/cardano-sign-service.cds +128 -0
  65. package/srv/cardano-sign-service.d.ts.map +1 -0
  66. package/srv/cardano-sign-service.js +401 -0
  67. package/srv/cardano-sign-service.js.map +1 -0
  68. package/srv/cardano-tx-service.cds +116 -196
  69. package/srv/cardano-tx-service.js +5 -308
  70. package/srv/cardano-tx-service.js.map +1 -1
  71. package/srv/server.d.ts.map +1 -1
  72. package/srv/server.js +60 -5
  73. package/srv/server.js.map +1 -1
  74. package/srv/utils/const.d.ts.map +1 -1
  75. package/srv/utils/const.js +5 -1
  76. package/srv/utils/const.js.map +1 -1
  77. package/srv/utils/error-codes.d.ts.map +1 -1
  78. package/srv/utils/error-codes.js +15 -0
  79. package/srv/utils/error-codes.js.map +1 -1
  80. package/srv/utils/errors.d.ts.map +1 -1
  81. package/srv/utils/errors.js +12 -1
  82. package/srv/utils/errors.js.map +1 -1
  83. package/srv/utils/mappers.d.ts.map +1 -1
  84. package/srv/utils/mappers.js +9 -29
  85. package/srv/utils/mappers.js.map +1 -1
  86. package/srv/utils/signing-helper.d.ts.map +1 -1
  87. package/srv/utils/signing-helper.js +43 -25
  88. package/srv/utils/signing-helper.js.map +1 -1
  89. package/srv/utils/tx-build-helper.d.ts.map +1 -1
  90. package/srv/utils/tx-build-helper.js +10 -4
  91. package/srv/utils/tx-build-helper.js.map +1 -1
  92. package/srv/utils/types.d.ts.map +1 -1
  93. package/srv/utils/types.js +2 -0
  94. package/srv/utils/types.js.map +1 -1
@@ -64,373 +64,94 @@ class CSLTxBuilder {
64
64
  logger.info(`Initialized with protocol parameters.`);
65
65
  }
66
66
  /**
67
- * Build unsigned ADA transfer transaction
68
- * @param req transaction build request
69
- * @param ctx transaction build context
70
- * @returns {Promise<TxBuildResult>} transaction build result
67
+ * Build unsigned transfer transaction (ADA-only or with native assets)
71
68
  */
72
- async buildUnsignedAdaTransfer(req, ctx) {
69
+ async buildUnsignedTransfer(req, ctx) {
73
70
  try {
74
- // prepare addresses
75
71
  const recipientAddress = CSL.Address.from_bech32(req.recipientAddress);
76
72
  const changeAddress = CSL.Address.from_bech32(req.changeAddress ?? req.senderAddress);
77
- // map ODATANO UTxOs -> CSL TransactionUnspentOutputs (with multi-asset support)
78
- // This allows spending UTxOs that contain native assets - they will be returned in the change output
79
73
  const cslUtxos = this._mapMultiAssetUtxosToCslUtxos(ctx.utxos);
80
- // create Transaction Builder from stored config
81
74
  const txb = CSL.TransactionBuilder.new(this.txBuilderConfig);
82
- // add recipient & output (lovelace + optional assets), with optional inline datum
83
- const amount = CSL.BigNum.from_str(String(req.lovelaceAmount));
84
- const outValue = CSL.Value.new(amount);
85
- // Optional: add native assets to the output (for locking tokens at script address)
75
+ // Build output value (lovelace + optional native assets)
76
+ const outValue = CSL.Value.new(CSL.BigNum.from_str(String(req.lovelaceAmount)));
86
77
  if (req.assets && req.assets.length > 0) {
87
- const multiAsset = CSL.MultiAsset.new();
88
- for (const asset of req.assets) {
89
- if (asset.unit === 'lovelace')
90
- continue;
91
- const { policyId, assetName } = (0, tx_build_helper_1.parseAssetUnit)(asset.unit);
92
- const policyHash = CSL.ScriptHash.from_bytes(Buffer.from(policyId, 'hex'));
93
- let assets = multiAsset.get(policyHash);
94
- if (!assets)
95
- assets = CSL.Assets.new();
96
- assets.insert(CSL.AssetName.new(Buffer.from(assetName, 'hex')), CSL.BigNum.from_str(asset.quantity));
97
- multiAsset.insert(policyHash, assets);
98
- }
99
- outValue.set_multiasset(multiAsset);
78
+ outValue.set_multiasset(this._buildCslMultiAsset(req.assets));
100
79
  }
101
80
  const out = CSL.TransactionOutput.new(recipientAddress, outValue);
102
- if (req.outputDatum) {
103
- const plutusData = CSL.PlutusData.from_json(JSON.stringify(req.outputDatum), CSL.PlutusDatumSchema.DetailedSchema);
104
- out.set_plutus_data(plutusData);
105
- }
81
+ this._attachInlineDatum(out, req.outputDatum);
106
82
  txb.add_output(out);
107
- // add inputs via coin selection + add change
108
83
  txb.add_inputs_from(cslUtxos, CSL.CoinSelectionStrategyCIP2.LargestFirstMultiAsset);
109
84
  txb.add_change_if_needed(changeAddress);
110
- // build unsigned tx
111
85
  const unsignedTx = txb.build_tx();
112
- // Export the complete transaction (with empty witness set) for cardano-cli
113
- const unsignedTxCbor = Buffer.from(unsignedTx.to_bytes()).toString("hex");
114
- // hash + fee + outputs
115
- const body = unsignedTx.body();
116
- const bodyBytes = body.to_bytes();
117
- const hash = (0, blake2b_1.default)(32).update(bodyBytes).digest('hex');
118
- const txBodyHash = hash;
119
- const feeLovelace = body.fee().to_str();
120
- const outputs = [];
121
- const txOuts = body.outputs();
122
- for (let i = 0; i < txOuts.len(); i++) {
123
- const o = txOuts.get(i);
124
- outputs.push({
125
- address: o.address().to_bech32(),
126
- lovelace: o.amount().coin().to_str(),
127
- });
128
- }
86
+ const txDetails = this._extractTxDetails(unsignedTx);
129
87
  logger.info(`Built unsigned transaction successfully.`);
130
- return {
131
- unsignedTxCbor,
132
- txBodyHash,
133
- senderAddress: req.senderAddress,
134
- network: this.cardanoClient.network,
135
- sizeBytes: unsignedTxCbor.length / 2, // hex to bytes
136
- builderEngine: this.name,
137
- feeLovelace: feeLovelace,
138
- inputs: ctx.utxos.map(u => ({
139
- txHash: u.txHash,
140
- index: u.outputIndex,
141
- lovelace: (0, tx_build_helper_1.getLovelace)(u).toString(),
142
- })),
143
- outputs,
144
- warnings: [],
145
- };
88
+ return this._buildResult(req, ctx, txDetails);
146
89
  }
147
90
  catch (err) {
148
- (0, tx_build_helper_1.mapBuilderError)(err, 'lovelace');
91
+ (0, tx_build_helper_1.mapBuilderError)(err);
149
92
  }
150
93
  }
151
94
  async buildUnsignedTransactionWithMetadata(req, ctx) {
152
95
  try {
153
- // prepare addresses
154
96
  const recipientAddress = CSL.Address.from_bech32(req.recipientAddress);
155
97
  const changeAddress = CSL.Address.from_bech32(req.changeAddress ?? req.senderAddress);
156
- // map ODATANO UTxOs -> CSL TransactionUnspentOutputs (with multi-asset support)
157
- // This allows spending UTxOs that contain native assets - they will be returned in the change output
158
98
  const cslUtxos = this._mapMultiAssetUtxosToCslUtxos(ctx.utxos);
159
- // create Transaction Builder from stored config
160
99
  const txb = CSL.TransactionBuilder.new(this.txBuilderConfig);
161
- // add recipient & output (lovelace)
162
100
  const amount = CSL.BigNum.from_str(String(req.lovelaceAmount));
163
101
  const outValue = CSL.Value.new(amount);
164
102
  const out = CSL.TransactionOutput.new(recipientAddress, outValue);
165
103
  txb.add_output(out);
166
- // add metadata if provided
167
104
  if (req.metadataJson) {
168
105
  const metadata = this._mapOdatanoMetadataToCSLMetadata(req.metadataJson);
169
106
  txb.set_metadata(metadata);
170
107
  }
171
- // add inputs via coin selection + add change
172
108
  txb.add_inputs_from(cslUtxos, CSL.CoinSelectionStrategyCIP2.LargestFirstMultiAsset);
173
109
  txb.add_change_if_needed(changeAddress);
174
- // build unsigned tx
175
110
  const unsignedTx = txb.build_tx();
176
- // Export the complete transaction (with empty witness set) for cardano-cli
177
- const unsignedTxCbor = Buffer.from(unsignedTx.to_bytes()).toString("hex");
178
- // hash + fee + outputs
179
- const body = unsignedTx.body();
180
- const bodyBytes = body.to_bytes();
181
- const hash = (0, blake2b_1.default)(32).update(bodyBytes).digest('hex');
182
- const txBodyHash = hash;
183
- const feeLovelace = body.fee().to_str();
184
- const outputs = [];
185
- const txOuts = body.outputs();
186
- for (let i = 0; i < txOuts.len(); i++) {
187
- const o = txOuts.get(i);
188
- outputs.push({
189
- address: o.address().to_bech32(),
190
- lovelace: o.amount().coin().to_str(),
191
- });
192
- }
111
+ const txDetails = this._extractTxDetails(unsignedTx);
193
112
  logger.info(`Built unsigned transaction with metadata successfully.`);
194
- return {
195
- unsignedTxCbor,
196
- txBodyHash,
197
- senderAddress: req.senderAddress,
198
- network: this.cardanoClient.network,
199
- sizeBytes: unsignedTxCbor.length / 2, // hex to bytes
200
- builderEngine: this.name,
201
- feeLovelace,
202
- inputs: ctx.utxos.map(u => ({
203
- txHash: u.txHash,
204
- index: u.outputIndex,
205
- lovelace: (0, tx_build_helper_1.getLovelace)(u).toString(),
206
- })),
207
- outputs,
208
- warnings: [],
209
- };
210
- }
211
- catch (err) {
212
- (0, tx_build_helper_1.mapBuilderError)(err, 'lovelace');
213
- }
214
- }
215
- async buildUnsignedMultiAssetTransaction(req, ctx) {
216
- if (!req.assets || req.assets.length === 0) {
217
- throw new Error('[CSLTxBuilder] buildUnsignedMultiAssetTransaction requires assets to be specified');
218
- }
219
- try {
220
- // prepare addresses
221
- const recipientAddress = CSL.Address.from_bech32(req.recipientAddress);
222
- const changeAddress = CSL.Address.from_bech32(req.changeAddress ?? req.senderAddress);
223
- // map ODATANO UTxOs -> CSL TransactionUnspentOutputs (with multi-asset support)
224
- const cslUtxos = this._mapMultiAssetUtxosToCslUtxos(ctx.utxos);
225
- // create Transaction Builder from stored config
226
- const txb = CSL.TransactionBuilder.new(this.txBuilderConfig);
227
- // Build output value with ADA + multi-assets
228
- const lovelace = CSL.BigNum.from_str(String(req.lovelaceAmount));
229
- const outputValue = CSL.Value.new(lovelace);
230
- // Create MultiAsset structure for native tokens
231
- const multiAsset = CSL.MultiAsset.new();
232
- for (const asset of req.assets) {
233
- // Skip 'lovelace' unit (already handled above)
234
- if (asset.unit === 'lovelace')
235
- continue;
236
- const { policyId, assetName } = (0, tx_build_helper_1.parseAssetUnit)(asset.unit);
237
- // Get or create Assets for this policy
238
- const policyHash = CSL.ScriptHash.from_bytes(Buffer.from(policyId, 'hex'));
239
- let assets = multiAsset.get(policyHash);
240
- if (!assets) {
241
- assets = CSL.Assets.new();
242
- }
243
- // Add asset with quantity
244
- const assetNameBytes = Buffer.from(assetName, 'hex');
245
- const cslAssetName = CSL.AssetName.new(assetNameBytes);
246
- const quantity = CSL.BigNum.from_str(asset.quantity);
247
- assets.insert(cslAssetName, quantity);
248
- multiAsset.insert(policyHash, assets);
249
- }
250
- // Set multi-asset on output value
251
- outputValue.set_multiasset(multiAsset);
252
- // Create output with all assets + optional inline datum
253
- const recipientOutput = CSL.TransactionOutput.new(recipientAddress, outputValue);
254
- if (req.outputDatum) {
255
- const plutusData = CSL.PlutusData.from_json(JSON.stringify(req.outputDatum), CSL.PlutusDatumSchema.DetailedSchema);
256
- recipientOutput.set_plutus_data(plutusData);
257
- }
258
- txb.add_output(recipientOutput);
259
- // add inputs via coin selection + add change
260
- txb.add_inputs_from(cslUtxos, CSL.CoinSelectionStrategyCIP2.LargestFirstMultiAsset);
261
- txb.add_change_if_needed(changeAddress);
262
- // build unsigned tx
263
- const unsignedTx = txb.build_tx();
264
- // Export the complete transaction (with empty witness set) for cardano-cli
265
- const unsignedTxCbor = Buffer.from(unsignedTx.to_bytes()).toString("hex");
266
- // hash + fee + outputs
267
- const body = unsignedTx.body();
268
- const bodyBytes = body.to_bytes();
269
- const hash = (0, blake2b_1.default)(32).update(bodyBytes).digest('hex');
270
- const txBodyHash = hash;
271
- const feeLovelace = body.fee().to_str();
272
- const outputs = [];
273
- const txOuts = body.outputs();
274
- for (let i = 0; i < txOuts.len(); i++) {
275
- const o = txOuts.get(i);
276
- outputs.push({
277
- address: o.address().to_bech32(),
278
- lovelace: o.amount().coin().to_str(),
279
- });
280
- }
281
- logger.info(`Built unsigned multi-asset transaction successfully.`);
282
- return {
283
- unsignedTxCbor,
284
- txBodyHash,
285
- senderAddress: req.senderAddress,
286
- network: this.cardanoClient.network,
287
- sizeBytes: unsignedTxCbor.length / 2, // hex to bytes
288
- builderEngine: this.name,
289
- feeLovelace,
290
- inputs: ctx.utxos.map(u => ({
291
- txHash: u.txHash,
292
- index: u.outputIndex,
293
- lovelace: (0, tx_build_helper_1.getLovelace)(u).toString(),
294
- })),
295
- outputs,
296
- warnings: [],
297
- };
113
+ return this._buildResult(req, ctx, txDetails);
298
114
  }
299
115
  catch (err) {
300
- // Extract asset unit from error message if possible
301
- const assetMatch = err?.message?.match(/not enough\s+([a-f0-9.]+)/i);
302
- const assetUnit = assetMatch?.[1] || 'assets';
303
- (0, tx_build_helper_1.mapBuilderError)(err, assetUnit);
116
+ (0, tx_build_helper_1.mapBuilderError)(err);
304
117
  }
305
118
  }
306
119
  async buildUnsignedMintTransaction(req, ctx) {
307
120
  try {
308
- // Determine execution units based on evaluator availability
309
- let finalExUnits = {
310
- mem: String(const_1.DEFAULT_EXECUTION_UNITS.mem),
311
- cpu: String(const_1.DEFAULT_EXECUTION_UNITS.cpu)
312
- };
313
- if (ctx.evaluateTransaction) {
314
- // Build first pass with high execution units for evaluation
315
- logger.debug(`[CSLTxBuilder] Building evaluation pass with high execution units`);
316
- const evalTx = this._buildMintTx(req, ctx, {
317
- mem: String(const_1.HIGH_EXECUTION_UNITS.mem),
318
- cpu: String(const_1.HIGH_EXECUTION_UNITS.cpu)
319
- });
320
- const evalTxCbor = Buffer.from(evalTx.to_bytes()).toString('hex');
321
- try {
322
- // Evaluate to get exact execution units
323
- const evalResults = await ctx.evaluateTransaction(evalTxCbor);
324
- logger.debug(`[CSLTxBuilder] Evaluation results: ${JSON.stringify(evalResults)}`);
325
- if (evalResults && evalResults.length > 0) {
326
- // Use the evaluated budget (take first result for single script)
327
- const budget = evalResults[0].budget;
328
- // Add safety margin to evaluated units
329
- finalExUnits = {
330
- mem: Math.ceil(budget.memory * const_1.EXECUTION_UNIT_BUFFER).toString(),
331
- cpu: Math.ceil(budget.cpu * const_1.EXECUTION_UNIT_BUFFER).toString()
332
- };
333
- logger.info(`[CSLTxBuilder] Using evaluated execution units: mem=${finalExUnits.mem}, cpu=${finalExUnits.cpu}`);
334
- }
335
- }
336
- catch (evalError) {
337
- logger.warn(`[CSLTxBuilder] Evaluation failed, using default units: ${evalError.message}`);
338
- // Fall back to defaults on evaluation failure
339
- }
340
- }
341
- else {
342
- logger.debug(`[CSLTxBuilder] No evaluator available, using default execution units`);
343
- }
344
- // Build final transaction with determined execution units
121
+ const finalExUnits = await this._evaluateExUnits((exUnits) => this._buildMintTx(req, ctx, exUnits), ctx.evaluateTransaction);
345
122
  const unsignedTx = this._buildMintTx(req, ctx, finalExUnits);
346
- logger.debug(`[CSLTxBuilder] Transaction built successfully`);
347
- // Export as CBOR hex
348
- const unsignedTxCbor = Buffer.from(unsignedTx.to_bytes()).toString('hex');
349
- // Extract transaction details from the transaction
350
- const body = unsignedTx.body();
351
- const bodyBytes = body.to_bytes();
352
- const hash = (0, blake2b_1.default)(32).update(bodyBytes).digest('hex');
353
- const txBodyHash = hash;
354
- const feeLovelace = body.fee().to_str();
355
- // Extract outputs
356
- const outputs = [];
357
- const txOuts = body.outputs();
358
- for (let i = 0; i < txOuts.len(); i++) {
359
- const o = txOuts.get(i);
360
- outputs.push({
361
- address: o.address().to_bech32(),
362
- lovelace: o.amount().coin().to_str(),
363
- });
364
- }
365
- // Compute script hash from the minting policy script (= policy ID)
366
- const mintScriptBytes = Buffer.from(req.mintingPolicyScript, 'hex');
367
- const mintPlutusScript = CSL.PlutusScript.new_v3(mintScriptBytes);
368
- const mintScriptHash = Buffer.from(mintPlutusScript.hash().to_bytes()).toString('hex');
369
- logger.info(`[CSLTxBuilder] Built unsigned minting transaction successfully. Fee: ${feeLovelace}`);
370
- return {
371
- unsignedTxCbor,
372
- txBodyHash,
373
- senderAddress: req.senderAddress,
374
- network: this.cardanoClient.network,
375
- sizeBytes: unsignedTxCbor.length / 2, // hex to bytes
376
- builderEngine: this.name,
377
- feeLovelace,
378
- inputs: ctx.utxos.map(u => ({
379
- txHash: u.txHash,
380
- index: u.outputIndex,
381
- lovelace: (0, tx_build_helper_1.getLovelace)(u).toString(),
382
- })),
383
- outputs,
384
- scriptHash: mintScriptHash,
385
- warnings: [],
386
- };
123
+ const txDetails = this._extractTxDetails(unsignedTx);
124
+ const { hashHex: scriptHash } = this._computeScriptHash(req.mintingPolicyScript);
125
+ logger.info(`Built unsigned minting transaction successfully. Fee: ${txDetails.feeLovelace}`);
126
+ return this._buildResult(req, ctx, txDetails, { scriptHash });
387
127
  }
388
128
  catch (error) {
389
- logger.error(`[CSLTxBuilder] buildUnsignedMintTransaction error: ${error?.message || error}`);
390
- (0, tx_build_helper_1.mapBuilderError)(error, 'lovelace');
129
+ logger.error(`BuildUnsignedMintTransaction error: ${error?.message || error}`);
130
+ (0, tx_build_helper_1.mapBuilderError)(error);
391
131
  }
392
132
  }
393
133
  /**
394
134
  * Helper to build mint transaction with specified execution units
395
135
  */
396
136
  _buildMintTx(req, ctx, exUnits) {
397
- // Prepare addresses
398
137
  const recipientAddress = CSL.Address.from_bech32(req.recipientAddress);
399
138
  const changeAddress = CSL.Address.from_bech32(req.changeAddress ?? req.senderAddress);
400
- // Create Transaction Builder
401
139
  const txb = CSL.TransactionBuilder.new(this.txBuilderConfig);
402
- // Parse the Plutus script from CBOR hex (CBOR-wrapped flat UPLC bytecode)
403
- const scriptBytes = Buffer.from(req.mintingPolicyScript, 'hex');
404
- const plutusScript = CSL.PlutusScript.new_v3(scriptBytes);
405
- const scriptHash = plutusScript.hash();
406
- // Create PlutusScriptSource - wrapper for the script
140
+ const { plutusScript, cslHash: scriptHash } = this._computeScriptHash(req.mintingPolicyScript);
407
141
  const scriptSource = CSL.PlutusScriptSource.new(plutusScript);
408
- // Create MintBuilder
142
+ // Create MintBuilder and track total minted value for output
409
143
  const mintBuilder = CSL.MintBuilder.new();
410
- // Track total minted value for output
411
144
  let totalMintedValue = CSL.Value.new(CSL.BigNum.from_str('0'));
412
- // Process each mint action
413
145
  for (const mintAction of req.mintActions) {
414
146
  const { assetName } = (0, tx_build_helper_1.parseAssetUnit)(mintAction.assetUnit);
415
- // Create asset name
416
- const assetNameBytes = Buffer.from(assetName, 'hex');
417
- const cslAssetName = CSL.AssetName.new(assetNameBytes);
418
- // Create mint quantity (can be negative for burning)
147
+ const cslAssetName = CSL.AssetName.new(Buffer.from(assetName, 'hex'));
419
148
  const mintQuantity = CSL.Int.new_i32(Number(mintAction.quantity));
420
- // Create redeemer with specified execution units
421
- let redeemerData;
422
- if (req.mintRedeemer) {
423
- redeemerData = CSL.PlutusData.from_json(JSON.stringify(req.mintRedeemer), CSL.PlutusDatumSchema.DetailedSchema);
424
- }
425
- else {
426
- redeemerData = CSL.PlutusData.new_integer(CSL.BigInt.from_str(String(mintAction.redeemer ?? 0)));
427
- }
149
+ const redeemerData = req.mintRedeemer
150
+ ? this._toPlutusData(req.mintRedeemer)
151
+ : CSL.PlutusData.new_integer(CSL.BigInt.from_str(String(mintAction.redeemer ?? 0)));
428
152
  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)));
429
- // Create MintWitness directly from PlutusScriptSource + Redeemer
430
153
  const mintWitness = CSL.MintWitness.new_plutus_script(scriptSource, redeemer);
431
- // Add to mint builder
432
154
  mintBuilder.add_asset(mintWitness, cslAssetName, mintQuantity);
433
- // Add to total minted value for output construction (only if quantity > 0)
434
155
  if (Number(mintAction.quantity) > 0) {
435
156
  const assetValue = CSL.Value.new(CSL.BigNum.from_str('0'));
436
157
  const multiAsset = CSL.MultiAsset.new();
@@ -441,43 +162,16 @@ class CSLTxBuilder {
441
162
  totalMintedValue = totalMintedValue.checked_add(assetValue);
442
163
  }
443
164
  }
444
- // Set the mint builder on transaction
445
165
  txb.set_mint_builder(mintBuilder);
446
- // Add required signers if specified (for Plutus validators checking extra_signatories)
447
- if (req.requiredSigners?.length) {
448
- for (const signerHex of req.requiredSigners) {
449
- txb.add_required_signer(CSL.Ed25519KeyHash.from_bytes(Buffer.from(signerHex, 'hex')));
450
- }
451
- }
166
+ this._addRequiredSigners(txb, req.requiredSigners);
452
167
  // Create output with minted assets + minimum lovelace
453
- const minLovelace = CSL.BigNum.from_str(String(req.lovelaceAmount));
454
- const outputValue = CSL.Value.new(minLovelace);
168
+ const outputValue = CSL.Value.new(CSL.BigNum.from_str(String(req.lovelaceAmount)));
455
169
  const finalOutputValue = outputValue.checked_add(totalMintedValue);
456
170
  const recipientOutput = CSL.TransactionOutput.new(recipientAddress, finalOutputValue);
457
- if (req.inlineDatum) {
458
- const datumData = CSL.PlutusData.from_json(JSON.stringify(req.inlineDatum), CSL.PlutusDatumSchema.DetailedSchema);
459
- recipientOutput.set_plutus_data(datumData);
460
- }
171
+ this._attachInlineDatum(recipientOutput, req.inlineDatum);
461
172
  txb.add_output(recipientOutput);
462
- // Find an ADA-only UTxO for collateral
463
- const collateralOdatanoUtxo = ctx.utxos.find(u => u.amount.every(a => a.unit.toLowerCase() === 'lovelace'));
464
- if (collateralOdatanoUtxo) {
465
- const collateralBuilder = CSL.TxInputsBuilder.new();
466
- const txHash = CSL.TransactionHash.from_bytes(Buffer.from(collateralOdatanoUtxo.txHash, 'hex'));
467
- const input = CSL.TransactionInput.new(txHash, collateralOdatanoUtxo.outputIndex);
468
- const address = CSL.Address.from_bech32(collateralOdatanoUtxo.address);
469
- const value = CSL.Value.new(CSL.BigNum.from_str((0, tx_build_helper_1.getLovelace)(collateralOdatanoUtxo).toString()));
470
- collateralBuilder.add_regular_input(address, input, value);
471
- txb.set_collateral(collateralBuilder);
472
- }
473
- // Exclude collateral UTxO so it is not consumed on successful tx.
474
- let fundingUtxos = collateralOdatanoUtxo
475
- ? ctx.utxos.filter(u => !(u.txHash === collateralOdatanoUtxo.txHash && u.outputIndex === collateralOdatanoUtxo.outputIndex))
476
- : ctx.utxos;
477
- if (fundingUtxos.length === 0 && ctx.utxos.length > 0) {
478
- logger.debug('[CSLTxBuilder] No funding UTxOs after collateral exclusion, including collateral in funding pool');
479
- fundingUtxos = ctx.utxos;
480
- }
173
+ // Collateral + funding UTxO separation
174
+ const { fundingUtxos } = this._setupCollateral(txb, ctx.utxos);
481
175
  // Add all funding UTxOs as explicit inputs instead of using coin selection.
482
176
  // CSL's add_inputs_from does not properly forward native tokens from selected
483
177
  // inputs to the change output during Plutus minting transactions, causing
@@ -487,22 +181,7 @@ class CSLTxBuilder {
487
181
  const fTxHash = CSL.TransactionHash.from_bytes(Buffer.from(u.txHash, 'hex'));
488
182
  const fInput = CSL.TransactionInput.new(fTxHash, u.outputIndex);
489
183
  const fAddress = CSL.Address.from_bech32(u.address);
490
- const fLovelace = CSL.BigNum.from_str((0, tx_build_helper_1.getLovelace)(u).toString());
491
- const fValue = CSL.Value.new(fLovelace);
492
- const fNonAdaAssets = u.amount.filter(a => a.unit.toLowerCase() !== 'lovelace' && BigInt(a.quantity) > 0n);
493
- if (fNonAdaAssets.length > 0) {
494
- const fMultiAsset = CSL.MultiAsset.new();
495
- for (const asset of fNonAdaAssets) {
496
- const { policyId, assetName } = (0, tx_build_helper_1.parseAssetUnit)(asset.unit);
497
- const pH = CSL.ScriptHash.from_bytes(Buffer.from(policyId, 'hex'));
498
- let assets = fMultiAsset.get(pH);
499
- if (!assets)
500
- assets = CSL.Assets.new();
501
- assets.insert(CSL.AssetName.new(Buffer.from(assetName, 'hex')), CSL.BigNum.from_str(asset.quantity));
502
- fMultiAsset.insert(pH, assets);
503
- }
504
- fValue.set_multiasset(fMultiAsset);
505
- }
184
+ const fValue = this._buildCslValue((0, tx_build_helper_1.getLovelace)(u), u.amount);
506
185
  fundingInputsBuilder.add_regular_input(fAddress, fInput, fValue);
507
186
  }
508
187
  txb.set_inputs(fundingInputsBuilder);
@@ -523,8 +202,7 @@ class CSLTxBuilder {
523
202
  policy.set(an, (policy.get(an) || 0n) + BigInt(a.quantity));
524
203
  }
525
204
  }
526
- // Adjust for burned tokens (negative mint quantities) — those tokens are
527
- // destroyed, so the change output must not include them.
205
+ // Adjust for burned tokens (negative mint quantities)
528
206
  const scriptHashHex = Buffer.from(scriptHash.to_bytes()).toString('hex');
529
207
  for (const mintAction of req.mintActions) {
530
208
  const qty = BigInt(mintAction.quantity);
@@ -559,99 +237,26 @@ class CSLTxBuilder {
559
237
  const nativeChangeValue = CSL.Value.new(CSL.BigNum.from_str('2000000'));
560
238
  nativeChangeValue.set_multiasset(nativeChangeMA);
561
239
  txb.add_output(CSL.TransactionOutput.new(changeAddress, nativeChangeValue));
562
- logger.debug('[CSLTxBuilder] Added explicit change output for existing native tokens from funding UTxOs');
240
+ logger.debug(`Added explicit change output for existing native tokens from funding UTxOs`);
563
241
  }
564
- // Calculate script data hash BEFORE add_change_if_needed
565
- // Always call calc_script_data_hash for Plutus transactions - CSL requires it
566
242
  const costModels = this._createCostModels('v3');
567
243
  txb.calc_script_data_hash(costModels);
568
- // Now add change
569
244
  txb.add_change_if_needed(changeAddress);
570
- // Build transaction and patch scriptDataHash (CSL's is incorrect for Conway PlutusV3)
571
245
  const tx = txb.build_tx();
572
246
  return this._patchScriptDataHash(tx);
573
247
  }
574
248
  async buildUnsignedPlutusSpendTransaction(req, ctx) {
575
249
  try {
576
- // Determine execution units - default or from evaluator
577
- let finalExUnits = {
578
- mem: const_1.DEFAULT_EXECUTION_UNITS.mem.toString(),
579
- cpu: const_1.DEFAULT_EXECUTION_UNITS.cpu.toString()
580
- };
581
- if (ctx.evaluateTransaction) {
582
- // Build evaluation pass with high execution units
583
- logger.debug(`[CSLTxBuilder] Building evaluation pass for Plutus spending`);
584
- const highExUnits = {
585
- mem: const_1.HIGH_EXECUTION_UNITS.mem.toString(),
586
- cpu: const_1.HIGH_EXECUTION_UNITS.cpu.toString()
587
- };
588
- const evalTx = this._buildPlutusSpendTx(req, ctx, highExUnits);
589
- const evalTxCbor = Buffer.from(evalTx.to_bytes()).toString('hex');
590
- try {
591
- const evalResults = await ctx.evaluateTransaction(evalTxCbor);
592
- logger.debug(`[CSLTxBuilder] Evaluation results: ${JSON.stringify(evalResults)}`);
593
- if (evalResults && evalResults.length > 0) {
594
- const budget = evalResults[0].budget;
595
- finalExUnits = {
596
- mem: Math.ceil(budget.memory * const_1.EXECUTION_UNIT_BUFFER).toString(),
597
- cpu: Math.ceil(budget.cpu * const_1.EXECUTION_UNIT_BUFFER).toString()
598
- };
599
- logger.info(`[CSLTxBuilder] Using evaluated execution units: mem=${finalExUnits.mem}, cpu=${finalExUnits.cpu}`);
600
- }
601
- }
602
- catch (evalError) {
603
- logger.warn(`[CSLTxBuilder] Evaluation failed, using default units: ${evalError.message}`);
604
- }
605
- }
606
- else {
607
- logger.debug(`[CSLTxBuilder] No evaluator available, using default execution units`);
608
- }
609
- // Build final transaction with determined execution units
250
+ const finalExUnits = await this._evaluateExUnits((exUnits) => this._buildPlutusSpendTx(req, ctx, exUnits), ctx.evaluateTransaction);
610
251
  const unsignedTx = this._buildPlutusSpendTx(req, ctx, finalExUnits);
611
- // Export as CBOR hex
612
- const unsignedTxCbor = Buffer.from(unsignedTx.to_bytes()).toString('hex');
613
- // Extract transaction details
614
- const body = unsignedTx.body();
615
- const bodyBytes = body.to_bytes();
616
- const hash = (0, blake2b_1.default)(32).update(bodyBytes).digest('hex');
617
- const txBodyHash = hash;
618
- const feeLovelace = body.fee().to_str();
619
- // Extract outputs
620
- const outputs = [];
621
- const txOuts = body.outputs();
622
- for (let i = 0; i < txOuts.len(); i++) {
623
- const o = txOuts.get(i);
624
- outputs.push({
625
- address: o.address().to_bech32(),
626
- lovelace: o.amount().coin().to_str(),
627
- });
628
- }
629
- // Compute script hash from the validator script
630
- const spendScriptBytes = Buffer.from(req.plutusScriptExecution.validatorScript, 'hex');
631
- const spendPlutusScript = CSL.PlutusScript.new_v3(spendScriptBytes);
632
- const spendScriptHash = Buffer.from(spendPlutusScript.hash().to_bytes()).toString('hex');
633
- logger.info(`[CSLTxBuilder] Built unsigned Plutus spending transaction. Fee: ${feeLovelace}`);
634
- return {
635
- unsignedTxCbor,
636
- txBodyHash,
637
- senderAddress: req.senderAddress,
638
- network: this.cardanoClient.network,
639
- sizeBytes: unsignedTxCbor.length / 2,
640
- builderEngine: this.name,
641
- feeLovelace,
642
- inputs: ctx.utxos.map(u => ({
643
- txHash: u.txHash,
644
- index: u.outputIndex,
645
- lovelace: (0, tx_build_helper_1.getLovelace)(u).toString(),
646
- })),
647
- outputs,
648
- scriptHash: spendScriptHash,
649
- warnings: [],
650
- };
252
+ const txDetails = this._extractTxDetails(unsignedTx);
253
+ const { hashHex: scriptHash } = this._computeScriptHash(req.plutusScriptExecution.validatorScript);
254
+ logger.info(`Built unsigned Plutus spending transaction. Fee: ${txDetails.feeLovelace}`);
255
+ return this._buildResult(req, ctx, txDetails, { scriptHash });
651
256
  }
652
257
  catch (error) {
653
- logger.error(`[CSLTxBuilder] buildUnsignedPlutusSpendTransaction error: ${error?.message || error}`);
654
- (0, tx_build_helper_1.mapBuilderError)(error, 'lovelace');
258
+ logger.error(`buildUnsignedPlutusSpendTransaction error: ${error?.message || error}`);
259
+ (0, tx_build_helper_1.mapBuilderError)(error);
655
260
  }
656
261
  }
657
262
  /**
@@ -660,17 +265,13 @@ class CSLTxBuilder {
660
265
  _buildPlutusSpendTx(req, ctx, exUnits) {
661
266
  const { plutusScriptExecution } = req;
662
267
  const scriptUtxoRef = plutusScriptExecution.scriptUtxo;
663
- // Prepare addresses
664
268
  const recipientAddress = CSL.Address.from_bech32(req.recipientAddress);
665
269
  const changeAddress = CSL.Address.from_bech32(req.changeAddress ?? req.senderAddress);
666
- // Create Transaction Builder
667
270
  const txb = CSL.TransactionBuilder.new(this.txBuilderConfig);
668
- // Parse the Plutus validator script from CBOR hex (CBOR-wrapped flat UPLC bytecode)
669
- const scriptBytes = Buffer.from(plutusScriptExecution.validatorScript, 'hex');
670
- const plutusScript = CSL.PlutusScript.new_v3(scriptBytes);
271
+ const { plutusScript } = this._computeScriptHash(plutusScriptExecution.validatorScript);
671
272
  const scriptSource = CSL.PlutusScriptSource.new(plutusScript);
672
- // Build redeemer (using DetailedSchema: { "constructor": 0, "fields": [] } format)
673
- const redeemerPlutusData = CSL.PlutusData.from_json(JSON.stringify(plutusScriptExecution.redeemer), CSL.PlutusDatumSchema.DetailedSchema);
273
+ // Build redeemer
274
+ const redeemerPlutusData = this._toPlutusData(plutusScriptExecution.redeemer);
674
275
  const redeemer = CSL.Redeemer.new(CSL.RedeemerTag.new_spend(), CSL.BigNum.from_str('0'), // index will be corrected by CSL
675
276
  redeemerPlutusData, CSL.ExUnits.new(CSL.BigNum.from_str(exUnits.mem), CSL.BigNum.from_str(exUnits.cpu)));
676
277
  // Add the script input using TxInputsBuilder
@@ -680,100 +281,40 @@ class CSLTxBuilder {
680
281
  // Find the script UTxO to get its value
681
282
  const scriptOdatanoUtxo = ctx.utxos.find(u => u.txHash === scriptUtxoRef.txHash && u.outputIndex === scriptUtxoRef.outputIndex);
682
283
  if (!scriptOdatanoUtxo) {
683
- throw new Error(`[CSLTxBuilder] Script UTxO ${scriptUtxoRef.txHash}#${scriptUtxoRef.outputIndex} not found in provided UTxOs`);
284
+ throw new Error(`Script UTxO ${scriptUtxoRef.txHash}#${scriptUtxoRef.outputIndex} not found in provided UTxOs`);
684
285
  }
685
- // Build full multi-asset value for the script UTxO (lovelace + native assets)
686
- const scriptUtxoValue = CSL.Value.new(CSL.BigNum.from_str((0, tx_build_helper_1.getLovelace)(scriptOdatanoUtxo).toString()));
687
286
  const scriptNonAdaAssets = scriptOdatanoUtxo.amount.filter(a => a.unit.toLowerCase() !== 'lovelace' && BigInt(a.quantity) > 0n);
688
- if (scriptNonAdaAssets.length > 0) {
689
- const scriptMultiAsset = CSL.MultiAsset.new();
690
- for (const asset of scriptNonAdaAssets) {
691
- const { policyId, assetName } = (0, tx_build_helper_1.parseAssetUnit)(asset.unit);
692
- const policyHash = CSL.ScriptHash.from_bytes(Buffer.from(policyId, 'hex'));
693
- let assets = scriptMultiAsset.get(policyHash);
694
- if (!assets)
695
- assets = CSL.Assets.new();
696
- assets.insert(CSL.AssetName.new(Buffer.from(assetName, 'hex')), CSL.BigNum.from_str(asset.quantity));
697
- scriptMultiAsset.insert(policyHash, assets);
698
- }
699
- scriptUtxoValue.set_multiasset(scriptMultiAsset);
700
- }
287
+ const scriptUtxoValue = this._buildCslValue((0, tx_build_helper_1.getLovelace)(scriptOdatanoUtxo), scriptOdatanoUtxo.amount);
701
288
  // Build PlutusWitness: datum handling depends on whether UTxO has inline datum
702
289
  let plutusWitness;
703
290
  if (plutusScriptExecution.datum) {
704
- // Hash-based datum — add to witness set
705
- const datumPlutusData = CSL.PlutusData.from_json(JSON.stringify(plutusScriptExecution.datum), CSL.PlutusDatumSchema.DetailedSchema);
706
- const datumSource = CSL.DatumSource.new(datumPlutusData);
291
+ const datumSource = CSL.DatumSource.new(this._toPlutusData(plutusScriptExecution.datum));
707
292
  plutusWitness = CSL.PlutusWitness.new_with_ref(scriptSource, datumSource, redeemer);
708
293
  }
709
294
  else {
710
- // Inline datum on UTxO — no datum witness needed
711
295
  plutusWitness = CSL.PlutusWitness.new_with_ref_without_datum(scriptSource, redeemer);
712
296
  }
713
- // Add Plutus script input
714
297
  scriptInputsBuilder.add_plutus_script_input(plutusWitness, scriptInput, scriptUtxoValue);
715
298
  txb.set_inputs(scriptInputsBuilder);
716
- // Add required signers if specified (for Plutus validators checking extra_signatories)
717
- if (req.requiredSigners?.length) {
718
- for (const signerHex of req.requiredSigners) {
719
- txb.add_required_signer(CSL.Ed25519KeyHash.from_bytes(Buffer.from(signerHex, 'hex')));
720
- }
721
- }
299
+ this._addRequiredSigners(txb, req.requiredSigners);
722
300
  // Separate sender UTxOs (excluding script UTxO)
723
301
  const senderUtxos = ctx.utxos.filter(u => !(u.txHash === scriptUtxoRef.txHash && u.outputIndex === scriptUtxoRef.outputIndex));
724
- // Find an ADA-only UTxO for collateral (from sender UTxOs only)
725
- const collateralOdatanoUtxo = senderUtxos.find(u => u.amount.every(a => a.unit.toLowerCase() === 'lovelace'));
726
- if (collateralOdatanoUtxo) {
727
- const collateralBuilder = CSL.TxInputsBuilder.new();
728
- const colTxHash = CSL.TransactionHash.from_bytes(Buffer.from(collateralOdatanoUtxo.txHash, 'hex'));
729
- const colInput = CSL.TransactionInput.new(colTxHash, collateralOdatanoUtxo.outputIndex);
730
- const colAddress = CSL.Address.from_bech32(collateralOdatanoUtxo.address);
731
- const colValue = CSL.Value.new(CSL.BigNum.from_str((0, tx_build_helper_1.getLovelace)(collateralOdatanoUtxo).toString()));
732
- collateralBuilder.add_regular_input(colAddress, colInput, colValue);
733
- txb.set_collateral(collateralBuilder);
734
- }
735
- // Create output for recipient BEFORE coin selection so CSL knows the full
736
- // output requirements (lovelace + assets + fee) when selecting funding UTxOs.
737
- // Include multi-assets from the script UTxO in the continuing output.
302
+ // Collateral + funding UTxO separation (from sender UTxOs only)
303
+ const { fundingUtxos } = this._setupCollateral(txb, senderUtxos);
304
+ // Create output for recipient with multi-assets from the script UTxO
738
305
  const outputValue = CSL.Value.new(CSL.BigNum.from_str(String(req.lovelaceAmount || 2_000_000)));
739
306
  if (scriptNonAdaAssets.length > 0) {
740
- const outputMultiAsset = CSL.MultiAsset.new();
741
- for (const asset of scriptNonAdaAssets) {
742
- const { policyId, assetName } = (0, tx_build_helper_1.parseAssetUnit)(asset.unit);
743
- const policyHash = CSL.ScriptHash.from_bytes(Buffer.from(policyId, 'hex'));
744
- let assets = outputMultiAsset.get(policyHash);
745
- if (!assets)
746
- assets = CSL.Assets.new();
747
- assets.insert(CSL.AssetName.new(Buffer.from(assetName, 'hex')), CSL.BigNum.from_str(asset.quantity));
748
- outputMultiAsset.insert(policyHash, assets);
749
- }
750
- outputValue.set_multiasset(outputMultiAsset);
307
+ outputValue.set_multiasset(this._buildCslMultiAsset(scriptNonAdaAssets));
751
308
  }
752
309
  const recipientOutput = CSL.TransactionOutput.new(recipientAddress, outputValue);
753
- if (req.inlineDatum) {
754
- const datumData = CSL.PlutusData.from_json(JSON.stringify(req.inlineDatum), CSL.PlutusDatumSchema.DetailedSchema);
755
- recipientOutput.set_plutus_data(datumData);
756
- }
310
+ this._attachInlineDatum(recipientOutput, req.inlineDatum);
757
311
  txb.add_output(recipientOutput);
758
- // Exclude collateral UTxO from coin selection so it is not consumed on successful tx.
759
- // This preserves the dedicated collateral UTxO for future Plutus transactions.
760
- // If no funding UTxOs remain after excluding collateral, fall back to including it —
761
- // the same UTxO can appear in both inputs and collateral_inputs (consumed either way).
762
- let fundingUtxos = collateralOdatanoUtxo
763
- ? senderUtxos.filter(u => !(u.txHash === collateralOdatanoUtxo.txHash && u.outputIndex === collateralOdatanoUtxo.outputIndex))
764
- : senderUtxos;
765
- if (fundingUtxos.length === 0 && senderUtxos.length > 0) {
766
- logger.debug('[CSLTxBuilder] No funding UTxOs after collateral exclusion, including collateral in funding pool');
767
- fundingUtxos = senderUtxos;
768
- }
312
+ // Coin selection from funding UTxOs
769
313
  const cslFundingUtxos = this._mapMultiAssetUtxosToCslUtxos(fundingUtxos);
770
314
  txb.add_inputs_from(cslFundingUtxos, CSL.CoinSelectionStrategyCIP2.LargestFirstMultiAsset);
771
- // Calculate script data hash
772
315
  const costModels = this._createCostModels('v3');
773
316
  txb.calc_script_data_hash(costModels);
774
- // Add change
775
317
  txb.add_change_if_needed(changeAddress);
776
- // Build transaction and patch scriptDataHash (CSL's is incorrect for Conway PlutusV3)
777
318
  const tx = txb.build_tx();
778
319
  return this._patchScriptDataHash(tx);
779
320
  }
@@ -788,15 +329,11 @@ class CSLTxBuilder {
788
329
  const redeemers = witnesses.redeemers();
789
330
  if (!redeemers || redeemers.len() === 0)
790
331
  return tx;
791
- // 1. Get redeemers CBOR from the built transaction
792
332
  const redeemersBytes = Buffer.from(redeemers.to_bytes());
793
- // 2. Get datums CBOR if any (hash-based datums in witness set)
794
333
  const plutusData = witnesses.plutus_data();
795
334
  const datumsBytes = plutusData && plutusData.len() > 0
796
335
  ? Buffer.from(plutusData.to_bytes())
797
336
  : Buffer.alloc(0);
798
- // 3. Compute correct language views using costModelsToLanguageViewCbor
799
- // Cost models are already correctly ordered by normalizeCostModels (uses toCostModelArrV3)
800
337
  const costModelsJson = JSON.parse(this.protocolParameters.costModels || '{}');
801
338
  const costModelsObj = {};
802
339
  const v3 = costModelsJson['plutus:v3'] || costModelsJson['PlutusV3'];
@@ -804,12 +341,10 @@ class CSLTxBuilder {
804
341
  costModelsObj.PlutusScriptV3 = Array.from((0, cardano_costmodels_ts_1.toCostModelArrV3)(v3)).map(Number);
805
342
  }
806
343
  const languageViews = Buffer.from((0, cardano_costmodels_ts_1.costModelsToLanguageViewCbor)(costModelsObj, { mustHaveV3: !!costModelsObj.PlutusScriptV3 }).toBuffer());
807
- // 4. Compute: blake2b256(redeemers || datums || languageViews)
344
+ // blake2b256(redeemers || datums || languageViews)
808
345
  const hashInput = Buffer.concat([redeemersBytes, datumsBytes, languageViews]);
809
346
  const hashOutput = Buffer.alloc(32);
810
347
  (0, blake2b_1.default)(32).update(hashInput).digest(hashOutput);
811
- // 5. Patch at raw byte level (CSL caches serialized bytes internally,
812
- // so set_script_data_hash + Transaction.new doesn't change the output)
813
348
  const wrongHash = tx.body().script_data_hash();
814
349
  if (!wrongHash)
815
350
  return tx;
@@ -825,47 +360,170 @@ class CSLTxBuilder {
825
360
  }
826
361
  hashOutput.copy(txBytes, idx);
827
362
  logger.info(`_patchScriptDataHash: patched ${wrongHashBytes.toString('hex').slice(0, 16)}... → ${hashOutput.toString('hex').slice(0, 16)}...`);
828
- // 6. Reconstruct transaction from patched bytes
829
363
  return CSL.Transaction.from_bytes(txBytes);
830
364
  }
831
365
  //---------------------------------------------------------------------------
832
- // Private Helper Methods
366
+ // Shared Helper Methods
367
+ //---------------------------------------------------------------------------
368
+ /** Convert JSON PlutusData to CSL PlutusData using DetailedSchema */
369
+ _toPlutusData(json) {
370
+ return CSL.PlutusData.from_json(JSON.stringify(json), CSL.PlutusDatumSchema.DetailedSchema);
371
+ }
372
+ /** Build CSL MultiAsset from an array of { unit, quantity } (skips lovelace entries) */
373
+ _buildCslMultiAsset(assets) {
374
+ const multiAsset = CSL.MultiAsset.new();
375
+ for (const asset of assets) {
376
+ if (asset.unit.toLowerCase() === 'lovelace')
377
+ continue;
378
+ const { policyId, assetName } = (0, tx_build_helper_1.parseAssetUnit)(asset.unit);
379
+ const policyHash = CSL.ScriptHash.from_bytes(Buffer.from(policyId, 'hex'));
380
+ let existing = multiAsset.get(policyHash);
381
+ if (!existing)
382
+ existing = CSL.Assets.new();
383
+ existing.insert(CSL.AssetName.new(Buffer.from(assetName, 'hex')), CSL.BigNum.from_str(asset.quantity));
384
+ multiAsset.insert(policyHash, existing);
385
+ }
386
+ return multiAsset;
387
+ }
388
+ /** Build CSL Value from lovelace + optional assets array */
389
+ _buildCslValue(lovelace, assets) {
390
+ const value = CSL.Value.new(CSL.BigNum.from_str(String(lovelace)));
391
+ const nonAda = assets?.filter(a => a.unit.toLowerCase() !== 'lovelace' && BigInt(a.quantity) > 0n);
392
+ if (nonAda && nonAda.length > 0) {
393
+ value.set_multiasset(this._buildCslMultiAsset(nonAda));
394
+ }
395
+ return value;
396
+ }
397
+ /** Extract CBOR, hash, fee, and outputs from a built CSL Transaction */
398
+ _extractTxDetails(unsignedTx) {
399
+ const unsignedTxCbor = Buffer.from(unsignedTx.to_bytes()).toString("hex");
400
+ const body = unsignedTx.body();
401
+ const txBodyHash = (0, blake2b_1.default)(32).update(body.to_bytes()).digest('hex');
402
+ const feeLovelace = body.fee().to_str();
403
+ const outputs = [];
404
+ const txOuts = body.outputs();
405
+ for (let i = 0; i < txOuts.len(); i++) {
406
+ const o = txOuts.get(i);
407
+ outputs.push({ address: o.address().to_bech32(), lovelace: o.amount().coin().to_str() });
408
+ }
409
+ return { unsignedTxCbor, txBodyHash, feeLovelace, outputs, sizeBytes: unsignedTxCbor.length / 2 };
410
+ }
411
+ /** Build the standard TxBuildResult object */
412
+ _buildResult(req, ctx, txDetails, extra) {
413
+ return {
414
+ unsignedTxCbor: txDetails.unsignedTxCbor,
415
+ txBodyHash: txDetails.txBodyHash,
416
+ senderAddress: req.senderAddress,
417
+ network: this.cardanoClient.network,
418
+ sizeBytes: txDetails.sizeBytes,
419
+ builderEngine: this.name,
420
+ feeLovelace: txDetails.feeLovelace,
421
+ inputs: ctx.utxos.map(u => ({
422
+ txHash: u.txHash,
423
+ index: u.outputIndex,
424
+ lovelace: (0, tx_build_helper_1.getLovelace)(u).toString(),
425
+ })),
426
+ outputs: txDetails.outputs,
427
+ ...extra,
428
+ warnings: [],
429
+ };
430
+ }
431
+ /** Add required signers (Ed25519 key hashes) to the transaction builder */
432
+ _addRequiredSigners(txb, signers) {
433
+ if (signers?.length) {
434
+ for (const signerHex of signers) {
435
+ txb.add_required_signer(CSL.Ed25519KeyHash.from_bytes(Buffer.from(signerHex, 'hex')));
436
+ }
437
+ }
438
+ }
439
+ /** Attach inline PlutusData datum to a transaction output */
440
+ _attachInlineDatum(output, datum) {
441
+ if (datum) {
442
+ output.set_plutus_data(this._toPlutusData(datum));
443
+ }
444
+ }
445
+ /** Parse PlutusV3 script hex and compute its hash */
446
+ _computeScriptHash(scriptHex) {
447
+ const plutusScript = CSL.PlutusScript.new_v3(Buffer.from(scriptHex, 'hex'));
448
+ const cslHash = plutusScript.hash();
449
+ return { plutusScript, hashHex: Buffer.from(cslHash.to_bytes()).toString('hex'), cslHash };
450
+ }
451
+ /**
452
+ * Evaluate execution units via a two-pass build: first with high units for evaluation,
453
+ * then apply the evaluated budget with a safety buffer. Falls back to defaults on failure.
454
+ */
455
+ async _evaluateExUnits(buildFn, evaluator) {
456
+ const defaultUnits = {
457
+ mem: String(const_1.DEFAULT_EXECUTION_UNITS.mem),
458
+ cpu: String(const_1.DEFAULT_EXECUTION_UNITS.cpu)
459
+ };
460
+ if (!evaluator) {
461
+ logger.debug('No evaluator available, using default execution units');
462
+ return defaultUnits;
463
+ }
464
+ logger.debug('Building evaluation pass with high execution units');
465
+ const evalTx = buildFn({
466
+ mem: String(const_1.HIGH_EXECUTION_UNITS.mem),
467
+ cpu: String(const_1.HIGH_EXECUTION_UNITS.cpu)
468
+ });
469
+ const evalTxCbor = Buffer.from(evalTx.to_bytes()).toString('hex');
470
+ try {
471
+ const evalResults = await evaluator(evalTxCbor);
472
+ logger.debug(`Evaluation results: ${JSON.stringify(evalResults)}`);
473
+ if (evalResults && evalResults.length > 0) {
474
+ const budget = evalResults[0].budget;
475
+ const result = {
476
+ mem: Math.ceil(budget.memory * const_1.EXECUTION_UNIT_BUFFER).toString(),
477
+ cpu: Math.ceil(budget.cpu * const_1.EXECUTION_UNIT_BUFFER).toString()
478
+ };
479
+ logger.info(`Using evaluated execution units: mem=${result.mem}, cpu=${result.cpu}`);
480
+ return result;
481
+ }
482
+ }
483
+ catch (evalError) {
484
+ logger.warn(`Evaluation failed, using default units: ${evalError.message}`);
485
+ }
486
+ return defaultUnits;
487
+ }
488
+ /**
489
+ * Find an ADA-only UTxO for collateral, set it on the builder,
490
+ * and return the remaining funding UTxOs with collateral excluded.
491
+ */
492
+ _setupCollateral(txb, utxos) {
493
+ const collateralUtxo = utxos.find(u => u.amount.every(a => a.unit.toLowerCase() === 'lovelace'));
494
+ if (collateralUtxo) {
495
+ const collateralBuilder = CSL.TxInputsBuilder.new();
496
+ const txHash = CSL.TransactionHash.from_bytes(Buffer.from(collateralUtxo.txHash, 'hex'));
497
+ const input = CSL.TransactionInput.new(txHash, collateralUtxo.outputIndex);
498
+ const address = CSL.Address.from_bech32(collateralUtxo.address);
499
+ const value = CSL.Value.new(CSL.BigNum.from_str((0, tx_build_helper_1.getLovelace)(collateralUtxo).toString()));
500
+ collateralBuilder.add_regular_input(address, input, value);
501
+ txb.set_collateral(collateralBuilder);
502
+ }
503
+ let fundingUtxos = collateralUtxo
504
+ ? utxos.filter(u => !(u.txHash === collateralUtxo.txHash && u.outputIndex === collateralUtxo.outputIndex))
505
+ : utxos;
506
+ if (fundingUtxos.length === 0 && utxos.length > 0) {
507
+ logger.debug('No funding UTxOs after collateral exclusion, including collateral in funding pool');
508
+ fundingUtxos = utxos;
509
+ }
510
+ return { collateralUtxo, fundingUtxos };
511
+ }
512
+ //---------------------------------------------------------------------------
513
+ // CSL Mapping & Configuration Helpers
833
514
  //---------------------------------------------------------------------------
834
515
  /**
835
516
  * Map ODATANO UTxOs to CSL TransactionUnspentOutputs (with multi-asset support)
836
- * @param utxos ODATANO UTxO array
837
- * @returns CSL TransactionUnspentOutputs
838
517
  */
839
518
  _mapMultiAssetUtxosToCslUtxos(utxos) {
840
519
  const outs = CSL.TransactionUnspentOutputs.new();
841
520
  logger.debug(`Mapping ${utxos.length} UTxOs to CSL format`);
842
521
  for (const u of utxos) {
843
522
  logger.debug(`UTxO ${u.txHash}:${u.outputIndex} has ${u.amount.length} amounts: ${JSON.stringify(u.amount)}`);
844
- const txHashBytes = Buffer.from(u.txHash, "hex");
845
- const txHash = CSL.TransactionHash.from_bytes(txHashBytes);
523
+ const txHash = CSL.TransactionHash.from_bytes(Buffer.from(u.txHash, "hex"));
846
524
  const input = CSL.TransactionInput.new(txHash, u.outputIndex);
847
525
  const addr = CSL.Address.from_bech32(u.address);
848
- // Build value with ADA + all native assets
849
- const lovelace = CSL.BigNum.from_str((0, tx_build_helper_1.getLovelace)(u).toString());
850
- const value = CSL.Value.new(lovelace);
851
- // Add native assets if any
852
- const nonAdaAssets = u.amount.filter(a => a.unit.toLowerCase() !== 'lovelace' && BigInt(a.quantity) > 0n);
853
- if (nonAdaAssets.length > 0) {
854
- const multiAsset = CSL.MultiAsset.new();
855
- for (const asset of nonAdaAssets) {
856
- const { policyId, assetName } = (0, tx_build_helper_1.parseAssetUnit)(asset.unit);
857
- const policyHash = CSL.ScriptHash.from_bytes(Buffer.from(policyId, 'hex'));
858
- let assets = multiAsset.get(policyHash);
859
- if (!assets) {
860
- assets = CSL.Assets.new();
861
- }
862
- const assetNameBytes = Buffer.from(assetName, 'hex');
863
- const assetNameObj = CSL.AssetName.new(assetNameBytes);
864
- assets.insert(assetNameObj, CSL.BigNum.from_str(asset.quantity));
865
- multiAsset.insert(policyHash, assets);
866
- }
867
- value.set_multiasset(multiAsset);
868
- }
526
+ const value = this._buildCslValue((0, tx_build_helper_1.getLovelace)(u), u.amount);
869
527
  const output = CSL.TransactionOutput.new(addr, value);
870
528
  outs.add(CSL.TransactionUnspentOutput.new(input, output));
871
529
  }
@@ -873,12 +531,8 @@ class CSLTxBuilder {
873
531
  }
874
532
  /**
875
533
  * Create a CSL TransactionBuilderConfig from protocol parameters
876
- * This config is created once and reused for all transactions
877
- * @param protocolParams LedgerProtocolParameter
878
- * @returns CSL.TransactionBuilderConfig
879
534
  */
880
535
  _createTxBuilderConfig(protocolParams) {
881
- // required values for CSL config
882
536
  const minFeeA = protocolParams.minFeeA;
883
537
  const minFeeB = protocolParams.minFeeB;
884
538
  const poolDeposit = protocolParams.poolDeposit;
@@ -888,21 +542,15 @@ class CSLTxBuilder {
888
542
  const coinsPerUtxoByte = protocolParams.coinsPerUtxoSize;
889
543
  const feeAlgo = CSL.LinearFee.new(CSL.BigNum.from_str(String(minFeeA)), CSL.BigNum.from_str(String(minFeeB)));
890
544
  // Plutus execution unit prices for script fee calculation
891
- // Protocol params may contain decimal prices (e.g., 0.0577) - convert to integer numerators
892
545
  const priceMemValue = protocolParams.priceMem;
893
546
  const priceStepValue = protocolParams.priceStep;
894
- // Convert decimal prices to integer numerators (or use defaults if already integers or missing)
895
547
  const priceMemNumerator = (typeof priceMemValue === 'number' && priceMemValue < 1)
896
- ? Math.round(priceMemValue * 10000) // 0.0577 -> 577
548
+ ? Math.round(priceMemValue * 10000)
897
549
  : (priceMemValue || 577);
898
550
  const priceStepNumerator = (typeof priceStepValue === 'number' && priceStepValue < 1)
899
- ? Math.round(priceStepValue * 10000000) // 0.0000721 -> 721
551
+ ? Math.round(priceStepValue * 10000000)
900
552
  : (priceStepValue || 721);
901
- const exUnitPrices = CSL.ExUnitPrices.new(CSL.UnitInterval.new(CSL.BigNum.from_str(String(priceMemNumerator)), // numerator
902
- CSL.BigNum.from_str('10000') // denominator (0.0577 per memory unit)
903
- ), CSL.UnitInterval.new(CSL.BigNum.from_str(String(priceStepNumerator)), // numerator
904
- CSL.BigNum.from_str('10000000') // denominator (0.0000721 per CPU step)
905
- ));
553
+ const exUnitPrices = CSL.ExUnitPrices.new(CSL.UnitInterval.new(CSL.BigNum.from_str(String(priceMemNumerator)), CSL.BigNum.from_str('10000')), CSL.UnitInterval.new(CSL.BigNum.from_str(String(priceStepNumerator)), CSL.BigNum.from_str('10000000')));
906
554
  const cfg = CSL.TransactionBuilderConfigBuilder.new()
907
555
  .fee_algo(feeAlgo)
908
556
  .pool_deposit(CSL.BigNum.from_str(String(poolDeposit)))
@@ -910,26 +558,21 @@ class CSLTxBuilder {
910
558
  .max_tx_size(Number(maxTxSize))
911
559
  .max_value_size(Number(maxValueSize))
912
560
  .coins_per_utxo_byte(CSL.BigNum.from_str(String(coinsPerUtxoByte)))
913
- .ex_unit_prices(exUnitPrices) // Critical for Plutus fee calculation
561
+ .ex_unit_prices(exUnitPrices)
914
562
  .build();
915
563
  logger.info(`TransactionBuilderConfig created with Plutus execution unit prices.`);
916
564
  return cfg;
917
565
  }
918
566
  /**
919
567
  * Map ODATANO metadata JSON to CSL GeneralTransactionMetadata
920
- * @param metadataJson JSON metadata object
921
- * @returns CSL GeneralTransactionMetadata
922
568
  */
923
569
  _mapOdatanoMetadataToCSLMetadata(metadataJson) {
924
- // Metadata must be an object with labels as keys
925
570
  if (typeof metadataJson !== 'object' || Array.isArray(metadataJson) || metadataJson === null) {
926
- throw new Error(`[CSLTxBuilder] Invalid metadata format. Expected object, got ${typeof metadataJson}`);
571
+ throw new Error(`Invalid metadata format. Expected object, got ${typeof metadataJson}`);
927
572
  }
928
573
  const metadata = CSL.GeneralTransactionMetadata.new();
929
574
  for (const [label, value] of Object.entries(metadataJson)) {
930
- // Convert label to BigNum
931
575
  const numericLabel = parseInt(label, 10);
932
- // Convert JSON Value to CSL TransactionMetadatum
933
576
  const txMetadatum = this._jsonToCSLMetadatum(value);
934
577
  logger.debug(`Created TransactionMetadatum for label ${numericLabel}`);
935
578
  metadata.insert(CSL.BigNum.from_str(String(numericLabel)), txMetadatum);
@@ -939,15 +582,9 @@ class CSLTxBuilder {
939
582
  }
940
583
  /**
941
584
  * Create Costmdls from protocol parameters for specific Plutus version
942
- * Required for correct script integrity hash calculation
943
- * @param version - Optional: specific Plutus version ('v1', 'v2', 'v3'). If not provided, adds all versions.
944
- * @returns CSL.Costmdls
945
585
  */
946
586
  _createCostModels(version) {
947
587
  const costModels = CSL.Costmdls.new();
948
- // Blockfrost returns cost models as objects ({name: value}), CSL expects arrays.
949
- // Convert object format to sorted-key array defensively (normalizeCostModels in mappers.ts
950
- // already does this at the backend level, but handle it here too for robustness).
951
588
  const toArray = (costs) => {
952
589
  if (Array.isArray(costs))
953
590
  return costs;
@@ -956,60 +593,42 @@ class CSLTxBuilder {
956
593
  }
957
594
  return null;
958
595
  };
596
+ const addCostModel = (language, costs, label) => {
597
+ const costModel = CSL.CostModel.new();
598
+ for (let i = 0; i < costs.length; i++) {
599
+ costModel.set(i, CSL.Int.new_i32(costs[i]));
600
+ }
601
+ costModels.insert(language, costModel);
602
+ logger.debug(`Added ${label} cost model with ${costs.length} parameters`);
603
+ };
959
604
  try {
960
- // Parse cost models JSON from protocol parameters
961
- // Format: { "plutus:v1": [array of 166 numbers], "plutus:v2": [array of 175 numbers], ... }
962
605
  const costModelsJson = JSON.parse(this.protocolParameters.costModels || '{}');
963
- // PlutusV1 cost model
964
606
  if (!version || version === 'v1') {
965
- const plutusV1Costs = toArray(costModelsJson['plutus:v1'] || costModelsJson['PlutusV1']);
966
- if (plutusV1Costs) {
967
- const plutusV1CostModel = CSL.CostModel.new();
968
- for (let i = 0; i < plutusV1Costs.length; i++) {
969
- plutusV1CostModel.set(i, CSL.Int.new_i32(plutusV1Costs[i]));
970
- }
971
- costModels.insert(CSL.Language.new_plutus_v1(), plutusV1CostModel);
972
- logger.debug(`[CSLTxBuilder] Added PlutusV1 cost model with ${plutusV1Costs.length} parameters`);
973
- }
607
+ const v1Costs = toArray(costModelsJson['plutus:v1'] || costModelsJson['PlutusV1']);
608
+ if (v1Costs)
609
+ addCostModel(CSL.Language.new_plutus_v1(), v1Costs, 'PlutusV1');
974
610
  }
975
- // PlutusV2 cost model
976
611
  if (!version || version === 'v2') {
977
- const plutusV2Costs = toArray(costModelsJson['plutus:v2'] || costModelsJson['PlutusV2']);
978
- if (plutusV2Costs) {
979
- const plutusV2CostModel = CSL.CostModel.new();
980
- for (let i = 0; i < plutusV2Costs.length; i++) {
981
- plutusV2CostModel.set(i, CSL.Int.new_i32(plutusV2Costs[i]));
982
- }
983
- costModels.insert(CSL.Language.new_plutus_v2(), plutusV2CostModel);
984
- logger.debug(`[CSLTxBuilder] Added PlutusV2 cost model with ${plutusV2Costs.length} parameters`);
985
- }
612
+ const v2Costs = toArray(costModelsJson['plutus:v2'] || costModelsJson['PlutusV2']);
613
+ if (v2Costs)
614
+ addCostModel(CSL.Language.new_plutus_v2(), v2Costs, 'PlutusV2');
986
615
  }
987
- // PlutusV3 cost model (check both "plutus:v3" and "PlutusV3" formats)
988
616
  if (!version || version === 'v3') {
989
- const plutusV3CostsRaw = toArray(costModelsJson['plutus:v3'] || costModelsJson['PlutusV3']);
990
- if (plutusV3CostsRaw) {
991
- // Pad to 297 parameters (Conway Chang 2) using defaults from cardano-costmodels-ts.
992
- // Blockfrost may return only 251 (Chang 1). The node expects 297 for scriptDataHash.
993
- // toCostModelArrV3() fills missing params with default values — same as Buildooor.
994
- const plutusV3Costs = Array.from((0, cardano_costmodels_ts_1.toCostModelArrV3)(plutusV3CostsRaw)).map(Number);
995
- const plutusV3CostModel = CSL.CostModel.new();
996
- for (let i = 0; i < plutusV3Costs.length; i++) {
997
- plutusV3CostModel.set(i, CSL.Int.new_i32(plutusV3Costs[i]));
998
- }
999
- costModels.insert(CSL.Language.new_plutus_v3(), plutusV3CostModel);
1000
- logger.debug(`[CSLTxBuilder] Added PlutusV3 cost model with ${plutusV3Costs.length} parameters`);
617
+ const v3CostsRaw = toArray(costModelsJson['plutus:v3'] || costModelsJson['PlutusV3']);
618
+ if (v3CostsRaw) {
619
+ // Pad to 297 parameters (Conway Chang 2) using defaults from cardano-costmodels-ts
620
+ const v3Costs = Array.from((0, cardano_costmodels_ts_1.toCostModelArrV3)(v3CostsRaw)).map(Number);
621
+ addCostModel(CSL.Language.new_plutus_v3(), v3Costs, 'PlutusV3');
1001
622
  }
1002
623
  }
1003
624
  }
1004
625
  catch (error) {
1005
- logger.warn(`[CSLTxBuilder] Failed to parse cost models: ${error}. Using empty cost models.`);
626
+ logger.warn(`Failed to parse cost models: ${error}. Using empty cost models.`);
1006
627
  }
1007
628
  return costModels;
1008
629
  }
1009
630
  /**
1010
631
  * Convert JSON value to CSL TransactionMetadatum
1011
- * @param value JSON value
1012
- * @returns CSL TransactionMetadatum
1013
632
  */
1014
633
  _jsonToCSLMetadatum(value) {
1015
634
  if (typeof value === 'number' || typeof value === 'bigint') {
@@ -1035,7 +654,7 @@ class CSLTxBuilder {
1035
654
  }
1036
655
  return CSL.TransactionMetadatum.new_map(map);
1037
656
  }
1038
- throw new Error(`[CSLTxBuilder] Unsupported metadata value type: ${typeof value}`);
657
+ throw new Error(`Unsupported metadata value type: ${typeof value}`);
1039
658
  }
1040
659
  }
1041
660
  exports.CSLTxBuilder = CSLTxBuilder;