<!--
Sitemap:
- [Tempo](/index): Explore Tempo's blockchain documentation, integration guides, and protocol specs. Build low-cost, high-throughput payment applications.
- [Accounts SDK – Getting Started](/accounts/): Set up the Tempo Accounts SDK to create, manage, and interact with accounts on Tempo.
- [Changelog](/changelog)
- [Tempo CLI](/cli/): A single binary for using Tempo Wallet from the terminal, making paid HTTP requests, and running a Tempo node.
- [Tempo Ecosystem Infrastructure](/ecosystem/): Explore Tempo's ecosystem partners providing bridges, wallets, node infrastructure, data analytics, security, and more for building on Tempo.
- [Learn](/learn/): Explore stablecoin use cases and Tempo's payments-optimized blockchain architecture for remittances, payouts, and embedded finance.
- [Tempo Protocol](/protocol/): Technical specifications and reference documentation for the Tempo blockchain protocol, purpose-built for global payments at scale.
- [SDKs](/sdk/): Integrate Tempo into your applications with SDKs for TypeScript, Go, Rust, and Foundry. Build blockchain apps in your preferred language.
- [Tempo CLI](/wallet/): A terminal client for Tempo wallet management, service discovery, and paid HTTP requests via the Machine Payments Protocol.
- [FAQ](/accounts/faq): Frequently asked questions about the Tempo Accounts SDK.
- [Deploying to Production](/accounts/production): Things to consider before deploying your application with the Tempo Accounts SDK to production.
- [Handlers](/accounts/server/): Server-side handlers for the Tempo Accounts SDK.
- [tempo download](/cli/download): Download chain snapshots for faster initial sync of a Tempo node.
- [tempo node](/cli/node): Command reference for running a Tempo node.
- [tempo request](/cli/request): A curl-like HTTP client that handles MPP payment negotiation automatically.
- [tempo wallet](/cli/wallet): Use Tempo Wallet from the terminal — authenticate, check balances, manage access keys, and discover services.
- [Block Explorers](/ecosystem/block-explorers): View transactions, blocks, accounts, and token activity on the Tempo network with block explorers.
- [Bridges & Exchanges](/ecosystem/bridges): Move assets to and from Tempo with cross-chain bridges and access deep DEX liquidity with exchange infrastructure.
- [Data & Analytics](/ecosystem/data-analytics): Query blockchain data on Tempo with indexers, analytics platforms, oracles, and monitoring tools.
- [Node Infrastructure](/ecosystem/node-infrastructure): Connect to Tempo with reliable RPC endpoints and managed node services from infrastructure partners.
- [Issuance & Orchestration](/ecosystem/orchestration): Move money globally between local currencies and stablecoins. Issue, transfer, and manage stablecoins on Tempo.
- [Security & Compliance](/ecosystem/security-compliance): Transaction scanning, threat detection, and compliance infrastructure for Tempo applications.
- [Smart Contract Libraries](/ecosystem/smart-contract-libraries): Build with account abstraction and programmable smart contract wallets on Tempo.
- [Wallets](/ecosystem/wallets): Integrate embedded, custodial, and institutional wallet infrastructure into your Tempo application.
- [!Replace Me!](/guide/_template)
- [Bridge via LayerZero](/guide/bridge-layerzero): Bridge tokens to and from Tempo using LayerZero. Covers Stargate pools and standard OFT adapters with cast commands and TypeScript examples.
- [Bridge via Relay](/guide/bridge-relay): Bridge tokens to and from Tempo using Relay. Includes supported token discovery, curl commands, TypeScript examples with viem, and status tracking.
- [Getting Funds on Tempo](/guide/getting-funds): Bridge assets to Tempo, add funds in Tempo Wallet, or use the faucet on testnet.
- [Stablecoin Issuance](/guide/issuance/): Create and manage your own stablecoin on Tempo. Issue tokens, manage supply, and integrate with Tempo's payment infrastructure.
- [Agentic Payments](/guide/machine-payments/): Make agentic payments using the Machine Payments Protocol (MPP) on Tempo — charge for APIs, MCP tools, and digital content with TIP-20 stablecoins.
- [Tempo Node](/guide/node/): Run your own Tempo node for direct network access. Set up RPC nodes for API access or validator nodes to participate in consensus.
- [Stablecoin Payments](/guide/payments/): Send and receive stablecoin payments on Tempo. Integrate payments with flexible fee options, sponsorship capabilities, and parallel transactions.
- [Exchange Stablecoins](/guide/stablecoin-dex/): Trade between stablecoins on Tempo's enshrined DEX. Execute swaps, provide liquidity, and query the onchain orderbook for optimal pricing.
- [Use Tempo Transactions](/guide/tempo-transaction/): Learn how to use Tempo Transactions for configurable fee tokens, fee sponsorship, batch calls, access keys, and concurrent execution.
- [Create & Use Accounts](/guide/use-accounts/): Create and integrate Tempo accounts with the universal Tempo Wallet or domain-bound passkeys.
- [Using Tempo with AI](/guide/using-tempo-with-ai): Give your AI coding agent Tempo documentation context and a wallet for autonomous transactions.
- [Partners](/learn/partners): Discover Tempo's ecosystem of stablecoin issuers, wallets, custody providers, compliance tools, and ramps.
- [What are stablecoins?](/learn/stablecoins): Learn what stablecoins are, how they maintain value through reserves, and the payment use cases they enable for businesses globally.
- [Tempo](/learn/tempo/): Discover Tempo, the payments-first blockchain with instant settlement, predictably low fees, and native stablecoin support.
- [Exchanging Stablecoins](/protocol/exchange/): Tempo's enshrined decentralized exchange for trading between stablecoins with optimal pricing, limit orders, and flip orders for liquidity provision.
- [Transaction Fees](/protocol/fees/): Pay transaction fees in any USD stablecoin on Tempo. No native token required—fees are paid directly in TIP-20 stablecoins with automatic conversion.
- [Tempo Improvement Proposals (TIPs)](/protocol/tips/)
- [Tempo Transactions](/protocol/transactions/): Learn about Tempo Transactions, a new EIP-2718 transaction type with passkey support, fee sponsorship, batching, and concurrent execution.
- [Connect to the Network](/quickstart/connection-details): Connect to Tempo using browser wallets, CLI tools, or direct RPC endpoints. Get chain ID, URLs, and configuration details.
- [Developer Tools](/quickstart/developer-tools): Explore Tempo's developer ecosystem with indexers, embedded wallets, node infrastructure, and analytics partners for building payment apps.
- [EVM Differences](/quickstart/evm-compatibility): Learn how Tempo differs from Ethereum. Understand wallet behavior, fee token selection, VM layer changes, and fast finality consensus.
- [Faucet](/quickstart/faucet): Get free test stablecoins on Tempo Testnet. Connect your wallet or enter any address to receive pathUSD, AlphaUSD, BetaUSD, and ThetaUSD.
- [Integrate Tempo](/quickstart/integrate-tempo): Build on Tempo Testnet. Connect to the network, explore SDKs, and follow guides for accounts, payments, and stablecoin issuance.
- [Predeployed Contracts](/quickstart/predeployed-contracts): Discover Tempo's predeployed system contracts including TIP-20 Factory, Fee Manager, Stablecoin DEX, and standard utilities like Multicall3.
- [Tempo Token List Registry](/quickstart/tokenlist)
- [Contract Verification](/quickstart/verify-contracts): Verify your smart contracts on Tempo using contracts.tempo.xyz. Sourcify-compatible verification with Foundry integration.
- [Wallet Integration Guide](/quickstart/wallet-developers): Integrate Tempo into your wallet. Handle fee tokens, configure gas display, and deliver enhanced stablecoin payment experiences for users.
- [Foundry for Tempo](/sdk/foundry/): Build, test, and deploy smart contracts on Tempo using the Foundry fork. Access protocol-level features with forge and cast tools.
- [Go](/sdk/go/): Build blockchain apps with the Tempo Go SDK. Send transactions, batch calls, and handle fee sponsorship with idiomatic Go code.
- [Python](/sdk/python/): Build blockchain apps with the Tempo Python SDK. Send transactions, batch calls, and handle fee sponsorship using web3.py.
- [Rust](/sdk/rust/): Build blockchain apps with the Tempo Rust SDK using Alloy. Query chains, send transactions, and manage tokens with type-safe Rust code.
- [TypeScript SDKs](/sdk/typescript/): Build blockchain apps with Tempo using Viem and Wagmi. Send transactions, manage tokens, and integrate AMM pools with TypeScript.
- [Tempo Wallet CLI Recipes](/wallet/recipes): Use practical Tempo Wallet CLI recipes for service discovery, paid requests, session management, and funding or transfers.
- [Tempo CLI Reference](/wallet/reference): Complete command and flag reference for tempo wallet and tempo request.
- [Use Tempo Wallet CLI with Agents](/wallet/use-with-agents): Connect Tempo Wallet CLI to your agent and understand the built-in features that make agent-driven paid requests reliable and safe.
- [Adapters](/accounts/api/adapters): Pluggable adapters for the Tempo Accounts SDK Provider.
- [dialog](/accounts/api/dialog): Adapter for the Tempo Wallet dialog, an embedded iframe or popup for account management.
- [Dialog.iframe](/accounts/api/dialog.iframe): Embed the Tempo Wallet auth UI in an iframe dialog element.
- [Dialog.popup](/accounts/api/dialog.popup): Open the Tempo Wallet auth UI in a popup window.
- [Dialog](/accounts/api/dialogs): Dialog modes for embedding the Tempo Wallet.
- [Expiry](/accounts/api/expiry): Utility functions for computing access key expiry timestamps.
- [local](/accounts/api/local): Key-agnostic adapter for defining arbitrary account types and signing mechanisms.
- [Provider](/accounts/api/provider): Create an EIP-1193 provider for managing accounts on Tempo.
- [webAuthn](/accounts/api/webAuthn): Adapter for passkey-based accounts using WebAuthn registration and authentication.
- [WebAuthnCeremony](/accounts/api/webauthnceremony): Pluggable strategy for WebAuthn registration and authentication ceremonies.
- [WebAuthnCeremony.from](/accounts/api/webauthnceremony.from): Create a WebAuthnCeremony from a custom implementation.
- [WebAuthnCeremony.server](/accounts/api/webauthnceremony.server): Server-backed WebAuthn ceremony that delegates to a remote handler.
- [Create & Use Accounts](/accounts/guides/create-and-use-accounts): Choose between universal wallet experiences or domain-bound passkey accounts for your app.
- [eth_fillTransaction](/accounts/rpc/eth_fillTransaction): Fill missing transaction fields like gas and nonce via the node.
- [eth_sendTransaction](/accounts/rpc/eth_sendTransaction): Send a transaction from the connected account.
- [eth_sendTransactionSync](/accounts/rpc/eth_sendTransactionSync): Send a transaction and wait for the receipt.
- [personal_sign](/accounts/rpc/personal_sign): Sign a message with the connected account.
- [wallet_authorizeAccessKey](/accounts/rpc/wallet_authorizeAccessKey): Authorize an access key for delegated transaction signing.
- [wallet_connect](/accounts/rpc/wallet_connect): Connect account(s) with optional capabilities like access key authorization.
- [wallet_disconnect](/accounts/rpc/wallet_disconnect): Disconnect the connected account(s).
- [wallet_getBalances](/accounts/rpc/wallet_getBalances): Get token balances for an account.
- [wallet_getCallsStatus](/accounts/rpc/wallet_getCallsStatus): Get the status of a batch of calls sent via wallet_sendCalls.
- [wallet_getCapabilities](/accounts/rpc/wallet_getCapabilities): Get account capabilities for specified chains.
- [wallet_revokeAccessKey](/accounts/rpc/wallet_revokeAccessKey): Revoke a previously authorized access key.
- [wallet_sendCalls](/accounts/rpc/wallet_sendCalls): Send a batch of calls from the connected account.
- [Handler.compose](/accounts/server/handler.compose): Compose multiple server handlers into a single handler.
- [Handler.feePayer](/accounts/server/handler.feePayer): Server handler that sponsors transaction fees for users.
- [Handler.webAuthn](/accounts/server/handler.webAuthn): Server-side WebAuthn ceremony handler for registration and authentication.
- [Kv](/accounts/server/kv): Key-value store adapters for server-side persistence.
- [tempoWallet](/accounts/wagmi/tempoWallet): Wagmi connector for the Tempo Wallet dialog.
- [webAuthn](/accounts/wagmi/webAuthn): Wagmi connector for passkey-based WebAuthn accounts.
- [Create a Stablecoin](/guide/issuance/create-a-stablecoin): Create your own stablecoin on Tempo using the TIP-20 token standard. Deploy tokens with built-in compliance features and role-based permissions.
- [Distribute Rewards](/guide/issuance/distribute-rewards): Distribute rewards to token holders using TIP-20's built-in reward mechanism. Allocate tokens proportionally based on holder balances.
- [Manage Your Stablecoin](/guide/issuance/manage-stablecoin): Configure stablecoin permissions, supply limits, and compliance policies. Grant roles, set transfer policies, and control pause/unpause functionality.
- [Mint Stablecoins](/guide/issuance/mint-stablecoins): Mint new tokens to increase your stablecoin's total supply. Grant the issuer role and create tokens with optional memos for tracking.
- [Use Your Stablecoin for Fees](/guide/issuance/use-for-fees): Enable users to pay transaction fees using your stablecoin. Add fee pool liquidity and integrate with Tempo's flexible fee payment system.
- [Agent Quickstart](/guide/machine-payments/agent): Use the tempo CLI to discover services, preview costs, and make paid requests from a terminal or AI agent — no SDK required.
- [Client Quickstart](/guide/machine-payments/client): Set up an MPP client on Tempo. Polyfill fetch to automatically pay for 402 responses with TIP-20 stablecoins.
- [Accept One-Time Payments](/guide/machine-payments/one-time-payments): Charge per request on Tempo using the mppx charge intent. Each request triggers a TIP-20 transfer that settles in ~500ms.
- [Accept Pay-As-You-Go Payments](/guide/machine-payments/pay-as-you-go): Session-based billing on Tempo with MPP payment channels. Clients deposit funds, sign off-chain vouchers, and pay per request without on-chain latency.
- [Server Quickstart](/guide/machine-payments/server): Add payment gating to any HTTP endpoint on Tempo with mppx middleware for Next.js, Hono, Express, and the Fetch API.
- [Accept Streamed Payments](/guide/machine-payments/streamed-payments): Per-token billing over Server-Sent Events on Tempo. Stream content word-by-word and charge per unit using MPP sessions with SSE.
- [Installation](/guide/node/installation): Install Tempo node using pre-built binaries, build from source with Rust, or run with Docker. Get started in minutes with tempoup.
- [Network Upgrades and Releases](/guide/node/network-upgrades): Timeline and details for Tempo network upgrades and important releases for node operators.
- [Operate your validator node](/guide/node/operate-validator): Day-to-day operations for Tempo validators. Node lifecycle, monitoring, metrics, log management, and Grafana dashboards.
- [Running an RPC Node](/guide/node/rpc): Set up and run a Tempo RPC node for API access. Download snapshots, configure systemd services, and monitor node health and sync status.
- [System Requirements](/guide/node/system-requirements): Minimum and recommended hardware specs for running Tempo RPC and validator nodes. CPU, RAM, storage, network, and port requirements.
- [Running a validator node](/guide/node/validator): Configure and run a Tempo validator node. Generate signing keys, participate in DKG ceremonies, and troubleshoot consensus issues.
- [ValidatorConfig V1 (Legacy)](/guide/node/validator-config-v1): Legacy validator management via ValidatorConfig V1. Key rotation, IP updates, and signing share recovery pre-T2.
- [ValidatorConfig V2](/guide/node/validator-config-v2): Manage your validator with ValidatorConfig V2. Self-service key rotation, IP updates, and ownership transfer.
- [Accept a Payment](/guide/payments/accept-a-payment): Accept stablecoin payments in your application. Verify transactions, listen for transfer events, and reconcile payments using memos.
- [Pay Fees in Any Stablecoin](/guide/payments/pay-fees-in-any-stablecoin): Configure users to pay transaction fees in any supported stablecoin. Eliminate the need for a separate gas token with Tempo's flexible fee system.
- [Send a Payment](/guide/payments/send-a-payment): Send stablecoin payments between accounts on Tempo. Include optional memos for reconciliation and tracking with TypeScript, Rust, or Solidity.
- [Send Parallel Transactions](/guide/payments/send-parallel-transactions): Submit multiple transactions concurrently using Tempo's expiring nonce system under-the-hood.
- [Sponsor User Fees](/guide/payments/sponsor-user-fees): Enable gasless transactions by sponsoring fees for your users. Set up a fee payer service and improve UX by removing friction from payment flows.
- [Attach a Transfer Memo](/guide/payments/transfer-memos)
- [Executing Swaps](/guide/stablecoin-dex/executing-swaps): Learn to execute instant stablecoin swaps on Tempo's DEX. Get price quotes, set slippage protection, and batch approvals with swaps.
- [Managing Fee Liquidity](/guide/stablecoin-dex/managing-fee-liquidity): Add and remove liquidity in the Fee AMM to enable stablecoin fee conversions. Monitor pools, check LP balances, and rebalance reserves.
- [Providing Liquidity](/guide/stablecoin-dex/providing-liquidity): Place limit and flip orders to provide liquidity on the Stablecoin DEX orderbook. Learn to manage orders and set prices using ticks.
- [View the Orderbook](/guide/stablecoin-dex/view-the-orderbook): Inspect Tempo's onchain orderbook using SQL queries. View spreads, order depth, individual orders, and recent trade prices with indexed data.
- [Add Funds to Your Balance](/guide/use-accounts/add-funds): Get test stablecoins on Tempo Testnet using the faucet. Request pathUSD, AlphaUSD, BetaUSD, and ThetaUSD tokens for development and testing.
- [Batch Transactions](/guide/use-accounts/batch-transactions)
- [Connect to Wallets](/guide/use-accounts/connect-to-wallets): Connect your application to EVM-compatible wallets like MetaMask on Tempo. Set up Wagmi connectors and add the Tempo network to user wallets.
- [Embed Passkey Accounts](/guide/use-accounts/embed-passkeys): Create domain-bound passkey accounts on Tempo using WebAuthn for secure, passwordless authentication with biometrics like Face ID and Touch ID.
- [Embed Tempo Wallet](/guide/use-accounts/embed-tempo-wallet): Embed the Tempo Wallet dialog into your application for a universal wallet experience with account management, passkeys, and fee sponsorship.
- [Scheduled Transactions](/guide/use-accounts/scheduled-transactions)
- [WebAuthn & P256 Signatures](/guide/use-accounts/webauthn-p256-signatures)
- [Onchain FX](/learn/tempo/fx): Access foreign exchange liquidity directly onchain with regulated non-USD stablecoin issuers and multi-currency fee payments on Tempo.
- [Agentic Payments](/learn/tempo/machine-payments): The Machine Payments Protocol (MPP) is an open standard for machine-to-machine payments, co-authored by Stripe and Tempo.
- [Tempo Transactions](/learn/tempo/modern-transactions): Native support for gas sponsorship, batch transactions, scheduled payments, and passkey authentication built into Tempo's protocol.
- [TIP-20 Tokens](/learn/tempo/native-stablecoins): Tempo's stablecoin token standard with payment lanes, stable fees, reconciliation memos, and built-in compliance for regulated issuers.
- [Performance](/learn/tempo/performance): High throughput and sub-second finality built on Reth SDK and Simplex Consensus for payment applications requiring instant settlement.
- [Privacy](/learn/tempo/privacy): Explore Tempo's opt-in privacy features enabling private balances and confidential transfers while maintaining issuer compliance.
- [Power AI agents with programmable money](/learn/use-cases/agentic-commerce): Power autonomous AI agents with programmable stablecoin payments for goods, services, and digital resources in real time.
- [Bring embedded finance to life with stablecoins](/learn/use-cases/embedded-finance): Enable platforms and marketplaces to streamline partner payouts, lower payment costs, and launch rewarding loyalty programs.
- [Send global payouts instantly](/learn/use-cases/global-payouts): Deliver instant, low-cost payouts to contractors, merchants, and partners worldwide with stablecoins, bypassing slow banking rails.
- [Enable true pay-per-use pricing](/learn/use-cases/microtransactions): Enable true pay-per-use pricing with sub-cent payments for APIs, content, IoT services, and machine-to-machine commerce.
- [Stablecoins for Payroll](/learn/use-cases/payroll): Faster payroll funding, cheaper cross-border payouts, and new revenue streams for payroll providers using stablecoins.
- [Send money home faster and cheaper](/learn/use-cases/remittances): Send cross-border payments faster and cheaper with stablecoins, eliminating correspondent banks and reducing transfer costs.
- [Move treasury liquidity instantly across borders](/learn/use-cases/tokenized-deposits): Move treasury liquidity instantly across borders with real-time visibility into global cash positions using tokenized deposits.
- [Consensus and Finality](/protocol/blockspace/consensus): Tempo uses Simplex BFT via Commonware for deterministic sub-second finality with Byzantine fault tolerance.
- [Blockspace Overview](/protocol/blockspace/overview): Technical specification for Tempo block structure including header fields, payment lanes, and system transaction ordering.
- [Payment Lane Specification](/protocol/blockspace/payment-lane-specification): Technical specification for Tempo payment lanes ensuring dedicated blockspace for payment transactions with predictable fees during congestion.
- [DEX Balance](/protocol/exchange/exchange-balance): Hold token balances directly on the Stablecoin DEX to save gas costs on trades, receive maker proceeds automatically, and trade more efficiently.
- [Executing Swaps](/protocol/exchange/executing-swaps): Learn how to execute swaps and quote prices on Tempo's Stablecoin DEX with exact-in and exact-out swap functions and slippage protection.
- [Providing Liquidity](/protocol/exchange/providing-liquidity): Provide liquidity on Tempo's DEX using limit orders and flip orders. Earn spreads while facilitating stablecoin trades with price-time priority.
- [Quote Tokens](/protocol/exchange/quote-tokens): Quote tokens determine trading pairs on Tempo's DEX. Each TIP-20 specifies a quote token, with pathUSD available as an optional neutral choice.
- [Stablecoin DEX](/protocol/exchange/spec): Technical specification for Tempo's enshrined DEX with price-time priority orderbook, flip orders, and multi-hop routing for stablecoin trading.
- [Fee AMM Overview](/protocol/fees/fee-amm/): Understand how the Fee AMM automatically converts transaction fees between stablecoins, enabling users to pay in any supported token.
- [Fee Specification](/protocol/fees/spec-fee): Technical specification for Tempo's fee system covering multi-token fee payment, fee sponsorship, token preferences, and validator payouts.
- [Fee AMM Specification](/protocol/fees/spec-fee-amm): Technical specification for the Fee AMM enabling automatic stablecoin conversion for transaction fees with fixed-rate swaps and MEV protection.
- [TIP-20 Rewards](/protocol/tip20-rewards/overview): Built-in reward distribution mechanism for TIP-20 tokens enabling efficient, opt-in proportional rewards to token holders at scale.
- [TIP-20 Rewards Distribution](/protocol/tip20-rewards/spec): Technical specification for the TIP-20 reward distribution system using reward-per-token accumulator pattern for scalable pro-rata rewards.
- [TIP-20 Token Standard](/protocol/tip20/overview): TIP-20 is Tempo's native token standard for stablecoins with built-in fee payment, payment lanes, transfer memos, and compliance policies.
- [TIP20](/protocol/tip20/spec): Technical specification for TIP-20, the optimized token standard extending ERC-20 with memos, rewards distribution, and policy integration.
- [TIP-403 Policy Registry](/protocol/tip403/overview): Learn how TIP-403 enables TIP-20 tokens to enforce access control through a shared policy registry with whitelist and blacklist support.
- [Overview](/protocol/tip403/spec): Technical specification for TIP-403, the policy registry system enabling whitelist and blacklist access control for TIP-20 tokens on Tempo.
- [TIP Title](/protocol/tips/_tip_template): Short description for SEO
- [TIP Process](/protocol/tips/tip-0000): Defines the Tempo Improvement Proposal lifecycle from draft to production.
- [State Creation Cost Increase](/protocol/tips/tip-1000): Increased gas costs for state creation operations to protect Tempo from adversarial state growth attacks.
- [Place-only mode for next quote token](/protocol/tips/tip-1001): A new DEX function for creating trading pairs against a token's staged next quote token, to allow orders to be placed on it.
- [Prevent crossed orders and allow same-tick flip orders](/protocol/tips/tip-1002): Changes to the Stablecoin DEX that prevent placing orders that would cross existing orders on the opposite side of the book, and allow flip orders to flip to the same tick.
- [Client order IDs](/protocol/tips/tip-1003): Addition of client order IDs to the Stablecoin DEX, allowing users to specify their own order identifiers for idempotency and easier order tracking.
- [Permit for TIP-20](/protocol/tips/tip-1004): Addition of EIP-2612 permit functionality to TIP-20 tokens, enabling gasless approvals via off-chain signatures.
- [Fix ask swap rounding loss](/protocol/tips/tip-1005): A fix for a rounding bug in the Stablecoin DEX where partial fills on ask orders can cause small amounts of quote tokens to be lost.
- [Burn At for TIP-20 Tokens](/protocol/tips/tip-1006): The burnAt function for TIP-20 tokens, enabling authorized administrators to burn tokens from any address.
- [Fee Token Introspection](/protocol/tips/tip-1007): Addition of fee token introspection functionality to the FeeManager precompile, enabling smart contracts to query the fee token being used for the current transaction.
- [Expiring Nonces](/protocol/tips/tip-1009): Time-bounded replay protection using transaction hashes instead of sequential nonce management.
- [Mainnet Gas Parameters](/protocol/tips/tip-1010): Initial gas parameters for Tempo mainnet launch including base fee pricing, payment lane capacity, and transaction gas limits.
- [Enhanced Access Key Permissions](/protocol/tips/tip-1011): Extends Access Keys with periodic spending limits, destination/function scoping, and limited calldata recipient scoping.
- [Compound Transfer Policies](/protocol/tips/tip-1015): Extends TIP-403 with compound policies that specify different authorization rules for senders and recipients.
- [Exempt Storage Creation from Gas Limits](/protocol/tips/tip-1016): Storage creation gas costs are charged but don't count against transaction or block gas limits, using a reservoir model aligned with EIP-8037 for correct GAS opcode semantics and EVM compatibility.
- [Validator Config V2 precompile](/protocol/tips/tip-1017): Validator Config V2 precompile for improved management of consensus participants
- [Signature Verification Precompile](/protocol/tips/tip-1020): A precompile for verifying Tempo signatures onchain.
- [Virtual Addresses for TIP-20 Deposit Forwarding](/protocol/tips/tip-1022): Precompile-native virtual addresses that auto-forward TIP-20 deposits to a registered master wallet, eliminating sweep transactions.
- [Embed consensus context in the block Header](/protocol/tips/tip-1031): Embed consensus context into the block header
- [T2 Hardfork Bug Fixes](/protocol/tips/tip-1036): Meta TIP collecting all audit-driven bug fixes and hardening changes gated behind the T2 hardfork.
- [Account Keychain Precompile](/protocol/transactions/AccountKeychain): Technical specification for the Account Keychain precompile managing access keys with expiry timestamps and per-token spending limits.
- [EIP-4337 Comparison](/protocol/transactions/eip-4337): How Tempo Transactions achieve EIP-4337 goals without bundlers, paymasters, or EntryPoint contracts.
- [EIP-7702 Comparison](/protocol/transactions/eip-7702): How Tempo Transactions extend EIP-7702 delegation with additional signature schemes and native features.
- [Tempo Transaction](/protocol/transactions/spec-tempo-transaction): Technical specification for the Tempo transaction type (EIP-2718) with WebAuthn signatures, parallelizable nonces, gas sponsorship, and batching.
- [T2 Network Upgrade](/protocol/upgrades/t2): Details and timeline for the T2 network upgrade including compound transfer policies, Validator Config V2, permit support for TIP-20, and audit-driven bug fixes.
- [T3 Network Upgrade](/protocol/upgrades/t3): Details and timeline for the T3 network upgrade, including enhanced access keys, signature verification, virtual addresses, and security hardening and gas correctness fixes.
- [Pay for Agent-to-Agent Services](/guide/machine-payments/use-cases/agent-to-agent): Hire agents for coding, design, writing, and email with Auto.exchange and AgentMail via MPP stablecoin payments on Tempo.
- [Pay for AI Models Per Request](/guide/machine-payments/use-cases/ai-model-access): Let your agents call OpenAI, Anthropic, Gemini, Mistral, and other LLMs without API keys using MPP stablecoin payments on Tempo.
- [Pay for Blockchain Data and Analytics](/guide/machine-payments/use-cases/blockchain-data): Query on-chain data from Alchemy, Allium, Nansen, Dune, and Codex using MPP stablecoin payments on Tempo — no API keys required.
- [Pay for Browser Automation and Web Scraping](/guide/machine-payments/use-cases/browser-automation): Run headless browsers, solve CAPTCHAs, and scrape web pages using Browserbase, 2Captcha, and Oxylabs via MPP on Tempo.
- [Pay for Compute and Code Execution](/guide/machine-payments/use-cases/compute-and-code-execution): Run code, deploy containers, and access GPU compute via MPP with stablecoin payments on Tempo — no cloud accounts needed.
- [Pay for Data Enrichment and Lead Generation](/guide/machine-payments/use-cases/data-enrichment-and-leads): Enrich contacts, find emails, profile companies, and generate leads using Apollo, Hunter, Clado, and more via MPP on Tempo.
- [Pay for Financial and Market Data](/guide/machine-payments/use-cases/financial-data): Access stock prices, forex rates, SEC filings, crypto data, and economic indicators via MPP with stablecoin payments on Tempo.
- [Pay for Image, Video, and Audio Generation](/guide/machine-payments/use-cases/image-and-media-generation): Generate images, videos, audio, and speech with fal.ai, OpenAI, Gemini, and Deepgram via MPP stablecoin payments on Tempo.
- [Pay for Maps, Geocoding, and Location Data](/guide/machine-payments/use-cases/location-and-maps): Access Google Maps, Mapbox, weather, and flight data via MPP with stablecoin payments on Tempo — no API keys required.
- [Monetize Your API with Agentic Payments](/guide/machine-payments/use-cases/monetize-your-api): Accept stablecoin payments for your API using MPP on Tempo. Charge per request without requiring signups, billing accounts, or API keys.
- [Pay for Object Storage and Git Repos](/guide/machine-payments/use-cases/storage): Store files and create Git repositories using MPP with stablecoin payments on Tempo — no cloud accounts required.
- [Pay for Translation and Language Services](/guide/machine-payments/use-cases/translation-and-language): Translate text, transcribe audio, and process language using DeepL, Deepgram, and other MPP services with stablecoin payments on Tempo.
- [Pay for Web Search and Research](/guide/machine-payments/use-cases/web-search-and-research): Let agents search the web, extract content, and crawl pages using MPP services like Parallel, Exa, Brave, and Firecrawl with stablecoin payments.
- [Setup](/sdk/typescript/prool/setup): Set up infinite pooled Tempo node instances in TypeScript with Prool for testing and local development of blockchain applications.
-->

