Tempo Network Integration
Tempo is a decentralized network for stablecoin payments and machine-to-machine transactions. Co-designed with Stripe as part of the Machine Payments Protocol (MPP) ecosystem, Tempo provides a settlement layer for cross-border stablecoin transfers with fast finality and low fees. Tenzro Network integrates with Tempo to enable direct participation in the Tempo network, allowing AI agents and model providers to settle payments in stablecoins (USDC, USDT) while maintaining TNZO as the native gas token.
Why Tempo Integration?
Tempo integration provides several key benefits for Tenzro Network participants:
- Stablecoin Settlement: Users and agents can pay for AI inference in USDC/USDT instead of volatile TNZO, reducing currency risk for model providers.
- Cross-Network Transfers: Bridge assets between Tenzro and other Tempo-connected networks (Ethereum, Solana, Polygon, etc.) without custodial exchanges.
- Fast Finality: Tempo provides sub-second transaction finality compared to Ethereum's 12-15 minutes, improving payment UX for streaming inference.
- Low Fees: Tempo optimizes for micropayments with fractional-cent fees, making per-token AI billing economically viable.
- Compliance: Tempo supports regulated stablecoin issuers (Circle USDC, Tether USDT) with built-in AML/KYC hooks.
Architecture Overview
Tenzro's Tempo integration consists of three main components:
┌─────────────────────────────────────────────────────┐
│ Tenzro Network │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ TempoBridgeAdapter │ │
│ │ - Token transfers to/from Tempo │ │
│ │ - Finality verification │ │
│ │ - Event monitoring │ │
│ └──────────────┬───────────────────────────────┘ │
│ │ │
│ ┌──────────────▼───────────────────────────────┐ │
│ │ TempoParticipant │ │
│ │ - Network registration │ │
│ │ - Identity management │ │
│ │ - TIP-20 token catalog │ │
│ └──────────────┬───────────────────────────────┘ │
│ │ │
│ ┌──────────────▼───────────────────────────────┐ │
│ │ Tip20Token / Tip20Balance │ │
│ │ - Token metadata (USDC, USDT, etc.) │ │
│ │ - Balance queries │ │
│ │ - Transfer operations │ │
│ └──────────────────────────────────────────────┘ │
│ │
└──────────────────┬──────────────────────────────────┘
│
│ HTTP/WebSocket
│
┌─────────▼─────────────────┐
│ Tempo Network │
│ - Stablecoin settlement │
│ - TIP-20 token registry │
│ - Cross-chain bridges │
└────────────────────────────┘
TIP-20 Token Standard
TIP-20 (Tempo Improvement Proposal 20) is the token standard used by the Tempo network. It defines metadata, transfer semantics, and on-chain representation for fungible tokens like stablecoins.
Tip20Token
The Tip20Token struct represents a TIP-20 token on Tempo:
pub struct Tip20Token {
pub token_id: String, // Unique token identifier
pub name: String, // Human-readable name ("USD Coin")
pub symbol: String, // Ticker symbol ("USDC")
pub decimals: u8, // Decimal places (6 for USDC, 18 for TNZO)
pub issuer: String, // Issuer address (Circle for USDC)
pub total_supply: u64, // Total circulating supply
pub network: String, // Origin network ("ethereum", "solana", "tenzro")
pub contract_address: Option<String>, // Contract address on origin network
}
// Common TIP-20 tokens on Tempo
let usdc = Tip20Token {
token_id: "tip20:usdc".into(),
name: "USD Coin".into(),
symbol: "USDC".into(),
decimals: 6,
issuer: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48".into(), // Circle
total_supply: 25_000_000_000_000_000, // 25 billion USDC
network: "ethereum".into(),
contract_address: Some("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48".into()),
};
let usdt = Tip20Token {
token_id: "tip20:usdt".into(),
name: "Tether USD".into(),
symbol: "USDT".into(),
decimals: 6,
issuer: "0xdac17f958d2ee523a2206206994597c13d831ec7".into(), // Tether
total_supply: 95_000_000_000_000_000, // 95 billion USDT
network: "ethereum".into(),
contract_address: Some("0xdac17f958d2ee523a2206206994597c13d831ec7".into()),
};
Tip20Balance
The Tip20Balance struct represents a user's balance for a specific TIP-20 token:
pub struct Tip20Balance {
pub token_id: String, // References Tip20Token
pub address: String, // Owner address
pub balance: u64, // Balance in smallest denomination
pub locked: u64, // Locked balance (escrow, pending transfers)
pub available: u64, // Available balance (balance - locked)
}
// Query balance
let balance = tempo_participant
.get_balance("tip20:usdc", &user_address)
.await?;
println!("USDC Balance: {} (available: {})",
balance.balance as f64 / 1e6,
balance.available as f64 / 1e6
);
TempoParticipant
The TempoParticipant struct manages Tenzro's registration with the Tempo network. Each Tenzro node can register as a Tempo participant to send/receive TIP-20 tokens:
use tenzro_payments::tempo::*;
// Initialize Tempo participant
let tempo_config = TempoConfig {
endpoint: "https://api.tempo.network".into(),
participant_id: Some("tenzro-node-1".into()),
api_key: Some(std::env::var("TEMPO_API_KEY")?),
network: "mainnet".into(),
};
let participant = TempoParticipant::new(tempo_config).await?;
// Register with Tempo network
participant.register(
"Tenzro Node 1".into(),
node_wallet_address.clone(),
vec!["tip20:usdc".into(), "tip20:usdt".into()], // Supported tokens
).await?;
println!("Registered with Tempo network: {}", participant.participant_id);
// Query supported tokens
let tokens = participant.list_supported_tokens().await?;
for token in tokens {
println!("Token: {} ({}) - {} decimals",
token.name, token.symbol, token.decimals);
}
// Query balance
let usdc_balance = participant.get_balance("tip20:usdc", &node_wallet_address).await?;
println!("USDC balance: {}", usdc_balance.balance as f64 / 1e6);
TempoBridgeAdapter
The TempoBridgeAdapter implements the BridgeAdapter trait for cross-network token transfers. It bridges TIP-20 tokens between Tenzro and other networks connected to Tempo:
Sending Tokens from Tenzro to Tempo
use tenzro_bridge::{BridgeAdapter, TenzroMessage};
use tenzro_payments::tempo::TempoBridgeAdapter;
let tempo_bridge = TempoBridgeAdapter::new(tempo_participant.clone());
// Bridge 100 USDC from Tenzro to Ethereum via Tempo
let message = TenzroMessage {
id: Uuid::new_v4().to_string(),
source_chain: "tenzro-mainnet".into(),
dest_chain: "ethereum".into(),
sender: tenzro_sender_address.clone(),
recipient: ethereum_recipient_address.clone(),
asset: "tip20:usdc".into(),
amount: 100_000_000, // 100 USDC (6 decimals)
data: vec![],
timestamp: chrono::Utc::now().timestamp() as u64,
nonce: 1,
signature: None,
signer_pubkey: None,
};
// Send via Tempo bridge
let tx_hash = tempo_bridge.send_message(&message).await?;
println!("Bridge transfer initiated: {}", tx_hash);
// Monitor transfer status
loop {
let status = tempo_bridge.verify_message(&message.id).await?;
match status.as_str() {
"pending" => {
println!("Transfer pending...");
tokio::time::sleep(Duration::from_secs(5)).await;
}
"confirmed" => {
println!("Transfer confirmed on source chain");
}
"delivered" => {
println!("Transfer delivered to destination chain");
break;
}
"failed" => {
println!("Transfer failed: {}", status);
break;
}
_ => {}
}
}
// Verify finality
let finality = tempo_bridge.check_finality(&tx_hash).await?;
println!("Finality: {:?}", finality);
Receiving Tokens from Tempo to Tenzro
// Listen for incoming transfers from Tempo
let mut event_stream = tempo_bridge.subscribe_events().await?;
while let Some(event) = event_stream.next().await {
match event {
TempoEvent::IncomingTransfer { transfer_id, from, to, token, amount } => {
println!("Incoming transfer: {} {} from {} to {}",
amount as f64 / 1e6, token, from, to);
// Verify transfer on Tempo network
let verified = tempo_participant.verify_transfer(&transfer_id).await?;
if verified {
// Credit recipient account on Tenzro
account_store.credit(
&to,
amount,
token.parse()?, // Convert "tip20:usdc" to Asset enum
).await?;
println!("Transfer credited to {}", to);
}
}
TempoEvent::FinalityReached { transfer_id } => {
println!("Transfer {} reached finality", transfer_id);
}
_ => {}
}
}
Using Tempo with MPP Payments
Tempo integration enables MPP payments to be settled in stablecoins instead of TNZO. This is particularly useful for AI inference payments where providers prefer predictable fiat-equivalent revenue:
use tenzro_payments::mpp::*;
use tenzro_payments::tempo::*;
// Model provider creates MPP challenge requesting USDC payment
let challenge = MppChallenge {
challenge_id: Uuid::new_v4().to_string(),
resource_url: "https://api.provider.com/inference".into(),
amount: 500_000, // 0.5 USDC (6 decimals)
currency: "tip20:usdc".into(), // Request payment in Tempo USDC
expires_at: chrono::Utc::now() + chrono::Duration::minutes(5),
recipient: provider_tempo_address.clone(),
metadata: Some(HashMap::from([
("model_id".into(), "gpt-4".into()),
("settlement_network".into(), "tempo".into()),
])),
};
// Client creates credential with Tempo settlement
let credential = MppCredential {
credential_id: Uuid::new_v4().to_string(),
challenge_id: challenge.challenge_id.clone(),
payer: client_tempo_address.clone(),
recipient: challenge.recipient.clone(),
amount: challenge.amount,
currency: challenge.currency.clone(),
signature: vec![],
timestamp: chrono::Utc::now(),
};
// Sign with wallet
let payload = credential.signing_payload();
let signature = wallet.sign(&payload).await?;
credential.signature = signature;
// Server verifies and settles via Tempo
let mpp = MppProtocol::new(challenge_store.clone(), settlement_engine.clone());
let verification = mpp.verify_credential(&credential).await?;
if verification.verified {
// Execute Tempo transfer
let transfer = tempo_participant.transfer(
&credential.payer,
&credential.recipient,
credential.amount,
"tip20:usdc",
).await?;
// Create receipt with Tempo transaction hash
let receipt = MppReceipt {
receipt_id: Uuid::new_v4().to_string(),
credential_id: credential.credential_id.clone(),
settlement_tx: Some(transfer.tx_hash.clone()),
amount: credential.amount,
currency: credential.currency.clone(),
timestamp: chrono::Utc::now(),
server_signature: vec![],
};
// Return receipt
HttpResponse::Ok()
.header("X-Payment-Receipt", base64::encode(serde_json::to_vec(&receipt)?))
.json(&inference_result)
}
Cross-Chain Settlement Flow
Here's a complete example of an AI inference payment where the user pays from Ethereum USDC and the provider receives on Tenzro:
// 1. USER (Ethereum): Initiate inference request
// User has USDC on Ethereum mainnet, wants to use Tenzro AI model
// 2. USER → TEMPO: Bridge USDC to Tempo network
let bridge_tx = ethereum_usdc_contract.approve(tempo_bridge_address, 10_000_000); // 10 USDC
let tempo_deposit = tempo_ethereum_bridge.deposit(10_000_000, "usdc", user_tempo_address);
// 3. TEMPO: Confirm deposit, credit user's Tempo account
// User now has 10 USDC on Tempo network
// 4. USER → TENZRO MODEL PROVIDER: Request inference with MPP challenge
let challenge = MppChallenge {
amount: 500_000, // 0.5 USDC
currency: "tip20:usdc".into(),
recipient: provider_tempo_address.clone(),
// ...
};
// 5. USER → TEMPO: Transfer USDC to provider
let tempo_transfer = tempo_participant.transfer(
&user_tempo_address,
&provider_tempo_address,
500_000,
"tip20:usdc",
).await?;
// 6. USER → TENZRO: Submit credential with Tempo transfer proof
let credential = MppCredential {
amount: 500_000,
currency: "tip20:usdc".into(),
// metadata includes tempo_transfer.tx_hash
// ...
};
// 7. TENZRO MODEL PROVIDER: Verify credential and Tempo transfer
let tempo_tx = tempo_participant.get_transaction(&tempo_transfer.tx_hash).await?;
assert!(tempo_tx.to == provider_tempo_address);
assert!(tempo_tx.amount >= 500_000);
// 8. TENZRO MODEL PROVIDER: Execute inference, return result
let inference_result = model.infer(user_request).await?;
// 9. PROVIDER → TEMPO → TENZRO: Bridge USDC earnings back to Tenzro (optional)
let bridge_back = tempo_bridge.send_message(&TenzroMessage {
source_chain: "tempo".into(),
dest_chain: "tenzro-mainnet".into(),
sender: provider_tempo_address.clone(),
recipient: provider_tenzro_address.clone(),
asset: "tip20:usdc".into(),
amount: 500_000,
// ...
}).await?;
// Provider now has 0.5 USDC on Tenzro, can swap to TNZO or hold
TempoConfig
The TempoConfig struct configures the Tempo network connection:
pub struct TempoConfig {
pub endpoint: String, // Tempo API endpoint
pub participant_id: Option<String>, // Participant identifier
pub api_key: Option<String>, // API key for authenticated access
pub network: String, // "mainnet", "testnet", "devnet"
}
impl Default for TempoConfig {
fn default() -> Self {
Self {
endpoint: "https://api.tempo.network".into(),
participant_id: None,
api_key: None,
network: "mainnet".into(),
}
}
}
// Load config from environment
let config = TempoConfig {
endpoint: std::env::var("TEMPO_ENDPOINT")
.unwrap_or_else(|_| "https://api.tempo.network".into()),
participant_id: std::env::var("TEMPO_PARTICIPANT_ID").ok(),
api_key: std::env::var("TEMPO_API_KEY").ok(),
network: std::env::var("TEMPO_NETWORK")
.unwrap_or_else(|_| "mainnet".into()),
};
Feature Flag
Tempo integration is gated behind the tempo-bridge feature flag in tenzro-payments. This allows nodes to optionally include Tempo support:
# Cargo.toml
[dependencies]
tenzro-payments = { version = "0.1.0", features = ["tempo-bridge"] }
# Without Tempo support (smaller binary, no stablecoin integration)
tenzro-payments = { version = "0.1.0", default-features = false, features = ["mpp", "x402"] }
Deployment Considerations
Network Participation: To use Tempo, Tenzro nodes must register as Tempo participants with a valid API key. Contact Tempo Labs for participant registration.
Token Whitelisting: Only tokens explicitly supported by both Tempo and Tenzro can be bridged. The initial whitelist includes USDC and USDT; additional tokens require governance approval.
Bridge Security: The Tempo bridge uses multi-signature validation and time-locks for large transfers. All bridge transactions are auditable on both networks.
Gas Costs: Bridge transfers incur gas costs on both source and destination chains. Tenzro nodes should maintain TNZO balances for gas, even when settling in stablecoins.
Finality: Tempo provides fast finality (sub-second), but cross-chain bridges may introduce latency based on source chain finality. Ethereum transfers require ~15 minutes for finality.
Current Implementation Status
As documented in CLAUDE.md, the Tempo integration is currently a stub implementation:
Status: Architectural prototype - API contracts defined but not connected to live Tempo network.
Implemented: Data structures (TempoConfig, TempoBridgeAdapter, Tip20Token, Tip20Balance, TempoParticipant), trait implementations for BridgeAdapter, type-safe API.
Not Implemented: Actual HTTP/WebSocket connection to Tempo network, real balance queries (currently return 0), real transfers (currently return fake tx hashes), finality verification (currently always returns Finalized).
Phase 4 development priorities include connecting the Tempo adapter to the live Tempo network. See the Development Priorities section in the Architecture documentation for the full roadmap.
Next Steps
- Review settlement architecture for payment finalization mechanics
- Explore bridge architecture for cross-chain transfer details
- Compare with MPP and x402 payment protocols
- Understand multi-asset wallet support for USDC/USDT