Skip to content
tron smart contracts, in one static binary

Ship TRON contracts
without the JavaScript.

Compile, test, deploy, and estimate — from a single Go binary.

SunSail replaces the JavaScript toolchain with a Foundry-inspired workflow: incremental compilation, Solidity-native tests on a real TVM interpreter, deployment scripts that build the actual signed transactions, and energy figures validated ±0 against live TRON.

Install with Go 1.26+: go install github.com/sailor-labs/sunsail/cmd/sun@latest

voyage — sun test
$ sun test
VoyageTest
  [PASS] testAddWaypoints (energy: 2250)
  [PASS] testSail (energy: 35034)
  [PASS] testSailToSamePositionReverts (energy: 2796)
  [PASS] testSailWithContext (energy: 31261)

4 passed, 0 failed

Why SunSail

static binary

One binary, zero runtime deps

CGO_ENABLED=0, statically linked, cross-compiled for linux/darwin/windows. No Node, no npm — just sun.

no javascript

A real TVM, not an EVM guess

The built-in interpreter implements TRON's divergences: the two-layer energy model, SSTORE pricing, exact precompiles, 0x41 addresses, TIP-6780.

±0 verified

Exact metering, proven on-chain

Planned Energy / Bandwidth / TRX-burn equal the solidified receipts exactly — no percentage tolerances, validated against java-tron on Nile.

The walkthrough, command by command

Every block below is real output from the sun binary, driving a tiny Voyage contract from an empty directory to a live contract on the Nile test network.

sun init — scaffold a Foundry-like project from embedded templates

$ sun init demo
Initialized SunSail project in demo
  .gitignore
  README.md
  lib/
  lib/.gitkeep
  out/
  sail.toml
  script/
  script/Deploy.s.sol
  src/
  src/Counter.sol
  test/
  test/Counter.t.sol

sun compile — incremental, deterministic; the pinned solc is downloaded and SHA-256-verified on first use

$ sun compile
downloading solc 0.8.23 (darwin/amd64)…
verified solc 0.8.23 (sha256)
Compiled 3 unit(s), 0 cached
  script/Deploy.s.sol
  src/Counter.sol
  test/Counter.t.sol

$ sun compile          # second run: nothing changed
Compiled 0 unit(s), 3 cached

sun test -vvv — Solidity-native tests on the local TVM, with cheatcodes, decoded events, and source-mapped traces

$ sun test -vvv
VoyageTest
  [PASS] testSail (energy: 35034)
        emit THbhyMMwMrp2zQe3csKqbj5yyRhnRPJizm topic0=0x74b1fee7…e07f data=0x…0005
        [ROOT] VoyageTest (energy: 35034)
          [STATICCALL] Voyage (energy: 392)
          [CALL] Voyage (energy: 27857)
  [PASS] testSailToSamePositionReverts (energy: 2796)
        [ROOT] VoyageTest (energy: 2796)
          [CALL] Voyage (energy: 811) ← revert: already there [src/Voyage.sol:25]

4 passed, 0 failed

The revert resolves to src/Voyage.sol:25 — the exact source line, not a bytecode offset.

sun estimate — exact Energy / Bandwidth / TRX-burn for a call to an already-deployed contract (positional syntax)

$ sun estimate TMts7FK5h5US99x16qCRkJUzd1VYtahd7o "sail(uint256)" 9 --network nile
Energy          19286   (estimate-energy)
Bandwidth         313
TRX burn         2.241600 TRX   (2241600 sun; energy 100 sun/u, bandwidth 1000 sun/B)

Prices (energy 100 sun/u, bandwidth 1000 sun/B) are read from the chain, never hardcoded.

sun script — build the REAL transaction sequence; each address derives from its transaction id

Without --broadcast, sun script prints the plan and touches nothing:

$ sun script script/DeployVoyage.s.sol --network nile
owner TESXcRcFMU2LwroehawwC2B3HgMYe3XSZ2
forking nile at solidified block 69344261
plan: DeployVoyage on nile (fork block 69344261, ref block 69344280, 1 txs)
   1  deploy →   TTh5G9khEZAyybTF3YHwCARtNJCCDQNSUD  energy 427793  bandwidth 3295  burn 46.074300 TRX
      txid 9ab38eec4865097a90408ee38474625432800c1c5d706d2dd0b93ee7688d857c
totals: energy 427793, bandwidth 3295, burn 46.074300 TRX

Plan cost == on-chain receipt, exactly

Broadcasting the same script to Nile deployed TMts7FK5h5US99x16qCRkJUzd1VYtahd7o (tx 960ce7c2…84db5d), and the solidified deploy receipt reported energy 427793 — the same number the plan predicted. A follow-up sail(5) interaction (tx 0b41a3dc…c805e41) metered 27857, matching its test-trace energy. Because addresses derive from the transaction id, the dry-run address (TTh5G9kh…) differs from the broadcast one — each build uses a fresh reference block, so a different txid yields a different address.

sun audit — Aderyn static analysis with a TVM-aware post-filter

$ sun audit
LOW  · src/Voyage.sol:2 · unspecific-solidity-pragma · Unspecific Solidity Pragma
LOW  · src/Voyage.sol:24 · unused-public-function · Public Function Not Used Internally
LOW  · src/Voyage.sol:33 · unused-public-function · Public Function Not Used Internally

0 high, 3 low (1 suppressed by the TVM post-filter — --show-suppressed to list)

One EVM-only finding is suppressed (never silently — --show-suppressed lists it); CI decides the gate with --fail-on high.

sun sonar — offline utilities: address conversion, selectors, CREATE2

$ sun sonar address TMts7FK5h5US99x16qCRkJUzd1VYtahd7o
base58 TMts7FK5h5US99x16qCRkJUzd1VYtahd7o
hex    4182cf3d4db8ede38e0ae4d674cba9965b3011b653
evm    0x82cf3d4dB8eDe38e0Ae4D674cBa9965B3011B653

$ sun sonar selector "sail(uint256)"
0x4a225440

The evm line is the EIP-55 checksummed form — the one solc accepts in a Solidity address literal.

The full cycle, end to end

sun init dapp && cd dapp
sun compile
sun test -vvv
sun script script/Deploy.s.sol --network nile --broadcast   # deploy first
sun estimate <deployed-address> "sail(uint256)" 9 --network nile
sun audit --fail-on high

From an empty directory to a live, metered contract on TRON — with one binary and no JavaScript.

Build on TRON, the fast way.