# ValidatorConfig V2

## Abstract

TIP-1017 defines ValidatorConfig V2, a new precompile for managing consensus participants. V2 improves lifecycle tracking so validator sets can be reconstructed for any epoch, and adds stricter input validation. It is designed to safely support permissionless validator rotation, and additionally allows separation of fee custody from day-to-day validator operations.

## Necessary background information

In Tempo, validator information is stored on-chain. This includes which nodes
make up the current committee, which nodes are intended to join or leave the
committee, and their network information (ingress, egress).

Each validator is uniquely identified by its ed25519 public key used for signing
all consensus p2p messages. For consensus itself, Tempo employs
bls12381 threshold cryptography, where each validator is assigned a private key
share corresponding to a section of the network public key. The network key itself
is undergoing a re-sharing Distributed Key Generation process every epoch, where
each epoch runs for a fixed number of blocks. The outcome of the DKG process is
written to last block of an epoch.

The DKG outcome contains the validators that made up the committee in epoch
`E-1` (called dealers), the validators that will make up the committee in `E`
(called players during `E-1`), and the validators that will participate as players
in the DKG process during epoch `E` to become committee members in `E+1`.

To determine the next players, validators read the contract state at the end
of the epoch and select all entries marked as active. The DKG outcome hence
determines who the committee members *are*, and the contract states who the
committee members *should be*.

