# Contract definition

# A basic contract format

type Storage = {
    -- The storage attributes in the contract are defined here, such as name: string

var M = Contract<Storage>()

function M:init()
    -- Add contract initialization logic here
    -- The storage of the contract must be initialized in this function

function M:on_deposit(num: int)
    -- Optional transfer function to the contract's callback function (single asset chain mode),this callback will be triggered when the user transfers to the contract, this function can not be written if not needed

function M:on_deposit_asset(arg: string)
    -- Optional transfer function to the contract (multi-asset chain mode), the parameter is "asset identification, transfer amount without precision", this callback will be triggered when the user transfers to the contract, this function can not be written if it is not needed

function M:on_destroy()
    -- Optional callback function triggered when the contract is destroyed

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

function M:demoApi1(arg1: string)
    --  Here is an example user-defined API function. A contract can have multiple custom API functions. demoApi1 is the function name here. The custom API function comes with a self variable to indicate the current contract
    -- In addition, there can be 0 parameters or a string type parameter

return M   -- Contract External API

# Contract External API

For Contract<Storage>objects defined in the form, each of its functions can be called externally (except for some special predefined methods), which we call the contract API. If the function keyword is decorated with the offline keyword, the method can only be called locally, and the result cannot be broadcast to the chain. The offline interface does not require payment.

The purpose of providing offline interface is to provide local query service more conveniently, so that you can query contract status without paying any GAS.

# Contract global variables

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 call.

# Contract Global Method

# How to call a contract that already exists on the chain in the contract

You can use the import_contract/import_contract_from_address function to refer to other formal contracts on the chain and return the object representing the referenced contract, so that the referenced user-defined API can be called through the returned object.

However, you cannot directly access the init/on_deposit/on_destroy/on_upgrade and storage objects of the referenced contract, only by calling the API

such as:

let demo = import_contract 'demo'
demo:hello("China")  -- This calls the hello function API of the official contract named demo, using "China" as the parameter

# Use of the built-in module of the contract

The module of the built-in library can be used directly in the contract without requiring Import library by default


# The entire life cycle process of a contract

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

A smart contract will always exist after it is registered on the chain. As long as it is not destroyed, its methods can be called at any time.

# Contract-defined constraints

• As a special module, the contract cannot define global variables and cannot modify the values of _ENV and _G. You can load the contract through import_contract'contract name' and return the information of the loaded contract module. The contract must return an object of record type. Represents the contract API, which must contain an init function. The contract has built-in attributes such as id, name, storage, etc. Be careful not to use the API with these names, otherwise it will be overwritten. • In the contract code, the contract object as a record type must return the record object at the end of the contract code. The return object represents the contract. If the contract storage is used in the contract, because the syntax has static type checking, you need to give the contract The storage attribute declares a type • The three attributes of the contract's id/name/storage are all provided by the blockchain during execution, and the three attributes themselves are read-only, but the content of the storage attribute can be changed • Storage need to declare the contract for the record type, the type of each attribute record type of storage can only be int, Number The, BOOL, String, Map<int>, Map<number>, Map<bool>, Map<string>, Array<int>, Array<number>, Array<bool>, Array<string>in which a certain kind of • The built-in library has a Contractgeneric type that can be used as the base class of the contract type. When specifically used, the variable to be returned by the contract can be declared as the instance type of Contract (you need to provide a record type as the contract storage type and as the contract type variable)

such as

type Storage = {               -- A record type is declared here, which is used as the storage type of the contract, and the name is customized
    name: string,                    -- This is just a type declaration. The self.storage of the contract still needs to be assigned to each item before it can be used.
    age: int,
    age2: int default 24,                -- The default value here has no effect on the initial storage value of the contract, because 

the storage of the contract is initialized by the blockchain

    error_property: int | string         -- This attribute will cause a compilation error because the storage record type attribute has 

type restrictions


let M = Contract<Storage>()    --- The contract type declared here is Contract<Storage>, and 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 a record type, to declare a member function of M, you can only use 

function M: funcName, not function M.funcName

    pprint("contract init running",self.id, self.name)        -- The self here refers to the value of the current object, which is the contract object here
    self.storage.name = 'hi'        -- Because the name attribute of the storage is used as a string type, the name attribute of the 

above Storage type should be declared as a string type

    let storage = self.storage
    storage.age = 100                -- Even if self.storage is assigned to other variables, the type of storage is still the Storage type 

declared above, and the type will be checked at compile time


function M:testSomeLogic()
    let contract2 = import_contract 'contract2'   --  Here you need to quote the contract name that has been on the chain. If you use a 

non-existing contract name, an error will be reported during compilation.

    contract2.storage.name = 'glua' -- This will report an error during compilation, because the storage of other contracts 

referenced in the contract cannot be directly manipulated

    self.storage.age = self.storage.age + 1
    if self.storage.age < 100 then
        transfer_from_contract_to_address('Fill in the target address here', 'HSR', 10000000)
    self.name = 'hello'            -- Error, the id/name/storage attributes of the contract are all read-only attributes and 

cannot be modified


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

return M
  • The contract can not directly operate the storage of other contracts referenced, nor can it call the init, on_deposit, on_upgrade, on_destroy APIs of the contract itself or other contracts.
  • When the contract code is compiled, the code outside the contract API will be loaded once, so if the code outside the contract API has runtime problems, it will also report an error when compiling the contract