Skip to content

Commit dd5cd7e

Browse files
authored
fix: filter utxos in etna import tx builder (#930)
* fix: filter utxos in etna import tx builder * fix: correct unsigned tx utxos on etna import tx builder * fix: use utxos that were imported
1 parent 2856c2f commit dd5cd7e

File tree

2 files changed

+59
-47
lines changed

2 files changed

+59
-47
lines changed

src/vms/pvm/etna-builder/builder.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,13 @@ describe('./src/vms/pvm/etna-builder/builder.test.ts', () => {
308308
);
309309

310310
expectTxs(unsignedTx.getTx(), expectedTx);
311+
312+
// Ensure that the unsigned tx utxos are the filtered utxos,
313+
// and not the inputUtxos registered in the spend helper.
314+
// This is only relevant for the ImportTx.
315+
expect(unsignedTx.utxos).toHaveLength(1);
316+
expect(unsignedTx.utxos).not.toContain(utxos[0]);
317+
expect(unsignedTx.utxos).not.toContain(utxos[1]);
311318
});
312319

313320
test('newExportTx', () => {

src/vms/pvm/etna-builder/builder.ts

Lines changed: 52 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -302,50 +302,52 @@ export const newImportTx: TxBuilderFn<NewImportTxProps> = (
302302
) => {
303303
const fromAddresses = addressesFromBytes(fromAddressesBytes);
304304

305-
const { importedInputs, importedAmounts } = utxos
306-
.filter(
307-
(utxo): utxo is Utxo<TransferOutput> =>
308-
isTransferOut(utxo.output) &&
309-
// Currently - only AVAX is allowed to be imported to the P-Chain
310-
utxo.assetId.toString() === context.avaxAssetID,
311-
)
312-
.reduce<{
313-
importedInputs: TransferableInput[];
314-
importedAmounts: Record<string, bigint>;
315-
}>(
316-
(acc, utxo) => {
317-
const { sigIndicies: inputSigIndices } =
318-
matchOwners(utxo.getOutputOwners(), fromAddresses, minIssuanceTime) ||
319-
{};
320-
321-
if (inputSigIndices === undefined) {
322-
// We couldn't spend this UTXO, so we skip to the next one.
323-
return acc;
324-
}
325-
326-
const assetId = utxo.getAssetId();
327-
328-
return {
329-
importedInputs: [
330-
...acc.importedInputs,
331-
new TransferableInput(
332-
utxo.utxoId,
333-
utxo.assetId,
334-
new TransferInput(
335-
utxo.output.amt,
336-
new Input(inputSigIndices.map((value) => new Int(value))),
337-
),
305+
const filteredUtxos = utxos.filter(
306+
(utxo): utxo is Utxo<TransferOutput> =>
307+
isTransferOut(utxo.output) &&
308+
// Currently - only AVAX is allowed to be imported to the P-Chain
309+
utxo.assetId.toString() === context.avaxAssetID,
310+
);
311+
312+
const { importedInputs, importedAmounts, inputUtxos } = filteredUtxos.reduce<{
313+
importedInputs: TransferableInput[];
314+
importedAmounts: Record<string, bigint>;
315+
inputUtxos: Utxo[];
316+
}>(
317+
(acc, utxo) => {
318+
const { sigIndicies: inputSigIndices } =
319+
matchOwners(utxo.getOutputOwners(), fromAddresses, minIssuanceTime) ||
320+
{};
321+
322+
if (inputSigIndices === undefined) {
323+
// We couldn't spend this UTXO, so we skip to the next one.
324+
return acc;
325+
}
326+
327+
const assetId = utxo.getAssetId();
328+
329+
return {
330+
importedInputs: [
331+
...acc.importedInputs,
332+
new TransferableInput(
333+
utxo.utxoId,
334+
utxo.assetId,
335+
new TransferInput(
336+
utxo.output.amt,
337+
new Input(inputSigIndices.map((value) => new Int(value))),
338338
),
339-
],
340-
importedAmounts: {
341-
...acc.importedAmounts,
342-
[assetId]:
343-
(acc.importedAmounts[assetId] ?? 0n) + utxo.output.amount(),
344-
},
345-
};
346-
},
347-
{ importedInputs: [], importedAmounts: {} },
348-
);
339+
),
340+
],
341+
importedAmounts: {
342+
...acc.importedAmounts,
343+
[assetId]:
344+
(acc.importedAmounts[assetId] ?? 0n) + utxo.output.amount(),
345+
},
346+
inputUtxos: [...acc.inputUtxos, utxo],
347+
};
348+
},
349+
{ importedInputs: [], importedAmounts: {}, inputUtxos: [] },
350+
);
349351

350352
if (importedInputs.length === 0) {
351353
throw new Error('no UTXOs available to import');
@@ -355,7 +357,7 @@ export const newImportTx: TxBuilderFn<NewImportTxProps> = (
355357

356358
const addressMaps = AddressMaps.fromTransferableInputs(
357359
importedInputs,
358-
utxos,
360+
filteredUtxos,
359361
minIssuanceTime,
360362
fromAddressesBytes,
361363
);
@@ -391,13 +393,16 @@ export const newImportTx: TxBuilderFn<NewImportTxProps> = (
391393
fromAddresses,
392394
initialComplexity: complexity,
393395
minIssuanceTime,
394-
utxos,
396+
utxos: filteredUtxos,
395397
},
396398
[useUnlockedUTXOs],
397399
context,
398400
);
399401

400-
const { changeOutputs, inputs, inputUTXOs } = spendResults;
402+
// Note: We don't use the `inputUTXOs` from `spendResults`
403+
// for the `UnsignedTx` because we want to use the original
404+
// UTXOs that were imported.
405+
const { changeOutputs, inputs } = spendResults;
401406

402407
return new UnsignedTx(
403408
new ImportTx(
@@ -411,7 +416,7 @@ export const newImportTx: TxBuilderFn<NewImportTxProps> = (
411416
Id.fromString(sourceChainId),
412417
importedInputs.sort(TransferableInput.compare),
413418
),
414-
inputUTXOs,
419+
inputUtxos,
415420
addressMaps,
416421
);
417422
};

0 commit comments

Comments
 (0)