## Motivation

The original ValidatorConfig precompile (frequently referred to V1 from here on),
was too permissive. It allowed addresses to arbitrarily change the values of
their entry in the contract, potentially breaking consensus. This and other
issues were:

1. **Key ownership verification**: V1 does not verify that the caller controls
   the private key corresponding to the public key being registered. A malicious
   validator could hence grief another validator by using their public key,
   breaking the consensus requirement that all keys be unique.
2. **Validator re-registration**: V1 allows deleted validators to be re-added
   with different parameters, complicating historical queries.
3. **Historical state dependency**: Because V1 contained a warmup epoch for new
   validators, and because these were not written to the DKG outcome, to sync
   a node always needed to keep up to twice the epoch length of blocks around,
   requiring bloated snapshots and preventing aggressive pruning.

Tempo solved problems 1 and 2 by assigning validators entries anonymous addresses.
Thus, only the contract owner could change or deactivate entries.

### How V2 solves these problems:

* ed25519 signature verification proves key ownership at registration time
* fields `addedAtHeight` and `deactivatedAtHeight` are controlled by the contract and
  cannot be mutated by the owner and allow historical state reconstruction.
* Public keys remain reserved forever (even after deactivation)
* Addresses are unique among current validators but can be reassigned via `transferValidatorOwnership`

