Definition of contract

A basic contract format

type Storage = {
    -- The properties of the storage in the contract are defined here. For example, name: string
}

var M = Contract<Storage>()

function M:init()
    -- Join the contract initialization logic here.
    -- Contract storage must be initialized in this function.
end

function M:on_deposit(num: int)
    -- Optional transfer to the contract callback function. When the user transfer to the contract will trigger the callback. You don't need to write the function if unnecessary.

end

function M:on_destroy()
    -- Optional callback function when the contract was destroyed
end

function M:on_upgrade()
    -- Optional callback function when a contract is upgraded to a formal contract
end

function M:demoApi1(arg1: string)
    -- Here is an example of a user-defined API function. A contract can have multiple custom API functions. demoApi1 is the name of the function here. The custom API function comes with a self variable representing the current contract.
    -- There may also be 0 arguments or a string type parameter
end

return M   -- Its necessary, indicateing which object is used to represent this contract.

global variable of contract

In the contract, the caller and caller_address global variables can be used to access the public key and address of the user who initiated the contract.

global method of contract

API of Contract

  • Use the global function ‘transfer_from_contract_to_address’ to transfer an amount of an asset to an address from the current contract (the contract where the function calls the code).The first argument is the destination address(string),The second parameter is asset name(for example ACT),The third parameter is 100,000 times the number of transfers(int64 type),must be positive.
Return value
     0   Transfer successfully
    -1  Unknown system exception
    -2  Asset_symbol exception
    -3  Contract address is illegal
    -4  Destination address is illegal
    -5  Account balance is insufficient to pay the transfer
    -6  The transfer amount is negative
  • Use the global function ‘get_contract_balance_amount’ to get a contract with the accuracy of the balance(accuracy is 100000),The first argument is the contract address(Support the balance of other contracts).The second parameter is asset name(for example ACT),Returns the balance of the contract with precision(int64 type),If there is an error or the contract does not exist, return a negative number.
Return value
    Non-negative number balance of contract account
    -1  Asset id exception
    -2  Contract address exception
  • Use the global function ‘get_chain_now’ to get the current time on the blockchain.No input parameters.
Return value
    positive number  Timestamp
    0     System exception
  • Use the global function ‘get_chain_random’ to get a pseudo-random number on the blockchain.But the same operation on this blockchain, different nodes at different times will get the same return result.(The actual operation is taken on the block prev_secret_hash and the deal is combined with the hash)
Return value
    Random result
  • Use the global function’ get_header_block_num’ to get the block number of the previous block.
Return value
    The block number of the current chain.
  • Use the global function ‘get_waited(num)’ to get a pseudo-random number from the data of the future block.<num> is the block number of the future block(But need to call again in the future.At that time the first num block is the past block will be able to know the results)
Return value
    positive number result value
    -1  The target block is not reached
    -2  The target block is less than 1
  • Use the global function ‘get_current_contract_address’ to get the contract address where it is called. No input parameters.
  • The global variable caller stores the public key of the user who call the contract. The global variable caller_address stores the account address of the user who call the contract
  • When transfer to contract,If the contract defines ‘on_deposit’(The parameter is the transfer amount),the API will be called after the transfer occurs. And ensure that the transfer and trigger this API is atomic. If failed, the overall rollback, transfer failure.
  • Use the statement emit EventName(arg: string) to throw an event. emit is a keyword. Write the event name as needed into EventName, Recorded by the block chain. Other nodes to sync to emit triggered event can call the local settings callback.
  • Use global function ‘get_transaction_fee()’ to get a transaction fee.
Return value
    positive number result value
    -1  Asset id exception
    -2  System exception
  • Use global function ‘transfer_from_contract_to_public_account(to_account_name: string, asset_type: string, amount: int)’ to transfer from the current contract to the account name on the chain. Return the state of transfer.
Return value
    0   Transfer successful
   -1  Unknown system exception
   -2  Asset_symbol exception
   -3  Contract address is illegal
   -4  Destination address is illegal
   -5  Account balance is insufficient to pay the transfer
   -6  Transfer amount is negative
   -7  Account name does not exist

Call an existed contract in a new contract

You can reference other contracts on the chain by the function ‘import_contract’ . The function returns the object representing the referenced contract so that the referenced user-defined API can be called by this returned object.

But can not directly access the variable init / on_deposit / on_destroy / on_upgrade and the storage object. They can be accessed by calling API.

For example:

let demo = import_contract 'demo'
demo:hello("China")  -- Here we call hello function API of the official contract demo,using "China" as a parameter.

The built-in module

The module can be used directly in the contract. Do not need to require.

table Module

