Smart Contract Development on Michelson (Tezos)

Tezos contracts fail formal verification when the architecture does not account for Michelson's specifics. We design and develop smart contracts turnkey — from logic audit to deployment. Our engineers eliminate failure classes before launch, reducing rework time.

Blockchain Development Services

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1375
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1349
  • B2B Advance company logo design
    B2B Advance company logo design
    794
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1087
  • AIDER company logo development
    AIDER company logo development
    1032
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1129

Michelson: why Tezos contracts break during formal verification

Michelson is a low-level language with a stack machine, where there are no familiar variables and loops: a program is assembled from a sequence of instructions, each of which pops values off the stack and puts the result back.

An error in the order of operands or in a type annotation does not "surface" at runtime — the contract simply fails formal verification and is not published to the network.

That is why smart contract development on Tezos begins not with code, but with a precise model of types and data flow.

The most common reason for failing verification is Michelson type errors. The stack machine strictly monitors what is on top and requires explicit annotations (:int, :nat, :address) where the type cannot be unambiguously inferred.

If an annotation diverges from the actual value — for example, nat is passed where int is expected — verification rejects the contract as a whole, not just a single branch.

The second category is unaccounted-for side effects.

Operations such as TRANSFER_TOKENS or CREATE_CONTRACT change the state of the network, and they cannot be called at an arbitrary place: the order of operations and failure handling (FAILWITH) must be designed so that partial execution does not leave the contract in an inconsistent state.

Tezos gas cost is a separate blow to a project. Suboptimal data structures — large map and big_map, unnecessary intermediate copies, sloppy loops via ITER — inflate gas consumption, and a transaction may fail to fit within the limit. We break down how this is addressed at the stage of designing types and structures.

What a well-designed Michelson contract architecture delivers

Storage architecture and operation cost

Michelson has no dynamic data structures in the usual sense: big-map is placed in a separate store, and the cost of an operation is determined by the volume of records in storage and the number of interpreter steps.

Therefore, laying out state across several big-maps with fixed-shape keys instead of a single nested tree reduces the cost of each transaction: the interpreter does not need to unfold deep nesting, and an update touches only one branch.

On real contracts, such restructuring cut gas consumption on mass operations by several times — with the same functionality.

Formal verification as part of the architecture

A contract where invariants are extracted into separate verifiable functions rather than smeared across IF branches passes formal verification faster and with fewer manual assumptions.

We design so that key properties — balance preservation, unavailability of repeated withdrawal, correctness of rights — are formulated as separate lemmas.

This eliminates an entire class of failures that tests do not catch: races between calls, unhandled FAILWITH branches, implicit overflows.

Comparison of development approaches

Approach When it fits Limitations
Hand-written Michelson Strict gas requirements, auditing of critical sections More expensive to maintain, higher entry barrier for the team
Compilation from a high-level language Fast product delivery, frequent logic Less control over storage layout and gas consumption
Hybrid: core in Michelson, compilable wrapper Complex contracts with a critical core Requires discipline at the boundary between layers

Security and verifiability

A separate layer is static analysis and type checking before deployment: it cuts off errors that rarely manifest on a test network but on the mainnet lead to loss of funds.

We fix invariants in the code and in the specification so that the auditor verifies properties rather than reconstructing the intent.

Michelson — a low-level stack-based Tezos language, where the cost and security of a contract are laid down at the stage of state layout rather than optimized afterwards.

Stages of development and deployment of a Michelson contract

Development of a Michelson contract begins not with code, but with formalizing the rules: who calls the entrypoints, which states are permissible, and what happens when conditions conflict.

At this stage we fix the storage scheme — the layout across big_map and regular storage fields — because the cost of operations and the ease of future changes depend on it.

An error in the data model is the most expensive: fixing it after launch requires migration and a repeat audit.

Next comes design, implementation, and verification — each step with a reproducible result that can be handed off to another team.

  1. Audit of the task and the contract's boundaries. We analyze the business logic, participant roles, external calls, and limits. The output is a specification of entrypoints and invariants that the contract must preserve under any scenario.
  2. Design of the storage scheme and call flows. We define the storage structure, Michelson data types, and placement of collections in big_map. We verify that operations do not hit gas limits as the number of records grows.
  3. Implementation in Michelson. We write the contract and auxiliary view entrypoints, keeping the logic flat and explicit — complex branching complicates the audit and raises the cost of an error. We compile against the current branch of the tooling.
  4. Testing. We run unit scenarios for successful and failing calls, check boundary states and access rights. Separately, we model behavior under parallel calls and repeated operations.
  5. Contract audit. An independent review of the logic, rights, and exception handling before publication. We close all findings with a record of changes so that edits do not break already-verified invariants.
  6. Deployment to the test network. We run the full lifecycle scenario on a copy of real data, measure gas consumption and behavior under failures.
  7. Handover of the contract to the client. We deliver the source code, storage scheme, audit report, and call instructions. The client's team can maintain the contract without us.

Describe your task — we will come back with an estimate and a plan for each stage.

How is Michelson different from high-level languages for Tezos?

Michelson is a low-level stack language into which any Tezos contract is ultimately compiled. High-level languages (LIGO, SmartPy, Archetype) provide syntax, types, and abstractions, but the same bytecode ends up on chain.

Hence the practical conclusion: michelson vs liquidity is not about "more powerful or weaker," but about which layer the team wants to control the result at.