# Specification

## Precompile Address

```solidity
address constant VALIDATOR_CONFIG_V2_ADDRESS = 0xCCCCCCCC00000000000000000000000000000001;
```

## Interface

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

/// @title IValidatorConfigV2 - Validator Config V2 Precompile Interface
/// @notice Interface for managing consensus validators with append-only, deactivate-once semantics
interface IValidatorConfigV2 {

    /// @notice Caller is not authorized.
    error Unauthorized();

    /// @notice Active validator address already exists.
    error AddressAlreadyHasValidator();

    /// @notice Public key already exists.
    error PublicKeyAlreadyExists();

    /// @notice Validator was not found.
    error ValidatorNotFound();

    /// @notice Validator is already deactivated.
    error ValidatorAlreadyDeactivated();

    /// @notice Public key is invalid.
    error InvalidPublicKey();

    /// @notice Validator address is invalid.
    error InvalidValidatorAddress();

    /// @notice Ed25519 signature verification failed.
    error InvalidSignature();

    /// @notice Contract is not initialized.
    error NotInitialized();

    /// @notice Contract is already initialized.
    error AlreadyInitialized();

    /// @notice Migration is not complete.
    error MigrationNotComplete();

    /// @notice V1 has no validators to migrate.
    error EmptyV1ValidatorSet();

