# 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
end
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
end
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
end
function M:on_destroy()
-- Optional callback function triggered when the contract is destroyed
end
function M:on_upgrade()
-- Optional callback function triggered when the contract is upgraded to a formal contract
end
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
end
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 Contract
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
end
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)
end
self.name = 'hello' -- Error, the id/name/storage attributes of the contract are all read-only attributes and
cannot be modified
end
function M:query()
pprint('query demo')
end
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