Compilation to Michelson is not free in terms of predictability. The compiler may unroll a loop, add type checks, or lay out storage differently — and you get a contract that works but consumes gas differently than you calculated.

Direct development in Michelson removes this layer: you see every instruction, every stack access, and you know exactly the storage size and call cost.

This is critical for contracts with a hard gas limit or for an audit where it is important to prove the absence of hidden execution paths.

The flip side is the cost of maintenance. Michelson lacks the usual higher-order functions, dictionaries, and strings in the form developers are used to.

Logic like "distribute shares among participants" turns into dozens of lines of manual stack management, where it is easy to mix up the order of operands. An error here costs more: types will not catch semantic confusion, and a deployed contract cannot be rolled back.

Therefore, comparing smart contract languages for Tezos in practice comes down to choosing an entry point. A high-level language is chosen for business logic where development speed and readability matter: tokens, voting, registries.

Michelson — when manual control over gas is needed, non-standard cryptography, or when the contract is written as a reference for audits and subsequent implementations.

Choosing a Tezos language is rarely "either-or." A working scheme: the core logic is written in a high-level language, while critical parts — signature verification, working with binary data, storage optimization — are moved into separate contracts in Michelson and called from the main one. This way the team gets both development speed and predictability where it matters.

Case study: how we eliminated a class of failures in a Michelson contract

The client came with a Michelson contract that worked "mostly fine": the audit was passed, tests were green, but in production some calls failed with an execution error, and the fee for successful operations varied across a wide range.

The investigation revealed a classic issue — computations in storage without accounting for the order of operations and branching, where FAILWITH triggered only after gas had already been deducted. The contract's behavior depended on state that no one checked before entering the critical branch.

What we changed

We rewrote the entry point: precondition checks (amount, permissions, value bounds) were moved to the beginning, before any changes to storage. The logic was split into small internal functions so that each branch could be read and tested separately. Duplicate computations were removed — intermediate values are calculated once and reused.

Metrics before and after

Metric Before After
Share of failed transactions noticeable isolated cases on boundary data
Gas consumption variance wide narrow, predictable
Incident investigation time hours minutes from logs

Failures of the "failed after deduction" class are gone entirely — now any error is returned before the state changes, and the caller sees the reason rather than the fact of deduction.

What this delivered

The contract became predictable: the same input yields the same gas consumption, and behavior on boundary values is described in tests rather than discovered in production.

We relied on the formal execution model of Michelson — it allows invariants to be verified before deployment, not after. For the client, this meant fewer manual investigations and a clear fee budget for each operation.

Optimizing a Tezos contract without sacrificing readability is a balance between strictness of checks and execution cost. We maintain it through tests for every branch and explicit preconditions.

Doubts before starting: compatibility, migration, access

Michelson compatibility with already running infrastructure is the first question we resolve before starting. A Michelson contract compiles into the same format as existing contracts on the Tezos network, so calls from external services, indexers, and wallets remain functional.

If you already have contracts in LIGO or SmartPy, migration does not require rewriting from scratch: we port the logic to Michelson, keep entry point interfaces compatible, and transfer the state with a separate script verified on a test network.

We design access and roles before deployment, not after. Administrative entry points (logic upgrade, pause, withdrawal of funds) are secured with multisignature, while operational ones use separate keys with a limited set of permissions. This eliminates the class of failures where a single compromised key exposes the entire contract.

What if the contract is already deployed and cannot be changed?

Michelson does not allow editing the code of an immutable contract, but a logic upgrade is built in advance — through a proxy contract or an entry point with permission checks.

If that option is not available, we analyze the current state, port the logic to a new contract, and prepare a transition plan with data preservation. Rollback to the previous version remains possible until the old contract is decommissioned.

What if we have our own team and want to maintain the code ourselves? We hand over not only the source code but also the reasoning behind decisions: why this storage structure was chosen, where the bottlenecks in gas consumption are, which checks are mandatory during upgrades. The client's engineers take over maintenance without us.

What if an audit is needed before launch? We prepare the contract for external review: remove unused entry points, document invariants, and attach test scenarios — this reduces audit time and the number of findings.

The client receives:

  • Michelson contract source code with comments for each entry point;
  • a role diagram and access matrix: who can call what;
  • deployment and state migration scripts verified on a test network;
  • a set of tests covering key scenarios and edge cases;
  • instructions for logic upgrades and the procedure for rollback;
  • documentation on how the contract interacts with external services.

Describe your task — we will come back with an estimate and a plan: what we migrate, which roles we secure, and in what order we move to deployment.

Timeline for Michelson contract development by stage

The timeline for Michelson is determined not by the volume of code, but by the formalization of invariants: a contract on Tezos describes state and transitions, and any ambiguity in the requirements surfaces at the verification stage, not while writing code.

That is why we build the schedule from the storage model and the list of conditions, not from the "number of lines." Below are approximate durations for the stages; they are not tied to calendar dates and are refined after the task is analyzed.

The spread in timelines comes precisely from the verification stage: the more external calls and conditions in the contract, the longer the analysis of edge cases takes.

If the data model and transitions are fixed in advance, implementation proceeds predictably, and post-handover revisions are kept to a minimum.

We work on the current branch of Tezos tools — Michelson and related development tools, so the contract is written right away to meet the network's current requirements, without rewriting for outdated syntax. Describe your task — we will get back to you with an estimate and a stage-by-stage plan.