@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,636 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BuildooorTxBuilder = void 0;
7
+ const buildooor_1 = require("@harmoniclabs/buildooor");
8
+ const uint8array_utils_1 = require("@harmoniclabs/uint8array-utils");
9
+ const tx_build_helper_1 = require("../../utils/tx-build-helper");
10
+ const cds_1 = __importDefault(require("@sap/cds"));
11
+ const cardano_ledger_ts_1 = require("@harmoniclabs/cardano-ledger-ts");
12
+ const TxMetadata_1 = require("@harmoniclabs/cardano-ledger-ts/dist/tx/metadata/TxMetadata");
13
+ const TxMetadatum_1 = require("@harmoniclabs/cardano-ledger-ts/dist/tx/metadata/TxMetadatum");
14
+ const plutus_data_1 = require("@harmoniclabs/plutus-data");
15
+ const const_1 = require("../../utils/const");
16
+ const logger = cds_1.default.log('BuildooorTxBuilder');
17
+ /**
18
+ * BuildooorTxBuilder - Implementation of CardanoTxBuilder using Buildooor library
19
+ */
20
+ class BuildooorTxBuilder {
21
+ name = 'BuildooorTxBuilder';
22
+ txBuilder;
23
+ cardanoClient;
24
+ /**
25
+ * Initialize the builder
26
+ * @param client - The CardanoClient instance
27
+ * @param protocolParams - Optional protocol parameters (if not provided, fetched from backend)
28
+ */
29
+ async init(client, protocolParams) {
30
+ this.cardanoClient = client;
31
+ const params = protocolParams ?? await client.getProtocolParameters();
32
+ const txbParameters = this._mapLedgerParametersToBuildooorParams(params);
33
+ this.txBuilder = new buildooor_1.TxBuilder(txbParameters);
34
+ logger.debug(`TxBuilder initialized with protocol parameters`);
35
+ }
36
+ /**
37
+ * Build unsigned ADA transfer transaction
38
+ * @param req transaction build request
39
+ * @param ctx transaction build context
40
+ * @returns {Promise<TxBuildResult>} transaction build result
41
+ */
42
+ async buildUnsignedAdaTransfer(req, ctx) {
43
+ try {
44
+ // mapping of ODATANO UTxO Type to ledger-ts UTxO objects (with multi-asset support)
45
+ // This allows spending UTxOs that contain native assets - they will be returned in the change output
46
+ const ledgerUtxos = ctx.utxos.map(utxo => this._mapMultiAssetUtxoToLedgerUtxo(utxo));
47
+ // and map to Buildooor TxIn objects for inputs
48
+ const inputs = ledgerUtxos.map(utxo => ({ utxo }));
49
+ // set Addresses
50
+ const recipientAddress = cardano_ledger_ts_1.Address.fromString(req.recipientAddress);
51
+ const changeAddress = cardano_ledger_ts_1.Address.fromString(req.changeAddress ?? req.senderAddress);
52
+ // set Amount
53
+ const amount = BigInt(String(req.lovelaceAmount));
54
+ // build new outputs for recipient
55
+ const txOutParams = {
56
+ address: recipientAddress,
57
+ value: cardano_ledger_ts_1.Value.lovelaces(amount),
58
+ };
59
+ if (req.outputDatum) {
60
+ txOutParams.datum = (0, tx_build_helper_1.jsonToPlutusData)(req.outputDatum);
61
+ }
62
+ const outputs = [new cardano_ledger_ts_1.TxOut(txOutParams)];
63
+ // build the transaction
64
+ const tx = await this.txBuilder.build({
65
+ inputs,
66
+ outputs,
67
+ changeAddress,
68
+ });
69
+ // full unsigned tx cbor (4-tuple, witness empty)
70
+ const unsignedTxBytes = tx.toCbor().toBuffer();
71
+ const unsignedTxCbor = (0, uint8array_utils_1.toHex)(unsignedTxBytes);
72
+ const txBodyHash = tx.hash.toString();
73
+ logger.debug(`Built unsigned transaction successfully.`);
74
+ return {
75
+ unsignedTxCbor: unsignedTxCbor,
76
+ txBodyHash: txBodyHash,
77
+ senderAddress: req.senderAddress,
78
+ network: this.cardanoClient.network,
79
+ sizeBytes: unsignedTxBytes.length,
80
+ builderEngine: this.name,
81
+ feeLovelace: tx.body.fee.toString(),
82
+ inputs: ctx.utxos.map(u => ({
83
+ txHash: u.txHash,
84
+ index: u.outputIndex,
85
+ lovelace: (0, tx_build_helper_1.getLovelace)(u).toString()
86
+ })),
87
+ outputs: tx.body.outputs.map((o) => ({
88
+ address: o.address?.toString?.() ?? "",
89
+ lovelace: o.value?.lovelaces?.toString?.() ?? "0"
90
+ })),
91
+ warnings: []
92
+ };
93
+ }
94
+ catch (err) {
95
+ (0, tx_build_helper_1.mapBuilderError)(err, 'lovelace');
96
+ }
97
+ }
98
+ async buildUnsignedTransactionWithMetadata(req, ctx) {
99
+ try {
100
+ // mapping of ODATANO UTxO Type to ledger-ts UTxO objects (with multi-asset support)
101
+ // This allows spending UTxOs that contain native assets - they will be returned in the change output
102
+ const ledgerUtxos = ctx.utxos.map(utxo => this._mapMultiAssetUtxoToLedgerUtxo(utxo));
103
+ // Buildooor TxIn objects for inputs
104
+ const inputs = ledgerUtxos.map(utxo => ({ utxo }));
105
+ // Addresses
106
+ const recipientAddress = cardano_ledger_ts_1.Address.fromString(req.recipientAddress);
107
+ const changeAddress = cardano_ledger_ts_1.Address.fromString(req.changeAddress ?? req.senderAddress);
108
+ // Amount
109
+ const amount = BigInt(String(req.lovelaceAmount));
110
+ const metadata = this._mapOdatanoMetadataToLedgerMetadata(req.metadataJson);
111
+ // build new outputs for recipient
112
+ const outputs = [
113
+ new cardano_ledger_ts_1.TxOut({
114
+ address: recipientAddress,
115
+ value: cardano_ledger_ts_1.Value.lovelaces(amount)
116
+ })
117
+ ];
118
+ // build the transaction
119
+ const tx = await this.txBuilder.build({
120
+ inputs,
121
+ outputs,
122
+ changeAddress,
123
+ metadata
124
+ });
125
+ // full unsigned tx cbor (4-tuple, witness empty)
126
+ const unsignedTxBytes = tx.toCbor().toBuffer();
127
+ const unsignedTxCbor = (0, uint8array_utils_1.toHex)(unsignedTxBytes);
128
+ const txBodyHash = tx.hash.toString();
129
+ logger.debug(`Built unsigned transaction successfully.`);
130
+ return {
131
+ unsignedTxCbor: unsignedTxCbor,
132
+ txBodyHash: txBodyHash,
133
+ senderAddress: req.senderAddress,
134
+ network: this.cardanoClient.network,
135
+ builderEngine: this.name,
136
+ sizeBytes: unsignedTxBytes.length,
137
+ feeLovelace: tx.body.fee.toString(),
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: tx.body.outputs.map((o) => ({
144
+ address: o.address?.toString?.() ?? "",
145
+ lovelace: o.value?.lovelaces?.toString?.() ?? "0"
146
+ })),
147
+ warnings: []
148
+ };
149
+ }
150
+ catch (err) {
151
+ (0, tx_build_helper_1.mapBuilderError)(err, 'lovelace');
152
+ }
153
+ }
154
+ async buildUnsignedMultiAssetTransaction(req, ctx) {
155
+ if (!req.assets || req.assets.length === 0) {
156
+ throw new Error('[BuildooorTxBuilder] buildUnsignedMultiAssetTransaction requires assets to be specified');
157
+ }
158
+ try {
159
+ // Map all available UTxOs to ledger UTxOs for Buildooor (let builder handle selection)
160
+ const ledgerUtxos = ctx.utxos.map(utxo => this._mapMultiAssetUtxoToLedgerUtxo(utxo));
161
+ // Buildooor TxIn objects for inputs
162
+ const inputs = ledgerUtxos.map(utxo => ({ utxo }));
163
+ // Addresses
164
+ const recipientAddress = cardano_ledger_ts_1.Address.fromString(req.recipientAddress);
165
+ const changeAddress = cardano_ledger_ts_1.Address.fromString(req.changeAddress ?? req.senderAddress);
166
+ // Build output value with ADA + assets
167
+ let outputValue = cardano_ledger_ts_1.Value.lovelaces(BigInt(req.lovelaceAmount));
168
+ for (const asset of req.assets) {
169
+ // Parse policyId and assetName from unit (format: policyId.assetName or policyId+assetName)
170
+ const { policyId, assetName } = (0, tx_build_helper_1.parseAssetUnit)(asset.unit);
171
+ const policyHash = new cardano_ledger_ts_1.Hash28(policyId);
172
+ const assetValue = cardano_ledger_ts_1.Value.singleAsset(policyHash, Buffer.from(assetName, 'hex'), BigInt(asset.quantity));
173
+ outputValue = cardano_ledger_ts_1.Value.add(outputValue, assetValue);
174
+ }
175
+ // Build output
176
+ const outputs = [
177
+ new cardano_ledger_ts_1.TxOut({
178
+ address: recipientAddress,
179
+ value: outputValue
180
+ })
181
+ ];
182
+ // Build the transaction
183
+ const tx = await this.txBuilder.build({
184
+ inputs,
185
+ outputs,
186
+ changeAddress,
187
+ });
188
+ // Full unsigned tx cbor (4-tuple, witness empty)
189
+ const unsignedTxBytes = tx.toCbor().toBuffer();
190
+ const unsignedTxCbor = (0, uint8array_utils_1.toHex)(unsignedTxBytes);
191
+ const txBodyHash = tx.hash.toString();
192
+ logger.debug(`Built unsigned multi-asset transaction successfully.`);
193
+ return {
194
+ unsignedTxCbor: unsignedTxCbor,
195
+ txBodyHash: txBodyHash,
196
+ senderAddress: req.senderAddress,
197
+ network: this.cardanoClient.network,
198
+ sizeBytes: unsignedTxBytes.length,
199
+ builderEngine: this.name,
200
+ feeLovelace: tx.body.fee.toString(),
201
+ inputs: ctx.utxos.map(u => ({
202
+ txHash: u.txHash,
203
+ index: u.outputIndex,
204
+ lovelace: (0, tx_build_helper_1.getLovelace)(u).toString()
205
+ })),
206
+ outputs: tx.body.outputs.map((o) => ({
207
+ address: o.address?.toString?.() ?? "",
208
+ lovelace: o.value?.lovelaces?.toString?.() ?? "0"
209
+ })),
210
+ warnings: []
211
+ };
212
+ }
213
+ catch (err) {
214
+ // Extract asset unit from error message if possible
215
+ const assetMatch = err?.message?.match(/not enough\s+([a-f0-9.]+)/i);
216
+ const assetUnit = assetMatch?.[1];
217
+ (0, tx_build_helper_1.mapBuilderError)(err, assetUnit);
218
+ }
219
+ }
220
+ async buildUnsignedMintTransaction(req, ctx) {
221
+ try {
222
+ // Map all available UTxOs to ledger UTxOs (let builder handle selection)
223
+ // Use multi-asset mapper to support burn transactions
224
+ const ledgerUtxos = ctx.utxos.map(utxo => this._mapMultiAssetUtxoToLedgerUtxo(utxo));
225
+ // Buildooor TxIn objects for inputs
226
+ const inputs = ledgerUtxos.map(utxo => ({ utxo }));
227
+ // Addresses
228
+ const recipientAddress = cardano_ledger_ts_1.Address.fromString(req.recipientAddress);
229
+ const changeAddress = cardano_ledger_ts_1.Address.fromString(req.changeAddress ?? req.senderAddress);
230
+ // Parse the minting policy script once
231
+ const scriptBytes = Buffer.from(req.mintingPolicyScript, 'hex');
232
+ const script = cardano_ledger_ts_1.Script.fromCbor(scriptBytes);
233
+ // Helper to build mints array with specified execution units
234
+ const buildMints = (exUnits) => {
235
+ const mints = [];
236
+ for (const mintAction of req.mintActions) {
237
+ const { policyId, assetName } = (0, tx_build_helper_1.parseAssetUnit)(mintAction.assetUnit);
238
+ const policyHash = new cardano_ledger_ts_1.Hash28(policyId);
239
+ const assetValue = cardano_ledger_ts_1.Value.singleAsset(policyHash, Buffer.from(assetName, 'hex'), BigInt(mintAction.quantity));
240
+ mints.push({
241
+ value: assetValue,
242
+ script: {
243
+ inline: script,
244
+ redeemer: new plutus_data_1.DataI(mintAction.redeemer ?? 0),
245
+ executionUnits: exUnits
246
+ }
247
+ });
248
+ }
249
+ return mints;
250
+ };
251
+ // Calculate total mint value for output (only positive quantities - mints, not burns)
252
+ let mintValue = cardano_ledger_ts_1.Value.lovelaces(0n);
253
+ for (const mintAction of req.mintActions) {
254
+ const quantity = BigInt(mintAction.quantity);
255
+ if (quantity > 0n) {
256
+ const { policyId, assetName } = (0, tx_build_helper_1.parseAssetUnit)(mintAction.assetUnit);
257
+ const policyHash = new cardano_ledger_ts_1.Hash28(policyId);
258
+ const assetValue = cardano_ledger_ts_1.Value.singleAsset(policyHash, Buffer.from(assetName, 'hex'), quantity);
259
+ mintValue = cardano_ledger_ts_1.Value.add(mintValue, assetValue);
260
+ }
261
+ }
262
+ // Build output value - recipient gets the minted assets + min ADA
263
+ let outputValue = cardano_ledger_ts_1.Value.lovelaces(BigInt(req.lovelaceAmount || 1_000_000));
264
+ outputValue = cardano_ledger_ts_1.Value.add(outputValue, mintValue);
265
+ // Build output
266
+ const outputs = [
267
+ new cardano_ledger_ts_1.TxOut({
268
+ address: recipientAddress,
269
+ value: outputValue
270
+ })
271
+ ];
272
+ // Find an ADA-only UTxO for collateral (Plutus scripts require ADA-only collateral)
273
+ const adaOnlyUtxo = ctx.utxos.find(u => u.amount.every(a => a.unit.toLowerCase() === 'lovelace'));
274
+ if (!adaOnlyUtxo) {
275
+ throw new Error('[BuildooorTxBuilder] No ADA-only UTxO available for collateral. Plutus scripts require ADA-only collateral.');
276
+ }
277
+ // Use only 1 collateral to minimize tx size
278
+ const collateralUtxos = [this._mapOdatanoUtxoToLedgerUtxo(adaOnlyUtxo)];
279
+ // Determine execution units based on evaluator availability
280
+ let finalExUnits = const_1.DEFAULT_EXECUTION_UNITS;
281
+ if (ctx.evaluateTransaction) {
282
+ // Build first pass with high execution units for evaluation
283
+ logger.debug(`Building evaluation pass with high execution units`);
284
+ const evalMints = buildMints(const_1.HIGH_EXECUTION_UNITS);
285
+ const evalTx = await this.txBuilder.build({
286
+ inputs,
287
+ outputs,
288
+ changeAddress,
289
+ mints: evalMints,
290
+ collaterals: collateralUtxos
291
+ });
292
+ const evalTxCbor = (0, uint8array_utils_1.toHex)(evalTx.toCbor().toBuffer());
293
+ try {
294
+ // Evaluate to get exact execution units
295
+ const evalResults = await ctx.evaluateTransaction(evalTxCbor);
296
+ logger.debug(`Evaluation results: ${JSON.stringify(evalResults)}`);
297
+ if (evalResults && evalResults.length > 0) {
298
+ // Use the evaluated budget (take first result for single script)
299
+ const budget = evalResults[0].budget;
300
+ // Add safety margin to evaluated units
301
+ finalExUnits = {
302
+ mem: Math.ceil(budget.memory * const_1.EXECUTION_UNIT_BUFFER),
303
+ cpu: Math.ceil(budget.cpu * const_1.EXECUTION_UNIT_BUFFER)
304
+ };
305
+ logger.info(`Using evaluated execution units: mem=${finalExUnits.mem}, cpu=${finalExUnits.cpu}`);
306
+ }
307
+ }
308
+ catch (evalError) {
309
+ logger.warn(`Evaluation failed, using default units: ${evalError.message}`);
310
+ // Fall back to defaults on evaluation failure
311
+ }
312
+ }
313
+ else {
314
+ logger.debug(`No evaluator available, using default execution units`);
315
+ }
316
+ // Build with final execution units
317
+ const mints = buildMints(finalExUnits);
318
+ // First build to calculate base fee
319
+ const txFirstPass = await this.txBuilder.build({
320
+ inputs,
321
+ outputs,
322
+ changeAddress,
323
+ mints: mints,
324
+ collaterals: collateralUtxos
325
+ });
326
+ // Add minimal buffer for witness set CBOR overhead (signing adds ~44 bytes)
327
+ const calculatedFee = BigInt(txFirstPass.body.fee.toString());
328
+ const witnessBuffer = BigInt(const_1.WITNESS_BUFFER_BYTES);
329
+ const adjustedMinFee = calculatedFee + witnessBuffer;
330
+ logger.debug(`First pass fee: ${calculatedFee}, rebuilding with witness buffer: ${adjustedMinFee}`);
331
+ // Final build with adjusted minimum fee to account for witness overhead
332
+ const tx = await this.txBuilder.build({
333
+ inputs,
334
+ outputs,
335
+ changeAddress,
336
+ mints: mints,
337
+ collaterals: collateralUtxos,
338
+ fee: adjustedMinFee
339
+ });
340
+ // Full unsigned tx cbor (4-tuple, witness empty)
341
+ const unsignedTxBytes = tx.toCbor().toBuffer();
342
+ const unsignedTxCbor = (0, uint8array_utils_1.toHex)(unsignedTxBytes);
343
+ const txBodyHash = tx.hash.toString();
344
+ logger.debug(`Built unsigned minting transaction successfully with fee: ${tx.body.fee.toString()}`);
345
+ return {
346
+ unsignedTxCbor: unsignedTxCbor,
347
+ txBodyHash: txBodyHash,
348
+ senderAddress: req.senderAddress,
349
+ network: this.cardanoClient.network,
350
+ builderEngine: this.name,
351
+ sizeBytes: unsignedTxBytes.length,
352
+ feeLovelace: tx.body.fee.toString(),
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: tx.body.outputs.map((o) => ({
359
+ address: o.address?.toString?.() ?? "",
360
+ lovelace: o.value?.lovelaces?.toString?.() ?? "0"
361
+ })),
362
+ warnings: []
363
+ };
364
+ }
365
+ catch (err) {
366
+ (0, tx_build_helper_1.mapBuilderError)(err, 'lovelace');
367
+ }
368
+ }
369
+ async buildUnsignedPlutusSpendTransaction(req, ctx) {
370
+ try {
371
+ const { plutusScriptExecution } = req;
372
+ // Parse the validator script
373
+ const scriptBytes = Buffer.from(plutusScriptExecution.validatorScript, 'hex');
374
+ const script = cardano_ledger_ts_1.Script.fromCbor(scriptBytes);
375
+ // Convert redeemer JSON to PlutusData
376
+ const redeemerData = (0, tx_build_helper_1.jsonToPlutusData)(plutusScriptExecution.redeemer);
377
+ // Determine datum: "inline" if no datum provided (assumes inline datum on UTxO), otherwise convert
378
+ const datum = plutusScriptExecution.datum
379
+ ? (0, tx_build_helper_1.jsonToPlutusData)(plutusScriptExecution.datum)
380
+ : "inline";
381
+ // Find the specific script UTxO in the provided context UTxOs
382
+ // The coordinator is responsible for including the script UTxO in ctx.utxos
383
+ const scriptUtxoRef = plutusScriptExecution.scriptUtxo;
384
+ const scriptOdatanoUtxo = ctx.utxos.find(u => u.txHash === scriptUtxoRef.txHash && u.outputIndex === scriptUtxoRef.outputIndex);
385
+ if (!scriptOdatanoUtxo) {
386
+ throw new Error(`[BuildooorTxBuilder] Script UTxO ${scriptUtxoRef.txHash}#${scriptUtxoRef.outputIndex} not found in provided UTxOs`);
387
+ }
388
+ // Map the script UTxO to ledger format
389
+ const scriptLedgerUtxo = this._mapMultiAssetUtxoToLedgerUtxo(scriptOdatanoUtxo);
390
+ // Map sender UTxOs (excluding the script UTxO) as regular inputs
391
+ const senderUtxos = ctx.utxos.filter(u => !(u.txHash === scriptUtxoRef.txHash && u.outputIndex === scriptUtxoRef.outputIndex));
392
+ const senderLedgerUtxos = senderUtxos.map(utxo => this._mapMultiAssetUtxoToLedgerUtxo(utxo));
393
+ // Addresses
394
+ const recipientAddress = cardano_ledger_ts_1.Address.fromString(req.recipientAddress);
395
+ const changeAddress = cardano_ledger_ts_1.Address.fromString(req.changeAddress ?? req.senderAddress);
396
+ // Build output
397
+ const outputValue = cardano_ledger_ts_1.Value.lovelaces(BigInt(req.lovelaceAmount || 2_000_000));
398
+ const outputs = [
399
+ new cardano_ledger_ts_1.TxOut({
400
+ address: recipientAddress,
401
+ value: outputValue
402
+ })
403
+ ];
404
+ // Find an ADA-only UTxO for collateral (from sender UTxOs only)
405
+ const adaOnlyUtxo = senderUtxos.find(u => u.amount.every(a => a.unit.toLowerCase() === 'lovelace'));
406
+ if (!adaOnlyUtxo) {
407
+ throw new Error('[BuildooorTxBuilder] No ADA-only UTxO available for collateral. Plutus scripts require ADA-only collateral.');
408
+ }
409
+ const collateralUtxos = [this._mapOdatanoUtxoToLedgerUtxo(adaOnlyUtxo)];
410
+ // Helper to build inputs with specified execution units
411
+ const buildInputs = (exUnits) => {
412
+ // Script input with witness
413
+ const scriptInput = {
414
+ utxo: scriptLedgerUtxo,
415
+ inputScript: {
416
+ script,
417
+ datum,
418
+ redeemer: redeemerData,
419
+ executionUnits: exUnits
420
+ }
421
+ };
422
+ // Regular sender inputs for fees
423
+ const regularInputs = senderLedgerUtxos.map(utxo => ({ utxo }));
424
+ return [scriptInput, ...regularInputs];
425
+ };
426
+ // Determine execution units based on evaluator availability
427
+ let finalExUnits = const_1.DEFAULT_EXECUTION_UNITS;
428
+ if (ctx.evaluateTransaction) {
429
+ // Build first pass with high execution units for evaluation
430
+ logger.debug(`Building evaluation pass with high execution units`);
431
+ const evalInputs = buildInputs(const_1.HIGH_EXECUTION_UNITS);
432
+ const evalTx = await this.txBuilder.build({
433
+ inputs: evalInputs,
434
+ outputs,
435
+ changeAddress,
436
+ collaterals: collateralUtxos
437
+ });
438
+ const evalTxCbor = (0, uint8array_utils_1.toHex)(evalTx.toCbor().toBuffer());
439
+ try {
440
+ const evalResults = await ctx.evaluateTransaction(evalTxCbor);
441
+ logger.debug(`Evaluation results: ${JSON.stringify(evalResults)}`);
442
+ if (evalResults && evalResults.length > 0) {
443
+ const budget = evalResults[0].budget;
444
+ finalExUnits = {
445
+ mem: Math.ceil(budget.memory * const_1.EXECUTION_UNIT_BUFFER),
446
+ cpu: Math.ceil(budget.cpu * const_1.EXECUTION_UNIT_BUFFER)
447
+ };
448
+ logger.info(`Using evaluated execution units: mem=${finalExUnits.mem}, cpu=${finalExUnits.cpu}`);
449
+ }
450
+ }
451
+ catch (evalError) {
452
+ logger.warn(`Evaluation failed, using default units: ${evalError.message}`);
453
+ }
454
+ }
455
+ else {
456
+ logger.debug(`No evaluator available, using default execution units`);
457
+ }
458
+ // Build with final execution units
459
+ const inputs = buildInputs(finalExUnits);
460
+ // First build to calculate base fee
461
+ const txFirstPass = await this.txBuilder.build({
462
+ inputs,
463
+ outputs,
464
+ changeAddress,
465
+ collaterals: collateralUtxos
466
+ });
467
+ // Add minimal buffer for witness set CBOR overhead
468
+ const calculatedFee = BigInt(txFirstPass.body.fee.toString());
469
+ const witnessBuffer = BigInt(const_1.WITNESS_BUFFER_BYTES);
470
+ const adjustedMinFee = calculatedFee + witnessBuffer;
471
+ logger.debug(`First pass fee: ${calculatedFee}, rebuilding with witness buffer: ${adjustedMinFee}`);
472
+ // Final build with adjusted minimum fee
473
+ const tx = await this.txBuilder.build({
474
+ inputs,
475
+ outputs,
476
+ changeAddress,
477
+ collaterals: collateralUtxos,
478
+ fee: adjustedMinFee
479
+ });
480
+ const unsignedTxBytes = tx.toCbor().toBuffer();
481
+ const unsignedTxCbor = (0, uint8array_utils_1.toHex)(unsignedTxBytes);
482
+ const txBodyHash = tx.hash.toString();
483
+ logger.debug(`Built unsigned Plutus spending transaction successfully with fee: ${tx.body.fee.toString()}`);
484
+ return {
485
+ unsignedTxCbor,
486
+ txBodyHash,
487
+ senderAddress: req.senderAddress,
488
+ network: this.cardanoClient.network,
489
+ builderEngine: this.name,
490
+ sizeBytes: unsignedTxBytes.length,
491
+ feeLovelace: tx.body.fee.toString(),
492
+ inputs: ctx.utxos.map(u => ({
493
+ txHash: u.txHash,
494
+ index: u.outputIndex,
495
+ lovelace: (0, tx_build_helper_1.getLovelace)(u).toString()
496
+ })),
497
+ outputs: tx.body.outputs.map((o) => ({
498
+ address: o.address?.toString?.() ?? "",
499
+ lovelace: o.value?.lovelaces?.toString?.() ?? "0"
500
+ })),
501
+ warnings: []
502
+ };
503
+ }
504
+ catch (err) {
505
+ (0, tx_build_helper_1.mapBuilderError)(err, 'lovelace');
506
+ }
507
+ }
508
+ //---------------------------------------------------------------------------
509
+ // Private Helper Methods
510
+ //---------------------------------------------------------------------------
511
+ /**
512
+ * Map ODATANO LedgerProtocolParameter to Buildooor's ProtocolParameters shape
513
+ * @param protocolParameters ledger protocol parameters
514
+ * @returns mapped protocol parameters
515
+ */
516
+ _mapLedgerParametersToBuildooorParams(protocolParameters) {
517
+ // Map LedgerProtocolParameter to Buildooor's ProtocolParameters shape
518
+ // Using defaultProtocolParameters as base and overriding with actual values
519
+ return {
520
+ ...cardano_ledger_ts_1.defaultProtocolParameters,
521
+ txFeePerByte: Number(protocolParameters.minFeeA),
522
+ txFeeFixed: Number(protocolParameters.minFeeB),
523
+ utxoCostPerByte: Number(protocolParameters.coinsPerUtxoSize),
524
+ poolDeposit: Number(protocolParameters.poolDeposit),
525
+ keyDeposit: Number(protocolParameters.keyDeposit),
526
+ maxTxSize: Number(protocolParameters.maxTxSize),
527
+ maxValueSize: Number(protocolParameters.maxValSize),
528
+ };
529
+ }
530
+ /**
531
+ * Map ODATANO UTxO to Ledger UTxO (ADA-only)
532
+ * @param utxos ODATANO UTxO
533
+ * @returns mapped Ledger UTxO
534
+ */
535
+ _mapOdatanoUtxoToLedgerUtxo(utxos) {
536
+ (0, tx_build_helper_1.assertAdaOnly)(utxos);
537
+ const outRef = new cardano_ledger_ts_1.TxOutRef({
538
+ id: utxos.txHash,
539
+ index: utxos.outputIndex
540
+ });
541
+ const addr = cardano_ledger_ts_1.Address.fromString(utxos.address);
542
+ const value = cardano_ledger_ts_1.Value.lovelaces((0, tx_build_helper_1.getLovelace)(utxos));
543
+ return new cardano_ledger_ts_1.UTxO({
544
+ utxoRef: outRef,
545
+ resolved: new cardano_ledger_ts_1.TxOut({
546
+ address: addr,
547
+ value,
548
+ datum: undefined,
549
+ refScript: undefined
550
+ })
551
+ });
552
+ }
553
+ /**
554
+ * Map ODATANO UTxO to Ledger UTxO (with multi-asset support)
555
+ * @param utxo ODATANO UTxO
556
+ * @returns mapped Ledger UTxO
557
+ */
558
+ _mapMultiAssetUtxoToLedgerUtxo(utxo) {
559
+ const outRef = new cardano_ledger_ts_1.TxOutRef({
560
+ id: utxo.txHash,
561
+ index: utxo.outputIndex
562
+ });
563
+ const addr = cardano_ledger_ts_1.Address.fromString(utxo.address);
564
+ // Build Value from all amounts
565
+ let value = cardano_ledger_ts_1.Value.lovelaces((0, tx_build_helper_1.getLovelace)(utxo));
566
+ for (const amount of utxo.amount) {
567
+ if (amount.unit.toLowerCase() !== 'lovelace') {
568
+ const { policyId, assetName } = (0, tx_build_helper_1.parseAssetUnit)(amount.unit);
569
+ const policyHash = new cardano_ledger_ts_1.Hash28(policyId);
570
+ const assetValue = cardano_ledger_ts_1.Value.singleAsset(policyHash, Buffer.from(assetName, 'hex'), BigInt(amount.quantity));
571
+ value = cardano_ledger_ts_1.Value.add(value, assetValue);
572
+ }
573
+ }
574
+ return new cardano_ledger_ts_1.UTxO({
575
+ utxoRef: outRef,
576
+ resolved: new cardano_ledger_ts_1.TxOut({
577
+ address: addr,
578
+ value,
579
+ datum: undefined,
580
+ refScript: undefined
581
+ })
582
+ });
583
+ }
584
+ /**
585
+ * Map ODATANO metadata JSON to Ledger TxMetadata
586
+ * @param metadataJson metadata in JSON format
587
+ * @returns mapped TxMetadata
588
+ */
589
+ _mapOdatanoMetadataToLedgerMetadata(metadataJson) {
590
+ if (!metadataJson) {
591
+ return new TxMetadata_1.TxMetadata({});
592
+ }
593
+ const metadata = {};
594
+ for (const [label, value] of Object.entries(metadataJson)) {
595
+ // convert label to number
596
+ const numericLabel = parseInt(label, 10);
597
+ // convert JSON value to TxMetadatum
598
+ const txMetadatum = this._jsonToTxMetadatum(value);
599
+ logger.debug(`Created TxMetadatum for label ${numericLabel}: ${txMetadatum.constructor.name}`);
600
+ metadata[numericLabel] = txMetadatum;
601
+ }
602
+ logger.debug(`Creating TxMetadata with ${Object.keys(metadata).length} labels`);
603
+ const txMetadata = new TxMetadata_1.TxMetadata(metadata);
604
+ logger.debug(`TxMetadata created: ${txMetadata.constructor.name}, instanceof check: ${txMetadata instanceof TxMetadata_1.TxMetadata}`);
605
+ return txMetadata;
606
+ }
607
+ /**
608
+ * Recursively convert JSON value to TxMetadatum
609
+ * @param value JSON value
610
+ * @returns corresponding TxMetadatum
611
+ */
612
+ _jsonToTxMetadatum(value) {
613
+ if (typeof value === 'number' || typeof value === 'bigint') {
614
+ return new TxMetadatum_1.TxMetadatumInt(BigInt(value));
615
+ }
616
+ if (typeof value === 'string') {
617
+ return new TxMetadatum_1.TxMetadatumText(value);
618
+ }
619
+ if (Array.isArray(value)) {
620
+ return new TxMetadatum_1.TxMetadatumList(value.map(v => this._jsonToTxMetadatum(v)));
621
+ }
622
+ if (typeof value === 'object' && value !== null) {
623
+ const map = [];
624
+ for (const [k, v] of Object.entries(value)) {
625
+ map.push({
626
+ k: new TxMetadatum_1.TxMetadatumText(k),
627
+ v: this._jsonToTxMetadatum(v)
628
+ });
629
+ }
630
+ return new TxMetadatum_1.TxMetadatumMap(map);
631
+ }
632
+ throw new Error(`[BuildooorTxBuilder] Unsupported metadata value type: ${typeof value}`);
633
+ }
634
+ }
635
+ exports.BuildooorTxBuilder = BuildooorTxBuilder;
636
+ //# sourceMappingURL=buildooor-tx.js.map