Blueprint SDK — API
Introduction
This article is the reference for the Python API provided by the blueprint SDK. The blueprint SDK Python API comprises all names from package hathor.nanocontracts
that can be imported in a blueprint module.
This API reference is divided into the following sections:
- Type aliases: simplify handling of the most common data types in contracts.
- Classes: define the structure required by Hathor engine for a blueprint module.
- Decorators: used to define the type of a method in the blueprint class.
- Objects: describe the interface of internal objects from the available classes. That is, you can use them through instances of available classes, but you cannot import and use their classes directly.
Not all names from the hathor.nanocontracts
package can be imported into a blueprint module. Only strictly allowed names may be imported. For the list of these names, see Imports at Blueprint development guidelines.
This article refers to the following version of the Blueprint SDK: branch experimental/nano-testnet-v1.8.0
of hathor-core
repository at GitHub.
Note that the Blueprint SDK is an experimental phase and evolving rapidly. Newer versions may include features not yet covered here. Moreover, backward compatibility is not guaranteed, and they may change the behavior described in this article.
This article is in final review stage. While most of its content is accurate, some errors may still be present.
Type aliases
Source code: type aliases in hathor.nanocontracts.types
Address
- Description: wallet address
- Alias for:
bytes
- Constraint: 20 bytes
- Example:
b'\x28\x1e\xd3\x2c\xcd\x0d\x28\xac\xea\x3a\xfc\xf4\x79\x8e\x2d\x4d\xb2\x14\x01\xed\xef\xa9\xa8\x98\x22'
- Usage:
from hathor.nanocontracts.types import Address
...
address: Address
...
# In base58check this wallet address is: H9L7do74fkLF7VHa662a4huGni1zKW4EGZ
address = b'\x28\x1e\xd3\x2c\xcd\x0d\x28\xac\xea\x3a\xfc\xf4\x79\x8e\x2d\x4d\xb2\x14\x01\xed\xef\xa9\xa8\x98\x22'
Amount
- Description: denotes an amount of tokens
- Alias for:
int
- Constraint: integer where the last two digits are decimals
- Example:
1025
represents 10.25 tokens - Usage:
from hathor.nanocontracts.types import Amount
...
amount: Amount
...
amount = 1025
BlueprintId
- Description: identifier of a blueprint. For built-in blueprints, ids are hardcode with the network's environment variables (testnet, mainnet, etc.). For on-chain blueprints, the id is the hash of the transaction that deployed it.
- Alias for:
VextexId
- Constraint: 32 bytes
- Example:
bytes.fromhex("3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595")
- Usage:
from hathor.nanocontracts.types import BlueprintId
...
blueprint: BlueprintId
...
blueprint = bytes.fromhex("3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595")
ContractId
- Description: identifier of a contract. It is recorded in the transaction where it was created.
- Alias for:
VextexId
- Constraint: 32 bytes
- Example:
bytes.fromhex("000063f99b133c7630bc9d0117919f5b8726155412ad063dbbd618bdc7f85d7a")
- Usage:
from hathor.nanocontracts.types import ContractId
...
contract: ContractId
...
contract = bytes.fromhex("000063f99b133c7630bc9d0117919f5b8726155412ad063dbbd618bdc7f85d7a")
Timestamp
- Description: timestamp following Unix epoch standard
- Alias for:
int
- Constraint: integer representing the number of seconds elapsed since January 1st, 1970 00:00:00 UTC.
- Example: timestamp for January 3, 2020, at noon UTC is
1578052800
- Usage:
from hathor.nanocontracts.types import Timestamp
...
timestamp: Timestamp
...
timestamp = 1578052800
TokenUid
- Description: identifier of a contract. It is recorded in the transaction where it was created.
- Alias for:
bytes
- Constraint: 32 bytes
- Example:
bytes.fromhex("00000943573723a28e3dd980c10e08419d0e00bc647a95f4ca9671ebea7d5669")
- Usage:
from hathor.nanocontracts.types import TokenUid
...
token: TokenUid
...
token = bytes.fromhex("00000943573723a28e3dd980c10e08419d0e00bc647a95f4ca9671ebea7d5669")
TxOutputScript
- Description: lock script of a transaction output
- Alias for:
bytes
- Constraint: 32 bytes
- Usage:
from hathor.nanocontracts.types import TxOutputScript
...
oracle: TxOutputScript
VertexId
- Description: identifier of a transaction in the ledger (blockchain). It is the hash of the transaction.
- Alias for:
bytes
- Constraint: 32 bytes
- Example:
bytes.fromhex("00000943573723a28e3dd980c10e08419d0e00bc647a95f4ca9671ebea7d5669")
- Usage:
from hathor.nanocontracts.types import VertexId
...
tx: VertexId
...
tx = bytes.fromhex("00000943573723a28e3dd980c10e08419d0e00bc647a95f4ca9671ebea7d5669")
NCAction
- Description: union type alias representing any of the four possible action types. Use for type annotations when a parameter, variable, or attribute can accept any action type.
- Alias for: one of the possible action classes:
NCDepositAction
,NCWithdrawalAction
,NCGrantAuthorityAction
, orNCAcquireAuthorityAction
. - Constraint: must be an instance of one of the classes above.
- Usage:
from hathor.nanocontracts.types import NCAction
...
actions: list[NCAction] = []
...
def fail_if_invalid_action(self, action: NCAction) -> None:
if action.token_uid != VALID_TOKEN:
... - Source code:
hathor.nanocontracts.types.NCAction
Classes
Blueprint
Serves as the base class for all blueprints. You must subclass Blueprint
exactly once in your blueprint module.
Usage:
from hathor.nanocontracts.types import Blueprint
...
# Define your blueprint class as subclass of Blueprint:
class Bet(Blueprint):
...
Objects:
Blueprint
provides contracts the following objects:
syscall
: instance attribute used for all external interactions outside the contract itself — e.g., reading and writing to the ledger, calling other contracts, etc. SeeBlueprint.syscall
at section Objects.log
: instance attribute used to record contract execution logs. Useful during development for debugging and testing, and in production for audits. SeeBlueprint.log
at section Objects.
Source code:
hathor.nanocontracts.blueprint.Blueprint
Context
Provides the public method with the context in which it was called — namely, who called it, when it was called, and what the origin transaction is. The origin transaction is the transaction that originated the current call chain; in other words, it contains the entry point call.
When Hathor engine invokes a public method, it always passes a Context
object as the first argument. Therefore, every public method must have a Context
object as its second parameter.
The Context
object is immutable, and what is passed is only a copy of the original maintained by Hathor engine. As such, you cannot, and should not attempt to, modify the call context in your blueprint's code.
Data attributes:
vertex
:VertexData
that contains the origin transaction.address
:Address
orContractId
that denotes who called the method (also known as the caller).timestamp
:Timestamp
that denotes the timestamp of the first block confirming the origin transaction.actions
:MappingProxyType
where the keys areTokenUid
and the values are tuples ofNCAction
objects. All actions requested in the current call. It may contain zero, one, or more elements.
Note that a token is associated with a tuple of actions. However, not all combinations of actions are possible for the same token. For example, Hathor engine will never pass to a public method a Context.actions
in which, for the same token UID (TokenUid)
, there are:
- Two actions (
NCAction
) of the same type (NCActionType
) — e.g., two deposits for the same token. - One
DEPOSIT
action and oneWITHDRAWAL
action. - One
GRANT_AUTHORITY
action and oneACQUIRE_AUTHORITY
action.
Method:
get_single_action
The following subsection describes the (only) method of this class that available to use in your blueprint.
Context.get_single_action
Given a token UID, returns the single NCAction
from actions
that should be associated with it — i.e., the action whose key matches the token. Otherwise, raises an exception.
get_single_action(self, token_uid: TokenUid) -> NCAction:
Parameter:
token_uid
:TokenUid
that identifies the single action.
Return: NCAction
if there is one and only one action associated with the token UID. If no action is found, raises an NCFail
exception. If more than one action is found, also raises an NCFail
exception.
Usage:
from hathor.nanocontracts.context import Context
...
# Second parameter of a public method is always a Context object:
@public(allow_deposit=True, allow_withdrawal=True)
def swap(self, ctx: Context) -> None:
"""Execute a token swap."""
# Check if there is exactly one action related to each token:
# You should use the method get_single_action for it:
action_a = ctx.get_single_action(self.token_a)
action_b = ctx.get_single_action(self.token_b)
...
Source code:
hathor.nanocontracts.context.Context
NCActionType
An enumeration (Enum
) that defines the possible types of actions a contract user (wallet or another contract) can perform on a contract's multi-token balance. Can be one of the following:
DEPOSIT
: deposit tokens into contract.WITHDRAWAL
: withdrawal tokens from contract.GRANT_AUTHORITY
: grant mint/melt authority to contract.ACQUIRE_AUTHORITY
: acquire mint/melt authority from contract.
For more about it, see Actions section at Nano contracts: how it works.
Usage:
from hathor.nanocontracts.types import NCActionType
...
# Checking the type of a given action
# You can use:
if not isinstance(action, NCDepositAction)
raise InvalidActions
# OR with NCActionType the equivalent:
if action.type != NCActionType.DEPOSIT:
raise InvalidActions
# Defining what actions a public method shall receive:
# You can use in the decorator:
@public(allow_deposit=True, allow_withdrawal=True)
# OR with NCActionType the equivalent:
@public(allow_actions=[NCActionType.DEPOSIT, NCActionType.WITHDRAWAL])
Source code:
hathor.nanocontracts.types.NCActionType
NCDepositAction
Used to create or validate actions of type DEPOSIT
.
Data attributes:
name
: always"DEPOSIT"
type
: alwaysNCActionType.DEPOSIT
token_uid
:TokenUid
that denotes the token the caller wants to deposit.amount
:Amount
that denotes the amount of tokens to be deposited.
Usage:
from hathor.nanocontracts.types import NCDepositAction
...
# Use it whenever you want to represent the specific deposit action:
# For example, to check if a given action is a deposit:
if not isinstance(action, NCDepositAction)
...
Source code:
hathor.nanocontracts.types.NCDepositAction
NCWithdrawalAction
Used to create or validate actions of type WITHDRAWAL
.
Data attributes:
name
: always"WITHDRAWAL"
type
: alwaysNCActionType.WITHDRAWAL
token_uid
:TokenUid
that denotes the token the caller wants to withdraw.amount
:Amount
that denotes the amount of tokens to be withdrawn.
Usage:
from hathor.nanocontracts.types import NCWithdrawalAction
...
# Use it whenever you want to represent the specific withdrawal action:
# For example, to check if a given action is a withdrawal:
if not isinstance(action, NCWithdrawalAction)
...
Source code:
hathor.nanocontracts.types.NCWithdrawalAction
NCGrantAuthorityAction
Used to create or validate actions of type GRANT_AUTHORITY
.
Data attributes:
name
: always"GRANT_AUTHORITY"
.type
: alwaysNCActionType.GRANT_AUTHORITY
.token_uid
:TokenUid
that denotes the token the caller wants to grant authority to contract.mint
:bool
; ifTrue
, contract will be allowed to issue new amounts of the token, increasing its total supply.melt
:bool
; ifTrue
, contract will be allowed to destroy amounts of the token, decreasing its total supply.
Usage:
from hathor.nanocontracts.types import NCGrantAuthorityAction
...
# Use it whenever you want to represent the specific grant authority action:
# For example, to check if a given action is a grant authority:
if not isinstance(action, NCGrantAuthorityAction)
...
Source code:
hathor.nanocontracts.types.NCGrantAuthorityAction
NCAcquireAuthorityAction
Used to create or validate actions of type ACQUIRE_AUTHORITY
.
Data attributes:
name
: always"ACQUIRE_AUTHORITY"
.type
: alwaysNCActionType.ACQUIRE_AUTHORITY
.token_uid
:TokenUid
that denotes the token the caller wants to grant authority to contract.mint
:bool
; ifTrue
, caller will be allowed to issue new amounts of the token, increasing its total supply.melt
:bool
; ifTrue
, caller will be allowed to destroy amounts of the token, decreasing its total supply.
Usage:
from hathor.nanocontracts.types import NCAcquireAuthorityAction
...
# Use it whenever you want to represent the specific acquire authority action:
# For example, to check if a given action is an acquire authority:
if not isinstance(action, NCAcquireAuthorityAction)
...
Source code:
hathor.nanocontracts.types.NCAcquireAuthorityAction
NCFail
An NCFail
exception signals to Hathor engine that a contract's creation or execution has failed. Use it in your blueprint's methods to indicate execution failures. Also, define subclasses of NCFail
to provide more specific and clearer failure reasons for contract users (wallets and other contracts).
Usage:
from hathor.nanocontracts.types import NCFail
...
# Raise a generic NCFail exception.
if len(ctx.actions) != 0:
raise NCFail('must be a single call')
...
# Define specific exception classes.
class WithdrawalNotAllowed(NCFail):
pass
...
# Raise a specific exception.
if action.type != NCActionType.DEPOSIT:
raise WithdrawalNotAllowed('must be deposit')
...
Source code:
hathor.nanocontracts.exception.NCFail
SignedData
Out-of-the-box support for oracles in blueprints. To achieve this, the blueprint should have a public method exclusively for oracle use, which receives a SignedData
as a parameter.
Data attributes:
data
: off-chain data that the oracle wants to feed into the contract. The type ofdata
is defined in theSignedData
parameter received by the oracle's public method referred to.script_input
:bytes
denoting the script used to authenticate the oracle.
Examples:
Suppose a sports betting contract in which an oracle should submit the result of a game after it has ended. In this scenario, SignedData[str]
specifies that the result in data
must be passed as a string. For example:
data: "Barcelona2x1Real-Madrid"
script_input: <unlock_script_to_auth_oracle_as_bytes>
Now, suppose an oracle needs to submit the exchange rate of 1 BTC in US dollars (USD) every minute. In this scenario, SignedData[int]
specifies that the result in data
must be passed as an integer, with the last two digits representing cents. For example:
data: 9654292
script_input: <unlock_script_to_auth_oracle_as_bytes>
Methods:
checksig
get_data_bytes
The following subsections describe each of these methods.
SignedData.checksig
Called in the oracle's public method to authenticate the oracle. That is, checks if this object's script_input
attribute resolves the oracle script registered in the contract state.
checksig(script: bytes) -> bool:
Parameter:
script
:bytes
that denotes the oracle script. That is, a script from the contract state used to authenticate the oracle.
Return: True
if this object's script_input
attribute unlocks script
received as parameter. That is, oracle is authenticated.
SignedData.get_data_bytes
Given a contract ID, returns a serialized version of data
as bytes
. Use it to perform unit tests on your blueprint.
get_data_bytes(self, contract_id: ContractId) -> bytes:
Parameter:
contract_id
:ContractId
that denotes a contract ID that is being used in the unit test.
Return: bytes
that denotes the serialized version of the attribute data
. Use it to generate the script_input
during unit tests.
Usage:
The following snippet presents how to write a public method that allows an oracle to feed off-chain data to a contract:
from hathor.nanocontracts.types import TxOutputScript
from hathor.nanocontracts.types import SignedData
# Script that must be resolved to authenticate oracle.
oracle_script: TxOutputScript
# SignedData[str] specifies that off-chain data being passed is of type string.
@public
def provide_offchain_data(self, ctx: Context, signed_data: SignedData[str]) -> None:
"""Called by oracle to input off-chain data into contract."""
# Check if script_input resolves oracle_script, to authenticate oracle.
if not signed_data.checksig(self.oracle_script):
raise NCFail('Oracle not authenticated')
# Once oracle authenticated, off-chain data can be input into contract.
self.offchain_data = signed_data.data
Source code:
hathor.nanocontracts.types.SignedData
Decorators
public
Decorator that marks a method in your blueprint class as 'public'. All public methods must be marked with the public
decorator. In addition, you must explicitly specify which types of actions the public method can receive. This should be done using the following keyword arguments:
- Option 1: use
allow_actions
to provide the list of allowedNCActionType
values. Example:allow_actions=[NCActionType.DEPOSIT, NCActionType.WITHDRAWAL])
. - Option 2: pass
True
for each action type you want to allow:
"""
- allow_deposit=True for DEPOSIT
- allow_withdrawal=True for WITHDRAWAL
- allow_grant_authority=True for GRANT_AUTHORITY
- allow_acquire_authority=True for ACQUIRE_AUTHORITY
"""
You cannot use allow_actions
(option 1) together with the other keyword arguments (option 2). Doing so will always raise a BlueprintSyntaxError
exception.
If an action type is not explicitly allowed using the keyword arguments in the @public
decorator, the public method will not be able to receive or handle that type of action. If you use the @public
decorator with no keyword arguments, the method will not be allowed to receive any actions. Finnaly, calling a public method with actions it is not allowed to handle will cause the method call to fail.
Usage:
from hathor.nanocontracts.types import public, NCActionType, SignedData
from hathor.nanocontracts.context import Context
...
# Mark all your public methods:
@public
def set_result(self, ctx: Context, result: SignedData[Result]) -> None:
"""Public method that CANNOT receive any type of action.
This is because you didn't explicitly allowed any type
alongside the public decorator.
"""
...
# If you want your method to receive any type of action
# You must do it explicitly:
@public(allow_actions=[NCActionType.DEPOSIT, NCActionType.WITHDRAWAL])
def swap(self, ctx: Context) -> None:
"""Public method that works with deposits and withdrawals.
It can receive ANY number of deposits and withdrawals
of any token. It is up to your code to validate these
actions.
We used the allow_actions arg to pass a list of enabled
actions, and we needed to use the NCActionTypes.
"""
...
# There are two alternative syntaxes to state the types of actions
# You either pass the allow_actions arg or:
@public(allow_deposit=True, allow_withdrawal=True)
def swap(self, ctx: Context) -> None:
"""Decorator using the other keyword args of public.
Both work the same way.
Here we do the same we did above but using one keyword arg
for each specific type of action.
"""
...
# It is not required to pass: one of them as False:
@public(allow_grant_authority=False, allow_acquire_authority=False)
def foo(self, ctx: Context) -> None:
"""False with <allow_some_action_type> arg changes nothing.
You may decide to use it just for human-reading purposes if you prefer.
"""
...
Source code:
hathor.nanocontracts.types.public
view
Decorator that marks a method in your blueprint class as 'view'. All view methods must be marked with the view
decorator.
Usage:
from hathor.nanocontracts.types import view
...
# Mark all your view methods.
@view
def get_max_withdrawal(self, address: Address) -> int:
"""Return the maximum amount available for withdrawal."""
...
Source code:
hathor.nanocontracts.types.view
fallback
Decorator that marks the method named 'fallback' in your blueprint class. That is, you can only use the @fallback
decorator once in the entire blueprint class, and only to mark a method named fallback
.
Just like with public methods, you must explicitly specify the allowed action types; use the same keyword arguments and follow the same rules, as described in subsection public
at Decorators.
Usage:
from hathor.nanocontracts.types import public, NCActionType
from hathor.nanocontracts.context import Context
...
# Mark your fallback method:
@fallback(allow_actions=[NCActionType.DEPOSIT, NCActionType.WITHDRAWAL, NCActionType.GRANT_AUTHORITY, NCActionType.ACQUIRE_AUTHORITY])
def fallback(self, ctx: Context) -> None:
"""THE only fallback method of your blueprint class.
You can only use this decorator with the method named 'fallback'.
Whenever a caller calls a non-existent PUBLIC method, Hathor engine
will invoke the fallback method.
Thus: just like any public method, you must explicitly state
what action types the method works with.
"""
...
Source code:
hathor.nanocontracts.types.fallback
Objects
Blueprint.syscall
For all interactions outside the scope of the blueprint itself (external interactions). That is, whenever you need to perform reads and writes to the ledger, call methods from other contracts, or access special features provided by Hathor engine.
Methods:
get_contract_id
get_blueprint_id
get_balance_before_current_call
get_current_balance
can_mint_before_current_call
can_mint
can_melt_before_current_call
revoke_authorities
mint_tokens
melt_tokens
create_token
call_view_method
call_public_method
emit_event
create_contract
The following subsections describe each of these methods.
syscall.get_contract_id
Returns the contract's own ID.
get_contract_id(self) -> ContractId:
syscall.get_blueprint_id
Returns the blueprint ID of the given contract.
get_blueprint_id(self, contract_id: Optional[ContractId] = None) -> BlueprintId:
Parameter:
contract_id
:ContractId
to query; defaults to the contract itself.
syscall.get_balance_before_current_call
Returns the balance of a token held by a contract; it does not take into account the effects of the current call.
get_balance_before_current_call(
self,
token_uid: Optional[TokenUid] = None,
*,
contract_id: Optional[ContractId] = None,
) -> Amount:
Parameters:
token_uid
:TokenUid
to check balance; defaults to 'HTR'.contract_id
:ContractId
to query; defaults to the contract itself.
Return: Amount
, such that:
- It takes into account:
- Everything that happened in the call chain before this call, including any actions.
- It does not take into account:
- Actions or changes that occurred during the current call.
- Actions or changes that will occur later in this call or in subsequent calls (since they haven’t happened yet, by definition).
Example: if a contract holds 50 HTR and the current call is requesting to withdraw 3 HTR, this method still returns 50 HTR.
syscall.get_current_balance
Returns the balance of a token held by a contract; it does take into account the actions and changes already performed in the current call.
get_current_balance(
self,
token_uid: Optional[TokenUid] = None,
*,
contract_id: Optional[ContractId] = None,
) -> Amount:
Parameters:
token_uid
:TokenUid
to check balance; defaults to 'HTR'.contract_id
:ContractId
to query; defaults to the contract itself.
Return: Amount
, such that:
- It takes into account:
- Everything that happened in the call chain before this call, including any actions.
- All actions and changes that occurred during the current call.
- It does not take into account:
- Actions or changes that will occur later in this call or in subsequent calls (since they haven’t happened yet, by definition).
Example: if a contract holds 50 HTR and the current call is requesting to withdraw 3 HTR, this method returns 47 HTR.
syscall.can_mint_before_current_call
Returns whether a contract has mint authority over a token; it does not take into account the actions and changes already performed in the current call.
can_mint_before_current_call(
self,
token_uid: TokenUid,
*,
contract_id: Optional[ContractId] = None,
) -> bool:
Parameters:
token_uid
:TokenUid
to check authority.contract_id
:ContractId
to query; defaults to the contract itself.
Return: bool
, such that:
True
means the contract has mint authority over the token.- It takes into account:
- Everything that happened in the call chain before this call, including any actions.
- It does not take into account:
- All actions and changes that occurred during the current call.
- Actions or changes that will occur later in this call or in subsequent calls (since they haven’t happened yet, by definition).
Example: if a contract has mint authority over a token and the current call revokes it before invoking this method, it still returns True
.
syscall.can_mint
Returns whether a contract has mint authority over a token; it does take into account the actions and changes already performed in the current call.
can_mint(
self,
token_uid: TokenUid,
*,
contract_id: Optional[ContractId] = None,
) -> bool:
Parameters:
token_uid
:TokenUid
to check authority.contract_id
:ContractId
to query; defaults to the contract itself.
Return: bool
, such that:
True
means the contract has mint authority over the token.- It takes into account:
- Everything that happened in the call chain before this call, including any actions.
- All actions and changes that occurred during the current call.
- It does not take into account:
- Actions or changes that will occur later in this call or in subsequent calls (since they haven’t happened yet, by definition).
Example: if a contract has mint authority over a token and the current call revokes it before invoking this method, it returns False
.
syscall.can_melt_before_current_call
Returns whether a contract has melt authority over a token; it does not take into account the actions and changes already performed in the current call.
can_melt_before_current_call(
self,
token_uid: TokenUid,
*,
contract_id: Optional[ContractId] = None,
) -> bool:
Parameters:
token_uid
:TokenUid
to check authority.contract_id
:ContractId
to query; defaults to the contract itself.
Return: bool
, such that:
True
means the contract has melt authority over the token.- It takes into account:
- Everything that happened in the call chain before this call, including any actions.
- It does not take into account:
- All actions and changes that occurred during the current call.
- Actions or changes that will occur later in this call or in subsequent calls (since they haven’t happened yet, by definition).
Example: if a contract has melt authority over a token and the current call revokes it before invoking this method, it still returns True
.
syscall.can_melt
Returns whether a contract has melt authority over a token; it does take into account the actions and changes already performed in the current call.
can_melt(
self,
token_uid: TokenUid,
*,
contract_id: Optional[ContractId] = None,
) -> bool:
Parameters:
token_uid
:TokenUid
to check authority.contract_id
:ContractId
to query; defaults to the contract itself.
Return: bool
, such that:
True
means the contract has melt authority over the token.- It takes into account:
- Everything that happened in the call chain before this call, including any actions.
- All actions and changes that occurred during the current call.
- It does not take into account:
- Actions or changes that will occur later in this call or in subsequent calls (since they haven’t happened yet, by definition).
Example: if a contract has melt authority over a token and the current call revokes it before invoking this method, it returns False
.
syscall.revoke_authorities
Revokes the contract’s own mint/melt authorities over a token.
revoke_authorities(
self,
token_uid: TokenUid,
*,
revoke_mint: bool,
revoke_melt: bool
) -> None:
Parameters:
token_uid
:TokenUid
to revoke authorities over.revoke_mint
:bool
; ifTrue
, will revoke mint authority.revoke_melt
:bool
; ifTrue
, will revoke melt authority.
syscall.mint_tokens
Mints a new amount of a given token and adds it to the contract balance.
def mint_tokens(self, token_uid: TokenUid, amount: int) -> None:
Parameters:
token_uid
:TokenUid
to mint; contract must have mint authority over it.amount
:Amount
to mint; contract must hold enough HTR in its balance to be consumed in the process.
syscall.melt_tokens
Melts an amount of a given token by removing it to the contract balance.
def melt_tokens(self, token_uid: TokenUid, amount: int) -> None:
Parameters:
token_uid
:TokenUid
to melt; contract must have melt authority over it.amount
:Amount
to melt; contract receives in its balance the redemption of HTR.
syscall.create_token
Creates a new fungible token and adds its initial supply to the contract balance.
create_token(
self,
token_name: str,
token_symbol: str,
amount: int,
mint_authority: bool = True,
melt_authority: bool = True,
) -> TokenUid:
Parameters:
token_name
:str
; 1-30 UTF-8 characters.token_symbol
:str
; 1-5 UTF-8 characters.amount
:Amount
that denotes initial supply of the token; contract must hold enough HTR in its balance to be consumed in minting initial supply.mint_authority
:bool
; ifTrue
, creates a mint authority for the contract over the new token.melt_authority
:bool
; ifTrue
, creates a melt authority for the contract over the new token.
Return: TokenUid
that uniquely identifies the token on the ledger (blockchain).
syscall.call_view_method
Calls a view method of another contract.
call_view_method(self, nc_id: ContractId, method_name: str, *args: Any, **kwargs: Any) -> Any:
Parameters:
nc_id
:ContractId
; contract to be called.method_name
:str
; view method to be called.- Use
*args
or**kwargs
to pass the method arguments according to the blueprint API of the target contract.
Return: you should know the blueprint API of target contract in order to properly handle return value.
syscall.call_public_method
Calls a public method of another contract.
call_public_method(
self,
nc_id: ContractId,
method_name: str,
actions: list[NCAction],
*args: Any,
**kwargs: Any,
) -> Any:
Parameters:
nc_id
:ContractId
; contract to be called.method_name
:str
; public method to be called.actions
: list ofNCAction
to be performed over target contract; caller contract (itself) must have sufficient funds to perform deposits and target contract must have sufficient funds for withdrawals.- Use
*args
or**kwargs
to pass the method arguments according to the blueprint API of the target contract.
Return: you should know the blueprint API of target contract in order to properly handle return value.
syscall.emit_event
Registers an event related to the current contract execution. The event is recorded by all full nodes configured to produce events — i.e., with the ledger events producer feature enabled — and can be consumed by client applications connected to them. For more on ledger events, see Ledger events at Highlighted configurations.
emit_event(self, data: bytes) -> None:
Parameter:
data
:bytes
that denotes the event to be registered; limited to 100 KiB. The event is marked as typeNC_EVENT
in the event database of each full node. Its contentdata
consists of raw bytes, intended for client applications that know how to interpret it.
syscall.create_contract
Create a new contract.
create_contract(
self,
blueprint_id: BlueprintId,
salt: bytes,
actions: list[NCAction],
*args: Any,
**kwargs: Any,
) -> tuple[ContractId, Any]:
Parameters:
blueprint_id
:BlueprintId
to instantiate new contract from.salt
:bytes
used to generate theContractId
for contracts created by the current contract itself. This value must be unique for each contract it creates. You can use UUIDs generated via a pseudo-random number generator, or even a simple counter.actions
: list ofNCAction
as required by methodinitialize
of chosen blueprint.- Use
*args
or**kwargs
to pass the arguments required by methodinitialize
of chosen blueprint.
Return: tuple where the first item is ContractId
that uniquely identifies the newly created contract; and second is the return value of method initialize
of the chosen blueprint. You should know the chosen blueprint API in order to properly handle return value.
Object:
rng
: instance attribute used for random number generation. Seesyscall.rng
at section Objects.
Source code:
hathor.nanocontracts.blueprint_env.BlueprintEnvironment
Blueprint.log
Use it to record method execution logs. Useful during development for debugging and testing, and in production for audits.
Methods:
debug
info
warn
error
The following subsections describe each of these methods.
log.debug
Create a new DEBUG
log entry.
debug(self, message: str, **kwargs: Any) -> None:
log.info
Create a new INFO
log entry.
info(self, message: str, **kwargs: Any) -> None:
log.warn
Create a new WARN
log entry.
warn(self, message: str, **kwargs: Any) -> None:
log.error
Create a new ERROR
log entry.
error(self, message: str, **kwargs: Any) -> None:
In addition to the log message (argument message
), all methods accept arbitrary arguments via **kwargs: Any
. These arguments will be recorded as key/value pairs. bytes
will be represented as hexadecimals, and all values will be recorded as strings.
syscall.rng
Random number generator (RNG) for use cases that require randomness. From Hathor engine's perspective, random number generation is deterministic — all nodes on the network will generate the same number. However, from the contract users' perspective, the number generation is truly random. It is built on the ChaCha20 algorithm.
Data attribute:
seed
:bytes
; seed used to create the RNG object. The seed is the double hash of the first block validating the transaction.
Methods:
randbits
randbelow
randrange
randint
choice
random
The following subsections describe each of these methods.
rng.randbits
Returns a random integer in the range , where is integer and . In other words, a random non-negative integer with size .
randbits(self, bits: int) -> int:
rng.randbelow
Returns a random integer in the range , where is integer and .
randbelow(self, n: int) -> int:
rng.randrange
Returns a random integer in the range with step size , where , , and are integers such that and . The result will always be within the given interval, and of the form for a random integer . For example: randrange(start=10, stop=20, step=2)
may return any of the following values: 10, 12, 14, 16, or 18.
randrange(self, start: int, stop: int, step: int = 1) -> int:
rng.randint
Returns a random integer in the range , where and are integers such that and .
randint(self, a: int, b: int) -> int:
rng.choice
Chooses a random element from the non-empty Sequence
.
choice(self, seq: Sequence[T]) -> T:
Parameter:
seq
: anySequence
type supported by the blueprint SDK. See Allowed value types at Blueprint development guidelines.
rng.random
Returns a random float in the range .
random(self) -> float:
Source code:
hathor.nanocontracts.rng.NanoRNG
What's next?
- Blueprint development: main guidelines to consult while developing a blueprint.
- Develop a blueprint — part 1: hands-on tutorial to assist developers to conceive and design a blueprint.
- Develop a blueprint — part 2: hands-on tutorial to assist developers to implement and test a blueprint.
- Set up a localnet: for integration testing of blueprints, nano contracts, and DApps.