ThaiChain Documentation
Complete guide for interacting with ThaiChain using Foundry Cast. Learn how to query blocks, transactions, tokens, deploy contracts, and manage fees.
Download SKILL.md for AI agentsA ready-to-use skill file for vibe-coding agents (Claude Code, Cursor, etc.) — drop it into your project to give the agent full ThaiChain context.
Network Information
Table of Contents
01. Install Foundry
Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust. castis Foundry's command-line tool for interacting with EVM smart contracts, sending transactions, and getting chain data.
Install via foundryup
curl -L https://getfoundry.sh/install | bashAfter installation, restart your terminal and run:
foundryupVerify Installation
cast --version02. Gas Fee Model
Important: ThaiChain has no volatile native gas token. Fees are paid in TIP-20 stablecoins — not native ETH.
- ▸ Default fee token: TCH (
0x20C0000000000000000000000000000000000000) - ▸ Transaction type:
0x76(Tempo transaction envelope withfeePayerfield) - ▸ Fee AMM: auto-converts any USD-denominated TIP-20 token to the validator's preferred fee token
- ▸ Any-token gas: Any TIP-20 token can pay for gas
- ▸ Sponsorship: a
feePayerfield allows third parties to cover fees
Because fees are paid in a stablecoin, you must hold a fee token balance (e.g. TCH) — not native ETH — to transact.
03. Setup
Common Variables
# RPC endpoint
RPC=https://rpc.thaichain.org
# Token & system addresses
TCH=0x20C0000000000000000000000000000000000000
TIP20_FACTORY=0x20fc000000000000000000000000000000000000
FEE_MANAGER=0xfeEC000000000000000000000000000000000000
STABLECOIN_DEX=0xdec0000000000000000000000000000000000000
# Private key (store as env variable)
export PRIVATE_KEY=<your_private_key>Predeployed System Contracts
| Address | Name | Purpose |
|---|---|---|
| 0x20C0...0000 | TCH | First stablecoin (default fee token) |
| 0x20fc...0000 | TIP-20 Factory | Create new TIP-20 tokens |
| 0xfeEC...0000 | Fee Manager | Fee payments & conversions |
| 0xdec0...0000 | Stablecoin DEX | Enshrined DEX for stablecoin swaps |
| 0x403c...0000 | TIP-403 Registry | Transfer policy registry |
| 0xcA11...CA11 | Multicall3 | Batch multiple calls |
| 0xba5E...a5Ed | CreateX | Deterministic CREATE2 deployment |
| 0x0000...8ba3 | Permit2 | Token approvals & transfers |
04. TIP-20 Token Standard
TIP-20 extends ERC-20 with payment-native features:
- ▸ Pay fees in any USD-denominated TIP-20 token (via Fee AMM)
- ▸ RBAC roles:
ISSUER_ROLE(mint/burn),PAUSE_ROLE,UNPAUSE_ROLE,BURN_BLOCKED_ROLE - ▸ Transfer memos (32-byte reference attached to transfers)
- ▸ Currency declaration — token tracks an asset (e.g.
"USD") - ▸ Supply caps and pause/unpause controls
- ▸ Compliance via TIP-403 Policy Registry (whitelist/blacklist)
05. Create a TIP-20 Token
Call createToken on the TIP-20 Factory:
cast send 0x20fc000000000000000000000000000000000000 \
"createToken(string,string,uint8,string,uint256)" \
"My USD Coin" "MUSD" 6 "USD" 1000000000000 \
--rpc-url https://rpc.thaichain.org \
--private-key $PRIVATE_KEYArgs: name, symbol, decimals, currency, maxSupply (use type(uint256).max for uncapped).
The factory deploys the token deterministically and assigns the caller all roles (issuer/pause/etc.). Read the new token's address from the TokenCreated event logs.
06. View Token Info
# View all token info at once
echo "Name: $(cast call $TCH 'name()(string)' --rpc-url $RPC)"
echo "Symbol: $(cast call $TCH 'symbol()(string)' --rpc-url $RPC)"
echo "Currency: $(cast call $TCH 'currency()(string)' --rpc-url $RPC)"
echo "Decimals: $(cast call $TCH 'decimals()(uint8)' --rpc-url $RPC)"
echo "Total Supply: $(cast call $TCH 'totalSupply()(uint256)' --rpc-url $RPC)"Note: TIP-20 tokens always use 6 decimals (THAICHAIN-TIP21)
07. View Balance
cast call $TCH "balanceOf(address)(uint256)" $ADMIN --rpc-url $RPCConversion Table (6 decimals)
| Amount | Raw Value |
|---|---|
| 1 TCH | 1000000 |
| 10 TCH | 10000000 |
| 100 TCH | 100000000 |
| 1,000 TCH | 1000000000 |
| 10,000 TCH | 10000000000 |
08. Mint Token
Note: You must have ISSUER_ROLE to mint tokens.
# Mint 100 TCH (100 * 10^6 = 100000000)
cast send $TCH \
"mint(address,uint256)" \
$RECIPIENT 100000000 \
--private-key $PRIVATE_KEY \
--rpc-url $RPC09. Transfer Token
# Transfer 1 TCH (1,000,000 units) to recipient
cast send $TCH \
"transfer(address,uint256)(bool)" \
$RECIPIENT 1000000 \
--private-key $PRIVATE_KEY \
--rpc-url $RPC10. Add Token to Fee AMM
To let users pay gas in a custom TIP-20 token (beyond the default TCH), add liquidity to the Stablecoin DEX.
# 1. Approve token for the DEX
cast send <TOKEN_ADDRESS> "approve(address,uint256)" \
0xdec0000000000000000000000000000000000000 \
$(cast max-uint256) \
--rpc-url https://rpc.thaichain.org \
--private-key $PRIVATE_KEY
# 2. Add liquidity (pair TOKEN + TCH)
cast send 0xdec0000000000000000000000000000000000000 \
"addLiquidity(address,uint256,address,uint256)" \
<TOKEN_ADDRESS> 1000000000 \
0x20C0000000000000000000000000000000000000 1000000000 \
--rpc-url https://rpc.thaichain.org \
--private-key $PRIVATE_KEYAfter liquidity exists, the Fee AMM will accept your token for fee payments and auto-convert to the validator's preferred fee token.
11. Fee Manager
Fee Manager precompile is located at 0xfeEC000000000000000000000000000000000000
Fee Flow
User sends transaction
│
▼
collect_fee_pre_tx → Deduct gas fee from user
│
▼
Transaction executes
│
▼
collect_fee_post_tx → Accumulate to collected_fees[validator][token]
│
▼
distributeFees() → Transfer accumulated fees → validator addressCheck Validator Fees
# Accumulated fees
cast call $FEE_MANAGER \
"collectedFees(address,address)(uint256)" \
$VALIDATOR $TCH --rpc-url $RPC
# Validator fee token
cast call $FEE_MANAGER \
"validatorTokens(address)(address)" \
$VALIDATOR --rpc-url $RPCDistribute Fees
# Anyone can call — transfers accumulated fees to validator
cast send $FEE_MANAGER \
"distributeFees(address,address)" \
$VALIDATOR $TCH \
--private-key $PRIVATE_KEY --rpc-url $RPC12. Gas Sponsorship
ThaiChain supports Tempo's fee sponsorship model — a third party (fee payer) signs the feePayerSignatureso users don't need to hold fee tokens.
Client-side (viem)
import { Account, withRelay } from "viem/tempo";
const account = Account.fromSecp256k1(privateKey);
const client = createWalletClient({
account,
chain: thaichain,
transport: withRelay(
http("https://rpc.thaichain.org"),
http("https://sponsor.thaichain.org"),
{ policy: "sign-and-broadcast" },
),
});
await client.sendTransaction({
to, data, value,
feePayer: true, // ← enables sponsorship
});Self-hosted Fee Payer
Deploy the fee-payer worker (Cloudflare Worker + Hono) with:
SPONSOR_PRIVATE_KEY=<sponsor_wallet_key>
TEMPO_ENV=thaichain
TEMPO_RPC_URL=https://rpc.thaichain.org13. Deploy Smart Contracts
Deploy with forge create
forge create src/MyContract.sol:MyContract \
--rpc-url https://rpc.thaichain.org \
--private-key $PRIVATE_KEY \
--constructor-args 0x20C0000000000000000000000000000000000000 \
--gas-limit 10000000 --slowDeploy with forge script
forge script script/Deploy.s.sol \
--rpc-url https://rpc.thaichain.org \
--broadcast \
--private-key $PRIVATE_KEY \
--slow⚠️ Gas limit gotcha: forge script may run out of gas. Add --gas-limit 10000000 --skip-simulation or use forge create per contract.
UUPS Upgradeable Proxy (two-step)
# Step 1 — Deploy implementation
forge create src/MyContractV2.sol:MyContractV2 \
--rpc-url https://rpc.thaichain.org \
--private-key $PRIVATE_KEY --gas-limit 10000000
# Step 2 — Deploy proxy
INIT_DATA=$(cast abi-encode "initialize(address)" 0xADMIN_ADDRESS)
forge create @openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol:ERC1967Proxy \
--rpc-url https://rpc.thaichain.org \
--private-key $PRIVATE_KEY --gas-limit 5000000 \
--constructor-args 0xIMPLEMENTATION_ADDRESS $INIT_DATA14. Verify Contracts
forge verify-contract 0xYOUR_CONTRACT_ADDRESS \
src/MyContract.sol:MyContract \
--chain-id 7 \
--verifier sourcify \
--verifier-url https://contracts.thaichain.org15. Tempo Transactions (0x76)
ThaiChain uses Tempo's transaction envelope (type 0x76) which extends EIP-1559 with:
- ▸
feeToken— which TIP-20 token pays the fee - ▸
feePayerSignature— optional sponsor signature - ▸
calls[]— multiple calls in one tx (batching) - ▸
accessList,authorizationList— standard fields
JSON-RPC Methods
# Fill missing fields (nonce, gas, fee payer)
eth_fillTransaction
# Broadcast and wait for receipt
eth_sendRawTransactionSync
# Sign-only policy
eth_signRawTransaction16. Decode Data
Decode Function Selector
cast 4byte 0xa6c07924
# Output: distributeFees(address,address)Decode Calldata
cast pretty-calldata 0xa6c079240000000000000000000000005266dfa5ae013674f8fdc832b7c601b838d94ee600000000000000000000000020c000000000000000000000000000000000000017. Example Scripts
Check All Tokens
#!/bin/bash
RPC=https://rpc.thaichain.org
TOKENS=(
"0x20C0000000000000000000000000000000000000"
)
for TOKEN in "${TOKENS[@]}"; do
echo "=== Token: $TOKEN ==="
echo "Name: $(cast call $TOKEN 'name()(string)' --rpc-url $RPC)"
echo "Symbol: $(cast call $TOKEN 'symbol()(string)' --rpc-url $RPC)"
echo "Currency: $(cast call $TOKEN 'currency()(string)' --rpc-url $RPC)"
echo "Decimals: $(cast call $TOKEN 'decimals()(uint8)' --rpc-url $RPC)"
echo "Supply: $(cast call $TOKEN 'totalSupply()(uint256)' --rpc-url $RPC)"
echo ""
doneCheck Validator Status
#!/bin/bash
RPC=https://rpc.thaichain.org
FEE_MANAGER=0xfeec000000000000000000000000000000000000
TCH=0x20C0000000000000000000000000000000000000
VALIDATORS=(
"0x5266Dfa5ae013674f8FdC832b7c601B838D94eE6"
)
for V in "${VALIDATORS[@]}"; do
echo "=== Validator: $V ==="
echo "Fee Token: $(cast call $FEE_MANAGER 'validatorTokens(address)(address)' $V --rpc-url $RPC)"
echo "Collected Fees: $(cast call $FEE_MANAGER 'collectedFees(address,address)(uint256)' $V $TCH --rpc-url $RPC)"
echo ""
done18. Useful Resources
Building with an AI agent?
Download the SKILL.md file and add it to your project. It gives Claude Code, Cursor, and other vibe-coding agents full context about ThaiChain — chain config, gas model, system contracts, and all the commands above.
Download SKILL.md