WinDeveloper Coin Tracker

  • Home
  • Solidity
  • Deploying & Running Solidity Smart Contracts on TON OS

Deploying & Running Solidity Smart Contracts on TON OS

Alexander Zammit

Alexander Zammit Photo

Software Development Consultant. Involved in the development of various Enterprise software solutions. Today focused on Blockchain and DLT technologies.

  • Published: Sep 04, 2020
  • Category: Solidity, Free TON
  • Votes: 5.0 out of 5 - 5 Votes
Cast your Vote
Poor Excellent

A few weeks ago, we walked through the installation of TON OS. It is now time to take this a step further. Today we deploy and run a Solidity smart contract. In the process we discover some key differences between Ethereum and the TON blockchain.

The full article source code is available on GitHub.

In case you missed it, start by going through Running TON OS on Windows.

We start from the smart contract discussed in, Designing an Unbounded List in Solidity. Despite being rather simple, the contract required quite a few changes.

Important: Since the initial contract was designed for Ethereum, the result of this conversion is certainly not optimal! Design considerations that apply to Ethereum do not necessarily directly apply to TON. Hence, I recommend caution before reusing the TON Solidity contract included with this article.

For those eager to look at the code, check these links:

BlockchainThings/UnboundedList/contracts/ListContract.sol

Initial Ethereum smart contract

BlockchainThings/FreeTONList/ListContract.sol

Final TON smart contract

BlockchainThings/FreeTONList/test

Node.js Test

BlockchainThings/FreeTONList/reference/ListContractContract.js

Auto-generated smart contract wrapper.

 

The Compilation Process

To begin, we need a Solidity to TON Virtual Machine (TVM) compiler. A few options are available for running the compiler. On Windows, I will run this as a docker container.

If you are following along, grab the smart contract and copy it to the machine where TON OS is installed.

Start TON OS with:
> tondev start

Next, start the compilation using:
> tondev sol ListContract.sol -l js -L deploy

Once again, I am using the tondev command line tool, which we introduced in Running TON OS on Windows. Here, the first two parameters are selecting solidity compilation and identifying the source file. The remaining parameters instruct tondev to generate a JavaScript wrapper for deploying and running the smart contract.

On compiling for the first time, the command pulls the compiler docker image:
Pulling [tonlabs/compilers:latest]...

Docker Desktop also prompts for file access, which I grant:

Docker File Sharing

With all this out of the way, compilation completes generating three files:
ListContract.tvc
ListContract.abi.json
ListContractContract.js

 

Contract Wrapper Class

ListContractContract.js contains a wrapper that facilitates deploying and running the smart contract in Node.js. You should generate ListContractContract.js yourself. The lazier ones can look at the wrapper code that was generated by my compilation.

At the very top of this file, we find the smart contract interface definition:
const abi = ...

Next, we have the package that groups together the ABI and the base64 encoded compilation result:
const pkg = ...

Finally, we have the wrapper class that encapsulates the functions for deploying and running the contract. For each public/external smart contract function the wrapper includes 2 functions. For example the Solidity function:
function add(address addr) external

...has the two wrapper functions:
add(params)
addLocal(params)

These provide us with 2 alternative methods for calling the function:
add wraps a call that is executed against a blockchain node incurring a gas fee.
addLocal wraps a call that is executed on a local VM without incurring a gas fee.

Local calls are useful for reading the current contract state and for emulating transactions before these are run on the blockchain. Using addLocal we can detect errors before sending the message to a node.

 

More about Gas

The discussion on wrapper functions is a good way to start digging into gas and transaction fees. But before going any further let me thank the TON community, especially Boris Ivanovsky for helping me better understand how gas works in Free TON.

Let's look at the solidity code starting from the constructor:

