Skip to main content
Tempo is fully compatible with the Ethereum Virtual Machine (EVM), targeting the Osaka hardfork specification. Deploy and interact with smart contracts using the same tools, languages, and frameworks as Ethereum.
All standard Ethereum JSON-RPC methods work out of the box. Existing contracts can be deployed to Tempo without modification in most cases.

What Works Identically

Smart Contracts

Solidity, Vyper, Yul compile to identical bytecode. Deploy contracts without modification.

Development Tools

Foundry, Hardhat, Remix work out of the box. No special configuration needed.

Wallets

MetaMask, Rainbow, WalletConnect connect seamlessly. Add Tempo as a custom network.

JSON-RPC API

All eth_* methods** work identically: eth_sendTransaction, eth_call, eth_estimateGas, etc.

Standard EVM Operations

✅ All EVM opcodes supported (Osaka hardfork set)
✅ Contract creation via CREATE and CREATE2
✅ Standard transaction types (0x00, 0x01, 0x02, 0x03)
✅ EIP-1559 fee market (dynamic fees)
✅ EIP-2930 access lists
✅ EIP-7702 authorization lists (with Tempo signatures)
✅ Events and logs with Bloom filters
✅ Standard block structure and headers
eth_call for read-only contract calls
eth_estimateGas for gas estimation
eth_getLogs for event queries

Tempo-Specific Additions

Tempo extends the EVM with payment-focused features:

1. Tempo Transactions (Type 0x76)

Native transaction format with batching, fee sponsorship, and passkeys:
// Standard Ethereum transaction
const ethTx = await wallet.sendTransaction({
  to: recipient,
  value: parseEther('1'),
});

// Tempo transaction with batching
const tempoTx = await wallet.sendTransaction({
  calls: [
    { to: recipient1, value: parseEther('1'), input: '0x' },
    { to: recipient2, value: parseEther('0.5'), input: '0x' },
  ],
});

Tempo Transactions

Learn about batching, fee sponsorship, and scheduled payments

2. Enshrined Precompiles

Tempo implements critical functionality as protocol-level precompiles:
ERC-20 compatible tokens with built-in memos, payment lanes, and compliance.Gas savings: ~50k gas per transfer vs 65k+ for standard ERC-20.
ITIP20 token = ITIP20(0x20c0000000000000000000000000000000000001);
token.transferWithMemo(recipient, 1000e6, "INV-12345");
Shared compliance policies across multiple tokens.
ITIP403Registry(REGISTRY_ADDRESS).createPolicy(hooksAddress);
token.setPolicyId(policyId);
Automatic stablecoin conversion for fee payments.Users pay in USDC, validators receive DAI — conversion handled automatically.
Protocol-level key management with passkeys, spending limits, and expiry.
IAccountKeychain(KEYCHAIN_ADDRESS).addKey(
    keyHash, SignatureType.P256, validUntil, spendingLimits
);
2D nonce tracking for parallel transaction processing.Query nonces per nonce key for out-of-order transaction submission.
On-chain order book for stablecoin swaps with limit orders.
IStablecoinDEX(DEX_ADDRESS).place(token, amount, isBid, tick);

3. Fee Market Differences

Fees denominated in USD (stablecoins):
// Ethereum (fees in ETH)
const ethTx = {
  maxFeePerGas: parseGwei('50'),        // 50 gwei in ETH
  maxPriorityFeePerGas: parseGwei('2'), // 2 gwei tip
};

// Tempo (fees in stablecoins, specified in attodollars)
const tempoTx = {
  maxFeePerGas: 20_000_000_000n,           // 20 billion attodollars/gas
  maxPriorityFeePerGas: 1_000_000_000n,    // 1 billion attodollars/gas
  feeToken: USDC_ADDRESS,                  // Optional: pay in specific token
};
Units:
  • Attodollars (10^-18 USD): Gas price unit
  • Microdollars (10^-6 USD): Token unit (TIP-20 uses 6 decimals)
  • Conversion: attodollars / 10^12 = microdollars
Fee conversion: If you specify feeToken, the Fee AMM automatically converts your payment to the validator’s preferred stablecoin.

4. Multi-Signature Support

Tempo supports three signature types natively:

secp256k1

Standard Ethereum signatures (65 bytes)

P256

Hardware-backed signing (129 bytes)

WebAuthn

Passkey signatures (variable size)
Contracts can verify any signature type:
function verifyTempoSignature(
    bytes32 hash,
    bytes memory signature,
    address signer
) public view returns (bool) {
    // Automatically handles secp256k1, P256, or WebAuthn
    // based on signature format
}

Key Differences from Ethereum

State Creation Costs (TIP-1000)

