Skip to content

CONFIG.md — sail.toml Schema & Project Layout

Living reference for the project configuration file and the tree produced by sun init. Tier-1 authority remains docs/ARCHITECTURE.md; this file documents the concrete schema implemented in internal/config and the templates in assets/templates/.


sail.toml

sail.toml is decoded with strict validation (github.com/pelletier/go-toml/v2, DisallowUnknownFields). Any key not in the schema is an error, reported with its line number — e.g. sail.toml:8: unknown key "networks.nile.typo". Type mismatches are reported the same way (sail.toml:3: cannot decode …). Typos never pass silently.

TOML structure note: top-level keys (remappings) must appear before any [table] header. Once a table header opens, subsequent bare keys belong to that table.

Sections

Key Type Required Description
remappings []string no Foundry-style import remappings, forwarded to solc's settings.remappings.
[solc] version string yes The pinned solc-tron release, e.g. "0.8.23".
[solc.checksums] table<str,str> no¹ SHA-256 hex digest of the solc-tron binary per platform, keyed by "<goos>/<goarch>".
[aderyn] version string no³ The pinned Cyfrin aderyn release run by sun audit, e.g. "0.6.8".
[aderyn.checksums] table<str,str> no³ SHA-256 hex digest of the extracted aderyn binary per platform, keyed by "<goos>/<goarch>".
[networks.<name>] fullnode string yes² host:port of the FullNode (protocol.Wallet) gRPC service — broadcast + unsolidified reads.
[networks.<name>] solidity string yes² host:port of the Solidity (protocol.WalletSolidity) gRPC service — solidified confirmation.
[profiles.<name>] solc_version string no Overrides solc.version when the profile is active (applied in a later sprint).
[profiles.<name>] remappings []string no Overrides the top-level remappings when the profile is active (applied in a later sprint).

¹ Checksums — sun compile downloads the pinned solc-tron release asset and requires and verifies the host platform's digest before ever executing the binary (ARCHITECTURE §1.5); a platform without a pinned digest cannot compile. The scaffolded template pins the official tv_0.8.23 digests (the darwin asset is a universal x86_64+arm64 binary, so both darwin keys share one digest; no linux/arm64 asset is published upstream). See docs/COMPILER.md for the full binary-management contract.

³ Aderyn — optional; only sun audit consumes it, under the same rule as solc: no digest for the host platform, no execution (§1.5). The release ships tar.xz archives, so the pinned digest is that of the extracted binary. Upstream publishes no Windows asset — sun audit reports that explicitly on Windows. See docs/AUDIT.md.

² Networks — if a [networks.<name>] table is declared, it must carry both fullnode and solidity. SunSail opens one ClientConn per (network, service) and never multiplexes the two services onto a single connection (ARCHITECTURE §5.7): broadcast always goes to the FullNode Wallet service; solidified confirmation to the WalletSolidity service. The <name> is free-form (mainnet, nile, or a custom network); the scaffolded default declares mainnet and nile. The optional jsonrpc key is the node's JSON-RPC HTTP endpoint, consumed ONLY by the fork storage reader (sun test --fork-url) — java-tron exposes no contract-storage read over gRPC (ARCHITECTURE Erratum E2, docs/FORKING.md); everything else stays gRPC-only.

Default endpoints (scaffolded)

Network FullNode (fullnode) Solidity (solidity) JSON-RPC (jsonrpc)
mainnet grpc.trongrid.io:50051 grpc.trongrid.io:50052 https://api.trongrid.io/jsonrpc
nile grpc.nile.trongrid.io:50051 grpc.nile.trongrid.io:50061 https://nile.trongrid.io/jsonrpc

Example

remappings = ["@openzeppelin/=lib/openzeppelin-contracts/"]

[solc]
version = "0.8.23"

[solc.checksums]
"linux/amd64" = "<sha256-hex>"

[networks.mainnet]
fullnode = "grpc.trongrid.io:50051"
solidity = "grpc.trongrid.io:50052"

[networks.nile]
fullnode = "grpc.nile.trongrid.io:50051"
solidity = "grpc.nile.trongrid.io:50061"

Project layout (sun init)

sun init [name] materializes a Foundry-like project from the binary's embedded templates (no satellite files). With no name it initializes the current directory; with a name it creates that subdirectory. A non-empty destination is refused unless --force is given; under --force the operation is idempotent (template files are overwritten, yielding the same tree).

<project>/
├── .gitignore            # ignores /out/ (build output)
├── sail.toml             # project config (two networks, pinned solc)
├── lib/                  # third-party sources (git submodules / vendored)
│   └── .gitkeep          # keeps the empty dir under version control
├── out/                  # build output — created here, gitignored, filled by `sun compile`
├── src/
│   └── Counter.sol       # example contract
├── test/
│   └── Counter.t.sol     # example test (plain require; cheatcodes arrive in a later sprint)
└── script/
    └── Deploy.s.sol      # example deployment script
  • out/ is the build-output directory. It is created at init so the layout is complete, and the scaffolded .gitignore ignores it — build artifacts are never committed. sun compile (Sprint 2) populates it.
  • lib/ carries a .gitkeep so the (otherwise empty) directory survives git add in the user's project. embed.FS cannot carry empty directories, so the scaffolder creates out/ explicitly while lib/.gitkeep rides along in the embed.
  • The example .sol files are static assets (no name interpolation), which keeps the scaffolded tree deterministic and byte-identical to the embedded templates — the property the e2e golden test asserts. They compile with plain solc in Sprint 2.