0

I’m trying to build a multi-sig transaction to mint an NFT. The transaction will take some input from users wallet via coin selection and add an additional NFT as output.

The problem begans, when the wallet have some ada but all of them are locked with some tokens. We all know that you can’t just send a token alone; there must be some ada attached to it.

Consider this UTXO, for example.

Hex:-

828258205abda375b18b2097b2011f22dbf2d14643ce5fd7478ba4df6b21c2b91d9fd1a70182583901c13c23461bf40f5e07e1a0a84759e8e6b75dc0c6f2364bfa52f0b45fe7230182c948c67dc319960f10910f8489818f6f85163f0deb83e6fc821a003be9eca5581c0445383201b56c4acedf544afa57cb88bfbc616c35b57e5b7dbad2d1a144505245531a000153d8581c5f4fa7ef47236a2b2a6b9455e7685b6d5993c925680d507fce3cd3eaa5504e6f7a6f6d69433152616e6b3331323801504e6f7a6f6d69433152616e6b3432383601504e6f7a6f6d69433152616e6b3533343201504e6f7a6f6d69433152616e6b3534373301504e6f7a6f6d69433152616e6b3534393601581c6eae3a98e41c11c85a1620357a1eeae0a8df772287d6cf71313ad311b1544e6f7a6f6d69436e66742050617373202330323401544e6f7a6f6d69436e66742050617373202330343301544e6f7a6f6d69436e66742050617373202330343601544e6f7a6f6d69436e66742050617373202330353801544e6f7a6f6d69436e66742050617373202330363001544e6f7a6f6d69436e66742050617373202330383401544e6f7a6f6d69436e66742050617373202330383601544e6f7a6f6d69436e66742050617373202330383801544e6f7a6f6d69436e66742050617373202330393201544e6f7a6f6d69436e66742050617373202330393501544e6f7a6f6d69436e66742050617373202330393701544e6f7a6f6d69436e66742050617373202331303201544e6f7a6f6d69436e66742050617373202331323001544e6f7a6f6d69436e66742050617373202331323401544e6f7a6f6d69436e66742050617373202331323901544e6f7a6f6d69436e66742050617373202331343301544e6f7a6f6d69436e66742050617373202331343801581c8b6e715851dead233ae5c4f8ed1f192e78461d2f64d0f4bb35b823cba153464643466f756e64657273506173733030373301581cf0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9aa1496a696d742e6b69726b01

CSL JSON:-

{
  "address": "addr1q8qncg6xr06q7hs8uxs2s36earntwhwqcmervjl62tctghl8yvqc9j2gce7uxxvkpugfzruy3xqc7mu9zclsm6urum7qlau4pp",
  "amount": {
    "coin": "3926508",
    "multiasset": {
      "0445383201b56c4acedf544afa57cb88bfbc616c35b57e5b7dbad2d1": {
        "50524553": "87000"
      },
      "5f4fa7ef47236a2b2a6b9455e7685b6d5993c925680d507fce3cd3ea": {
        "4e6f7a6f6d69433152616e6b33313238": "1",
        "4e6f7a6f6d69433152616e6b34323836": "1",
        "4e6f7a6f6d69433152616e6b35333432": "1",
        "4e6f7a6f6d69433152616e6b35343733": "1",
        "4e6f7a6f6d69433152616e6b35343936": "1"
      },
      "6eae3a98e41c11c85a1620357a1eeae0a8df772287d6cf71313ad311": {
        "4e6f7a6f6d69436e667420506173732023303234": "1",
        "4e6f7a6f6d69436e667420506173732023303433": "1",
        "4e6f7a6f6d69436e667420506173732023303436": "1",
        "4e6f7a6f6d69436e667420506173732023303538": "1",
        "4e6f7a6f6d69436e667420506173732023303630": "1",
        "4e6f7a6f6d69436e667420506173732023303834": "1",
        "4e6f7a6f6d69436e667420506173732023303836": "1",
        "4e6f7a6f6d69436e667420506173732023303838": "1",
        "4e6f7a6f6d69436e667420506173732023303932": "1",
        "4e6f7a6f6d69436e667420506173732023303935": "1",
        "4e6f7a6f6d69436e667420506173732023303937": "1",
        "4e6f7a6f6d69436e667420506173732023313032": "1",
        "4e6f7a6f6d69436e667420506173732023313230": "1",
        "4e6f7a6f6d69436e667420506173732023313234": "1",
        "4e6f7a6f6d69436e667420506173732023313239": "1",
        "4e6f7a6f6d69436e667420506173732023313433": "1",
        "4e6f7a6f6d69436e667420506173732023313438": "1"
      },
      "8b6e715851dead233ae5c4f8ed1f192e78461d2f64d0f4bb35b823cb": {
        "464643466f756e646572735061737330303733": "1"
      },
      "f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a": {
        "6a696d742e6b69726b": "1"
      }
    }
  },
  "plutus_data": null,
  "script_ref": null
}

This wallet has 3.92 ADA in it, more than enough to overcome the min utxo rules.

But all of these ADA are locked with the tokens of that utxo. Which means if we add another token, we also have to add some additional lovelace from another UTXO; otherwise, this will throw a BabbageOutputTooSmallUTxO error.

