The purpose of ECLL is to provide a language that will be simple and friendly for users to write contracts in, but at the same time easily and efficiently compile into Ethereum script code. ECLL abstracts away concepts like pointers, direct memory access and jumping in favor of a more traditional syntax of conditionals, loops, arrays and variables. Function support, including first-class-function support, will be left to a future more powerful, but less efficient, language that will be denoted EHLL ("Ethereum High Level Language")
The specification will be in three parts. The first part will define the language as an abstract syntax tree, formalized using LISP-style S-expression syntax, the second part will provide specifications for what each operation does, and the third will show how the AST is to be rendered in text.
An expression is defined as follows:
- A number is an expression
- A variable name is an expression
("arr" a b)
is an expression, wherea
andb
are expressions. This is the equivalent of C'sa[b]
.(OP a b)
is an expression, whereOP
is in the set(+ - * / % s/ s% ^ < > <= >= ==)
(OP a)
is an expression, wherea
is in the set(- !)
- The special values
tx.datan
,tx.value
,tx.sender
,contract.address
,block.number
,block.difficulty
,block.timestamp
,block.parenthash
andblock.basefee
are expressions ("pseudo" a b)
is an expression, wherea
is a pseudoarray andb
is an expression("fun" a b...)
is an expression, wherea
is a returning function andb...
contains the valid number of arguments
A pseudofunction is defined as follows:
block.contract_storage
is a pseudofunction
A pseudoarray is defined as follows:
tx.data
andcontract.storage
are pseudoarrays("pfun" a b...)
wherea
is a pseudofunction andb...
contains the valid number of arguments is a pseudoarray
contract.storage
is a mutable pseudoarray, and the other two are immutable pseudoarrays.
A returning function is defined as follows:
block.account_balance
,sha256
,sha3
,ripemd160
,ecvalid
are returning functionsarray
is a returning function
A multireturning function is defined as follows:
ecsign
andecrecover
are multireturning functions
A multiexpression is defined as follows:
("mfun" a b...)
wherea
is a multireturning function andb...
contains the valid number of arguments is a multiexpression
An acting function is defined as follows:
mktx
is an acting function
A left-hand-side expression is defined as follows:
- A variable is a left-hand-side expression.
("arr" a b)
is a left-hand-side expression, wherea
is a left-hand-side expression andb
is an expression.("pseudo" a b)
is a left-hand-side expression, wherea
is a mutable pseudoarray andb
is an expression.
A statement is defined as follows:
("afun" a b...)
wherea
is an acting function andb...
contains the valid number of arguments is a statement("set" a b)
wherea
is a left-hand-side expression andb
is an expression is a statement("mset" a... b)
wherea...
is a list of left-hand-side expressions with the valid number of arguments andb
is a multiexpression is a statement("seq" a...)
wherea...
are statements("if" a b)
wherea
is an expression andb
is a statement("if" a1 b1 "elif" a2 b2 "elif" a3 b3 ...)
whereak
are expressions andbk
are statements("if" a1 b1 "elif" a2 b2 "elif" a3 b3 ... "else" c)
whereak
are expressions andbk
andc
are statements("while" a b)
wherea
is an expression andb
is a statement"exit"
Pre-compilation, if there are n
variables, each variable will be assigned an index in [0,n-1]
. Let ind(x)
be the index of variable x
, and let M[x]
be the contract memory at index x
In the context of a right-hand-side expression:
- Arithmetic works in the obvious way.
(* (+ 3 5) (+ 4 8))
, for example, returns 96. a && b
is a shortcut for!(!(a) + !(b))
a || b
is a shortcut for!(!(a + b))
- A variable
x
should becomeM[ind(x)]
("arr" a b)
returnsM[M[ind(a)]+b]
tx.datan
returns the number of data fields in the transaction("pseudo" "tx.data" i)
returns the ith data field in the transaction or zero ifi >= tx.datan
tx.sender
,tx.value
,block.basefee
,block.difficulty
,block.timestamp
,block.parenthash
andcontract.address
return the obvious values.("pseudo "contract.storage" i)
returns the contract storage at indexi
.("fun" "block.account_balance" a)
returns the balance of addressa
("pseudo" ("pfun" "block.contract_storage" a) b)
returns the contract storage of accounta
at indexb
("mfun" "ecsign" hash key)
returns thev,r,s
triple from the signature("mfun" "ecrcover" h v r s)
returns thex,y
public key("mfun" "sha256" len arr)
returns the SHA256 hash of the string of lengthlen
starting fromM[ind(arr)]
.sha3
andripemd160
work similarly("fun" "array")
returns2^160 + M[2^256 - 1]
and setsM[2^256 - 1] <- 2^160 + M[2^256 - 1]
In the context of a left-hand-side expression:
- A variable
x
returnsind(x)
("arr" a b)
returnsa + b
wherea
andb
have already been evaluated("pseudo" a b)
returns an object which, if set, modifies the value of the pseudoarray referent at indexa
tob
instead of doing a memory set
In the context of a statement:
("afun" "mktx" dest value dn ds)
creates a transaction sendingvalue
ether todest
withdn
data fields starting fromM[ind(ds)]
("set" a b)
setsM[a] <- b
wherea
andb
have already been evaluated("mset" a... b)
sequentially sets the memory index at each of the values ina...
to a value from the multiexpressionb
("if" a b)
,("while" a b)
and derivatives will work as they do in other languages
- Arithmetic is done using infix notation using the C++ operator precedence:
^
then* / % s/ s%
then+ -
then< <= > >=
then==
then&&
then||
. That is to say, arithmetic formulas will behave as they normally do; for example,(+ (* 3 5) (* x y))
would be (3 * 5 + x * y) and(* (+ 3 5) (+ x y))
would be((3 + 5) * (x + y))
("arr" a b)
and("pseudo" a b)
will both be represented asa[b]
("fun" a b...)
,("mfun" a b...)
,("afun" a b...)
and("pfun" a...)
will all be represented asa(b1,b2, ... ,bn)
- Multiexpressions will be represented as
a1, a2, ... an
("set" a b)
and("mset a... b)
will be represented asa = b
anda1, a2 ... ,an = b
, respectively.("seq" a...)
consists of each of the statements on their own separate line("if" a b)
and("while" a b)
will be represented as in Python, using the keyword directly followed by the expression fora
and the indented statement forb
- Values can be represented either as integers or inside string quotes, in which case they will be treated as hexadecimal
exit
exits.
Examples:
Factorial:
x = 1
n = 1
while x < 10:
n = n * x
x = x + 1
Fibonacci sequence:
a = array()
a[0] = 1
a[1] = 1
i = 2
while i < 70:
a[i] = a[i-1] + a[i-2]
i = i + 1
mktx("0676d13c8d2cf5e9e988cc3b5f6dfb5a6a3938fa",a[69],70,a)
A real example - simple forwarding contract to create a transferable account:
if tx.value < tx.basefee * 200:
exit
else:
a = contract.storage[2^256 - 1]
if !(tx.sender == a) && (tx.sender > 0):
exit
if tx.data[0] == 2^160:
contract.storage[2^256 - 1] = tx.data[1]
else:
if a == 0:
contract.storage[2^256 - 1] = tx.sender
o = array()
i = 0
while i < tx.datan - 1:
o[i] = tx.data[i+1]
i = i + 1
mktx(tx.data[0],tx.value - tx.basefee * 250,i,o)
("seq" a...)
is implemented by separating the statements with semicolons. Newlines are ignored.("if" a b)
and("while" a b)
will be represented as in C++, storinga
inside brackets andb
inside curly brackets
The AST is used as the code directly, with the minor modification that ("fun" a b...)
, ("pfun" a b...)
and ("mfun" a b...)
are replaced with (a b...)
.