Reference

Overview

Aergo uses Lua, a lightweight scripting language, as its smart contract language. The following is an example of a simple coinstack smart contract written in Lua that stores key-value pairs in a blockchain state store and reads the values:

-- Storing key-values in the state store
function set(key, value)
    system.setItem(key, value)
end

-- Returns the value corresponding to the key in the state store
function get(key)
    return system.getItem(key)
end

-- Output the hash value of the previous block. The output is printed on the server log.
function printPrevBlock()
    system.print(system.getPrevBlockHash())
end

-- Functions to be called for contract declare as abi
abi.register(set, get, printBlock)

To read and write data on a blockchain within a contract, you must use the system package. In the above example, the smart contract provides the function to access the key-value repository through the setItem and getItem functions of the system package and store the data permanently. In addition, a simple debug message can be output to the node’s log file via the print function.

system package

This packages provides blockchain information and store/get state

getBlockheight()

This function returns the block number that contains the current contract transaction.

getPrevBlockHash()

This function returns the hash of the previous block

getTimestamp()

This function returns the creation start time of the block that contains current contract transaction.

getTxhash()

This function returns the id of the current contract transaction.

getAmount()

This function returns number of AER sent with contract call. Return type is string.

isFeeDelegation()

This function returns whether the transaction is using fee delegation.

getContractID()

This function returns the current contract address.

getCreator()

This function returns creator address of current contract

getOrigin()

This function returns sender address of current transaction

getSender()

This function returns caller address of current contract

isContract(address)

This function return if address is contract then true else false. when address is invalid then raise error

setItem(key, value)

This function sets the value corresponding to key to the storage belonging to current contract

Restrictions:

  • key type can only be string (number type is implicitly converted to string)
  • value type can be: number, string, table

getItem(key)

This function returns the value corresponding to key in storage belonging to current contract

  • If there is no value corresponding to key, it returns nil.

contract package

This packages provides contract operation

send(address, amount)

This function transfers an amount of coins from this contract to the given address. The Amount can be in string, number or bignum formats. The default unit is AER.

contract.send("Amh4S9pZgoJpxdCoMGg6SXEpAstTaTQNfQdZFsE26NpkqPwmaWod", 1)
contract.send("Amh4S9pZgoJpxdCoMGg6SXEpAstTaTQNfQdZFsE26NpkqPwmaWod", "1 aergo 10 gaer")
contract.send("Amh4S9pZgoJpxdCoMGg6SXEpAstTaTQNfQdZFsE26NpkqPwmaWod", bignum.number("999999999999999"))

deploy(code, args…)

The deploy function creates a contract account using code and args, and returns the corresponding address and the returned value(s) from the constructor function.

If you use an existing address instead of code, it will be deployed with the code from the given address.

In addition, you can call the value function to send coins to the new contract. The value function accepts a string, a number or a bignum as the argument like the send function.

    src = [[
        function hello(say)
            return "Hello " .. say .. system.getItem("made")
        end
        function constructor(check)
            system.setItem("made", check)
        end
        abi.register(hello)
        abi.payable(constructor)
    ]]
    addr = contract.deploy.value("1 aergo")(src, system.getContractID())

call(address, function_name, args…)

The call function executes a function on another contract and returns the result. The call is executed in the context of the target contract’s state.

We can also call the value function to send an amount of native aergo tokens to the contract.

The value function accepts a string, a number or a bignum as the argument like the send function.

contract.call("Amh4S9pZgoJpxdCoMGg6SXEpAstTaTQNfQdZFsE26NpkqPwmaWod", "inc", 1)
contract.call.value(10)("Amh4S9pZgoJpxdCoMGg6SXEpAstTaTQNfQdZFsE26NpkqPwmaWod", "inc", 2)

If an error occurs on the called contract (or another one called by it) then the execution is halted and the transaction is marked as failed.

To avoid that, use the pcall command, like this:

local success, result = pcall(function()
  return contract.call("Amh4S9pZgoJpxdCoMGg6SXEpAstTaTQNfQdZFsE26NpkqPwmaWod", "do_something", 123, "id")
end)
if success == false then
  -- the call failed
  ...
end
return result

On this case when the called function fails, no error is raised and the contract execution continues. It just returns false to indicate the failure.

Restrictions

  • maximum call depth of 64 calls in one transaction

delegatecall(address, function_name, args…)

The delegatecall function loads the code from the target address and executes it in the context of the calling contract.

Storage, current address and balance still refer to the calling contract, only the code is taken from the called address.

Notice that the call can change the state of the current contract.

This is used to implement the “library” feature, with reusable library code.

contract.delegatecall("Amh4S9pZgoJpxdCoMGg6SXEpAstTaTQNfQdZFsE26NpkqPwmaWod", "inc", 1)

Restrictions

  • maximum call depth of 64 calls in one transaction

balance(address)

This function returns the balance of the given address in AER. The return type is string. If address is nil then it returns the balance of the current address.

contract.balance()  -- get balance of current contract address 
contract.balance("Amh4S9pZgoJpxdCoMGg6SXEpAstTaTQNfQdZFsE26NpkqPwmaWod")

