Skip to main content
Welcome to your journey into TON smart contract development! In this comprehensive tutorial, you’ll learn to build, deploy, and interact with a smart contract from scratch.

What you’ll learn

By the end of this tutorial, you’ll have:
  • Built a complete smart contract in Tolk
  • Deployed it to TON testnet
  • Interacted with it using TypeScript scripts
  • Mastered the fundamentals of TON development

What is a TON smart contract?

Understanding the basics

A smart contract is a computer program stored on TON Blockchain — a distributed database that many computers maintain together. It runs on the TVM (TON Virtual Machine) — the “computer” that runs smart contract code on TON. The contract is made of two parts:
  • Code (compiled TVM instructions) - the “rules” or “program logic”
  • Data (persistent state) - the “memory” that remembers things between interactions
Both are stored at a specific address on TON Blockchain, a unique identifier for each smart contract.

Prerequisites

  • Basic programming - Understanding of variables, functions, if/else statements
  • Command line basics - Comfortable opening terminal and running commands
  • Node.js (v22 or later) — Download here
    • Check if installed: node -v in terminal
  • TON wallet

Tutorial overview

This tutorial is organized into six clear steps that build upon each other: Let’s dive into development!

Step 1: Development environment setup

We’ll use Blueprint as our development toolkit for smart contracts. Start a new project with:
This will create a project Example with a contract FirstContract. The project structure will look like this:
Now, move into the project directory:

Step 2: Understanding smart contract architecture

Every smart contract in TON is typically divided into three sections: storage, messages, and getters.
  • Storage: Defines the contract’s persistent data. For example, our counter variable must keep its value across calls from different users.
  • Messages: Define how the contract reacts to incoming messages. On TON, the primary way to interact with contracts is by sending messages. Each processed message produces a transaction — a recorded change on the blockchain (like “Alice sent 5 TON to Bob”).
  • Getters: Provide read-only access to contract data without modifying state. For example, we’ll create a getter to return the current value of the counter.

Step 3: Writing the smart contract

We’ll build a simple counter contract:
  • The counter starts from an initial number.
  • Users can send an increase message to increment it, or a reset message to drop it to zero.
  • A getter function will let anyone query the current counter value.
We’ll use Tolk to implement this. Tolk looks familiar if you know TypeScript or Rust, but it’s designed specifically for smart contract development.

3.1 Defining contract storage

First, we need a way to store the counter value. Tolk makes this simple with :
./contracts/first_contract.tolk
Behind the scenes, structures know how to serialize and deserialize themselves into cells — the fundamental way TON stores data. This happens through the fromCell and toCell functions - Tolk automatically converts between your nice structures and the cell format that TON understands. You may think of cells like containers that hold data on TON:
  • Each cell can store up to 1023 bits of data.
  • Cells can reference other cells (like links).
  • Everything on TON (contracts, messages, storage) is made of cells.
Now that we can store data, let’s handle our first messages.

3.2 Implementing message handlers

The main entry point for processing messages in a Tolk contract is the onInternalMessage function. It receives one argument — the incoming message. Among its fields, the most important one for us is body, which contains the payload sent by a user or another contract. Tolk structures are also useful for defining message bodies. In our case, we’ll define two messages:
  • IncreaseCounter — with one field increaseBy, used to increment the counter.
  • ResetCounter — used to reset the counter to zero.
Each structure has a unique prefix (0x7e8764ef and 0x3a752f06), widely called opcodes, that lets the contract distinguish between them.
./contracts/first_contract.tolk
To group them together, we’ll use a union. Unions allow multiple types to be bundled into a single type that can be serialized and deserialized automatically:
./contracts/first_contract.tolk
Now we can write our message handler:
./contracts/first_contract.tolk

3.3 Adding getter functions

Finally, let’s implement a getter so users can read the current counter value:
./contracts/first_contract.tolk

3.4 Complete contract code

We now have a complete smart contract with:
  • Storage: persistent counter value
  • Messages: IncreaseCounter and ResetCounter handlers
  • Getter: currentCounter
Here’s the full source code of contracts/first_contract.tolk:
./contracts/first_contract.tolk
🎉 Congratulations — you’ve built your first smart contract in Tolk!

Step 4: Compiling your contract

The next step is to build our contract — compile it into bytecode that can be executed by the TVM. With Blueprint, this takes one command:
Expected output:
This compilation artifact contains the contract bytecode and will be used in the deployment step. In the next section, we’ll learn how to deploy this contract to the TON blockchain and interact with it using scripts and wrappers.

Step 5: Deploying to testnet

Ready to put your contract on-chain? 🚀 To deploy, we first need a wrapper class. Wrappers implement the Contract interface and make it easy to interact with contracts from TypeScript. Create a file ./wrappers/FirstContract.ts with the following code:
./wrappers/FirstContract.ts

