Solidity to LIGO translation challenges (sol2ligo update #2)

Julian Konchunas
Madfish Solutions
Published in
5 min readFeb 22, 2020

We had a rough time building this system for the last month. LIGO is vastly different from Solidity, so we are slowly, but steadily converging them. As our goal is to get a few popular contracts transpiled in a meaningful way, we are prioritizing some features over another. One of the main pieces we have been working on is called Router.

A router is a system in Michelson (low-level language for LIGO) that allows to pass arguments and make a call to any method specified in the contract. In Solidity, It is quite easy to call a single method of contract from outside. Just get the name from ABI, properly arrange arguments and you are good to go. However, in LIGO you can only call the main method. To work around the problem smart guys have came up with a router, namely: a special structure that allows users to pass the method name and its arguments straight into the main method. It will take care of the branching, make a specified call and return its value. Usually, developers of the contract do it manually, deciding which methods are accessible from the main entrypoint.

However, we believe it is crucial for our transpiler to remove this burden from the developer’s shoulders and let the machine do its work (so to speak). Sol2ligo analyzes input code, finds all declared methods and determines if a function should be added to the router. We analyze Solidity modifiers like public, internal, pure, view to detect the most suitable way to include the method. We have also improved our state extraction code which picks methods that should accept and return a contract state depending on pure/view modifiers in Solidity input code.

Let’s take a look at how we translate a simple Solidity method declaration into a LIGO function:

Translation from Solidity into LIGO

As you can see, even the simplest methods have to be augmented with opList and state variables to achieve the same logic as they have in Solidity.

But if you want to call a method from outside it gets more complex since you have to provide a router, so, practically, every method is being translated into the following piece:

Notice how `arg` gets tossed into three different places

This one is much more complex. Initially, record named <funcname>_args containing all arguments is formed for every method. Then, a composite type variant enclosing this record is generated and put into router_enum. Later, according to LIGO requirements, a method declaration is translated and modified to return both an operation list and a record for keeping state. Finally, the main function is created and it performs matching of input params with router_enum for calling a specific action. To better understand why all these complications are necessary, you may refer to the Entrypoints section of LIGO documentation.

Conversion highlights

One may wonder how we translate such different languages and, to be fair, that’s not an easy task. We want to share some highlights and clever hacks we have had to apply to get the best-looking result. I’m not sure if they’d settle since every the smallest detail raises heated debates inside our team of how to provide the best result for an end-user. Thereby, I’ve collected a few interesting cases to look at.

Array declaration

It’s implemented this way because LIGO doesn’t have any array support and map with an unsigned integer key is well suited for this job. Notice how we lose array size in the process - it doesn’t make sense to have a fixed length for a map

Map access

In Solidity, maps will return a default value if no value is found under the specified key. In LIGO we have to emulate this behavior by using one neat trick —case is an expression! It enables us to use its results anywhere in the code, even on the right-hand side.

C-like for loop

This is the exact case of for loop translation I have described in our previous story. Solidity doesn’t support for3, so we have had to transform it into a while loop. You can also notice that LIGO doesn’t have += operator, so this one has to be emulated as well.

Changelog

It was a fruitful month, so here are some key things we have been working on:

  • Types and their default values: numbers, strings, bytes. Aliases for bytes<num> and int<num>
  • Modifiers inlining, basic libraries unrolling, improved handling for pure functions
  • Comparisons, bitwise math, negations, pow
  • Cryptographic functions conversion
  • Stubs for event and emit, more warnings and more explanations
  • Improved type inference for number types and arrays
  • Better tuples support
  • require and assert (shout out to LIGO team for helping out)
  • Basic support for address methods like (send, transfer)
  • enum and struct declarations and definitions
  • Tests for all of that

Work in progress

Lots of things are being changed, fixed and broken in the process. We change API and output trying to balance out readability and correctness. If you want to try it out be aware that’s an unventured territory, since no real-world testing has been done yet.

Check out repo at https://github.com/madfish-solutions/sol2ligo

Show us your support by leaving a star!

Subscribe for updates

Facebook and Twitter

https://tezos.org.ua/ — Tezos Ukraine community website
https://twitter.com/UkraineTezos
— Tezos Ukraine twitter

--

--

Julian Konchunas
Madfish Solutions

Blockchain engineer at @madfish.solutions. Ex-Ubisoft. Telegram channel https://t.me/tenxer