3

How can I concatenate a string and a tez value in Liquidity?

let a = 1.00tz;
let b = "Amount is: "; // <- how to get a in there?

A more real world example;

I have an addCost in my storage (admins can change it to track $ price) and I would like to reflect the cost in fail messages:

if (amount < storage.addCost) {
  Current.failwith("Not enough money, at least <addCost> to add")
};

How can I get the addCost value in there...?

5
  • There is no nice way to do this. Maybe the real question ought to be about why you want to do this... I can't think of a good reason. – Tom May 23 '19 at 1:41
  • @Tom I added a more real world example for why I want to do this. Surely it's not impossible...? I could make the addCost a string, but then I would need to convert it to a tez value when comparing to current.amount() 🤷‍♂️ – asbjornenge May 23 '19 at 6:36
  • Figured out I can do Current.failwith(("Not enough money", storage.addCost)) to at least communicate the amount in the fail message. However, still think there has to be a way to do this...? – asbjornenge May 23 '19 at 6:58
  • Do you really want to ask "how to format tez as string", or something more like "how to provide user-friendly error data"? The latter question is interesting and seems to be the real question here. If you want to ask the latter, I suggest explaining your goals more explicitly, and your thoughts about various approaches. If you really want to ask the former, I can provide a boring answer. :) – Tom May 23 '19 at 18:05
  • For now, and for other people who might find this later, I want to ask the former and collect a boring answer 😉 Feel free to add some notes regarding the latter to your answer if you feel like it 👍 – asbjornenge May 23 '19 at 21:12
2

There are no Michelson ops for formatting values as strings.

Unless a protocol change adds such operations, it seems that the best way to convert mutez (or int, nat) to string will be to write the algorithm manually:

  • First, we can convert mutez to nat by dividing by 1 mutez, using EDIV.
  • Now we can do a loop, iteratively dividing the nat value by 10, again using EDIV. The remainders give us each successive digit of the decimal representation. We must map each digit 0-9 to the corresponding string "0"-"9", perhaps using a literal map nat string. Of course, once we get to zero, we are done.
  • If we want to format mutez as a tez string, we need to add a decimal place and might drop trailing zeros after the decimal.
  • To concatenate the string bits together we will use CONCAT. There are two forms of CONCAT. I think it is generally more gas-efficient to build up a list string and CONCAT it all at once, rather than doing a bunch of binary string/string CONCATs.

It seems like this is going to require a decent chunk of code. This will cost you some origination burn. You will probably consume gas for this code on every transaction to your contract, whether the code is executed or not.

You can mitigate this gas cost by putting the conversion code once in a lambda. You will still pay the cost once per transaction.

You could mitigate the gas cost even more by doing some big_map gymnastics, loading the lambda from the big_map only when it is needed.

Perhaps, in your case, the gas costs are not a concern. I don't know.

1
  • Thanks for a fantastic and detailed answer 🙌🎉 Although disappointing it is this hard to do such a "common" operation. Thanks 👍 – asbjornenge May 24 '19 at 7:26

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.