1

I have an application where I want to avoid using 3rd binaries like cardano-cli. I need to calculate what is minimal UTXO size for TX where N different tokens is sent.

Right now cardano-wallet HTTP endpoint returns error 404 with long error string that contains correct value


{
  "code": "utxo_too_small",
  "message": "Some outputs have ada values that are too small. There's a minimum ada value specified by the protocol that each output must satisfy. I'll handle that minimum value myself when you do not explicitly specify an ada value for an output. Otherwise, you must specify enough ada. Here are the problematic outputs:   - Expected min coin value: 1.413762     TxOut:       address: 00064225...d59deacc       coin: 1.400000       tokens:         - policy: 9e87deb6ec1e989f125652b4883728fdd308e4223a0396f45b2023c2           tokens:             - token: 766d3774657374486f736b794361736847726162               quantity: 1 "
}

I want to be able to calculate this value myself. What I do right now is that I take "base min UTXO" size of 1.6ADA for lovelace and 1 token and then add 0.3ADA for each extra token, so if someone is sending 2 tokens it asks for 1.9ADA deposit.

That works but isn't correct and I probably use unnecessarily large deposits (it doesn't matter so much as whole deposit - tx fees is returned back to user) but I'd like to make this correct.

So what is the correct algorithm to calculate precise fee? Mind this is just a simple transaction of ADA and some native assets to single output address, no plutus or other complex things to consider.

2
  • Check out this Q/A: cardano.stackexchange.com/questions/2545/… Manual calculation of a cbor is an "involved process", as Andrew puts it. I'm not sure the exact way to do it though.
    – zhekson
    Feb 8 at 14:17
  • unfortunatelly that doesn't explain how to calculate it without cardano-cli, eg. how to get TX size without it?
    – Petr
    Feb 8 at 14:27

2 Answers 2

2

There is documentation to calculate the minimum ADA value for a tx output however it is not only out of date(e.g. coinSize has since increased to 2, asset names now hex-encoded instead of ASCII chars), but also difficult to decipher into working code.

After quite a bit of effort I implemented the particular algorithm in C# which should translate to other languages without much difficulty:

public record struct NativeAssetValue(
    string PolicyId, string AssetNameHex, long Quantity);

public record struct TokenBundle(
    long LovelaceValue, NativeAssetValue[] NativeAssets);

public static long CalculateMinUtxoLovelace(
    TokenBundle txOutBundle,
    int lovelacePerUtxoWord = 34482, // utxoCostPerWord in protocol params (could change in the future)
    int policyIdSizeBytes = 28, // 224 bit policyID (won't change in forseeable future)
    bool hasDataHash = false) // for UTxOs with smart contract datum
{
    const int fixedUtxoPrefixWords = 6;
    const int fixedUtxoEntryWithoutValueSizeWords = 27; // The static parts of a UTxO: 6 + 7 + 14 words
    const int coinSizeWords = 2; // since updated from 0 in docs.cardano.org/native-tokens/minimum-ada-value-requirement
    const int adaOnlyUtxoSizeWords = fixedUtxoEntryWithoutValueSizeWords + coinSizeWords;
    const int fixedPerTokenCost = 12;
    const int byteRoundUpAddition = 7; 
    const int bytesPerWord = 8; // One "word" is 8 bytes (64-bit)
    const int fixedDataHashSizeWords = 10;

    var isAdaOnly = txOutBundle.NativeAssets.Length == 0;
    if (isAdaOnly)
        return lovelacePerUtxoWord * adaOnlyUtxoSizeWords; // 999978 lovelaces or 0.999978 ADA

    // Get distinct policyIDs and assetNames
    var policyIds = new HashSet<string>();
    var assetNameHexadecimals = new HashSet<string>();
    foreach (var customToken in txOutBundle.NativeAssets)
    {
        policyIds.Add(customToken.PolicyId);
        assetNameHexadecimals.Add(customToken.AssetNameHex);
    }

    // Calculate (prefix + (numDistinctPids * 28(policyIdSizeBytes) + numTokens * 12(fixedPerTokenCost) + tokensNameLen + 7) ~/8)
    var tokensNameLen = assetNameHexadecimals.Sum(an => an.Length) / 2; // 2 hexadecimal chars = 1 Byte
    var valueSizeWords = fixedUtxoPrefixWords + (
        (policyIds.Count * policyIdSizeBytes)
        + (txOutBundle.NativeAssets.Length * fixedPerTokenCost)
        + tokensNameLen + byteRoundUpAddition) / bytesPerWord;
    var dataHashSizeWords = hasDataHash ? fixedDataHashSizeWords : 0;

    var minUtxoLovelace = lovelacePerUtxoWord 
        * (fixedUtxoEntryWithoutValueSizeWords + valueSizeWords + dataHashSizeWords);

    return minUtxoLovelace;
}

Special thanks to Pal's visualisation from his Shelley Stuffs Repository which was of tremendous help Visualisation

0

I think the answer is supposed to be extractable from the docs

https://docs.cardano.org/native-tokens/minimum-ada-value-requirement

but its not very clear (to me).

I'm also pretty suspicious

sumAssetNameLengths B = length myAssetNameSBS + length yourAssetNameSBS + length someAssetNameSBS

implies that the blockchain economises when two assets share a token name (but different currency symbol). This is weird.


I found your Q after looking for the same answer ... => I also don't really know yet

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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