Type hinting is highly recommended for the input parameters and return value. When querying SCORE's APIs, API specification is generated based on its type hints. If type hints are not given, only function names will return.
readonly=True) def func1(arg1: int, arg2: str) -> int: return 100 (
Possible data types for function parameters are
Dict type parameters are not supported yet.
Returning types can be
When you handle exceptions in your contract, it is recommended to use revert function rather than using an exception inherited from IconServiceBaseException.
Every classes must inherit
IconScoreBase. Contracts not derived from
IconScoreBase can not be deployed.
This is a python init function. This function is called when the contract is loaded at each node.
Member variables can be declared here, however, Declaring member variables which not managed by states is prohibited.
Utilities of DB such as VarDB, DictDB, ArrayDB can be declared here as a member variable as follows.
self._total_supply = VarDB(self._TOTAL_SUPPLY, db, value_type=int) self._decimals = VarDB(self._DECIMALS, db, value_type=int) self._balances = DictDB(self._BALANCES, db, value_type=int)
Also, parent's init function must be called as follows.
This function is called when the contract is deployed for the first time, and will not be called again on contract update or deletion afterward.
This is the place where you initialize the state DB.
VarDB, DictDB, ArrayDB are utility classes wrapping the state DB.
key can be a number or characters, and
value_type can be
key does not exist, these classes return 0 when
int, return "" when
None when the
VarDB can be used to store simple key-value state, and DictDB behaves more like python dict.
DictDB does not maintain order, whereas ArrayDB, which supports length and iterator, maintains order.
theloop for the key
name on the state DB:
VarDB('name', db, value_type=str).set('theloop')
Example) Getting value by the key
name = VarDB('name', db, value_type=str).get() print(name) ## 'theloop'
Example 1) One-depth dict (test_dict1['key']):
test_dict1 = DictDB('test_dict1', db, value_type=int) test_dict1['key'] = 1 ## set print(test_dict1['key']) ## get 1 print(test_dict1['nonexistence_key']) # prints 0 (key does not exist and value_type=int)
Example 2) Two-depth dict (test_dict2['key1']['key2']):
test_dict2 = DictDB('test_dict2', db, value_type=str, depth=2) test_dict2['key1']['key2'] = 'a' ## set print(test_dict2['key1']['key2']) ## get 'a' print(test_dict2['key1']['nonexistent_key']) # prints "" (key does not exist and value_type=str)
If the depth is more than 2, dict[key] returns new DictDB.
Attempting to set a value to the wrong depth in the DictDB will raise an exception.
test_dict3 = DictDB('test_dict3', db, value_type=int, depth=3) test_dict3['key1']['key2']['key3'] = 1 ## ok test_dict3['key1']['key2'] = 1 ## raise mismatch exception test_dict2 = test_dict3['key']['key2'] test_dict2['key1'] = 1 ## ok
ArrayDB supports one dimensional array only.
ArrayDB supports put, get, and pop. Does not support insert (adding elements in the middle of array).
test_array = ArrayDB('test_array', db, value_type=int) test_array.put(0) test_array.put(1) test_array.put(2) test_array.put(3) print(len(test_array)) ## prints 4 print(test_array.pop()) ## prints 3 test_array = 0 ## ok # test_array = 1 ## error for e in test_array: ## ok print(e) print(test_array[-1]) ## ok # print(test_array[-100]) ## error
Functions decorated with
@external can be called from outside the contract. These functions are registered on the exportable API list.
Any attempt to call a non-external function from outside the contract will fail.
If a function is decorated with 'readonly' parameters, i.e.,
@external(readonly=True), the function will have read-only access to the state DB. This is similar to view keyword in Solidity.
If the read-only external function is also decorated with
@payable, the function call will fail.
Duplicate declaration of
@external will raise IconScoreException on import time.
Only functions with
@payable decorator are permitted to transfer icx coins.
Transferring 0 icx is acceptable.
If msg.value (icx) is passed to non-payable function, the call will fail.
@eventlog decorator will include logs in its TxResult as 'eventlogs'.
It is recommended to declare a function without implementation body. Even if the function has a body, it does not be executed.
When declaring a function, type hinting is a must. Without type hinting, transaction will fail. The default value for the parameter can be set.
indexed parameter is set in the decorator, designated number of parameters in the order of declaration will be indexed and included in the Bloom filter. At most 3 parameters can be indexed, And index can't exceed the number of parameters(will raise an error).
Indexed parameters and non-indexed parameters are separately stored in TxResult.
# Declaration def FundTransfer1(self, _backer: Address, _amount: int, _isContribution: bool): pass (indexed=1) # The first param (backer) will be indexed def FundTransfer2(self, _backer: Address, _amount: int, _isContribution: bool): pass # Execution self.FundTransfer1(self.msg.sender, amount, True) self.FundTransfer2(self.msg.sender, amount, True)
Possible data types for function parameters are primitive types (int, str, bytes, bool, Address).
Array, Dictionary and None type parameter is not supported.
fallback function can not be decorated with
@external. (i.e., fallback function is not allowed to be called by external contract or user.)
This fallback function is executed whenever the contract receives plain icx coins without data.
If the fallback function is not decorated with
@payable, the icx coin transfers to the contract will fail.
InterfaceScore is an interface class used to invoke other SCORE's function.
This interface should be used instead of legacy 'call' function.
Usage syntax is as follows.
class TokenInterface(InterfaceScore): def transfer(self, addr_to: Address, value: int) -> bool: pass
If other SCORE has the function that has the same signature as defined here with
then that function can be invoked via InterfaceScore class object.
@eventlog decorator, it is recommended to declare a function without implementation body.
If there is a function body, it will be simply ignored.
You need to get an InterfaceScore object by using IconScoreBase's built-in function
create_interface_score('score address', 'interface class').
Using the object, you can invoke other SCORE's external function as if it is a local function call.
token_score = self.create_interface_score(self._addr_token_score.get(), TokenInterface) token_score.transfer(self.msg.sender, value)
This function returns an object, through which you have an access to the designated SCORE's external functions.
- msg.sender :
Address of the account who called this function.
If other contact called this function, msg.sender points to the caller contract's address.
- msg.value :
Amount of icx that the sender attempts to transfer to the current SCORE.
- tx.origin : The account who created the transaction.
- tx.index : Transaction index.
- tx.hash : Transaction hash.
- tx.timestamp : Transaction creation time.
- tx.nonce : (optional) random value.
icx.transfer(addr_to(address), amount(integer)) -> bool
Transfers designated amount of icx coin to
If exception occurs during execution, the exception will be escalated.
Returns True if coin transfer succeeds.
icx.send(addr_to(address), amount(integer)) -> bool
Sends designated amount of icx coin to
Basic behavior is same as transfer, the difference is that exception is caught inside the function.
Returns True when coin transfer succeeded, False when failed.
Developer can force a revert exception.
If the exception is thrown, all the changes in the state DB in current transaction will be rolled back.
Computes hash using the input
Converts a python object
obj to a JSON string
Parses a JSON string
src and converts to a python object
- prefix: AddressPrefix.EOA(0) or AddressPrefix.CONTRACT(1)
- body: 20-byte address body part
- is_contract: Whether the address is SCORE
- to_bytes(self) -> bytes:
Returns data as bytes from the address object
address: str) -> Address:
A static method creates an address object from given 42-char string
- from_data(prefix: AddressPrefix, data: bytes) -> Address:
A static method creates an address object(type of
prefix) using given bytes