    /// @notice Migration index is out of order.
    error InvalidMigrationIndex();

    /// @notice Address is not in valid `IP:port` format.
    /// @param input Invalid input.
    /// @param backtrace Additional error context.
    error NotIpPort(string input, string backtrace);

    /// @notice Address is not a valid IP address.
    /// @param input Invalid input.
    /// @param backtrace Additional error context.
    error NotIp(string input, string backtrace);

    /// @notice Ingress IP is already in use by an active validator.
    /// @param ingress Conflicting ingress address.
    error IngressAlreadyExists(string ingress);

    /// @notice Validator information
    /// @param publicKey Ed25519 communication public key.
    /// @param validatorAddress Validator address.
    /// @param ingress Inbound address in `<ip>:<port>` format.
    /// @param egress Outbound address in `<ip>` format.
    /// @param index Immutable validators-array position.
    /// @param addedAtHeight Block height when entry was added.
    /// @param deactivatedAtHeight Block height when entry was deactivated (`0` if active).
    /// @param feeRecipient The fee recipient the node will set when proposing blocks as a leader.
    struct Validator {
        bytes32 publicKey;
        address validatorAddress;
        string ingress;
        string egress;
        uint64 index;
        uint64 addedAtHeight;
        uint64 deactivatedAtHeight;
        address feeRecipient;
    }

