# Write your first smart contract with Blueprint (https://docs-dmpho5eos-ton-core-docs.vercel.app/llms/contract-dev/blueprint/first-smart-contract/content.md)





<Callout type="note">
  [Acton](/llms/contract-dev/acton/content.md) is the recommended tool for new smart contract projects. Blueprint remains supported for existing projects.
</Callout>

This tutorial covers building, deploying, and interacting with a smart contract on TON from start to finish using [Blueprint](/llms/contract-dev/blueprint/content.md).

## Prerequisites [#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](https://nodejs.org/)
  * Check if installed: `node -v` in terminal
* Installed [TON wallet](/llms/ecosystem/wallet-apps/tonkeeper/content.md) with [Toncoin on testnet](/llms/ecosystem/wallet-apps/get-coins/content.md)

## Development environment [#development-environment]

<Steps>
  <Step>
    ### Set up development environment [#set-up-development-environment]

    Use the [Blueprint](/llms/contract-dev/blueprint/overview/content.md) development toolkit for smart contracts. Start a new project with:

    ```bash
    npm create ton@latest -- Example --contractName FirstContract --type tolk-empty
    ```

    This command creates a project named "Example", containing a contract named "FirstContract".

    The generated project structure is:

    <FenceTable>
      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
    </FenceTable>
  </Step>

  <Step>
    ### Move into the project directory [#move-into-the-project-directory]

    ```bash
    cd Example
    ```
  </Step>
</Steps>

## What is a smart contract [#what-is-a-smart-contract]

A smart contract is a program stored on <Tooltip tip="A distributed database that many computers maintain together." cta="Learn more" href="/foundations/shards">TON blockchain</Tooltip> and executed by the <Tooltip tip="The “computer” which executes smart contracts on TON blockchain." cta="Learn more" href="/tvm/overview">TVM</Tooltip>.
On-chain, every contract consists of two components:

* Code — compiled [TVM instructions](/llms/tvm/instructions/content.md), defines the contract's logic.
* Data — persistent state, stores information between interactions.

Both are stored at a specific [address](/llms/foundations/addresses/overview/content.md) on TON blockchain, which is a unique identifier for each smart contract. Smart contracts interact with each other only through [messages](/llms/foundations/messages/overview/content.md).

### Smart contract layout [#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](/llms/tvm/get-method/content.md) 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](/llms/from-ethereum/content.md), get methods cannot be called from other contracts. Inter-contract communication uses **messages** only.

## Write a smart contract [#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**](/llms/tolk/overview/content.md) language.

The TON ecosystem provides editor plugins with syntax support for IDEs and code editors. View them [here](/llms/contract-dev/ide/overview/content.md).

<Steps>
  <Step>
    ### Define contract storage [#define-contract-storage]

    Open the `./contracts/first_contract.tolk` file.

    To define contract storage, store the `counter` value. Tolk makes it simple with <Tooltip tip="A structure is a composite data type that groups named fields, each having its own type, into one unit." cta="Learn more" href="/tolk/overview">structures</Tooltip>:

    ```tolk title="./contracts/first_contract.tolk"
    struct Storage {
        // the current counter value
        counter: uint64;
    }

    // load contract data from persistent storage
    fun Storage.load() {
        return Storage.fromCell(contract.getData())
    }

    // save contract data to persistent storage
    fun Storage.save(self) {
        contract.setData(self.toCell())
    }
    ```

    Structures serialize and deserialize automatically into [cells](/llms/foundations/serialization/cells/content.md), the storage unit in TON. The `fromCell` and `toCell` functions handle conversion between structures and cells.
  </Step>

  <Step>
    ### Implement message handlers [#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.

    ```tolk title="./contracts/first_contract.tolk"
    struct(0x7e8764ef) IncreaseCounter {
        increaseBy: uint32
    }

    struct(0x3a752f06) ResetCounter {}
    ```

    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.

    ```tolk title="./contracts/first_contract.tolk"
    type AllowedMessage = IncreaseCounter | ResetCounter;
    ```

    Now implement the message handler:

    ```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 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
            }
        }
    }
    ```
  </Step>

  <Step>
    ### Add getter functions [#add-getter-functions]

    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;
    }
    ```
  </Step>

  <Step>
    ### Complete contract code [#complete-contract-code]

    The contract now includes:

    * Storage — persistent `counter` value
    * Messages — `IncreaseCounter` and `ResetCounter` handlers
    * Get methods — `currentCounter`

    <Accordions>
      <Accordion title="Full source code">
        ```tolk title="./contracts/first_contract.tolk"
        struct Storage {
            counter: uint64;
        }

        fun Storage.load() {
            return Storage.fromCell(contract.getData());
        }

        fun Storage.save(self) {
            contract.setData(self.toCell());
        }

        struct(0x7e8764ef) IncreaseCounter {
            increaseBy: uint32
        }

        struct(0x3a752f06) ResetCounter {}

        type AllowedMessage = IncreaseCounter | ResetCounter;

        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();
                }

                ResetCounter => {
                    var storage = lazy Storage.load();
                    storage.counter = 0;
                    storage.save();
                }

                else => {
                    assert(in.body.isEmpty()) throw 0xFFFF;
                }
            }
        }

        get fun currentCounter(): int {
            val storage = lazy Storage.load();
            return storage.counter;
        }
        ```
      </Accordion>
    </Accordions>
  </Step>
</Steps>

## Compile the contract [#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
```