table.concat(table, sep, start=1, end=the length of the array part of the table) Separate the table from <start> to <end> and connect each item with <seq>.Return the string after slicing.

table.insert(table, pos, value) Insert a value at the <pos> position of the array . If only pass 2 parameters <table> and <value>,insert the value after the last position of the array. No return value.

table.append(table, value) Insert the value after the last position of the array. No return value.

table.length(table) Get the length of the array portion of the array

table.remove(table, pos=the length of the array part of the table) Delete and return the array part of the element in the <pos> position. Subsequent elements will be moved forward. Parameter <pos> is optional and its default value is the length of the table. That is, delete from the last element.This method returns the deleted value.

table.sort(table) Sort the given table in ascending order.Element type in the table need to be the same. No return value.

For Example:

> tbl = {"alpha", "beta", "gamma", "delta"}
> table.sort(tbl)
> print(table.concat(tbl, ", "))
alpha, beta, delta, gamma

Global function rawlen. Return the number of array part in the table.

math module

math.abs(n) Gets the absolute value of parameter <n>.

math.ceil(n) Return the smallest integer that is not less than <n>.

math.floor(n) Return the largest integer that does not exceed <n>.

math.max(n1,n2,…) Return the maximum value of multiple values in the parameter list, requiring at least one parameter.

math.maxinteger The maxium integer.

math.min(n1,n2,…) Return the minimum value of multiple values in the parameter list, requiring at least one parameter.

math.mininteger The minimum integer.

math.pi Constant pi, 3.14159265358…

math.sqrt(n) Get the square root of <n>

math.tointeger(n) Convert the parameter <n> into an integer. If <n> itself is an integer, directly convert <n> into an integer. If you can not convert, return nil

math.type(num) Determine whether the first argument <num> is an integer or a floating point.

string module

string.split(str, sep) Divide <str> by <sep> into multiple strings. Return a string array.

string.byte(s) Return the ASCII number of the first character of the string.

string.char(i1, i2, …) Return the string constructed by the ascii characters corresponding to input integers.

string.find(str, pattern, [init=1, [plain=nil]]) Find the <pattern> string in the <str>. Start from position <init>. <plain> Indicates whether the <pattern> is used as a plain text string instead of a pattern string.Returns the first character index of the first substring that satisfies the condition or nil.

The pattern string can be used to match some of the substrings in the source string with the following symbols.

  • .(point): Match any character.
  • %a: Match any letter.
  • %c: Match any control character (for example : n)
  • %d: Match any number.
  • %l: Match any lower letter.
  • %p: Match any punctuation.
  • %s: Match any blank character.
  • %u: Match any capital letter.
  • %w: Match any letter or number.
  • %x: Match any hexadecimal number.
  • %z: Match any character that represents 0.
  • %x( <x> is a non-alphabetic non-numeric character): Match character <x>. Mainly used to deal with the expression of the characters(^$()%.[]*+-?) in the expression of the matching problem. For example, match ‘%%’ and ‘%’.
  • [Several character classes]: Match any character contained in []. for example [%w_] match with any letter or number. Or match with underline.
  • [^Several character classes]: Match any character not contained in []. for example [^%s] match any non-whitespace character.

For example:

let p1 = "%d%d:%d%d"
let s = "2016/11/11 11:11"
let a = string.find(s, p1)  -- The result of a is 11.That is, the index of the first character of the substring "11:11".
let b = string.sub(s, a)    -- The result of b is "11:11".
let c = string.find(s, p1, 3)  -- The result of c is 12.
let d = string.find(s, p1, 1, true)  -- The result of d is nil.Because the fourth argument is true, so we regard <p1> as a normal string to match.

string.format(formatstring, …args) Similar to method sprintf in C language.

For example:

let a = string.format("hello, %s, the number is %d", "China", 123)

string.gmatch(str, pattern) Return the iterator that traverses the pattern string pattern in <str>.

For example:

t = {}
s = "from=world, to=Lua"
var k = nil
var v = nil
for k, v in string.gmatch(s, "(%w+)=(%w+)") do
   t[k] = v
end

string.gsub(str, pattern, replacer, [n]) Replace the sub-string in <str> that match <pattern> with <replacer> or function.

string.len(str) Get the length of <str>.

string.match(str, pattern, [init=1]) Find the first substring the matches <pattern>.Starting from position <init>.

string.rep(str, n, [sep=’’]) Return the result of <str> repeating <n> times separate by <sep>.

string.reverse(str) Return the reverse of <str>.

string.sub(str, i, [,j=-1]) Get the substring of <str> from character <i> to <j> (including <i> and <j>).<i> and <j> can be negative,representing the <i>th and <j>th character of <str> from opposite position.

