---
updatedAt: 2026-10-13T07:08:49.000Z
agentTools:
  projectIndex: https://developers.tron.network/llms.txt
---

# Migrating Ethereum contracts to TRON

Step-by-step walkthrough for porting an existing Ethereum (Solidity) contract to TRON: recompile, swap SDKs, switch testnets, and verify.

> 📘 **Prerequisites**
>
> * [TVM vs EVM](https://developers.tron.network/docs/tvm-vs-evm)
> * [Solidity on TRON](https://developers.tron.network/docs/smart-contract-language)

If you have an existing Solidity contract that runs on Ethereum (or any EVM-compatible chain), this page is the practical walkthrough for porting it to TRON. It complements the reference page [TVM vs EVM](https://developers.tron.network/docs/tvm-vs-evm) — go there for the complete list of opcode and precompile differences. This page focuses on the *steps* you take rather than enumerating every difference.

For project-level migration of a Truffle/Hardhat repo (directory layout, network config, test framework), see [Migrating a Truffle project to TronBox](https://developers.tron.network/docs/truffle-to-tronbox-project-migration-guide).

## At a glance: what changes

|                       | Ethereum (EVM)                 | TRON (TVM)                                                                |
| :-------------------- | :----------------------------- | :------------------------------------------------------------------------ |
| Virtual machine       | EVM (Solidity, Vyper)          | TVM (Solidity; largely EVM-compatible — see [TVM vs EVM](https://developers.tron.network/docs/tvm-vs-evm)) |
| Transaction fee model | Gas paid in ETH; market-priced | Bandwidth + Energy; both can be obtained by staking TRX                   |
| Native token          | ETH (18 decimals)              | TRX (6 decimals)                                                          |
| Address format        | Hex (`0x…`, 20 bytes)          | Base58Check (`T…`, 21 bytes including the `0x41` prefix)                  |
| Compiler              | upstream solc                  | [TRON solc](https://github.com/tronprotocol/solidity)                     |
| Block explorer        | Etherscan                      | [TRONSCAN](https://tronscan.org/)                                         |
| JS SDK                | ethers.js / web3.js            | [TronWeb](https://tronweb.network/)                                       |
| Java SDK              | Web3j                          | [Trident](https://github.com/tronprotocol/trident)                        |
| Python SDK            | web3.py                        | [TronPy](https://pypi.org/project/tronpy/)                                |
| Project framework     | Truffle / Hardhat / Foundry    | [TronBox](https://tronbox.io/)                                            |
| Browser IDE           | Remix                          | [TronIDE](https://www.tronide.io/)                                        |

## Step 1 — Recompile with the TRON solc

TRON's Solidity compiler is a fork of upstream solc with TRON-specific extensions (`trcToken`, `transferToken`, the `trx`/`sun` unit keywords). Most Ethereum contracts compile unchanged, but you must use the TRON distribution.

If you use TronBox (recommended), TRON solc is bundled. Set `compilers.solc.version` in `tronbox-config.js` to your contract's pragma version:

```javascript
compilers: {
  solc: {
    version: '0.8.20'
  }
}
```

If you build solc yourself or use TronIDE, take the TRON distribution from [github.com/tronprotocol/solidity](https://github.com/tronprotocol/solidity).

> 🚧 Pin the compiler version
>
> For reproducible builds, pin an exact TRON solc version in `tronbox-config.js` and commit the dependency lockfile. Your own contracts may use an exact `pragma solidity X.Y.Z;`. Third-party packages may use compatible version ranges; make sure the configured compiler satisfies every dependency's pragma, and do not edit installed dependency sources. See [Solidity on TRON](https://developers.tron.network/docs/smart-contract-language).

## Step 2 — Audit your contract for opcode-level differences

A handful of opcodes behave differently on TVM. **Most contracts are not affected** — but if your code touches one of these, you need to fix it before migrating:

| If your contract uses...                 | Action                                                                                                                                                                                                                                                                                                         |
| :--------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `block.difficulty` (`DIFFICULTY` opcode) | Returns `0` on TVM. Replace with a different randomness source or use it only as a tiebreaker.                                                                                                                                                                                                                 |
| `block.gaslimit` (`GASLIMIT`)            | Returns `0` on TVM. Remove the dependency.                                                                                                                                                                                                                                                                     |
| `tx.gasprice` (`GASPRICE`)               | Returns `energyPrice` on TVM (currently 100 sun on Mainnet; verify it through `wallet/getchainparameters`). Adjust any cost-prediction logic.                                                                                                                                                                  |
| `block.basefee` (`BASEFEE`)              | Returns `energyPrice`. EIP-1559 is not implemented; remove fee-bumping logic.                                                                                                                                                                                                                                  |
| `Ripemd160` precompile (`0x03`)          | EVM computes RIPEMD-160. TVM's legacy `0x03` behavior computes SHA-256 over the input, takes the first 20 bytes, and hashes those bytes with SHA-256 again. When chain parameter `#60` (`getAllowTvmCompatibleEvm`) is enabled, EVM-compatible RIPEMD-160 is available through `EthRipemd160` at `0x00020003`. |
| `0x09` precompile                        | EVM is `Blake2F`; TVM is `BatchValidateSign`. Different functionality entirely — your call will fail. Remove or rewrite.                                                                                                                                                                                       |

For the full opcode table, see [TVM vs EVM](https://developers.tron.network/docs/tvm-vs-evm).

## Step 3 — Handle CREATE2 carefully

The TVM uses `0x41` as the `CREATE2` prefix; the EVM uses `0xff`. Handle this difference according to how the address is calculated.

**Case A: you use Solidity's `new {salt: ...}` syntax.** No source change is required. The compiler emits the `CREATE2` opcode, and the TVM applies TRON's `0x41` address-derivation rule when executing it:

```solidity
pragma solidity 0.8.20;

contract Token1 {
    uint256 public value;
    constructor(uint256 _value) {
        value = _value;
    }
}

contract Factory {
    event ContractCreated(address);

    function createContract(bytes32 _salt, uint256 _x) external {
        Token1 newToken = new Token1{salt: _salt}(_x);
        emit ContractCreated(address(newToken));
    }
}
```

**Case B: you use OpenZeppelin's `Create2` library.** Use the official [OpenZeppelin Contracts for TRON](https://github.com/OpenZeppelin/tron-contracts). Its `Create2.computeAddress` implementation already uses TRON's `0x41` prefix, so no custom fork or wrapper is required:

```solidity
import {Create2} from "@openzeppelin/tron-contracts/utils/Create2.sol";

function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer)
    internal pure returns (address)
{
    return Create2.computeAddress(salt, bytecodeHash, deployer);
}
```

Pin a tested package version when installing it, for example `npm install --save-exact @openzeppelin/tron-contracts@5.6.0`. Patch versions are released independently from the Ethereum package, so do not copy the `@openzeppelin/contracts` patch version.

**Case C: you compute the CREATE2 address with custom assembly.** If you retain custom inline assembly for address derivation, change the prefix byte from Ethereum's `0xff` to TRON's `0x41`:

```solidity
function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer)
    internal pure returns (address addr)
{
    assembly {
        let ptr := mload(0x40)
        mstore(add(ptr, 0x40), bytecodeHash)
        mstore(add(ptr, 0x20), salt)
        mstore(ptr, deployer)
        let start := add(ptr, 0x0b)
        mstore8(start, 0x41)  // <-- 0x41 on TRON, 0xff on Ethereum
        addr := keccak256(start, 85)
    }
}
```

Do not use the Ethereum `@openzeppelin/contracts/utils/Create2.sol` implementation to predict a TRON CREATE2 address. Its prediction will not match the address deployed by the TVM.

## Step 4 — Adjust native-token decimal precision

The smallest unit of ETH is `wei` (10⁻¹⁸ ETH); the smallest unit of TRX is `sun` (10⁻⁶ TRX). If your contract does any arithmetic on native-token base units — for example, comparing balances against hard-coded constants — you'll need to rescale.

Code that uses `1 ether` or `1e18` to represent 1 ETH should change to `1 trx` or `1e6` to represent 1 TRX. A token's decimal precision is defined by `decimals` in its contract. It is independent of the `1 TRX = 10^6 sun` conversion and should not automatically be changed to six decimal places.

The TRON Solidity compiler accepts `trx` and `sun` as unit literals — see [Solidity on TRON](https://developers.tron.network/docs/smart-contract-language).

## Step 5 — Swap SDKs in your off-chain code

| In Ethereum code             | Change to                                          |
| :--------------------------- | :------------------------------------------------- |
| `web3.js` / `ethers.js`      | [TronWeb](https://tronweb.network/)                |
| `Web3j` (Java)               | [Trident](https://github.com/tronprotocol/trident) |
| `web3.py`                    | [TronPy](https://pypi.org/project/tronpy/)         |
| Hardhat tests using `ethers` | TronBox tests using TronWeb                        |

The function-call shapes are similar (each SDK has a `contract(abi).at(address)` style), but the network endpoint, address format, and signing flow differ. See [Interacting with contracts](https://developers.tron.network/docs/smart-contract-interaction) for the TronWeb call patterns.

## Step 6 — Switch testnets

Replace your Goerli/Sepolia endpoint with **Shasta** (TRON's primary public testnet):

|         | Endpoint                         | Faucet                                                   |
| :------ | :------------------------------- | :------------------------------------------------------- |
| Shasta  | `https://api.shasta.trongrid.io` | [Shasta faucet](https://www.trongrid.io/shasta/#request) |
| Mainnet | `https://api.trongrid.io`        | (no faucet — buy TRX)                                    |

For TronBox specifically, set the `shasta` network in `tronbox-config.js`:

```javascript
networks: {
  shasta: {
    privateKey: process.env.PRIVATE_KEY,
    userFeePercentage: 50,
    feeLimit: 100 * 1e6,                     // 100 TRX
    fullHost: 'https://api.shasta.trongrid.io',
    network_id: '2'
  }
}
```

See [Quickstart](https://developers.tron.network/docs/quickstart) for a full example.

## Step 7 — Deploy and verify

Deploy with TronBox (`tronbox migrate --network shasta`) or via TronIDE — see [Deploying](https://developers.tron.network/docs/deploying). Confirm the deployment landed on-chain through `wallet/gettransactioninfobyid`.

Once deployed on Mainnet, **verify the source on TRONSCAN** so users can audit the contract. The verification flow on TRONSCAN is similar to Etherscan's — see [Contract verification](https://developers.tron.network/docs/contract-verification).

## Common migration gotchas

* **TRX transfers don't always run `fallback`.** TRX can reach a contract through two distinct protocol-level mechanisms, and they have different semantics for the receiving contract:

  1. **`TransferContract`** (system contract, sent via `wallet/createtransaction`) — bypasses the contract's `fallback` / `receive` function entirely. The TVM is not invoked.
  2. **`TriggerSmartContract` with `callValue`** — runs the contract's payable function or `fallback` like a normal EVM call.

  On Ethereum, any value transfer to a contract triggers its `receive` / `fallback`. If your contract relies on `fallback` running on every incoming transfer (deposit tracking, event emission, internal accounting), audit the call paths: incoming TRX from a plain `TransferContract` will be invisible to your contract code. Patterns that work on Ethereum but break on TRON include deposit detection by `fallback` event, balance-difference-based reentrancy guards in `fallback`, and reflexive ERC-20 contracts that rebase on incoming ETH.
* **Address encoding mismatches.** TRON addresses are 21 bytes (with the `0x41` prefix), not 20 — *at the chain boundary*. Inside the TVM, addresses are 20 bytes, identical to Ethereum (see [TVM vs EVM — Address representation](https://developers.tron.network/docs/tvm-vs-evm#address-representation)). When converting between TRON and Ethereum representations outside the VM (RPC payloads, signed orders bridged between chains, off-chain address derivation), strip or add the prefix byte and Base58Check-encode/decode.
* **The source of the `CHAINID` value differs from the EVM.** EVM networks use predefined Chain IDs, whereas TVM's return rule is controlled by chain parameters. Chain parameter No. 71 (`getAllowOptimizedReturnValueOfChainId`) enables the optimized Chain ID return rule. TRON Mainnet and the Shasta and Nile testnets currently have this parameter enabled, so their Chain IDs are the decimal integers represented by the last 4 bytes of their genesis block IDs (Mainnet is `728126428`) and are numerically equivalent to the hexadecimal values returned by JSON-RPC `eth_chainId`. In TIP-712 and similar schemes that include the Chain ID in the signing domain, a client and contract using different Chain IDs will fail signature verification. If multiple networks reuse the same signing domain without another network-separation condition, the same signature may be accepted across networks, creating a replay risk. Contracts should obtain the current Chain ID from `block.chainid`, while clients and backends should query `eth_chainId`; verify during deployment and startup that all components use the same numeric value. See [TVM vs EVM — `CHAINID` semantics](https://developers.tron.network/docs/tvm-vs-evm#chainid-semantics).
* **`SELFDESTRUCT` does not delete existing contracts.** When the self-destruct-restriction gate is on (the current Mainnet state), `SELFDESTRUCT` on a contract that was not created in the current transaction transfers balance, TRC-10 holdings, and stake-for-self to the obtainer but does **not** clear code or storage — same shape as Ethereum's EIP-6780. The operation additionally reverts when the contract has active Stake 1.0 in lock period, delegated resources (Stake 1.0 or 2.0), or pending Stake 2.0 unfreezes. Do not rely on `SELFDESTRUCT` as an upgrade primitive. See [TVM vs EVM — `SELFDESTRUCT`](https://developers.tron.network/docs/tvm-vs-evm#selfdestruct-restricted-behavior).
* **Energy estimation needs production-shape inputs.** A function that costs 50,000 Energy on a small dataset can cost 500,000 Energy at production scale. Estimate with realistic inputs through `wallet/triggerconstantcontract`.
* **The Dynamic Energy Model affects popular contracts.** If your contract is called frequently, its `energy_factor` rises and per-call Energy cost increases. See [FeeLimit & Energy cost](https://developers.tron.network/docs/set-feelimit).

***

## Related resources

* [TVM vs EVM](https://developers.tron.network/docs/tvm-vs-evm) — full reference for opcode and precompile differences
* [Solidity on TRON](https://developers.tron.network/docs/smart-contract-language) — TRON-specific Solidity language extensions
* [Migrating a Truffle project to TronBox](https://developers.tron.network/docs/truffle-to-tronbox-project-migration-guide) — project-level migration walkthrough
* [Quickstart](https://developers.tron.network/docs/quickstart) — TronBox deploy walkthrough
* [Contract verification](https://developers.tron.network/docs/contract-verification) — verify the migrated contract on TRONSCAN
* [FeeLimit & Energy cost](https://developers.tron.network/docs/set-feelimit) — calibrate `fee_limit` for the new cost profile