An Introduction to LLL for Ethereum Smart Contract Development

Daniel Ellison
ConsenSys Media
Published in
4 min readMay 5, 2017

--

An example of LLL source code

Ethereum is hot. Very hot. As I write this, Ether has just hit the $100 mark. For this and other reasons there are now legions of developers coming to the platform. Most of them are under the mistaken impression that Ethereum has one, and only one, language for smart contract development. I’m here to change that misapprehension.

In the beginning

Back when Vitalik, Jeff, Gavin and the rest were developing the beginnings of Ethereum, they knew they didn’t want to write code directly in assembler. The EVM, or Ethereum Virtual Machine, is a very simple VM with only basic jumps and stack manipulation for program control. But they also knew they couldn’t come up with an all-encompassing high-level language in an acceptable time frame. As a result, LLL was born. Since then, Solidity has taken over as the preferred smart contract development language.

I have come here not to bury Solidity, but to praise it (to inversely paraphrase Mark Antony). Solidity is an excellent solution to a difficult problem: How can we attract regular developers to an entirely new platform? The answer is, give them a system that is familiar to them. This results in a reduced on-boarding time; a developer can start writing smart contracts much sooner if they’re familiar with any C-like language.

A definition

LLL stands for Low-level Lisp-like Language. I know some of you just threw up a little in your mouth at the mention of Lisp, but please bear with me. For better or worse, LLL is a much simpler language than Lisp. Yes, it uses those awful parentheses but believe me when I say that you get used to them.

Justification

Now you might ask why anyone would want to use anything but Solidity, much less a language that looks like Lisp. Well, LLL confers a few advantages. LLL — as its name suggests—is a lower-level language than Solidity. This manifests itself in a couple of ways.

First, the developer has direct access to memory and storage. This allows you to arrange your contract data in any way you like, optimizing for the most efficient access. Second, you have complete access to all EVM opcodes. When compiled, these LLL operators translate directly to EVM opcodes, giving your contract power and brevity. Third, and expanding on the brevity idea, LLL contracts compile down to much smaller binaries than Solidity contracts. In a recent reimplementation I did, the LLL binary ended up 70% smaller than the Solidity equivalent.

One more thing to note: the assembly code produced by LLL is so concise and straightforward that it looks like it’s been coded by hand in assembly. In contrast, Solidity’s assembly output is a wild mess of redundant opcodes and odd jumps.

An example

Without further ado, I’ll now present a code example which is the equivalent of a Solidity modifier implemented in LLL.

(def 'only-node-owner (node)
(when (!= (caller) (get-owner node))
(panic)))

There, that wasn’t so hard, was it? This code snippet illustrates many characteristics of LLL. First, it shows the use of parentheses. The only thing you really have to worry about is that your parentheses match.

Second, it illustrates the definition of an LLL macro with the def keyword. Macros in LLL aid in clarifying your source code. One thing to note: Whenever you invoke a macro in your source, upon compilation the macro’s definition will be substituted for the macro name.

Third, it reveals that LLL uses prefix notation. So instead of 2 + 3 you would use + 2 3 The operator is always followed by its operands. It makes sense in the context of a symbolic expression, which is the other defining characteristic of LLL (and Lisp). Here’s a definition from Your Dictionary. Symbolic expressions are

a means of representing semistructured data in human-readable text form, mostly composed of symbols and lists and extensively used in the Lisp programming language.

This macro is used to ensure that only the owner of the node passed in is able to affect the node. In LLL, multiple consecutive instructions need to be enclosed in a list using the seq keyword. Modifiers are ideally placed at the start of a sequence, like this:

(function forward-call
(seq (only-node-owner node)
(if (= destination *create-flag*)
(more code here…

The use of only-node-owner here causes a panic if the caller isn’t the node’s owner. panic is the LLL equivalent of Solidity’s throw instruction.

Conclusion

I don’t want to dive in too deep here; I’ll leave that to future articles. With this article I just wanted to give you a taste of what LLL is all about and provide a sense of what writing a contract in the language looks like. I hope this has — at the very least — whetted your appetite for an alternative to Solidity for Ethereum contract development. I’ll be writing more articles like this, some a lot more technical. I’ll also be producing instructional videos and screencasts, which will be published on the ConsenSysMedia channel on YouTube.

Read More:

Building and Installing Ethereum Compilers
Compiling an LLL Contract for the First Time
Deploying Your First LLL Contract — Part 1
Deploying Your First LLL Contract — Part 2
The Structure of an LLL Contract — Part 1
The Structure of an LLL Contract — Part 2
The Structure of an LLL Contract — Part 3

Like this piece? Sign up here for the ConsenSys weekly newsletter.

Disclaimer: The views expressed by the author above do not necessarily represent the views of Consensys AG. ConsenSys is a decentralized community with ConsenSys Media being a platform for members to freely express their diverse ideas and perspectives. To learn more about ConsenSys and Ethereum, please visit our website.

--

--