string.upper(str) Convert characters in <str> into capital and return.

time module

time.add(timestamp, field, offset) Return a new timestamp. <field> is one of the string in year/month/day/hour/minute/second. <offset> can be changing from postive, zero to negative.

time.tostr(timestamp) Convert timestamp into string. The format is %yy-%m-%d %H:%M:%S.

time.difftime(timestamp1, timestamp2) Compare the number of seconds between 2 timestamps.

json module

json.dumps(any lua value) Change the lua value to a json string.

json.loads(string) Change the json string to lua.If failed ,return nil

utf8 module

utf8.char(…) Accepts several numbers in the parameter.Return the corresponding UTF8 encoded byte sequence.

utf8.charpattern Constant,Can match a string pattern of UTF8 byte sequence.

utf8.codes(s) Return an iterator of all the characters in the variable <s>, encoded by utf8.

utf8.codepoint(s, i, j) Returns the character string of utf8 encoded within string [i, j]

utf8.len(s, i, j) Returns the character length of the string <s> in the index [i, j] encoded by utf8.

utf8.offset(s, n, i) Returns the byte index from the i-th byte of the nth character in <s>.

The entire lifecycle process of a contract

  • Write a contract
  • Compile contract
  • Registration contract to become a temporary contract on the blockchain
  • Upgrade contract to become a formal contract / destroy temporary contract
  • Call the contract API
  • Transfer to contract

Constraints on contractual definition

  • As a special module,the global variable can not be defined in the contract. The value of _ENV, _G can not be modified. You can load a contract by function ‘importing_contract(contract name)’ Returns the loaded contract module information. The contract must return a record type of object,representing the api of the contract, which must contain an init function。Contract has id, name, storage and other built-in properties. Be careful not to use these names API, Otherwise they will be covered.
  • In contract code, as a record type of the contract object , you must return the record object at the end of the contract code. The return object represents this contract. If the contract storage is used in the contract, because the syntax has a static type check,you need to declare a type for the contract’s storage property.
  • The contract’s id / name / storage properties are provided by the blockchain at the time of execution and the three properties themselves are read-only. But the contents of the storage property can be changed
  • Contract storage needs to be declared as record type. The type of the record’s record type can only be one of int, number, bool, string, Map<int>, Map<number>, Map<bool>, Map<string>, Array<int>, Array<number>, Array<bool>, Array<string>.
  • The built-in library has a Contract <T> generic that can be used as a base class for contract types. The variable that returned by the contract can be declared as an instance type of the contract.(Need to provide a record type as the type of contract storage and contract type variable)

For example

type Storage = {               -- Here we declare a record type that is used as the type of storage for the contract. Name customization
    name: string,                    -- Type declaration. self.storage of contract need to be initalized.
    age: int,
    age2: int default 24,                -- The default value here does not work for the initial storage value of the contract, because the contract storage is initialized by the blockchain.
    error_property: int | string         -- This attribute will cause compilation errors,because the storage record type attribute has a type limit
}

let M = Contract<Storage>()    --- The type of contract here is Contract <Storage>. Contract <T> is a generic type where the generic parameter type T is the type of the record attribute

function M:init()                    -- Because the M variable is the record type, declaration of M member function can only use statment M: funcName way, and can not use function M.funcName way
    pprint("contract init running",self.id, self.name)        --  self refers to the current object, that is the value of the contract object here
    self.storage.name = 'hi'        -- Because the name attribute of the storage is used as a string type,the above storage type name attribute to be declared as string type
    let storage = self.storage
    storage.age = 100                -- Even if self.storage is assigned to other variables, the storage type is still the type of storage declared above. Type will be examined during the compilation time.
end

function M:testSomeLogic()
    let contract2 = import_contract 'contract2'   -- Here you need to refer to the name of the contract has been linked. If you use a contract name that does not exist, it will report an error.
    contract2.storage.name = 'Achain' -- This will cause compile the error. Because the contract can not directly operate the storage of other contracts cited.
    self.storage.age = self.storage.age + 1
    if self.storage.age < 100 then
        transfer_from_contract_to_address('Fill in the destination address here', 'ACT', 10000000)
    end
    self.name = 'hello'            -- error,The contract's id / name / storage attribute is read-only, can not be modified.
end

function M:query()
    pprint('query demo')
end

return M
  • The contract can not be directly operated by the other contract of the storage and can not call the contract itself or other contract init, on_deposit, on_upgrade, on_destroy API. or it will report an error.
  • When the code is compiled, it will load the code outside the contract API. So if the contract API outside the code has run-time problems will be in the contract when the error.