Skip to content

TON DeFi: Opportunities & Risks

💡
Before you start

Python 3, a terminal, and for the last step the tonaddr.py file from the TON introduction tutorial. If you do not have it, open Introduction to TON and do its step 1 first — two minutes, one file. Check Python with python3 --version; nothing else is installed.

No wallet is connected and no DeFi protocol is touched. You will build a working automated market maker in about twenty lines and then attack it, which teaches the mechanism far better than a live swap would — and costs nothing when it goes wrong.

DeFi on TON

Decentralized Finance (DeFi) on TON is a growing ecosystem of financial applications that operate without intermediaries. You can swap tokens, provide liquidity, lend assets, and earn yield — all through smart contracts on the TON blockchain.

The integration with Telegram makes TON DeFi more accessible than other chains, but this accessibility also means less experienced users are exposed to real financial risks. This tutorial focuses on understanding those risks.

Decentralized Exchanges (DEXs)

DEXs allow you to swap one token for another without a centralized intermediary:

  • STON.fi: The largest TON DEX. Uses an automated market maker (AMM) model with liquidity pools
  • DeDust: The second largest DEX. Offers both volatile and stable swap pools with optimized routing
  • How swaps work: You trade against a liquidity pool (a smart contract holding two tokens). The price is determined by the ratio of tokens in the pool
  • Slippage: Large trades can move the price against you. Always set a reasonable slippage tolerance (0.5-1% for stable pairs, 1-5% for volatile)

Liquidity Provision

You can earn fees by depositing tokens into DEX liquidity pools:

  • How it works: Deposit equal value of two tokens into a pool. You earn a share of trading fees proportional to your share of the pool
  • LP tokens: You receive LP (Liquidity Provider) tokens representing your position. These can be redeemed for your share plus earned fees
  • Typical APR: 5-50%+ depending on the pool and trading volume
⚠️
Impermanent loss is real

When the price ratio of your deposited tokens changes, you suffer "impermanent loss" — you end up with less value than if you had simply held the tokens. The more volatile the pair, the greater the risk. This loss becomes permanent when you withdraw.

Lending and Borrowing

TON's lending ecosystem is still maturing. Protocols like Evaa allow:

  • Lending: Deposit tokens to earn interest from borrowers
  • Borrowing: Provide collateral and borrow other assets
  • Liquidation risk: If your collateral value drops below the required ratio, your position is liquidated (sold at a discount) to repay the loan

Smart Contract Risks on TON

TON's asynchronous, message-passing architecture creates unique smart contract vulnerabilities:

  • Bounce handling bugs: When a message to a contract fails, a bounce message is sent back. If the sender contract does not handle bounces correctly, funds can be lost in limbo
  • Message ordering: Because contracts communicate asynchronously across shards, the order of message delivery is not guaranteed. This can create race conditions
  • Storage fee drainage: Contracts that accumulate too much state data without paying storage fees can be frozen, locking user funds
  • Reentrancy (different from Ethereum): While traditional reentrancy is less of a concern due to the message model, new patterns of "callback reentrancy" via message chains exist
  • Unverified contracts: Many TON contracts are not open-source or audited. You cannot verify what the code does before interacting with it

How to Evaluate a TON DeFi Project

1
Check for audits

Has the project been audited by a reputable security firm? Look for audit reports on the project's documentation or GitHub. Major firms auditing TON projects include CertiK, Quantstamp, and Trail of Bits.

2
Verify open-source code

Is the smart contract code publicly available? Closed-source contracts are a red flag — you cannot verify what they do with your funds.

3
Research the team

Are the developers known and reputable? Anonymous teams are higher risk. Check their track record on previous projects.

4
Check TVL and age

How much total value is locked (TVL) in the protocol? How long has it been running? Newer protocols with low TVL are higher risk. Established protocols with significant TVL have more to lose from exploits.

5
Be skeptical of unrealistic yields

If a protocol offers 1000% APY, ask where the yield comes from. Sustainable yields come from trading fees or lending interest. Unsustainable yields come from token emissions (printing new tokens), which dilute value over time.

Common DeFi Scams on TON

  • Fake DEX frontends: Phishing sites that look like STON.fi or DeDust but steal your wallet connection
  • Honeypot tokens: Tokens you can buy but cannot sell — the sell function is disabled in the contract
  • Rug pulls: Projects that collect liquidity then drain the pool and disappear
  • Flash loan manipulation: Attackers exploit price oracles to drain lending protocols
  • Approval exploits: Malicious contracts that request unlimited token approval, then drain your wallet later

Now Build a DEX and Attack It, in Five Steps

