Skip to main content
This tutorial covers building, deploying, and interacting with a smart contract on TON from start to finish.

Prerequisites

  • Basic programming: variables, functions, if/else statements
  • Basic familiarity with a command‑line interface and executing commands
  • Node.js—v22 or later— download here
    • Check if installed: node -v in terminal
  • Installed TON wallet with Toncoin on testnet

Development environment

1

Set up development environment

Use the Blueprint development toolkit for smart contracts. Start a new project with:
This command creates a project named “Example”, containing a contract named “FirstContract”.The generated project structure is:
2

Move into the project directory

What is a smart contract

A smart contract is a program stored on and executed by the . On-chain, every contract consists of two components:
  • Code — compiled TVM instructions, defines the contract’s logic.
  • Data — persistent state, stores information between interactions.
Both are stored at a specific address on TON blockchain, which is a unique identifier for each smart contract. Smart contracts interact with each other only through messages.

Smart contract layout

A contract’s code consists of three functional sections: storage, messages, and get methods:
  • Storage holds the contract’s persistent state. Example: the counter variable keeps its value across calls from different users.
  • Messages are receivers defined in the contract’s code that specify how the contract should react to each incoming message. Each message triggers a specific action or changes the state according to the contract’s logic.
  • Get methods are read-only functions that return contract data without modifying state. Example: a get method that returns the current counter value. Due to the TON architecture, get methods cannot be called from other contracts. Inter-contract communication uses messages only.

Write a smart contract

To build a simple counter contract:
  • Start with an initial counter value.
  • Send increase messages to add to the counter or reset messages to set it to 0.
  • Call a get method to return the current counter value.
The contract uses Tolk language. The TON ecosystem provides editor plugins with syntax support for IDEs and code editors. View them here.
1

Define contract storage

Open the ./contracts/first_contract.tolk file.To define contract storage, store the counter value. Tolk makes it simple with :
./contracts/first_contract.tolk
Structures serialize and deserialize automatically into cells, the storage unit in TON. The fromCell and toCell functions handle conversion between structures and cells.
2

Implement message handlers

To process messages, implement the onInternalMessage function. It receives one argument — the incoming message. Focus on the body field, which contains the payload sent by a user or another contract.Define two message structures:
  • IncreaseCounter — contains one field increaseBy to increment the counter.
  • ResetCounter — resets the counter to 0.
Each structure has a unique prefix —0x7e8764ef and 0x3a752f06— called opcodes, that allows the contract to distinguish between messages.
./contracts/first_contract.tolk
To avoid manual deserialization of each message, group the messages into a union. A union bundles multiple types into a single type and supports automatic serialization and deserialization.
./contracts/first_contract.tolk
Now implement the message handler:
./contracts/first_contract.tolk
3

Add getter functions

Write a getter function to return the current counter:
./contracts/first_contract.tolk
4

Complete contract code

The contract now includes:
  • Storage — persistent counter value
  • Messages — IncreaseCounter and ResetCounter handlers
  • Get methods — currentCounter
./contracts/first_contract.tolk

Compile the contract

To build the contract, compile it into bytecode for execution by the TVM. Use Blueprint with command:
Expected output:
The compilation artifact contains the contract bytecode. This file is required for deployment. Next, deploy the contract to the TON blockchain and interact with it using scripts and wrappers.

Deploy to testnet

1

Create a wrapper file

To deploy, create a wrapper class. Wrappers make it easy to interact with contracts from TypeScript.Open the ./wrappers/FirstContract.ts file and replace its content with the following code:
./wrappers/FirstContract.ts
Wrapper class details:
  • @ton/core — a library with base TON types.
  • The function createFromConfig builds a wrapper using:
    • code — compiled bytecode
    • data — the initial storage layout
  • The contract address is derived deterministically from code and data using contractAddress.
  • The method sendDeploy sends the first message with stateInit, the structure holding the contract’s initial code and data, which triggers deployment. In practice, this can be an empty message with Toncoin attached.
2

Create the deployment script

Open the ./scripts/deployFirstContract.ts file and replace its content with the following code. It deploys the contract.
./scripts/deployFirstContract.ts
The sendDeploy method accepts three arguments. Only two arguments are passed because provider.open automatically provides the ContractProvider as the first argument.
3

Run the script

TON provides two networks for deployment:
  • testnet — developer sandbox.
  • mainnet — production blockchain.
This tutorial uses testnet. Mainnet deployment is possible once the contract is verified and ready for production.Run the script with:
For flags and options, see the Blueprint deployment guide.
4

Confirm transaction

Scan the QR code displayed in the console, and confirm the transaction in the wallet app.Expected output:
The link opens the contract on Tonviewer, a blockchain explorer showing transactions, messages and account states.Next, interact with the contract by sending messages and calling getter functions.

Contract interaction

Deployment also counts as a message sent to the contract. The next step is to send a message with a body to trigger contract logic.
1

Update wrapper class

Update the wrapper class with three methods: sendIncrease, sendReset, and getCounter:
./wrappers/FirstContract.ts
The main difference from the deploy message is that these methods include a message body. The body is a cell that contains the instructions.Building message bodiesCells are constructed using the beginCell method:
  • beginCell() creates a new cell builder.
  • storeUint(value, bits) appends an unsigned integer with a fixed bit length.
  • endCell() finalizes the cell.
ExamplebeginCell().storeUint(0x7e8764ef, 32).storeUint(42, 32).endCell()
  • First 32 bits: 0x7e8764ef — opcode for “increase”
  • Next 32 bits: 42 — increase by this amount
2

Send messages to the contract

With the contract deployed and wrapper methods in place, the next step is to send messages to it.Create a script ./scripts/sendIncrease.ts that increases the counter:
./scripts/sendIncrease.ts
Replace <CONTRACT_ADDRESS> with the address obtained in the deployment step.To run this script:
Expected result:
What happens during execution:
  1. Blueprint connects to the wallet using the TON Connect protocol.
  2. The script builds a transaction with a message body containing opcode 0x7e8764ef and value 42.
  3. The wallet displays transaction details for confirmation.
  4. After approval, the transaction is sent to the network.
  5. Validators include the transaction in a newly produced block.
  6. The contract receives the message, processes it in onInternalMessage, and updates the counter.
  7. The script returns the resulting transaction hash; inspect it in the explorer.
3

Reset the counter

To reset the counter, create a script ./scripts/sendReset.ts:
./scripts/sendReset.ts
To run this script:
Expected result:
4

Read contract data with get methods

Get methods are special functions in TON smart contracts that run locally on a node. Unlike message-based interactions, get methods are:
  • Free — no gas fees, as the call does not modify the blockchain state.
  • Instant — no need to wait for blockchain confirmation.
  • Read-only — can only read data; cannot modify storage or send messages.
To call a get method, use getCounter(), which calls the contract’s getter provider.get('currentCounter'):
./scripts/getCounter.ts
To run this script:
After resetting the counter, the expected output:
The full code for this tutorial is available in the GitHub repository. It includes all contract files, scripts, and wrappers ready to use.