Tx.collectFrom([specificUtxo]) In Conjunction With .pay.toAddress() With Multi Asset Doesn't Correctly Subtract Assets From Wallet UTxOs
tx.collectFrom([specificUtxo]) in Conjunction with .pay.toAddress() with Multi Asset Doesn't Correctly Subtract Assets from Wallet UTxOs
The Lucid Evolution project is a great initiative that aims to simplify the process of building and managing transactions on the Cardano network. However, like any complex system, it's not immune to bugs and issues. In this article, we'll delve into a specific problem that arises when using the tx.collectFrom([specificUtxo])
method in conjunction with the .pay.toAddress()
method, particularly when dealing with multi-asset transactions.
The issue at hand is that when collecting a specific UTXO using tx.collectFrom([specificUtxo])
and then paying to an address using .pay.toAddress()
, the native assets are not correctly subtracted from the wallet's UTXOs. This results in an incorrect balance of native assets, leading to a ConwayUtxowFailure
error.
To better understand the issue, let's set the stage. We're building an application that allows a payer to pay a fixed amount in a native asset without having to spend a minimum UTXO lovelace amount. This is achieved by preparing vendor UTXOs with a lovelace amount and using multi-sig to spend the prepared UTXO alongside the payer's UTXOs that contain the native asset to pay with.
When building a transaction that collects the prepared UTXO from the vendor's address and then tries to pay with the payer's UTXOs, the current transaction building logic does not correctly balance the native assets. This results in the payment amount not being subtracted from the payer's output, leading to an incorrect balance of native assets.
To reproduce the issue, let's assume we have a prepared UTXO from the vendor like this:
{
address: "addr_test1q...vendor_address"
lovelace: 2_500_000n,
"{policyId+nameHex}": 100n
}
And a UTXO from the payer like this:
{
address: "addr_test1q...payer_address"
lovelace: 50_000_000n,
"{policyId+nameHex}": 100n
}
We initialize Lucid like this:
const lucid = await Lucid(blockfrost, "Preprod");
lucid.selectWallet.fromAddress(payerAddress, []);
We obtain the prepared UTXO and calculate the assets that should be paid to it:
const payoutAssets: Assets = preparedUtxo.assets;
if (payoutAssets[assetUnit]) {
payoutAssets[assetUnit] += BigInt(assetQuantity);
} else {
payoutAssets[assetUnit] = BigInt(assetQuantity);
}
We then create this transaction:
const unsignedTx = await tx
.collectFrom([preparedUtxo])
.pay
.ToAddress(
preparedUtxo.address,
payoutAssets,
).complete();
If we deserialize the CBOR from this transaction and inspect the outputs, the result will be two outputs:
{
address: "addr_test1q...vendor_address"
lovelace: 2_500_000n,
"{policyId+nameHex}": 200n
},
{
address: "addr_test1q...payer_address"
lovelace: 49_879_028n, // paid the tx fee
"{policyId+nameHex}": 100n
}
If we deserialize the CBOR from this transaction and inspect the outputs, the result should be two outputs:
{
address: "addr_test1q...vendor_address"
lovelace: 2_500_000n,
"{policyId+nameHex}": 200n
},
{
address: "addr_test1q...payer_address"
lovelace: 49_879_028n // paid the tx fee
}
The error details are as follows:
Error: {"contents":{"contents":{"contents":{"era":"ShelleyBasedEraConway","error":["ConwayUtxowFailure (UtxoFailure (ValueNotConservedUTxO (MaryValue (Coin 8118479572) (MultiAsset (fromList [(PolicyID {policyID = ScriptHash)].......
The Cardano network being used is Preprod.
No additional context is provided.
The following validations have been performed:
- Read the documentation
- Check that there isn't already an issue that reports the same bug to avoid creating a duplicate
- The provided reproduction is a minimal reproducible example of the bug
Q&A: tx.collectFrom([specificUtxo]) in Conjunction with .pay.toAddress() with Multi Asset Doesn't Correctly Subtract Assets from Wallet UTxOs
A: The issue is that when collecting a specific UTXO using tx.collectFrom([specificUtxo])
and then paying to an address using .pay.toAddress()
, the native assets are not correctly subtracted from the wallet's UTXOs. This results in an incorrect balance of native assets, leading to a ConwayUtxowFailure
error.
A: The expected behavior is that the native assets should be correctly subtracted from the wallet's UTXOs, resulting in a balanced transaction.
A: To reproduce the issue, follow these steps:
- Initialize Lucid like this:
const lucid = await Lucid(blockfrost, "Preprod");
- Select the wallet from the payer's address:
lucid.selectWallet.fromAddress(payerAddress, []);
- Obtain the prepared UTXO and calculate the assets that should be paid to it:
const payoutAssets: Assets = preparedUtxo.assets;
- Create the transaction using
tx.collectFrom([preparedUtxo])
and.pay.toAddress()
methods.
A: The error message is a ConwayUtxowFailure
error with the following details:
Error: {"contents":{"contents":{"contents":{"era":"ShelleyBasedEraConway","error":["ConwayUtxowFailure (UtxoFailure (ValueNotConservedUTxO (MaryValue (Coin 8118479572) (MultiAsset (fromList [(PolicyID {policyID = ScriptHash)].......
A: The Cardano network being used is Preprod.
A: Yes, the following validations have been performed:
- Read the documentation
- Check that there isn't already an issue that reports the same bug to avoid creating a duplicate
- The provided reproduction is a minimal reproducible example of the bug
A: The next step is to investigate the transaction building logic and identify the cause of the incorrect balance of native assets. This may involve reviewing the code, testing different scenarios, and consulting with experts in the field.