Tempo implements elevated storage costs to prevent state bloat:
OperationEthereumTempoChange
New state element (SSTORE)20,000 gas250,000 gas+12.5x
Account creation0 gas250,000 gasNew cost
Contract deployment (per byte)200 gas1,000 gas+5x
Contract deployment (base)32,000 gas500,000 gas+15.6x
Rationale: At 20,000 TPS, Tempo could create 120TB of state per year at Ethereum prices. Higher costs deter state bloat attacks.
Migration Impact: Contracts that create significant state (many storage slots or large mappings) will have higher deployment and runtime costs.

Gas Limits

ParameterEthereumTempoPurpose
Block gas limit30M500MTotal capacity
Transaction gas cap30M30MMax per tx
Payment laneN/A450MTIP-20 transfers
General laneN/A30MSmart contracts
Why higher block gas?
  • 500ms block times vs 12s (24x faster)
  • Dedicated payment lane for high throughput
  • State rent economics prevent bloat despite larger blocks

Block Time

  • Ethereum: ~12 seconds
  • Tempo: ~500 milliseconds (0.5s)
Implications:
  • Faster transaction confirmation
  • More frequent block.timestamp updates (useful for time-sensitive logic)
  • Higher block number growth rate
// Ethereum: ~7,200 blocks per day
// Tempo: ~172,800 blocks per day

function timeBasedLogic() public {
    // Use block.timestamp for time checks, not block.number
    require(block.timestamp >= unlockTime, "Too early");
}
Best Practice: Use block.timestamp for time-based logic, not block.number. Block numbers grow 24x faster on Tempo.

Finality

  • Ethereum: ~15 minutes for practical finality (2 epochs)
  • Tempo: <1 second (Simplex consensus)
Impact: Applications can treat confirmed transactions as final almost immediately.
// Ethereum: Wait for confirmations
const receipt = await tx.wait(5); // Wait 5 blocks (~1 min)

// Tempo: Single confirmation is final
const receipt = await tx.wait(1); // &lt;1 second, irreversible

No Beacon Chain / Merge Concepts

Tempo uses Simplex consensus from genesis — there’s no separate consensus layer or beacon chain:
  • ❌ No beacon chain API
  • ❌ No merge transition
  • ❌ No execution/consensus layer split
  • ✅ Single unified node software

Uncle/Ommer Blocks

Tempo does not have uncle blocks:
  • block.ommersHash always returns empty hash
  • No uncle rewards
  • Simpler fork choice rule (Simplex BFT)

Contract Migration Guide

Step 1: Review Storage Usage

Identify contracts with heavy storage usage:
// High storage cost on Tempo
contract ManyMappings {
    mapping(address => uint256) public balances;        // 250k gas per new entry
    mapping(address => bool) public whitelist;          // 250k gas per new entry
    mapping(address => UserData) public userData;       // 250k per struct (per slot)
    
    // Cheaper: Use TIP-20 for balances (enshrined, optimized)
    ITIP20 public token; // Only 50k gas per transfer
}
Optimization: Use TIP-20 tokens instead of custom balance mappings where possible.

Step 2: Test Gas Costs

Deploy to Tempo testnet and compare gas usage:
# Foundry gas reports
forge test --gas-report --rpc-url https://rpc.moderato.tempo.xyz

Step 3: Update Time-Based Logic

Replace block.number with block.timestamp for time checks:
// ❌ Avoid: Block numbers grow 24x faster on Tempo
require(block.number > unlockBlock, "Locked");

// ✅ Use timestamps instead
require(block.timestamp > unlockTime, "Locked");

Step 4: Handle Fee Differences

Update gas estimates for user-facing apps:
// Ethereum gas estimation
const ethGas = await contract.estimateGas.myFunction();
const ethCost = ethGas * gasPrice; // in ETH

// Tempo gas estimation
const tempoGas = await contract.estimateGas.myFunction();
const tempoCost = tempoGas * baseFee; // in attodollars
const tempoCostUSD = tempoCost / 1e18; // convert to dollars

Step 5: Test Signature Handling

If your contract verifies signatures, test with P256/WebAuthn:
// Supports all Tempo signature types
import "@tempo/contracts/SignatureVerifier.sol";

contract MyContract is SignatureVerifier {
    function verifyUser(bytes32 hash, bytes memory sig, address user) public view {
        require(recoverSigner(hash, sig) == user, "Invalid signature");
    }
}

Tool Compatibility

Foundry

Full support — no configuration changes needed:
# Deploy to Tempo
forge create MyContract \
  --rpc-url https://rpc.moderato.tempo.xyz \
  --private-key $PRIVATE_KEY

# Run tests against Tempo fork
forge test --fork-url https://rpc.moderato.tempo.xyz

Hardhat

Full support — add Tempo network to config:
// hardhat.config.js
module.exports = {
  networks: {
    tempo: {
      url: 'https://rpc.moderato.tempo.xyz',
      chainId: 42431,
      accounts: [process.env.PRIVATE_KEY],
    },
  },
};