    /// @notice Get active validators.
    /// @return validators Active validators (`deactivatedAtHeight == 0`).
    function getActiveValidators() external view returns (Validator[] memory validators);

    /// @notice Get contract owner.
    /// @return Owner address.
    function owner() external view returns (address);

    /// @notice Get total validators, including deactivated entries.
    /// @return count Validator count.
    function validatorCount() external view returns (uint64);

    /// @notice Get validator by array index.
    /// @param index Validators-array index.
    /// @return validator Validator at `index`.
    function validatorByIndex(uint64 index) external view returns (Validator memory);

    /// @notice Get validator by address.
    /// @param validatorAddress Validator address.
    /// @return validator Validator for `validatorAddress`.
    function validatorByAddress(address validatorAddress) external view returns (Validator memory);

    /// @notice Get validator by public key.
    /// @param publicKey Ed25519 public key.
    /// @return validator Validator for `publicKey`.
    function validatorByPublicKey(bytes32 publicKey) external view returns (Validator memory);

    /// @notice Get next epoch configured for a fresh DKG ceremony.
    /// @return epoch Epoch number, or `0` if none is scheduled.
    function getNextFullDkgCeremony() external view returns (uint64);

    /// @notice Add a new validator (owner only)
    /// @dev Requires Ed25519 signature over a unique digest generated from inputs. 
    /// @param validatorAddress New validator address.
    /// @param publicKey Validator Ed25519 communication public key.
    /// @param ingress Inbound address `<ip>:<port>`.
    /// @param egress Outbound address `<ip>`.
    /// @param feeRecipient The fee recipient the validator sets when proposing.
    /// @param signature Ed25519 signature proving key ownership.
    function addValidator(
        address validatorAddress,
        bytes32 publicKey,
        string calldata ingress,
        string calldata egress,
        address feeRecipient,
        bytes calldata signature
    ) external returns (uint64);

    /// @notice Deactivate a validator (owner or validator only).
    /// @dev Sets `deactivatedAtHeight` to current block height.
    /// @param idx Validator index.
    function deactivateValidator(uint64 idx) external;

    /// @notice Rotate a validator to a new identity (owner or validator only).
    /// @dev Preserves index stability by appending a copy of the existing entry and updating the entry in-place.
    /// @param idx Validator index to rotate.
    /// @param publicKey New Ed25519 communication public key.
    /// @param ingress New inbound address `<ip>:<port>`. Must be different from the rotated-out validator (changing port is enough).
    /// @param egress New outbound address `<ip>`.
    /// @param signature Ed25519 signature proving new key ownership.
    function rotateValidator(
        uint64 idx,
        bytes32 publicKey,
        string calldata ingress,
        string calldata egress,
        bytes calldata signature
    ) external;

