Most times, when people start learning how to write smart contracts, the first thing they hear about is Solidity and Ethereum. That was the first thing I heard about too. It's what most tutorials focus on, and for good reason. Solidity made it possible to write programs that live on a blockchain, and Ethereum became the place where many people got started.

But Solidity isn’t the only smart contract language out there. And Ethereum isn’t the only blockchain that supports decentralized applications.

There’s also TON, short for The Open Network. It was created by Telegram, but it's now a public, community-driven chain. It’s fast, lightweight, and handles things a bit differently from what you might be used to on Ethereum. That includes how smart contracts are written. When I started exploring the TON documentation, I came across four different languages for writing smart contracts: Tact, Tolk, FunC, and Fift. I won’t go deep into all four here.

This guide focuses on the Tact language, and we’ll see how to use it to build a basic voting contract that lets users cast votes and check results on-chain.

Why I Decided to Learn Tact First

The TON ecosystem actually supports multiple languages, each serving different use cases, levels of abstraction, and developer experience. Here’s a quick overview of the of each of them:

Tact provides and a quicker path for building and deploying contracts on the TON blockchain.

Understanding How Tact Works

Before we start writing code, it’s important to understand how Tact smart contracts are structured. A typical Tact contract includes a few core components:

Tact uses message-based communication, which is how all interactions on TON work. Each contract receives a message and processes it in its own receive block. This message-based structure helps organize your contract logic in a modular, maintainable way.

Let’s now apply this in a real example by building a simple voting contract.

Building Your First Voting Contract with Tact (Using the TON Web IDE)

In this section, we'll walk through how to implement a basic voting system using Tact. This voting contract will allows users to vote for predefined candidates and tracks the total number of votes each candidate receives.

We’ll be doing everything inside the TON Web IDE, which is an in-browser tool where you can write, build, and test your contracts without installing anything locally.

Step 1 – Open the TON Web IDE

Step 2 – Writing the Voting Contract Code

After creating your project, open the main.tact file. You’ll see a boilerplate setup:

// Import the Deployable trait so the contract can be deployed easily
import "@stdlib/deploy";

contract BlankContract with Deployable {
    init() {
        
    }
}

Now, let’s map out our own code.

First, we’ll define the message structure for voting:

// Import the Deployable trait so the contract can be deployed easily
import "@stdlib/deploy";

// Define a message structure for voting
message Vote {
    candidate: Int as uint32; // 1 = Alice, 2 = Bob
}

This is the Vote message. When someone wants to vote, they’ll send a message to the contract that includes a number:

Tact uses this structure to process the incoming vote and decide which candidate gets the point.

Next, we’ll set up our contract and add two state variables to keep track of each candidate’s votes:

...
contract VotingContract with Deployable {

    // State variables to track votes
    votesAlice: Int as uint32;
    votesBob: Int as uint32;

Inside the contract, we defined two variables:

We’ll now initialize those vote counts to zero inside the init block to set the starting state of the contract when it's first deployed.

    init() {
        self.votesAlice = 0;
        self.votesBob = 0;
    }

The init block runs only once, right when the contract is deployed and it sets both vote counts to zero.

Now comes the logic. When a vote is sent, we want the contract to check who the vote is for and increase the correct vote count.

    // Handle vote messages
    receive(msg: Vote) {
        if (msg.candidate == 1) {
            self.votesAlice += 1;
        } else if (msg.candidate == 2) {
            self.votesBob += 1;
        }
    }

So when a vote is received:

Finally, we’ll create getter functions to let anyone query the vote count for each candidate without changing the contract state.

    // Getter for Alice's votes
    get fun getVotesForAlice(): Int {
        return self.votesAlice;
    }

    // Getter for Bob's votes
    get fun getVotesForBob(): Int {
        return self.votesBob;
    }
}

These two getter functions let us check the number of votes each candidate has received without modifying anything in the contract. It’s a read-only operation.

Below is the full voting contract code:

import "@stdlib/deploy";

// Define a message structure for voting
message Vote {
    candidate: Int as uint32; // 1 = Alice, 2 = Bob
}

contract VotingContract with Deployable {

    // State variables to track votes
    votesAlice: Int as uint32;
    votesBob: Int as uint32;

    init() {
        self.votesAlice = 0;
        self.votesBob = 0;
    }

    // Handle vote messages
    receive(msg: Vote) {
        if (msg.candidate == 1) {
            self.votesAlice += 1;
        } else if (msg.candidate == 2) {
            self.votesBob += 1;
        }
    }

    // Getter for Alice's votes
    get fun getVotesForAlice(): Int {
        return self.votesAlice;
    }

    // Getter for Bob's votes
    get fun getVotesForBob(): Int {
        return self.votesBob;
    }
}

Step 4 – Build and Deploy the Contract

Step 5 – Interact With the Contract

Once deployed, scroll down and you’ll see two sections:

To cast a vote: In the Vote section, enter 1 in the candidate input field and click Send. You’ve just voted for Alice! You can repeat this to cast more votes.

To check the vote count: Click Call under getVotesForAlice and check the logs panel to see the vote count

In my test run, I voted for Alice 9 times and Bob 6 times, and the getter functions showed exactly that.

💭 Final Thoughts: Keep Building, Keep Exploring

🙌 Congrats if you read all the way through!

Now that you’ve seen how a simple voting contract works in Tact, you’ve taken your first step into smart contract development on TON. This contract might be basic, but the structure and concepts apply to more complex logic too.

If you want to keep experimenting, try extending this contract or exploring other prebuilt templates from https://tact-by-example.org/all. The TON Web IDE also makes it easy to try out different use cases and it comes with templates as well to help you build and learn faster.

So go ahead, tweak, test, build something better.