Introduction to FunC: how to start developing on TON?
Tags: web3, ton, beginners, tutorial
Published: 2022-09-22 13:47:47

I've started developing in TON last year. At the beginning there were just a few articles and a bit of docs at [ton.org](https://ton.org). Now its ecosystem is growing, so many can find this blockchain interesting. In this article I want to share my experience with beginners through a guide with some useful examples. 

We will build & deploy a simple "Hello world" contract.

## Overview
TON is PoS blockchain based on the actor model, interaction between contracts can be done only by messages, unlike EVM-powered chains, where you can call other contracts during computations. 

Any data stored in TON blockchain is stored in **cells**. Cells can contain up to 1023 bits and 4 references to other cells. Cell itself is immutable, to read from it you need to create a **slice**. Generally speaking, slices are read-only cell representation. To create a new cell here comes another type - **builder**. Builder is the opposite of slice: write-only cell representation, which can be converted to cell. Cells can be serialised to **Bag Of Cells (BoC)**. BoC will contain the root cell and the whole referenced cells' tree.

Let's speak about tools. Now there are two languages:
- fift - low-level language, has interpreter, so can be used for scripting
- func - high-level imperative language, compiled to fift

...and one more thing: [Tact](https://tact-lang.org/) language, designed specifically for the beginners, is under development. 
In this article we'll focus only on FunC, but if you want, you may read about Fift yourself.

## Installing build tools
The best way (at the time of writing) to install actual versions of tools is to compile them yourself.

UPDATE: now you can download precompiled binaries from official [ton-blockchain/ton](https://github.com/ton-blockchain/ton/releases) releases.

### Building func & fift from source
**Requirements**
- cmake ([For MacOS] XCode build Tools will be ok)
- make ([For MacOS] XCode build Tools will be ok)
- openssl ([For MacOS] can be installed via ```brew install openssl```)
1. Clone TON [monorepo](https://github.com/ton-blockchain/ton) and fetch submodules 
```bash
git clone https://github.com/ton-blockchain/ton
cd ton
git submodule init
git submodule update
```  

2. Create build folder ```bash
mkdir build
cd build
```  
3. Run cmake to create build environment ```bash 
# make sure that OPENSSL_ROOT_DIR is set
# For MacOS users: export OPENSSL_ROOT_DIR=/usr/local/opt/openssl/
cmake ..
```  

4. Build func & fift ```bash
make func fift
cd crypto
```  

5. Install binaries to library directories or add them to `PATH` ```bash
cp fift /usr/local/bin/fift
cp func /usr/local/bin/func
```  
Now we are ready to start!

## Creating a project
I suggest using the Node.js environment for FunC projects, but feel free to use any other environment you want. Also, I suppose  My common project structure is:
```
├── build
│   └── ...build results
├── manage
│   └── deploy.ts
├── contracts
│   └── hello-world.fc
├── test
│   └── ...tests
├── build.sh
├── jest.config.js
├── package.json
└── tsconfig.json
```
- *build* folder contains built result.
- *manage* folder contains scripts for deploy, dump smart contract data from blockchain, etc.
- *test* folder contains tests.

To init Node.js project with Typescript:
```bash
yarn init
yarn add -D typescript ts-node @types/node
yarn run tsc --init
```

## Creating smart-contract
A bit of theory: smart-contract in TON can handle two types of messages: *internal* & *external*. Internal messages are sent from one contract to another, externals - from the internet to contract. Another way to interact with contract is to call *get methods*. Get methods are used for computing some  helpful information from the current contract's state and executed offchain. They are marked with the `method_id` keyword.

So, the contract consists of two entrypoints, `recv_internal` and `recv_external`. Also, contracts can contain as many get methods as you want.

Our contract will respond to any internal message with *Hello, world!* in comment and count how many messages were handled. Counter will be accessible through the get method.


This is `hello-world.fc` structure:
```func
() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
    return ();
}

() recv_external(slice in_msg) impure {
    ;; Do not accept external messages
    throw(0xffff);
}

int counter() method_id {
    return 1;
}

slice owner() method_id {
    return null();
}
```
We have three methods: `recv_internal` for handling internal messages, `recv_external` for handling external messages (rejects any external message) and `counter` get method. 

### Installing `stdlib.fc`
There are two ways to include `stdlib.fc`: copy to project sources or install via yarn/npm.
**From sources**
Download [stdlib.fc](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/stdlib.fc) and copy to the `src/contracts`. Then, include it:
```func
#include "stdlib.fc";
```

**From npm**
```bash
yarn add ton-stdlib
```
Include from node-modules:
```func
#include "../../node_modules/ton-stdlib/func/stdlib.fc";
```

### Sending message
Let's modify our `recv_internal` to send a message back to the user.
```
() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
    slice cs = in_msg_full.begin_parse();
    cs~skip_bits(4); ;; skip flags

    slice sender_address = cs~load_msg_addr();

    ;; Send message
    var message = begin_cell()
        .store_uint(0x10, 6)
        .store_slice(sender_address)
        .store_coins(0)
        .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
        .store_uint(0, 32)
        .store_slice("Hello, world!")
        .end_cell();

    send_raw_message(message, 64);
}
```
At first, we should get the sender address. According to the [TL-b scheme](https://github.com/ton-blockchain/ton/blob/35d17249e6b54d67a5781ebf26e4ee98e56c1e50/crypto/block/block.tlb#L123), we should skip 4 bits (scheme tag & flags), after goes sender address. 
```func
slice cs = in_msg_full.begin_parse();
cs~skip_bits(4);

slice sender_address = cs~load_msg_addr();
```
Then, we know the sender address and can send a response.
```func
;; Send message
var message = begin_cell()
    .store_uint(0x10, 6)         ;; flags
    .store_slice(sender_address) ;; destination
    .store_coins(0)              ;; amount 
    .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; metadata
    .store_uint(0, 32)
    .store_slice("Hello, world!") ;; payload
    .end_cell();

send_raw_message(message, 64);
```
Message is structured that way: 
1. 4 bits of flags
 - internal message tag (1 zero bit)
 - ihr_disabled (*Instant Hypercube Routing*, learn more at [tblkch.pdf](https://ton-blockchain.github.io/docs/tblkch.pdf))
 - bounce (can the receiving part bounce a message?)
 - bounced (means that receiving part failed, so the message returned to contract)
2. Source address (2 zero bits, address is none)
3. Destination address
4. Message metadata
5. Content

> The message cell layout better described [here](https://ton.org/docs/#/smart-contracts/messages?id=sending-messages). 

We are sending message with flag *64*, it means that the amount increased by the remaining value of the inbound message.
> You can find more about `send_raw_message` flags [here](https://ton.org/docs/#/func/stdlib?id=send_raw_message). 

Message will be sent after computations, because transactions are split into different phases: storage, credit, compute, action and bounce. Phases described at the [brief TVM overview](https://ton.org/docs/#/smart-contracts/tvm_overview).

### Updating counter
After sending a message we need to update the counter.
```func
var cs = get_data().begin_parse();
var counter = cs~load_uint(32);

set_data(
    begin_cell()
        .store_uint(counter + 1, 32)
        .store_slice(cs)
    .end_cell()
);
```
At first, we are getting the old counter from the contract's data. Then, we store the new data with the updated counter.
### Adding get-methods
`counter` should return just single integer with the current state of counter, `owner` should return owner.
```
int counter() method_id {
    var data = get_data().begin_parse();
    return data~load_uint(32);
}

slice owner() method_id {
    var data = get_data().begin_parse();
    data~skip_bits(32);
    return data~load_msg_addr();
}
```
### Result
```func
#include "../../node_modules/ton-stdlib/func/stdlib.fc";

() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
    slice cs = in_msg_full.begin_parse();
    cs~skip_bits(4);

    slice sender_address = cs~load_msg_addr();

    ;; Send message
    var message = begin_cell()
        .store_uint(0x10, 6) ;; 010000
        .store_slice(sender_address)
        .store_coins(0)
        .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
        .store_uint(0, 32)
        .store_slice("Hello, world!")
        .end_cell();

    send_raw_message(message, 64);

    ;; Update counter
    var cs = get_data().begin_parse();
    var counter = cs~load_uint(32);

    set_data(
        begin_cell()
            .store_uint(counter + 1, 32)
            .store_slice(cs)
        .end_cell()
    );
}

() recv_external(slice in_msg) impure {
    throw(0xffff);
}

int counter() method_id {
    var data = get_data().begin_parse();
    return data~load_uint(32);
}

slice owner() method_id {
    var data = get_data().begin_parse();
    data~skip_bits(32);
    return data~load_msg_addr();
}
```

## Writing *build.sh*
To deploy & test contract we need to compile & serialise it to BoC. It can be done by single command:
```bash
func -Wbuild/hello-world.boc src/contracts/hello-world.fc | fift >> /dev/null
```
At first, we build hello-world.fc using `func`, with the flag `-W`. Flag -W appends Fift code for serialisation of the resulting contract to BoC. `func` emits fift code that is further passed to `fift` interpreter. Then, we forward interpreter's `stdout` to */dev/null* to avoid messy outputs in the console.

## Deploying  
There are several ways to deploy contract: via [`toncli`](https://github.com/disintar/toncli), via fift & lite-client, via [`tonweb`](https://github.com/toncenter/tonweb), via [`ton`](https://github.com/tonwhales/ton), etc.

In this guide I we will deploy contract to the sandbox network using [Tonhub Sandbox](https://apps.apple.com/app/ton-development-wallet/id1607857373) and [`ton`](https://github.com/tonwhales/ton) library.

1. [Get some Sandbox coins](https://developers.tonhub.com/docs/sandbox)
1. Install dependencies
```bash
yarn add ton-core ton qrcode-terminal qs
yarn add -D @types/qs @types/qrcode-terminal
```
2. Add `manage/deploy-sandbox.ts` script
```ts
import { Cell, Builder, storeStateInit, contractAddress, StateInit, toNano, beginCell, Address } from 'ton-core';
import { readFileSync } from 'fs';
import qs from 'qs';
import qrcode from 'qrcode-terminal';

// Create a data cell similar to the initial contract state
const dataCell = beginCell()
    .storeUint(0, 32)                        // counter
    .storeAddress(Address.parse('INSERT_ADDRESS'))
    .endCell();

// Load code from build
const codeCell = Cell.fromBoc(readFileSync('./build/hello-world.boc'))[0];

// Calculate address from code & data
const address = contractAddress(0, {
    code: codeCell,
    data: dataCell
});
  
// Prepare init message
const initCell = new Builder();

storeStateInit({
    code: codeCell,
    data: dataCell,
})(initCell);

// Encode link to deploy contract
let link = 'https://test.tonhub.com/transfer/' + address.toString({ testOnly: true }) + '?' + qs.stringify({
    text: 'Deploy contract',
    amount: toNano(1).toString(10),
    init: initCell.asCell().toBoc({ idx: false }).toString('base64')
});

console.log('Address: ' + address.toString({ testOnly: true }));

qrcode.generate(link, { small: true }, (code) => {
     console.log(code)
});
```
3. Run script & scan qr code in the Sandbox wallet 
```bash
yarn ts-node manage/deploy-sandbox.ts
```
## Testing contract
Send any amount to the address you've got from the deployment step and you will get "Hello, world!" back. Check the transactions in the [sandbox explorer](https://tonsandbox.com/explorer).

## Getting counter & owner
Create a `.ts` file in the `manage` folder and name it like `dump-sandbox.ts`. Replace address to yours. Create `TonClient` with a sandbox endpoint and call get methods.
```ts
import { TonClient } from 'ton'
import { Address } from 'ton-core'

let address = Address.parse('YOUR CONTRACT ADDRESS');

(async () => {
    // Create sandbox API client
    const client = new TonClient({
        endpoint: 'https://sandbox.tonhubapi.com/jsonRPC'
    });

    // Call get method and log the result
    let counter = await client.runMethod(address, 'counter');
    console.log('Counter', parseInt(counter.stack[0][1], 16));

    let owner = await client.runMethod(address, 'owner');
    let ownerCell = Cell.fromBoc(Buffer.from(owner.stack[0][1].bytes, 'base64'))[0];
    let ownerAddress = ownerCell.beginParse().loadAddress()?.toFriendly({ testOnly: true });
    console.log('Address: ', ownerAddress);
})()
```

## Congratulations
You've set up environment, built your first contract on TON and tried it in the sandbox network 🚀

### Further reading
- [Learn FunC in Y minutes](https://learnxinyminutes.com/docs/func/)
- [ton.org docs](https://ton.org/docs/#/)
- [Smart-contract guidelines](https://ton.org/docs/#/howto/smart-contract-guidelines)
- Examples: [nft & ft contracts](https://github.com/ton-blockchain/token-contract)
- Examples: [PoW giver contract](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/pow-testgiver-code.fc)
- Examples: [wallet v4 contract](https://github.com/ton-blockchain/wallet-contract)
- [tblkch.pdf](https://ton-blockchain.github.io/docs/tblkch.pdf)