constructor() public {
        tvm.accept();
        . . .

This starts with a call to accept from the tvm namespace. With this call, the constructor is accepting to pay for the fees incurred on deployment.

Indeed, TON contracts can pay for their own gas. By default, the caller is expected to pay for gas, but the contract may choose to absorb this cost itself.

This is true for all public/external functions. Indeed, going back to the solidity code, note the modifier preceding the constructor:

modifier alwaysAccept {
        tvm.accept();
        _;
    }

This is being applied to the add, remove, and update solidity functions. Whether you would really want to do this, is of course application specific.

Another interesting difference between Ethereum and TON is the gas consumption of pure and view functions. These do not incur gas in Ethereum. However, in TON this works a little bit differently.

As already explained, in TON we have a choice between running functions locally or on the blockchain. If a pure/view function is run on the blockchain, gas is incurred. To avoid paying such fees these calls must be made locally.

Let us see this at work in our test.js code. Here functions that change the contract state are run on the blockchain, but view functions are run locally. For example this is what the list remove call looks like:
await list.remove({id: id})

. . . and here is an example call to a view function:
let first = await list.firstItemLocal()

 

The Giver Contract and Contract Deployment

As already discussed, the constructor is paying the gas fees for its own deployment. This is achieved by pre-loading the contract with credit before it is deployed. This is achieved as follows:

  1. Pre-compute the address for the contract that is about to be deployed
  2. Transfer credit to this address
  3. Deploy the contract

On a live blockchain, before going through this procedure, one would first need to somehow purchase or earn some TON Crystals. In our TON OS test environment credit is free! The installation is initialized with the Giver contract to which we can simply ask for credit.

Our node.js code includes the giver.js helper. This code is largely copied from the TON OS docs. To see the complete procedure in action, look at the load_credit function:

async function load_credit(client, package, constructorParams, keyPair, credit) {
    //Determine the contract address
    const futureAddress = (await client.contracts.createDeployMessage({
        package,
        constructorParams,
        keyPair,
    })).address

    //Load contract with credit
    await get_grams_from_giver(client, futureAddress, credit)
    return futureAddress
}

 

Mappings, Memory and Storage

The TON mapping type is more feature rich than its Ethereum counterpart. Amongst other things, it includes the ability to iterate over mapping entries and to check for key existence.

This meant that we could now use the TON fetch function to verify item inclusion:

function remove(uint256 id) external alwaysAccept {
   (bool exists, ListElement elem) = items.fetch(id);
   require(exists && (elem.addr != address(0x0)), ...

Another difference that had to be accounted for, is the fact that memory/storage access modifiers have no effect in TON. As this compiler warning pointed out:
"Warning: Memory access modifiers have no effect in TON."

For example, our Ethereum add function included this code:
ListElement storage prevElem = items[lastItem()];
...
prevElem.next = nextItem;

With this code, changes to prevElem are persisted to storage in Ethereum, but not in TON. So the code was replaced by:
items[lastItem()].next = nextItem;

If you refer to the GitHub file change history, you will see that all changes related to mappings were dictated by these 2 considerations.

 

Node.js Test

The Node.js test is quite simple. It relies on one external package, ton-client-node-js. To run this code:

  1. Save all files under FreeTONList to the machine running TON OS.

  2. Compile the smart contract as shown earlier, to generate the ListContractContract.js wrapper. Alternatively, copy this from GitHub to the same directory where the solidity file is located (/FreeTONList).

  3. Move to the directory /FreeTONList/test and restore the node packages with:
    > npm install

  4. Run the test with:
    > node test

Contract Address before deployment: 0:a5046a2e1f3653c3ad35ecf3c5384486d4bdccb8f7007ed12c6231001b51e0a0
Contract Deployed: 0:a5046a2e1f3653c3ad35ecf3c5384486d4bdccb8f7007ed12c6231001b51e0a0
First Item Id 0x1
Last Item Id 0x5
First Item Id 0x2
Last Item Id 0x4
Next Read Pos: 0x0
Addr List Out: 0:0000000000000000000000000000000000000000000000000000000000000002, 0:0000000000000000000000000000000000000000000000000000000000000004

The test starts by deploying a new contract instance, loads it with credit, adds 5 list entries and removes 3 of them. In between, it logs the ids of the first and last items. Finally, it dumps the items remaining in the list.

 

Concluding Remarks

This concludes my first Free TON Solidity contract. Converting this Ethereum contract to run on TON forced me to take my first dive. However, my learning journey has only just started. Post a comment if you enjoyed reading about this and I will keep you posted as I dig deeper into the blockchain of the free...

 

References

Running TON OS on Windows

Source code for this article

TON Solidity API

RunLocal Method

Run get method

 

Copyright 2024 All rights reserved. BlockchainThings.io