    /// @notice Update validator IP addresses (owner or validator only).
    /// @param idx Validator index.
    /// @param ingress New inbound address `<ip>:<port>`.
    /// @param egress New outbound address `<ip>`.
    function setIpAddresses(
        uint64 idx,
        string calldata ingress,
        string calldata egress
    ) external;

    /// @notice Update validator fee recipient (owner or validator only).
    /// @param idx Validator index.
    /// @param feeRecipient New fee recipient.
    function setFeeRecipient(
        uint64 idx,
        address feeRecipient
    ) external;

    /// @notice Transfer validator entry to a new address (owner or validator only).
    /// @dev Reverts if `newAddress` conflicts with an active validator.
    /// @param idx Validator index.
    /// @param newAddress New validator address.
    function transferValidatorOwnership(uint64 idx, address newAddress) external;

    /// @notice Transfer contract ownership (owner only).
    /// @param newOwner New owner address.
    function transferOwnership(address newOwner) external;

    /// @notice Set next fresh DKG ceremony epoch (owner only).
    /// @param epoch Epoch where ceremony runs (`epoch + 1` uses new polynomial).
    function setNextFullDkgCeremony(uint64 epoch) external;

    /// @notice Migrate one validator by V1 index (owner only).
    /// @param idx V1 validator index.
    function migrateValidator(uint64 idx) external;

    /// @notice Initialize V2 and enable reads (owner only).
    /// @dev Requires all V1 indices to be processed.
    function initializeIfMigrated() external;

    /// @notice Check initialization state.
    /// @return initialized True if initialized.
    function isInitialized() external view returns (bool);

    /// @notice Get initialization block height.
    /// @return height Initialization height (`0` if not initialized).
    function getInitializedAtHeight() external view returns (uint64);
}
```

## Overview

* Migration incrementally reads and copies validator entries from V1 into V2.
* During migration, the consensus layer continues reading V1 until `initializeIfMigrated()` completes.
* Validator history are append-only, and deactivation is one-way.
* Historical validator sets are reconstructed from `addedAtHeight` and `deactivatedAtHeight`.
* Validator `index` is stable for the lifetime of an entry.
* Writes for post-migration operations are gated by `isInitialized()`.

## State Model

V2 stores validators in one append-only array, with lookup indexes by address and public key.

* `addedAtHeight`: block height where the entry becomes visible to CL epoch filtering.
* `deactivatedAtHeight`: `0` means active; non-zero marks irreversible deactivation.
* `index`: immutable array position assigned at creation.
* `initialized`: one-way migration flag toggled by `initializeIfMigrated()`.

### Fee Recipient Separation

Each validator entry includes a `feeRecipient` that can differ from the validator's control address. This enables operators to route protocol fees to a dedicated treasury wallet, while retaining a separate validator or treasury-ops multisig for operational calls. This separation reduces blast radius during key compromise: operational key exposure does not cause historically collected fees held by the custody wallet to be lost.

## Operation Semantics

### Lifecycle Operations

* `addValidator`: appends a new active entry after validation and signature verification.
* `deactivateValidator`: marks an existing active entry as deactivated at current block height.
* `rotateValidator`: to keep `index` stable, this updates the active entry in place and appends the entry to be deactivated. Active validator count is unchanged.

### Network And Ownership Operations

* `setIpAddresses`: updates ingress and egress for an active validator, enforcing address format and ingress uniqueness among active entries.
* `setFeeRecipient`: updates the destination address that receives network fees from block proposing.
* `transferValidatorOwnership`: rebinds a validator entry to a new address provided the address is not used by another active entry.

### Migration And Phase-Gating Operations

* `migrateValidator`: copies one V1 entry into V2 in descending index order.
* `initializeIfMigrated`: switches V2 to initialized state after all V1 indices have been processed.
* Mutators are phase-gated: migration mutators are blocked after init, and post-init mutators are blocked before init.

### Input Validation And Safety Checks

ValidatorConfig V2 enforces the following checks:

1. Validator ed25519 public keys must be unique across all validators (active and inactive).
2. Validator addresses must be unique across active validators.
3. `ingress` must be a valid `IP:port`, and unique across active validators.
4. `egress` must be a valid IP.
5. `addValidator` and `rotateValidator` require a signature from the Ed25519 key being installed.

### Ed25519 Signature Verification

When adding or rotating a validator, the caller must provide an Ed25519 signature proving ownership of the public key.

**Namespace:** `addValidator` uses `b"TEMPO_VALIDATOR_CONFIG_V2_ADD_VALIDATOR"` and `rotateValidator` uses `b"TEMPO_VALIDATOR_CONFIG_V2_ROTATE_VALIDATOR"`.

**Messages:**

```
addValidatorMessage = keccak256(
    bytes8(chainId)             // uint64: Prevents cross-chain replay
    || contractAddress          // address: Prevents cross-contract replay
    || validatorAddress         // address: Binds to specific validator address
    || uint8(ingress.length)    // uint8: Length of ingress
    || ingress                  // string: Binds network configuration
    || uint8(egress.length)     // uint8: Length of egress
    || egress                   // string: Binds network configuration
    || feeRecipient             // address: Binds fee recipients when proposing.
)