event(eventName, args…)

This function fires an event containing eventName and args. The event is recorded in the blockchain in the contract result receipt. It is also broadcasted to all the listening applications connected to the blockchain network that have subscribed to this event.

The user can search for events that happened in the past and also receive notifications of new events by using RPC.

contract.event("send", 1, "toaddress") 

Restrictions

  • maximum length of event name: 64
  • maximum size of event argument: 4k
  • maximum number of events in one transaction: 50

Examples: searching for event and receiving notification with aergocli

Searching for events with name “send” from the contract address AmhbdCEg4TUFm6Hpdoz8d81eSdzRncsekBLN3mYgLCbAVdPnu1MZ

./aergocli event list --address AmhbdCEg4TUFm6Hpdoz8d81eSdzRncsekBLN3mYgLCbAVdPnu1MZ --event send

Searching for events where argument 0 is 1 and argument 1 is “toaddress” in the contract address AmhbdCEg4TUFm6Hpdoz8d81eSdzRncsekBLN3mYgLCbAVdPnu1MZ

./aergocli event list --address AmhbdCEg4TUFm6Hpdoz8d81eSdzRncsekBLN3mYgLCbAVdPnu1MZ --argfilter '{"0":1, "1":"toaddress"}'

Getting notifications for events with name “send” for contract AmhbdCEg4TUFm6Hpdoz8d81eSdzRncsekBLN3mYgLCbAVdPnu1MZ

./aergocli event stream --address AmhbdCEg4TUFm6Hpdoz8d81eSdzRncsekBLN3mYgLCbAVdPnu1MZ --event send

stake(amount), unstake(amount), vote([bps,…]), voteDao(arg1, arg2,…)

You can do governance (stake, unstake, vote, voteDao) in the smart contract, using the contract address

