Skip to content

zigguratt/lll-stdlib

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 

Repository files navigation

A standard library of LLL macros

WARNING: stdlib.lll is a work in progress. There will be changes, additions, and deletions.

In my explorations of LLL over the past few months I've come up with a small set of macros that make working with the language easier, and make reading the Lisp-like source code clearer. Some of the things that appear in these seemingly simple macros took a very long time to resolve owing to the dearth of LLL documentation—or even source code, for that matter.

I'm hoping to change this situation by creating and documenting these macros, publishing other LLL-based projects, writing articles, and participating in online and real-life communities to inform others that LLL is a perfectly viable contract development language.

The code

Since LLL is probably unfamiliar to most visitors to this repository, I'll go through the code step-by-step, clarifying what's happening at each step. You'll notice that I've added a fair bit of documentation to the macro library to make it easier to understand. The documentation is in Ethereum's Natural Specification Format which uses Doxygen notation. There's currently nothing that can extract this documentation from LLL source, but at least it's there if and when that capability does arrive.

How macros work

LLL macros do one thing: they substitute symbols in source code with the value that's been assigned to the symbol in its macro definition. This process happens at compile time. For example, with a macro definition like this:

(def 'scratch-one 0x00)

when the compiler encounters the string "scratch-one" in an LLL source file it will substitute that string with 0x00. Similarly, given this macro definition:

(def 'shift-left (input)
  (mul input (exp 2 224)))

when the compiler encounters

(shift-left (calldataload 0))

in source, it replaces that code with

(mul (calldataload 0) (exp 2 224))

LLL macros fall into two categories: simple assignments—what I call constants—and more extensive code substitutions. Both effectively do the same thing, but I find it helpful to separate them out conceptually in order to clarify source code.

Constant definitions

At the top of the stdlib.lll file is a set of simple assignments. The first two should be self-explanatory: true and false. LLL doesn't have a concept of booleans so this is a convenient way to use true and false in your code instead of 1 and 0. In case it was unclear, macros are defined using the LLL keyword def.

stdlib-version

The next macro, stdlib-version, is used to verify that you're using the appropriate version of this macro library in your contracts. A simple comparison between this number and the number you've defined in your source allows you to abort your contract's deployment if there's a version mismatch.

invalid-location

When your contract determines that there's been an unrecoverable error, it should "throw" an error. That is accomplished by causing the contract to jump to an invalid location. That is actually what Solidity's throw does. In LLL, you would use the jump keyword and give it a location that is not tagged as a valid jump destination. Currently that location is 0x02. So in order to cause a "throw" you would use the invalid-location constant:

(jump invalid-location)

This will change once the panic keyword that was recently added—and subsequently broken— is repaired. Then it would simply be a matter of doing:

(throw)

hold-back

hold-back, here defined as 1000, is the amount of gas that is withheld from the amount being sent to another contract to pay for its execution. The idea is that your contract will need gas to continue execution in the event that the callee "throws" and consumes all gas sent to it. So instead of sending all of your gas, you hold back a small amount. 1000 is a bit high, but it's also an extremely small portion of 1 eth so it's not unreasonable.

scratch-one & scratch-two

The next set of macros under Memory layout define how memory is used by these macros. The first two, scratch-one and scratch-two, aren't Dr. Seuss characters as you might expect. They're used as temporary memory locations for various operations. In this library only scratch-one is used, but it's not unusual for both to be used for a longer keccak hash calculation.

Contract call constants

The next four definitions concern contract calls and their call and return values. When you call another contract from within your own contract you get a boolean return value indicating success or failure of the call. For convenience you can store this return value in return-code:

(mstore return-code (call (- (gas) hold-back)...

return-data

return-data is the memory location the calling contract specifies as the target of any data returned from the called contract.

To be continued...

About

A standard library of LLL macros

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published