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 that can be imported in a blueprint module.
This API reference is divided into the following sections:
- Constants: utility constants.
- Types: types used in the development of contracts.
- Decorators: used to define a blueprint and the type of methods in it.
- Functions: utility functions.
- Objects: describe the interface of internal objects from the available types. That is, you can use them through instances of available types, but you cannot import and use them directly.
Not all names from the hathor 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 SDK — guidelines.
Constants
HATHOR_TOKEN_UID
The TokenUid for Hathor's native token, HTR (equivalent to TokenUid(b'\x00'), or 00 in hexadecimal).
Usage:
from hathor import public, Context, HATHOR_TOKEN_UID
...
@public
def some_method(self, ctx: Context) -> None:
action = ctx.get_single_action(HATHOR_TOKEN_UID)
...
Source code:
hathor.HATHOR_TOKEN_UID
Types
Source code: types in hathor
Address
- Description: wallet address
- Wrapper for:
bytes - Constraint: 25 bytes
- Example:
Address(b'(\xf3\xb5}\xf3\xf6\xa2"\x80\x8eWr\xa9\x08\xbd\xfb\xa1\xfc\xd3-R\xd2d\x16\x95') - Usage:
from hathor import Address
...
address: Address
...
address = Address.from_str('HUjk1F977H9Ct8UEx3Zgc4yFw5VEkmdGJY')
Amount
- Description: denotes an amount of tokens, a non-negative integer where the last two digits represent decimals.
- Wrapper for:
int - Constraints:
- Max value:
2**(32 * 7) - 1 = 26959946667150639794667015087019630673637144422540572481103610249215 - Min value:
0
- Max value:
- Example:
1025represents 10.25 tokens - Usage:
from hathor import Amount
...
amount: Amount
...
amount = Amount(1025)
BlueprintId
- Description: identifier of a blueprint which is the hash of the transaction that deployed it.
- Wrapper for:
bytes - Constraint: 32 bytes
- Example:
BlueprintId(bytes.fromhex("3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595")) - Usage:
from hathor import BlueprintId
...
blueprint: BlueprintId
...
blueprint = BlueprintId(bytes.fromhex("3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595"))
ContractId
- Description: identifier of a contract. It is recorded in the transaction where it was created.
- Wrapper for:
bytes - Constraint: 32 bytes
- Example:
bytes.fromhex("000063f99b133c7630bc9d0117919f5b8726155412ad063dbbd618bdc7f85d7a") - Usage:
from hathor import ContractId
...
contract: ContractId
...
contract = ContractId(bytes.fromhex("000063f99b133c7630bc9d0117919f5b8726155412ad063dbbd618bdc7f85d7a"))
Timestamp
- Description: non-negative integer representing the number of seconds elapsed since January 1st, 1970 00:00:00 UTC (Unix epoch standard).
- Wrapper for:
int - Constraints:
- Max value:
2**(4 * 8) - 1 = 4294967295 - Min value:
0
- Max value:
- Example: timestamp for January 3, 2020, at noon UTC is
Timestamp(1578052800) - Usage:
from hathor import Timestamp
...
timestamp: Timestamp
...
timestamp = Timestamp(1578052800)
TokenUid
- Description: identifier of a token. It is recorded in the transaction where it was created.
- Wrapper for:
bytes - Constraint: 32 bytes
- Example:
TokenUid(bytes.fromhex("00000943573723a28e3dd980c10e08419d0e00bc647a95f4ca9671ebea7d5669")) - Usage:
from hathor import TokenUid
...
token: TokenUid
...
token = TokenUid(bytes.fromhex("00000943573723a28e3dd980c10e08419d0e00bc647a95f4ca9671ebea7d5669"))
TxOutputScript
- Description: lock script of a transaction output
- Wrapper for:
bytes - Constraint: maximum of
2**16 = 65536bytes - Usage:
from hathor import TxOutputScript
...
oracle: TxOutputScript
VertexId
- Description: identifier of a transaction in the ledger (blockchain). It is the hash of the transaction.
- Wrapper for:
bytes - Constraint: 32 bytes
- Example:
VertexId(bytes.fromhex("00000943573723a28e3dd980c10e08419d0e00bc647a95f4ca9671ebea7d5669")) - Usage:
from hathor import VertexId
...
tx: VertexId
...
tx = VertexId(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.
- Union 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 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
CallerId
- Description: union type alias representing one of the possible caller types. Use for type annotations when a parameter, variable, or attribute can accept any caller type.
- Union for one of the possible caller types:
AddressorContractId. - Constraint: must be an instance of one of the classes above.
- Usage:
from hathor import CallerId, public, Context
...
caller: CallerId
...
@public
def some_method(self, ctx: Context) -> None:
self.caller = ctx.caller_id
...
- Source code:
hathor.nanocontracts.types.CallerId
NCArgs, NCRawArgs, and NCParsedArgs
NCArgs is an union type alias representing one of the possible args types to be used in fallback methods:
NCRawArgs: raw bytes representing args that could not be parsed given the method signature.NCParsedArgs: parsedargsandkwargs.
Usage:
from hathor import fallback, Context, NCArgs
...
@fallback(allow_deposit=True)
def fallback(self, ctx: Context, method_name: str, nc_args: NCArgs) -> None:
match nc_args:
case NCRawArgs():
a, b = nc_args.try_parse_as((str, int))
...
case NCParsedArgs(args, kwargs):
...
case _:
raise NCFail('unexpected')
- Source code:
hathor.nanocontracts.types.NCArgs
NCFee
A dataclass representing a fee payment for operations that require fees, such as minting fee-based tokens.
Data attributes:
token_uid:TokenUidthat denotes the token used to pay the fee.amount:intthat denotes the amount of tokens to be used as fee payment.
Usage:
from hathor import NCFee, TokenUid, HATHOR_TOKEN_UID
...
# Create a fee payment using HTR
htr_fee = NCFee(token_uid=HATHOR_TOKEN_UID, amount=100)
# Use fees when calling another contract's public method
contract.public(deposit_action, fees=[htr_fee]).some_method()
Source code:
hathor.nanocontracts.types.NCFee
Blueprint
Serves as the base class for all blueprints. You must subclass Blueprint exactly once in your blueprint module.
Usage:
from hathor import Blueprint, export
...
# Define your blueprint class as subclass of Blueprint:
@export
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.syscallat section Objects.log: instance attribute used to record contract execution logs. Useful during development for debugging and testing, and in production for audits. SeeBlueprint.logat section Objects.
Source code:
hathor.nanocontracts.blueprint.Blueprint
VertexData
A dataclass containing information about the vertex that originated the nano method call chain. It can be obtained from a Context, via ctx.vertex.
Data attributes:
version: theTxVersion, which is an integer representing the vertex's version.hash: the vertex's hash (its ID).nonce: the vertex's nonce.signal_bits: the vertex's signal bits.weight: the vertex's weight.inputs: a tuple ofTxInputDatacontaining data about each input of this vertex.outputs: a tuple ofTxOutputDatacontaining data about each output of this vertex.tokens: a tuple ofTokenUidcontaining hashes of this vertex's tokens.parents: a tuple ofVertexIdcontaining hashes of this vertex's parents.headers: a tuple ofHeaderDatacontaining data about each header of this vertex.
Source code:
hathor.nanocontracts.vertex_data.VertexData
TxVersion
An integer enumeration (IntEnum) representing the version of a vertex (transaction or block). It can be one of the following:
REGULAR_BLOCK = 0: A regular block.REGULAR_TRANSACTION = 1: A regular transaction.TOKEN_CREATION_TRANSACTION = 2: A transaction that creates a new custom token.MERGE_MINED_BLOCK = 3: A merge-mined block.ON_CHAIN_BLUEPRINT = 6: An on-chain blueprint.
Source code:
hathor.transaction.base_transaction.TxVersion
TxInputData
A dataclass containing information about a transaction input. It can be obtained from a VertexData, via vertex.inputs.
Data attributes:
tx_id:VertexIdof the transaction containing the output being spent.index: the index of the output being spent within that transaction.data: the unlocking script data asbytes.info: aTxOutputDatacontaining information about the output being spent.
Source code:
hathor.nanocontracts.vertex_data.TxInputData
TxOutputData
A dataclass containing information about a transaction output. It can be obtained from a VertexData via vertex.outputs, or from a TxInputData via input.info.
Data attributes:
value: the output value as an integer.raw_script: the locking script as rawbytes.parsed_script: aScriptInfoobject with parsed script details, orNoneif the script could not be parsed.token_data: the token data byte, which encodes the token index and authority flags.
Source code:
hathor.nanocontracts.vertex_data.TxOutputData
ScriptInfo
A dataclass containing parsed information from a transaction output script. It can be obtained from a TxOutputData via output.parsed_script.
Data attributes:
type: aScriptTypeindicating the type of script (P2PKHorMultiSig).address: the destination address as a string.timelock: the timelock timestamp as an integer, orNoneif no timelock is present.
Source code:
hathor.nanocontracts.vertex_data.ScriptInfo
ScriptType
A string enumeration (StrEnum) representing the type of an output script. It can be one of the following:
P2PKH = 'P2PKH': Pay-to-Public-Key-Hash script.MULTI_SIG = 'MultiSig': Multi-signature script (P2SH).
Source code:
hathor.nanocontracts.vertex_data.ScriptType
BlockData
A dataclass containing information about the block that confirmed and executed the nano method call. It can be obtained from a Context, via ctx.block.
Data attributes:
hash: theVertexId(hash) of the block.timestamp: the block's timestamp as an integer (Unix epoch) — this may be used for time-based operations on blueprints.height: the block's height in the blockchain.
Source code:
hathor.nanocontracts.vertex_data.BlockData
HeaderData
A marker class representing an arbitrary vertex header. It serves as a base class for specific header types. It can be obtained from a VertexData, via vertex.headers.
Source code:
hathor.nanocontracts.vertex_data.HeaderData
NanoHeaderData
A dataclass (subclass of HeaderData) containing information about the nano contract header of a transaction. This header contains the nano contract call information.
Data attributes:
nc_seqnum: the sequence number of the vertex.nc_id: thenc_idof the vertex.nc_method: the name of the method being called as a string.nc_args_bytes: the serialized method arguments asbytes.
Source code:
hathor.nanocontracts.vertex_data.NanoHeaderData
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 (after self).
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:VertexDatathat contains the origin transaction.block:BlockDatathat contains information about the block that confirmed (and therefore executed) the origin transaction.caller_id: ACallerIdwhich is either anAddressor aContractIdthat denotes who called the method.actions: Immutable dict where the keys areTokenUidand the values are tuples ofNCActionobjects. All actions requested in the current call. It may contain zero, one, or more elements.actions_list: The same asactions, but organized in a list with all actions for convenience.
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
DEPOSITaction and oneWITHDRAWALaction. - One
GRANT_AUTHORITYaction and oneACQUIRE_AUTHORITYaction.
Methods:
get_single_actionget_caller_addressget_caller_contract_id
The following subsection describes the methods of this class that are 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:TokenUidthat 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 import Context, public
...
# 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
Context.get_caller_address
Given a CallerId, extracts an Address if the caller is a wallet address (that is, the method was called directly by a transaction), or None if it's a ContractId (that is, the method was called by another contract).
get_caller_address(self) -> Address | None:
Return: Address if the caller is a wallet address, and None otherwise.
Usage:
from hathor import Context, public
...
@public
def swap(self, ctx: Context) -> None:
address_or_none = ctx.get_caller_address()
...
Source code:
hathor.nanocontracts.context.Context
Context.get_caller_contract_id
Given a CallerId, extracts a ContractId if the caller is a another contract, or None if it's a wallet address (that is, the method was called directly by a transaction).
get_caller_contract_id(self) -> ContractId | None:
Return: ContractId if the caller is a contract, and None otherwise.
Usage:
from hathor import Context, public
...
@public
def swap(self, ctx: Context) -> None:
contract_id_or_none = ctx.get_caller_contract_id()
...
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 import NCActionType
...
# Checking the type of a given action
# You can use:
if not isinstance(action, NCDepositAction):
raise InvalidAction
# OR with NCActionType the equivalent:
if action.type != NCActionType.DEPOSIT:
raise InvalidAction
# 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.DEPOSITtoken_uid:TokenUidthat denotes the token the caller wants to deposit.amount:Amountthat denotes the amount of tokens to be deposited.
Usage:
from hathor 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.WITHDRAWALtoken_uid:TokenUidthat denotes the token the caller wants to withdraw.amount:Amountthat denotes the amount of tokens to be withdrawn.
Usage:
from hathor 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:TokenUidthat denotes the token the caller wants to grant authority to contract.mint:bool; ifTrue, the contract will be allowed to issue new amounts of the token, increasing its total supply.melt:bool; ifTrue, the contract will be allowed to destroy amounts of the token, decreasing its total supply.
Usage:
from hathor 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:TokenUidthat denotes the token the caller wants to grant authority to contract.mint:bool; ifTrue, the caller will be allowed to issue new amounts of the token, increasing its total supply.melt:bool; ifTrue, the caller will be allowed to destroy amounts of the token, decreasing its total supply.
Usage:
from hathor 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 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 ofdatais defined in theSignedDatageneric parameter which is defined in the public method that uses it.script_input:bytesdenoting 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:
checksigget_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(self, contract_id: ContractId, script: bytes) -> bool:
Parameter:
contract_id:ContractIddenoting the contract that was intended to authenticate this signed data.script:bytesthat 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 successfully unlocks the script received as parameter. That is, the oracle is authenticated.
SignedData.get_data_bytes
Given a contract ID, returns a serialized version of data as bytes. Useful to perform unit tests on your blueprint.
get_data_bytes(self, contract_id: ContractId) -> bytes:
Parameter:
contract_id:ContractIddenoting the contract that was intended to authenticate this signed data.
Return: bytes that denotes the serialized version of the attribute data. Useful 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 import TxOutputScript, SignedData, public, Context, NCFail
# Script that must be resolved to authenticate oracle.
oracle_script: TxOutputScript
# Data to be fed by oracle
offchain_data: str | None
# 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.syscall.get_contract_id(), self.oracle_script):
raise NCFail('Oracle not authenticated')
# Once oracle authenticated, off-chain data can be saved in the contract.
self.offchain_data = signed_data.data
Source code:
hathor.nanocontracts.types.SignedData
Decorators
export
Decorator that marks a class in your blueprint module as the Blueprint. It must only be used once, decorating a class that inherits from Blueprint.
Usage:
from hathor import export, Blueprint
...
@export
class MyBlueprint(Blueprint):
...
Source code:
hathor.nanocontracts.types.export
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, if any. This should be done using one of the following keyword arguments:
- Option 1: pass
Truefor 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
"""
- Option 2: use
allow_actionsto provide the list of allowedNCActionTypevalues. Example:allow_actions=[NCActionType.DEPOSIT, NCActionType.WITHDRAWAL]).
You cannot use allow_actions (option 2) together with the other keyword arguments (option 1). Doing so will always raise a BlueprintSyntaxError exception.
You can also use allow_reentrancy=True to allow the method to be called again during its own execution (reentrancy). By default, reentrancy is not allowed (allow_reentrancy=False).
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. Finally, calling a public method with actions it is not allowed to handle will cause the method call to fail.
Usage:
from hathor import public, NCActionType, SignedData, 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 allow 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 import view, Address
...
# 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. You can also use allow_reentrancy=True to allow the fallback method to be called again during its own execution (reentrancy). By default, reentrancy is not allowed (allow_reentrancy=False). For more on it, see Method fallback at Blueprint development guidelines
Usage:
from hathor import fallback, Context, NCArgs
...
# Mark your fallback method:
@fallback(allow_deposit=True)
def fallback(self, ctx: Context, method_name: str, nc_args: NCArgs) -> 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
Functions
sha3
Calculate the SHA3-256 of some data.
Usage:
from hathor import sha3, public, Context
...
@public
def some_method(self, ctx: Context, data: bytes) -> None:
my_hash = sha3(data)
...
Source code:
hathor.nanocontracts.utils.sha3
verify_ecdsa
Verify a cryptographic signature using a compressed public key for a SECP256K1 curve.
Usage:
from hathor import verify_ecdsa, public, Context
...
@public
def some_method(self, ctx: Context, public_key: bytes, data: bytes, signature: bytes) -> None:
is_valid = verify_ecdsa(public_key, data, signature)
...
Source code:
hathor.nanocontracts.utils.verify_ecdsa
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_idget_blueprint_idget_current_code_blueprint_idget_balance_before_current_callget_current_balancecan_mint_before_current_callcan_mintcan_melt_before_current_callcan_meltrevoke_authoritiesmint_tokensmelt_tokenscreate_deposit_tokencreate_fee_tokenemit_eventchange_blueprintget_contractget_proxysetup_new_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 current nano contract. Note that during a proxy call, this method returns the blueprint ID of the caller's contract, not the blueprint ID of the blueprint that owns the running code.
get_blueprint_id(self) -> BlueprintId:
syscall.get_current_code_blueprint_id
Returns the blueprint ID of the blueprint that owns the currently running code. This is useful during proxy calls, where get_blueprint_id returns the caller's blueprint ID, and this method returns the blueprint ID of the code being executed.
get_current_code_blueprint_id(self) -> BlueprintId:
syscall.get_balance_before_current_call
Returns the balance of a token held by this contract; it does not take into account the effects of the current call.
get_balance_before_current_call(self, token_uid: TokenUid | None = None) -> Amount:
Parameter:
token_uid:TokenUidto check balance; defaults to HTR.
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 this contract; it does take into account the actions and changes already performed in the current call.
get_current_balance(self, token_uid: TokenUid | None = None) -> Amount:
Parameter:
token_uid:TokenUidto check balance; defaults to HTR.
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 this 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) -> bool:
Parameter:
token_uid:TokenUidto check authority.
Return: bool, such that:
Truemeans 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 this 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) -> bool:
Parameter:
token_uid:TokenUidto check authority.
Return: bool, such that:
Truemeans 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 this 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) -> bool:
Parameter:
token_uid:TokenUidto check authority.
Return: bool, such that:
Truemeans 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 this 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) -> bool:
Parameter:
token_uid:TokenUidto check authority.
Return: bool, such that:
Truemeans 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:TokenUidto 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.
mint_tokens(
self,
token_uid: TokenUid,
amount: int,
*,
fee_payment_token: TokenUid = TokenUid(HATHOR_TOKEN_UID)
) -> None:
Parameters:
token_uid:TokenUidto mint; contract must have mint authority over it.amount:Amountto mint.fee_payment_token:TokenUidused to pay the minting fee; defaults to HTR.
syscall.melt_tokens
Melts an amount of a given token by removing it from the contract balance.
melt_tokens(
self,
token_uid: TokenUid,
amount: int,
*,
fee_payment_token: TokenUid = TokenUid(HATHOR_TOKEN_UID)
) -> None:
Parameters:
token_uid:TokenUidto melt; contract must have melt authority over it.amount:Amountto melt.fee_payment_token:TokenUidused to pay the melting fee; defaults to HTR.
syscall.create_deposit_token
Creates a new deposit-based fungible token and adds its initial supply to the contract balance.
create_deposit_token(
self,
*,
token_name: str,
token_symbol: str,
amount: int,
mint_authority: bool = True,
melt_authority: bool = True,
salt: bytes = b'',
) -> TokenUid:
Parameters:
token_name:str; 1-30 UTF-8 characters.token_symbol:str; 1-5 UTF-8 characters.amount:Amountthat 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.salt:bytesused to generate a uniqueTokenUid; defaults to empty bytes.
Return: TokenUid that uniquely identifies the token on the ledger (blockchain).
syscall.create_fee_token
Creates a new fee-based fungible token and adds its initial supply to the contract balance.
create_fee_token(
self,
*,
token_name: str,
token_symbol: str,
amount: int,
mint_authority: bool = True,
melt_authority: bool = True,
salt: bytes = b'',
fee_payment_token: TokenUid = TokenUid(HATHOR_TOKEN_UID)
) -> TokenUid:
Parameters:
token_name:str; 1-30 UTF-8 characters.token_symbol:str; 1-5 UTF-8 characters.amount:Amountthat denotes initial supply of the token.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.salt:bytesused to generate a uniqueTokenUid; defaults to empty bytes.fee_payment_token:TokenUidused to pay the token creation fee; defaults to HTR.
Return: TokenUid that uniquely identifies the token on the ledger (blockchain).
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:bytesthat denotes the event to be registered; limited to 1 KiB. The event is marked as typeNC_EVENTin the event database of each full node. Its contentdataconsists of raw bytes, intended for client applications that know how to interpret it.
syscall.change_blueprint
Changes the contract blueprint. Use it for upgradeable contracts.
change_blueprint(self, blueprint_id: BlueprintId) -> None:
Parameter:
blueprint_id:BlueprintId; denotes the new blueprint for the contract.
syscall.get_contract
Returns a ContractAccessor for interacting with another contract. Use this to call view or public methods on other contracts.
get_contract(
self,
contract_id: ContractId,
*,
blueprint_id: BlueprintId | Collection[BlueprintId] | None,
) -> ContractAccessor:
Parameters:
contract_id:ContractIdof the contract to interact with.blueprint_id: the expectedBlueprintIdof the contract, or a collection of accepted blueprints, orNoneif any blueprint is accepted. If the contract's actual blueprint doesn't match, anNCFailexception is raised.
Return: a ContractAccessor object. See ContractAccessor for usage details.
Usage:
from hathor import public, Context, ContractId, BlueprintId, NCDepositAction
@public(allow_withdrawal=True)
def call_another_contract(self, ctx: Context, target: ContractId) -> None:
# Get a contract accessor, accepting any blueprint
contract = self.syscall.get_contract(target, blueprint_id=None)
# Call a view method
result = contract.view().get_value()
# Call a public method with actions
deposit = NCDepositAction(token_uid=HATHOR_TOKEN_UID, amount=100)
contract.public(deposit).deposit()
syscall.get_proxy
Returns a ProxyAccessor for invoking methods from another blueprint as if this contract had that method. This is useful for contracts that delegate functionality to other blueprints.
get_proxy(self, blueprint_id: BlueprintId) -> ProxyAccessor:
Parameter:
blueprint_id:BlueprintIdof the blueprint to use.
Return: a ProxyAccessor object. See ProxyAccessor for usage details.
Usage:
from hathor import public, Context, BlueprintId
@public
def proxy_call(self, ctx: Context, target_blueprint: BlueprintId) -> None:
# Get a proxy accessor
proxy = self.syscall.get_proxy(target_blueprint)
# Call a view method from the target blueprint
result = proxy.view().some_view_method()
# Call a public method from the target blueprint
proxy.public().some_public_method(arg1, arg2)
syscall.setup_new_contract
Sets up the creation of a new contract. Returns an InitializeMethodAccessor that must be used to call the initialize method of the new contract.
setup_new_contract(
self,
blueprint_id: BlueprintId,
*actions: NCAction,
fees: Sequence[NCFee] | None = None,
salt: bytes,
) -> InitializeMethodAccessor:
Parameters:
blueprint_id:BlueprintIdto instantiate the new contract from.*actions:NCActionobjects to be performed during initialization.fees: optional sequence ofNCFeeobjects for fee payment.salt:bytesused to generate theContractIdfor the new contract. This value must be unique for each contract created by this contract. You can use UUIDs generated via a pseudo-random number generator, or even a simple counter.
Return: an InitializeMethodAccessor object. See InitializeMethodAccessor for usage details.
Usage:
from hathor import public, Context, BlueprintId, NCDepositAction
@public(allow_deposit=True)
def create_child_contract(self, ctx: Context, blueprint: BlueprintId) -> ContractId:
# Setup new contract creation with initial deposit
deposit = NCDepositAction(token_uid=HATHOR_TOKEN_UID, amount=1000)
accessor = self.syscall.setup_new_contract(blueprint, deposit, salt=b'unique_salt')
# Call initialize to actually create the contract
contract_id, result = accessor.initialize(param1="value1", param2=42)
return contract_id
Blueprint.syscall.log
Use it to record method execution logs. Useful during development for debugging and testing, and in production for audits. Then, at runtime, use the full node HTTP API to retrieve the logs.
Methods:
debuginfowarnerror
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.
Source code:
hathor.nanocontracts.nc_exec_logs.NCLogger
Blueprint.syscall.rng
Random number generator (RNG) for use cases that require randomness. This RNG is deterministic based on the hash of the block that executes the contract. As a result, all nodes on the network will generate the same number. However, since it is virtually impossible to know this value beforehand, true randomness is achieved at the time of contract execution. It is based 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:
randbytesrandbitsrandbelowrandrangerandintchoicerandom
The following subsections describe each of these methods.
rng.randbytes
Returns a random string of bytes of the specified size, where is an integer and .
randbytes(self, size: int) -> bytes:
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 .
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: anySequencetype 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
ContractAccessor
An accessor object for interacting with another contract. Returned by syscall.get_contract. It provides methods to call view and public methods on the target contract, and to query its balance and authorities.
Methods:
get_contract_id: returns theContractIdof the target contract.get_blueprint_id: returns theBlueprintIdof the target contract.get_current_balance: returns the current balance for a given token on the target contract.can_mint: returns whether the target contract has mint authority over a token.can_melt: returns whether the target contract has melt authority over a token.view: prepares a call to a view method. Returns an object where calling any attribute as a method will invoke that view method on the target contract.public: prepares a call to a public method with the given actions. Returns an object where calling any attribute as a method will invoke that public method on the target contract.get_view_method: returns a callable for a view method with a dynamic name.get_public_method: returns a callable for a public method with a dynamic name.
ContractAccessor.view
Prepares a call to a view method on the target contract. Returns an intermediate object that allows calling any view method by name.
view(self) -> Any:
Return: an object where you can call any method name, which will invoke the corresponding view method on the target contract.
Usage:
# Call a view method named 'get_balance' on another contract
contract = self.syscall.get_contract(target_id, blueprint_id=None)
balance = contract.view().get_balance(address)
ContractAccessor.public
Prepares a call to a public method on the target contract with the specified actions. Returns an intermediate object that allows calling any public method by name.
public(
self,
*actions: NCAction,
fees: Sequence[NCFee] | None = None,
forbid_fallback: bool = False
) -> Any:
Parameters:
*actions:NCActionobjects to be performed during the call. The caller contract must have sufficient funds for deposits, and the target contract must have sufficient funds for withdrawals.fees: optional sequence ofNCFeeobjects for fee payment.forbid_fallback: ifTrue, the call will fail if the method is not found instead of invoking the fallback method.
Return: an object where you can call any method name, which will invoke the corresponding public method on the target contract. Note that each prepared public call can only be used once because it consumes the provided actions.
Usage:
# Call a public method named 'deposit' on another contract
contract = self.syscall.get_contract(target_id, blueprint_id=None)
deposit_action = NCDepositAction(token_uid=HATHOR_TOKEN_UID, amount=100)
contract.public(deposit_action).deposit()
ContractAccessor.get_view_method
Returns a callable accessor for a view method with a dynamic name. Useful when the method name is stored in a variable.
get_view_method(self, method_name: str) -> ViewMethodAccessor:
Parameter:
method_name: the name of the view method to call.
Return: a ViewMethodAccessor that can be called multiple times with different arguments.
Usage:
contract = self.syscall.get_contract(target_id, blueprint_id=None)
method = contract.get_view_method("get_balance")
balance = method(address)
ContractAccessor.get_public_method
Returns a callable accessor for a public method with a dynamic name. Useful when the method name is stored in a variable.
get_public_method(
self,
method_name: str,
*actions: NCAction,
fees: Sequence[NCFee] | None = None,
forbid_fallback: bool = False,
) -> PublicMethodAccessor:
Parameters:
method_name: the name of the public method to call.*actions:NCActionobjects to be performed during the call.fees: optional sequence ofNCFeeobjects for fee payment.forbid_fallback: ifTrue, the call will fail if the method is not found.
Return: a PublicMethodAccessor that can only be called once because it consumes the provided actions.
Usage:
contract = self.syscall.get_contract(target_id, blueprint_id=None)
deposit_action = NCDepositAction(token_uid=HATHOR_TOKEN_UID, amount=100)
method = contract.get_public_method("deposit", deposit_action)
method()
Source code:
hathor.nanocontracts.contract_accessor.ContractAccessor
ProxyAccessor
An accessor object for invoking methods from another blueprint as if the current contract had that method. Returned by syscall.get_proxy. This is useful for contracts that delegate functionality to other blueprints.
Methods:
get_blueprint_id: returns theBlueprintIdof the proxy blueprint.view: prepares a proxy call to a view method.public: prepares a proxy call to a public method with the given actions.get_view_method: returns a callable for a proxy view method with a dynamic name.get_public_method: returns a callable for a proxy public method with a dynamic name.
ProxyAccessor.view
Prepares a proxy call to a view method. Returns an intermediate object that allows calling any view method by name.
view(self) -> Any:
Return: an object where you can call any method name, which will invoke the corresponding view method from the proxy blueprint on the current contract.
Usage:
proxy = self.syscall.get_proxy(blueprint_id)
result = proxy.view().some_view_method(arg1, arg2)
ProxyAccessor.public
Prepares a proxy call to a public method with the specified actions. Returns an intermediate object that allows calling any public method by name.
public(
self,
*actions: NCAction,
fees: Sequence[NCFee] | None = None,
forbid_fallback: bool = False
) -> Any:
Parameters:
*actions:NCActionobjects to be performed during the call.fees: optional sequence ofNCFeeobjects for fee payment.forbid_fallback: ifTrue, the call will fail if the method is not found instead of invoking the fallback method.
Return: an object where you can call any method name, which will invoke the corresponding public method from the proxy blueprint. Note that each prepared proxy call can only be used once.
Usage:
proxy = self.syscall.get_proxy(blueprint_id)
proxy.public().some_public_method(arg1, arg2)
ProxyAccessor.get_view_method
Returns a callable accessor for a proxy view method with a dynamic name.
get_view_method(self, method_name: str) -> ProxyViewMethodAccessor:
Parameter:
method_name: the name of the view method to call.
Return: a ProxyViewMethodAccessor that can be called multiple times with different arguments.
ProxyAccessor.get_public_method
Returns a callable accessor for a proxy public method with a dynamic name.
get_public_method(
self,
method_name: str,
*actions: NCAction,
fees: Sequence[NCFee] | None = None,
forbid_fallback: bool = False,
) -> ProxyPublicMethodAccessor:
Parameters:
method_name: the name of the public method to call.*actions:NCActionobjects to be performed during the call.fees: optional sequence ofNCFeeobjects for fee payment.forbid_fallback: ifTrue, the call will fail if the method is not found.
Return: a ProxyPublicMethodAccessor that can only be called once. It also supports call_with_nc_args(nc_args: NCArgs) for use within fallback methods.
Source code:
hathor.nanocontracts.proxy_accessor.ProxyAccessor
InitializeMethodAccessor
An accessor object for creating a new contract. Returned by syscall.setup_new_contract. It provides the initialize method to actually create the contract.
Methods:
initialize: creates the new contract by calling itsinitializemethod with the provided arguments.
InitializeMethodAccessor.initialize
Creates the new contract by calling its initialize method.
initialize(self, *args: Any, **kwargs: Any) -> tuple[ContractId, object]:
Parameters:
- Use
*argsor**kwargsto pass the arguments required by theinitializemethod of the chosen blueprint.
Return: a tuple where:
- The first item is the
ContractIdthat uniquely identifies the newly created contract. - The second item is the return value of the
initializemethod. You should know the chosen blueprint API to properly handle this return value.
Each InitializeMethodAccessor can only be used once. Attempting to call initialize again will raise an NCFail exception. To create another contract, you must call syscall.setup_new_contract again.
Usage:
from hathor import public, Context, BlueprintId, NCDepositAction, HATHOR_TOKEN_UID
@public(allow_deposit=True)
def create_child(self, ctx: Context, blueprint: BlueprintId) -> ContractId:
# Setup the new contract with an initial deposit
deposit = NCDepositAction(token_uid=HATHOR_TOKEN_UID, amount=1000)
accessor = self.syscall.setup_new_contract(
blueprint,
deposit,
salt=b'unique_salt_for_this_contract'
)
# Create the contract by calling initialize
contract_id, init_result = accessor.initialize(owner=ctx.caller_id)
return contract_id
Source code:
hathor.nanocontracts.initialize_method_accessor.InitializeMethodAccessor