Skip to main content

Porting from other EVM chains

Solidity contracts can run on Joltzu, but a migration needs more than a new RPC URL. Review how your application treats the gas asset, ERC-20 balances, account authority, block time and external infrastructure.

This guide describes the current reference-node profile. Start with a local devnet; public testnet/mainnet endpoints and launch parameters remain proposed.

1. Understand native JOLT and its ERC-20 interface​

Native JOLT and the configured JOLT ERC-20 adapter expose one balance. Both use 18 decimals. Sending through either interface reduces the same account cash. There is no deposit, wrapping step or second balance behind the adapter.

Arc explains a similar model for USDC: native USDC pays gas and travels through msg.value, while its ERC-20 interface accesses the same funds. Arc uses 18 decimals for native value and 6 for ERC-20 amounts, truncating the lower 12 native decimal places when reading the token balance. Arc's stablecoin-native model describes that distinction.

Network / asset modelNative interfaceToken interfaceBalance relationship
Ethereum ETH / WETHETH, 18 decimalsWETH, 18 decimalsSeparate holdings; wrapping exchanges one for the other
Arc USDCUSDC, 18 decimalsUSDC, 6 decimalsOne balance, different precision
Joltzu JOLTJOLT, 18 decimalsConfigured JOLT adapter, 18 decimalsOne balance, identical units

Do not bring Arc's 10^12 conversion into JOLT accounting. For JOLT, address.balance, eth_getBalance, native asset 0 and the configured token's balanceOf describe the same raw amount at the same state. Other assets can have different decimals; read their metadata individually.

Solidity's ether suffix means the numeric multiplier 10^18. It does not select the ETH asset. On Joltzu, msg.value == 1 ether means one JOLT. Use integer base units internally and show JOLT in wallet, checkout and fee displays.

Native services and gas settlement also use this cash record.

If an account holds 2 JOLT, transferring 0.25 JOLT through the token interface leaves 1.75 JOLT through both interfaces, before gas fees. Adding the two displayed balances would incorrectly report 3.5 JOLT. A contract that stores a user's native deposit and also counts its own JOLT token balance can similarly issue duplicate claims on the same cash.

Check the configured adapter​

The JOLT token address comes from the network's genesis configuration. An adapter need not be configured on every devnet. 0x1002 is a privileged adapter backend, not the JOLT ERC-20 token address. There is no confirmed public adapter address to copy from this guide.

With Foundry installed and the actual adapter address set in JOLTZU_JOLT_TOKEN, compare reads at one committed height:

export ETH_RPC_URL=http://127.0.0.1:30400
export JOLTZU_OWNER=0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
: "${JOLTZU_JOLT_TOKEN:?Set the JOLT adapter address from this network's genesis}"
JOLTZU_READ_HEIGHT=$(cast block-number)

cast balance "$JOLTZU_OWNER" --block "$JOLTZU_READ_HEIGHT"
cast call "$JOLTZU_JOLT_TOKEN" 'decimals()(uint8)' \
--block "$JOLTZU_READ_HEIGHT"
cast call "$JOLTZU_JOLT_TOKEN" 'balanceOf(address)(uint256)' \
"$JOLTZU_OWNER" --block "$JOLTZU_READ_HEIGHT"

Expect 18 decimals and equal raw balance values for an admitted reference JOLT adapter. An independently deployed token called “JOLT” does not establish that relationship. See JOLT and fees and asset policy.

2. Adapt payments, allowances and routers​