Every risk in this article — slippage, front-running, impermanent loss, fake tokens — comes out of one formula that fits on a line. In the next twenty-five minutes you will implement that formula, watch your own trade get sandwiched by a bot, find the setting that stops it, price what a liquidity provider actually gives up, and check a token’s real identity. Every number below came from running these files.

1
Build the exchange

Go: open a terminal in a folder you can write to, e.g. cd ~/Desktop (Windows: cd %USERPROFILE%\Desktop).

Do: save this as amm.py. It prints nothing; it is the exchange the next four steps trade against. There is no order book and no buyer on the other side — just a pool whose two balances must multiply to the same number after every trade.

class Pool:
    """A constant-product market maker: ton * jetton never falls."""
    def __init__(self, ton, jetton):
        self.ton, self.jetton = float(ton), float(jetton)

    @property
    def price(self):
        return self.ton / self.jetton

    def buy(self, spend_ton):
        k = self.ton * self.jetton
        got = self.jetton - k / (self.ton + spend_ton)
        self.ton += spend_ton
        self.jetton -= got
        return got

    def sell(self, jettons):
        k = self.ton * self.jetton
        got = self.ton - k / (self.jetton + jettons)
        self.jetton += jettons
        self.ton -= got
        return got

You should see: nothing at all. python3 amm.py returns you to the prompt, which means it parses.

If not: IndentationError means the methods lost their indent under class. If @property raises a syntax error, the decorator line must sit immediately above def price with nothing between.

2
Discover why a big trade gets a worse price

Go: same folder.

Do: save this as impact.py and run it. Each line starts from an identical, untouched pool.

from amm import Pool

for spend in (10, 100, 1_000, 10_000):
    p = Pool(100_000, 100_000)
    fair = spend / p.price
    got = p.buy(spend)
    print(f"spend {spend:>6,} TON -> {got:>10,.1f} jettons "
          f"(a fair price would give {fair:>10,.1f}, so you lose {1 - got / fair:.2%})")

You should see: the cost of size:

spend     10 TON ->       10.0 jettons (a fair price would give       10.0, so you lose 0.01%)
spend    100 TON ->       99.9 jettons (a fair price would give      100.0, so you lose 0.10%)
spend  1,000 TON ->      990.1 jettons (a fair price would give    1,000.0, so you lose 0.99%)
spend 10,000 TON ->    9,090.9 jettons (a fair price would give   10,000.0, so you lose 9.09%)

This is price impact, and it is not a fee — it is the mathematics of the pool. Your own buying moves the price against you, and the loss grows with your size relative to the pool. It is the reason a thin pool is dangerous regardless of how honest the project is: the smaller the pool, the more a normal trade costs you.

If not: if every line shows the same loss, the pool is being reused instead of recreated — Pool(...) must be constructed inside the loop.

3
Get sandwiched, and count the attacker’s profit

Go: same folder. Your pending trade is visible before it executes. A bot buys just before you and sells just after.

Do: save this as sandwich.py and run it.

from amm import Pool

victim_spend = 1_000

p = Pool(100_000, 100_000)
alone = p.buy(victim_spend)
print(f"if nobody interferes, you get   : {alone:,.1f} jettons")

p = Pool(100_000, 100_000)
attacker_jettons = p.buy(20_000)          # the attacker buys FIRST, pushing the price up
yours = p.buy(victim_spend)               # your order now fills at the worse price
attacker_ton = p.sell(attacker_jettons)   # the attacker sells straight after
print(f"sandwiched, you actually get    : {yours:,.1f} jettons")
print(f"you lost                        : {alone - yours:,.1f} jettons ({1 - yours / alone:.2%})")
print(f"the attacker put in 20,000 TON and took out {attacker_ton:,.1f}, "
      f"profit {attacker_ton - 20_000:,.1f} TON")

You should see: nearly a third of your purchase taken, legally, by arithmetic:

if nobody interferes, you get   : 990.1 jettons
sandwiched, you actually get    : 688.7 jettons
you lost                        : 301.4 jettons (30.44%)
the attacker put in 20,000 TON and took out 20,306.5, profit 306.5 TON

Nothing was hacked. The attacker used the same public exchange you did, in the same way, a moment earlier and a moment later. This is why a swap that looked fine when you pressed the button can settle far worse — and why the next step’s setting exists.

If not: if the attacker’s profit is negative, the sell is happening before your buy — the three calls must run in the order written.

4
Find the setting that refuses the bad fill

Go: same folder. Every swap interface has a slippage tolerance, usually hidden behind a gear icon and usually left alone.

Do: save this as tolerance.py and run it. The attack from step 3 happens every time; only your floor changes.

