Skip to content
Gav Wood edited this page Feb 20, 2014 · 25 revisions

LLL is the Ethereum PoC Low-level Lisp-like Language.

It is Lisp-like in syntax rather than anything deeper and is designed to make easier the task of writing a program in ES-1. It is automatically compiled in Ethereum PoC series including PoC-3 upwards.

A contract written in LLL takes the form of a single expression. An expression may be one of:

  • A quoted string e.g. "Hello world!" or "This is \"Great\"". Such strings have maximum 32 characters are evaluate to the 32 byte value with the ASCII encoding of the string aligned to the left-side (i.e. in the high-order bytes should it be interpreted as a big endian integer).
  • An integer, optionally prefixed with 0x for hex base and suffixed with any of the standard Ethereum denominations. e.g. 69, 0x42 or 3finney.
  • An executed expression which takes the form of a parenthesised list of expressions e.g. (add 1 2), with the first expression of the list being the operation and the others being operands.

All instructions of the ES-1 spec, excepting the hash instructions are valid, though you generally shouldn't need to use the stack manipulation and jump operations.

In addition, several control flow operations are provided:

  • (if PRED Y N) executes PRED, pops the stack and executes Y if the popped value is non-zero otherwise N.
  • (when PRED BODY) executes PRED, pops the stack and executes BODY if the popped value is non-zero.
  • (unless PRED BODY) executes PRED, pops the stack and executes Y if the popped value is zero.
  • (for PRED BODY) executes PRED, pops the stack and executes BODY if the popped value is non-zero before repeating all again indefinitely.
  • (seq A B ...) executes all following expressions in order.

And two shorthand forms for storing and loading to the permanent store:

  • (INT), (STRING) treats the value as an address and fetches to the top of the stack the value from storage (i.e. executes a PUSH & SLOAD)
  • (INT, EXPR), (STRING, EXPR) treats the first value as an address as above and stores the EXPR in storage at that address.

You'll generally want to enclose your programs with (seq ...) so you can execute more than a single expression.

Examples:

Simple cash splitter - removes 100 finney, then splits the rest amongst each of the addresses given as data items.

(seq
  (unless (gt (txvalue) 100finney) (stop))
  ("_value" (div (sub (txvalue) 100finney) (txdatan)))
  ("_i" 0)
  (for (lt ("_i") (txdatan))
    (mktx (txdata ("_i")) ("value") 0)
    ("_i" (add ("_i") 1))
  )
)
Clone this wiki locally