Arc's porting checklist calls attention to wrappers, allowances, restricted transfers and native/token routing. Review those paths on Joltzu using its own rules:

  • Replace WETH assumptions. The JOLT adapter has no deposit() or withdraw() wrapping API. Rewrite wrap/unwrap calls and native refund branches. Token-only router paths can use the configured adapter; native-value paths must explicitly handle JOLT already held by the contract.
  • Keep the transfer mechanism explicit. A native CALL with value can execute the recipient's receive or fallback code. A JOLT ERC-20 transfer does not invoke those callbacks. Changing from one path to the other can break deposit recognition, refund handling or reentrancy assumptions.
  • Separate routing identity from asset identity. A frontend's native-asset sentinel and the adapter address select different call mechanisms, even though they spend the same funds. Keep them distinct in routing tables and normalize them to one underlying asset for balances and pool validation.
  • Apply allowances to the right path. transferFrom needs the token owner's approval. Spending a contract's own native JOLT does not consume that ERC-20 allowance. Restrict payout and rescue methods with application authorization; token approvals alone cannot protect the contract's native cash.
  • Read allowance behavior from the actual token. The reference genesis JOLT adapter decrements every delegated allowance, including uint256.max. Do not assume maximum approval is inexhaustible. Native AMM LP tokens have a different maximum-allowance convention.
  • Leave room for gas. A token payment and gas draw from the same JOLT balance. The payer's maximum fee is reserved before execution; that cash is unavailable to the payment until unused gas is refunded.

Transfer failures and available cash​

A sufficient total balance does not guarantee a successful transfer. Native and adapter movements share admission, send/receive eligibility, expiry, hard locks and cumulative debit controls. If enabled by genesis, fund energy also limits newly received JOLT. Development defaults permit ordinary JOLT sending/receiving; explicit policy can replace those defaults. Trading permission is separate and starts disabled.

Check the result of low-level native calls and handle ERC-20 failures. Retain application authorization and reentrancy guards. Do not turn a failed refund into a silently completed withdrawal. A reverted execution rolls back its transfers, native service changes and logs, while the outer transaction can still consume gas and its nonce.

Ordinary send/receive restrictions do not waive protocol fees. Hard-locked cash and configured fund energy still constrain maximum-fee escrow; fee charging and refunds have their own settlement rules.

Cash, order reservations and lending claims are distinct. Resting spot bids are reconciled when other JOLT spending consumes their backing; a supply position is not additional spendable cash. See native services before replacing escrow balances with native positions.

AMMs and other tokens​

The native AMM provides a Uniswap V2-compatible factory/pair interface for eligible, listed native assets. Use its actual factory, current adapter addresses and pair initialization-code hash. Pair creation is controlled by the configured administrator. There is no bundled native router; an adapted token-to-token router still needs testing.

Do not create a JOLT/native-JOLT pair or count adapter/native balances as two reserves: they are one asset. An arbitrary ERC-20 contract can maintain its own EVM storage, but deploying it does not automatically register it with the native ledger or make it eligible for native pools. Adapter changes also require refreshing addresses and, for versioned dynamic assets, delegated approvals.

3. Match the EVM, transactions and native SDK​

Use the deployment guide's solc 0.8.37, Osaka, optimizer-runs 200 settings as a reproducible starting point. Review dependencies that assume a newer fork, a specific Ethereum system contract or a predeployed router. If runtime-code eligibility is enabled, factories and proxy implementations must satisfy it too.

Assumption to reviewCurrent Joltzu behavior / migration action
All Osaka transaction types are acceptedReplay-protected legacy, EIP-2930 type 0x1 and EIP-1559 type 0x2 are accepted. Blob transactions and EIP-7702 authorizations are not.
An Ethereum-style base-fee market determines feesBASEFEE is zero. Type-2 effective price is min(maxFeePerGas, maxPriorityFeePerGas) and must be nonzero; the priority cap cannot exceed the fee cap. Obtain current suggestions from RPC rather than importing another chain's floor.
A successful simulation authorizes the transactioneth_call and gas estimation do not establish signed account-policy approval. Admission and intervening state can still reject execution.
Any gas limit up to the block limit is admittedThe pool refuses transactions whose gas limit exceeds 1,000,000, although eth_call and eth_estimateGas accept up to 16,777,216. Split large deployments and batches.
Every indexed transaction is an Ethereum envelopeNative JZTX transactions appear as type 0x4a. Explorer and indexing decoders must handle them.
Standard Ethereum proof and tracing methods are availableeth_getProof, eth_createAccessList, debug_*, trace_*, txpool_* and state overrides are unavailable. Use supported logs and native proofs where appropriate.

