Getting Started: Writing a Counter Smart Contract in FunC for TON

In the wide application of blockchain technology, smart contracts are undoubtedly one of the most promising areas. Smart contracts allow us to execute trusted transactions and agreements in a decentralized environment. TON (The Open Network), as an emerging blockchain platform, has attracted the attention of many developers with its efficient and scalable features. This article will guide you through the basics of writing TON smart contracts by implementing a simple counter contract, teaching you the fundamental techniques of writing smart contracts using the FunC language.

Understanding TON and FunC

Before diving into writing smart contracts, we need to have an understanding of TON and FunC.

Introduction to TON

TON is a blockchain platform developed by the Telegram team, designed to address the scalability and speed issues in existing blockchain systems. TON’s features include:

  • High Performance: TON can process millions of transactions, achieving fast confirmation.
  • Scalability: TON supports unlimited scaling through sharding technology.
  • User-Friendly: TON provides users with an easy-to-use blockchain ecosystem.

FunC Language

FunC is the primary programming language for TON smart contracts. It is a high-level, statically typed, imperative programming language. The design goal of FunC is to provide a concise, efficient, and easy-to-understand language, allowing developers to quickly get started with smart contract writing.

Writing the Counter Contract

Next, we will step by step create a simple counter contract. This contract will allow users to increase the counter’s value and provide methods to query the current value and ID of the counter.

Importing the Standard Library

#include "imports/stdlib.fc";

First, we need to import the standard library, which contains common functions and operations required for contract writing. This line of code is the foundation for writing contracts.

Defining Opcodes

const op::increase = "op::increase"c;

In TON smart contracts, we often need to define opcodes. By prefixing a string with c, we can convert it into an opcode, which is used for message handling within the contract.

Defining Global Storage Variables

global int ctx_id;
global int ctx_counter;

Every smart contract has its own storage space for persisting data. Here we define two global variables ctx_id and ctx_counter, used to store the unique identifier and value of the counter, respectively.

Loading Data

() load_data() impure {
    var ds = get_data().begin_parse();
    ctx_id = ds~load_uint(32);
    ctx_counter = ds~load_uint(32);
    ds.end_parse();
}

To operate on the stored data within the contract, we need to load data from persistent storage into global variables. The load_data function implements this functionality. It first retrieves the stored data, then parses the data, and assigns the parsed values to ctx_id and ctx_counter.

Storing Data

() save_data() impure {
    set_data(
        begin_cell()
            .store_uint(ctx_id, 32)
            .store_uint(ctx_counter, 32)
            .end_cell()
    );
}

In contrast to loading data, the save_data function is used to store the values of global variables in persistent storage. It creates a new cell, stores the variable values in this cell, and then sets it as the contract’s data.

Receiving Internal Messages

() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
    if (in_msg_body.slice_empty?()) {
        return ();
    }
    slice cs = in_msg_full.begin_parse();
    int flags = cs~load_uint(4);
    if (flags & 1) {
        return ();
    }
    load_data();
    int op = in_msg_body~load_uint(32);
    int query_id = in_msg_body~load_uint(64);
    if (op == op::increase) {
        int increase_by = in_msg_body~load_uint(32);
        ctx_counter += increase_by;
        save_data();
        return ();
    }
    throw(0xffff);
}

The recv_internal function is the main function of the contract, which is called when the contract receives a message from other contracts. It performs the following operations:

  • Checks if the message body is empty and ignores it if so.
  • Checks if the message is bounced and ignores it if so.
  • Calls load_data to load the stored variables.
  • Reads the operation code and query ID from the message.
  • If the operation code is op::increase, it reads the value to increase by, updates the counter, and calls save_data to save the new counter value.
  • If the operation code is not known, it throws an exception.

Implementing Get Methods

int get_counter() method_id {
    load_data();
    return ctx_counter;
}

int get_id() method_id {
    load_data();
    return ctx_id;
}

Get methods serve as the external interface of the contract, allowing external callers to read contract data. Here, we provide two get methods: get_counter and get_id, used to obtain the value and unique identifier of the counter, respectively.

Details of Get Methods

Get methods play an important role in smart contracts as they allow external entities to access the contract’s state in a read-only manner. In TON, get methods are marked with the method_id keyword, which means they cannot modify the contract’s state but can only return data.

get_counter Method

int get_counter() method_id {
    load_data();
    return ctx_counter;
}

This method first calls load_data to ensure we have the latest stored data. It then returns the current value of ctx_counter. This method can be called by any external entity that knows the contract’s address to query the current value of the counter.

get_id Method

int get_id() method_id {
    load_data();
    return ctx_id;
}

Similar to get_counter, the get_id method also loads the data first and then returns the value of ctx_id. This method allows external entities to obtain the unique identifier of the contract.

Deploying and Interacting with the Contract

After writing the contract, the next step is to deploy it to the TON network. Deploying the contract involves the following steps:

  1. Compile the contract code: Use the compiler provided by TON to compile the FunC code into bytecode.
  2. Deploy the contract: Use a TON wallet or other deployment tool to deploy the compiled bytecode to the network.
  3. Interact with the contract: Use the API provided by TON or client libraries to send messages to the contract, execute the op::increase operation, or call the get methods.

Security and Best Practices

Security is of utmost importance when writing smart contracts. Here are some best practices for writing TON smart contracts:

  • Validate inputs: Always validate input data from external sources to avoid potential security vulnerabilities.
  • Simplify logic: Contract logic should be as simple as possible; complex logic is more prone to errors.
  • Use get methods: For read-only operations, use get methods instead of sending messages to save resources.
  • Error handling: Properly handle errors and exceptional cases to avoid the contract from stopping responding due to exceptions.

Conclusion

Through the steps above, we have successfully implemented a basic counter contract. Although this contract’s functionality is relatively simple, it covers the core concepts and techniques required for writing TON smart contracts. After mastering these basics, you can continue to explore more complex contract logic and build feature-rich decentralized applications.

In the world of TON, the application prospects of smart contracts are limitless. From simple counters to complex financial protocols, smart contracts can be applied in various scenarios. As the TON ecosystem continues to mature, there will be more tools and resources available for developers, making smart contract development more efficient and convenient.

Finally, we encourage you to continue learning and practicing, turning your ideas into reality. TON provides a platform full of opportunities, allowing you to innovate at the forefront of blockchain technology. Writing smart contracts is just the beginning; let’s build a more open, transparent, and decentralized future together.