All articles
Comparisons·June 25, 2026·5 min read

FunC vs Tact: Which TON Smart Contract Language to Use

FunC vs Tact TON smart contract comparison: gas costs, safety, tooling, and a real verdict on which language to ship with in 2026.

FunC has been the only real option for writing TON smart contracts since the chain launched in 2020. Tact showed up in 2023, backed by the TON Foundation, promising to make contract development feel less like assembly and more like TypeScript. Three years later, both are in production use, and the choice between them is no longer "stable vs experimental" — it's a genuine tradeoff between control and velocity.

We've shipped and audited contracts in both. Here's what actually differs once you're past the hello-world stage.

FunC: close to the metal, close to the TVM

FunC compiles almost directly to TVM (TON Virtual Machine) assembly. It's a stack-based, continuation-passing language that looks like a distant cousin of C mixed with Lisp. There's no real type system beyond int, cell, slice, builder, cont, and tuples. You manage cell serialization by hand — every load_uint, store_ref, and end_parse is explicit.

That's the whole appeal and the whole cost. You see exactly what bytecode gets generated, which matters when you're optimizing for gas on a chain where storage rent and forward-fee estimation are unforgiving if you get message construction wrong. Jettons, most DEX routers, and nearly every high-throughput TON contract still in production — Stonfi, early Ton Whales pools — were written in FunC because the authors needed to hand-tune cell layouts and control exactly how many references a cell carries (the TVM caps this at 4, and blowing past it silently breaks serialization if you're not careful).

The downside is that FunC has no memory safety net. Forget an end_parse() and the compiler won't stop you — you'll find out at runtime, or worse, in production. There's no exhaustiveness checking on message opcodes, no compiler-enforced access control, and stack manipulation bugs (wrong argument order into a function call) are a real and recurring source of audit findings.

Tact: message-first, type-checked, and opinionated

Tact throws out the stack-machine mental model. You write contract blocks with typed fields, receive() handlers keyed by message type or string, and get fun methods for off-chain reads. It compiles down to FunC internally (or directly to TVM assembly as of more recent toolchain versions), so you're not giving up TVM compatibility — you're giving up manual cell packing.

contract Counter {
    counter: Int as uint32 = 0;

    receive("increment") {
        self.counter += 1;
    }

    get fun value(): Int {
        return self.counter;
    }
}

The equivalent FunC isn't dramatically longer, but it requires you to hand-roll the cell parsing:

() recv_internal(int msg_value, cell in_msg, slice in_msg_body) impure {
    var ds = get_data().begin_parse();
    var counter = ds~load_uint(32);
    ds.end_parse();
    counter += 1;
    set_data(begin_cell().store_uint(counter, 32).end_cell());
}

Tact's compiler generates that serialization for you from the struct definition, and it will refuse to compile if a message handler doesn't match a declared type. Traits (Tact's version of interfaces/mixins) let you compose ownership, upgradability, and Jetton-standard behavior without copy-pasting boilerplate between contracts — something every FunC codebase ends up doing anyway via #include.

The cost is a thinner abstraction over gas. Tact's automatic serialization is generally efficient, but it doesn't always produce the tightest possible cell packing, and until you've read the generated FunC output, you're trusting the compiler's judgment on layout decisions that used to be yours to make.

Where the gap actually bites

Gas is the first place people notice a difference, and it's smaller than the FunC diehards claim — usually single-digit percentage overhead on typical contracts, more on ones with deeply nested structs. Tooling maturity is the bigger gap in practice: FunC has years of accumulated Blueprint test suites, TON Sandbox coverage, and community-audited patterns for Jettons and NFTs. Tact's standard library and trait ecosystem are newer and smaller, though growing fast, and you'll occasionally hit a compiler edge case that requires dropping into inline FunC via asm functions.

FunC vs Tact at a glance

Dimension FunC Tact
Abstraction level Stack machine, manual cells Typed structs, message handlers
Learning curve Steep — TVM internals required Moderate — familiar to TS/Rust devs
Gas efficiency Maximum, fully manual Near-optimal, compiler-managed
Safety No type/memory safety net Compile-time type + exhaustiveness checks
Ecosystem maturity Battle-tested since 2020 Growing fast, smaller audit history
Best fit DEXs, routers, gas-critical infra Standard tokens, DAOs, most dApps

The verdict

If you're building a Jetton, an NFT collection, a DAO, a vault, or basically any contract that isn't sitting on the hot path of a high-frequency router, use Tact. The type safety catches the exact class of bugs that show up most often in TON audits — missing bounce handling, mismatched message opcodes, unchecked sender addresses — before they ever reach a reviewer. Our smart contract audit engagements consistently find fewer critical issues in Tact codebases for this reason, not because Tact devs are more careful, but because the compiler is doing part of the reviewer's job.

Reach for FunC when gas is genuinely the bottleneck — a DEX router processing thousands of swaps a day, a bridge relayer, anything where a few thousand extra gas units per call compounds into real cost at scale. You'll also end up in FunC territory if you're patching or extending an existing FunC-based protocol; rewriting a production router in Tact for its own sake isn't worth the migration risk.

Mixed codebases are common and fine — Tact for the outer contract logic, asm or inline FunC for the one function where cell layout has to be exact. If you're scoping a new TON contract and aren't sure which side of that line you're on, our strategy consultation sessions cover exactly this kind of language and architecture call before you write a line of code, and code review catches the FunC-specific footguns — unbounded loops, missing bounce handlers — that a linter won't.

We run the same head-to-head process on execution infrastructure outside TON. If you're also running Solana bots, our comparisons of Jito bundles against standard RPC for landing trades, Yellowstone gRPC against websocket RPC, and Jupiter versus Raydium routing follow the same logic: pick the tool that matches your actual bottleneck, not the one with the most hype.

Getting an existing TON contract audited before mainnet is the cheapest insurance you'll buy this quarter — talk to us about a smart contract audit before you deploy.

Need a bot like this built?

We design, build and run trading bots on Solana, Hyperliquid and Polymarket.

Start a project
#TON#FunC#Tact#smart-contract-development#blockchain-audit