Native policy also applies to ordinary Ethereum-signed transactions. A key may no longer independently authorize an account after its owner changes the policy. Threshold account plans use separate authority nonces and approvals; the relayer and fee payer can differ from the account executing the calls. Do not use tx.origin to identify the authorized user. Do not assume an ERC-4337 bundler, EntryPoint or paymaster is deployed just because contracts can execute EVM code. See account policies and transaction submission.

Import native services from the SDK​

Install and remap @joltzu/solidity-sdk using the SDK instructions. Pin a package/node revision; a public registry release is still TBD. Import typed libraries rather than copying handwritten precompile ABIs.

Native precompiles have no EVM bytecode, so address.code.length cannot establish their availability. Some mutation calls to empty addresses can appear successful on another EVM chain. Check the chain ID, genesis identity and service configuration before using them.

This reader uses the SDK's native-JOLT constant and spot read helper, with an explicit chain-ID guard:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.37;

import {Precompiles} from "@joltzu/solidity-sdk/contracts/Precompiles.sol";
import {Spot} from "@joltzu/solidity-sdk/contracts/Spot.sol";

contract JoltzuBalanceReader {
uint256 public immutable expectedChainId;
error WrongChain();

constructor(uint256 chainId) {
if (block.chainid != chainId) revert WrongChain();
expectedChainId = chainId;
}

function balances(address owner)
external view returns (uint256 evmBalance, uint256 nativeBalance)
{
if (block.chainid != expectedChainId) revert WrongChain();
return (owner.balance, Spot.balanceOf(Precompiles.NATIVE_JOLT, owner));
}
}

For the default local devnet, deploy with constructor argument 31337. The two returned balances should match. A chain-ID guard cannot distinguish two different genesis configurations that reuse the same ID; validate the RPC network identity as well.

4. Review time, finality and opcode assumptions​

The RPC exposes committed state. latest, safe, finalized and pending block tags resolve to the committed head; the pending nonce additionally includes consecutive pool transactions. A submission hash means pool admission, not inclusion. Wait for a committed receipt and check status. Under the consensus fault assumptions, committed blocks do not reorganize; a remote RPC response still needs the trust or verification your application requires.

The reference clock uses signed 500 ms slots relative to an immutable genesis time. Block timestamps remain whole Unix seconds. Slots can be skipped, consecutive blocks can share a timestamp, and the default devnet suppresses empty blocks. An idle chain's height and exposed block timestamp can therefore stay unchanged.

Use block number and log ordinal for ordering. Do not estimate elapsed time by multiplying block count by 500 ms, or require strictly increasing timestamps. Test auctions, interest accrual, lock expiry and keepers through idle periods: time-based execution is evaluated against the timestamp of a newly committed block. A slot duration does not promise transaction latency or a public production SLA.

EVM / block featureWhat to check
BLOCKHASHReturns persisted native Joltzu block identifiers for the preceding 256 blocks. Ethereum RPC block hashes are a different namespace; the native identifier is carried in the Ethereum header's extraData. Do not compare them as identical hashes.
PREVRANDAOValidator blocks use the development randomness beacon; a standalone local execution runner can expose zero. The production randomness/DKG design remains incomplete. Use a verified, configured VRF/oracle for security-sensitive randomness; a JSN VRF guide does not establish a live deployment.
SELFDESTRUCTThe Osaka baseline retains old contracts' code/storage. Newly created destroyed accounts clear code/storage/nonce at outer commit but preserve any remaining balance, including later funding. Self-beneficiary destruction preserves JOLT. Debit-policy denial can revert the frame. Test beneficiary and refund paths on Joltzu.
Zero-address value transfersNative transfers follow the ordinary account model and do not burn JOLT supply. The reference ERC-20 adapter rejects a zero owner/recipient/spender. Do not treat either path as an interchangeable burn mechanism.