Ethers.js / Viem

Full support — use standard provider:
import { createPublicClient, http } from 'viem';

const client = createPublicClient({
  chain: {
    id: 42431,
    name: 'Tempo Testnet',
    network: 'tempo',
    nativeCurrency: { name: 'USD', symbol: 'USD', decimals: 6 },
    rpcUrls: {
      default: { http: ['https://rpc.moderato.tempo.xyz'] },
    },
  },
  transport: http(),
});

Web3.js

Full support:
import Web3 from 'web3';

const web3 = new Web3('https://rpc.moderato.tempo.xyz');

Remix IDE

Full support — use “Injected Provider” with MetaMask connected to Tempo.

RPC Differences

Standard Methods (Identical)

All standard eth_* methods work identically:
  • eth_blockNumber, eth_getBalance, eth_call
  • eth_sendTransaction, eth_sendRawTransaction
  • eth_getTransactionReceipt, eth_getLogs
  • eth_estimateGas, eth_gasPrice, eth_maxPriorityFeePerGas
  • eth_getCode, eth_getStorageAt

Tempo Extensions

Additional RPC methods:
// Fund address on testnet (faucet)
await provider.send('tempo_fundAddress', [address]);

// Get nonce for specific nonce key
const nonce = await provider.send('tempo_getNonce', [
  address,
  nonceKey, // U256 nonce key
]);

// Estimate gas for Tempo transaction
const gas = await provider.send('eth_estimateGas', [{
  calls: [/* batch calls */],
  nonceKey: '0x1',
}]);

Transaction Receipts

Tempo receipts include additional fields:
interface TempoTransactionReceipt extends TransactionReceipt {
  feeToken?: string;          // Token used for fee payment
  feePayer?: string;          // Address that paid fees (if sponsored)
  batchIndex?: number;        // Index of call in batch
  subBlockIndex?: number;     // Sub-block index (if validator tx)
}

Osaka Hardfork Features

Tempo targets the Osaka hardfork EVM specification:

EOF (EVM Object Format)

Improved bytecode format with code/data separation and versioning.

Verkle Trees

Efficient state proofs for light clients (future optimization).

Account Abstraction

EIP-7702 authorization lists natively supported (Tempo signatures).

Blob Transactions

EIP-4844 style data availability (adapted for Tempo’s model).
Osaka features may be implemented progressively. Check the Tempo roadmap for current status.

Common Pitfalls

Problem: Blocks are 24x more frequent on Tempo (500ms vs 12s).Solution: Use block.timestamp for all time-based logic.
// ❌ Bad
require(block.number > unlockBlock);

// ✅ Good
require(block.timestamp > unlockTime);
Problem: State creation costs differ from Ethereum.Solution: Always use eth_estimateGas, don’t hardcode gas limits.
// ❌ Bad
const tx = await contract.myFunction({ gasLimit: 100_000 });

// ✅ Good
const estimated = await contract.estimateGas.myFunction();
const tx = await contract.myFunction({ gasLimit: estimated });
Problem: Fees are denominated in stablecoins (attodollars), not ETH (gwei).Solution: Display fees in USD, not ETH.
// Convert attodollars to USD
const feeUSD = (gasUsed * baseFee) / 1e18;
console.log(`Transaction cost: $${feeUSD.toFixed(6)}`);
Problem: Using custom ERC-20 contracts when TIP-20 would be cheaper.Solution: Use TIP-20 for tokens that need high throughput and low fees.See TIP-20 Token Standard.

Testing Strategy

  1. Local Development: Use Foundry’s anvil or Hardhat’s node (Ethereum-compatible)
  2. Integration Testing: Deploy to Tempo testnet and run full test suite
  3. Gas Profiling: Compare gas costs between Ethereum and Tempo
  4. Load Testing: Test with Tempo’s higher throughput (500ms blocks)
  5. Production: Deploy to Tempo mainnet

Migration Checklist

1

Audit Storage Usage

Identify contracts with heavy SSTORE operations. Calculate new deployment costs.
2

Update Time Logic

Replace all block.number time checks with block.timestamp.
3

Test on Testnet

Deploy to Tempo testnet (Moderato) and run comprehensive test suite.
4

Profile Gas Costs

Compare gas costs with Ethereum. Optimize high-cost operations.
5

Update Frontend

Adjust gas estimation and fee display for stablecoin denomination.
6

Deploy to Mainnet

After thorough testing, deploy to Tempo mainnet.

Further Reading

Quickstart: EVM Compatibility

Practical guide to deploying Ethereum contracts on Tempo

TIP-1000: State Creation Costs

Technical specification of elevated storage pricing

Tempo Transactions

Leverage native batching and fee sponsorship

Smart Contract Development

Complete guide to building on Tempo