Skip to main content
In practice, contract sources are split into multiple files. A project containing multiple contracts shares the same codebase (messages, storage definitions, and related logic). To use symbols in another file, that file must be imported. Upon import, all its symbols become available.

Import a file to access its symbols

For example, error codes are placed in errors.tolk:
const ERR_ZERO_BALANCE = 123
// ...
To use it from parse.tolk, an explicit import is required:
import "errors"

fun validate(balance: coins) {
    assert (balance > 0) throw ERR_ZERO_BALANCE;
}
An IDE inserts imports automaticallyFortunately, when selecting items from auto‑completion, imports are inserted automatically. It works in both JetBrains and VSCode-based IDEs.

All top‑level symbols must have unique names

Notice that no export const ... or export fun ... declarations needed. All constants, functions, etc. are visible. No module‑private symbols exist. It means that all symbols must have unique names. Having fun log() in multiple files results in a “duplicate declaration” error if both files are imported.

Techniques to avoid name collisions

  1. Use long, descriptive names for top-level symbols, especially in multi‑contract projects
    • Good: ReturnExcessesBack, WalletStorage
    • Bad: Excesses, Storage
  2. Group integer constants to enums
  3. Prefer methods to global-scope functions (read an idiom)

Symbols that may be repeated across the project

When developing multiple contracts, each contract has its own file (compilation target). They do not import one another; therefore,
  • onInternalMessage and onExternalMessage
  • get fun
  • and other declarations in a contract file don’t conflict with each other
// file: a-contract.tolk
import "storage"
import "errors"

fun onInternalMessage(in: InMessage) {
    // ...
}

get fun name() {
    return "a"
}
// file: b-contract.tolk
import "storage"
import "errors"

fun onInternalMessage(in: InMessage) {
    // ...
}

get fun name() {
    return "b"
}
Get methods conceptually belong to the scope of a specific contract. Typically, in a multi-contract project, each contract file contains only
  • its entrypoints,
  • probably, a union with allowed messages,
  • and little else; the remaining codebase is shared.
For instance, struct SomeMessage, outgoing for contract A, is incoming for contract B. When deploying one contract from another, a storage struct must be known. And similar. As a guideline, group messages / errors / utils / etc. in shared files, and use only minimal declarations inside each contract.tolk.