from amm import Pool

victim_spend, expected = 1_000, 990.1

for tolerance in (0.005, 0.01, 0.05, 0.30, 0.50):
    p = Pool(100_000, 100_000)
    p.buy(20_000)                                   # attacker front-runs
    got = p.buy(victim_spend)
    minimum = expected * (1 - tolerance)
    verdict = "FILLED" if got >= minimum else "REVERTED -- your funds stay put"
    print(f"slippage tolerance {tolerance:>5.1%}: you would receive {got:>7,.1f}, "
          f"floor {minimum:>7,.1f}  -> {verdict}")

You should see: the protection working until you switch it off yourself:

slippage tolerance  0.5%: you would receive   688.7, floor   985.1  -> REVERTED -- your funds stay put
slippage tolerance  1.0%: you would receive   688.7, floor   980.2  -> REVERTED -- your funds stay put
slippage tolerance  5.0%: you would receive   688.7, floor   940.6  -> REVERTED -- your funds stay put
slippage tolerance 30.0%: you would receive   688.7, floor   693.1  -> REVERTED -- your funds stay put
slippage tolerance 50.0%: you would receive   688.7, floor   495.1  -> FILLED

Read the last line carefully, because it is the most common self-inflicted loss in DeFi. A swap that keeps failing tempts people to raise the tolerance until it goes through — and a high tolerance is an instruction to accept any price down to that floor. If a swap fails at 1%, the honest reading is that the pool is too thin for your size or somebody is waiting for you, and the answer is a smaller trade, not a bigger allowance.

If not: if every line says FILLED, the minimum comparison is inverted — the trade fills only when what you receive is at or above the floor.

5
Check which token you are actually buying

Go: same folder, beside tonaddr.py.

Do: save this as identity.py and run it. On TON a token is a jetton, and its identity is the address of its minter contract — not its name, which anybody may copy.

import hashlib
from tonaddr import encode

def jetton_address(minter_name):
    """A jetton's identity is its MINTER contract address, never its name or symbol."""
    return encode(hashlib.sha256(minter_name.encode()).digest())

real = jetton_address("official-usdt-minter")
fake = jetton_address("copycat-usdt-minter")

print("both tokens display the same name and symbol: USDT")
print("the one in the official list :", real)
print("the one the swap page linked :", fake)
print("same token:", real == fake)

You should see: two entirely different tokens wearing one name:

both tokens display the same name and symbol: USDT
the one in the official list : EQANPe5J8_ntdEoqZklA9p0LaK9thJO4S5qZiGLOvJpEw74F
the one the swap page linked : EQBgf-LvUCwVuaVed8cR7PaJ2o6KRMAAc5RIz1AJFy5mK5qK
same token: False

Creating a jetton called USDT with the same logo costs almost nothing, and a swap link can point at it. Your wallet will show the name it was given. Before a first swap into any token, compare the minter address in the swap interface against the address published by the project itself — on their own site or in the exchange’s verified list — character by character at both ends. Do it once per token; after that your wallet remembers.

If not: ModuleNotFoundError: No module named 'tonaddr' means the file from the introduction tutorial is not in this folder. Your two addresses will differ from the ones above only if the quoted minter names were changed.

🎉
Check yourself before moving on

Without scrolling up: your swap keeps failing with “price impact too high”, and a helpful reply in a group chat tells you to set slippage to 49%. What is actually happening, and what should you do? Answer: the pool is too thin for the size you are trading, or a bot is positioned to sandwich you — either way the failure is the protection doing its job, as step 4 showed. Raising the tolerance does not fix the price; it instructs the swap to accept a far worse one, which is exactly what the person advising you may be waiting for. The right responses are to trade a smaller amount, split it into several, choose a pool with deeper liquidity, or not to trade this token at all.

Now do it without the page: in sandwich.py, shrink the pool to Pool(10_000, 10_000) and re-run it, then grow it to Pool(1_000_000, 1_000_000). Watch the attacker’s profit on the same 1,000 TON trade rise and fall. You have just derived the rule that governs safe trade sizing: what matters is not how much you are trading, but how much you are trading relative to the pool.

Summary

  • TON DeFi includes DEXs, liquidity pools, lending, and yield farming
  • Impermanent loss is the biggest risk for liquidity providers
  • TON's asynchronous contracts create unique vulnerability classes
  • Always check for audits, open-source code, and team reputation before using a protocol
  • Unrealistic yields are the primary red flag for scams
  • Start small, test with amounts you can afford to lose, and never invest more than you can lose
🎉
You can now navigate TON DeFi safely!

With this knowledge, you can evaluate projects critically and protect yourself from the most common risks.