<!--
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.
- [Connect to Tempo Zones](/guide/private-zones/): Learn how Tempo Zones work alongside the public chain and follow guides for depositing, sending within a zone, routing pathUSD across zones, swapping into betaUSD, and withdrawing.
- [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 RPC Reference](/protocol/rpc/): Reference for Tempo-specific JSON-RPC methods in the tempo, consensus, and admin namespaces, plus modified eth_ behavior.
- [Tempo Improvement Proposals (TIPs)](/protocol/tips/): Browse all Tempo Improvement Proposals (TIPs) — design documents that specify protocol changes and serve as the source of truth for implementation.
- [Tempo Transactions](/protocol/transactions/): Learn about Tempo Transactions, a new EIP-2718 transaction type with passkey support, fee sponsorship, batching, and concurrent execution.
- [Tempo Zones](/protocol/zones/): Tempo Zones are private execution environments on Tempo Mainnet where balances, transfers, and transaction history are invisible to the public chain.
- [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): Query token metadata, icons, and prices on Tempo using the Uniswap Token Lists-compatible API, and submit new tokens via PR to the registry.
- [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 Foundry. Access Tempo protocol features with forge, cast, anvil, and chisel.
- [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): Fills missing transaction fields and returns wallet-aware metadata.
- [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 (Deprecated)](/accounts/server/handler.feePayer): Deprecated — use Handler.relay with feePayer option instead.
- [Handler.relay](/accounts/server/handler.relay): Server handler that proxies certain RPC requests with wallet-aware enrichment.
- [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.
- [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.
- [Node Security](/guide/node/security): Security best practices for Tempo node operators, covering key management, network configuration, release verification, and data integrity.
- [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.
- [Upgrade Cadence](/guide/node/upgrade-cadence): How Tempo schedules and communicates network upgrades, including timelines, notification windows, and what to expect as a node operator.
- [Running a validator node](/guide/node/validator): Overview of running a Tempo validator node.
- [Managing validator keys](/guide/node/validator-keys): Generate, rotate, and recover validator signing keys and shares.
- [Controlling validator lifecycle](/guide/node/validator-lifecycle): Start, stop, register, rotate, deactivate, and transfer ownership of your Tempo validator.
- [Monitoring a validator](/guide/node/validator-monitoring): Monitor validator health with metrics, Grafana dashboards, and log management.
- [Validator Onboarding](/guide/node/validator-setup): Generate signing keys and run your Tempo validator node for the first time.
- [Checking validator status](/guide/node/validator-status): Understand validator state transitions, check your validator's participation status, and query on-chain status.
- [Troubleshooting and FAQ](/guide/node/validator-troubleshooting): Common issues and solutions for Tempo validator operators.
- [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): Attach 32-byte reference memos to TIP-20 transfers for payment reconciliation, invoice matching, and exchange deposit tracking on Tempo.
- [Use virtual addresses for deposits](/guide/payments/virtual-addresses): Register a virtual-address master, derive deposit addresses offchain, and watch TIP-20 deposits land directly in the registered wallet.
- [Connect to a Zone](/guide/private-zones/connect-to-a-zone): Connect to Tempo Zones on testnet using Zone A and Zone B RPC URLs, chain IDs, and a minimal viem client setup for private flows.
- [Deposit to a Zone](/guide/private-zones/deposit-to-a-zone): Deposit pathUSD from your public-chain balance into Zone A and confirm the resulting zone balance.
- [Send tokens across zones](/guide/private-zones/send-tokens-across-zones): Send pathUSD from Zone A into Zone B by routing a same-token withdrawal through Tempo's L1 router and confirming the target deposit.
- [Send tokens within a zone](/guide/private-zones/send-tokens-within-a-zone): Send pathUSD inside Zone A with a signed zone transfer and confirm the updated zone balance.
- [Swap stablecoins across zones](/guide/private-zones/swap-across-zones): Swap pathUSD from Zone A into betaUSD on Zone B by routing a zone withdrawal through Tempo's L1 router and confirming the target deposit.
- [Withdraw from a Zone](/guide/private-zones/withdraw-from-a-zone): Withdraw pathUSD from Zone A back to your public-chain balance with a direct zone outbox withdrawal.
- [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.
- [Authorize access keys](/guide/use-accounts/authorize-access-keys): Authorize access keys on Tempo. Use a secondary signing key to send transactions without repeated passkey prompts, with spending limits and expiry for security.
- [Batch Transactions](/guide/use-accounts/batch-transactions): Execute multiple operations atomically in a single Tempo Transaction using native protocol-level batching with one signature and lower gas costs.
- [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): Schedule Tempo transactions to execute within a specific time window using validAfter and validBefore timestamps for vesting, offers, and delayed execution.
- [WebAuthn & P256 Signatures](/guide/use-accounts/webauthn-p256-signatures): Sign Tempo transactions with passkeys, Face ID, Touch ID, or hardware security keys using native WebAuthn and P256 signature support on EOA accounts.
- [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.
- [Virtual addresses for TIP-20 deposits](/protocol/tip20/virtual-addresses): Understand how TIP-20 virtual addresses work, why they remove sweep transactions, and how to attribute forwarded deposits on Tempo.
- [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.
- [Allow same-tick flip orders](/protocol/tips/tip-1030): Relaxes the placeFlip validation to allow flipTick to equal tick, enabling flip orders that flip to the same price.
- [Embed consensus context in the block Header](/protocol/tips/tip-1031): Embed consensus context into the block header
- [Implicit Approval List](/protocol/tips/tip-1035): Defines an in-protocol list of precompiles that may call system_transfer_from to pull TIP-20 tokens without requiring a prior approve() call.
- [T2 Hardfork Bug Fixes](/protocol/tips/tip-1036): Meta TIP collecting all audit-driven bug fixes and hardening changes gated behind the T2 hardfork.
- [T3 Hardfork Meta TIP](/protocol/tips/tip-1038): Meta TIP collecting all bug fixes, security hardening, and gas correctness changes gated behind the T3 hardfork.
- [T4 Hardfork Meta TIP](/protocol/tips/tip-1046): Meta TIP collecting all bug fixes and security hardening changes gated behind the T4 hardfork.
- [Revert code creation and set code at addresses with TIP-20 prefix](/protocol/tips/tip-1047): Reject contract creation and EIP-7702 delegations that produce addresses in the TIP-20 reserved prefix range
- [Keep the same order ID when flip orders flip](/protocol/tips/tip-1056): Reuses the same order ID when a flip order flips and emits OrderFlipped instead of creating a new order ID and emitting OrderPlaced.
- [Account Keychain Precompile](/protocol/transactions/AccountKeychain): Technical specification for the Account Keychain precompile managing access keys with expiry timestamps, spending limits, and call-scope restrictions.
- [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, and virtual addresses.
- [T4 Network Upgrade](/protocol/upgrades/t4): Details and timeline for the T4 network upgrade, including consensus context in the block header and bundled bug fixes.
- [Accounts](/protocol/zones/accounts): Account-scoped access control on Tempo Zones, including private balances, private allowances, and the two-layer privacy model.
- [Tempo Zone Architecture](/protocol/zones/architecture): Architecture of Tempo Zones, including contract layout, sequencer management, chain IDs, and the trust model.
- [Zone Bridging](/protocol/zones/bridging): Deposit and withdraw TIP-20 tokens between Tempo Mainnet and Tempo Zones, including encrypted deposits and composable withdrawal callbacks.
- [Execution & Gas](/protocol/zones/execution): Specification for gas accounting, fee tokens, fixed TIP-20 gas costs, contract creation limits, and token management on Tempo Zones.
- [Zone Proving](/protocol/zones/proving): Batch submission and proof verification for Tempo zones, including the state transition function, ZK and TEE deployment modes, and ancestry proofs.
- [Zone RPC](/protocol/zones/rpc): Authenticated JSON-RPC interface for Tempo Zones with per-account scoping, timing side channel mitigations, and event filtering.
- [Foundry Integration](/sdk/foundry/mpp): Use Foundry tools (cast, forge, anvil, chisel) with MPP-gated RPC endpoints on Tempo — automatic 402 handling with zero config.
- [Signature Verification in Foundry](/sdk/foundry/signature-verifier): Verify secp256k1, P256, and WebAuthn signatures in smart contracts using the TIP-1020 SignatureVerifier precompile with Foundry.
- [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.
-->

# Tempo Transaction

## Abstract

This spec introduces native protocol support for the following features, using a new Tempo transaction type:

* WebAuthn/P256 signature validation - enables passkey accounts
* Parallelizable nonces - allows higher tx throughput for each account
* Gas sponsorship - allows apps to pay for their users' transactions
* Call Batching - allows users to multicall efficiently and atomically
* Scheduled Txs - allow users to specify a time window in which their tx can be executed
* Access Keys - allow a sender's key to provision scoped access keys with spending limits

## Motivation

Current accounts are limited to secp256k1 signatures and sequential nonces, creating UX and scalability challenges.\
Users cannot leverage modern authentication methods like passkeys, applications face throughput limitations due to sequential nonces.

## Specification

### Transaction Type

A new EIP-2718 transaction type is introduced with type byte `0x76`:

```rust
pub struct TempoTransaction {
    // Standard EIP-1559 fields
    chain_id: ChainId,                          // EIP-155 replay protection
    max_priority_fee_per_gas: u128,
    max_fee_per_gas: u128,
    gas_limit: u64,
    calls: Vec<Call>,                           // Batch of calls to execute atomically
    access_list: AccessList,                    // EIP-2930 access list

    // nonce-related fields
    nonce_key: U256,                            // 2D nonce key (0 = protocol nonce, >0 = user nonces)
    nonce: u64,                                 // Current nonce value for the nonce key

    // Optional features
    fee_token: Option<Address>,                 // Optional fee token preference
    fee_payer_signature: Option<Signature>,     // Sponsored transactions (secp256k1 only)
    valid_before: Option<u64>,                  // Transaction expiration timestamp (seconds)
    valid_after: Option<u64>,                   // Transaction can only be included after this timestamp (seconds)
    key_authorization: Option<SignedKeyAuthorization>, // Access key authorization (optional)
    aa_authorization_list: Vec<TempoSignedAuthorization>, // EIP-7702 style authorizations with AA signatures
}

// Call structure for batching
pub struct Call {
    to: TxKind,      // Can be Address or Create
    value: U256,
    input: Bytes     // Calldata for the call
}

// Key authorization for provisioning access keys
// RLP encoding: [chain_id, key_type, key_id, expiry?, limits?, allowed_calls?]
pub struct KeyAuthorization {
    chain_id: u64,                              // Chain ID for replay protection (0 = valid on any chain)
    key_type: SignatureType,                    // Type of key: Secp256k1 (0), P256 (1), or WebAuthn (2)
    key_id: Address,                            // Key identifier (address derived from public key)
    expiry: Option<u64>,                        // Unix timestamp when key expires (omit / None for never expires)
    limits: Option<Vec<TokenLimit>>,            // TIP20 spending limits (None = unlimited spending)
    allowed_calls: Option<Vec<CallScope>>,      // Call-scope allowlist (None = unrestricted; Some(empty) = scoped deny-all)
}

// Signed key authorization (authorization + root key signature)
pub struct SignedKeyAuthorization {
    authorization: KeyAuthorization,
    signature: PrimitiveSignature,              // Root key's signature over keccak256(rlp(authorization))
}

// TIP20 spending limit for access keys
pub struct TokenLimit {
    token: Address,                             // TIP20 token address
    limit: U256,                                // Maximum spending amount for this token
    period: u64,                                // Recurring period in seconds (0 = one-time, non-zero = recurring)
}

// Call-scope allowlist entry: a target contract and its allowed selector rules
pub struct CallScope {
    target: Address,                            // Target contract address
    selector_rules: Vec<SelectorRule>,          // Allowed selectors on that target (empty = any selector allowed)
}

// Selector rule: a function selector and optional recipient allowlist (for recipient-bound TIP-20 selectors)
pub struct SelectorRule {
    selector: [u8; 4],                          // 4-byte function selector
    recipients: Vec<Address>,                   // Allowed recipients (empty = any recipient)
}
```

### Signature Types

Four signature schemes are supported. The signature type is determined by length and type identifier:

#### secp256k1 (65 bytes)

```rust
pub struct Signature {
    r: B256,        // 32 bytes
    s: B256,        // 32 bytes
    v: u8           // 1 byte (recovery id)
}
```

**Format**: No type identifier prefix (backward compatible). Total length: 65 bytes.
**Detection**: Exactly 65 bytes with no type identifier.

#### P256 (130 bytes)

```rust
pub struct P256SignatureWithPreHash {
    typeId: u8,         // 0x01
    r: B256,            // 32 bytes
    s: B256,            // 32 bytes
    pub_key_x: B256,    // 32 bytes
    pub_key_y: B256,    // 32 bytes
    pre_hash: bool      // 1 byte
}
```

**Format**: Type identifier `0x01` + 129 bytes of signature data. Total length: 130 bytes. The `typeId` is a wire format prefix (not a struct field) prepended during encoding.

Note: Some P256 implementers (like Web Crypto) require the digests to be pre-hashed before verification.
If `pre_hash` is set to `true`, then before verification: `digest = sha256(digest)`.

#### WebAuthn (Variable length, max 2KB)

```rust
pub struct WebAuthnSignature {
    typeId: u8,                 // 0x02
    webauthn_data: Bytes,       // Variable length (authenticatorData || clientDataJSON)
    r: B256,                    // 32 bytes
    s: B256,                    // 32 bytes
    pub_key_x: B256,            // 32 bytes
    pub_key_y: B256             // 32 bytes
}
```

**Format**: Type identifier `0x02` + variable webauthn\_data + 128 bytes (r, s, pub\_key\_x, pub\_key\_y). Total length: variable (minimum 129 bytes, maximum 2049 bytes). The `typeId` is a wire format prefix prepended during encoding. Parse by working backwards: last 128 bytes are r, s, pub\_key\_x, pub\_key\_y.

#### Keychain (Variable length)

```rust
pub struct KeychainSignature {
    typeId: u8,                     // 0x03
    user_address: Address,          // 20 bytes - root account address
    signature: PrimitiveSignature   // Inner signature (Secp256k1, P256, or WebAuthn)
}
```

**Format**: Type identifier `0x03` + user\_address (20 bytes) + inner signature. The `typeId` is a wire format prefix prepended during encoding.
**Purpose**: Allows an access key to sign on behalf of a root account. The handler validates that `user_address` has authorized the access key in the AccountKeychain precompile.

### Address Derivation

#### secp256k1

```solidity
address(uint160(uint256(keccak256(abi.encode(x, y)))))
```

#### P256 and WebAuthn

```solidity
function deriveAddressFromP256(bytes32 pubKeyX, bytes32 pubKeyY) public pure returns (address) {    
    // Hash 
    bytes32 hash = keccak256(abi.encodePacked(
        pubKeyX,
        pubKeyY
    ));
    
    // Take last 20 bytes as address
    return address(uint160(uint256(hash)));
}
```

### Tempo Authorization List

The `aa_authorization_list` field enables EIP-7702 style delegation with support for all three AA signature types (secp256k1, P256, and WebAuthn), not just secp256k1.

#### Structure

```rust
pub struct TempoSignedAuthorization {
    inner: Authorization,      // Standard EIP-7702 authorization
    signature: TempoSignature,    // Can be Secp256k1, P256, or WebAuthn
}
```

Each authorization in the list:

* Delegates an account to a specified implementation contract
* Is signed by the account's authority using any supported signature type
* Follows EIP-7702 semantics for delegation and execution

#### Validation

* Cannot have `Create` calls when `aa_authorization_list` is non-empty (follows EIP-7702 semantics)
* Authority address is recovered from the signature and matched against the authorization

### Parallelizable Nonces

* **Protocol nonce (key 0)**: Existing account nonce, incremented for regular txs, 7702 authorization, or `CREATE`
* **User nonces (keys 1-N)**: Enable parallel execution with special gas schedule
* **Reserved sequence keys**: Nonce sequence keys with the most significant byte `0x5b` are reserved for protocol-managed validator sequencing.

#### Account State Changes

* `nonces: mapping(uint256 => uint64)` - 2D nonce tracking

**Implementation Note:** Nonces are stored in the storage of a designated precompile at address `0x4E4F4E4345000000000000000000000000000000` (ASCII hex for "NONCE"), as there is currently no clean way to extend account state in Reth.

**Storage Layout at 0x4E4F4E4345:**

* Storage key: `keccak256(abi.encode(account_address, nonce_key))`
* Storage value: `nonce` (uint64)

Note: Protocol Nonce key (0), is directly stored in the account state, just like normal transaction types.

#### Nonce Precompile

The nonce precompile implements the following interface for managing 2D nonces:

```solidity
/// @title INonce - Nonce Precompile Interface
/// @notice Interface for managing 2D nonces as per the Tempo Transaction spec
/// @dev This precompile manages user nonce keys (1-N) while protocol nonces (key 0)
///      are handled directly by account state. Each account can have multiple
///      independent nonce sequences identified by a nonce key.
interface INonce {
    /// @notice Emitted when a nonce is incremented for an account and nonce key
    /// @param account The account whose nonce was incremented
    /// @param nonceKey The nonce key that was incremented
    /// @param newNonce The new nonce value after incrementing
    event NonceIncremented(address indexed account, uint256 indexed nonceKey, uint64 newNonce);
    /// @notice Thrown when trying to access protocol nonce (key 0) through the precompile
    /// @dev Protocol nonce should be accessed through account state, not this precompile
    error ProtocolNonceNotSupported();
    /// @notice Thrown when an invalid nonce key is provided
    error InvalidNonceKey();
    /// @notice Thrown when a nonce value would overflow
    error NonceOverflow();
    /// @notice Get the current nonce for a specific account and nonce key
    /// @param account The account address
    /// @param nonceKey The nonce key (must be > 0, protocol nonce key 0 not supported)
    /// @return nonce The current nonce value
    function getNonce(address account, uint256 nonceKey) external view returns (uint64 nonce);
}
```

#### Precompile Implementation

The precompile contract maintains a single storage mapping:

```solidity
contract Nonce is INonce {
    /// @dev Mapping from account -> nonce key -> nonce value
    mapping(address => mapping(uint256 => uint64)) private nonces;
}
```

#### Gas Schedule

For transactions using nonce keys:

1. **Protocol nonce (key 0)**: No additional gas cost
   * Uses the standard account nonce stored in account state

2. **Existing user key (nonce > 0)**: Add 5,000 gas to base cost
   * Rationale: Cold SLOAD (2,100) + warm SSTORE reset (2,900)

3. **New user key (nonce == 0)**: Add 22,100 gas to base cost
   * Rationale: Cold SLOAD (2,100) + SSTORE set for 0 → non-zero (20,000)

We specify the complete gas schedule in more detail in the [gas costs section](#gas-costs)

### Transaction Validation

#### Signature Validation

1. Determine type from signature format:
   * 65 bytes (no type identifier) = secp256k1
   * First byte `0x01` + 129 bytes = P256 (total 130 bytes)
   * First byte `0x02` + variable data = WebAuthn (total 129-2049 bytes)
   * First byte `0x03` + 20 bytes + inner signature = Keychain
   * Otherwise invalid
2. Apply appropriate verification:
   * secp256k1: Standard `ecrecover`
   * P256: P256 curve verification with provided public key (sha256 pre-hash if flag set)
   * WebAuthn: Parse clientDataJSON, verify challenge and type, then P256 verify
   * Keychain: Verify inner signature, then validate access key authorization via AccountKeychain precompile

#### Nonce Validation

1. Fetch sequence for given nonce key
2. Verify sequence matches transaction
3. Increment sequence

#### Fee Payer Validation (if present)

1. Verify fee payer signature (K1 only initially)
2. Recover payer address via `ecrecover`
3. Deduct fees from payer instead of sender

### Fee Payer Signature Details

The Tempo Transaction Type (0x76) supports **gas sponsorship** where a third party (fee payer) can pay transaction fees on behalf of the sender. This is achieved through dual signature domains—the sender signs with transaction type byte `0x76`, while the fee payer signs with magic byte `0x78` to ensure domain separation and prevent signature reuse attacks.

#### Signing Domains

##### Sender Signature

For computing the transaction hash that the sender signs:

* Fields are preceded by transaction type byte `0x76`
* Field 11 (`fee_token`) is encoded as empty string (`0x80`) **if and only if** `fee_payer_signature` is present. This allows the fee payer to specify the fee token.
* Field 12 (`fee_payer_signature`) is encoded as:
  * Single byte `0x00` if fee payer signature will be present (placeholder)
  * Empty string `0x80` if no fee payer

**Sender Signature Hash:**

```rust
// When fee_payer_signature is present:
sender_hash = keccak256(0x76 || rlp([
    chain_id,
    max_priority_fee_per_gas,
    max_fee_per_gas,
    gas_limit,
    calls,
    access_list,
    nonce_key,
    nonce,
    valid_before,
    valid_after,
    0x80,  // fee_token encoded as EMPTY (skipped)
    0x00   // placeholder byte for fee_payer_signature
]))

// When no fee_payer_signature:
sender_hash = keccak256(0x76 || rlp([
    chain_id,
    max_priority_fee_per_gas,
    max_fee_per_gas,
    gas_limit,
    calls,
    access_list,
    nonce_key,
    nonce,
    valid_before,
    valid_after,
    fee_token,  // fee_token is INCLUDED
    0x80        // empty for no fee_payer_signature
]))
```

##### Fee Payer Signature

Only included for sponsored transactions. For computing the fee payer's signature hash:

* Fields are preceded by **magic byte `0x78`** (different from transaction type `0x76`)
* Field 11 (`fee_token`) is **always included** (20-byte address or `0x80` for None)
* Field 12 is serialized as the **sender address** (20 bytes). This commits the fee payer to sponsoring a specific sender.

**Fee Payer Signature Hash:**

```rust
fee_payer_hash = keccak256(0x78 || rlp([  // Note: 0x78 magic byte
    chain_id,
    max_priority_fee_per_gas,
    max_fee_per_gas,
    gas_limit,
    calls,
    access_list,
    nonce_key,
    nonce,
    valid_before,
    valid_after,
    fee_token,      // fee_token ALWAYS included
    sender_address  // 20-byte sender address
    key_authorization,
]))
```

#### Key Properties

1. **Sender Flexibility**: By omitting `fee_token` from sender signature when fee payer is present, the fee payer can specify which token to use for payment without invalidating the sender's signature
2. **Fee Payer Commitment**: Fee payer's signature includes `fee_token` and `sender_address`, ensuring they agree to:
   * Pay for the specific sender
   * Use the specific fee token
3. **Domain Separation**: Different magic bytes (`0x76` vs `0x78`) prevent signature reuse attacks between sender and fee payer roles
4. **Deterministic Fee Payer**: The fee payer address is statically recoverable from the transaction via secp256k1 signature recovery

#### Validation Rules

**Signature Requirements:**

* Sender signature MUST be valid (secp256k1, P256, or WebAuthn depending on signature length)
* If `fee_payer_signature` present:
  * MUST be recoverable via secp256k1 (only secp256k1 supported for fee payers)
  * Recovery MUST succeed, otherwise transaction is invalid
* If `fee_payer_signature` absent:
  * Fee payer defaults to sender address (self-paid transaction)

**Token Preference:**

* When `fee_token` is `Some(address)`, this overrides any account/validator-level preferences
* Validation ensures the token is a valid TIP-20 token with sufficient balance/liquidity
* Failures reject the transaction before execution (see Token Preferences spec)

**Fee Payer Resolution:**

* Fee payer signature present → recovered address via `ecrecover`
* Fee payer signature absent → sender address
* This address is used for all fee accounting (pre-charge, refund) via TIP Fee Manager precompile

#### Transaction Flow

1. **User prepares transaction**: Sets `fee_payer_signature` to placeholder (`Some(Signature::default())`)
2. **User signs**: Computes sender hash (with fee\_token skipped) and signs
3. **Fee payer receives** user-signed transaction
4. **Fee payer verifies** user signature is valid
5. **Fee payer signs**: Computes fee payer hash (with fee\_token and sender\_address) and signs
6. **Complete transaction**: Replace placeholder with actual fee payer signature
7. **Broadcast**: Transaction is sent to network with both signatures

#### Error Cases

* `fee_payer_signature` present but unrecoverable → invalid transaction
* Fee payer balance insufficient for `gas_limit * max_fee_per_gas` in fee token → invalid
* Any sender signature failure → invalid
* Malformed RLP → invalid

### RLP Encoding

The transaction is RLP encoded as follows:

**Signed Transaction Envelope:**

```
0x76 || rlp([
    chain_id,
    max_priority_fee_per_gas,
    max_fee_per_gas,
    gas_limit,
    calls,                   // RLP list of Call structs
    access_list,
    nonce_key,
    nonce,
    valid_before,            // 0x80 if None
    valid_after,             // 0x80 if None
    fee_token,               // 0x80 if None
    fee_payer_signature,     // 0x80 if None, RLP list [v, r, s] if Some
    aa_authorization_list,   // EIP-7702 style authorization list with AA signatures
    key_authorization?,      // Only encoded if present (backwards compatible)
    sender_signature         // TempoSignature bytes (secp256k1, P256, WebAuthn, or Keychain)
])
```

**Call Encoding:**

```
rlp([to, value, input])
```

**Key Authorization Encoding:**

```
rlp([
    chain_id,
    key_type,
    key_id,
    expiry?,         // Optional trailing field (omitted or 0x80 if None)
    limits?,         // Optional trailing field (omitted or 0x80 if None)
    signature        // PrimitiveSignature bytes
])
```

**Notes:**

* Optional fields encode as `0x80` (EMPTY\_STRING\_CODE) when `None`
* The `key_authorization` field is truly optional - when `None`, no bytes are encoded (backwards compatible)
* The `calls` field is a list that must contain at least one Call (empty calls list is invalid)
* The `sender_signature` field is the final field and contains the TempoSignature bytes (secp256k1, P256, WebAuthn, or Keychain)
* KeyAuthorization uses RLP trailing field semantics for optional `expiry`, `limits`, and `allowed_calls`

### WebAuthn Signature Verification

WebAuthn verification follows the [Daimo P256 verifier approach](https://github.com/daimo-eth/p256-verifier/blob/master/src/WebAuthn.sol).

#### Signature Format

```
signature = authenticatorData || clientDataJSON || r (32) || s (32) || pubKeyX (32) || pubKeyY (32)
```

Parse by working backwards:

* Last 32 bytes: `pubKeyY`
* Previous 32 bytes: `pubKeyX`
* Previous 32 bytes: `s`
* Previous 32 bytes: `r`
* Remaining bytes: `authenticatorData || clientDataJSON` (requires parsing to split)

#### Authenticator Data Structure (minimum 37 bytes)

```
Bytes 0-31:   rpIdHash (32 bytes)
Byte 32:      flags (1 byte)
              - Bit 0 (0x01): User Presence (UP) - must be set
Bytes 33-36:  signCount (4 bytes)
```

#### Verification Steps

```python
def verify_webauthn(tx_hash: bytes32, signature: bytes, require_uv: bool) -> bool:
    # 1. Parse signature
    pubKeyY = signature[-32:]
    pubKeyX = signature[-64:-32]
    s = signature[-96:-64]
    r = signature[-128:-96]
    webauthn_data = signature[:-128]

    # Parse authenticatorData and clientDataJSON
    # Minimum authenticatorData is 37 bytes
    # Simple approach: try to decode clientDataJSON from different split points
    authenticatorData, clientDataJSON = split_webauthn_data(webauthn_data)

    # 2. Validate authenticator data
    if len(authenticatorData) < 37:
        return False

    flags = authenticatorData[32]
    if not (flags & 0x01):  # UP bit must be set
        return False

    # 3. Validate client data JSON
    if not contains(clientDataJSON, '"type":"webauthn.get"'):
        return False

    challenge_b64url = base64url_encode(tx_hash)
    challenge_property = '"challenge":"' + challenge_b64url + '"'
    if not contains(clientDataJSON, challenge_property):
        return False

    # 4. Compute message hash
    clientDataHash = sha256(clientDataJSON)
    messageHash = sha256(authenticatorData || clientDataHash)

    # 5. Verify P256 signature
    return p256_verify(messageHash, r, s, pubKeyX, pubKeyY)
```

#### What We Verify

* Authenticator data minimum length (37 bytes)
* User Presence (UP) flag is set
* `"type":"webauthn.get"` in clientDataJSON
* Challenge matches tx\_hash (Base64URL encoded)
* P256 signature validity

#### What We Skip

* Origin verification (not applicable to blockchain)
* RP ID hash validation (no central RP in decentralized context)
* Signature counter (anti-cloning left to application layer)
* Backup flags (account policy decision)

#### Parsing authenticatorData and clientDataJSON

Since authenticatorData has variable length, finding the split point requires:

1. Check if AT flag (bit 6) is set at byte 32
2. If not set, authenticatorData is exactly 37 bytes
3. If set, need to parse CBOR credential data (complex, see implementation)
4. Everything after authenticatorData is clientDataJSON (valid UTF-8 JSON)

**Simplified approach:** For TempoTransactions, wallets should send minimal authenticatorData (37 bytes, no AT/ED flags) to minimize gas costs and simplify parsing.

### Access Keys

A sender can choose to authorize an Access Key to sign transactions on the sender's behalf. This is useful to enable
flows where a root key (e.g. a passkey) would provision a short-lived (scoped) Access Key to
be able to sign transactions on the sender's behalf without inducing another passkey prompt.

More information about Access Keys can be found in the [Account Keychain Specification](./AccountKeychain).

A sender can authorize a key by signing over a "key authorization" item that contains the following information:

* **Chain ID** for replay protection (0 = valid on any chain)
* **Key type** (Secp256k1, P256, or WebAuthn)
* **Key ID** (address derived from the public key)
* **Expiration** timestamp of when the key should expire (optional - None means never expires)
* TIP20 token **spending limits** for the key (optional - None means unlimited spending):
  * Each limit carries a `period` (0 = one-time, non-zero = recurring in seconds). Recurring limits roll over to `max` when `current_timestamp >= periodEnd`.
  * Root key can update limits via `updateSpendingLimit()` without revoking the key. Updates reset `remaining` and `max` to `newLimit` while preserving `period` and `periodEnd`.
  * Note: Spending limits only apply to TIP20 token transfers, not ETH or other asset transfers
* **Call scopes** for the key (optional - None means unrestricted):
  * Each entry pins a `target` contract and a list of allowed selector rules. An empty selector list on a target means any selector is allowed on that target.
  * Selector rules can additionally constrain TIP-20 recipient-bearing selectors to a recipient allowlist.
  * `Some([])` (an empty top-level allowlist) means scoped deny-all.

Access-key-signed transactions cannot perform contract creation. Calls within the batch that would `CREATE` or `CREATE2` (including via factory contracts) are rejected. Use the Root Key for deployment flows.

#### RLP Encoding

**Unsigned Format:**

The root key signs over the keccak256 hash of the RLP encoded `KeyAuthorization`:

```
key_authorization_digest = keccak256(rlp([chain_id, key_type, key_id, expiry?, limits?, allowed_calls?]))

chain_id = u64 (0 = valid on any chain)
key_type = 0 (Secp256k1) | 1 (P256) | 2 (WebAuthn)
key_id = Address (derived from the public key)
expiry = Option<u64> (unix timestamp, None = never expires; omitted expiry is translated to u64::MAX when the protocol calls the precompile)
limits = Option<Vec<[token, limit, period]>> (None = unlimited spending; period = 0 means one-time)
allowed_calls = Option<Vec<[target, [[selector, [recipient, ...]], ...]]>> (None = unrestricted; Some([]) = scoped deny-all)
```

**Signed Format:**

The signed format (`SignedKeyAuthorization`) includes all fields with the `signature` appended:

```
signed_key_authorization = rlp([chain_id, key_type, key_id, expiry?, limits?, allowed_calls?, signature])
```

The `signature` is a `PrimitiveSignature` (secp256k1, P256, or WebAuthn) signed by the root key.

Note: `expiry`, `limits`, and `allowed_calls` use RLP trailing field semantics — they can be omitted entirely when None.

:::warning\[Expiry encoding]
For `key_authorization`, the canonical non-expiring encoding omits `expiry` (`None`).

There is one decoder nuance: because `KeyAuthorization` uses canonical trailing optional fields, an explicit empty `expiry` placeholder (`0x80`) is also interpreted as `None` when another trailing optional field follows it. But a final `expiry` encoded as zero/empty is rejected, and a literal `0x00` is invalid RLP for this field.

Do not hand-encode `expiry = 0` or rely on `Some(0)` as a sentinel. The supported encoding to target is omission, and the protocol translates omitted expiry to `u64::MAX` when materializing the `AccountKeychain.authorizeKey(...)` call.
:::

Intrinsic gas for `key_authorization` accounts for the storage written for periodic-limit state and call-scope entries. See [TIP-1011](/protocol/tips/tip-1011#intrinsic-gas-for-key-authorization) for slot-counting rules.

#### Keychain Precompile

The Account Keychain precompile (deployed at address `0xAAAAAAAA00000000000000000000000000000000`) manages authorized access keys for accounts. It enables root keys to provision scoped access keys with expiry timestamps and per-TIP20 token spending limits.

**See the [Account Keychain Specification](./AccountKeychain) for complete interface details, storage layout, and implementation.**

#### Protocol Behavior

The protocol enforces Access Key authorization and spending limits natively.

##### Transaction Validation

When a TempoTransaction is received, the protocol:

1. **Identifies the signing key** from the transaction signature
   * If signature is a `Keychain` variant: extracts the `keyId` (address) of the Access Key
   * Otherwise: treats it as the Root Key (keyId = address(0))

2. **Validates KeyAuthorization** (if present in transaction)
   * The `key_authorization` field in `TempoTransaction` provisions a NEW Access Key
   * Root Key MUST sign:
     * The `key_authorization` digest: `keccak256(rlp([chain_id, key_type, key_id, expiry?, limits?, allowed_calls?]))`
   * Access Key (being authorized) CAN sign the same tx which it is authorized in.
   * This enables "authorize and use" in a single transaction

3. **Sets transaction context**
   * Stores `transactionKey[account] = keyId` in protocol state
   * Used to enforce authorization hierarchy during execution, can
     also be used by DApps to see which key authorized the current tx.

4. **Validates Key Authorization** (for Access Keys)
   * Queries precompile: `getKey(account, keyId)` returns `KeyInfo`
   * Checks key is active (not revoked)
   * Checks expiry: `current_timestamp < expiry`; non-expiring keys are stored with `expiry = u64::MAX`
   * Rejects transaction if validation fails

##### Authorization Hierarchy Enforcement

The protocol enforces a strict two-tier hierarchy:

**Root Key** (keyId = address(0)):

* The account's primary key (address matches account address)
* Can call ALL precompile functions
* No spending limits
* Can authorize, revoke, and update Access Keys

**Access Keys** (keyId != address(0)):

* Secondary keys authorized by Root Key
* CANNOT call mutable precompile functions (`authorizeKey`, `revokeKey`, `updateSpendingLimit`, `setAllowedCalls`, `removeAllowedCalls`)
* Precompile functions check: `transactionKey[msg.sender] == 0` before allowing mutations
* Subject to per-TIP20 token spending limits and call-scope checks during execution
* Cannot create contracts (`CREATE` and `CREATE2` are rejected anywhere in the call batch)
* Can have expiry timestamps

When an Access Key attempts to call any mutable keychain function:

1. Transaction executes normally until the precompile call
2. Precompile checks `getTransactionKey()` returns non-zero (Access Key)
3. Call reverts with `UnauthorizedCaller` error
4. Entire transaction is reverted

##### Spending Limit Enforcement

The protocol tracks and enforces spending limits for TIP20 token transfers:

**Scope:** Only TIP20 `transfer()`, `transferWithMemo()`, `approve()`, and `startReward()` calls are tracked

* Spending limits only apply when `msg.sender == tx.origin` (direct EOA calls)
* When a contract makes transfers on behalf of the user, spending limits do NOT apply (e.g., `transferFrom()`)
* Native value transfers are NOT limited
* NFT transfers are NOT limited
* Other asset types are NOT limited

**Tracking:** During transaction execution, when an Access Key's transaction directly calls TIP20 methods:

1. Protocol intercepts `transfer(to, amount)`, `transferWithMemo()`, `approve(spender, amount)`, and `startReward()` calls
2. For `transfer`/`transferWithMemo`, the full `amount` is checked against the remaining limit
3. For `approve`, only **increases** in approval (new approval minus previous allowance) are checked and counted against the limit
4. Queries: `getRemainingLimitWithPeriod(account, keyId, token)`, which returns `(remaining, periodEnd)` and reflects any periodic rollover
5. Checks: relevant amount (transfer amount or approval increase) `<= remaining`
6. If check fails: reverts with `SpendingLimitExceeded`
7. If check passes: decrements the limit by the relevant amount
8. Updates are stored in precompile state

**Root Key Behavior:** Spending limit checks are skipped entirely (no limits apply)

**Recurring Limits:** When a `TokenLimit.period` is non-zero, the limit recurs. `remaining` rolls over to `max` once `current_timestamp >= periodEnd`, and `periodEnd` advances by `period`. Callers observe rollover state via `getRemainingLimitWithPeriod`.

**Limit Updates:**

* Limits deplete as tokens are spent
* Root Key can call `updateSpendingLimit(keyId, token, newLimit)` to set new limits
* Setting a new limit REPLACES both `remaining` and `max` with `newLimit`. The configured `period` and current `periodEnd` are preserved.

##### Call Scope Enforcement

When an Access Key has stored call scopes (`allowed_calls` was set at authorization, or `setAllowedCalls(...)` was called later), the protocol enforces them on top-level calls signed by that Access Key:

1. For each call in the batch, look up the matching `(target, selector)` allowlist entry
2. If the target is not in the allowlist, or the selector is not allowed on that target, revert with `CallNotAllowed`
3. For recipient-bound TIP-20 selectors (e.g., `transfer`, `transferFrom`, `transferWithMemo`), additionally enforce that the call's recipient is in the rule's recipient allowlist (when non-empty)
4. Access keys with `allowed_calls = None` are unrestricted; `Some([])` is scoped deny-all

##### Contract Creation Restriction

Access-key-signed transactions cannot perform contract creation. Any `CREATE` or `CREATE2` (including via factory contracts or internal calls) reverts the transaction. Use the Root Key for deployment flows.

##### Creating and Using KeyAuthorization

**First-Time Authorization Flow:**

1. **Generate Access Key**
   ```typescript
   // Generate a new P256 or secp256k1 key pair
   const accessKey = generateKeyPair("p256"); // or "secp256k1"
   const keyId = deriveAddress(accessKey.publicKey);
   ```

2. **Create Authorization Message**
   ```typescript
   // Define key parameters
   const keyAuth = {
     chain_id: 1,
     key_type: SignatureType.P256,      // 1
     key_id: keyId,                     // address derived from public key
     expiry: timestamp + 86400,         // 24 hours from now; omit this field for a non-expiring key authorization
     limits: [
       // One-time limit (period = 0)
       { token: USDG_ADDRESS, limit: 1000000000, period: 0 },                  // 1000 USDG (6 decimals), one-time
       // Recurring weekly limit (period = 604800 seconds)
       { token: DAI_ADDRESS, limit: 500000000000000000000n, period: 604800 }   // 500 DAI / week
     ],
     // Optional call scopes — omit for an unrestricted key
     allowed_calls: [
       {
         target: USDG_ADDRESS,
         selector_rules: [
           // transfer(address,uint256) restricted to a single recipient
           { selector: "0xa9059cbb", recipients: [TRUSTED_RECIPIENT] }
         ]
       }
     ]
   };

   // Compute digest: keccak256(rlp([chain_id, key_type, key_id, expiry, limits, allowed_calls]))
   const authDigest = computeAuthorizationDigest(keyAuth);
   ```

3. **Root Key Signs Authorization**
   ```typescript
   // Sign with Root Key (e.g., passkey prompt)
   const rootSignature = await signWithRootKey(authDigest);
   ```

4. **Build TempoTransaction**
   ```typescript
   const tx = {
     chain_id: 1,
     nonce: await getNonce(account),
     nonce_key: 0,
     calls: [{ to: recipient, value: 0, input: "0x" }],
     gas_limit: 200000,
     max_fee_per_gas: 1000000000,
     max_priority_fee_per_gas: 1000000000,
     key_authorization: {
       authorization: keyAuth,
       signature: rootSignature  // Root Key's signature on authDigest
     },
     // ... other fields
   };
   ```

5. **Access Key Signs Transaction**
   ```typescript
   // Sign transaction with the NEW Access Key being authorized
   const txHash = computeTxSignatureHash(tx);
   const accessSignature = await signWithAccessKey(txHash, accessKey);

   // Wrap in Keychain signature
   const finalSignature = {
     Keychain: {
       user_address: account,
       signature: { P256: accessSignature }  // or Secp256k1
     }
   };
   ```

6. **Submit Transaction**
   * Protocol validates Root Key signed the `key_authorization`
   * Protocol calls `authorizeKey()` on the precompile to store the key
   * Protocol validates Access Key signature on transaction
   * Transaction executes with spending limits enforced

**Subsequent Usage (Key Already Authorized):**

```typescript
// Access Key is already authorized, just sign transactions directly
const tx = {
  chain_id: 1,
  nonce: await getNonce(account),
  calls: [{ to: recipient, value: 0, input: calldata }],
  key_authorization: null,  // No authorization needed
  // ... other fields
};

const txHash = computeTxSignatureHash(tx);
const accessSignature = await signWithAccessKey(txHash, accessKey);

const finalSignature = {
  Keychain: {
    user_address: account,
    signature: { P256: accessSignature }
  }
};

// Submit - protocol validates key is authorized and not expired
```

##### Key Management Operations

**Revoking an Access Key:**

```typescript
// Must be signed by Root Key
const tx = {
  chain_id: 1,
  nonce: await getNonce(account),
  calls: [{
    to: ACCOUNT_KEYCHAIN_ADDRESS,
    value: 0,
    input: encodeCall("revokeKey", [keyId])
  }],
  // ... sign with Root Key
};
```

**Updating Spending Limits:**

```typescript
// Must be signed by Root Key
const tx = {
  chain_id: 1,
  nonce: await getNonce(account),
  calls: [{
    to: ACCOUNT_KEYCHAIN_ADDRESS,
    value: 0,
    input: encodeCall("updateSpendingLimit", [
      keyId,
      USDG_ADDRESS,
      2000000000  // New limit: 2000 USDG
    ])
  }],
  // ... sign with Root Key
};
```

**Note:** After updating, both `remaining` and `max` are set to `newLimit`. The configured `period` and current `periodEnd` are preserved.

##### Querying Key State

Applications can query key information and spending limits:

```typescript
// Check if key is authorized and get info
const keyInfo = await precompile.getKey(account, keyId);
// Returns: { signatureType, keyId, expiry, enforceLimits, isRevoked }

// Check remaining spending limit and current period end for a token
const { remaining, periodEnd } = await precompile.getRemainingLimitWithPeriod(
  account, keyId, USDG_ADDRESS
);
// Returns: (uint256 remaining, uint64 periodEnd). Reflects periodic rollover.

// Inspect call scopes
const { isScoped, scopes } = await precompile.getAllowedCalls(account, keyId);
// isScoped = false → key is unrestricted
// isScoped = true, scopes = [...] → key is scoped to those (target, selector, recipient) entries
// isScoped = true, scopes = [] → scoped deny-all (also returned for missing/revoked/expired keys)

// Get which key signed current transaction (callable from contracts)
const currentKey = await precompile.getTransactionKey();
// Returns: address (0x0 for Root Key, keyId for Access Key)
```

## Rationale

### Signature Type Detection by Length

Using signature length for type detection avoids adding explicit type fields while maintaining deterministic parsing. The chosen lengths (65, 129, variable) are naturally distinct.

### Linear Gas Scaling for Nonce Keys

The progressive pricing model prevents state bloat while keeping initial keys affordable. The 20,000 gas increment approximates the long-term state cost of maintaining each additional nonce mapping.

### No Nonce Expiry

Avoiding expiry simplifies the protocol and prevents edge cases where in-flight transactions become invalid. Wallets handle nonce key allocation to prevent conflicts.

### Backwards Compatibility

This spec introduces a new transaction type and does not modify existing transaction processing. Legacy transactions continue to work unchanged. We special case `nonce key = 0` (also referred to as the protocol nonce key) to maintain compatibility with existing nonce behavior.

## Gas Costs

### Signature Verification Gas Schedule

Different signature types incur different base transaction costs to reflect their computational complexity:

| Signature Type | Base Gas Cost | Calculation | Rationale |
|----------------|---------------|-------------|-----------|
| **secp256k1** | 21,000 | Standard | Includes 3,000 gas for ecrecover precompile |
| **P256** | 26,000 | 21,000 + 5,000 | Base 21k + additional 5k for P256 verification |
| **WebAuthn** | 26,000 + variable data cost | 26,000 + (calldata gas for clientDataJSON) | Base P256 cost plus variable cost for clientDataJSON based on size |
| **Keychain** | Inner signature + 3,000 | primitive\_sig\_cost + 3,000 | Inner signature cost + key validation overhead (2,100 SLOAD + 900 buffer) |

**Rationale:**

* The base 21,000 gas for standard transactions already includes the cost of secp256k1 signature verification via ecrecover (3,000 gas)
* [EIP 7951](https://eips.ethereum.org/EIPS/eip-7951) sets P256 verification cost at 6,900 gas. We add 1,100 gas to account for the additional 65 bytes of signature size (129 bytes total vs 64 bytes for secp256k1), giving 8,000 gas total. Since the base 21k already includes 3,000 gas for ecrecover (which P256 doesn't use), the net additional cost is 8,000 - 3,000 = **5,000 gas**.
* WebAuthn signatures require additional computation to parse and validate the clientDataJSON structure. We cap the total signature size at 2kb. The signature is also  charged using the same gas schedule as calldata (16 gas per non-zero byte, 4 gas per zero byte) to prevent the use of this signature space from spam.
* Keychain signatures wrap a primitive signature and are used by access keys. They add 3,000 gas to cover key validation during transaction validation (cold SLOAD to verify key exists + processing overhead).
* Individual per-signature-type gas costs allow us to add more advanced verification methods in the future like multisigs, which could have dynamic gas pricing.

### Nonce Key Gas Schedule

Transactions using parallelizable nonces incur additional costs based on the nonce key usage pattern:

#### Case 1: Protocol Nonce (Key 0)

* **Additional Cost:** 0 gas
* **Total:** 21,000 gas (base transaction cost)
* **Rationale:** Maintains backward compatibility with existing transaction flow

#### Case 2: Existing User Nonce Key (nonce > 0)

* **Additional Cost:** 5,000 gas
* **Total:** 26,000 gas
* **Rationale:** Cold SLOAD (2,100) + warm SSTORE reset (2,900) for incrementing an existing nonce

#### Case 3: New User Nonce Key (nonce == 0)

* **Additional Cost:** 22,100 gas
* **Total:** 43,100 gas
* **Rationale:** Cold SLOAD (2,100) + SSTORE set (20,000) for writing to a new storage slot

**Rationale for Fixed Pricing:**

1. **Simplicity:** Fixed costs based on actual EVM storage operations are straightforward to reason about
2. **Storage Pattern Alignment:** Costs directly mirror EVM cold SSTORE costs for new vs existing slots
3. **State Growth:** Creating new nonce keys incurs the higher cost naturally through SSTORE set pricing

### Key Authorization Gas Schedule

When a transaction includes a `key_authorization` field to provision a new access key, additional intrinsic gas is charged to cover signature verification and storage operations. This gas is charged **before execution** as part of the transaction's intrinsic gas cost.

#### Gas Components

| Component | Gas Cost | Notes |
|-----------|----------|-------|
| **Signature verification** | 3,000 (secp256k1) / 8,000 (P256) / 8,000 + calldata (WebAuthn) | Verifying the root key's signature on the authorization |
| **Key storage** | 22,000 | Cold SSTORE to store new key (0→non-zero) |
| **Overhead buffer** | 5,000 | Buffer for event emission, storage reads, and other overhead |
| **Per spending limit** | 22,000 each | Cold SSTORE per token limit (0→non-zero) |

**Signature verification rationale:** KeyAuthorization requires an *additional* signature verification beyond the transaction signature. Unlike the transaction signature (where ecrecover cost is included in the base 21k), KeyAuthorization must pay the full verification cost:

* **secp256k1**: 3,000 gas (ecrecover precompile cost)
* **P256**: 8,000 gas (6,900 from EIP-7951 + 1,100 for signature size). Note: the transaction signature schedule charges only 5,000 additional gas for P256 because it subtracts the 3,000 ecrecover "savings" already in base 21k. KeyAuthorization pays the full 8,000.
* **WebAuthn**: 8,000 + calldata gas for webauthn\_data

#### Gas Formula

```
KEY_AUTH_BASE_GAS = 30,000  # For secp256k1 signature (3,000 + 22,000 + 5,000)
KEY_AUTH_BASE_GAS = 35,000  # For P256 signature (5,000 + 3,000 + 22,000 + 5,000)
KEY_AUTH_BASE_GAS = 35,000 + webauthn_calldata_gas  # For WebAuthn signature

PER_LIMIT_GAS = 22,000  # Per spending limit entry

total_key_auth_gas = KEY_AUTH_BASE_GAS + (num_limits * PER_LIMIT_GAS)
```

#### Examples

| Configuration | Gas Cost | Calculation |
|--------------|----------|-------------|
| secp256k1, no limits | 30,000 | Base only |
| secp256k1, 1 limit | 52,000 | 30,000 + 22,000 |
| secp256k1, 3 limits | 96,000 | 30,000 + (3 × 22,000) |
| P256, no limits | 35,000 | Base with P256 verification |
| P256, 2 limits | 79,000 | 35,000 + (2 × 22,000) |

#### Rationale

1. **Pre-execution charging**: KeyAuthorization is validated and executed during transaction validation (before the EVM runs), so its gas must be included in intrinsic gas
2. **Storage cost alignment**: The 22,000 gas per storage slot approximates EVM cold SSTORE costs for new slots
3. **DoS prevention**: Progressive cost based on number of limits prevents abuse through excessive limit creation

### Reference Pseudocode

```python
def calculate_calldata_gas(data: bytes) -> uint256:
    """
    Calculate gas cost for calldata based on zero and non-zero bytes

    Args:
        data: bytes to calculate cost for

    Returns:
        gas_cost: uint256
    """
    CALLDATA_ZERO_BYTE_GAS = 4
    CALLDATA_NONZERO_BYTE_GAS = 16

    gas = 0
    for byte in data:
        if byte == 0:
            gas += CALLDATA_ZERO_BYTE_GAS
        else:
            gas += CALLDATA_NONZERO_BYTE_GAS

    return gas


def calculate_signature_verification_gas(signature: PrimitiveSignature) -> uint256:
    """
    Calculate gas cost for verifying a primitive signature.

    Returns the ADDITIONAL gas beyond the base 21k transaction cost.
    - secp256k1: 0 (already included in base 21k via ecrecover)
    - P256: 5,000 (8,000 full cost - 3,000 ecrecover already in base 21k)
    - WebAuthn: 5,000 + calldata gas for webauthn_data
    """
    # P256 full verification cost is 8,000 (6,900 from EIP-7951 + 1,100 for signature size)
    # But base 21k already includes 3,000 for ecrecover, so additional cost is 5,000
    P256_ADDITIONAL_GAS = 5_000

    if signature.type == Secp256k1:
        return 0  # Already included in base 21k
    elif signature.type == P256:
        return P256_ADDITIONAL_GAS
    elif signature.type == WebAuthn:
        webauthn_data_gas = calculate_calldata_gas(signature.webauthn_data)
        return P256_ADDITIONAL_GAS + webauthn_data_gas
    else:
        revert("Invalid signature type")


def calculate_key_authorization_gas(key_auth: SignedKeyAuthorization) -> uint256:
    """
    Calculate the intrinsic gas cost for a KeyAuthorization.

    This is charged BEFORE execution as part of transaction validation.

    Args:
        key_auth: SignedKeyAuthorization with fields:
            - signature: PrimitiveSignature (root key's signature)
            - limits: Optional[List[TokenLimit]]              # each carries a `period`
            - allowed_calls: Optional[List[CallScope]]        # call-scope allowlist

    Returns:
        gas_cost: uint256

    Note: This is a simplified illustration. See TIP-1011 for the canonical
    slot-counting rules covering periodic-limit state and call-scope storage.
    """
    # Constants - KeyAuthorization pays FULL signature verification costs
    # (not the "additional" costs used for transaction signatures)
    ECRECOVER_GAS = 3_000   # Full ecrecover cost
    P256_FULL_GAS = 8_000   # Full P256 cost (6,900 + 1,100)
    COLD_SSTORE_SET_GAS = 22_000  # Storage cost for new slot
    OVERHEAD_BUFFER = 5_000  # Buffer for event emission, storage reads, etc.

    gas = 0

    # Step 1: Signature verification cost (full cost, not additional)
    if key_auth.signature.type == Secp256k1:
        gas += ECRECOVER_GAS  # 3,000
    elif key_auth.signature.type == P256:
        gas += P256_FULL_GAS  # 8,000
    elif key_auth.signature.type == WebAuthn:
        webauthn_data_gas = calculate_calldata_gas(key_auth.signature.webauthn_data)
        gas += P256_FULL_GAS + webauthn_data_gas  # 8,000 + calldata

    # Step 2: Key storage
    gas += COLD_SSTORE_SET_GAS  # 22,000 - store new key (0 → non-zero)

    # Step 3: Overhead buffer
    gas += OVERHEAD_BUFFER  # 5,000

    # Step 4: Per-limit storage cost (each TokenLimit carries period state)
    num_limits = len(key_auth.limits) if key_auth.limits else 0
    gas += num_limits * COLD_SSTORE_SET_GAS  # 22,000 per limit

    # Step 5: Per-call-scope storage cost (target + selector + recipients).
    # See TIP-1011 for exact slot accounting; this counts one slot per
    # (target, selector, recipient) triple as a conservative approximation.
    num_scope_slots = 0
    if key_auth.allowed_calls:
        for scope in key_auth.allowed_calls:
            for rule in scope.selector_rules:
                # one slot for the (target, selector) entry, plus one per recipient
                num_scope_slots += 1 + max(len(rule.recipients), 0)
    gas += num_scope_slots * COLD_SSTORE_SET_GAS

    return gas


def calculate_tempo_tx_base_gas(tx):
    """
    Calculate the base gas cost for a TempoTransaction

    Args:
        tx: TempoTransaction object with fields:
            - signature: TempoSignature (variable length)
            - nonce_key: uint192
            - nonce: uint64
            - sender_address: address
            - key_authorization: Optional[SignedKeyAuthorization]

    Returns:
        total_gas: uint256
    """

    # Constants
    BASE_TX_GAS = 21_000
    EXISTING_NONCE_KEY_GAS = 5_000   # Cold SLOAD (2,100) + warm SSTORE reset (2,900)
    NEW_NONCE_KEY_GAS = 22_100       # Cold SLOAD (2,100) + SSTORE set (20,000)
    KEYCHAIN_VALIDATION_GAS = 3_000  # 2,100 SLOAD + 900 processing buffer

    # Step 1: Determine signature verification cost
    # For Keychain signatures, use the inner primitive signature
    if tx.signature.type == Keychain:
        inner_sig = tx.signature.inner_signature
    else:
        inner_sig = tx.signature

    signature_gas = BASE_TX_GAS + calculate_signature_verification_gas(inner_sig)

    # Add keychain validation overhead if using access key
    if tx.signature.type == Keychain:
        signature_gas += KEYCHAIN_VALIDATION_GAS

    # Step 2: Calculate nonce key cost
    if tx.nonce_key == 0:
        # Protocol nonce (backward compatible)
        nonce_gas = 0
    else:
        # User nonce key
        current_nonce = get_nonce(tx.sender_address, tx.nonce_key)

        if current_nonce > 0:
            # Existing nonce key - cold SLOAD + warm SSTORE reset
            nonce_gas = EXISTING_NONCE_KEY_GAS
        else:
            # New nonce key - cold SLOAD + SSTORE set
            nonce_gas = NEW_NONCE_KEY_GAS

    # Step 3: Calculate key authorization cost (if present)
    if tx.key_authorization is not None:
        key_auth_gas = calculate_key_authorization_gas(tx.key_authorization)
    else:
        key_auth_gas = 0

    # Step 4: Calculate total base gas
    total_gas = signature_gas + nonce_gas + key_auth_gas

    return total_gas
```

## Security Considerations

### Mempool DOS Protection

Transaction pools perform pre-execution validation checks before accepting transactions. These checks are performed for free by the nodes, making them potential DOS vectors. The three primary validation checks are:

1. **Signature verification** - Must be valid
2. **Nonce verification** - Must match current account nonce
3. **Balance check** - Account must have sufficient balance to pay for transaction

This transaction type impacts all three areas:

#### Signature Verification Impact

* **P256 signatures**: Fixed computational cost similar to ecrecover.
* **WebAuthn signatures**: Variable cost due to clientDataJSON parsing, but **capped at 2KB total signature size** to prevent abuse
* **Mitigation**: All signature types have bounded computational costs that are in the same ballpark as standard ecrecover.

#### Nonce Verification Impact

* **2D nonce lookup**: Requires additional storage read from nonce precompile
* **Cost**: Equivalent to a cold SLOAD (~2,100 gas worth of free computation)
* **Mitigation**: Cost is bounded to a manageable value.

#### Fee Payer Impact

* **Additional account read**: When fee payer is specified, must fetch fee payer's account to verify balance
* **Cost**: Effectively doubles the free account access work for sponsored transactions
* **Mitigation**: Cost is still bounded to a single additional account read.

#### Comparison to Ethereum

The introduction of 7702 delegated accounts already created complex cross-transaction dependencies in the mempool, which prevents any static pool checks from being useful.
Because a single transaction can invalidate multiple others by spending balances of multiple accounts

**Assessment:** While this transaction type introduces additional pre-execution validation costs, all costs are bounded to reasonable limits. The mempool complexity issues around cross-transaction dependencies already exist in Ethereum due to 7702 and accounts with code, making static validation inherently difficult. So the incremental cost from this transaction type is acceptable given these existing constraints.

## T2 → T3 migration

:::info\[Migration appendix]
This section captures changes introduced by the [T3 network upgrade](/protocol/upgrades/t3) for integrators migrating from T2. The spec above is the canonical post-T3 specification; this appendix exists for reference and will be removed in a future docs revision.
:::

T3 expanded access keys through [TIP-1011](/protocol/tips/tip-1011) in the following ways:

* `KeyAuthorization` gained `allowed_calls` (call-scope allowlist).
* `TokenLimit` gained `period` (recurring vs. one-time spending limits).
* The signed payload `SignedKeyAuthorization { authorization, signature }` is unchanged in shape, but `authorization` now uses the expanded `KeyAuthorization` and new RLP encoding. Low-level integrators that manually encode `key_authorization` must branch pre-T3 vs post-T3 — the post-T3 digest and signed payload include `allowed_calls?` in addition to `expiry?` and `limits?`.
* A non-expiring `key_authorization` omits `expiry` in tx RLP. At the Account Keychain ABI boundary, the protocol translates that omission to `u64::MAX`. Literal `0` is not a valid non-expiring sentinel to rely on.
* Access-key validation gained two new execution checks: call scopes must pass before execution begins, and access-key-signed transactions may not perform contract creation anywhere in the batch.
* The Account Keychain precompile ABI changed in lockstep — `authorizeKey(...)` now takes a `KeyRestrictions` tuple, `getRemainingLimit(...)` is replaced by `getRemainingLimitWithPeriod(...)`, and `setAllowedCalls(...)` / `removeAllowedCalls(...)` / `getAllowedCalls(...)` are added. See the [Account Keychain spec](./AccountKeychain) for full details.
* Intrinsic gas for `key_authorization` accounts for periodic-limit state and call-scope storage. See [TIP-1011](/protocol/tips/tip-1011#intrinsic-gas-for-key-authorization) for the canonical slot-counting rules.

### Pre-T3 KeyAuthorization (reference only)

Before T3, `KeyAuthorization` did not include `allowed_calls`, and `TokenLimit` did not include `period`:

```rust
pub struct KeyAuthorization {
    chain_id: u64,
    key_type: SignatureType,
    key_id: Address,
    expiry: Option<u64>,
    limits: Option<Vec<TokenLimit>>,
}

pub struct TokenLimit {
    token: Address,
    limit: U256,
}
```

The pre-T3 digest was `keccak256(rlp([chain_id, key_type, key_id, expiry?, limits?]))` and the signed payload was `rlp([chain_id, key_type, key_id, expiry?, limits?, signature])`.
