How to Create, Test and Deploy Tezos Smart Contracts?
Blockchain technology is rapidly gaining traction as more and more companies are diving into its uses. From developing applications on existing blockchain platforms to building new blockchain platforms altogether, innovators are finding ways to make the use of blockchain technology more viable.
The Tezos blockchain platform was created as a “new-generation” decentralized platform. Founded by Kathleen Breitman and Arthur Breitman, Tezos is a decentralized, open-source blockchain platform to execute P2P transactions and deploy smart contracts. Tez or Tezzie (XTZ) is the native cryptocurrency of the Tezos blockchain platform.
Even though it sounds very much like its counterparts NEO, Ethereum, ICON, etc., a few significant features make it very different, and probably the most preferred choice for building dApps and deploying smart contracts today. These features include:
- Self Amendment Protocol – A self-amending cryptographic ledger designed to avoid hard forking and enable automatic platform updates.
- On-Chain Governance – Token holders can participate in protocol governance. The election cycle gives a systematic procedure to enable stakeholders to agree on the proposed amendments in the protocol.
- Liquid Proof of Stake Consensus Mechanism – A chain-based Proof of Stake consensus algorithm, also known as Liquid PoS. Bakers are responsible for creating and endorsing blocks and stake a portion of their own capital to incentivize honest behavior.
- Michelson Language – A low-level, stack-based programming language to write smart contracts on the Tezos blockchain and designed to facilitate formal verification.
Tezos enables its users to create smart contracts, which are small programs stored and executed on the blockchain. Smart contracts in Tezos are unique as they are written in Michelson. With the help of formal verification, Tezos makes smart contracts more dependable and secure.
This article is focused on Tezos smart contracts and will answer the following questions:
- What are the various components of Tezos smart contracts?
- How to create a Tezos smart contract?
- How to deploy and interact with a Tezos smart contract?
Let’s begin by learning about the various components of Tezos smart contracts.
What are the various components of Tezos smart contracts?
As the Tezos blockchain platform has unique features, the smart contracts it deploys also have many components that you must learn about, to seamlessly create and deploy Tezos smart contracts.
The important components of Tezos smart contracts are:
- Contract Type
- Transactions
- Storage Fees
- Code
- Fees
- Intra-Transaction Semantics
- Inter-Transaction Semantics
Let’s discuss these components in detail.
Contract Type
Presently, the Tezos ledger has two types of accounts that can hold tokens and be transaction destinations:
- Implicit Account – A non-programmable account whose tokens can be spent and delegated by a public key. Its address is the public key hash, starting with tz1, tz2, or tz3.
- Smart Contract – A programmable account with a unique hash, depending on its operation (starting with KT1) and transactions to it, can provide data.
According to Tezos, “a safe way to think about this is to consider that implicit accounts are smart contracts that always succeed to receive tokens, and does nothing else.”
Tezos uses stateful accounts instead of unspent outputs. Those accounts are generally known as contracts when they specify executable code. Also, since an account is a type of contract, even with no executable code, both are generally known as contracts.
Each contract has a “manager.” In the case of accounts, the managers are the owners of the account. Managers can spend funds associated with the contracts if the contract is labeled as spendable.
A contract is formally represented as:
type contract = { counter: int; (* counter to prevent repeat attacks *) manager: id; (* hash of the contract 's manager public key *) balance: Int64.t; (* balance held *) signer: id option; (* id of the signer *) code: opcode list; (* contract code as a list of opcodes *) storage: data list; (* storage of the contract *) spendable: bool; (* may the money be spent by the manager? *) delegatable: bool; (* may the manager change the signing key? *) }
Transactions
A transaction refers to a message sent from one contract to another. Transactions can be sent:
- from a contract, if they are signed by using the manager’s key.
- programmatically by code executing in the contract.
When a transaction is received, the amount is added to the destination contract’s balance. Then, the destination contract’s code is executed, which can:
- use the parameters passed to it.
- read and write the contract’s storage.
- change the signature key.
- post transactions to other contracts.
A transaction’s counter is responsible for preventing replay attacks. The counter increases by one after the transaction is applied, which prevents the transaction’s reuse. A transaction is invalid if the contract’s counter is not equal to the transaction’s counter.
A transaction message is represented as:
type transaction = { amount: amount; (* amount being sent *) parameters: data list; (* parameters passed to the script *) (* counter (invoice id) to avoid repeat attacks *) counter: int; destination: contract hash; }
Storage Fees
Storage inflicts a cost on the network. So, a minimum fee of XTZ 1 is charged for each increased byte in the storage. It is withdrawn from the contract’s balance.
Code
With its design inspired by Forth, Scheme, ML, and Cat, the Michelson language is stack-based, with:
- high-level data types.
- primitives.
- strict static type checking.
Michelson is the domain-specific language to write smart contracts on the Tezos blockchain, which doesn’t have any variables. A Michelson program is an instructions’ series running in sequence. Each instruction:
- receives the stack resulting from the previous instruction as an input.
- rewrites the input for the next instruction.
The stack contains:
- immediate values
- heap-allocated structures
There are also various other high-level languages available for programming Smart Contracts for Tezos, which can later be compiled into Michelson. These languages intend to simplify the development experience so that the focus is more on the content of Smart Contracts rather than the implementation.
Currently, options in development include:
- LIGO
- SmartPy
- Morley/Lorentz
Fees
Though the Tezos system handles transactions similarly to Ethereum, they handle fees very differently. Tezos simplifies the construction by imposing a hard cap on the number of steps for which programs can run. Execution can be broken down into multiple steps, and numerous program transactions can be used to fully execute a program if the hard cap is too tight.
As Tezos is amendable:
- caps can be changed in the future.
- advanced primitives can be introduced as new operation codes.
The signature key may also be changed by issuing a signed message requesting change if the account permits.
LeewayHertz Tezos Blockchain Development Services
Develop and launch your Tezos dApp with us.
Inter-Transaction Semantics
An operation included in the blockchain is a sequence of “external operations.” It is signed as a whole by a source address.
There are three kinds of operations:
-
- Transactions – for transferring tokens to implicit accounts, or transferring tokens and parameters to a smart contract (or a specific entrypoint of a smart contract)
- Originations – to create new smart contracts from its Michelson source code, an initial amount of tokens and initial storage contents transferred from the source
- Delegations – to assign the source’s tokens to another implicit account’s stake without transferring any tokens. Smart contracts emit “internal operations,” which are run in sequence after the external transaction completes, as in the following schema for a sequence of two external operations. Smart contracts called by internal transactions can also emit internal operations in turn. The internal operations of a given external operation’s interpretations use a queue, as in the following example, also with two external operations. Now that you understand Tezos smart contracts’ components, let’s move on to learn how to create, test, and deploy smart contracts.
How to Create, Test and Deploy a Tezos Smart Contract?
Here, we will show you how to create, test, and deploy a Tezos smart contract with the help of an example. We are developing a generic liquidity pool contract to enable users to deposit and withdraw Tezos tokens without any restrictions. In our example, user John will deposit 10 tez to the contract, and then user Katy will withdraw 5 tez.
How to create a Tezos Smart Contract?
We are going to write the contract in the LIGO language and then later compile it to Michelson.
Step 1: Install LIGO CLI
To compile and test the contract, we need to install the LIGO CLI. After installing it, we can start writing the contract by opening the following in our editor:
v1-public-pool.ligo
For our contract, we can create a folder and file with the following commands:
$ mkdir tezos-defi && cd tezos-defi $ touch v1-public-pool.ligo
Even though the current version of Tezos supports a unique entry point for each contract, we can use arguments indicating the operations we want to perform to work around this limitation. Here, we will illustrate two operations – Deposit and Withdraw. In our contract, we will represent them with a variant type, like the following:
type entry_action is | Deposit | Withdraw
Also, the storage will be a simple record with a liquidity field of the tez type, which will represent the funds in our pool.
type finance_storage is record liquidity: tez; end
Here, we are using tez for declarations and mtz for literals, as LIGO had an issue with mixing them up.
After this, we’ll work on defining the entry point.
Step 2: Define the Entry Point
The method chosen to listen to external communication is referred to as the entry point for a contract in Tezos. It receives two arguments:
- The parameters for the method.
- The contract storage.
Also, it has a fixed signature for the:
- response
- operation list
- storage
In the contract entry point, the parameter’s type will be entry_action, which we already defined as follows:
type entry_action is | Deposit | Withdraw
For this parameter, the value must be one of the values defined by either the Deposit or the Withdraw operation, while the method’s body will be empty. The output will be delegated according to the operation’s value, and the storage will be propagated in both cases.
function main (const action: entry_action; var finance_storage: finance_storage): (list (operation) * finance_storage) is block { skip } with case action of | Deposit(param) -> depositImp(finance_storage) | Withdraw (param) -> withdrawImp(finance_storage) end;
After defining the entry point, we will set up the deposit operation.
Step 3: Set Up the Deposit Operation
The deposit operation takes the amount sent to the transaction origin. If the amount is zero, it will fail.
As we’re going to modify the storage, we’ll define it as var.
function depositImp(var finance_storage: finance_storage) : (list(operation) * finance_storage) is block { if amount = 0mtz then skip //fail(“No tez transferred!”); else block { finance_storage.liquidity := finance_storage.liquidity + amount; } } with(noOperations, finance_storage) We will return noOperations as the first component in the return value and will define this in the contract's global scope. This defines a list initialized with nil. const noOperations: list(operation) = nil;
We will return noOperations as the first component in the return value and will define this in the contract’s global scope. This defines a list initialized with nil.
const noOperations: list(operation) = nil;
After setting up the deposit operation, we’ll set up the withdrawal operation.
Step 4: Set Up the Withdraw Operation
The “Withdraw” operation enables users to withdraw a fixed tez amount from the liquidity pool. To make the transfer to the sender, whether the contract has enough tez or not is checked by validation. We’ll use the global “sender” from LIGO, which refers to the transaction’s origination account.
If the validation succeeds:
- contract storage will revert
- liquidity pool will remain unchanged
function withdrawImp(var finance_storage: finance_storage): (list(operation) * finance_storage) block { const withdrawAmount: tez = 1000000mtz; var operations: list(operation) := nil; if withdrawAmount > finance_storage.liquidity then skip //fail("No funds to withdraw!") else block { const receiver: contract(unit) = get_contract(sender); const payoutOperation: operation = transaction(unit, withdrawAmount, receiver); operations:= list payoutOperation end; finance_storage.liquidity := finance_storage.liquidity - withdrawAmount; } } with(operations, finance_storage)
After the operations are set up, the smart contract is created and it is now time to test it.
How to deploy the contract?
The deployment of a Tezos smart contract is called “origination.”
It represents the creation of an account that has a script attached to the smart contract. The address of contracts created via originations starts with KT1… (originated accounts) instead of implicit accounts with addresses beginning with tz1… (also tz2 or tz3).
To deploy the contract, we run the following command:
# ./alphaclient.sh shell $ alpha-client originate contract v1-public-pool for contractOwner transferring 0 from contractOwner running v1-public-pool.tz --init 0 --burn-cap 2.314
tezos-client provides us with the following command to list the address of the contracts deployed:
# ./alphaclient.sh shell $ alpha-client list known contracts > contract: KT1HkL7uH53X12CZeH8Jv9H2AYHrA9M9xDUt Contract memorized as v1-public-pool.
Interacting with the contract
To make the contract functional, we must feed the contract with tez. So, we will deposit 10,000 tez using the contractOwner account.
The first step is to obtain the parameter to send to the contract’s entry point. We can get the Michelson code with the following command:
# /tezos-defi working directory $ ligo compile-parameter v1-public-pool.ligo -s pascaligo main “Deposit(unit)” > (Left Unit)
We can now call the contract
# ./alphaclient.sh shell $ alpha-client transfer 10000 from contractOwner to v1-public-pool --arg “(Left Unit)” --burn-cap 0.004
We can also verify the result in multiple ways. For example, we can query the contract storage:
# ./alphaclient.sh shell $ alpha-client get script storage for v1-public-pool > 10000000000
We can also check that the contractOwner wallet has decreased by 10,000 tez.
# ./alphaclient.sh shell $ alpha-client get balance for contractOwner > 8044.90585 ꜩ
We can also use a block explorer.
Now, we can try the withdraw operation. This time the katy account will withdraw some tez from our liquidity pool. As we have transferred 5 tez to katy, it will be enough to pay transaction fees. To obtain the Michelson instruction, we run the following command:
X-INT ~/develop/tezos-defi-dev-experience/tezos-ligo [master|...4] 15:54 $ ligo compile-parameter v1-public-pool.ligo -s pascaligo main "Withdraw(unit)" (Right Unit)
Now, we call the withdrawal contract method.
# ./alphaclient.sh shell $ alpha-client get balance for katy $ alpha-client transfer 0 from katy to v1-public-pool --arg “(Right Unit)” $ alpha-client get balance for katy
Conclusion
Tezos is a generic and self-amending crypto-ledger, so it is becoming one of the most popular blockchain platforms. Its unique features are attracting more and more people towards it. By following the right steps, one can conveniently deploy smart contracts on the Tezos blockchain platform.
Get in touch with our Tezos blockchain experts who can create and deploy Tezos smart contracts for your business.
Start a conversation by filling the form
All information will be kept confidential.
Insights
Generative AI for Regulatory Compliance: Benefits, integration approaches, use cases, best practices, and future trends
Generative AI is reshaping the field of regulatory compliance by enhancing risk management, boosting operational efficiency, and improving compliance monitoring.
Generative AI for marketing: Overview, use cases, integration strategies, and future outlook
Generative AI is transforming the marketing landscape by enhancing content creation, customer interaction, and data analysis.
Generative AI in due diligence: Integration approaches, use cases, challenges and future outlook
Generative AI is reshaping the due diligence landscape, establishing new data analysis and processing benchmarks.