Skip to main content
The Rust SDK provides type-safe, high-performance bindings for building on Tempo. Built with Alloy, it gives Rust developers native access to TIP-20 tokens, batch payments, and Tempo-specific transaction types.

Installation

Add tempo-alloy to your Cargo.toml:
[dependencies]
tempo-alloy = { git = "https://github.com/tempoxyz/tempo" }
alloy = { version = "0.7", features = ["providers", "rpc-types-eth", "sol-types"] }
tokio = { version = "1", features = ["full"] }
tempo-alloy is currently in development. Install from the GitHub repository until it’s published to crates.io.

Quick Start

Configure Provider

Connect to Tempo using TempoNetwork:
use alloy::providers::{Provider, ProviderBuilder};
use tempo_alloy::TempoNetwork;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let provider = ProviderBuilder::new_with_network::<TempoNetwork>()
        .connect("https://rpc.moderato.tempo.xyz")
        .await?;

    let block_number = provider.get_block_number().await?;
    println!("Current block: {}", block_number);

    Ok(())
}

Get Token Balance

Query TIP-20 token balances:
use alloy::primitives::address;
use alloy::providers::ProviderBuilder;
use tempo_alloy::{TempoNetwork, contracts::precompiles::ITIP20};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let provider = ProviderBuilder::new_with_network::<TempoNetwork>()
        .connect(&std::env::var("RPC_URL")?)
        .await?;

    let token = ITIP20::new(
        address!("0x20c0000000000000000000000000000000000001"), // AlphaUSD
        &provider,
    );

    let balance = token
        .balanceOf(address!("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb"))
        .call()
        .await?;

    println!("Balance: {:?}", balance._0);

    Ok(())
}

Send Transfer

Send a TIP-20 token transfer:
use alloy::primitives::{U256, address};
use alloy::providers::ProviderBuilder;
use tempo_alloy::{TempoNetwork, contracts::precompiles::ITIP20};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let provider = ProviderBuilder::new_with_network::<TempoNetwork>()
        .connect(&std::env::var("RPC_URL")?)
        .await?;

    let token = ITIP20::new(
        address!("0x20c0000000000000000000000000000000000001"),
        &provider,
    );

    let receipt = token
        .transfer(
            address!("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb"),
            U256::from(100_000_000), // 100 tokens (6 decimals)
        )
        .send()
        .await?
        .get_receipt()
        .await?;

    println!("Transfer successful: {:?}", receipt.transaction_hash);

    Ok(())
}

Key Types

TempoNetwork

The core network configuration for Tempo:
use tempo_alloy::TempoNetwork;
use alloy::providers::ProviderBuilder;

// Create a provider with Tempo network types
let provider = ProviderBuilder::new_with_network::<TempoNetwork>()
    .connect("https://rpc.moderato.tempo.xyz")
    .await?;
TempoNetwork provides:
  • TxType: Tempo transaction types (Legacy, EIP-1559, EIP-2930, EIP-7702, AA)
  • TxEnvelope: Signed transaction envelopes
  • ReceiptEnvelope: Transaction receipts with Tempo-specific fields
  • TransactionRequest: Builder for creating transactions

TempoTransactionRequest

Build Tempo-specific transactions:
use tempo_alloy::rpc::TempoTransactionRequest;
use tempo_alloy::primitives::transaction::Call;
use alloy::primitives::{Address, U256, Bytes};

let request = TempoTransactionRequest {
    calls: vec![
        Call {
            to: token_address.into(),
            input: transfer_calldata.into(),
            value: U256::ZERO,
        },
    ],
    fee_token: Some(token_address),
    valid_after: Some(1234567890),
    valid_before: Some(1234567900),
    ..Default::default()
};

TIP-20 Interface

Type-safe contract bindings:
use tempo_alloy::contracts::precompiles::ITIP20;

// Create a contract instance
let token = ITIP20::new(token_address, &provider);

// Call read-only functions
let name = token.name().call().await?;
let symbol = token.symbol().call().await?;
let decimals = token.decimals().call().await?;
let total_supply = token.totalSupply().call().await?;

// Send transactions
let receipt = token
    .transfer(recipient, amount)
    .send()
    .await?
    .get_receipt()
    .await?;

TIP-20 Tokens

Transfer with Memo

Include reconciliation data:
use alloy::primitives::B256;

let memo = B256::left_padding_from("INV-12345".as_bytes());

let receipt = token
    .transferWithMemo(
        address!("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb"),
        U256::from(100_000_000),
        memo,
    )
    .send()
    .await?
    .get_receipt()
    .await?;

Watch Transfer Events

Listen for incoming transfers:
use futures::StreamExt;

