func2tolk
Overview
Port a legacy FunC smart contract to Tolk while preserving:
- TL-B layout (storage and message bodies)
- opcode numbers and error codes
- observable behavior (including bounce handling and send modes)
Prefer Acton as the development framework (build, wrappers, native Tolk tests, scripts, deploy, verify).
Use acton func2tolk as a first-pass converter when it accelerates the job, then treat the output as a draft to audit and refactor into idiomatic, testable Tolk.
For common mappings and gotchas, open references/porting-checklist.md. For public idiomatic examples and original FunC baselines, open references/repo-examples.md.
Non-negotiable rules (MUST follow)
These rules are mandatory for every FunC -> Tolk porting run.
- MUST follow the idioms in this skill and in:
https://docs.ton.org/languages/tolk/idioms-conventions
- MUST use typed message schemas + union dispatch (
type Allowed... + lazy ...fromSlice(...) + match) for known opcode families.
- MUST use structs for storage/message decoding instead of imperative field-by-field parsing in business logic.
- MUST use typed maps (
map<K, V>) and typed helpers in core logic; raw dict parsing is allowed only at boundaries and must be wrapped immediately.
- MUST keep TL-B compatibility constraints explicit when deviating from idiomatic code (for example:
UnsafeBodyNoRef, any_address?, legacy inline-body quirks).
- MUST open and apply
references/porting-checklist.md during the run.
- MUST NOT end a porting run before completing the mandatory completion gate in this file.
Forbidden unless explicitly justified by compatibility:
- manual opcode
if/elseif ladders for messages that can be modeled as tagged structs + union + match
- ad-hoc message parsing (
load_uint/load_msg_addr/...) in domain logic where a typed struct is feasible
- raw
udict_* operations spread through core business branches instead of typed map helpers
- finishing the run without a checklist-based self-audit
Portable references and example corpus
- Skill references in this skill directory:
references/porting-checklist.md
references/repo-examples.md
- Primary public style oracle:
https://github.com/ton-blockchain/acton-contracts
- Each contract suite contains Acton/Tolk contracts, native tests, scripts, generated wrappers, benchmark snapshots where relevant, and a README linking to the original FunC implementation.
- High-value Acton/Tolk suites to study:
jetton-v2 paired with https://github.com/ton-blockchain/jetton-contract/tree/jetton-2.0
nft paired with https://github.com/ton-blockchain/nft-contract
w5 paired with https://github.com/ton-blockchain/wallet-contract-v5
dns paired with https://github.com/ton-blockchain/dns-contract
multisig-v2 paired with https://github.com/ton-blockchain/multisig-contract-v2
highload-v3 paired with https://github.com/ton-blockchain/highload-wallet-contract-v3
notcoin paired with https://github.com/OpenBuilders/notcoin-contract
config paired with https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/config-with-ownable-params.fc
elector paired with https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/elector-code.fc
counter in acton-contracts has no original FunC counterpart; use it only for minimal Acton project mechanics.
- Acton docs:
https://github.com/ton-blockchain/acton/tree/master/docs/content/docs
- otherwise use
acton --help and the official hosted docs
- Tolk idioms and conventions (docs-first style target):
https://docs.ton.org/languages/tolk/idioms-conventions
- Before running
acton ..., cd to the directory that contains Acton.toml (Acton resolves contracts/config relative to it).
Porting workflow (Acton-first)
1) Identify the behavioral contract
- Locate FunC entrypoints (
recv_internal, recv_external, run_ticktock, getters) and list handled opcodes.
- Extract invariants to preserve:
- exact storage layout (bit widths, field order, presence/absence of refs)
- message TL-B layout (including
Either payloads and Maybe fields)
- gas/reserve behavior and send modes
- Treat existing JS/TS tests as the spec; translate them into native Tolk tests as you port.
2) Set up Acton project structure
- Scaffold or reuse a project:
acton new PROJECT --template empty|counter|jetton
- Define contracts in
Acton.toml under [contracts.*].
- Use
[mappings] to avoid deep relative imports (mirror the @stdlib/... import style).
- If the system is multi-contract (Jetton minter + wallet, collection + item, etc.), model dependencies in
Acton.toml and use acton build --graph to sanity-check the dependency DAG.
3) Translate storage and TL-B types first
- Split types into files importable by tests/wrappers (avoid putting message/storage structs inside the contract entrypoint file):
errors.tolk — error codes
messages.tolk — message TL-B structs and union types
storage.tolk — persistent structs and helpers
- Model contract data as
struct Storage { ... } (and related structs).
- Implement storage helpers:
fun Storage.load() { return Storage.fromCell(contract.getData()) }
fun Storage.save(self) { contract.setData(self.toCell()) }
- If FunC has “maybe initialized” storage, model it explicitly (for example a wrapper that starts parsing a
slice, checks remaining refs, and then parses either Initialized or NotInitialized variants).
4) Translate message bodies using tagged structs
- Replace FunC
op::... + manual parsing with tagged structs:
struct (0x...) SomeMessage { ... }
- Create a union type for dispatch:
type AllowedMessage = A | B | C
- Parse using lazy decoding:
val msg = lazy AllowedMessage.fromSlice(in.body);
- To match legacy encodings, prefer correct types over ad-hoc bit twiddling:
RemainingBitsAndRefs for “remainder” payloads / snaked formats
Cell<T> for ref payloads
any_address? when the legacy encoding is not address?-compatible
5) Implement entrypoints in Acton style
- Internal messages:
fun onInternalMessage(in: InMessage) { ... }
- Bounced messages:
fun onBouncedMessage(in: InMessageBounced) { ... }
- Use it to restore balances/supply on failed sends (common in Jettons).
- External messages (wallets, signatures):
fun onExternalMessage(inMsgBody: slice) { ... }
- Prefer
match for dispatch; decide explicitly whether to ignore unknown/empty bodies or throw 0xFFFF.
- If you must handle bounces manually inside
onInternalMessage, use compiler policies (for example @on_bounced_policy("manual")) and document why.
6) Replace low-level message building
- Prefer
createMessage({ ... }) + .send(SEND_MODE_...) over manual builder encoding.
- Keep send modes and bounce flags consistent with FunC behavior.
- Prefer
@stdlib/gas-payments helpers when porting contracts that rely on raw_reserve, accept_message, storage fee reservation, etc.
7) Migrate tests (JS/TS → native Tolk)
- Write Acton tests in
tests/*.test.tolk.
- Use
net.treasury("name") to create funded emulated accounts.
- Use
expect(res) matchers on returned transaction lists.
- Generate wrappers (recommended):
acton wrapper CONTRACT_ID --test
- Keep shared types in a separate file (for example
contracts/types.tolk) so wrappers/tests can import them without importing contract entrypoints (avoids duplicate onInternalMessage definitions).
8) Iterate with Acton tooling
- Build:
acton build (add --clear-cache when debugging).
- Test:
acton test (use --filter, --coverage, --debug as needed).
- Debug compilation differences:
acton disasm (use --source-map when available).
- Dry-run deployment logic locally:
acton script scripts/deploy.tolk (no real TON spent).
- Broadcast only after local success:
acton script scripts/deploy.tolk --broadcast --net testnet|mainnet
9) Verify on-chain
- After deployment:
acton verify CONTRACT_ID --address EQ... --net testnet|mainnet
High-signal FunC -> idiomatic Tolk patterns (from acton-contracts)
Use this section as the default translation strategy unless compatibility constraints force lower-level code.
A) Opcode dispatch: replace if (op == ...) chains with typed unions + match
FunC baseline:
op = in_msg_body~load_uint(32); if (op == op::x) { ... } if (op == op::y) { ... }
Idiomatic Tolk:
- declare tagged message structs in
messages.tolk
- group them into a union (
type AllowedMessage = A | B | C)
- parse lazily and dispatch with
match
type AllowedMessage = Transfer | Burn | TopUp
fun onInternalMessage(in: InMessage) {
val msg = lazy AllowedMessage.fromSlice(in.body);
match (msg) {
Transfer => { ... }
Burn => { ... }
TopUp => { ... }
else => throw 0xFFFF
}
}
Why this is better:
- preserves opcode compatibility while eliminating manual parse drift
- keeps branch-local parsing strongly typed
- scales better for large opcode sets (wallet-v5/vesting/telemint style)
B) Parse bodies into structs, not ad-hoc loads
Prefer:
struct (0x...) for externally visible opcodes
- nested structs for grouped payloads (
TransferOwnershipData, MsgInner, etc.)
Cell<T> when payload is explicitly by-reference
RemainingBitsAndRefs for true remainder payloads / snake / either wrappers
Avoid:
- repeated
load_uint/load_msg_addr/load_ref in business logic when a struct can model the same layout.
C) Use dedicated messages.tolk modules for inbound formats
Pattern used broadly in ports:
- keep all incoming message schemas in
messages.tolk
- import message types into contract entrypoints
- define
Allowed... unions near message definitions
This keeps entrypoint files focused on behavior, not byte parsing.
D) Storage translation: tuple loaders -> typed storage structs + methods
FunC baseline:
(a, b, c) load_data() and save_data(a, b, c)
Idiomatic Tolk:
struct Storage { ... }
Storage.load() / Storage.save()
- keep TL-B layout identical (field order + ref/inline shape)
struct Storage { totalSupply: coins adminAddress: address? }
fun Storage.load() { return Storage.fromCell(contract.getData()) }
fun Storage.save(self) { contract.setData(self.toCell()) }
E) Model multi-shape state explicitly (initialized vs not initialized)
Common in NFT/DNS/Telemint items:
- FunC checks
slice_bits() > 0 or ref presence to decide shape.
Idiomatic Tolk:
- loader wrapper + explicit parsers:
startLoading...()
.isInitialized()
.parseNotInitialized()
.parseInitialized()
This keeps state transitions explicit and avoids brittle remainder parsing.
F) Dictionaries: replace raw udict_* plumbing with typed maps
FunC baseline:
- manual
udict_get?, udict_set_ref, udict_delete?, udict_delete_get_min
Idiomatic Tolk:
map<K, V> aliases + typed helpers (exists, set, delete, findFirst, iterateNext)
- convert low-level dicts only at boundaries (
createMapFromLowLevelDict(...))
Examples:
- DNS records:
type DnsRecords = map<uint256, cell>
- Wallet extensions:
map<uint256, bool>
- Query bitmaps:
map<uint13, Bitmap>
G) Prefer createMessage(...) over manual begin_cell() message assembly
Default:
- build outgoing messages with
createMessage({ ... })
- send via
.send(SEND_MODE_...)
Keep manual builders only when:
- emulating historical quirks exactly
- forcing exact inline/no-ref body layout
H) External signature flows: parse suffixes/remainders deliberately
For wallet-style externals:
- separate signature from signed payload (
getLastBits/removeLastBits or explicit field ordering)
- validate hash/signature first
- update replay state (
seqno / query maps), persist, commit
- then execute actions/messages
This mirrors hardened wallet-v5/highload patterns.
I) Bounce handling: prefer onBouncedMessage with typed bounced unions
FunC baseline:
- bounce branch in
recv_internal + manual load_uint
Idiomatic Tolk:
onBouncedMessage(in: InMessageBounced)
- optional union for bounced op subset:
type BounceOpToHandle = A | B
- restore balances/supply from typed parsed message
J) Compatibility levers to preserve legacy TL-B/behavior
Use these intentionally when matching FunC tests:
UnsafeBodyNoRef { forceInline: ... } for no-ref body compatibility
any_address? when legacy layout is broader than address?
- explicit
(Either Cell ^Cell) remainder validation helpers for payload correctness
- keep unknown-op behavior identical (
throw 0xFFFF vs ignore empty/body)
K) Domain-specific recipes extracted from real ports
Jetton family:
AllowedMessageToMinter / AllowedMessageToWallet
- typed bounce restore (
InternalTransferStep | BurnNotification...)
- typed forward payload (
RemainingBitsAndRefs) + validator helper
NFT + DNS items:
- explicit uninitialized->initialized transition path
- ownership transfer payload as nested struct
- map-based content records instead of imperative dict bit twiddling
Highload wallet:
- validate minimal shape early, then typed parse
- model replay bitmaps as typed map + helper methods
- typed validation for outbound raw message shape before forwarding
Vesting / policy wallets:
- whitelist maps as typed
map<address, ()>
- validate allowed opcodes using union parsing +
match (instead of numeric opcode allowlists spread across branches)
Mandatory completion gate (before ending any porting run)
Do not end the run until all items below are checked.
- Checklist was opened and applied:
references/porting-checklist.md
- Dispatch is idiomatic:
- known message families use tagged structs + union +
match
- Parsing is typed:
- no avoidable imperative parsing in business branches
- Storage is typed:
Storage.load() / Storage.save() (or explicit initialized/uninitialized loader pattern)
- Dict usage is typed:
map<K,V> + helpers in logic; boundary raw dict decoding is wrapped
- Compatibility exceptions are documented:
- any non-idiomatic construct is justified as TL-B/behavior parity
- Build/tests status is reported:
- run relevant
acton build/acton test (or report exact blocker)
- Final response includes a short self-audit summary against the gate above.
If any gate item cannot be completed, do not silently finish. Report the blocker and list incomplete gate items explicitly.
Using acton-contracts as a style oracle
Use https://github.com/ton-blockchain/acton-contracts as the default public style oracle for production ports:
- use each suite's README to find the original FunC repo or file
- use the original FunC source as the behavioral baseline, especially for edge cases
- use the matching Acton/Tolk suite as the target style for module layout, typed storage, message unions, wrappers, scripts, tests, and benchmark snapshots
Prefer searching by:
- opcode hex (for example
0xd53276db)
- message type name (for example
InternalTransferStep)
- storage field name (for example
totalSupply, ownerAddress)
Open references/repo-examples.md for suite mappings, original FunC links, and search targets.
Docs-first lookups
- For Tolk language details and standard library, use TON Docs pages under
languages/tolk/ (overview, serialization, standard library, FunC comparison).
- For Acton specifics (commands, wrappers, testing, scripts), use Acton docs and
acton --help.
1---2name: func2tolk3description: Port TON smart contracts from FunC (.fc/.func) to modern Tolk (.tolk) with Acton: use acton func2tolk when helpful, then refactor storage/messages/opcodes into typed structs and serialization, implement Acton-style entrypoints, generate wrappers, migrate JS/TS tests to native Tolk tests, and use acton build/test/script/verify/disasm to preserve TL-B compatibility and behavior. Use when asked to convert, port, or modernize FunC contracts into idiomatic Tolk.4---56# func2tolk78## Overview910Port a legacy FunC smart contract to Tolk while preserving:11- TL-B layout (storage and message bodies)12- opcode numbers and error codes13- observable behavior (including bounce handling and send modes)1415Prefer Acton as the development framework (build, wrappers, native Tolk tests, scripts, deploy, verify).1617Use `acton func2tolk` as a first-pass converter when it accelerates the job, then treat the output as a draft to audit and refactor into idiomatic, testable Tolk.1819For common mappings and gotchas, open `references/porting-checklist.md`. For public idiomatic examples and original FunC baselines, open `references/repo-examples.md`.2021## Non-negotiable rules (MUST follow)2223These rules are mandatory for every FunC -> Tolk porting run.2425- MUST follow the idioms in this skill and in:26 - `https://docs.ton.org/languages/tolk/idioms-conventions`27- MUST use typed message schemas + union dispatch (`type Allowed...` + `lazy ...fromSlice(...)` + `match`) for known opcode families.28- MUST use structs for storage/message decoding instead of imperative field-by-field parsing in business logic.29- MUST use typed maps (`map<K, V>`) and typed helpers in core logic; raw dict parsing is allowed only at boundaries and must be wrapped immediately.30- MUST keep TL-B compatibility constraints explicit when deviating from idiomatic code (for example: `UnsafeBodyNoRef`, `any_address?`, legacy inline-body quirks).31- MUST open and apply `references/porting-checklist.md` during the run.32- MUST NOT end a porting run before completing the mandatory completion gate in this file.3334Forbidden unless explicitly justified by compatibility:3536- manual opcode `if/elseif` ladders for messages that can be modeled as tagged structs + union + `match`37- ad-hoc message parsing (`load_uint/load_msg_addr/...`) in domain logic where a typed struct is feasible38- raw `udict_*` operations spread through core business branches instead of typed map helpers39- finishing the run without a checklist-based self-audit4041## Portable references and example corpus4243- Skill references in this skill directory:44 - `references/porting-checklist.md`45 - `references/repo-examples.md`46- Primary public style oracle:47 - `https://github.com/ton-blockchain/acton-contracts`48 - Each contract suite contains Acton/Tolk contracts, native tests, scripts, generated wrappers, benchmark snapshots where relevant, and a README linking to the original FunC implementation.49- High-value Acton/Tolk suites to study:50 - `jetton-v2` paired with `https://github.com/ton-blockchain/jetton-contract/tree/jetton-2.0`51 - `nft` paired with `https://github.com/ton-blockchain/nft-contract`52 - `w5` paired with `https://github.com/ton-blockchain/wallet-contract-v5`53 - `dns` paired with `https://github.com/ton-blockchain/dns-contract`54 - `multisig-v2` paired with `https://github.com/ton-blockchain/multisig-contract-v2`55 - `highload-v3` paired with `https://github.com/ton-blockchain/highload-wallet-contract-v3`56 - `notcoin` paired with `https://github.com/OpenBuilders/notcoin-contract`57 - `config` paired with `https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/config-with-ownable-params.fc`58 - `elector` paired with `https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/elector-code.fc`59- `counter` in `acton-contracts` has no original FunC counterpart; use it only for minimal Acton project mechanics.60- Acton docs:61 - `https://github.com/ton-blockchain/acton/tree/master/docs/content/docs`62 - otherwise use `acton --help` and the official hosted docs63- Tolk idioms and conventions (docs-first style target):64 - `https://docs.ton.org/languages/tolk/idioms-conventions`65- Before running `acton ...`, `cd` to the directory that contains `Acton.toml` (Acton resolves contracts/config relative to it).6667## Porting workflow (Acton-first)6869### 1) Identify the behavioral contract7071- Locate FunC entrypoints (`recv_internal`, `recv_external`, `run_ticktock`, getters) and list handled opcodes.72- Extract invariants to preserve:73 - exact storage layout (bit widths, field order, presence/absence of refs)74 - message TL-B layout (including `Either` payloads and `Maybe` fields)75 - gas/reserve behavior and send modes76- Treat existing JS/TS tests as the spec; translate them into native Tolk tests as you port.7778### 2) Set up Acton project structure7980- Scaffold or reuse a project:81 - `acton new PROJECT --template empty|counter|jetton`82- Define contracts in `Acton.toml` under `[contracts.*]`.83- Use `[mappings]` to avoid deep relative imports (mirror the `@stdlib/...` import style).84- If the system is multi-contract (Jetton minter + wallet, collection + item, etc.), model dependencies in `Acton.toml` and use `acton build --graph` to sanity-check the dependency DAG.8586### 3) Translate storage and TL-B types first8788- Split types into files importable by tests/wrappers (avoid putting message/storage structs inside the contract entrypoint file):89 - `errors.tolk` — error codes90 - `messages.tolk` — message TL-B structs and union types91 - `storage.tolk` — persistent structs and helpers92- Model contract data as `struct Storage { ... }` (and related structs).93- Implement storage helpers:94 - `fun Storage.load() { return Storage.fromCell(contract.getData()) }`95 - `fun Storage.save(self) { contract.setData(self.toCell()) }`96- If FunC has “maybe initialized” storage, model it explicitly (for example a wrapper that starts parsing a `slice`, checks remaining refs, and then parses either Initialized or NotInitialized variants).9798### 4) Translate message bodies using tagged structs99100- Replace FunC `op::...` + manual parsing with tagged structs:101 - `struct (0x...) SomeMessage { ... }`102- Create a union type for dispatch:103 - `type AllowedMessage = A | B | C`104- Parse using lazy decoding:105 - `val msg = lazy AllowedMessage.fromSlice(in.body);`106- To match legacy encodings, prefer correct types over ad-hoc bit twiddling:107 - `RemainingBitsAndRefs` for “remainder” payloads / snaked formats108 - `Cell<T>` for ref payloads109 - `any_address?` when the legacy encoding is not `address?`-compatible110111### 5) Implement entrypoints in Acton style112113- Internal messages:114 - `fun onInternalMessage(in: InMessage) { ... }`115- Bounced messages:116 - `fun onBouncedMessage(in: InMessageBounced) { ... }`117 - Use it to restore balances/supply on failed sends (common in Jettons).118- External messages (wallets, signatures):119 - `fun onExternalMessage(inMsgBody: slice) { ... }`120- Prefer `match` for dispatch; decide explicitly whether to ignore unknown/empty bodies or throw `0xFFFF`.121- If you must handle bounces manually inside `onInternalMessage`, use compiler policies (for example `@on_bounced_policy("manual")`) and document why.122123### 6) Replace low-level message building124125- Prefer `createMessage({ ... })` + `.send(SEND_MODE_...)` over manual `builder` encoding.126- Keep send modes and bounce flags consistent with FunC behavior.127- Prefer `@stdlib/gas-payments` helpers when porting contracts that rely on `raw_reserve`, `accept_message`, storage fee reservation, etc.128129### 7) Migrate tests (JS/TS → native Tolk)130131- Write Acton tests in `tests/*.test.tolk`.132- Use `net.treasury("name")` to create funded emulated accounts.133- Use `expect(res)` matchers on returned transaction lists.134- Generate wrappers (recommended):135 - `acton wrapper CONTRACT_ID --test`136- Keep shared types in a separate file (for example `contracts/types.tolk`) so wrappers/tests can import them without importing contract entrypoints (avoids duplicate `onInternalMessage` definitions).137138### 8) Iterate with Acton tooling139140- Build: `acton build` (add `--clear-cache` when debugging).141- Test: `acton test` (use `--filter`, `--coverage`, `--debug` as needed).142- Debug compilation differences: `acton disasm` (use `--source-map` when available).143- Dry-run deployment logic locally: `acton script scripts/deploy.tolk` (no real TON spent).144- Broadcast only after local success:145 - `acton script scripts/deploy.tolk --broadcast --net testnet|mainnet`146147### 9) Verify on-chain148149- After deployment:150 - `acton verify CONTRACT_ID --address EQ... --net testnet|mainnet`151152## High-signal FunC -> idiomatic Tolk patterns (from acton-contracts)153154Use this section as the default translation strategy unless compatibility constraints force lower-level code.155156### A) Opcode dispatch: replace `if (op == ...)` chains with typed unions + `match`157158FunC baseline:159- `op = in_msg_body~load_uint(32); if (op == op::x) { ... } if (op == op::y) { ... }`160161Idiomatic Tolk:162- declare tagged message structs in `messages.tolk`163- group them into a union (`type AllowedMessage = A | B | C`)164- parse lazily and dispatch with `match`165166```tolk167type AllowedMessage = Transfer | Burn | TopUp168169fun onInternalMessage(in: InMessage) {170 val msg = lazy AllowedMessage.fromSlice(in.body);171 match (msg) {172 Transfer => { ... }173 Burn => { ... }174 TopUp => { ... }175 else => throw 0xFFFF176 }177}178```179180Why this is better:181- preserves opcode compatibility while eliminating manual parse drift182- keeps branch-local parsing strongly typed183- scales better for large opcode sets (wallet-v5/vesting/telemint style)184185### B) Parse bodies into structs, not ad-hoc loads186187Prefer:188- `struct (0x...)` for externally visible opcodes189- nested structs for grouped payloads (`TransferOwnershipData`, `MsgInner`, etc.)190- `Cell<T>` when payload is explicitly by-reference191- `RemainingBitsAndRefs` for true remainder payloads / snake / either wrappers192193Avoid:194- repeated `load_uint/load_msg_addr/load_ref` in business logic when a struct can model the same layout.195196### C) Use dedicated `messages.tolk` modules for inbound formats197198Pattern used broadly in ports:199- keep all incoming message schemas in `messages.tolk`200- import message types into contract entrypoints201- define `Allowed...` unions near message definitions202203This keeps entrypoint files focused on behavior, not byte parsing.204205### D) Storage translation: tuple loaders -> typed storage structs + methods206207FunC baseline:208- `(a, b, c) load_data()` and `save_data(a, b, c)`209210Idiomatic Tolk:211- `struct Storage { ... }`212- `Storage.load()` / `Storage.save()`213- keep TL-B layout identical (field order + ref/inline shape)214215```tolk216struct Storage { totalSupply: coins adminAddress: address? }217fun Storage.load() { return Storage.fromCell(contract.getData()) }218fun Storage.save(self) { contract.setData(self.toCell()) }219```220221### E) Model multi-shape state explicitly (initialized vs not initialized)222223Common in NFT/DNS/Telemint items:224- FunC checks `slice_bits() > 0` or ref presence to decide shape.225226Idiomatic Tolk:227- loader wrapper + explicit parsers:228 - `startLoading...()`229 - `.isInitialized()`230 - `.parseNotInitialized()`231 - `.parseInitialized()`232233This keeps state transitions explicit and avoids brittle remainder parsing.234235### F) Dictionaries: replace raw `udict_*` plumbing with typed maps236237FunC baseline:238- manual `udict_get?`, `udict_set_ref`, `udict_delete?`, `udict_delete_get_min`239240Idiomatic Tolk:241- `map<K, V>` aliases + typed helpers (`exists`, `set`, `delete`, `findFirst`, `iterateNext`)242- convert low-level dicts only at boundaries (`createMapFromLowLevelDict(...)`)243244Examples:245- DNS records: `type DnsRecords = map<uint256, cell>`246- Wallet extensions: `map<uint256, bool>`247- Query bitmaps: `map<uint13, Bitmap>`248249### G) Prefer `createMessage(...)` over manual `begin_cell()` message assembly250251Default:252- build outgoing messages with `createMessage({ ... })`253- send via `.send(SEND_MODE_...)`254255Keep manual builders only when:256- emulating historical quirks exactly257- forcing exact inline/no-ref body layout258259### H) External signature flows: parse suffixes/remainders deliberately260261For wallet-style externals:262- separate signature from signed payload (`getLastBits/removeLastBits` or explicit field ordering)263- validate hash/signature first264- update replay state (`seqno` / query maps), persist, commit265- then execute actions/messages266267This mirrors hardened wallet-v5/highload patterns.268269### I) Bounce handling: prefer `onBouncedMessage` with typed bounced unions270271FunC baseline:272- bounce branch in `recv_internal` + manual `load_uint`273274Idiomatic Tolk:275- `onBouncedMessage(in: InMessageBounced)`276- optional union for bounced op subset:277 - `type BounceOpToHandle = A | B`278- restore balances/supply from typed parsed message279280### J) Compatibility levers to preserve legacy TL-B/behavior281282Use these intentionally when matching FunC tests:283- `UnsafeBodyNoRef { forceInline: ... }` for no-ref body compatibility284- `any_address?` when legacy layout is broader than `address?`285- explicit `(Either Cell ^Cell)` remainder validation helpers for payload correctness286- keep unknown-op behavior identical (`throw 0xFFFF` vs ignore empty/body)287288### K) Domain-specific recipes extracted from real ports289290Jetton family:291- `AllowedMessageToMinter` / `AllowedMessageToWallet`292- typed bounce restore (`InternalTransferStep | BurnNotification...`)293- typed forward payload (`RemainingBitsAndRefs`) + validator helper294295NFT + DNS items:296- explicit uninitialized->initialized transition path297- ownership transfer payload as nested struct298- map-based content records instead of imperative dict bit twiddling299300Highload wallet:301- validate minimal shape early, then typed parse302- model replay bitmaps as typed map + helper methods303- typed validation for outbound raw message shape before forwarding304305Vesting / policy wallets:306- whitelist maps as typed `map<address, ()>`307- validate allowed opcodes using union parsing + `match` (instead of numeric opcode allowlists spread across branches)308309## Mandatory completion gate (before ending any porting run)310311Do not end the run until all items below are checked.3123131. Checklist was opened and applied:314 - `references/porting-checklist.md`3152. Dispatch is idiomatic:316 - known message families use tagged structs + union + `match`3173. Parsing is typed:318 - no avoidable imperative parsing in business branches3194. Storage is typed:320 - `Storage.load()` / `Storage.save()` (or explicit initialized/uninitialized loader pattern)3215. Dict usage is typed:322 - `map<K,V>` + helpers in logic; boundary raw dict decoding is wrapped3236. Compatibility exceptions are documented:324 - any non-idiomatic construct is justified as TL-B/behavior parity3257. Build/tests status is reported:326 - run relevant `acton build`/`acton test` (or report exact blocker)3278. Final response includes a short self-audit summary against the gate above.328329If any gate item cannot be completed, do not silently finish. Report the blocker and list incomplete gate items explicitly.330331## Using acton-contracts as a style oracle332333Use `https://github.com/ton-blockchain/acton-contracts` as the default public style oracle for production ports:334335- use each suite's README to find the original FunC repo or file336- use the original FunC source as the behavioral baseline, especially for edge cases337- use the matching Acton/Tolk suite as the target style for module layout, typed storage, message unions, wrappers, scripts, tests, and benchmark snapshots338339Prefer searching by:340- opcode hex (for example `0xd53276db`)341- message type name (for example `InternalTransferStep`)342- storage field name (for example `totalSupply`, `ownerAddress`)343344Open `references/repo-examples.md` for suite mappings, original FunC links, and search targets.345346## Docs-first lookups347348- For Tolk language details and standard library, use TON Docs pages under `languages/tolk/` (overview, serialization, standard library, FunC comparison).349- For Acton specifics (commands, wrappers, testing, scripts), use Acton docs and `acton --help`.