@odatano/core 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/@cds-models/CardanoSignService/index.d.ts +12 -0
- package/@cds-models/CardanoSignService/index.d.ts.map +1 -1
- package/@cds-models/CardanoSignService/index.js.map +1 -1
- package/@cds-models/CardanoSignService/index.ts +2 -0
- package/@cds-models/CardanoTransactionService/index.d.ts +32 -6
- package/@cds-models/CardanoTransactionService/index.d.ts.map +1 -1
- package/@cds-models/CardanoTransactionService/index.js.map +1 -1
- package/@cds-models/CardanoTransactionService/index.ts +11 -9
- package/@cds-models/odatano/cardano/index.d.ts +12 -0
- package/@cds-models/odatano/cardano/index.d.ts.map +1 -1
- package/@cds-models/odatano/cardano/index.js.map +1 -1
- package/@cds-models/odatano/cardano/index.ts +2 -0
- package/README.md +2 -2
- package/db/schema.cds +8 -0
- package/package.json +1 -1
- package/srv/blockchain/backends/gerolamo-backend.d.ts.map +1 -0
- package/srv/blockchain/backends/gerolamo-backend.js +321 -0
- package/srv/blockchain/backends/gerolamo-backend.js.map +1 -0
- package/srv/blockchain/backends/koios-backend.d.ts.map +1 -1
- package/srv/blockchain/backends/koios-backend.js +19 -9
- package/srv/blockchain/backends/koios-backend.js.map +1 -1
- package/srv/blockchain/cardano-tx-builder.d.ts.map +1 -1
- package/srv/blockchain/cardano-tx-builder.js +93 -10
- package/srv/blockchain/cardano-tx-builder.js.map +1 -1
- package/srv/blockchain/transaction-building/buildooor-tx.d.ts.map +1 -1
- package/srv/blockchain/transaction-building/buildooor-tx.js +216 -55
- package/srv/blockchain/transaction-building/buildooor-tx.js.map +1 -1
- package/srv/blockchain/transaction-building/csl-tx.d.ts.map +1 -1
- package/srv/blockchain/transaction-building/csl-tx.js +179 -20
- package/srv/blockchain/transaction-building/csl-tx.js.map +1 -1
- package/srv/cardano-tx-service.cds +24 -3
- package/srv/cardano-tx-service.d.ts.map +1 -1
- package/srv/cardano-tx-service.js +174 -6
- package/srv/cardano-tx-service.js.map +1 -1
- package/srv/utils/mappers.d.ts.map +1 -1
- package/srv/utils/mappers.js +2 -0
- package/srv/utils/mappers.js.map +1 -1
- package/srv/utils/plutus-placeholders.d.ts.map +1 -0
- package/srv/utils/plutus-placeholders.js +87 -0
- package/srv/utils/plutus-placeholders.js.map +1 -0
- package/srv/utils/types.d.ts.map +1 -1
- package/srv/utils/types.js.map +1 -1
|
@@ -73,11 +73,13 @@ class CardanoTransactionBuilder {
|
|
|
73
73
|
async buildSimpleAdaTransaction(req, protocolParameters) {
|
|
74
74
|
const builder = await this.ensureInitialized();
|
|
75
75
|
// prepare the transaction build context
|
|
76
|
+
const senderUtxos = await this._fetchUtxosForAddress(req.senderAddress);
|
|
77
|
+
const forcedUtxos = await this._resolveForceInputs(req.forceInputs ?? [], senderUtxos);
|
|
76
78
|
const txContext = {
|
|
77
|
-
utxos:
|
|
79
|
+
utxos: mergeUtxosUnique(senderUtxos, forcedUtxos),
|
|
78
80
|
protocolParameters: protocolParameters
|
|
79
81
|
};
|
|
80
|
-
logger.debug(`Prepared build context: ${txContext.utxos.length} UTxOs for coin selection`);
|
|
82
|
+
logger.debug(`Prepared build context: ${txContext.utxos.length} UTxOs for coin selection (${forcedUtxos.length} forced)`);
|
|
81
83
|
// Build the unsigned transfer transaction
|
|
82
84
|
const txBuildResult = await builder.buildUnsignedTransfer(req, txContext);
|
|
83
85
|
logger.debug(`Built simple ADA transaction successfully.`);
|
|
@@ -93,11 +95,13 @@ class CardanoTransactionBuilder {
|
|
|
93
95
|
async buildTransactionWithMetadata(req, protocolParameters) {
|
|
94
96
|
const builder = await this.ensureInitialized();
|
|
95
97
|
// Prepare the transaction build context
|
|
98
|
+
const senderUtxos = await this._fetchUtxosForAddress(req.senderAddress);
|
|
99
|
+
const forcedUtxos = await this._resolveForceInputs(req.forceInputs ?? [], senderUtxos);
|
|
96
100
|
const txContext = {
|
|
97
|
-
utxos:
|
|
101
|
+
utxos: mergeUtxosUnique(senderUtxos, forcedUtxos),
|
|
98
102
|
protocolParameters: protocolParameters
|
|
99
103
|
};
|
|
100
|
-
logger.debug(`Prepared build context: ${txContext.utxos.length} UTxOs for coin selection`);
|
|
104
|
+
logger.debug(`Prepared build context: ${txContext.utxos.length} UTxOs for coin selection (${forcedUtxos.length} forced)`);
|
|
101
105
|
// Build the unsigned transaction with metadata
|
|
102
106
|
const txBuildResult = await builder.buildUnsignedTransactionWithMetadata(req, txContext);
|
|
103
107
|
logger.debug(`Built transaction with metadata successfully.`);
|
|
@@ -116,11 +120,13 @@ class CardanoTransactionBuilder {
|
|
|
116
120
|
}
|
|
117
121
|
const builder = await this.ensureInitialized();
|
|
118
122
|
// Prepare the transaction build context
|
|
123
|
+
const senderUtxos = await this._fetchUtxosForAddress(req.senderAddress);
|
|
124
|
+
const forcedUtxos = await this._resolveForceInputs(req.forceInputs ?? [], senderUtxos);
|
|
119
125
|
const txContext = {
|
|
120
|
-
utxos:
|
|
126
|
+
utxos: mergeUtxosUnique(senderUtxos, forcedUtxos),
|
|
121
127
|
protocolParameters: protocolParameters
|
|
122
128
|
};
|
|
123
|
-
logger.debug(`Prepared build context: ${txContext.utxos.length} UTxOs for coin selection`);
|
|
129
|
+
logger.debug(`Prepared build context: ${txContext.utxos.length} UTxOs for coin selection (${forcedUtxos.length} forced)`);
|
|
124
130
|
// Build the unsigned transfer transaction (unified with simple ADA transfer)
|
|
125
131
|
const txBuildResult = await builder.buildUnsignedTransfer(req, txContext);
|
|
126
132
|
logger.debug(`Built multi-asset transaction successfully.`);
|
|
@@ -145,15 +151,17 @@ class CardanoTransactionBuilder {
|
|
|
145
151
|
const builder = await this.ensureInitialized();
|
|
146
152
|
const cardanoClient = this.client;
|
|
147
153
|
// Prepare the transaction build context
|
|
154
|
+
const senderUtxos = await this._fetchUtxosForAddress(req.senderAddress);
|
|
155
|
+
const forcedUtxos = await this._resolveForceInputs(req.forceInputs ?? [], senderUtxos);
|
|
148
156
|
const txContext = {
|
|
149
|
-
utxos:
|
|
157
|
+
utxos: mergeUtxosUnique(senderUtxos, forcedUtxos),
|
|
150
158
|
protocolParameters: protocolParameters,
|
|
151
159
|
// Pass evaluator if Ogmios is available for dynamic execution unit calculation
|
|
152
160
|
evaluateTransaction: cardanoClient.hasOgmiosBackend()
|
|
153
161
|
? (cbor) => cardanoClient.evaluateTransaction(cbor)
|
|
154
162
|
: undefined
|
|
155
163
|
};
|
|
156
|
-
logger.debug(`Prepared build context: ${txContext.utxos.length} UTxOs for coin selection`);
|
|
164
|
+
logger.debug(`Prepared build context: ${txContext.utxos.length} UTxOs for coin selection (${forcedUtxos.length} forced)`);
|
|
157
165
|
if (txContext.evaluateTransaction) {
|
|
158
166
|
logger.debug(`Ogmios available - will use dynamic script evaluation`);
|
|
159
167
|
}
|
|
@@ -205,14 +213,18 @@ class CardanoTransactionBuilder {
|
|
|
205
213
|
inlineDatum: scriptOutput.inlineDatum,
|
|
206
214
|
});
|
|
207
215
|
}
|
|
216
|
+
// Resolve forced inputs (may overlap with sender UTxOs or the script UTxO — dedup below).
|
|
217
|
+
// The builder itself will skip any forced ref that matches scriptRef.
|
|
218
|
+
const forcedUtxos = await this._resolveForceInputs(req.forceInputs ?? [], allUtxos);
|
|
219
|
+
const mergedUtxos = mergeUtxosUnique(allUtxos, forcedUtxos);
|
|
208
220
|
const txContext = {
|
|
209
|
-
utxos:
|
|
221
|
+
utxos: mergedUtxos,
|
|
210
222
|
protocolParameters: protocolParameters,
|
|
211
223
|
evaluateTransaction: cardanoClient.hasOgmiosBackend()
|
|
212
224
|
? (cbor) => cardanoClient.evaluateTransaction(cbor)
|
|
213
225
|
: undefined
|
|
214
226
|
};
|
|
215
|
-
logger.debug(`Prepared build context: ${txContext.utxos.length} UTxOs for coin selection (${senderUtxos.length} sender + ${allUtxos.length - senderUtxos.length} script)`);
|
|
227
|
+
logger.debug(`Prepared build context: ${txContext.utxos.length} UTxOs for coin selection (${senderUtxos.length} sender + ${allUtxos.length - senderUtxos.length} script + ${forcedUtxos.length} forced)`);
|
|
216
228
|
if (txContext.evaluateTransaction) {
|
|
217
229
|
logger.debug(`Ogmios available - will use dynamic script evaluation`);
|
|
218
230
|
}
|
|
@@ -223,6 +235,59 @@ class CardanoTransactionBuilder {
|
|
|
223
235
|
logger.debug(`Built Plutus spending transaction successfully.`);
|
|
224
236
|
return txBuildResult;
|
|
225
237
|
}
|
|
238
|
+
/**
|
|
239
|
+
* Resolve a list of {txHash, outputIndex} refs to full UTxO records.
|
|
240
|
+
* Prefers matching against already-fetched sender UTxOs to avoid extra backend calls.
|
|
241
|
+
* Throws TransactionValidationError if any ref cannot be resolved (missing or spent).
|
|
242
|
+
* Deduplicates input refs before lookup.
|
|
243
|
+
*
|
|
244
|
+
* @param refs forced-input refs from the request
|
|
245
|
+
* @param senderUtxos sender UTxOs already fetched (for cheap matching)
|
|
246
|
+
* @returns resolved UTxOs, in the same order as deduped refs
|
|
247
|
+
*/
|
|
248
|
+
async _resolveForceInputs(refs, senderUtxos) {
|
|
249
|
+
if (!refs || refs.length === 0)
|
|
250
|
+
return [];
|
|
251
|
+
// Dedup by "txHash#index" key
|
|
252
|
+
const seen = new Set();
|
|
253
|
+
const dedupedRefs = refs.filter(r => {
|
|
254
|
+
const key = `${r.txHash}#${r.outputIndex}`;
|
|
255
|
+
if (seen.has(key))
|
|
256
|
+
return false;
|
|
257
|
+
seen.add(key);
|
|
258
|
+
return true;
|
|
259
|
+
});
|
|
260
|
+
const resolved = [];
|
|
261
|
+
for (const ref of dedupedRefs) {
|
|
262
|
+
// 1) Cheap path: already in sender UTxOs
|
|
263
|
+
const local = senderUtxos.find(u => u.txHash === ref.txHash && u.outputIndex === ref.outputIndex);
|
|
264
|
+
if (local) {
|
|
265
|
+
resolved.push(local);
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
// 2) Fallback: fetch the producing transaction and look up the output.
|
|
269
|
+
// If the output does not exist OR the UTxO has already been spent, treat as missing.
|
|
270
|
+
let tx;
|
|
271
|
+
try {
|
|
272
|
+
tx = await this.client.getTransaction(ref.txHash);
|
|
273
|
+
}
|
|
274
|
+
catch (err) {
|
|
275
|
+
throw new errors_1.TransactionValidationError(`forceInput ${ref.txHash}#${ref.outputIndex} not found on-chain`, err);
|
|
276
|
+
}
|
|
277
|
+
const output = tx?.outputs?.find(o => o.outputIndex === ref.outputIndex);
|
|
278
|
+
if (!output) {
|
|
279
|
+
throw new errors_1.TransactionValidationError(`forceInput ${ref.txHash}#${ref.outputIndex} not found on-chain`);
|
|
280
|
+
}
|
|
281
|
+
resolved.push({
|
|
282
|
+
txHash: ref.txHash,
|
|
283
|
+
outputIndex: ref.outputIndex,
|
|
284
|
+
address: output.address,
|
|
285
|
+
amount: output.amount,
|
|
286
|
+
inlineDatum: output.inlineDatum,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
return resolved;
|
|
290
|
+
}
|
|
226
291
|
/**
|
|
227
292
|
* Fetch UTxOs for a given address
|
|
228
293
|
* @param address bech32 address
|
|
@@ -239,4 +304,22 @@ class CardanoTransactionBuilder {
|
|
|
239
304
|
}
|
|
240
305
|
}
|
|
241
306
|
exports.CardanoTransactionBuilder = CardanoTransactionBuilder;
|
|
307
|
+
/**
|
|
308
|
+
* Merge two UTxO lists, skipping refs already present in the first.
|
|
309
|
+
* Used to add forced UTxOs to a context without duplicating existing entries.
|
|
310
|
+
*/
|
|
311
|
+
function mergeUtxosUnique(base, extra) {
|
|
312
|
+
if (extra.length === 0)
|
|
313
|
+
return base;
|
|
314
|
+
const seen = new Set(base.map(u => `${u.txHash}#${u.outputIndex}`));
|
|
315
|
+
const result = [...base];
|
|
316
|
+
for (const u of extra) {
|
|
317
|
+
const key = `${u.txHash}#${u.outputIndex}`;
|
|
318
|
+
if (!seen.has(key)) {
|
|
319
|
+
seen.add(key);
|
|
320
|
+
result.push(u);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return result;
|
|
324
|
+
}
|
|
242
325
|
//# sourceMappingURL=cardano-tx-builder.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cardano-tx-builder.js","sourceRoot":"","sources":["cardano-tx-builder.ts"],"names":[],"mappings":";;;;;;AAAA,mDAA2B;AAI3B,oFAA+E;AAG/E,
|
|
1
|
+
{"version":3,"file":"cardano-tx-builder.js","sourceRoot":"","sources":["cardano-tx-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildooor-tx.d.ts","sourceRoot":"","sources":["buildooor-tx.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,cAAc,EAAE,aAAa,EAAkC,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"buildooor-tx.d.ts","sourceRoot":"","sources":["buildooor-tx.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,cAAc,EAAE,aAAa,EAAkC,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AA4BhM,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAKlD;;GAEG;AACH,qBAAa,kBAAmB,YAAW,gBAAgB;IACzD,SAAgB,IAAI,wBAAwB;IAC5C,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,aAAa,CAAiB;IAEtC;;;;OAIG;IACU,IAAI,CAAC,MAAM,EAAE,aAAa,EAAE,cAAc,CAAC,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAQlG;;OAEG;IACU,qBAAqB,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAqCvF,oCAAoC,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IA4BtG,4BAA4B,CAAC,GAAG,EAAE,kBAAkB,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAkGlG,mCAAmC,CAAC,GAAG,EAAE,yBAAyB,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAgL7H,kFAAkF;IAClF,OAAO,CAAC,iBAAiB;IAuBzB,8CAA8C;IAC9C,OAAO,CAAC,YAAY;IA8BpB;;;OAGG;YACW,gBAAgB;IAgC9B,qFAAqF;YACvE,uBAAuB;IAQrC;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;IAe9B;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAcxB,sEAAsE;IACtE,OAAO,CAAC,iBAAiB;IAazB,+CAA+C;IAC/C,OAAO,CAAC,WAAW;IAQnB;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAa5B,+FAA+F;IAC/F,OAAO,CAAC,mBAAmB;IAO3B;;;OAGG;IACH,OAAO,CAAC,+BAA+B;IAavC;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAuB3B;;OAEG;IACH,OAAO,CAAC,qCAAqC;IAa7C;;OAEG;IACH,OAAO,CAAC,2BAA2B;IAcnC;;OAEG;IACH,OAAO,CAAC,8BAA8B;IAetC;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAiBzB;;OAEG;IACH,OAAO,CAAC,mCAAmC;IAoB3C;;OAEG;IACH,OAAO,CAAC,kBAAkB;CA0B3B"}
|
|
@@ -8,6 +8,7 @@ const buildooor_1 = require("@harmoniclabs/buildooor");
|
|
|
8
8
|
const uint8array_utils_1 = require("@harmoniclabs/uint8array-utils");
|
|
9
9
|
const tx_build_helper_1 = require("../../utils/tx-build-helper");
|
|
10
10
|
const errors_1 = require("../../utils/errors");
|
|
11
|
+
const plutus_placeholders_1 = require("../../utils/plutus-placeholders");
|
|
11
12
|
const cds_1 = __importDefault(require("@sap/cds"));
|
|
12
13
|
const cardano_ledger_ts_1 = require("@harmoniclabs/cardano-ledger-ts");
|
|
13
14
|
const TxMetadata_1 = require("@harmoniclabs/cardano-ledger-ts/dist/tx/metadata/TxMetadata");
|
|
@@ -42,8 +43,6 @@ class BuildooorTxBuilder {
|
|
|
42
43
|
if (!ctx.utxos || ctx.utxos.length === 0) {
|
|
43
44
|
throw new errors_1.InsufficientFundsError('lovelace', BigInt(req.lovelaceAmount || 0), 0n);
|
|
44
45
|
}
|
|
45
|
-
const ledgerUtxos = ctx.utxos.map(utxo => this._mapMultiAssetUtxoToLedgerUtxo(utxo));
|
|
46
|
-
const allInputs = ledgerUtxos.map(utxo => ({ utxo }));
|
|
47
46
|
const recipientAddress = cardano_ledger_ts_1.Address.fromString(req.recipientAddress);
|
|
48
47
|
const changeAddress = cardano_ledger_ts_1.Address.fromString(req.changeAddress ?? req.senderAddress);
|
|
49
48
|
const amount = BigInt(String(req.lovelaceAmount));
|
|
@@ -53,12 +52,17 @@ class BuildooorTxBuilder {
|
|
|
53
52
|
outputValue = this._buildLedgerValue(amount, req.assets);
|
|
54
53
|
}
|
|
55
54
|
const outputs = [this._buildTxOut(recipientAddress, outputValue, req.outputDatum)];
|
|
56
|
-
//
|
|
57
|
-
const
|
|
58
|
-
|
|
55
|
+
// Partition: forced UTxOs become fixed inputs; rest is the coin-selection pool
|
|
56
|
+
const { forced, rest } = this._partitionForcedInputs(ctx.utxos, req.forceInputs);
|
|
57
|
+
const forcedInputs = forced.map(u => ({ utxo: this._mapMultiAssetUtxoToLedgerUtxo(u) }));
|
|
58
|
+
const candidateInputs = rest.map(u => ({ utxo: this._mapMultiAssetUtxoToLedgerUtxo(u) }));
|
|
59
|
+
// Coin selection on candidates only; forced inputs are prepended unconditionally
|
|
60
|
+
const selected = this.txBuilder.keepRelevant(outputValue, candidateInputs);
|
|
61
|
+
const inputs = [...forcedInputs, ...selected];
|
|
62
|
+
logger.debug(`Coin selection: ${selected.length}/${candidateInputs.length} UTxOs selected (${forcedInputs.length} forced) for transfer`);
|
|
59
63
|
const tx = await this.txBuilder.build({ inputs, outputs, changeAddress });
|
|
60
64
|
logger.debug(`Built unsigned transaction successfully.`);
|
|
61
|
-
return this._buildResult(req, ctx, this._extractTxDetails(tx));
|
|
65
|
+
return this._buildResult(req, ctx, this._extractTxDetails(tx), { forcedInputsUsed: forcedInputs.length });
|
|
62
66
|
}
|
|
63
67
|
catch (err) {
|
|
64
68
|
(0, tx_build_helper_1.mapBuilderError)(err);
|
|
@@ -66,20 +70,22 @@ class BuildooorTxBuilder {
|
|
|
66
70
|
}
|
|
67
71
|
async buildUnsignedTransactionWithMetadata(req, ctx) {
|
|
68
72
|
try {
|
|
69
|
-
const ledgerUtxos = ctx.utxos.map(utxo => this._mapMultiAssetUtxoToLedgerUtxo(utxo));
|
|
70
|
-
const allInputs = ledgerUtxos.map(utxo => ({ utxo }));
|
|
71
73
|
const recipientAddress = cardano_ledger_ts_1.Address.fromString(req.recipientAddress);
|
|
72
74
|
const changeAddress = cardano_ledger_ts_1.Address.fromString(req.changeAddress ?? req.senderAddress);
|
|
73
75
|
const amount = BigInt(String(req.lovelaceAmount));
|
|
74
76
|
const metadata = this._mapOdatanoMetadataToLedgerMetadata(req.metadataJson);
|
|
75
77
|
const outputValue = cardano_ledger_ts_1.Value.lovelaces(amount);
|
|
76
78
|
const outputs = [new cardano_ledger_ts_1.TxOut({ address: recipientAddress, value: outputValue })];
|
|
77
|
-
//
|
|
78
|
-
const
|
|
79
|
-
|
|
79
|
+
// Partition: forced UTxOs become fixed inputs; rest is the coin-selection pool
|
|
80
|
+
const { forced, rest } = this._partitionForcedInputs(ctx.utxos, req.forceInputs);
|
|
81
|
+
const forcedInputs = forced.map(u => ({ utxo: this._mapMultiAssetUtxoToLedgerUtxo(u) }));
|
|
82
|
+
const candidateInputs = rest.map(u => ({ utxo: this._mapMultiAssetUtxoToLedgerUtxo(u) }));
|
|
83
|
+
const selected = this.txBuilder.keepRelevant(outputValue, candidateInputs);
|
|
84
|
+
const inputs = [...forcedInputs, ...selected];
|
|
85
|
+
logger.debug(`Coin selection: ${selected.length}/${candidateInputs.length} UTxOs selected (${forcedInputs.length} forced) for metadata transfer`);
|
|
80
86
|
const tx = await this.txBuilder.build({ inputs, outputs, changeAddress, metadata });
|
|
81
87
|
logger.debug(`Built unsigned transaction successfully.`);
|
|
82
|
-
return this._buildResult(req, ctx, this._extractTxDetails(tx));
|
|
88
|
+
return this._buildResult(req, ctx, this._extractTxDetails(tx), { forcedInputsUsed: forcedInputs.length });
|
|
83
89
|
}
|
|
84
90
|
catch (err) {
|
|
85
91
|
(0, tx_build_helper_1.mapBuilderError)(err);
|
|
@@ -92,7 +98,45 @@ class BuildooorTxBuilder {
|
|
|
92
98
|
// Parse the minting policy script once
|
|
93
99
|
const scriptBytes = Buffer.from(req.mintingPolicyScript, 'hex');
|
|
94
100
|
const script = cardano_ledger_ts_1.Script.fromCbor(scriptBytes);
|
|
95
|
-
//
|
|
101
|
+
// Calculate total mint value for output (only positive quantities - mints, not burns)
|
|
102
|
+
let mintValue = cardano_ledger_ts_1.Value.lovelaces(0n);
|
|
103
|
+
for (const mintAction of req.mintActions) {
|
|
104
|
+
const quantity = BigInt(mintAction.quantity);
|
|
105
|
+
if (quantity > 0n) {
|
|
106
|
+
const { assetName } = (0, tx_build_helper_1.parseAssetUnit)(mintAction.assetUnit);
|
|
107
|
+
mintValue = cardano_ledger_ts_1.Value.add(mintValue, cardano_ledger_ts_1.Value.singleAsset(script.hash, Buffer.from(assetName, 'hex'), quantity));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Partition forced vs candidate UTxOs. Forced inputs are already committed;
|
|
111
|
+
// collateral and coin selection operate on the remainder only.
|
|
112
|
+
const { forced, rest } = this._partitionForcedInputs(ctx.utxos, req.forceInputs);
|
|
113
|
+
const forcedInputs = forced.map(u => ({ utxo: this._mapMultiAssetUtxoToLedgerUtxo(u) }));
|
|
114
|
+
// Collateral + funding separation (from candidates only — forced inputs cannot double as collateral)
|
|
115
|
+
const { collateralUtxos, fundingUtxos } = this._setupCollateral(rest);
|
|
116
|
+
const fundingLedgerUtxos = fundingUtxos.map(utxo => this._mapMultiAssetUtxoToLedgerUtxo(utxo));
|
|
117
|
+
const allFundingInputs = fundingLedgerUtxos.map(utxo => ({ utxo }));
|
|
118
|
+
// Coin selection: only need enough ADA from funding UTxOs (minted tokens come from thin air)
|
|
119
|
+
const requiredFundingValue = cardano_ledger_ts_1.Value.lovelaces(BigInt(req.lovelaceAmount));
|
|
120
|
+
const selectedFunding = this.txBuilder.keepRelevant(requiredFundingValue, allFundingInputs);
|
|
121
|
+
const inputs = [...forcedInputs, ...selectedFunding];
|
|
122
|
+
logger.debug(`Coin selection: ${selectedFunding.length}/${allFundingInputs.length} UTxOs selected (${forcedInputs.length} forced) for mint`);
|
|
123
|
+
// FR-3: resolve __INPUT_IDX__ placeholders in mintRedeemer + inlineDatum after final input order is known.
|
|
124
|
+
const sortedInputs = (0, plutus_placeholders_1.sortInputsLikeBuildooor)([
|
|
125
|
+
...forced.map(u => ({ txHash: u.txHash, outputIndex: u.outputIndex })),
|
|
126
|
+
...this._extractFundingRefs(selectedFunding)
|
|
127
|
+
]);
|
|
128
|
+
const resolveCtx = { sortedInputs };
|
|
129
|
+
const resolvedMintRedeemer = req.mintRedeemer
|
|
130
|
+
? (0, plutus_placeholders_1.resolveIndexPlaceholders)(req.mintRedeemer, resolveCtx)
|
|
131
|
+
: undefined;
|
|
132
|
+
const resolvedInlineDatum = req.inlineDatum
|
|
133
|
+
? (0, plutus_placeholders_1.resolveIndexPlaceholders)(req.inlineDatum, resolveCtx)
|
|
134
|
+
: undefined;
|
|
135
|
+
// Build output — recipient gets the minted assets + min ADA
|
|
136
|
+
let outputValue = cardano_ledger_ts_1.Value.lovelaces(BigInt(req.lovelaceAmount));
|
|
137
|
+
outputValue = cardano_ledger_ts_1.Value.add(outputValue, mintValue);
|
|
138
|
+
const outputs = [this._buildTxOut(recipientAddress, outputValue, resolvedInlineDatum)];
|
|
139
|
+
// Helper to build mints array with specified execution units (uses pre-resolved redeemer)
|
|
96
140
|
const buildMints = (exUnits) => {
|
|
97
141
|
const mints = [];
|
|
98
142
|
for (const mintAction of req.mintActions) {
|
|
@@ -101,8 +145,8 @@ class BuildooorTxBuilder {
|
|
|
101
145
|
value: cardano_ledger_ts_1.Value.singleAsset(script.hash, Buffer.from(assetName, 'hex'), BigInt(mintAction.quantity)),
|
|
102
146
|
script: {
|
|
103
147
|
inline: script,
|
|
104
|
-
redeemer:
|
|
105
|
-
? (0, tx_build_helper_1.jsonToPlutusData)(
|
|
148
|
+
redeemer: resolvedMintRedeemer
|
|
149
|
+
? (0, tx_build_helper_1.jsonToPlutusData)(resolvedMintRedeemer)
|
|
106
150
|
: new plutus_data_1.DataI(mintAction.redeemer ?? 0),
|
|
107
151
|
executionUnits: exUnits
|
|
108
152
|
}
|
|
@@ -110,27 +154,6 @@ class BuildooorTxBuilder {
|
|
|
110
154
|
}
|
|
111
155
|
return mints;
|
|
112
156
|
};
|
|
113
|
-
// Calculate total mint value for output (only positive quantities - mints, not burns)
|
|
114
|
-
let mintValue = cardano_ledger_ts_1.Value.lovelaces(0n);
|
|
115
|
-
for (const mintAction of req.mintActions) {
|
|
116
|
-
const quantity = BigInt(mintAction.quantity);
|
|
117
|
-
if (quantity > 0n) {
|
|
118
|
-
const { assetName } = (0, tx_build_helper_1.parseAssetUnit)(mintAction.assetUnit);
|
|
119
|
-
mintValue = cardano_ledger_ts_1.Value.add(mintValue, cardano_ledger_ts_1.Value.singleAsset(script.hash, Buffer.from(assetName, 'hex'), quantity));
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
// Build output — recipient gets the minted assets + min ADA
|
|
123
|
-
let outputValue = cardano_ledger_ts_1.Value.lovelaces(BigInt(req.lovelaceAmount));
|
|
124
|
-
outputValue = cardano_ledger_ts_1.Value.add(outputValue, mintValue);
|
|
125
|
-
const outputs = [this._buildTxOut(recipientAddress, outputValue, req.inlineDatum)];
|
|
126
|
-
// Collateral + funding separation
|
|
127
|
-
const { collateralUtxos, fundingUtxos } = this._setupCollateral(ctx.utxos);
|
|
128
|
-
const fundingLedgerUtxos = fundingUtxos.map(utxo => this._mapMultiAssetUtxoToLedgerUtxo(utxo));
|
|
129
|
-
const allFundingInputs = fundingLedgerUtxos.map(utxo => ({ utxo }));
|
|
130
|
-
// Coin selection: only need enough ADA from funding UTxOs (minted tokens come from thin air)
|
|
131
|
-
const requiredFundingValue = cardano_ledger_ts_1.Value.lovelaces(BigInt(req.lovelaceAmount));
|
|
132
|
-
const inputs = this.txBuilder.keepRelevant(requiredFundingValue, allFundingInputs);
|
|
133
|
-
logger.debug(`Coin selection: ${inputs.length}/${allFundingInputs.length} UTxOs selected for mint`);
|
|
134
157
|
// Evaluate execution units
|
|
135
158
|
const finalExUnits = await this._evaluateExUnits(async () => {
|
|
136
159
|
const evalTx = await this.txBuilder.build({
|
|
@@ -146,7 +169,7 @@ class BuildooorTxBuilder {
|
|
|
146
169
|
collaterals: collateralUtxos, requiredSigners: req.requiredSigners
|
|
147
170
|
});
|
|
148
171
|
logger.debug(`Built unsigned minting transaction successfully with fee: ${tx.body.fee.toString()}`);
|
|
149
|
-
return this._buildResult(req, ctx, this._extractTxDetails(tx), { scriptHash: script.hash.toString() });
|
|
172
|
+
return this._buildResult(req, ctx, this._extractTxDetails(tx), { scriptHash: script.hash.toString(), forcedInputsUsed: forcedInputs.length });
|
|
150
173
|
}
|
|
151
174
|
catch (err) {
|
|
152
175
|
(0, tx_build_helper_1.mapBuilderError)(err);
|
|
@@ -158,17 +181,11 @@ class BuildooorTxBuilder {
|
|
|
158
181
|
// Parse the validator script
|
|
159
182
|
const scriptBytes = Buffer.from(plutusScriptExecution.validatorScript, 'hex');
|
|
160
183
|
const script = cardano_ledger_ts_1.Script.fromCbor(scriptBytes);
|
|
161
|
-
// Convert redeemer JSON to PlutusData
|
|
162
|
-
const redeemerData = (0, tx_build_helper_1.jsonToPlutusData)(plutusScriptExecution.redeemer);
|
|
163
|
-
// Determine datum: "inline" if no datum provided (assumes inline datum on UTxO), otherwise convert
|
|
164
184
|
if (!plutusScriptExecution.datum) {
|
|
165
185
|
// When no explicit datum is provided, the UTxO must have an inline datum.
|
|
166
186
|
// If neither exists, the Plutus validator will fail with a cryptic error.
|
|
167
187
|
logger.debug('No datumJson provided — expecting inline datum on script UTxO');
|
|
168
188
|
}
|
|
169
|
-
const datum = plutusScriptExecution.datum
|
|
170
|
-
? (0, tx_build_helper_1.jsonToPlutusData)(plutusScriptExecution.datum)
|
|
171
|
-
: "inline";
|
|
172
189
|
// Find the specific script UTxO in the provided context UTxOs
|
|
173
190
|
const scriptUtxoRef = plutusScriptExecution.scriptUtxo;
|
|
174
191
|
const scriptOdatanoUtxo = ctx.utxos.find(u => u.txHash === scriptUtxoRef.txHash && u.outputIndex === scriptUtxoRef.outputIndex);
|
|
@@ -180,33 +197,98 @@ class BuildooorTxBuilder {
|
|
|
180
197
|
const senderUtxos = ctx.utxos.filter(u => !(u.txHash === scriptUtxoRef.txHash && u.outputIndex === scriptUtxoRef.outputIndex));
|
|
181
198
|
const recipientAddress = cardano_ledger_ts_1.Address.fromString(req.recipientAddress);
|
|
182
199
|
const changeAddress = cardano_ledger_ts_1.Address.fromString(req.changeAddress ?? req.senderAddress);
|
|
183
|
-
//
|
|
200
|
+
// Partition forced inputs (excluding any ref that matches the script UTxO — silently ignored)
|
|
201
|
+
const forceInputsFiltered = (req.forceInputs ?? []).filter(r => !(r.txHash === scriptUtxoRef.txHash && r.outputIndex === scriptUtxoRef.outputIndex));
|
|
202
|
+
const { forced, rest } = this._partitionForcedInputs(senderUtxos, forceInputsFiltered);
|
|
203
|
+
const forcedInputs = forced.map(u => ({ utxo: this._mapMultiAssetUtxoToLedgerUtxo(u) }));
|
|
204
|
+
// Collateral + funding separation (from candidates only — forced inputs cannot double as collateral)
|
|
205
|
+
const { collateralUtxos, fundingUtxos } = this._setupCollateral(rest);
|
|
206
|
+
const fundingLedgerUtxos = fundingUtxos.map(utxo => this._mapMultiAssetUtxoToLedgerUtxo(utxo));
|
|
207
|
+
// Coin selection: funding UTxOs only need to cover fee + min change (script UTxO covers the output).
|
|
208
|
+
// Extra outputs (FR-2) add their lovelace + assets to the requirement; assets that the script UTxO
|
|
209
|
+
// already provides will net out, so over-requesting is harmless — keepRelevant prefers smaller sets.
|
|
210
|
+
const allFundingInputs = fundingLedgerUtxos.map(utxo => ({ utxo }));
|
|
211
|
+
let requiredFundingValue = cardano_ledger_ts_1.Value.lovelaces(BigInt(req.lovelaceAmount || const_1.MIN_CHANGE_LOVELACE));
|
|
212
|
+
if (req.extraOutputs && req.extraOutputs.length > 0) {
|
|
213
|
+
for (const extra of req.extraOutputs) {
|
|
214
|
+
requiredFundingValue = cardano_ledger_ts_1.Value.add(requiredFundingValue, this._buildLedgerValue(BigInt(extra.lovelaceAmount), extra.assets));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const selectedFundingInputs = this.txBuilder.keepRelevant(requiredFundingValue, allFundingInputs);
|
|
218
|
+
logger.debug(`Coin selection: ${selectedFundingInputs.length}/${allFundingInputs.length} UTxOs selected (${forcedInputs.length} forced) for Plutus spend`);
|
|
219
|
+
// FR-3: compute the final input order (replicates Buildooor's lex sort) and resolve any
|
|
220
|
+
// __INPUT_IDX__ placeholders in redeemer / datum / output datums BEFORE PlutusData encoding.
|
|
221
|
+
const sortedInputs = this._computeSortedInputs(scriptUtxoRef, forced, this._extractFundingRefs(selectedFundingInputs));
|
|
222
|
+
const resolveCtx = { sortedInputs };
|
|
223
|
+
const resolvedRedeemer = (0, plutus_placeholders_1.resolveIndexPlaceholders)(plutusScriptExecution.redeemer, resolveCtx);
|
|
224
|
+
const redeemerData = (0, tx_build_helper_1.jsonToPlutusData)(resolvedRedeemer);
|
|
225
|
+
const datum = plutusScriptExecution.datum
|
|
226
|
+
? (0, tx_build_helper_1.jsonToPlutusData)((0, plutus_placeholders_1.resolveIndexPlaceholders)(plutusScriptExecution.datum, resolveCtx))
|
|
227
|
+
: "inline";
|
|
228
|
+
const resolvedPrimaryInlineDatum = req.inlineDatum
|
|
229
|
+
? (0, plutus_placeholders_1.resolveIndexPlaceholders)(req.inlineDatum, resolveCtx)
|
|
230
|
+
: undefined;
|
|
231
|
+
const resolvedExtraOutputs = this._resolveExtraOutputPlaceholders(req.extraOutputs, resolveCtx);
|
|
232
|
+
const resolvedMintRedeemer = req.mintRedeemer
|
|
233
|
+
? (0, plutus_placeholders_1.resolveIndexPlaceholders)(req.mintRedeemer, resolveCtx)
|
|
234
|
+
: undefined;
|
|
235
|
+
// FR-1: combined spend+mint flow. When mintActions are present, build mints alongside the spend input.
|
|
236
|
+
const hasMint = !!(req.mintActions && req.mintActions.length > 0 && req.mintingPolicyScript);
|
|
237
|
+
let mintScript;
|
|
238
|
+
let mintScriptHash;
|
|
239
|
+
if (hasMint) {
|
|
240
|
+
mintScript = cardano_ledger_ts_1.Script.fromCbor(Buffer.from(req.mintingPolicyScript, 'hex'));
|
|
241
|
+
mintScriptHash = mintScript.hash.toString();
|
|
242
|
+
}
|
|
243
|
+
// Build outputs — include multi-assets from script UTxO in continuing output
|
|
184
244
|
const scriptNonAdaAssets = scriptOdatanoUtxo.amount.filter(a => a.unit.toLowerCase() !== 'lovelace' && BigInt(a.quantity) > 0n);
|
|
185
245
|
let outputValue = cardano_ledger_ts_1.Value.lovelaces(BigInt(req.lovelaceAmount || const_1.MIN_CHANGE_LOVELACE));
|
|
186
246
|
if (scriptNonAdaAssets.length > 0) {
|
|
187
247
|
outputValue = this._buildLedgerValue(BigInt(req.lovelaceAmount || const_1.MIN_CHANGE_LOVELACE), scriptOdatanoUtxo.amount);
|
|
188
248
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
249
|
+
// If mints are present and no extraOutputs handle the minted assets, attach positive mints to the primary output.
|
|
250
|
+
if (hasMint && (!resolvedExtraOutputs || resolvedExtraOutputs.length === 0)) {
|
|
251
|
+
for (const action of req.mintActions) {
|
|
252
|
+
const qty = BigInt(action.quantity);
|
|
253
|
+
if (qty > 0n) {
|
|
254
|
+
const { assetName } = (0, tx_build_helper_1.parseAssetUnit)(action.assetUnit);
|
|
255
|
+
outputValue = cardano_ledger_ts_1.Value.add(outputValue, cardano_ledger_ts_1.Value.singleAsset(mintScript.hash, Buffer.from(assetName, 'hex'), qty));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const outputs = [this._buildTxOut(recipientAddress, outputValue, resolvedPrimaryInlineDatum)];
|
|
260
|
+
// Append extra outputs (FR-2). Each is independently min-ADA checked so consumers
|
|
261
|
+
// get a clear, field-attributed error before Buildooor's coin selection runs.
|
|
262
|
+
this._appendExtraOutputs(outputs, resolvedExtraOutputs);
|
|
263
|
+
// Helper to build mints array (FR-1) with specified execution units
|
|
264
|
+
const buildMints = hasMint
|
|
265
|
+
? (exUnits) => req.mintActions.map(action => {
|
|
266
|
+
const { assetName } = (0, tx_build_helper_1.parseAssetUnit)(action.assetUnit);
|
|
267
|
+
return {
|
|
268
|
+
value: cardano_ledger_ts_1.Value.singleAsset(mintScript.hash, Buffer.from(assetName, 'hex'), BigInt(action.quantity)),
|
|
269
|
+
script: {
|
|
270
|
+
inline: mintScript,
|
|
271
|
+
redeemer: resolvedMintRedeemer
|
|
272
|
+
? (0, tx_build_helper_1.jsonToPlutusData)(resolvedMintRedeemer)
|
|
273
|
+
: new plutus_data_1.DataI(action.redeemer ?? 0),
|
|
274
|
+
executionUnits: exUnits
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
})
|
|
278
|
+
: undefined;
|
|
198
279
|
// Helper to build inputs with specified execution units
|
|
199
280
|
const buildInputs = (exUnits) => {
|
|
200
281
|
const scriptInput = {
|
|
201
282
|
utxo: scriptLedgerUtxo,
|
|
202
283
|
inputScript: { script, datum, redeemer: redeemerData, executionUnits: exUnits }
|
|
203
284
|
};
|
|
204
|
-
return [scriptInput, ...selectedFundingInputs];
|
|
285
|
+
return [scriptInput, ...forcedInputs, ...selectedFundingInputs];
|
|
205
286
|
};
|
|
206
287
|
// Evaluate execution units
|
|
207
288
|
const finalExUnits = await this._evaluateExUnits(async () => {
|
|
208
289
|
const evalTx = await this.txBuilder.build({
|
|
209
290
|
inputs: buildInputs(const_1.HIGH_EXECUTION_UNITS), outputs, changeAddress,
|
|
291
|
+
mints: buildMints?.(const_1.HIGH_EXECUTION_UNITS),
|
|
210
292
|
collaterals: collateralUtxos, requiredSigners: req.requiredSigners
|
|
211
293
|
});
|
|
212
294
|
if (!evalTx) {
|
|
@@ -216,12 +298,18 @@ class BuildooorTxBuilder {
|
|
|
216
298
|
}, ctx.evaluateTransaction);
|
|
217
299
|
// Build with final execution units + witness buffer
|
|
218
300
|
const inputs = buildInputs(finalExUnits);
|
|
301
|
+
const mints = buildMints?.(finalExUnits);
|
|
219
302
|
const tx = await this._buildWithWitnessBuffer({
|
|
220
303
|
inputs, outputs, changeAddress,
|
|
304
|
+
mints,
|
|
221
305
|
collaterals: collateralUtxos, requiredSigners: req.requiredSigners
|
|
222
306
|
});
|
|
223
307
|
logger.debug(`Built unsigned Plutus spending transaction successfully with fee: ${tx.body.fee.toString()}`);
|
|
224
|
-
return this._buildResult(req, ctx, this._extractTxDetails(tx), {
|
|
308
|
+
return this._buildResult(req, ctx, this._extractTxDetails(tx), {
|
|
309
|
+
scriptHash: script.hash.toString(),
|
|
310
|
+
mintScriptHash,
|
|
311
|
+
forcedInputsUsed: forcedInputs.length
|
|
312
|
+
});
|
|
225
313
|
}
|
|
226
314
|
catch (err) {
|
|
227
315
|
(0, tx_build_helper_1.mapBuilderError)(err);
|
|
@@ -310,6 +398,25 @@ class BuildooorTxBuilder {
|
|
|
310
398
|
logger.debug(`First pass fee: ${calculatedFee}, rebuilding with witness buffer: ${adjustedMinFee}`);
|
|
311
399
|
return this.txBuilder.build({ ...buildParams, fee: adjustedMinFee });
|
|
312
400
|
}
|
|
401
|
+
/**
|
|
402
|
+
* Split UTxOs into forced inputs (must be consumed) and remaining candidates (free for
|
|
403
|
+
* coin selection / collateral). Refs in forceInputs that are not present in utxos are
|
|
404
|
+
* silently ignored — the resolver upstream has already validated existence.
|
|
405
|
+
*/
|
|
406
|
+
_partitionForcedInputs(utxos, forceInputs) {
|
|
407
|
+
if (!forceInputs || forceInputs.length === 0)
|
|
408
|
+
return { forced: [], rest: utxos };
|
|
409
|
+
const forcedKeys = new Set(forceInputs.map(r => `${r.txHash}#${r.outputIndex}`));
|
|
410
|
+
const forced = [];
|
|
411
|
+
const rest = [];
|
|
412
|
+
for (const u of utxos) {
|
|
413
|
+
if (forcedKeys.has(`${u.txHash}#${u.outputIndex}`))
|
|
414
|
+
forced.push(u);
|
|
415
|
+
else
|
|
416
|
+
rest.push(u);
|
|
417
|
+
}
|
|
418
|
+
return { forced, rest };
|
|
419
|
+
}
|
|
313
420
|
/**
|
|
314
421
|
* Find an ADA-only UTxO for collateral, map it, and return remaining funding UTxOs.
|
|
315
422
|
* Throws if no ADA-only UTxO is available.
|
|
@@ -346,6 +453,60 @@ class BuildooorTxBuilder {
|
|
|
346
453
|
}
|
|
347
454
|
return new cardano_ledger_ts_1.TxOut(params);
|
|
348
455
|
}
|
|
456
|
+
/**
|
|
457
|
+
* Compute the post-sort input order so __INPUT_IDX__ placeholders (FR-3) resolve to the
|
|
458
|
+
* same indices Buildooor will assign during build(). Order: script-input first by convention,
|
|
459
|
+
* then forced + funding sorted lexicographically on (txHash, outputIndex).
|
|
460
|
+
* Buildooor sorts ALL inputs together; we mirror that exact behaviour via sortInputsLikeBuildooor.
|
|
461
|
+
*/
|
|
462
|
+
_computeSortedInputs(scriptUtxoRef, forcedUtxos, fundingRefs) {
|
|
463
|
+
const all = [
|
|
464
|
+
{ txHash: scriptUtxoRef.txHash, outputIndex: scriptUtxoRef.outputIndex },
|
|
465
|
+
...forcedUtxos.map(u => ({ txHash: u.txHash, outputIndex: u.outputIndex })),
|
|
466
|
+
...fundingRefs
|
|
467
|
+
];
|
|
468
|
+
return (0, plutus_placeholders_1.sortInputsLikeBuildooor)(all);
|
|
469
|
+
}
|
|
470
|
+
/** Extract InputRef list from the Buildooor-shaped funding inputs returned by keepRelevant. */
|
|
471
|
+
_extractFundingRefs(fundingInputs) {
|
|
472
|
+
return fundingInputs.map(i => ({
|
|
473
|
+
txHash: i.utxo.utxoRef.id.toString(),
|
|
474
|
+
outputIndex: i.utxo.utxoRef.index
|
|
475
|
+
}));
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Resolve __INPUT_IDX__ placeholders (FR-3) inside each extraOutput.inlineDatum.
|
|
479
|
+
* Returns a new array with resolved datums; non-datum fields pass through unchanged.
|
|
480
|
+
*/
|
|
481
|
+
_resolveExtraOutputPlaceholders(extraOutputs, resolveCtx) {
|
|
482
|
+
if (!extraOutputs || extraOutputs.length === 0)
|
|
483
|
+
return extraOutputs;
|
|
484
|
+
return extraOutputs.map(extra => ({
|
|
485
|
+
...extra,
|
|
486
|
+
inlineDatum: extra.inlineDatum
|
|
487
|
+
? (0, plutus_placeholders_1.resolveIndexPlaceholders)(extra.inlineDatum, resolveCtx)
|
|
488
|
+
: undefined
|
|
489
|
+
}));
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Append parsed extraOutputs (FR-2) to the outputs array, enforcing min-ADA per entry.
|
|
493
|
+
* Throws TransactionValidationError with the required min-ADA in the message so consumers
|
|
494
|
+
* can adjust without having to inspect Buildooor internals.
|
|
495
|
+
*/
|
|
496
|
+
_appendExtraOutputs(outputs, extraOutputs) {
|
|
497
|
+
if (!extraOutputs || extraOutputs.length === 0)
|
|
498
|
+
return;
|
|
499
|
+
for (let i = 0; i < extraOutputs.length; i++) {
|
|
500
|
+
const extra = extraOutputs[i];
|
|
501
|
+
const value = this._buildLedgerValue(BigInt(extra.lovelaceAmount), extra.assets);
|
|
502
|
+
const txOut = this._buildTxOut(cardano_ledger_ts_1.Address.fromString(extra.address), value, extra.inlineDatum);
|
|
503
|
+
const minLovelaces = this.txBuilder.getMinimumOutputLovelaces(txOut);
|
|
504
|
+
if (BigInt(extra.lovelaceAmount) < minLovelaces) {
|
|
505
|
+
throw new errors_1.TransactionValidationError(`extraOutputs[${i}].lovelaceAmount (${extra.lovelaceAmount}) is below required min-ADA ${minLovelaces.toString()} for the given address/assets/datum`);
|
|
506
|
+
}
|
|
507
|
+
outputs.push(txOut);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
349
510
|
//---------------------------------------------------------------------------
|
|
350
511
|
// UTxO Mapping & Configuration Helpers
|
|
351
512
|
//---------------------------------------------------------------------------
|