5.1 Understanding the wrapper class

  • We depend on @ton/core — a library with base TON types.
  • The function createFromConfig constructs a wrapper using code (compiled bytecode) and data (the initial storage layout).
  • The contract address is derived deterministically from code + data using contractAddress. If two contracts have the same code and init data, the calculation of the address will result in the same value.
  • The method sendDeploy sends the first message with stateInit, which triggers deployment. In practice, this can be an empty message with some TON coins attached.

5.2 Choosing your network

TON has two networks available for deployment:
  • testnet — developer sandbox.
  • mainnet — production blockchain.
For this tutorial, we’ll use testnet since it’s free and perfect for learning. You can always deploy to mainnet later once you’re confident in your contract.

5.3 Creating the deployment script

Blueprint makes deployment simple. Create a new script ./scripts/deployFirstContract.ts:
./scripts/deployFirstContract.ts
The sendDeploy method accepts three arguments, but we only pass two because provider.open automatically supplies the ContractProvider as the first argument. Run the script with (learn more about Blueprint deployment):
Choose your wallet, scan the QR code shown in the console, and approve the transaction in your wallet app. Expected output:
Follow the link in the console to see your contract on the Tonviewer. Blockchain explorers like Tonviewer allow you to inspect transactions, smart contracts, and account states on the TON blockchain. 🎉 Congratulations! Your contract is live on testnet. Let’s interact with it by sending messages and calling get methods.

Step 6: Contract interaction

Technically speaking, we’ve already sent messages to the contract - the deploy message in previous steps. Now let’s see how to send messages with a body. First of all, we should update our wrapper class with three methods: sendIncrease, sendReset, and getCounter:
./wrappers/FirstContract.ts
The only difference from the deploy message is that we pass a body to it — remember the cells I talked about previously? The message body is a cell that contains our instructions.

Building message bodies

Construction of cells starts with the beginCell method (learn more about cell serialization):
  • beginCell() - creates a new cell builder
  • storeUint(value, bits) - adds an unsigned integer of specified bit length
  • endCell() - finalizes the cell
Example: beginCell().storeUint(0x7e8764ef, 32).storeUint(42, 32).endCell()
  • First 32 bits: 0x7e8764ef (opcode for “increase”)
  • Next 32 bits: 42 (increase by this amount)

6.1 Sending messages to your contract

Now that our contract is deployed and we have wrapper methods, let’s interact with it by sending messages. Let’s create a script ./scripts/sendIncrease.ts that would increase the counter:
./scripts/sendIncrease.ts
Do not forget to replace <CONTRACT_ADDRESS> with your actual contract address from Step 5!

Understanding the script breakdown:

  • Address parsing: Address.parse() converts the string address to a TON Address object
  • Contract opening: provider.open() creates a connection to the deployed contract
  • Value attachment: toNano('0.05') converts 0.05 TON to nanotons (the smallest TON unit)
  • Message parameters: increaseBy: 42 tells the contract to increase the counter by 42
  • Transaction waiting: waitForLastTransaction() waits for the transaction to be processed on-chain
To run this script:
Expected result:

What happens during execution:

  1. Wallet Connection: Blueprint connects to your wallet using TON Connect protocol
  2. Transaction Building: The script creates a transaction with the message body containing the opcode 0x7e8764ef and the value 42
  3. User Approval: Your wallet app shows the transaction details for approval
  4. Blockchain Processing: Once approved, the transaction is sent to the TON network
  5. Validator Consensus: Validators need to produce a new block containing your transaction
  6. Contract Execution: The contract receives the message, processes it in the onInternalMessage function, and updates the counter
  7. Confirmation: The transaction hash is returned, and you can view it on the explorer
Let’s create a script ./scripts/sendReset.ts that would reset the counter:
./scripts/sendReset.ts
To run this script:
Expected result:

6.2 Reading contract data with get methods

Get methods are special functions in TON smart contracts that allow you to read data without modifying the contract state or spending gas fees. Unlike message-based interactions, get methods:
  • Cost nothing: No gas fees required since they don’t modify blockchain state
  • Execute instantly: No need to wait for blockchain confirmation
  • Read-only: Cannot change contract storage or send messages
To call a get method, use provider.get(<GET_METHOD_NAME>):
./scripts/getCounter.ts

Understanding the get method execution:

  1. Direct contract call: The getCounter() method directly calls the contract’s currentCounter getter
  2. Instant response: The result is returned immediately without blockchain confirmation
  3. Data parsing: Our wrapper automatically converts the returned stack value to a JavaScript number
To run this script:
Expected output:

🎉 Tutorial complete!

Congratulations! You’ve successfully built, deployed, and interacted with your first TON smart contract from scratch. This is a significant achievement in your blockchain development journey!

Continue Your Learning Journey

Ready to build more advanced contracts? Here’s your roadmap:

Next Steps

Advanced Topics

🎉 Happy coding on TON! You’re now equipped with the fundamentals to build amazing smart contracts. The blockchain world awaits your innovations! 🚀