Emurgo / cardano-serialization-lib

This is a library, written in Rust, for serialization & deserialization of data structures used in Cardano's Haskell implementation of Alonzo along with useful utility functions.
Other
231 stars 125 forks source link

UTxO Balance Insufficient from add_inputs_from LargestFirstMultiAsset #600

Closed TanyaMio closed 1 week ago

TanyaMio commented 1 year ago

I am trying to build a transaction that will send a token to a contract address. The transaction has 1 explicit output:

txBuilder.add_output(
    CardanoWasm.TransactionOutputBuilder
      .new()
      .with_address(contractAddress)
      .with_data_hash(CardanoWasm.hash_plutus_data(CardanoWasm.PlutusData.new_constr_plutus_data(datum)))
      .next()
      .with_asset_and_min_required_coin_by_utxo_cost(
        multiasset,
        CardanoWasm.DataCost.new_coins_per_byte(
          CardanoWasm.BigNum.from_str(protocolParameters.coins_per_utxo_size)
        )
      )
      .build()
  );

I am requesting wallet UTxOs from the wallet API. Here is the response:

[
    "82825820a37e3349c08374c5f96f46e26d72841acb6089e270d61c15d90e1aaaeaa3da6c0082583900c886f62c56c6671f6a5c37f43927b674a6e6258b2075ed59385b84cf5a66cb93faa190ea818790d69cd144f273fa64646c26f107ad157828821a001484d0a1581c39788d5355bce70fd1364021f395060f484e077129286308455ee34ea1446c65616601",
    "82825820a37e3349c08374c5f96f46e26d72841acb6089e270d61c15d90e1aaaeaa3da6c0182583900c886f62c56c6671f6a5c37f43927b674a6e6258b2075ed59385b84cf5a66cb93faa190ea818790d69cd144f273fa64646c26f107ad1578281a1db60187"
]

And the decoded values:

[
  [h'A37E3349C08374C5F96F46E26D72841ACB6089E270D61C15D90E1AAAEAA3DA6C', 0], 
  [
    h'00C886F62C56C6671F6A5C37F43927B674A6E6258B2075ED59385B84CF5A66CB93FAA190EA818790D69CD144F273FA64646C26F107AD157828',
    [1344720, {h'39788D5355BCE70FD1364021F395060F484E077129286308455EE34E': {h'6C656166': 1}}]
  ]
]

[
  [h'A37E3349C08374C5F96F46E26D72841ACB6089E270D61C15D90E1AAAEAA3DA6C', 1], 
  [
    h'00C886F62C56C6671F6A5C37F43927B674A6E6258B2075ED59385B84CF5A66CB93FAA190EA818790D69CD144F273FA64646C26F107AD157828', 
    498467207
  ]
]

From there I am creating a TransactionUnspentOutputs object:

let inputs = CardanoWasm.TransactionUnspentOutputs.new();
utxos.forEach(function(element) {
    let utxo = CardanoWasm.TransactionUnspentOutput.from_hex(element);
    inputs.add(utxo);
});

And if I check the result by comparing utxos.get(i) and inputs.get(i).to_hex() - the strings are identical. Which leads me to believe there is no error during type conversion.

Here is the screenshot of txBuilder.get_explicit_output().to_js_value(), utxos and inputs right before calling txBuilder.add_inputs_from(inputs, CardanoWasm.CoinSelectionStrategyCIP2.LargestFirstMultiAsset);

image

It should be working fine, since I have more than enough ADA in my wallet, and the token is there as well. But I am still getting the "UTxO Balance Insufficient" error. Is there any way to fix this?

lisicky commented 1 year ago

Hi @TanyaMio ! Could you provide full code to reproduce your error ?

TanyaMio commented 1 year ago

@lisicky I don't think I am able to share full code, since it's a company project. Is there a more specific place where the issue might be coming from? If there is, I will be able to share other snippets.

kommander commented 1 year ago

@TanyaMio I had exactly the same issue, trying to mint a token and send it in the same transaction using the TransactionOutputBuilder. I guess it doesn't recognize the minted asset when using the MintBuilder, so it does not exist in the UTxO and therefor the "UTxO Balance Insufficient".

What works is this:

const txoBuilder = CardanoWasm.TransactionOutputBuilder.new();
const dataCost = CardanoWasm.DataCost.new_coins_per_byte(CardanoWasm.BigNum.from_str(protocolParameters.coins_per_utxo_size || '4310'));
const multiAsset = CardanoWasm.MultiAsset.from_json('{"' + policyId + '":{"' + cardanoAssetName.to_hex() + '":"' + mintAsset.quantity + '"}}');
const txo = txoBuilder.with_address(outputAddr).next().with_asset_and_min_required_coin_by_utxo_cost(multiAsset, dataCost);
txBuilder.add_mint_asset_and_output_min_required_coin(allScript, cardanoAssetName, CardanoWasm.Int.from_str(mintAsset.quantity), txo);

When cost are set in the txBuilder config, this also works:

const txoBuilder = CardanoWasm.TransactionOutputBuilder.new();
const txo = txoBuilder.with_address(outputAddr).next();
txBuilder.add_mint_asset_and_output_min_required_coin(allScript, cardanoAssetName, CardanoWasm.Int.from_str(mintAsset.quantity), txo);

Why it does not work with the MintBuilder is a mystery for me atm 🤷

lisicky commented 1 year ago

@TanyaMio without full example we can only try to guess. Call get_total_input and get_total_output from TransactionBuilder before calling add_inputs_from and provide cbor of both values that you get. Also you can try to create a fake input with huge amount of ada to exclude situation when you have not enough ada.

lisicky commented 1 year ago

@kommander under the hood add_mint_asset_and_output_min_required_coin uses the MintBuilder. Could you provide cbor of get_total_input and get_total_output result ? If you can provide an isolated code example to reproduce the problem it would be better.

kommander commented 1 year ago

@lisicky thanks for looking into this. Here's the repro: https://github.com/kommander/csl-utxo-insufficient-balance-repro

lisicky commented 1 year ago

@kommander please check get_total_input and get_total_output values. Anyway, building manually by from_json can be tricky. And I recommend using it only for debugging because if cardano protocol is changed, it would be more difficult to find mistakes.

Example for you case, how you can build MultiAsset without from_json :

const multiAsset = CardanoWasm.MultiAsset.new();
const asset = CardanoWasm.Assets.new();
asset.insert(cardanoAssetName, BigNum.from_str(String(mintAsset.quantity)));
multiAsset.insert(allScript.hash(), asset);
kommander commented 1 year ago

@lisicky Creating the MultiAsset manually using the CSL API works, no error. So it seems to be an issue with the MultiAsset.from_json like you assumed ✅

As the actual error happens when calling txBuilder.add_inputs_from, I can only get the total inputs/outputs directly before that, which are the same when using from_json and when creating the MultiAsset manually:

totalInputs {
  "coin": "0",
  "multiasset": {
    "b2a3ddf612558ffbf387fdbd2bc71a88cb20049399bee4ae30929c05": {
      "546573744173736574": "1000000"
    }
  }
}
totalOutputs {
  "coin": "1176630",
  "multiasset": {
    "b2a3ddf612558ffbf387fdbd2bc71a88cb20049399bee4ae30929c05": {
      "546573744173736574": "1000000"
    }
  }
}
lisicky commented 1 week ago

Close it because current conversation is not related to original topic