let mut transfers = token
    .Transfer_filter()
    .watch()
    .await?
    .into_stream();

while let Some(Ok((transfer, _))) = transfers.next().await {
    println!(
        "Transfer: {} -> {}: {}",
        transfer.from, transfer.to, transfer.value
    );
}

Filter Transfer Events

Query historical transfers:
let filter = token
    .Transfer_filter()
    .from_block(0)
    .to_block(1000);

let transfers = filter.query().await?;

for (transfer, log) in transfers {
    println!("Block {}: {} tokens", log.block_number, transfer.value);
}

Batch Payments

Send multiple transfers atomically:
use alloy::sol_types::SolCall;
use alloy::providers::Provider;
use tempo_alloy::primitives::transaction::Call;
use tempo_alloy::rpc::TempoTransactionRequest;
use tempo_alloy::contracts::precompiles::ITIP20;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let provider = ProviderBuilder::new_with_network::<TempoNetwork>()
        .connect(&std::env::var("RPC_URL")?)
        .await?;

    let token_address = address!("0x20c0000000000000000000000000000000000001");

    let calls = vec![
        Call {
            to: token_address.into(),
            input: ITIP20::transferCall {
                to: address!("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb"),
                amount: U256::from(100_000_000),
            }
            .abi_encode()
            .into(),
            value: U256::ZERO,
        },
        Call {
            to: token_address.into(),
            input: ITIP20::transferCall {
                to: address!("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"),
                amount: U256::from(50_000_000),
            }
            .abi_encode()
            .into(),
            value: U256::ZERO,
        },
    ];

    let pending = provider
        .send_transaction(TempoTransactionRequest {
            calls,
            ..Default::default()
        })
        .await?;

    let tx_hash = pending.tx_hash();
    println!("Batch transaction sent: {:?}", tx_hash);

    Ok(())
}

Advanced Provider Configuration

Random 2D Nonces

Use random 2D nonces for parallel transaction submission:
use tempo_alloy::provider::TempoProviderBuilderExt;

let provider = ProviderBuilder::new_with_network::<TempoNetwork>()
    .with_random_2d_nonces()
    .connect("https://rpc.moderato.tempo.xyz")
    .await?;

Expiring Nonces

Use expiring nonces (TIP-1009) for transaction management:
use tempo_alloy::provider::TempoProviderBuilderExt;

let provider = ProviderBuilder::new_with_network::<TempoNetwork>()
    .with_expiring_nonces()
    .connect("https://rpc.moderato.tempo.xyz")
    .await?;
See TIP-1009 for details on expiring nonces.

Module Structure

The tempo-alloy crate is organized into:
use tempo_alloy::{
    TempoNetwork,                    // Network configuration
    contracts::precompiles::*,       // TIP-20, TIP-403, and other precompiles
    primitives::*,                   // Core Tempo types
    provider::TempoProviderBuilderExt, // Provider extensions
    rpc::*,                          // RPC types and transaction builders
    fillers::*,                      // Transaction fillers (nonces, gas, etc.)
};
See crates/alloy/src/lib.rs for the complete module layout.

Examples

The Tempo repository includes comprehensive examples:
ExampleDescription
configure_providerConnect to Tempo
get_balanceQuery token balances
transferSend basic transfer
transfer_with_memoTransfer with reconciliation data
batch_paymentsAtomic multi-recipient payments
watch_transfersListen for transfer events
watch_transfers_with_memoWatch transfers with memos
mint_tokensMint TIP-20 tokens
burn_tokensBurn TIP-20 tokens
mint_fee_liquidityAdd fee pool liquidity
Run examples with:
cargo run --example <example_name> -p tempo-alloy
Set the RPC_URL environment variable first:
export RPC_URL="https://rpc.moderato.tempo.xyz"

Error Handling

Handle Alloy transport and contract errors:
use alloy::transports::TransportError;
use alloy::contract::Error as ContractError;

match token.transfer(recipient, amount).send().await {
    Ok(pending) => {
        let receipt = pending.get_receipt().await?;
        println!("Success: {:?}", receipt.transaction_hash);
    }
    Err(e) => {
        eprintln!("Transfer failed: {}", e);
    }
}

Feature Flags

Available Cargo features:
[dependencies]
tempo-alloy = { 
    git = "https://github.com/tempoxyz/tempo",
    features = ["asm-keccak", "tempo-compat"]
}
  • asm-keccak: Use optimized assembly for keccak hashing
  • tempo-compat: Include Reth compatibility types for node integrations

Next Steps

Alloy Documentation

Learn about the Alloy framework

TIP-20 Reference

Complete TIP-20 specification

Tempo Transactions

Account abstraction features

Source Code

View the SDK source on GitHub