Expected output:

```ansi
Build script running, compiling FirstContract
🔧 Using tolk version 1.1.0...

✅ Compiled successfully! Cell BOC result:

{
  "hash": "fbfb4be0cf4ed74123b40d07fb5b7216b0f7d3195131ab21115dda537bad2baf",
  "hashBase64": "+/tL4M9O10EjtA0H+1tyFrD30xlRMashEV3aU3utK68=",
  "hex": "b5ee9c7241010401005b000114ff00f4a413f4bcf2c80b0102016202030078d0f891f24020d72c23f43b277c8e1331ed44d001d70b1f01d70b3fa0c8cb3fc9ed54e0d72c21d3a9783431983070c8cb3fc9ed54e0840f01c700f2f40011a195a1da89a1ae167fe3084b2d"
}

✅ Wrote compilation artifact to build/FirstContract.compiled.json
```

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 [#deploy-to-testnet]

<Steps>
  <Step>
    ### Create a wrapper file [#create-a-wrapper-file]

    To deploy, create a wrapper class. Wrappers make it easy to interact with contracts from TypeScript.
    A wrapper is an off-chain helper that represents a deployed contract, builds message bodies as cells, sends messages, and wraps get-method calls and response parsing.

    Keeping scripts and tests behind the same wrapper helps:

    * encode message bodies consistently,
    * parse get-method results in one place,
    * keep deployment and address-derivation logic together,
    * avoid repeating low-level cell-building code in every script.

    Blueprint creates `./wrappers/FirstContract.ts` when it scaffolds the project, and the Tolk compiler emits a `contract.abi.json` describing the contract's messages and get methods. In this tutorial the wrapper is edited manually, which is the recommended path for Tolk today. For naming conventions and the deeper pattern, see [Wrappers in Blueprint](/llms/contract-dev/blueprint/develop/content.md).

    In this step, update `./wrappers/FirstContract.ts` for the counter contract.

    Open the `./wrappers/FirstContract.ts` file and replace its content with the following code:

    ```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 },
        ) {}

        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 sendDeploy(provider: ContractProvider, via: Sender, value: bigint) {
            await provider.internal(via, {
                value,
                sendMode: SendMode.PAY_GAS_SEPARATELY,
            });
        }
    }
    ```

    Wrapper class details:

    * [`@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](/llms/foundations/addresses/overview/content.md) 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.
  </Step>

  <Step>
    ### Create the deployment script [#create-the-deployment-script]

    Open the `./scripts/deployFirstContract.ts` file and replace its content with the following code.
    It deploys the contract.

    ```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')
            )
        );

        await firstContract.sendDeploy(provider.sender(), toNano('0.05'));

        await provider.waitForDeploy(firstContract.address);
    }
    ```

    The `sendDeploy` method accepts three arguments. Only two arguments are passed because `provider.open` automatically provides the `ContractProvider` as the first argument.
  </Step>

  <Step>
    ### Run the script [#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.

    <Callout type="danger" title="Funds at risk">
      On-chain deployments spend Toncoin and are irreversible. Verify the network before executing. Use testnet for practice; use mainnet only for actual deployment.
    </Callout>

    Run the script with:

    ```bash
    npx blueprint run deployFirstContract --testnet --tonconnect --tonviewer
    ```

    For flags and options, see the [Blueprint deployment guide](/llms/contract-dev/blueprint/deploy/content.md).
  </Step>

  <Step>
    ### Confirm transaction [#confirm-transaction]

    <Callout title="Wallet required" type="note">
      If a wallet is not installed, check the [wallet section](/llms/ecosystem/wallet-apps/tonkeeper/content.md) to select and install a wallet before deploying the contract. Make sure the wallet is funded with [Toncoin on the testnet](/llms/ecosystem/wallet-apps/get-coins/content.md).
    </Callout>

    Scan the QR code displayed in the console, and confirm the transaction in the wallet app.

    Expected output:

    ```text
    Using file: deployFirstContract
    ? Choose your wallet Tonkeeper

    <QR_CODE_HERE>

    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
    ```

    The link opens the contract on [Tonviewer](/llms/ecosystem/explorers/tonviewer/content.md), a [blockchain explorer](/llms/ecosystem/explorers/overview/content.md) showing transactions, messages and [account states](/llms/foundations/status/content.md).

    Next, interact with the contract by sending messages and calling getter functions.
  </Step>
</Steps>

## Contract interaction [#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.

<Steps>
  <Step>
    ### Update wrapper class [#update-wrapper-class]

    Update the wrapper class with three methods: `sendIncrease`, `sendReset`, and `getCounter`:

    ```typescript title="./wrappers/FirstContract.ts" expandable
    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 },
        ) {}

        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 sendDeploy(provider: ContractProvider, via: Sender, value: bigint) {
            await provider.internal(via, {
                value,
                sendMode: SendMode.PAY_GAS_SEPARATELY,
                body: beginCell().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 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 getCounter(provider: ContractProvider) {
            const result = await provider.get('currentCounter', []);
            return result.stack.readNumber();
        }
    }
    ```

    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**

    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.

    **Example**

    `beginCell().storeUint(0x7e8764ef, 32).storeUint(42, 32).endCell()`

    * First 32 bits: `0x7e8764ef` — opcode for "increase"
    * Next 32 bits: `42` — increase by this amount
  </Step>

  <Step>
    ### Send messages to the contract [#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:

    ```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('<CONTRACT_ADDRESS>');

    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();
    }
    ```

    Replace `<CONTRACT_ADDRESS>` with the address obtained in the deployment step.

    <Callout type="danger" title="Funds at risk">
      On-chain deployments spend Toncoin and are irreversible. Verify the network before executing. Use testnet for practice; use mainnet only for actual deployment.
    </Callout>

    To run this script:

    ```bash
    npx blueprint run sendIncrease --testnet --tonconnect --tonviewer
    ```

    Expected result:

    ```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
    ```

    What happens during execution:

    1. Blueprint connects to the wallet using the [TON Connect](/llms/ecosystem/ton-connect/overview/content.md) 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.

    <Callout title="&#x22;Composability&#x22;" type="&#x22;tip&#x22;">
      Other contracts can also send messages to this contract. This enables composition: different contracts can combine their logic with this one, reuse it as a component, and build new behaviors that were not originally anticipated.
    </Callout>
  </Step>

  <Step>
    ### Reset the counter [#reset-the-counter]

    To reset the counter, create a script `./scripts/sendReset.ts`:

    ```typescript title="./scripts/sendReset.ts"
    import { Address, toNano } from '@ton/core';
    import { FirstContract } from '../wrappers/FirstContract';
    import { NetworkProvider } from '@ton/blueprint';

    const contractAddress = Address.parse('<CONTRACT_ADDRESS>');

    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();
    }
    ```

    To run this script:

    ```bash
    npx blueprint run sendReset --testnet --tonconnect --tonviewer
    ```

    Expected result:

    ```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
    ```
  </Step>

  <Step>
    ### Read contract data with get methods [#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')`:

    ```typescript title="./scripts/getCounter.ts"
    import { Address } from '@ton/core';
    import { FirstContract } from '../wrappers/FirstContract';
    import { NetworkProvider } from '@ton/blueprint';

    const contractAddress = Address.parse('<CONTRACT_ADDRESS>');

      export async function run(provider: NetworkProvider) {
        const firstContract = provider.open(new FirstContract(contractAddress));
        //  `getCounter()` сalls the contract's `currentCounter` getter
        const counter = await firstContract.getCounter(); // returns instantly
        console.log('Counter: ', counter); // wrapper parses stack into JS number
      }
    ```

    <Callout>
      Get methods are available **off-chain only** — JavaScript clients, web apps, etc. — through RPC providers. Contracts cannot call getters on other contracts — inter-contract interaction uses only messages.
    </Callout>

    <Callout type="danger" title="Funds at risk">
      On-chain deployments spend Toncoin and are irreversible. Verify the network before executing. Use testnet for practice; use mainnet only for actual deployment.
    </Callout>

    To run this script:

    ```bash
    npx blueprint run getCounter --testnet --tonconnect
    ```

    After resetting the counter, the expected output:

    ```bash
    Using file: getCounter
    Counter: 0
    ```
  </Step>
</Steps>

The full code for this tutorial is available in the [GitHub repository](https://github.com/ton-org/docs-examples/tree/main/guidebook/first-smart-contract/Example). It includes all contract files, scripts, and wrappers ready to use.