These are Joltzu rules. Arc's EVM differences, including zero-address restrictions, always-zero PREVRANDAO and a minimum gas price, must not be copied into this integration.

5. Index movements once​

Arc documents two USDC event streams, separated by emitter and decimals. Joltzu also exposes native and adapter streams, but both JOLT streams use 18 decimals.

StreamEmitterCoverage
Native JOLT0xfffffffffffffffffffffffffffffffffffffffeExplicit native movements, including internal calls, contract creation, native service settlement and adapter transfers
ERC-20 JOLTThis network's configured adapter addressToken-interface transfers of that same JOLT

Both use Transfer(address,address,uint256) with indexed sender/recipient. The topic is:

0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef

For complete native-transfer accounting, filter the system emitter. An adapter transfer can also emit its token log; adding both doubles the deposit. Native system logs omit zero-value and self transfers. Fees, fee refunds and genesis allocations are not Transfer logs, and reverted execution leaves no movement logs. Calculate execution fees from receipt gasUsed × effectiveGasPrice, and identify the actual payer when sponsorship is involved.

Transaction value alone misses internal transfers. Transaction from can be a relayer rather than the account that supplied the funds; use the movement log's sender. Deduplicate Ethereum logs by network/genesis identity, Ethereum block hash and logIndex. Persist applied effects before advancing a cursor, backfill after disconnects and split bounded queries. Follow the event indexing guide.

6. Reconfigure infrastructure and migrate state deliberately​

Rebuild your deployment registry for this network: chain ID, RPC, genesis identity, JOLT adapter, factories, oracle feeds, service contracts and any bridge counterparts. Local development uses 31337; proposed public testnet/mainnet profiles use 56580 / 5658. Endpoint existence and contract addresses must be verified before a public deployment.

Recompute EIP-712 domains, verifying contracts, deadlines and nonces. Do not reuse signatures or approvals from the original chain. Predict CREATE2 addresses from the actual deployer and bytecode, then verify deployed code; the original chain's factory need not exist here. Migrating a contract does not move its storage, users' token balances, approvals or liquidity. Plan a separately authorized state/liquidity transition.

The local RPC rejects browser web-page Origins and provides no node-side signing. Use client-side signing with the supported wallet flow, plus an appropriately configured server relay or gateway. Review RPC methods, limits and gateway setup; do not expose a local validator as an unrestricted public RPC.

Joltzu-hosted Envio and The Graph URLs remain TBD. A development asset named USDC does not establish Circle issuance or a working CCTP/bridge route. Verify issuer, asset identity, custody and bridge support independently. Arc's USDC bridge instructions do not establish those services on Joltzu.

7. Run migration tests on a real Joltzu devnet​

Keep ordinary Solidity unit tests, then exercise host behavior against the reference node. A standard Anvil instance does not implement Joltzu's native ledger, policies, services or block clock.

  • Transfer through native value and the configured adapter; compare the same-height balances, callbacks and one-time event accounting.
  • Exercise delegated allowances, native payouts, maximum-fee reservation, sponsorship and insufficient spendable cash. Include explicit policy denial, hard locks and optional fund energy when your target profile enables them.
  • Revert after several EVM/native operations and verify rollback, failed receipt status, retained fee charging and absence of transfer logs.
  • Test wrap/unwrap replacements, router refunds, pool asset identity, adapter addresses and runtime eligibility for factories/proxies.
  • Test SELFDESTRUCT, reentrancy and randomness assumptions on the node rather than relying only on ordinary EVM mocks.
  • Exercise same-second blocks, skipped slots, idle periods, expired actions, pending nonces and reconnect/backfill behavior.
  • Verify network identity, SDK version, deployed addresses, account approvals and infrastructure availability before enabling a public environment.

Continue with deploying Solidity, calling native services and the implementation status.