contract.stake("1 aergo") 
contract.vote(["<bp1 address>", "<bp2 address>"....)
contract.voteDao("gasprice", "5")
contract.unstake("1 aergo")

Built-in Functions

Lua provides the language itself as a useful function and basic package. It provides useful functions such as string management functions, so you can easily create smart contracts using these functions. Please refer to the Lua Reference Manual for detailed syntax, explanation, basic built-in functions and packages.

Since Lua Smart Contract is performed in Blockchain, OS related functions including input / output are not provided for stability and security.

Here is a list of the main functions that are not available.

print, loadstring, dofile, loadfile and module

Here is a list of the default packages that are not available.

coroutine, io, os and debug

The string, math, and table packages are available. However, you can not use the random, randomseed functions in the math package.

DB package

If the smart contract is handling simple types of data, it would not be difficult to implement using only the basic APIs (getItem(), setItem()). However, complex data structures, data association, scope queries, filtering, sorting, and other features require the complexity and size of the data logic so developers can not focus on critical business logic. To solve this problem, Aergo supports SQL. This section details the types and usage of SQL APIs available in smart contracts

Note: The db package is currently only available on private networks and publicly on SQL TestNet.

exec(sql)

This function performs DDL or DML statements

query(sql)

This function performs SELECT statements and returns a result set object

prepare(sql)

This function creates a prepared statement and returns a statement object

-- create customer table 
function createTable()
  db.exec([[create table if not exists customer(
        id text,
        passwd text,
        name text,
        birth text,
        mobile text
    )]])
end

function insert(id, passwd, name, birth, mobile)
  db.exec("insert into customer values (?, ?, ?, ?, ?)",
      id, passwd, name, birth, mobile)
end

Functions of result set object

Object functions must be called with the : operator

get

This function returns a result row

function query(id)
  local rt = {}
  local rs = db.query("select * from customer where id like '%' || ? || '%'", id)
  while rs:next() do 
    local col1, col2, col3, col4, col5 = rs:get()
    local item = {
        id = col1,
        passwd = col2,
        name = col3,
        birth = col4,
        mobile = col5
    }
    table.insert(rt, item)
  end
  return rt
end

Functions of prepared statement object

You can use the parameters in the SELECT or DML statement to view, add, modify, or delete information.

query(bind1, bind2, ….)

This function executes a SELECT statement using the specified argument values corresponding to bind parameters and returns a result set object

exec(bind1, bind2, ….)

This function executes a DML statement using the specified argument values corresponding to bind parameters

SQL Restrictions

Smart Contract’s SQL processing engine is built on SQLite. Therefore, detailed SQL usage grammar can be found at https://sqlite.org/lang.html and https://sqlite.org/lang_corefunc.html. However, because of the stability and security of the Aergo, not all SQL is allowed.

types

Allow only SQL datatypes corresponding to Lua strings and numbers (int, float).

  • text
  • integer
  • real
  • null
  • date, datetime

SQL statement

The allowed SQL statements are listed below. However, DDL and DML are only allowed in smart contract transactions.

  • DDL
    • TABLE: ALTER, CREATE, DROP
    • VIEW: CREATE, DROP
    • INDEX: CREATE, DROP
  • DML
    • INSERT, UPDATE, DELETE, REPLACE
  • Query
    • SELECT

Function

The following is an unavailable list

  • Data and time related functions can be used except ‘now’ timestring and ‘localtime’ modifier
  • load_XXX function
  • random function
  • sqlite_XXX function

For a list of other functions and descriptions, please refer to the links below.

Constraint

The following constraints can be used.

  • NOT NULL
  • DEFAULT
  • UNIQUE
  • PRIMARY KEY(FOREIGN KEY)
  • CHECK
function init_database()
    db.exec("drop table if exists customer")
    db.exec("create table customer (cid integer PRIMARY KEY ASC AUTOINCREMENT , passwd text , cname text, birthdate date, rgdate date)")
    db.exec("insert into customer (cid, passwd, cname, birthdate, rgdate) values (100 ,'passwd1','홍길동', date('1988-01-03'),date('2018-05-30'))")
    db.exec("insert into customer (passwd, cname, birthdate, rgdate) values ('passwd2','김철수', date('1978-11-03'),date('2018-05-30'))")
    db.exec("insert into customer (passwd, cname, birthdate, rgdate) values ('passwd3','이영미', date('1938-04-23'),date('2018-05-30'))")

    db.exec("drop table if exists product")
    db.exec("create table product (pid integer PRIMARY KEY ASC AUTOINCREMENT, pname text, price real, rgdate date)")
    db.exec("insert into product (pid, pname, price, rgdate) values (1000 ,'사과',1000, date('2018-05-30'))")
    db.exec("insert into product (pname, price, rgdate) values ('수박',10000, date('2018-05-30'))")
    db.exec("insert into product (pname, price, rgdate) values ('포도',3500, date('2018-05-30'))")

    db.exec("drop table if exists order_hist")
    db.exec("create table order_hist (oid integer PRIMARY KEY ASC AUTOINCREMENT, cid integer, pid integer, p_cnt integer, total_price real, rgtime datetime, FOREIGN KEY(cid) REFERENCES customer(cid), FOREIGN KEY(pid) REFERENCES product(pid) )")
    db.exec("insert into order_hist(oid, cid, pid,  p_cnt, total_price,rgtime) values(10000,100,1000,3,3000, datetime('2018-05-30 16:00:00'))")
    db.exec("insert into order_hist(cid, pid, p_cnt, total_price, rgtime) values(100,1000,3,3000, datetime('2018-05-30 17:00:00'))")
    db.exec("insert into order_hist(cid, pid, p_cnt, total_price, rgtime) values(100,1000,3,3000, datetime('2018-05-30 18:00:00'))")
end

json package

Json package is provided for user convenience in input and output. This package allows automatic conversion between JSON format strings and Lua Table structures.

encode(arg)

This function returns a JSON-formatted string with the given lua value.

decode(string)

This function converts a string in JSON format to the corresponding Lua structure and returns it

crypto package

sha256(arg)

This function computes the SHA-256 hash of the argument.

ecverify(message, signature, address)

This function verifies the address associated with the public key from elliptic curve signature.

function validate_sig(data, signature, address)
    msg = crypto.sha256(data)
    return crypto.ecverify(msg, signature, address)
end

bignum package

Since the lua number type has a limit on the range that can be represented by an integer (less than 2 ^ 53), the bignum module is used to provide an exact operation for larger numbers.

  • Notice
    • == Operations on bignum and other types always return false.
    • Bignum does not allow a decimal point.
    • Bignum value range : -(2^256 - 1) ~ (2^256 -1)

number(x)

This function make bignum object with argument x(string or number)

isneg(x)

Check bignum x if negative than return true else false

iszero(x)

Check bignum x if zero than return true else false

tonumber(x)

Convert bignum x to lua number

tostring(x) (bignum.tostring(x) same as tostring(x))

Convert bignum x to lua string

neg(x) (same as -x)

Negate bignum x and return as bignum

sqrt(x)

Returns the square root of a positive number as bignum. not permitted negative value to x

compare(x, y)

Compare two big numbers. Return value is 0 if equal, -1 if x is less than y and +1 if x is greater than y.

add(x, y) (same as x + y)

Add two big numbers and return bignum

sub(x, y) (same as x - y)

Subtract two big numbers and return bignum

mul(x, y) (same as x * y)

Multiply two big numbers and return bignum

mod(x, y) (same as x % y)

Returns the bignum remainder after bignum x is divided by bignum y

div(x, y) (same as x / y)

Divide two big numbers and return bignum

pow(x, y) (same as x ^ y)

Power of two big numbers and return bignum. not permitted negative value to y

divmod(x, y)

Returns a pair of big numbers consisting of their quotient and remainder

powmod(x, y, m)

Return the bignum remainder after pow(bignum x,bignum y) is divided by bignum m. not permitted negative value to y

isbignum(x)

Return true if x is bignum else false

tobyte(x)

Return byte string of bignum x. not permitted negative value to x

frombyte(x)

Return bignum from byte string x

function factorial(n,f)
 for i=2,n do f=f*i end
 return f
end
for i=1,30 do
  local b=factorial(i,bignum.number(1))
  return bignum.mod(b, 10)
end