"transaction submit error ShelleyTxValidationError ShelleyBasedEraBabbage (ApplyTxError [UtxowFailure (UtxoFailure (BabbageOutputTooSmallUTxO [((Addr Mainnet (KeyHashObj (KeyHash \"c13c23461bf40f5e07e1a0a84759e8e6b75dc0c6f2364bfa52f0b45f\")) (StakeRefBase (KeyHashObj (KeyHash \"e7230182c948c67dc319960f10910f8489818f6f85163f0deb83e6fc\"))),Value 3671749 (fromList [(PolicyID {policyID = ScriptHash \"0445383201b56c4acedf544afa57cb88bfbc616c35b57e5b7dbad2d1\"},fromList [(50524553,87000)]),(PolicyID {policyID = ScriptHash \"5f4fa7ef47236a2b2a6b9455e7685b6d5993c925680d507fce3cd3ea\"},fromList [(4e6f7a6f6d69433152616e6b33313238,1),(4e6f7a6f6d69433152616e6b34323836,1),(4e6f7a6f6d69433152616e6b35333432,1),(4e6f7a6f6d69433152616e6b35343733,1),(4e6f7a6f6d69433152616e6b35343936,1)]),(PolicyID {policyID = ScriptHash \"6eae3a98e41c11c85a1620357a1eeae0a8df772287d6cf71313ad311\"},fromList [(4e6f7a6f6d69436e667420506173732023303234,1),(4e6f7a6f6d69436e667420506173732023303433,1),(4e6f7a6f6d69436e667420506173732023303436,1),(4e6f7a6f6d69436e667420506173732023303538,1),(4e6f7a6f6d69436e667420506173732023303630,1),(4e6f7a6f6d69436e667420506173732023303834,1),(4e6f7a6f6d69436e667420506173732023303836,1),(4e6f7a6f6d69436e667420506173732023303838,1),(4e6f7a6f6d69436e667420506173732023303932,1),(4e6f7a6f6d69436e667420506173732023303935,1),(4e6f7a6f6d69436e667420506173732023303937,1),(4e6f7a6f6d69436e667420506173732023313032,1),(4e6f7a6f6d69436e667420506173732023313230,1),(4e6f7a6f6d69436e667420506173732023313234,1),(4e6f7a6f6d69436e667420506173732023313239,1),(4e6f7a6f6d69436e667420506173732023313433,1),(4e6f7a6f6d69436e667420506173732023313438,1)]),(PolicyID {policyID = ScriptHash \"8b6e715851dead233ae5c4f8ed1f192e78461d2f64d0f4bb35b823cb\"},fromList [(464643466f756e646572735061737330303733,1)]),(PolicyID {policyID = ScriptHash \"9c241d1a9269da78a85a0e1269ecd5f56477641df28b2ce5ccd65188\"},fromList [(4e6f7a6f6d69436e667420506173732023303234,1)]),(PolicyID {policyID = ScriptHash \"f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a\"},fromList [(6a696d742e6b69726b,1)])]),NoDatum,SNothing),Coin 4055710)]))])

I'm looking for a way to prevent this kind of coin selection. Is there a way to calculate how much ADA of a UTXo isn't locked with any assets?

Also, how do you calculate the minimum output amount that are required to avoid OutputTooSmall error?

2 Answers 2

0

Is there a way to calculate how much ADA of a UTxO isn't locked with any assets?

I am not sure I follow the question. All the ADA inside a UTxO is locked with all the tokens inside the UTxO, they are all in the same boat, you cannot consume only part of a UTxO, you either spend it as a whole (and return some change) or you don't spend it at all.

Maybe what you meant is "how to calculate how much ADA I can spend from a UTxO without raising a transaction error", in which case we come to your next question, which effectively the same.

how do you calculate the minimum output amount that are required to avoid OutputTooSmall error?

You can see a complete response in the Cardano documentation. In summary

minUTxoVal = (160 + sizeInBytes (TxOut)) * coinsPerUTxOByte

Some Cardano tools offer easy ways to calculate this value. In lucid, you can use the min_ada_required function. In PyCardano, you can use the min_lovelace function.

3
  • I think I got your point, but I am still unsure of one thing. Let's say I picked an UTXO with random improve method that contains 2 ADA. Then I added 20 NFT as output. As you might guess, adding 20 NFT will set the minUtxo way higher than 2 ADA as the tx size will be big. But I can only get the sizeInBytes(TxOut) once I build the transaction. And if the transaction is already built, what's the point of retrieving minUTXOValue then? What should be the appropriate approach here?
    – Alexis
    Feb 28 at 18:45
  • "And if the transaction is already built, what's the point of retrieving minUTXOValue then?" In your case the point is to not have it raise an error. When we calculate what fees we need to pay, we do the same thing in the background, we first the transaction with an arbitrary amount for the fees and then we calculate it and change it. You would do the same thing, you first create the transaction and then calculate the min utxo value and change it in the transaction again, seems redundant, but in your case is what you need to do.
    – Mateus
    Feb 28 at 22:37
  • Unless somehow you already know how the new output UTxO will look like (what values it will contain), in which case you don't need to build the full transaction, you can simply use the sizeInBytes(TxOut) function and give it as an argument the output you expect
    – Mateus
    Feb 28 at 22:38
0

I think what you need to do is figure out what mph + token names you have in the utxos, and then craft an output that consolidates the separate UTXOs (with their own minting policy + token name) into one output. I would recommend you checkout Helios which has coin selection algorithms that may be able to help with that. You may also need to augment your own offchain code by examining the following code snippet from the helios.js library to help create this specific output.

https://github.com/Hyperion-BT/helios/blob/main/helios.js#L35660

enter image description here

Here is a related problem where I needed to find out the number of a specific tokens in the input UTXOs. This is because when I minted more of them, I wanted the output to be both the amount of tokens in the input + the new minted ones.

enter image description here

Source code: https://github.com/lley154/littercoin/blob/preprod-6.33/app/pages/index.tsx

3

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.