From 9342f4bcb10ff109da1f17637708fdc6a31e6db5 Mon Sep 17 00:00:00 2001 From: aigerimu <89766357+aigerimu@users.noreply.github.com> Date: Tue, 28 Oct 2025 23:08:32 +0700 Subject: [PATCH 01/10] / --- contract-dev/first-smart-contract.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contract-dev/first-smart-contract.mdx b/contract-dev/first-smart-contract.mdx index e82cff932..5da114878 100644 --- a/contract-dev/first-smart-contract.mdx +++ b/contract-dev/first-smart-contract.mdx @@ -10,10 +10,10 @@ Welcome to your journey into TON smart contract development! In this comprehensi 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 +- **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? From 476a337bb0d80439a77422bd711476d9bf3e4f13 Mon Sep 17 00:00:00 2001 From: aigerimu <89766357+aigerimu@users.noreply.github.com> Date: Fri, 14 Nov 2025 02:02:02 +0700 Subject: [PATCH 02/10] mid --- contract-dev/first-smart-contract.mdx | 885 +++++++++++++------------- 1 file changed, 447 insertions(+), 438 deletions(-) diff --git a/contract-dev/first-smart-contract.mdx b/contract-dev/first-smart-contract.mdx index 5da114878..6a4583e77 100644 --- a/contract-dev/first-smart-contract.mdx +++ b/contract-dev/first-smart-contract.mdx @@ -3,91 +3,78 @@ title: "Your first smart contract" --- import { Aside } from "/snippets/aside.jsx"; +import { FenceTable } from "/snippets/fence-table.jsx"; -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. +This tutorial covers building, deploying, and interacting with a smart contract on TON from start to finish. -## What you'll learn +## Prerequisites -By the end of this tutorial, you'll have: +- Basic programming: variables, functions, if/else statements +- Basic familiarity with a command‑line interface and executing commands +- Node.js—v22 or later— [download here](https://nodejs.org/) + - Check if installed: `node -v` in terminal +- [TON wallet](/ecosystem/wallet-apps/overview) -- **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? +## Development environment -### Understanding the basics + + -A **smart contract** is a computer program stored on [TON Blockchain](/foundations/overview) — a distributed database that many computers maintain together. It runs on the [TVM](/tvm/overview) (TON Virtual Machine) — the "computer" that runs smart contract code on TON. + Use the [Blueprint](/contract-dev/blueprint/overview) development toolkit for smart contracts. Start a new project with: -The contract is made of two parts: + ```bash + npm create ton@latest -- Example --contractName FirstContract --type tolk-empty + ``` -- **Code** (compiled TVM instructions) - the "rules" or "program logic" -- **Data** (persistent state) - the "memory" that remembers things between interactions + This command creates a project named "Example", containing a contract named "FirstContract". -Both are stored at a specific **address** on TON Blockchain, a unique identifier for each smart contract. + The generated project structure is: -## Prerequisites + + Example/ + ├──contracts/ # smart contract source code + │ └── first_contract.tolk # main contract file + ├── scripts/ # deployment and on-chain interaction scripts + │ └── deployFirstContract.ts # script to deploy the contract + ├── tests/ # testing specifications + │ └── FirstContract.spec.ts # contract test file + ├── wrappers/ # TypeScript wrappers for contract interaction + │ ├── FirstContract.ts # wrapper class for the smart contract + │ └── FirstContract.compile.ts # configuration for compiling contract -- **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](https://nodejs.org/) - - Check if installed: `node -v` in terminal -- **TON wallet** - see the [wallet apps overview](/ecosystem/wallet-apps/overview) + -## Tutorial overview + -This tutorial is organized into six clear steps that build upon each other: + -| Step | What You'll Do | Key Skills | -| ------------------------------------------------------------------ | --------------------------- | ---------------------------------------------------- | -| **[Step 1](#step-1%3A-development-environment-setup)** | Set up Blueprint toolkit | Project structure, development environment | -| **[Step 2](#step-2%3A-understanding-smart-contract-architecture)** | Learn contract architecture | Storage, messages, getters concept | -| **[Step 3](#step-3%3A-writing-the-smart-contract)** | Write contract in Tolk | Programming, message handling, data structures | -| **[Step 4](#step-4%3A-compiling-your-contract)** | Compile to bytecode | Build process, TVM compilation | -| **[Step 5](#step-5%3A-deploying-to-testnet)** | Deploy to blockchain | Testnet deployment, wallet integration | -| **[Step 6](#step-6%3A-contract-interaction)** | Interact with contract | Message sending, get methods, TypeScript integration | + -Let's dive into development! + ```bash + cd Example + ``` -## Step 1: Development environment setup + + -We'll use [**Blueprint**](/contract-dev/blueprint/overview) as our development toolkit for smart contracts. Start a new project with: -```bash -npm create ton@latest -- Example --contractName FirstContract --type tolk-empty -``` - -This will create a project **Example** with a contract **FirstContract**. - -The project structure will look like this: - -```text -Example/ -├── contracts/ # Smart contract source code -│ └── first_contract.tolk # Main contract file -├── scripts/ # Deployment and on-chain interaction scripts -│ └── deployFirstContract.ts # Script to deploy the contract -├── tests/ # Testing specifications -│ └── FirstContract.spec.ts # Contract test file -└── wrappers/ # TypeScript wrappers for contract interaction - ├── FirstContract.ts # Wrapper class for the smart contract - └── FirstContract.compile.ts # Configuration for compiling contract -``` +## Smart contract architecture - +A **smart contract** is a computer program stored on [TON Blockchain](/foundations/overview) — a distributed database that many computers maintain together. It runs on the [TVM](/tvm/overview) (TON Virtual Machine) — the "computer" that runs smart contract code on TON. -Now, move into the project directory: +The contract is made of two parts: -```bash -cd Example -``` +- **Code** (compiled TVM instructions) - the "rules" or "program logic" +- **Data** (persistent state) - the "memory" that remembers things between interactions -## Step 2: Understanding smart contract architecture +Both are stored at a specific **address** on TON Blockchain, a unique identifier for each smart contract. Every smart contract in TON is typically divided into three sections: **storage**, **messages**, and **getters**. @@ -102,189 +89,188 @@ Every smart contract in TON is typically divided into three sections: **storage* Inter-contract communication is possible only through **messages**. -## Step 3: Writing the smart contract +## Write the smart contract -We’ll build a simple **counter** 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 `getter` to return the current counter value. -- 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. +The contract uses [**Tolk**](/languages/tolk). Tolk syntax is similar to TypeScript and Rust, designed for smart contract development. -We'll use [**Tolk**](/languages/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 + To define contract storage, store the counter value. Tolk makes it simple with structures: -First, we need a way to store the counter value. Tolk makes this simple with structures: - -```tolk title="./contracts/first_contract.tolk" -struct Storage { + ```tolk title="./contracts/first_contract.tolk" + struct Storage { counter: uint64; // the current counter value -} + } -// load contract data from persistent storage -fun Storage.load() { + // load contract data from persistent storage + fun Storage.load() { return Storage.fromCell(contract.getData()) -} + } -// save contract data to persistent storage -fun Storage.save(self) { + // save contract data to persistent storage + fun Storage.save(self) { contract.setData(self.toCell()) -} -``` + } + ``` -Behind the scenes, structures know how to serialize and deserialize themselves into [cells](/foundations/serialization/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: + Structures serialize and deserialize automatically into [cells](/foundations/serialization/cells), the storage unit in TON. The `fromCell` and `toCell` functions handle conversion between structures and cells. -- 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 + 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. -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. + Define two message structures: -Tolk structures are also useful for defining message bodies. In our case, we’ll define two messages: + - `IncreaseCounter` — contains one field `increaseBy` to increment the counter. + - `ResetCounter` — resets the counter to 0. -- `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`— called opcodes, that which allows the contract to distinguish between messages. -Each structure has a unique prefix (`0x7e8764ef` and `0x3a752f06`), widely called opcodes, that lets the contract distinguish between them. -```tolk title="./contracts/first_contract.tolk" -struct(0x7e8764ef) IncreaseCounter { + ```tolk title="./contracts/first_contract.tolk" + struct(0x7e8764ef) IncreaseCounter { increaseBy: uint32 -} + } -struct(0x3a752f06) ResetCounter {} -``` + struct(0x3a752f06) ResetCounter {} + ``` -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: + Group the messages into a union. A union bundles multiple types into a single type and supports automatic serialization and deserialization: -```tolk title="./contracts/first_contract.tolk" -type AllowedMessage = IncreaseCounter | ResetCounter; -``` + ```tolk title="./contracts/first_contract.tolk" + type AllowedMessage = IncreaseCounter | ResetCounter; + ``` -Now we can write our message handler: + Now implement the message handler: -```tolk title="./contracts/first_contract.tolk" -fun onInternalMessage(in: InMessage) { + ```tolk title="./contracts/first_contract.tolk" + fun onInternalMessage(in: InMessage) { // use `lazy` to defer parsing until fields are accessed val msg = lazy AllowedMessage.fromSlice(in.body); - // matching our union to determine body structure + // matching the union to determine body structure match (msg) { - IncreaseCounter => { - // load contract storage lazily (efficient for large or partial reads/updates) - var storage = lazy Storage.load(); - storage.counter += msg.increaseBy; - storage.save(); - } - - ResetCounter => { - var storage = lazy Storage.load(); - storage.counter = 0; - storage.save(); - } - - // this match branch would be executed if the message body does not match IncreaseCounter or ResetCounter structures - else => { - // reject user message (throw) if body is not empty - assert(in.body.isEmpty()) throw 0xFFFF - } - } -} -``` - - - -### 3.3 Adding getter functions - -Finally, let’s implement a getter so users can read the current counter value: - -```tolk title="./contracts/first_contract.tolk" -get fun currentCounter(): int { + IncreaseCounter => { + // load contract storage lazily (efficient for large or partial reads/updates) + var storage = lazy Storage.load(); + storage.counter += msg.increaseBy; + storage.save(); + } + + ResetCounter => { + var storage = lazy Storage.load(); + storage.counter = 0; + storage.save(); + } + + // this match branch would be executed if the message body does not match IncreaseCounter or ResetCounter structures + else => { + // reject user message (throw) if body is not empty + assert(in.body.isEmpty()) throw 0xFFFF + } + } + } + ``` + + + + + Write a getter function to return the current counter: + + ```tolk title="./contracts/first_contract.tolk" + get fun currentCounter(): int { val storage = lazy Storage.load(); return storage.counter; -} -``` + } + ``` -### 3.4 Complete contract code + -We now have a complete smart contract with: + -- **Storage**: persistent `counter` value -- **Messages**: `IncreaseCounter` and `ResetCounter` handlers -- **Getter**: `currentCounter` + The contract now includes: + - Storage — persistent `counter` value + - Messages — `IncreaseCounter` and `ResetCounter` handlers + - Getter — `currentCounter` function -Here’s the full source code of `contracts/first_contract.tolk`: + -```tolk title="./contracts/first_contract.tolk" expandable -struct Storage { - counter: uint64; -} + ```tolk title="./contracts/first_contract.tolk" + struct Storage { + counter: uint64; + } -fun Storage.load() { - return Storage.fromCell(contract.getData()); -} + fun Storage.load() { + return Storage.fromCell(contract.getData()); + } -fun Storage.save(self) { - contract.setData(self.toCell()); -} + fun Storage.save(self) { + contract.setData(self.toCell()); + } -struct(0x7e8764ef) IncreaseCounter { - increaseBy: uint32 -} + struct(0x7e8764ef) IncreaseCounter { + increaseBy: uint32 + } -struct(0x3a752f06) ResetCounter {} + struct(0x3a752f06) ResetCounter {} -type AllowedMessage = IncreaseCounter | ResetCounter; + type AllowedMessage = IncreaseCounter | ResetCounter; -fun onInternalMessage(in: InMessage) { - val msg = lazy AllowedMessage.fromSlice(in.body); + fun onInternalMessage(in: InMessage) { + val msg = lazy AllowedMessage.fromSlice(in.body); - match (msg) { - IncreaseCounter => { - var storage = lazy Storage.load(); - storage.counter += msg.increaseBy; - storage.save(); - } + match (msg) { + IncreaseCounter => { + var storage = lazy Storage.load(); + storage.counter += msg.increaseBy; + storage.save(); + } - ResetCounter => { - var storage = lazy Storage.load(); - storage.counter = 0; - storage.save(); - } + ResetCounter => { + var storage = lazy Storage.load(); + storage.counter = 0; + storage.save(); + } - else => { - assert(in.body.isEmpty()) throw 0xFFFF; - } + else => { + assert(in.body.isEmpty()) throw 0xFFFF; + } + } } -} -get fun currentCounter(): int { - val storage = lazy Storage.load(); - return storage.counter; -} -``` + get fun currentCounter(): int { + val storage = lazy Storage.load(); + return storage.counter; + } + ``` -🎉 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: +## Compile the contract + +To build the contract, compile it into bytecode for execution by the TVM. Use Blueprint with command: ```bash npx blueprint build FirstContract @@ -307,354 +293,377 @@ Build script running, compiling FirstContract ✅ Wrote compilation artifact to build/FirstContract.compiled.json ``` -This compilation artifact contains the **contract bytecode** and will be used in the deployment step. +The compilation artifact contains the contract bytecode. This file is required for deployment. -In the next section, we'll learn how to **deploy this contract to the TON blockchain** and interact with it using scripts and wrappers. +Next, deploy the contract to the TON blockchain and interact with it using scripts and wrappers. -## Step 5: Deploying to testnet +## Deploy 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 wrapper class to deploy and interact with the contract. Wrappers implement the `Contract` interface and TypeScript integration. -Create a file `./wrappers/FirstContract.ts` with the following code: + Create `./wrappers/FirstContract.ts` with the following code: -```typescript title="./wrappers/FirstContract.ts" -import { Address, beginCell, Cell, Contract, contractAddress, ContractProvider, Sender, SendMode } from '@ton/core'; + ```typescript title="./wrappers/FirstContract.ts" + import { Address, beginCell, Cell, Contract, contractAddress, ContractProvider, Sender, SendMode } from '@ton/core'; -export class FirstContract implements Contract { + export class FirstContract implements Contract { constructor( - readonly address: Address, - readonly init?: { code: Cell; data: Cell }, + readonly address: Address, + readonly init?: { code: Cell; data: Cell }, ) {} static createFromConfig(config: { counter: number }, code: Cell, workchain = 0) { - const data = beginCell().storeUint(config.counter, 64).endCell(); - const init = { code, data }; - return new FirstContract(contractAddress(workchain, init), init); - } + const data = beginCell().storeUint(config.counter, 64).endCell(); + const init = { code, data }; + return new FirstContract(contractAddress(workchain, init), init); + } async sendDeploy(provider: ContractProvider, via: Sender, value: bigint) { - await provider.internal(via, { - value, - sendMode: SendMode.PAY_GAS_SEPARATELY, - }); - } -} -``` + await provider.internal(via, { + value, + sendMode: SendMode.PAY_GAS_SEPARATELY, + }); + } + } + ``` + + Wrapper class details: -### 5.1 Understanding the wrapper class + - [`@ton/core`](https://www.npmjs.com/package/@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](/foundations/addresses/overview) is derived deterministically from `code + data` using `contractAddress`. + - The method `sendDeploy` sends the first message with `stateInit`, triggering deployment. In practice, this can be an empty message with Toncoin attached. -- We depend on [`@ton/core`](https://www.npmjs.com/package/@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](/foundations/addresses/overview) 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: + TON provides two networks for deployment: -- **testnet** — developer sandbox. -- **mainnet** — production blockchain. + - **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. + This tutorial uses testnet. Mainnet deployment is possible once the contract is verified and ready for production. -### 5.3 Creating the deployment script + -Blueprint makes deployment simple. Create a new script `./scripts/deployFirstContract.ts`: + -```typescript title="./scripts/deployFirstContract.ts" -import { toNano } from '@ton/core'; -import { FirstContract } from '../wrappers/FirstContract'; -import { compile, NetworkProvider } from '@ton/blueprint'; + Write a script `./scripts/deployFirstContract.ts` to deploy the contract: -export async function run(provider: NetworkProvider) { + ```typescript title="./scripts/deployFirstContract.ts" + import { toNano } from '@ton/core'; + import { FirstContract } from '../wrappers/FirstContract'; + import { compile, NetworkProvider } from '@ton/blueprint'; + + export async function run(provider: NetworkProvider) { const firstContract = provider.open( - FirstContract.createFromConfig( - { counter: Math.floor(Math.random() * 10000000) }, - await compile('FirstContract') - ) + FirstContract.createFromConfig( + { counter: Math.floor(Math.random() * 10000000) }, + await compile('FirstContract') + ) ); await firstContract.sendDeploy(provider.sender(), toNano('0.05')); await provider.waitForDeploy(firstContract.address); -} -``` + } + ``` -The `sendDeploy` method accepts three arguments, but we only pass two because `provider.open` automatically supplies the ContractProvider as the first argument. + The `sendDeploy` method accepts three arguments. Only two arguments are passed because `provider.open` automatically provides the `ContractProvider` as the first argument. -Run the script with ([learn more about Blueprint deployment](/contract-dev/blueprint/deploy)): + -```bash -npx blueprint run deployFirstContract --testnet --tonconnect --tonviewer -``` + -Choose your wallet, scan the QR code shown in the console, and approve the transaction in your wallet app. + Run the script with: -Expected output: + ```bash + npx blueprint run deployFirstContract --testnet --tonconnect --tonviewer + ``` -```text -Using file: deployFirstContract -? Choose your wallet Tonkeeper + Learn more about [Blueprint deployment](/contract-dev/blueprint/deploy). - + + -Connected to wallet at address: ... -Sending transaction. Approve in your wallet... -Sent transaction -Contract deployed at address kQBz-OQQ0Olnd4IPdLGZCqHkpuAO3zdPqAy92y6G-UUpiC_o -You can view it at https://testnet.tonviewer.com/kQBz-OQQ0Olnd4IPdLGZCqHkpuAO3zdPqAy92y6G-UUpiC_o -``` + + + Choose the wallet, scan the QR code displayed in the console, and confirm the transaction in your wallet app. + + Expected output: + + ```text + Using file: deployFirstContract + ? Choose your wallet Tonkeeper -Follow the link in the console to see your contract on the **Tonviewer**. Blockchain explorers like [Tonviewer](/ecosystem/explorers/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. + Connected to wallet at address: ... + Sending transaction. Approve in your wallet... + Sent transaction + Contract deployed at address kQBz-OQQ0Olnd4IPdLGZCqHkpuAO3zdPqAy92y6G-UUpiC_o + You can view it at https://testnet.tonviewer.com/kQBz-OQQ0Olnd4IPdLGZCqHkpuAO3zdPqAy92y6G-UUpiC_o + ``` -## Step 6: Contract interaction + The link opens the contract on [Tonviewer](/ecosystem/explorers/tonviewer), a [blockchain explorer](/ecosystem/explorers/overview) showing transactions, messages, and account data. + + Next, interact with the contract by sending messages and calling getter functions. + + + + + +## 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`: -```typescript title="./wrappers/FirstContract.ts" -import { Address, beginCell, Cell, Contract, contractAddress, ContractProvider, Sender, SendMode } from '@ton/core'; + -export class FirstContract implements Contract { - constructor( - readonly address: Address, - readonly init?: { code: Cell; data: Cell }, - ) {} + + Update our wrapper class with three methods: `sendIncrease`, `sendReset`, and `getCounter`: - static createFromConfig(config: { counter: number }, code: Cell, workchain = 0) { - const data = beginCell().storeUint(config.counter, 64).endCell(); - const init = { code, data }; - return new FirstContract(contractAddress(workchain, init), init); - } + ```typescript title="./wrappers/FirstContract.ts" expandable + import { Address, beginCell, Cell, Contract, contractAddress, ContractProvider, Sender, SendMode } from '@ton/core'; - async sendDeploy(provider: ContractProvider, via: Sender, value: bigint) { - await provider.internal(via, { - value, - sendMode: SendMode.PAY_GAS_SEPARATELY, - body: beginCell().endCell(), - }); + export class FirstContract implements Contract { + constructor( + readonly address: Address, + readonly init?: { code: Cell; data: Cell }, + ) {} + + static createFromConfig(config: { counter: number }, code: Cell, workchain = 0) { + const data = beginCell().storeUint(config.counter, 64).endCell(); + const init = { code, data }; + return new FirstContract(contractAddress(workchain, init), init); } - async sendIncrease( - provider: ContractProvider, - via: Sender, - opts: { - increaseBy: number; - value: bigint; - }, - ) { - await provider.internal(via, { - value: opts.value, - sendMode: SendMode.PAY_GAS_SEPARATELY, - body: beginCell().storeUint(0x7e8764ef, 32).storeUint(opts.increaseBy, 32).endCell(), - }); + async sendDeploy(provider: ContractProvider, via: Sender, value: bigint) { + await provider.internal(via, { + value, + sendMode: SendMode.PAY_GAS_SEPARATELY, + body: beginCell().endCell(), + }); } - async sendReset( - provider: ContractProvider, - via: Sender, - opts: { - value: bigint; - }, - ) { - await provider.internal(via, { - value: opts.value, - sendMode: SendMode.PAY_GAS_SEPARATELY, - body: beginCell().storeUint(0x3a752f06, 32).endCell(), - }); + async sendIncrease( + provider: ContractProvider, + via: Sender, + opts: { + increaseBy: number; + value: bigint; + }, + ) { + await provider.internal(via, { + value: opts.value, + sendMode: SendMode.PAY_GAS_SEPARATELY, + body: beginCell().storeUint(0x7e8764ef, 32).storeUint(opts.increaseBy, 32).endCell(), + }); } - async getCounter(provider: ContractProvider) { - const result = await provider.get('currentCounter', []); - return result.stack.readNumber(); + async sendReset( + provider: ContractProvider, + via: Sender, + opts: { + value: bigint; + }, + ) { + await provider.internal(via, { + value: opts.value, + sendMode: SendMode.PAY_GAS_SEPARATELY, + body: beginCell().storeUint(0x3a752f06, 32).endCell(), + }); } -} -``` -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. + async getCounter(provider: ContractProvider) { + const result = await provider.get('currentCounter', []); + return result.stack.readNumber(); + } + } + ``` -### Building message bodies + 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. -Construction of cells starts with the `beginCell` method ([learn more about cell serialization](/foundations/serialization/cells)): + **Building message bodies** -- `beginCell()` - creates a new cell builder -- `storeUint(value, bits)` - adds an unsigned integer of specified bit length -- `endCell()` - finalizes the cell + Construction of cells starts with the `beginCell` method: -**Example**: `beginCell().storeUint(0x7e8764ef, 32).storeUint(42, 32).endCell()` + - `beginCell()` - creates a new cell builder + - `storeUint(value, bits)` - adds an unsigned integer of specified bit length + - `endCell()` - finalizes the cell -- First 32 bits: `0x7e8764ef` (opcode for "increase") -- Next 32 bits: `42` (increase by this amount) + **Example**: `beginCell().storeUint(0x7e8764ef, 32).storeUint(42, 32).endCell()` -### 6.1 Sending messages to your contract + - First 32 bits: `0x7e8764ef` (opcode for "increase") + - Next 32 bits: `42` (increase by this amount) + -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: + Now that our contract is deployed and we have wrapper methods, let's interact with it by sending messages. -```typescript title="./scripts/sendIncrease.ts" -import { Address, toNano } from '@ton/core'; -import { FirstContract } from '../wrappers/FirstContract'; -import { NetworkProvider } from '@ton/blueprint'; + Let's create a script `./scripts/sendIncrease.ts` that would increase the counter: -const contractAddress = Address.parse(''); + ```typescript title="./scripts/sendIncrease.ts" + import { Address, toNano } from '@ton/core'; + import { FirstContract } from '../wrappers/FirstContract'; + import { NetworkProvider } from '@ton/blueprint'; -export async function run(provider: NetworkProvider) { - const firstContract = provider.open(new FirstContract(contractAddress)); - await firstContract.sendIncrease(provider.sender(), { value: toNano('0.05'), increaseBy: 42 }); - await provider.waitForLastTransaction(); -} -``` + const contractAddress = Address.parse(''); -Do not forget to replace `` with your actual contract address from Step 5! + export async function run(provider: NetworkProvider) { + const firstContract = provider.open(new FirstContract(contractAddress)); + await firstContract.sendIncrease(provider.sender(), { value: toNano('0.05'), increaseBy: 42 }); + await provider.waitForLastTransaction(); + } + ``` -#### Understanding the script breakdown: + Do not forget to replace `` with your actual contract address from Step 5! -- **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 + **Understanding the script breakdown:** -To run this script: + - **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 -```bash -npx blueprint run sendIncrease --testnet --tonconnect --tonviewer -``` + To run this script: -Expected result: + ```bash + npx blueprint run sendIncrease --testnet --tonconnect --tonviewer + ``` -```text -Using file: sendIncrease -Connected to wallet at address: ... -Sending transaction. Approve in your wallet... -Sent transaction -Transaction 0fc1421b06b01c65963fa76f5d24473effd6d63fc4ea3b6ea7739cc533ba62ee successfully applied! -You can view it at https://testnet.tonviewer.com/transaction/fe6380dc2e4fab5c2caf41164d204e2f41bebe7a3ad2cb258803759be41b5734 -``` + Expected result: -#### What happens during execution: + ```text + Using file: sendIncrease + Connected to wallet at address: ... + Sending transaction. Approve in your wallet... + Sent transaction + Transaction 0fc1421b06b01c65963fa76f5d24473effd6d63fc4ea3b6ea7739cc533ba62ee successfully applied! + You can view it at https://testnet.tonviewer.com/transaction/fe6380dc2e4fab5c2caf41164d204e2f41bebe7a3ad2cb258803759be41b5734 + ``` -1. **Wallet Connection**: Blueprint connects to your wallet using [TON Connect](/ecosystem/ton-connect/overview) protocol -1. **Transaction Building**: The script creates a transaction with the message body containing the opcode `0x7e8764ef` and the value `42` -1. **User Approval**: Your wallet app shows the transaction details for approval -1. **Blockchain Processing**: Once approved, the transaction is sent to the TON network -1. **Validator Consensus**: Validators need to produce a new block containing your transaction -1. **Contract Execution**: The contract receives the message, processes it in the `onInternalMessage` function, and updates the counter -1. **Confirmation**: The transaction hash is returned, and you can view it on the explorer + **What happens during execution:** - + 1. **Wallet Connection**: Blueprint connects to your wallet using [TON Connect](/ecosystem/ton-connect/overview) protocol + 1. **Transaction Building**: The script creates a transaction with the message body containing the opcode `0x7e8764ef` and the value `42` + 1. **User Approval**: Your wallet app shows the transaction details for approval + 1. **Blockchain Processing**: Once approved, the transaction is sent to the TON network + 1. **Validator Consensus**: Validators need to produce a new block containing your transaction + 1. **Contract Execution**: The contract receives the message, processes it in the `onInternalMessage` function, and updates the counter + 1. **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: + + -```typescript title="./scripts/sendReset.ts" -import { Address, toNano } from '@ton/core'; -import { FirstContract } from '../wrappers/FirstContract'; -import { NetworkProvider } from '@ton/blueprint'; + + Let's create a script `./scripts/sendReset.ts` that would reset the counter: -const contractAddress = Address.parse(''); + ```typescript title="./scripts/sendReset.ts" + import { Address, toNano } from '@ton/core'; + import { FirstContract } from '../wrappers/FirstContract'; + import { NetworkProvider } from '@ton/blueprint'; -export async function run(provider: NetworkProvider) { - const firstContract = provider.open(new FirstContract(contractAddress)); - await firstContract.sendReset(provider.sender(), { value: toNano('0.05') }); - await provider.waitForLastTransaction(); -} -``` + const contractAddress = Address.parse(''); -To run this script: + export async function run(provider: NetworkProvider) { + const firstContract = provider.open(new FirstContract(contractAddress)); + await firstContract.sendReset(provider.sender(), { value: toNano('0.05') }); + await provider.waitForLastTransaction(); + } + ``` -```bash -npx blueprint run sendReset --testnet --tonconnect --tonviewer -``` + To run this script: -Expected result: + ```bash + npx blueprint run sendReset --testnet --tonconnect --tonviewer + ``` -```text -Using file: sendReset -Connected to wallet at address: ... -Sending transaction. Approve in your wallet... -Sent transaction -Transaction 0fc1421b06b01c65963fa76f5d24473effd6d63fc4ea3b6ea7739cc533ba62ee successfully applied! -You can view it at https://testnet.tonviewer.com/transaction/fe6380dc2e4fab5c2caf41164d204e2f41bebe7a3ad2cb258803759be41b5734 -``` + Expected result: -### 6.2 Reading contract data with get methods + ```text + Using file: sendReset + Connected to wallet at address: ... + Sending transaction. Approve in your wallet... + Sent transaction + Transaction 0fc1421b06b01c65963fa76f5d24473effd6d63fc4ea3b6ea7739cc533ba62ee successfully applied! + You can view it at https://testnet.tonviewer.com/transaction/fe6380dc2e4fab5c2caf41164d204e2f41bebe7a3ad2cb258803759be41b5734 + ``` -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 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: -```typescript title="./scripts/getCounter.ts" -import { Address } from '@ton/core'; -import { FirstContract } from '../wrappers/FirstContract'; -import { NetworkProvider } from '@ton/blueprint'; + - **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 -const contractAddress = Address.parse(''); + To call a get method, use `provider.get()`: -export async function run(provider: NetworkProvider) { - const firstContract = provider.open(new FirstContract(contractAddress)); - const counter = await firstContract.getCounter(); - console.log('Counter: ', counter); -} -``` - -#### Understanding the get method execution: - -1. **Direct contract call**: The `getCounter()` method directly calls the contract's `currentCounter` getter -1. **Instant response**: The result is returned immediately without blockchain confirmation -1. **Data parsing**: Our wrapper automatically converts the returned stack value to a JavaScript number + ```typescript title="./scripts/getCounter.ts" + import { Address } from '@ton/core'; + import { FirstContract } from '../wrappers/FirstContract'; + import { NetworkProvider } from '@ton/blueprint'; - - -To run this script: - -```bash -npx blueprint run getCounter --testnet --tonconnect -``` + const contractAddress = Address.parse(''); -Expected output: + export async function run(provider: NetworkProvider) { + const firstContract = provider.open(new FirstContract(contractAddress)); + const counter = await firstContract.getCounter(); + console.log('Counter: ', counter); + } + ``` -```bash -Using file: getCounter -Counter: 42 -``` + **Understanding the get method execution:** -## 🎉 Tutorial complete! + 1. **Direct contract call**: The `getCounter()` method directly calls the contract's `currentCounter` getter + 1. **Instant response**: The result is returned immediately without blockchain confirmation + 1. **Data parsing**: Our wrapper automatically converts the returned stack value to a JavaScript number -**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 + To run this script: -Ready to build more advanced contracts? Here's your roadmap: + ```bash + npx blueprint run getCounter --testnet --tonconnect + ``` -#### **Next Steps** + Expected output: -- [Tolk Language Guide](/languages/tolk) - Master advanced Tolk features and syntax -- [Blueprint Documentation](/contract-dev/blueprint/overview) - Learn advanced development patterns -- [Tutorial Example Repository](https://github.com/ton-org/docs-examples/tree/main/guidebook/first-smart-contract/Example) - Complete working code from this tutorial + ```bash + Using file: getCounter + Counter: 42 + ``` -#### **Advanced Topics** + -- [Gas Optimization](/contract-dev/gas) - Reduce transaction costs -- [Security Best Practices](/contract-dev/security) - Protect your contracts + -**🎉 Happy coding on TON!** You're now equipped with the fundamentals to build amazing smart contracts. The blockchain world awaits your innovations! 🚀 + + From df95910bf691a41ea97385015f3395a3736acda4 Mon Sep 17 00:00:00 2001 From: aigerimu <89766357+aigerimu@users.noreply.github.com> Date: Fri, 14 Nov 2025 13:40:01 +0700 Subject: [PATCH 03/10] upd --- contract-dev/first-smart-contract.mdx | 115 ++++++++++++-------------- 1 file changed, 52 insertions(+), 63 deletions(-) diff --git a/contract-dev/first-smart-contract.mdx b/contract-dev/first-smart-contract.mdx index 6a4583e77..07829fc75 100644 --- a/contract-dev/first-smart-contract.mdx +++ b/contract-dev/first-smart-contract.mdx @@ -65,28 +65,26 @@ This tutorial covers building, deploying, and interacting with a smart contract -## Smart contract architecture +## Smart contract layout -A **smart contract** is a computer program stored on [TON Blockchain](/foundations/overview) — a distributed database that many computers maintain together. It runs on the [TVM](/tvm/overview) (TON Virtual Machine) — the "computer" that runs smart contract code on TON. +A smart contract is a program stored on [TON Blockchain](/foundations/overview) and executed by the [TVM](/tvm/overview). +On-chain, every contract consists of two components: -The contract is made of two parts: +- Code — compiled [TVM instructions](), defines the contract's logic +- Data — persistent state, stores information between interactions -- **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 the TON Blockchain, which uniquely identifies each smart contract. -Both are stored at a specific **address** on TON Blockchain, a unique identifier for each smart contract. +A contract's data is organized into three functional sections: storage, messages, and getters: -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](/foundations/transaction). Each processed message produces a [transaction](/foundations/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. +- Storage holds the contract’s persistent state. Example: the counter variable keeps its value across calls from different users. +- Messages define how the contract reacts to incoming messages. On TON, the main way to interact with contracts is by sending [messages](). Each processed message produces a [transaction](), a recorded change on the blockchain. For example, a transfer of TON between accounts. +- Getters are read-only functions that return contract data without modifying state. Example: a getter that returns the current counter value. ## Write the smart contract @@ -427,8 +425,7 @@ Next, deploy the contract to the TON blockchain and interact with it using scrip ## 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. - +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. @@ -494,51 +491,50 @@ Technically speaking, we've already sent messages to the contract - the deploy m } ``` - 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. + 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 bodies** - Construction of cells starts with the `beginCell` method: + Cells 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. - - `beginCell()` - creates a new cell builder - - `storeUint(value, bits)` - adds an unsigned integer of specified bit length - - `endCell()` - finalizes the cell + **Example** - **Example**: `beginCell().storeUint(0x7e8764ef, 32).storeUint(42, 32).endCell()` + `beginCell().storeUint(0x7e8764ef, 32).storeUint(42, 32).endCell()` - - First 32 bits: `0x7e8764ef` (opcode for "increase") - - Next 32 bits: `42` (increase by this amount) + - First 32 bits: `0x7e8764ef` — opcode for "increase" + - Next 32 bits: `42` — increase by this amount - Now that our contract is deployed and we have wrapper methods, let's interact with it by sending messages. + With the contract deployed and wrapper methods in place, the next step is to send messages to it. - Let's create a script `./scripts/sendIncrease.ts` that would increase the counter: + Create a script `./scripts/sendIncrease.ts` that increases the counter: ```typescript title="./scripts/sendIncrease.ts" import { Address, toNano } from '@ton/core'; import { FirstContract } from '../wrappers/FirstContract'; import { NetworkProvider } from '@ton/blueprint'; + // `Address.parse()` converts string to address object const contractAddress = Address.parse(''); export async function run(provider: NetworkProvider) { + // `provider.open()` creates a connection to the deployed contract const firstContract = provider.open(new FirstContract(contractAddress)); + // `toNano('0.05')` converts 0.05 TON to nanotons + // `increaseBy: 42` tells the contract to increase the counter by 42 await firstContract.sendIncrease(provider.sender(), { value: toNano('0.05'), increaseBy: 42 }); + // `waitForLastTransaction()` waits for the transaction to be processed on-chain await provider.waitForLastTransaction(); } ``` - Do not forget to replace `` 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 + Replace `` with the address obtained in the deployment step. To run this script: @@ -557,23 +553,24 @@ Technically speaking, we've already sent messages to the contract - the deploy m You can view it at https://testnet.tonviewer.com/transaction/fe6380dc2e4fab5c2caf41164d204e2f41bebe7a3ad2cb258803759be41b5734 ``` - **What happens during execution:** + What happens during execution: + + 1. Blueprint connects to the wallet using the [TON Connect](/ecosystem/ton-connect/overview) protocol. + 1. The script builds a transaction with a message body containing opcode `0x7e8764ef` and value `42`. + 1. The wallet displays transaction details for confirmation. + 1. After approval, the transaction is sent to the network. + 1. Validators include the transaction in a newly produced block. + 1. The contract receives the message, processes it in `onInternalMessage`, and updates the counter. + 1. The script returns the resulting transaction hash, and you can inspect it in the explorer. - 1. **Wallet Connection**: Blueprint connects to your wallet using [TON Connect](/ecosystem/ton-connect/overview) protocol - 1. **Transaction Building**: The script creates a transaction with the message body containing the opcode `0x7e8764ef` and the value `42` - 1. **User Approval**: Your wallet app shows the transaction details for approval - 1. **Blockchain Processing**: Once approved, the transaction is sent to the TON network - 1. **Validator Consensus**: Validators need to produce a new block containing your transaction - 1. **Contract Execution**: The contract receives the message, processes it in the `onInternalMessage` function, and updates the counter - 1. **Confirmation**: The transaction hash is returned, and you can view it on the explorer - Run the script with: @@ -395,7 +394,7 @@ Next, deploy the contract to the TON blockchain and interact with it using scrip title="Wallet required" type="note" > - If a wallet is not installed, check the [wallet](/ecosystem/wallet-apps/web) to select and install a wallet before deploying the contract. Make sure the wallet is funded with [Toncoin on the testnet](/contract-dev/testing/testnet-tokens). + If a wallet is not installed, check the [wallet section](/ecosystem/wallet-apps/web) to select and install a wallet before deploying the contract. Make sure the wallet is funded with [Toncoin on the testnet](/contract-dev/testing/testnet-tokens). Scan the QR code displayed in the console, and confirm the transaction in the wallet app. From 7fba5a2f973de3db7e7408c23f5c580fa841b740 Mon Sep 17 00:00:00 2001 From: aigerimu <89766357+aigerimu@users.noreply.github.com> Date: Wed, 19 Nov 2025 18:11:47 +0700 Subject: [PATCH 09/10] upd --- contract-dev/first-smart-contract.mdx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/contract-dev/first-smart-contract.mdx b/contract-dev/first-smart-contract.mdx index c0f7332d5..9774e8fb8 100644 --- a/contract-dev/first-smart-contract.mdx +++ b/contract-dev/first-smart-contract.mdx @@ -104,7 +104,8 @@ The contract uses [**Tolk**](/languages/tolk) language. ```tolk title="./contracts/first_contract.tolk" struct Storage { - counter: uint64; // the current counter value + // the current counter value + counter: uint64; } // load contract data from persistent storage @@ -384,7 +385,7 @@ Next, deploy the contract to the TON blockchain and interact with it using scrip npx blueprint run deployFirstContract --testnet --tonconnect --tonviewer ``` - Learn more about [Blueprint deployment](/contract-dev/blueprint/deploy). + For flags and options, see the [Blueprint deployment guide](/contract-dev/blueprint/deploy). Date: Wed, 26 Nov 2025 12:47:35 +0700 Subject: [PATCH 10/10] link --- contract-dev/first-smart-contract.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contract-dev/first-smart-contract.mdx b/contract-dev/first-smart-contract.mdx index 9774e8fb8..05b5d48ec 100644 --- a/contract-dev/first-smart-contract.mdx +++ b/contract-dev/first-smart-contract.mdx @@ -13,7 +13,7 @@ This tutorial covers building, deploying, and interacting with a smart contract - Basic familiarity with a command‑line interface and executing commands - Node.js—`v22` or later— [download here](https://nodejs.org/) - Check if installed: `node -v` in terminal -- Installed [TON wallet](/ecosystem/wallet-apps/web) with [Toncoin on testnet](/contract-dev/testing/testnet-tokens) +- Installed [TON wallet](/ecosystem/wallet-apps/web) with [Toncoin on testnet](/ecosystem/wallet-apps/get-coins) ## Development environment @@ -395,7 +395,7 @@ Next, deploy the contract to the TON blockchain and interact with it using scrip title="Wallet required" type="note" > - If a wallet is not installed, check the [wallet section](/ecosystem/wallet-apps/web) to select and install a wallet before deploying the contract. Make sure the wallet is funded with [Toncoin on the testnet](/contract-dev/testing/testnet-tokens). + If a wallet is not installed, check the [wallet section](/ecosystem/wallet-apps/web) to select and install a wallet before deploying the contract. Make sure the wallet is funded with [Toncoin on the testnet](/ecosystem/wallet-apps/get-coins). Scan the QR code displayed in the console, and confirm the transaction in the wallet app.