Nano contracts: how it works
Nano contracts feature is being progressively rolled out but is still in beta testing, and currently only available on Hathor Network testnet. It is not yet available on Hathor Network mainnet.
Introduction
This article explains how the nano contracts feature works. Understanding this functioning is essential to anyone implementing nano contracts into a use case.
Glossary
This term is used throughout this article:
-
Nano contract is a type of smart contract exclusive to Hathor technology. Every smart contract deployed on Hathor blockchain is referred to as a nano contract. The distinguishing factor of a nano contract compared to a conventional smart contract is that Hathor platform provides contract templates, making it possible to create contracts on Hathor blockchain without needing to develop their code.
Architecture
The following diagram illustrates the architecture of the nano contracts feature:
This diagram depicts two elements of a full node: the source code of a Hathor client, and the copy of Hathor blockchain (i.e., the ledger). Let's detail what (concerning the nano contracts feature) is inside each of these two elements.
Hathor client (source code)
In the conventional smart contracts approach (as seen in Ethereum platform), each contract registered on chain has its own source code and state. Conversely, on Hathor, a nano contract registered on chain does not have its own source code. Rather, each nano contract instance has its state and a reference to a blueprint.
A blueprint is the source code used to model multiple use cases, from which nano contracts are instantiated. In other words, all nano contracts referencing the same blueprint share the same source code.
A blueprint has attributes and methods that model the behavior of the nano contracts instantiated from it. Hathor platform provides a set of built-in blueprints, which make up a catalog.
The left side of the diagram, within the Hathor client, shows the representation of the blueprints and the catalog they comprise. In the blueprint catalog, we can see all the built-in blueprints available on Hathor platform for contract creation. Further to the left, we see the representation of the source code for one of these blueprints, in this case, the liquidity pool.
Hathor blockchain (ledger)
On the right side of the diagram, we have Hathor blockchain. Within it, the representation of all registered contracts, and alongside, all transactions related to each of these contracts.
A nano contract recorded on chain is defined by:
- a unique id;
- a reference to a blueprint; and
- a state
Still on the right side of the diagram, one can see in instance A the representation of these elements. The arrow extending from instance A to the left side of the diagram represents that this nano contract references the liquidity pool blueprint. In this instance, we also see the contract's stored state.
Finally, we see a linked list of transactions representing all transactions recorded on Hathor blockchain related to this nano contract. The first transaction is the one that created the contract, and all subsequent ones execute the contract.
Differently from conventional smart contracts, when creating a new nano contract, a user does not provide the source code of the contract. Rather, they select a blueprint, and provide all data that such blueprint needs to initiate its first state. This is done by submitting to the blockchain a transaction to create a nano contract.
Anatomy
The following diagram depicts the anatomy of a nano contract:
A nano contract has a unique id and an instance of a blueprint class, which includes attributes and methods. Additionally, it has a multi-token balance. The values of its attributes, combined with the multi-token balance, constitute the state of the contract.
Multi-token balance
Just like a regular wallet, a contract can hold any amounts of multiple different tokens, which comprise its multi-token balance. The balance of a contract is changed through deposits and withdrawals. The contract is both the owner and custodian of the balance and any deposit or withdrawal is authorized by it. However, the balance of a contract is not one of the attributes of a contract.
Attributes
A contract has a set of attributes. With the exception of the multi-token balance, all other contract data is stored as attributes. During its execution, a contract can change the value of its attributes but cannot directly change its balance. Instead, it authorizes or denies the entire set of deposits and withdrawals requested by a user.
A contract may specify which tokens it can transact with in its attributes. For example, contract ABC might specify in its attributes that it only authorizes deposits in tokens A, B, and C, limiting its multi-token balance to these tokens. Conversely, contract CDE might not restrict its operations to specific tokens, potentially authorizing deposits in any token registered on Hathor blockchain.
State
The set of all attributes of a contract, with their respective values in a given moment, combined with its multi-token balance, defines the state of the contract. Hathor blockchain keeps the state of all recorded contracts. This means that Hathor uses the account-based model for contracts bookkeeping.
To know more about the bookkeeping model of Hathor, see Bookkeeping model at encyclopedia
Methods
Blueprints, and as a consequence, nano contracts, have three types of methods1:
- Public methods
- View methods
- Internal methods
Public methods
Public methods are used to create or execute a contract. A smart contract is a program that runs only when called. Thus, executing a contract means executing one of its public methods in order to change its state. In other words, they provide the functionalities of the contract to users.
Public methods are the only methods whose execution may result in changes to the contract state. Although not mandatory, in most cases, it is expected that the successful execution of a public method results in changes to the contract's state. In turn, a change in a contract's state is always the result of its execution.
Public methods do not directly change the contract's balance. This means that they don't contain statements that increase or decrease the amount of tokens the contract holds. Instead, they only authorize or deny withdrawal and deposit requests made by users, and it is up to Hathor protocol to perform these requests.
Public methods are called by users only from within a nano contract transaction submitted to Hathor Network. To create or execute a contract, a user submits a new nano contract transaction to Hathor Network, specifying a public method to be called. Thus, as part of its transaction processing, Hathor protocol calls the public method, as requested by the user.
For example, suppose a certain nano contract registered on Hathor blockchain has a functionality to provide loans. This contract has the public method request_loan
which, when called, will evaluate and authorize or deny a user's loan request. To execute this contract, Alice submits a nano contract transaction to Hathor Network calling the public method request_loan
. During its execution, this method may change the values of any of its attributes and will authorize or deny the loan, which is requested as a withdrawal from the contract's balance. These changes in the attributes and the contract's balance are what we refer to as changes in the contract's state.
To reiterate:
- Public methods are utilized by users to create or execute nano contracts.
- To utilize the functionality of a contract, a user needs to call the respective public method.
- To call a public method, a user needs to submit to Hathor Network a nano contract transaction requesting the call.
- Public methods cannot directly change the contract's balance; in fact, no method can directly change the contract's balance.
- Public methods can change their contract's attributes.
- During execution, public methods update the contract's attributes and authorize or deny Hathor protocol to perform the deposits and withdrawals requested by the user.
View methods
View methods perform logic whose only effect is their return value and serve both other methods and Hathor users. They can be called internally by another method of the contract. They can also be called by users via the Hathor full node API for querying purposes.
View methods cannot change the state of the contract. They can be used to handle part of the business logic of the contract, as long as they do not attempt to change the contract's attributes.
Regarding users, the full node API already provides endpoints for reading the state and transaction history of contracts. However, sometimes a user may need information that is not directly available in the contract's state but can be computed and returned by a view method. For this, users can send requests to the full node API to query view methods.
For example, consider that the loan contract has a public method request_loan
and a view method assess_credit
. If Alice wants to request a loan, she submits a nano contract transaction that calls request_loan
with her loan details and credit information. During execution, request_loan
calls assess_credit
to evaluate Alice's credit and decide whether or not should provide the loan. Now, if Alice wants to check her credit conditions before making the loan request (a public method call), she can use a full node API request to directly call assess_credit
.
To reiterate:
- View methods are utilized by the contract to implement part of its business logic, and by users to obtain information not directly available in the contract's state.
- View methods cannot be called from blockchain transactions.
- View methods can be called internally by other methods and externally by users via the full node API.
- View methods cannot change the contract state.
- The only effect of a view method execution is its return value.
Internal methods
Internal methods are used to implement internal logic of the contract. They are not available externally, to respond to user calls, neither via transaction — such as public methods — nor via full node API requests — such as view methods. They can only be called internally by other methods.
For example, the loan contract might have an internal method authorize_loan
, which is called during the execution of the public method request_loan
. It will receive as parameters Alice's credit profile, generated by assess_credit
, and should return TRUE
or FALSE
to authorize or deny the loan. In this way, since assess_credit
is a view method, Alice may query it before applying for a loan to know her credit profile and her chances of getting the loan. However, since authorize_loan
is internal, only during the execution of request_loan
will Alice find out whether her loan will be authorized or not.
To reiterate:
- Internal methods are utilized internally by the contract to implement part of its business logic.
- Internal methods cannot be called from blockchain transactions like public methods.
- Internal methods cannot be called from the full node API like view methods.
- Internal methods can only be called internally by other methods.
Transactions
A nano contract transaction (also known as NC transaction) is a type of transaction used to create or execute a nano contract. The following diagram depicts the anatomy of a nano contract transaction:
Specific fields
When creating an NC transaction, there are three fields that a user must provide that do not exist in other types of transactions:
nc_id
nc_method
nc_args
nc_id
The nc_id
for contracts is the ID of the transaction that registered them. In turn, nc_id
for blueprints is the identifier used in the blueprints catalog.
nc_method
nc_method
identifies the method of the blueprint that will be called. Only public methods can be called from NC transactions. Each blueprint implements its own set of methods, — according to its own business logic — that dictates how the contract works. There is only one method that is mandatorily implemented in all blueprints: initialize
. initialize
is a public method used to create new contracts and must always be used along an nc_id
that identifies a blueprint.
nc_args
nc_args
is the set of arguments that should be passed in a method call. Each method of each blueprint requires a different set of arguments.
Inputs/outputs → deposits/withdrawals
The set of inputs and outputs of an NC transaction is translated into a set of actions that the user wants to perform to/from the contract. An action can be a deposit or a withdrawal that a user wants to make to/from a contract.
To understand how this translation works, let's compare it with ordinary fund transfers. The following diagram shows the relationship of inputs and outputs in a transaction where Alice transfers 10 tokens A to Bob:
Note that in this case, the sum of the inputs amounts matches the sum of the outputs amounts. This does not happen in NC transactions. Unlike the transfer between Alice and Bob, where Alice consumed a UTXO as input and Bob received a UTXO as output, a contract neither consumes nor receives UTXOs. Instead, a user performs deposit and withdrawal actions in a contract. These actions are translated from the difference between the sums of inputs and outputs.
The following diagram shows the relationship of inputs and outputs in an NC transaction where Alice wants to deposit 10 tokens A into a nano contract:
Note that the sum of the inputs is 10 tokens A greater than the sum of the outputs. For a given token, when the sum of inputs is greater than the sum of outputs, it translates as a deposit. Now, suppose Alice wants to withdraw 10 tokens B from a nano contract:
Note that the sum of the outputs is 10 tokens B greater than the sum of the inputs. For a given token, when the sum of outputs is greater than the sum of inputs, it translates as a withdrawal. Now suppose Alice wants to deposit 10 tokens A and withdraw 10 tokens B:
Note that a single NC transaction can have none, one, or multiple deposit and withdrawal actions in any combination. Additionally, since the translation into actions is based on the difference in the sum of amounts between inputs and outputs, it is not possible to have two actions related to the same token. In other words, Hathor protocol consolidates all inputs and outputs related to a single token into a single deposit or withdrawal action.
For example, this prevents a scenario where "Alice wants to deposit 10 tokens A into the contract and withdraw 20 tokens A from the contract". In this case, Hathor protocol consolidates that "Alice wants to withdraw 10 tokens A from the contract".
Workflow
A nano contract transaction has the following workflow:
- Submission
- Validation
- Method execution
- Resolution
The following diagram illustrates the workflow of a nano contract transaction:
Submission
A user creates a new nano contract transaction — filling the nano contract's specific fields nc_id
, nc_method
, nc_args
— and submits it to Hathor Network.
Validation
Hathor protocol then begins the validation phase of the transaction. First, the common validations applicable to all transactions are performed. If the transaction passes these validations, the specific validations for NC transactions are then conducted. If the transaction is considered valid, it is added to the mempool, to wait for the next block. At this point, the execution of the contract has not happened yet. A contract is only executed when the transaction is confirmed by a block.
Method execution
When a new block is received, as part of the process of adding the transaction to the blockchain, Hathor protocol invokes the public method specified in the NC transaction to create/execute the contract.
During its execution, the public method may initialize/change any of the contract's attributes and must decide whether to authorize or deny the entire set of deposits and withdrawals requested by the user in the transaction.
If the public method completes without returning an exception, it means that the contract has authorized all the requested deposits and withdrawals. Conversely, if the public method returns an exception to Hathor protocol, it means that it has denied the entire user request.
For example, suppose Alice wants to obtain a loan of 100 tokens A from a loan contract. To do this, she submits an NC transaction calling the request_loan
method with a request to withdraw 100 tokens A. When invoked for execution by Hathor protocol, request_loan
performs a credit assessment of Alice to decide whether to authorize the loan request. request_loan
does not return an exception, indicating that it authorizes the loan.
Now, suppose Bob wants to obtain a loan of 1000 tokens A from the same loan contract. He then submits an NC transaction calling request_loan
with a request to withdraw 1000 tokens A. When invoked for execution by Hathor protocol, request_loan
performs a credit assessment of Bob and returns an exception. This indicates that the contract did not authorize the loan.
Resolution
Upon the completion of the public method's execution, Hathor protocol regains control. Up to this point, no changes to the contract state have been committed. That is, no attribute updates made by the called method are recorded in the ledger. It is up to Hathor protocol to commit these changes.
If Hathor protocol does not receive an exception as the return from the called public method, it considers the contract creation/execution successful. Hathor protocol commits the attribute changes made during the method's running to the ledger and carries out the entire set of deposits and withdrawals, meaning the contract's state is updated.
Conversely, if Hathor protocol does receive an exception as the return from the called public method, it considers the contract creation/execution a failure. Hathor protocol then discards the attribute changes made during the method's execution and does not carry out any deposits or withdrawals, leaving the contract's state unchanged.
Regardless of the outcome of the contract creation/execution, the NC transaction is added to the blockchain. If the contract's running succeeds, the transaction is marked as 'success'. Otherwise, it is marked as 'fail' and its inputs/outputs are voided.
For example, in Alice's case from the previous subsection, request_loan
returned nothing to Hathor protocol. Hathor protocol does not know the business logic of the loan contract. All it knows is that request_loan
made several updates to its attributes and authorized the completion of the entire set of deposits and withdrawals described in the transaction. In Alice's case, it is a single withdrawal of 100 tokens A. Hathor protocol will then update the attributes in the ledger and subtract 100 tokens A from the contract's balance. Finally, it will add the transaction to the blockchain marked as 'success', formalizing the transfer of 100 tokens A from the contract's balance to Alice's wallet.
In Bob's case, request_loan
returned an exception to Hathor protocol. Hathor protocol understands that there was a failure in the contract's execution and that the contract did not authorize the completion of the entire set of deposits and withdrawals described in the transaction. In Bob's case, it is a single withdrawal of 1000 tokens A. Hathor protocol will then discard the changes made during the method's running and will not make any update to the contract's state. Finally, it adds the transaction to the blockchain marked as 'fail', with its inputs/outputs voided.
Life cycle
The following diagram illustrates the life cycle of a nano contract:
The life of a contract begins with the transaction that creates it — i.e., deploys it on chain. From this point, users can read and execute the contract as long as the blockchain network remains operational — in other words, it lasts indefinitely. Contract executions are performed via transactions, while contract readings are typical readings from the blockchain. Contract executions may either succeed or fail.
The contract life line depicted in the diagram represents the history of a contract. The history of a contract comprises the sequence of all transactions registered in the blockchain related to such contract. The history of a contract always begins with the transaction that created the contract, and proceeds with all valid transactions that attempted to execute the contract — i.e., those that succeeded in executing the contract, and those that failed to execute the contract.
Reading the contract doesn't count as part of the contract history — i.e., if one reads a contract this interactions will not be added to this contract's history. Furthermore, contract creation, execution and reading are the possible interactions that one may have with a contract. In the next section we discuss in detail how these interactions comprise an overall mechanics of a nano contract.
Mechanics
The following diagram illustrates the overall mechanics of a nano contract, throughout its life cycle:
This diagram provides an overview of the possible interactions with a nano contract, how these interactions take place, and who the participants are. In the following subsections, we explain these elements in detail.
Roles
There are three different roles that Hathor users may assume once interacting with a contract:
- contract creator;
- contract user; and
- oracle.
Contract creator
A contract creator is the one that submits a nano contract transaction proposal to create a new contract. Once a contract is created, this role is over, and the contract creator has no influence over the contract state and work, throughout the remaining life cycle of the contract.
In other words, a contract creator has no administrative authority over their contract. They cannot remove the contract, make it stop to work, or forbid specific contract users to use it.
Contract user
A contract user is anyone who reads or executes a contract.
Oracle
An oracle is a special type of contract user that provides off-chain data that a contract may need to work. A blueprint may require none, one, or multiple oracles to address different off-chain data needs. For example, in a sports bet contract, an oracle is required to inform the result of a match.
Note that although the contract creator does not have administrative authority over a contract, such authority can be implemented within a blueprint via oracles.
Interactions
There are three possible interactions with a contract:
- creation;
- execution; and
- reading.
Contract creation
The following diagram illustrates the creation of a contract:
To create a contract, a contract creator should choose a blueprint, and submit to Hathor Network, a nano contract transaction filled with the nc_id
of the chosen blueprint, initialize
as the nc_method
, all required arguments of nc_args
, and inputs and outputs (according to the initialize
method of each blueprint).
Remember that inputs and outputs are translated by Hathor protocol as deposits and withdrawals, respectively. Therefore, an NC transaction for creating a contract should never have withdrawals, as it makes no sense to attempt to withdraw from a contract that does not yet exist. In turn, the creation of a contract may or may not be accompanied (or even tied) to deposits.
Let's revisit the loan contract example from the previous subsections. Suppose there is a 'lending' blueprint on Hathor platform that allows the creation of loan contracts. A bank intends to use this blueprint to offer 10 million tokens A for loans. To do this, it must submit an NC transaction to Hathor Network such that:
nc_id
references the lending blueprint on Hathor platform;nc_method
calls theinitialize
method;nc_args
sends a set of arguments as defined in this blueprint's specificinitialize
implementation to set up the loan contract attributes; andinputs
andoutputs
consolidate a deposit of 10 million tokens A to fund the contract.
Contract execution
The following diagram illustrates the execution of a contract:
To execute a contract, a user should submit to Hathor Network a nano contract transaction filled with the nc_id
of the contract, the nc_method
that will be called, the due arguments in nc_args
, and the due inputs and outputs.
There are two types of contract executions: (1) those that involve deposits and withdrawals within a contract, and (2) those that provide off-chain data to a contract. The first type (1), involving deposits and withdrawals, is carried out by regular users of the contract; the second type (2), providing off-chain data, is performed by oracles.
In other words, whenever a contract user who is not an oracle requests the execution of a contract, they are interested in making one or more deposits and/or withdrawals from the contract.
For example, to obtain the loan described in the previous subsection, Alice must submit an NC transaction to Hathor Network such that:
nc_id
points to the specific loan contract registered on Hathor blockchain;nc_method
calls therequest_loan
method;nc_args
sends a set of arguments as defined in this blueprint's specificrequest_loan
implementation to evaluate Alice's credit conditions; andinputs
andoutputs
consolidate a withdrawal of 100 tokens A, which constitutes Alice's loan request.
As for oracles, for each oracle role defined in a blueprint, a corresponding set of public methods is also defined, exclusively for use by the assigned oracle.
For example, in the lending blueprint, there could be a public method such as set_interest_rate
accessible only to the bank manager acting as an oracle. This method allows the oracle to adjust the interest rates applicable to the loans managed by the contract.
Contract reading
The following diagram illustrates the reading of a contract:
Anyone can read all data of a contract. Reading a contract does not need and cannot be done within a nano contract transaction. Moreover, there are three types of readings that can be made in a contract:
- state reading;
- history reading; and
- querying the contract.
State reading means reading the current state of the contract — i.e., the value of the contract's attributes and its multi-token balance. History reading means reading the history of the contract — i.e., the ordered list of all NC transactions that called the contract's execution. Finally, querying the contract means requesting information that is not readily available in the contract's state but can be computed and returned by a view method of the contract from its state.
To better understand each of these three types of reading, let's revisit the loan contract example. A user can use the state reading endpoint of the full node API to read the value of the contract's attributes. They might do this, for instance, to check if the contract has funds of the token they want to borrow or what the standard interest rate is. They can use the history reading endpoint to find out how many times the contract has approved or rejected loan applications. Finally, they can use the query endpoint to request the execution of the view method assess_credit
to determine, without sending an NC transaction, the likelihood of obtaining the loan by providing their credit profile as arguments.
Key takeaways
This article explained how nano contracts works dividing it into the following sections:
- In 'Architecture', we explained how the nano contracts feature works at the protocol level.
- In 'Anatomy', we presented the elements that an instance of a nano contract recorded on chain has.
- In 'Transactions', we explained the anatomy and workflow of the transactions related to nano contracts.
- In 'Life cycle', we explained what happens with an instance of a contract recorded on chain once it is created.
- And finally, in 'Mechanics', we explained how this life cycle takes place, by detailing the roles that Hathor users may assume while interacting with contracts and what these interactions are.
With this, we covered all the theory one needs to know to be capable of implementing nano contracts into a use case. Note that this theory holds the same regardless of the wallet application being used, but how to put it into practice differs for each wallet application.
What's next?
- About nano contracts: to get an overview of nano contracts.
Footnotes
-
Hathor adopts a nomenclature for its method types inspired by the functions nomenclature in Solidity. This way, there is similarity between the behavior of public, view, and internal methods in Hathor and their counterparts in Solidity. This is done to make nano contracts intuitive and easy for blockchain developers. However, note that there is no exact match between a method type in Hathor and the corresponding function in Solidity. Therefore, although they share the same essence, not everything that applies to a public function in Solidity applies to a public method in Hathor, and so on. ↩