rotateValidatorMessage = keccak256(
    bytes8(chainId)             // uint64: Prevents cross-chain replay
    || contractAddress          // address: Prevents cross-contract replay
    || validatorAddress         // address: Binds to specific validator address
    || uint8(ingress.length)    // uint8: Length of ingress
    || ingress                  // string: Binds network configuration
    || uint8(egress.length)     // uint8: Length of egress
    || egress                   // string: Binds network configuration
)
```

The Ed25519 signature is computed over the operation-specific message with the namespace parameter (see commonware's [signing scheme](https://github.com/commonwarexyz/monorepo/blob/abb883b4a8b42b362d4003b510bd644361eb3953/cryptography/src/ed25519/scheme.rs#L38-L40) and [union format](https://github.com/commonwarexyz/monorepo/blob/abb883b4a8b42b362d4003b510bd644361eb3953/utils/src/lib.rs#L166-L174)).

## Compatibility And Upgrade Behavior

### Changes From V1

1. V2 preserves append-only history with irreversible deactivation instead of mutable active/inactive toggling.
2. V2 enforces stronger input checks in the precompile, including signature-backed key ownership.
3. V2 keeps validator index stable across lifecycle operations.

### Consensus Layer Read Behavior

The Consensus Layer checks `v2.isInitialized()` to determine which contract to read:

* **`initialized == false`**: CL reads from V1.
* **`initialized == true`**: CL reads from V2.

This read switch is implemented in CL logic. V2 does not proxy reads to V1.

## Consensus Layer Integration

**IP address changes**: `setIpAddresses` is expected to take effect in CL peer configuration on the next finalized block.

**Validator addition and deactivation**: there is no warmup or cooldown in V2. Added validators are added to the DKG player set on the next epoch; deactivated validators leave on the next epoch.
(both in the case of successful DKG rounds; on failure DKG still falls back to its previous state, which might include validators that are marked inactive as per the contract).

**Fee recipients**: Fee recipients are included now to be used in the future in a not yet determined hardfork.

### DKG Player Selection

The consensus layer determines DKG players for epoch `E+1` by reading state at `boundary(E) - 1` and filtering:

```
players(E+1) = validators.filter(v =>
    v.addedAtHeight < boundary(E) &&
    (v.deactivatedAtHeight == 0 || v.deactivatedAtHeight >= boundary(E))
)
```

This enables node recovery and late joining without historical account state.

## Migration from V1

On networks that start directly with V2 (no V1 state), `initializeIfMigrated` can be called immediately when the V1 validator count is zero.

Because `SSTORE` cost is high under TIP-1000, migration is done one validator at a time to reduce out-of-gas risk on large sets.

### Full Migration Steps

1. At fork activation, the V2 precompile goes live. However, CL continues reading from the V1 precompile.
2. The owner calls `migrateValidator(n-1)` with `n` being the validator count in the V1 precompile.
3. On the first migration call, V2 copies owner from V1 if unset and snapshots the V1 validator count, then continues in descending index order. The snapshotted count is used for all subsequent index validation to prevent V1 mutations from breaking migration ordering.
4. After all indices are processed, owner calls `initializeIfMigrated()`, which flips `initialized` and activates CL reads from V2.

### Migration Edge Cases

1. **Validator goes offline during migration**: admin deactivates the validator in V1. If that index has already been migrated the admin will also deactivates it in V2.
2. **Invalid V1 entry encountered**: the index is still marked as processed (migrated, overwritten, or skipped by implementation rules) so global completion is not blocked.
3. **Zero validators in V1**: The migration flow requires at least 1 validator to be in the V1 precompile.
4. **Validator count overflow**: We use uint8s to cache the number of V1 validators and the number of skipped V1 validators. V1 currently has 14 validators, so a limit of 255 is sufficient.

### Permitted Calls During Migration

| Contract | Caller | Allowed calls |
| --- | --- | --- |
| V2 (pre-init) | owner | `deactivateValidator`, `migrateValidator`, `initializeIfMigrated` |
| V2 (pre-init) | validator | `deactivateValidator` (theoretically; in Tempo these addresses are unowned) |
| V2 (post-init) | any | `migrateValidator` and `initializeIfMigrated` are blocked |
| V1 (during migration window) | owner and validators | all V1 calls remain available (subject to V1 authorization and key ownership assumptions) |

# Security

## Considerations

* **Migration timing**: migration and `initializeIfMigrated()` should complete before an epoch boundary to avoid DKG disruption.
* **Pre-migration validation**: admins should run a validation script against V1 state to detect entries that would fail V2 checks.
* **State parity before init**: admins should verify V1/V2 state consistency before finalizing with `initializeIfMigrated()`.
* **Signature domain separation**: signatures for `addValidator` and `rotateValidator` are bound to chain ID, precompile address, namespace, validator address, and endpoint payload.

## Race And Griefing Risks

* Stable `index` values prevent races between concurrent state-changing calls.
* Append-only history and permissionless rotation require query paths that remain safe as history grows.

## Testing Requirements

Unit tests should cover all control-flow branches in added functions, including initialization gating, migration completion checks, and index-based query behavior under large validator sets.

## Invariants

### Identity and Uniqueness

1. **Unique active addresses**: No two active validators share the same `validatorAddress`. Deactivated addresses may be reused.
2. **Unique public keys**: No two validators (including deactivated) share the same `publicKey`.
3. **Ingress uniqueness across active validators**: In `getActiveValidators()`, no two validators share the same ingress `<IP>:<port>`.
4. **Valid public keys**: All validators must have valid ed25519 `publicKey`s.

### Lifecycle and Storage Behavior

1. **Append-only validator array**: `validatorsArray` length can only increase.
2. **Entry index immutability**: Once a validator entry is created at index `i`, that entry can never move to another index. A previously deactivated operator may later be re-added as a new entry at a different index.
3. **Deactivate-once**: `deactivatedAtHeight` can only transition from 0 to a non-zero value, never back.
4. **Add increases exactly one entry**: A successful `addValidator` call increases `getActiveValidators().length` by exactly one.
5. **Rotation preserves active cardinality**: A successful `rotateValidator` call does not change `getActiveValidators().length`.
6. **Deactivation decrements active cardinality by one**: A successful `deactivateValidator` call decreases `getActiveValidators().length` by exactly one.

### Query Correctness

1. **Full-set reconstruction by index**: Reading `validatorByIndex(i)` for all `i` in `0..validatorCount()-1` must reconstruct exactly the ordered validator set.
2. **Validator activity consistency**: Filtering the reconstructed validator set by `deactivatedAtHeight == 0` must produce exactly `getActiveValidators()` (same members, order not important).
3. **Address round-trip for active entries**: For any `i` where `validatorByIndex(i).deactivatedAtHeight == 0`, `validatorByAddress(validatorByIndex(i).validatorAddress).index == i`.
4. **Public-key round-trip for all entries**: For any `i < validatorCount()`, `validatorByPublicKey(validatorByIndex(i).publicKey).index == i`.
5. **Index round-trip for all entries**: For any `i < validatorCount()`, `validatorByIndex(i).index == i`.

### Migration and Initialization

1. **Initialization phase gating**: Before initialization, post-init mutators are blocked; after initialization, migration mutators are blocked.
2. **Initialized once**: The `initialized` flag can only transition from `false` to `true`, never back.
3. **Migration completion gate**: Each V1 index must be processed exactly once (migrated or skipped), and `initializeIfMigrated()` stays blocked until all indices are processed.
4. **Skipped-index counter monotonicity**: `migrationSkippedCount` is monotonically non-decreasing and may only change during `migrateValidator`.
5. **DKG continuity at initialization**: On successful `initializeIfMigrated`, `getNextFullDkgCeremony()` in V2 equals the value read from V1 at that moment.
6. **Owner bootstrap during migration**: If V2 owner is unset on first migration call, owner is copied from V1 exactly once and then used for all migration authorization checks.
