npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@openzeppelin/hardhat-tron

v0.1.0

Published

Hardhat plugin for compiling and deploying Solidity contracts to TRON TVM.

Readme

@openzeppelin/hardhat-tron

Hardhat plugin for compiling and deploying Solidity contracts to the TRON Virtual Machine.

npm version License: MIT

Write Solidity for TRON the same way you would for any EVM chain. The plugin compiles your contracts with tron-solc (a TVM-targeted fork of solc), runs them against a local TRON node (java-tron, TRE) that it spins up for you in Docker, and exposes the familiar hre.ethers.* surface so unmodified ethers tests work against the TVM.

The public API is documented under src/types.d.ts. Releases follow SemVer; see CHANGELOG.md for what's in each version.

Prerequisites

Before installing, make sure you have:

  1. Node.js 20 or newernode --version
  2. Docker running locally — the plugin spawns a TRON node in a container
  3. A Hardhat projectnpx hardhat init in an empty folder if you don't have one yet

Quick Start

1. Install

npm install --save-dev @openzeppelin/hardhat-tron \
  hardhat \
  @nomicfoundation/hardhat-ethers \
  @nomicfoundation/hardhat-chai-matchers \
  ethers

Peer deps: hardhat ^2.26, @nomicfoundation/hardhat-ethers ^3, @nomicfoundation/hardhat-chai-matchers ^2, ethers ^6.14.

2. Configure Hardhat

Open (or create) hardhat.config.cjs:

require('@nomicfoundation/hardhat-ethers');
require('@nomicfoundation/hardhat-chai-matchers');
require('@openzeppelin/hardhat-tron');

module.exports = {
  solidity: {
    version: '0.8.26',
    settings: {
      optimizer: { enabled: true, runs: 200 },
      evmVersion: 'cancun',
      viaIR: true,
      // Embed source as literal text in metadata so verification
      // services (Sourcify, etc.) can reconstruct it deterministically.
      metadata: { bytecodeHash: 'ipfs', useLiteralContent: true },
    },
  },
  tre: {
    autoStart: true,
    image: 'tronbox/tre:dev',
    compiler: { target: 'tron' },
  },
  defaultNetwork: 'tre',
  networks: {
    tre: {
      url: process.env.TRE_URL || 'http://127.0.0.1:9090/jsonrpc',
      tron: true,
      accounts: [process.env.TRE_PRIVATE_KEY || '0xdd23ca549a97cb330b011aebb674730df8b14acaee42d211ab45692699ab8ba5'],
    },
  },
};

The default key shown above is a well-known TRE dev key — fine for local tests, never use it on a real network.

3. Compile

npx hardhat compile

The first run fetches tron-solc (a few MB), verifies its SHA-256 against the canonical tronprotocol/solc-bin manifest, and writes artifacts to artifacts/. Subsequent runs use the cache.

4. Run your tests

npx hardhat test

The plugin pulls tronbox/tre:dev on first use, starts a TRE container, waits for the node to be ready, runs your Mocha tests, then stops and removes the container.

Writing a test

Your existing ethers-based tests work as-is:

// test/Counter.test.js
const { expect } = require('chai');
const { ethers } = require('hardhat');

describe('Counter', () => {
  it('increments', async () => {
    const counter = await ethers.deployContract('Counter');
    await counter.increment();
    expect(await counter.value()).to.equal(1n);
  });
});

For TRON-specific operations (time-warp, balance manipulation, raw TronWeb access), the plugin exposes hre.tre:

const { tronWeb, address } = hre.tre.makeTronWeb();
const counter = await hre.tre.deployContract('Counter');
await hre.tre.mine(tronWeb); // tre_mine cheatcode
await hre.tre.setBlockTime(tronWeb, 0); // instamine
await hre.tre.setAccountBalance(tronWeb, addr, 10n ** 18n);

Troubleshooting

"Cannot connect to the Docker daemon" — Docker isn't running. Start it and try again.

Tests hang on startup — first-time image pull can take a minute or two. Run docker pull tronbox/tre:dev manually to see progress.

Port 9090 already in use — set tre.port to a free port in your config (and update the network url to match).

Want to manage the container yourself? Set tre.autoStart: false and start TRE manually with docker run --rm -p 9090:9090 tronbox/tre:dev.


Compile

Loading the plugin redirects Hardhat's Solidity compile pipeline at tron-solc — a TVM-targeted fork of solc published on tronprotocol/solc-bin. No code changes are required in the consumer; just add the plugin to hardhat.config.cjs:

require('@openzeppelin/hardhat-tron');

module.exports = {
  solidity: {
    version: '0.8.26',
    settings: {
      /* … */
    },
  },
  tre: {
    compiler: {
      // 'tron' (default): always compile with tron-solc.
      // 'tron-when-network-tron': only on tron-typed networks.
      target: 'tron',
      // Optional glob filters. Empty arrays = no restriction.
      include: ['contracts/**/*.sol'],
      exclude: ['contracts/mocks/**'],
      // Route artifacts to artifacts-tron/ instead of artifacts/.
      separateArtifacts: false,
    },
  },
};

The compiler wasm is fetched on demand from the canonical mirror (https://raw.githubusercontent.com/tronprotocol/solc-bin/main), SHA-256-verified against the manifest, and cached under Hardhat's standard compilers-v2/ directory. Override the mirror via tre.compiler.mirror.

Batched compiles

[email protected] hits a wasm linear-memory ceiling on large source trees (empirically between ~90 and ~230 files, depending on optimizer settings and constructor complexity). For projects above that ceiling, the plugin exposes a tron:compile-batches task that compiles in passes, accumulating artifacts and reusing the compile cache so shared imports compile only once:

npx hardhat tron:compile-batches

Each batch is declared either inline or via a separate file:

tre: {
  compiler: {
    batches: [
      { name: '01-utils',  dirs: ['contracts/utils'] },
      { name: '02-access', dirs: ['contracts/access'] },
      { name: '03-token',  dirs: ['contracts/token'], extraLeaves: ['SafeERC20'] },
    ],
    // Or, equivalently:
    batchesPath: './tron-batches.config.cjs',
  },
}

Each batch entry supports:

| Field | Type | Effect | | ------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | name | string (required) | Display label and progress key | | include | string[] (optional) | Raw globs, used as-is — skips the dirs/extraLeaves expansion | | dirs | string[] (optional) | Expanded to ${dir}/**/*.sol. Each contracts/<x> automatically pairs with contracts/exposed/<x> for hardhat-exposed consumers | | extraLeaves | string[] (optional) | Bare basenames, expanded to **/<name>.sol for scattered top-level files |

The task wipes paths.artifacts and paths.cache once at the start so the run is reproducible, then mutates tre.compiler.include between passes — the source-resolver subtask re-reads this on every invocation, so each compile sees only the current batch's source set.

Auto-up: TRE lifecycle on hardhat test

When a Hardhat network is declared tron: true and tre.autoStart is left at its default (true), npx hardhat test spawns a TRE container before tests run and tears it down on exit:

module.exports = {
  networks: {
    tre: {
      url: 'http://127.0.0.1:9090/jsonrpc',
      tron: true,
      accounts: [process.env.TRE_PRIVATE_KEY],
    },
  },
  tre: {
    image: 'tronbox/tre:dev', // default
    port: 9090, // default; host side, container exposes 9090
    jarPath: './tre/FullNode.jar', // optional patched-jar bind mount
    autoStart: true, // default — master switch
    autoStartOnTest: true, // default — per-task gate
    keepRunning: false, // default — set true to skip teardown
    readinessTimeoutMs: 60_000, // default — wait-for-ready budget
  },
};

The lifecycle wrapper skips spawning when:

  • tre.autoStart is false,
  • the per-task gate is false (autoStartOnTest / autoStartOnCompile / autoStartOnNode),
  • the selected network does not have tron: true, or
  • something is already responding on the network's URL (manual docker-compose up -d, a teammate's existing container, or a previous run left up by keepRunning).

Teardown is skipped if keepRunning is true OR if a pre-existing container was reused — the plugin never tears down a container it didn't spawn.

Note that TRE containers are single-boot: the image rewrites its own config on every start, so a stopped container can never be restarted successfully. If a configured containerName collides with a stopped leftover (e.g. from a prior keepRunning: true run whose node has since exited), the plugin removes it and runs a fresh container instead. A running container on that name is never removed — if it exists but doesn't answer the readiness probe (say, another process's node still booting), the plugin's docker run fails loudly with a name conflict rather than killing it.

Per-task gates

The three Hardhat tasks the plugin can auto-spawn for behave slightly differently:

| Task | Gate | Default | Teardown | | ----------------- | -------------------- | ------- | -------------------------------------------------- | | hardhat test | autoStartOnTest | true | Yes (unless keepRunning) | | hardhat compile | autoStartOnCompile | false | Yes (unless keepRunning) | | hardhat node | autoStartOnNode | true | Nonode is long-running; user owns cleanup |

hardhat compile is opt-in because tron-solc compiles wasm-locally and the compile pipeline itself doesn't need a running container; spinning a 1–2 GB Java container for a 30-second compile is rarely the right trade. Flip the gate on if your task graph initialises the network connection at compile time (some plugins probe chainId, perform network-typed config validation, etc.).

hardhat node never auto-tears-down — the container has to outlive the node process for the dev RPC to be useful. Clean up with docker rm -f <container-name>; the wrapper logs the name on spawn so it's one copy-paste away.

Notes

The plugin installs a global HTTP keep-alive agent at module load. This eliminates the per-request TCP handshake overhead on the thousands of /wallet/* round-trips a typical test suite makes against the local java-tron container. See src/runtime/http-agent.js for the rationale and tuning notes.

Patched FullNode.jar

The cheatcodes that mutate VM state — setAccountBalance, setAccountCode, setAccountStorageAt, unlockAccounts, snapshot, revert, plus instant time-warps via setNextBlockTimestamp — call tre_* JSON-RPC methods that stock java-tron does not implement. They live on a custom /tre endpoint served by a patched FullNode.jar built from a minor java-tron fork. Each call returns { supported, ... }, so tests degrade cleanly when running against a stock node.

Building the patched jar

The Java sources live under docker/src/ and the build pipeline is a self-contained bash script:

bash docker/build-jar.sh

What it does:

  1. Spins a temporary tronbox/tre:dev container.
  2. Uses its bundled OpenJDK 8 toolchain to compile docker/src/**/*.java against the image's unpatched FullNode.jar (used as the classpath).
  3. Repacks the upstream jar with the patched .class files overlaid (a small Python ZIP step handles top-level classes plus their nested inner classes).
  4. Pulls the resulting jar back to the host as tre/FullNode.jar. The temp container is torn down on exit.

Output is gitignored — it's reproducible from docker/src/ and a 100+ MB jar isn't worth committing.

Files touched by the patch

| File | Effect | | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | org/tron/core/services/jsonrpc/tre/TreJsonRpc.java | New JSON-RPC interface declaring the tre_* methods | | org/tron/core/services/jsonrpc/tre/TreJsonRpcImpl.java | Implementations — direct AccountStore / CodeStore / ContractStore writes, snapshot/revert via LinkedHashMap, version probe returns v1.0.4-oz-tron | | org/tron/core/services/jsonrpc/tre/TreImpersonationRegistry.java | Per-process whitelist of base58 addresses that bypass ECRecover in validateSignature | | org/tron/core/capsule/TransactionCapsule.java | Patched: inline check against TreImpersonationRegistry in validateSignature so txs with a whitelisted owner_address skip ECRecover + permission/weight | | org/tron/consensus/dpos/DposSlot.java | Patched: one-shot timestamp override hooks so tre_setNextBlockTimestamp can fast-forward chain time without 1:1 wall-clock waits |

Wiring the jar into Hardhat

Once built, point the lifecycle wrapper at it with tre.jarPath:

tre: {
  jarPath: './tre/FullNode.jar',
},

The lifecycle wrapper bind-mounts the file over the image's stock jar (/tron/FullNode/FullNode.jar:ro) so the container starts already-patched. The runtime probes (src/runtime/time.js, snapshot stack in src/runtime/ethers-bridge.js) detect the patched jar via tre_version returning the -oz-tron suffix.

Signer rebalancing between test files

Tests draw from a pool of 10 deterministically-derived signers (Hardhat's well-known test test test … junk mnemonic, HD path m/44'/60'/0'/0/i). The deployer is index 0; the remaining nine are funded at first use via tre_setAccountBalance — a direct AccountStore write that sidesteps the witness budget capping the docker-compose pre-funding.

Long test runs spend down those balances. The @openzeppelin/hardhat-tron/signers subpath exposes refundSigners(hre), which resets every cached signer back to its initial balance. Hooking it into a Mocha afterEach in test helpers keeps later files from inheriting depleted state from earlier ones. The call is idempotent (tre_setAccountBalance is a direct write, not a transfer) and parallelized — typical cost is one ~15 ms round-trip per fixture.

License

MIT