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

# TIP-1011: Enhanced Access Key Permissions

## Abstract

This TIP extends Access Keys with three permission features:

1. **Periodic spending limits** that reset on fixed intervals.
2. **Call scoping** that limits what addresses a key can call and which selectors it can use.
3. **Limited calldata recipient scoping** for token transfer/approval selectors.

## Motivation

Currently Access Keys support per-token limits and expiry, but miss two practical controls.

### Periodic Spending Limits

One-time limits cannot express recurring allowances.

**Use cases:**

1. Subscription billing (`10 USDC / month`).
2. Payroll schedules (monthly budgeted payouts).
3. Rate-limited agent/API budgets.

### Call Scoping (Target + Selector Set)

Users need finer controls than "any call". They want keys like:

1. "Only call `swap()` and `exactInput()` on DEX X."
2. "Only call gameplay methods on contract Y."
3. "Only perform plain transfers, not token extension methods."
4. "Only vote() on governance contracts."

**Current workaround**: Deploy a proxy contract that enforces destination/function restrictions, adding gas overhead and complexity.

### Recipient-Bound Token Calls

Target + selector scoping still allows an access key to move funds to arbitrary recipients for token methods like `transfer` and `approve`.

Users need a narrower policy: the key may call transfer/approve selectors, but only when the recipient/spender matches a configured address.

This TIP intentionally adds a narrow calldata rule (first ABI `address` argument equality) instead of a generic calldata policy language.

***

# Specification

## Extended Data Structures

Conventions used in this section:

1. Protocol/RLP structs are written with Rust-like `Option<...>` notation.
2. Solidity ABI structs are listed separately where ABI cannot directly represent protocol `Option` semantics.

### TokenLimit

**Current:**

```solidity
struct TokenLimit {
    address token;
    uint256 amount;
}
```

**Proposed:**

```solidity
struct TokenLimit {
    address token;
    uint256 amount;  // One-time cap when period == 0, per-period cap when period > 0
    uint64 period;  // Period duration in seconds (0 = one-time limit)
}
```

Design note: `period` is specified as an explicit field (instead of packed into `token`) to keep encoding/auditing straightforward and avoid migration risk for existing limit semantics.

Runtime state is derived and stored by the precompile (not signed):

```text
TokenLimitState {
    remainingInPeriod: uint256,
    periodEnd: uint64,
}
```

Initialization and persistence:

1. `TokenLimitState` is initialized when the key is authorized (or a limit is created via root mutation), not lazily at first spend.
2. `period == 0` initializes `remainingInPeriod = limit` and `periodEnd = 0`.
3. `period > 0` initializes `remainingInPeriod = limit` and `periodEnd = authorize_time + period`.
4. For a given `(account,key,token)`, there is exactly one active `TokenLimit`; duplicate token entries in a single authorization MUST be rejected.

### CallScope

Call scoping uses explicit vectors in the protocol model:

```text
CallScope {
    target: address,
    selector_rules: Vec<SelectorRule>,   // [] => any selector on this target
}
```

Solidity ABI representation for precompile methods:

```solidity
struct CallScope {
    address target;
    SelectorRule[] selectorRules;
}
```

Solidity ABI and protocol semantics match directly:

1. `selectorRules = []` allows any selector on `target`.
2. `selectorRules = [r1, ...]` allows exactly the listed selectors.
3. To remove a target scope in the Solidity precompile API, callers MUST use `removeAllowedCalls(keyId, target)`.

`selector_rules` behavior:

1. `[]`: allow any selector.
2. `[r1, r2, ...]`: allow exactly the listed selector rules.

In the Solidity precompile API, omitting a target scope blocks that target; `selectorRules = []` does not.

### SelectorRule

```text
SelectorRule {
    selector: bytes4,
    recipients: Vec<address>, // [] => any recipient, [a1, ...] => only listed recipients
}
```

Solidity ABI representation for precompile methods:

```solidity
struct SelectorRule {
    bytes4 selector;
    address[] recipients;
}
```

Solidity ABI and protocol semantics match directly:

1. `recipients = []` allows any recipient for that selector.
2. `recipients = [a1, ...]` constrains the selector to that recipient set.

`SelectorRule.recipients` behavior:

1. `[]` => no calldata recipient checks for this selector.
2. `[a1, a2, ...]` => enforce `arg0` recipient membership for this selector.
3. Selector rules MUST be unique per target (`selector` appears at most once).

Supported constrained selectors in this TIP:

1. `0xa9059cbb` => `transfer(address,uint256)`
2. `0x095ea7b3` => `approve(address,uint256)`
3. `0x95777d59` => `transferWithMemo(address,uint256,bytes32)`

If a selector rule uses `recipients = [..]`, then:

1. `target` MUST be a TIP-20 token address.
2. `selector` MUST be one of the constrained selectors above.
3. Otherwise, key authorization MUST be rejected.

For these selectors, the constrained field is ABI argument `0` (the first `address` argument).

Selector width is fixed at 4 bytes.

1. Each `SelectorRule.selector` MUST be exactly 4 bytes.
2. Implementations MUST revert when decoding or accepting any selector whose length is not exactly 4 bytes.
3. Selectorless calls (`calldata.length < 4`) and fallback/receive routing are scope-matchable only for address-only scopes (`selector_rules = []`). They MUST be rejected when explicit selector matching is required.
4. Contracts with non-standard selector parsing are NOT supported.

Examples:

1. `{ target: 0x123, selector_rules: [{selector: 0xaabbccdd, recipients: []}, {selector: 0xeeff0011, recipients: []}] }`: allow two selectors on one target.
2. `{ target: 0x123, selector_rules: [] }`: address-only scoping (any calldata shape on `0x123`, including selectorless/fallback-style calls).
3. `allowedCalls = None`: unrestricted key.
4. `allowedCalls = Some([])`: key is authorized but cannot make scoped calls.
5. `{ target: tokenX, selector_rules: [{selector: 0xa9059cbb, recipients: [0xReceiver]}] }`: allow `transfer` only when `to == 0xReceiver`.
6. `{ target: tokenX, selector_rules: [{selector: 0xa9059cbb, recipients: [0xA, 0xB]}] }`: allow `transfer` only when `to` is in `{0xA, 0xB}`.
7. Distinct target scopes are independent: allowing selector `s` on target `A` never allows selector `s` on target `B`.

### KeyAuthorization

Existing fields remain, with a trailing optional call-scope field:

```text
KeyAuthorization {
    chain_id: u64,
    key_type: SignatureType,
    key_id: address,
    expiry: Option<u64>,
    limits: Option<Vec<TokenLimit>>,
    allowed_calls: Option<Vec<CallScope>>,  // New trailing field
}
```

## Interface Changes

### Events

```solidity
/// @notice Emitted when an access key spends tokens against a spending limit
/// @param account The account whose key was used
/// @param publicKey The public key (address) that initiated the spend
/// @param token The token address being spent
/// @param amount The amount spent in this transaction
/// @param remainingLimit The remaining spending limit after this spend
event AccessKeySpend(
    address indexed account,
    address indexed publicKey,
    address indexed token,
    uint256 amount,
    uint256 remainingLimit
);
```

This event MUST be emitted whenever an access-key transaction deducts from a spending limit (one-time or periodic).

### IAccountKeychain.sol

```solidity
/// @notice Authorizes a key with enhanced permissions
/// @param keyId The key identifier (address derived from public key)
/// @param signatureType 0: secp256k1, 1: P256, 2: WebAuthn
/// @param expiry Block timestamp when key expires
/// @param enforceLimits Whether spending limits are enforced for this key
/// @param spendingLimits Token spending limits (may include periodic limits)
/// @param allowAnyCalls Whether the key is unrestricted (`true`) or scoped by `allowedCalls` (`false`)
/// @param allowedCalls Per-target call scopes for this key.
function authorizeKey(
    address keyId,
    SignatureType signatureType,
    uint64 expiry,
    bool enforceLimits,
    TokenLimit[] calldata spendingLimits,
    bool allowAnyCalls,
    CallScope[] calldata allowedCalls
) external;

/// @notice Creates or replaces one target scope for a key
/// @dev Root key only. If `target` does not exist, creates a new scope; otherwise replaces it atomically.
/// @dev `scope.selectorRules = []` allows any selector on `scope.target`; it does not block the target.
/// @dev If a selector rule has `recipients`, `target` MUST be TIP-20 and `selector` MUST be transfer/approve (+memo).
/// @dev For each selector rule, `recipients = []` means no recipient restriction.
function setAllowedCalls(
    address keyId,
    CallScope calldata scope
) external;

/// @notice Removes one target scope for a key
function removeAllowedCalls(address keyId, address target) external;

/// @notice Returns whether a key is call-scoped together with its configured call scopes
/// @dev `isScoped = false` means unrestricted.
/// @dev `isScoped = true && calls.length == 0` means scoped deny-all.
function getAllowedCalls(
    address account,
    address keyId
) external view returns (bool isScoped, CallScope[] memory calls);

/// @notice Returns remaining limit for a token, accounting for period resets
function getRemainingLimit(
    address account,
    address keyId,
    address token
) external view returns (uint256 remaining, uint64 periodEnd);
```

`getAllowedCalls(account, keyId)` semantics:

1. `isScoped = false, calls = []`: unrestricted key.
2. `isScoped = true, calls = []`: scoped key with no allowed targets.
3. `isScoped = true, calls = [c1, ...]`: scoped key with the listed allowlist.
4. Missing, revoked, or expired access keys return `isScoped = true, calls = []`.

## Semantic Behavior

### Periodic Limit Reset Logic

On each spend attempt for `(account, key, token)`:

1. Implementations MUST load the configured `TokenLimit` and runtime `TokenLimitState`.
2. If `period == 0`, the limit is one-time and no period rollover is applied.
3. If `period > 0` and `block.timestamp >= periodEnd`, implementations MUST reset `remainingInPeriod` to `limit` and advance `periodEnd` by whole multiples of `period` so that `periodEnd > block.timestamp`.
4. If `amount > remainingInPeriod`, implementations MUST revert `SpendingLimitExceeded()`.
5. Otherwise, implementations MUST decrement `remainingInPeriod` by `amount`.

`updateSpendingLimit(account,key,token,newLimit)` semantics:

1. MUST update the configured `limit` for that `(account,key,token)`.
2. MUST set `remainingInPeriod = newLimit`.
3. MUST NOT change `period`.
4. MUST NOT change `periodEnd`.
5. Therefore changing `period` requires re-authorizing the key (or removing and recreating that token limit entry).

### Call Validation Logic

Call-scope checks use map lookups keyed by `(account_key, target, selector)` plus optional selector-level recipient allowlists.

Scoped-call validation is a transaction-validity check performed before execution. If any call fails validation, the transaction is invalid and MUST NOT enter execution.

#### Access-Key Contract Creation Ban

If a transaction is signed with an access key (`key != Address::ZERO`), contract creation MUST be rejected in all configurations.

This ban applies regardless of:

1. Whether `allowed_calls` is `None` or `Some(...)`.
2. Whether any target scope has `selector_rules = []` (allow-any-selector).
3. Whether the creation call appears in a batch.

Only the root key (`key == Address::ZERO`) may submit contract-creation calls; this is not a global create ban.

#### Single Call Validation

For each call:

1. If the transaction uses an access key and the call is contract creation, implementations MUST reject the transaction as invalid before execution.
2. If `allowed_calls = None`, implementations MUST allow the call (subject to the contract-creation ban).
3. If `allowed_calls = Some(...)`, implementations MUST enforce target and selector matching.
4. If no target scope exists for `destination`, implementations MUST reject the transaction as invalid before execution.
5. If the target scope is `selector_rules = []`, implementations MUST allow the call, including selectorless/fallback-style calldata.
6. If the target scope has explicit selector rules and calldata does not provide at least 4 selector bytes, implementations MUST reject the transaction as invalid before execution.
7. If the target scope has explicit selector rules, there MUST be a rule for the selector; otherwise implementations MUST reject the transaction as invalid before execution.
8. If the matched rule has `recipients = []`, implementations MUST allow the call.
9. If the matched rule has `recipients = [a1, ...]`, implementations MUST decode ABI argument `0` as an `address` and require membership in that list.
10. For a selector rule with a non-empty `recipients` list, if calldata is shorter than `4 + 32` bytes, implementations MUST reject the transaction as invalid before execution.
11. For a selector rule with a non-empty `recipients` list, implementations MUST enforce canonical ABI `address` encoding for argument `0` (upper 12 bytes zero) before membership check; otherwise implementations MUST reject the transaction as invalid before execution.

#### Batch Validation

For AA transactions with multiple calls, each call MUST be validated independently before execution begins. If any call fails scope validation, the transaction is invalid and the batch MUST NOT enter execution.

### Root-Controlled Scope Updates

`setAllowedCalls` MUST be root-key-only and MUST apply create-or-replace semantics per target.

1. `setAllowedCalls(keyId, [])` MUST revert; an empty scope batch is ambiguous and MUST NOT act as a no-op or mode toggle.
2. `selectorRules = []` sets `selector_rules = None` semantics (any selector allowed on `target`).
3. `removeAllowedCalls(keyId, target)` disables that target scope.
4. Implementations MUST enforce at most one scope per target for each `(account, key)`.
5. Selector rules MUST be unique by `selector` within a target scope.
6. If any rule has `recipients = Some([..])`, `target` MUST be a TIP-20 token address.
7. If any rule has `recipients = Some([..])`, its `selector` MUST be one of:
   1. `0xa9059cbb` (`transfer(address,uint256)`)
   2. `0x095ea7b3` (`approve(address,uint256)`)
   3. `0x95777d59` (`transferWithMemo(address,uint256,bytes32)`)
8. If any rule has `recipients = Some([..])`, each recipient in that list MUST be non-zero.
9. If any rule has `recipients = Some([..])`, recipients in that list MUST be unique.
10. If any selector-rule validity rule is violated, implementations MUST reject the authorization (or revert `setAllowedCalls`).

Rationale for rule 2 (`removeAllowedCalls` disables a target scope):

1. This avoids unbounded gas from deletion-time slot iteration.
2. Prior selector rows may remain in state, but the removed target scope no longer participates in matching.

### Interaction Rules

1. Keys may mix one-time and periodic token limits.
2. Spending limits and call scopes are independent checks; both must pass.
3. `updateSpendingLimit()` updates limit and `remainingInPeriod`, but does not change `period` or `periodEnd`.
4. `allowed_calls = None` is unrestricted for non-create calls; `Some([])` is scoped mode with no allowed calls.
5. Every scope has an explicit target address, so there is no wildcard-target precedence ambiguity.
6. Per-target updates are create-or-replace and duplicate target scopes are not allowed.
7. Selector-level recipient allowlists are optional and only valid for TIP-20 targets and the constrained selectors above.
8. Selector-level recipient allowlists are checked after selector match and before call execution.
9. This TIP does not introduce generic calldata predicates, offset math, or wildcard argument matching.

Wallet UX recommendation (non-consensus):

1. Wallets SHOULD default to scoped keys (non-empty `selectorRules`) and require explicit user opt-in for unrestricted target scopes (`selectorRules = []`).

## Gas And Complexity Bounds

This TIP only specifies the additional intrinsic gas delta for call scopes in handler-side `key_authorization` charging.

Existing key-authorization charging (signature verification, existing-key read, base key write, token-limit writes, and buffer) remains unchanged.

Definitions:

1. `SSTORE_SET = sstore_set_without_load_cost`.

2. `S` = number of targets with configured call scope.

3. `K` = total selector rules across all configured targets.

4. `C` = total selector rules with a non-empty `recipients` list.

5. `W` = total recipient entries across all constrained selector rules.
   Scoped-call storage writes counted for intrinsic gas:

6. Restricted-mode marker: `1` slot when `allowed_calls` is `Some(...)`.

7. Each target scope writes `3` slots: target-set length, target-set value, and target-set position.

8. Each selector rule writes `3` slots: selector-set length, selector-set value, and selector-set position.

9. Each recipient-constrained selector writes `1` additional slot for recipient-set length.

10. Each recipient entry writes `2` slots: recipient-set value and recipient-set position.

```text
gas_key_authorization_new = gas_key_authorization_existing
                          + SSTORE_SET * scope_slots

scope_slots = 0                    if allowed_calls is None
            = 1                    if allowed_calls is Some([])   // explicit restricted-mode marker
            = 1 + 3*S + 3*K + C + 2*W    if allowed_calls is Some(scopes)
```

Justification for `1 + 3*S + 3*K + C + 2*W`: `1` stores restricted mode, each target scope materializes as three set writes, each selector rule materializes as three set writes, each constrained selector writes one recipient-set length slot, and each recipient writes two set-membership slots.

Bounds:

1. Implementations MUST reject any selector rule with a non-empty `recipients` list whose `target` is not a TIP-20 token address.
2. Implementations MUST reject any selector rule with a non-empty `recipients` list and selector outside the fixed constrained-selector set.
3. Implementations MUST reject duplicate selector rules for the same `(target, selector)`.
4. Implementations MUST reject duplicate recipients inside a selector rule.

No additional flat gas is specified here for precompile methods (`setAllowedCalls`, `getAllowedCalls`, etc.); those are charged by normal EVM metering at execution time.

## Encoding

### Signing Format

Authorization digest format:

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

limits = rlp([token, limit])              if period == 0
       = rlp([token, limit, period])      if period > 0
```

RLP safety note:

1. Implementations MUST use canonical RLP encoding for all fields.
2. The signed payload is a typed RLP list; distinct field tuples produce distinct canonical encodings (no cross-field preimage ambiguity under canonical RLP).

### Transaction Authorization RLP

```text
KeyAuthorization := RLP([
    chain_id: u64,
    key_type: u8,
    key_id: address,
    expiry?: uint64,
    limits?: [TokenLimit, ...],
    allowed_calls?: [CallScope, ...]
])

TokenLimit := RLP([
    token: address,
    limit: uint256,
    period: uint64
])

// Canonical one-time form omits `period` entirely.
// Omitted `period` decodes to `period = 0`, i.e. a non-periodic one-time spending limit.
TokenLimit(one-time) := RLP([
    token: address,
    limit: uint256
])

CallScope := RLP([
    target: address,
    selector_rules: [SelectorRule, ...] | []
])

SelectorRule := RLP([
    selector: bytes4,
    recipients: [address, ...] | []
])
```

Optional encoding rules:

1. Optional scalar fields (`expiry`) use `None => 0x80`.
2. `limits = None` uses `0x80`.
3. Top-level `allowed_calls = None` is canonically omitted on wire. Implementations MUST also accept explicit `0x80` for `allowed_calls = None` as equivalent non-canonical input.
4. Nested scope-list fields (`selector_rules` and `recipients`) are always encoded explicitly. Allow-all uses RLP empty list (`0xc0`).
5. Non-empty list values encode as normal lists.
6. For `TokenLimit`, one-time limits (`period == 0`) canonically use the two-field form. Implementations MUST also accept the explicit three-field form with `period = 0` as equivalent non-canonical input.
7. Each `SelectorRule.selector` MUST decode to exactly 4 bytes; otherwise the authorization MUST be rejected.

***

## Precompile Storage Changes

Current layout:

1. `keys[account][keyId] -> AuthorizedKey`
2. `spending_limits[(account,keyId)][token] -> U256`

Additive periodic-limit layout:

| Mapping | Type | Description |
|---------|------|-------------|
| `spending_limits[account_key][token]` | `U256` | Remaining amount / `remainingInPeriod` |
| `spending_limit_period_state[account_key][token]` | struct `{ max, period, period_end }` | Periodic limit metadata |

Call-scope storage is account-scoped and represented as nested scope membership, with a key-level scoped/unrestricted flag:

| Path | Type | Description |
|------|------|-------------|
| `key_scopes[account_key].is_scoped` | `bool` | Whether the key is unrestricted or uses scoped target membership |
| `key_scopes[account_key].targets` | `Set<address>` | Scoped target membership |
| `key_scopes[account_key].target_scopes[target].selectors` | `Set<u32>` | Explicit selector membership |
| `key_scopes[account_key].target_scopes[target].selector_scopes[selector].recipients` | `Set<address>` | Selector-level recipient membership |

Absent target or selector entries represent disabled inner scopes; implementations do not need separate target-level or selector-level mode bits.

`account_key = keccak256(account || key_id)` to avoid cross-account collisions for shared key IDs.

Implementations MAY maintain additional internal indexes or equivalent layouts so long as semantics remain unchanged.

## Hardfork-Gated Features

The following MUST be fork-gated:

1. New `TokenLimit` decode/encode behavior.
2. `allowed_calls` decode/encode behavior.
3. `selector_rules` decode/encode behavior.
4. Periodic reset logic.
5. Call-scope validation logic.
6. Selector-rule recipient-allowlist calldata validation logic.
7. New precompile storage writes/reads for periodic + call-scope data.
8. New precompile storage writes/reads for selector-level recipient allowlists.
9. Updated precompile read APIs (`getAllowedCalls(account,key)`, richer `getRemainingLimit`).
10. New mutator function `setAllowedCalls`, which can only be called by root key.
11. Global ban on contract creation when using access keys.
12. Selector-width enforcement (`selector length == 4` only).
13. Constrained-selector allowlist and argument-0 canonical address checks.
14. TIP-20 target verification for selector rules with recipient allowlists.

Pre-fork blocks MUST replay with pre-fork semantics to preserve state roots.

***

# Invariants

1. `periodEnd` is monotonic and never set to the past.
2. `remainingInPeriod <= limit` after any operation.
3. Expiry check runs before spending and call-scope checks.
4. If `key != Address::ZERO`, any contract-creation call MUST cause the transaction to be rejected as invalid before execution, regardless of `allowed_calls`.
5. `allowed_calls = None` allows all non-create calls; `allowed_calls = Some(...)` requires target+selector-rule match and otherwise causes the transaction to be rejected as invalid before execution.
6. In scoped mode, calldata must contain at least 4 selector bytes only when explicit selector matching is required; address-only scopes allow selectorless/fallback-style calldata.
7. For each `(account, key)`, target scopes are unique, selector rules are unique per target, and recipients are unique per selector rule.
8. `setAllowedCalls(..., scope.selectorRules = [])` allows any selector on that target; `removeAllowedCalls(keyId, target)` disables that target scope.
9. Selector rules with recipient allowlists are valid only for TIP-20 targets and only for the fixed constrained selector set.
10. For recipient-allowlisted rules, calldata argument `0` must be a canonically encoded ABI address and must be in the configured recipient set.
11. In the Solidity ABI, `selectorRules[i].recipients = []` means that selector has no recipient restriction.

## Test Cases

1. Periodic reset after elapsed period.
2. No rollover of unused periodic allowance.
3. Address + multi-selector scope allow.
4. Address-only allow (`selector_rules=[]`).
5. Deny when no scope matches.
6. `allowed_calls=None` allows all non-create calls.
7. `allowed_calls=Some([])` denies all calls.
8. Mixed one-time and periodic token limits.
9. Existing keys continue to function after the fork.
10. Batch validation rejects the transaction before execution when any call is invalid.
11. Shared key IDs across accounts cannot overwrite each other’s scopes.
12. Reject calls that do not provide at least 4 selector bytes when explicit selector matching is required.
13. `setAllowedCalls(..., scope.selectorRules = [])` allows any selector on that target.
14. `setAllowedCalls` create-or-replace semantics are enforced.
15. `removeAllowedCalls(keyId, target)` removes that target scope; if no target scopes remain, the key stays scoped but matches no calls.
16. Address-only scopes allow selectorless/fallback-style calls to the scoped target.
17. Access-key transactions with CREATE as first call are rejected.
18. Access-key transactions with any CREATE in a batch are rejected.
19. For constrained TIP-20 selectors (`transfer`, `approve`, `transferWithMemo`), calls succeed iff calldata argument `0` is in the configured recipient set.
20. Single-recipient and multi-recipient selector rules both enforce the same membership rule.
21. Reject the transaction before execution when a selector rule with a recipient allowlist is matched and calldata is shorter than `4 + 32` bytes.
22. Reject the transaction before execution when a selector rule with a recipient allowlist is matched and ABI argument `0` is not canonically encoded as an address.
23. Reject selector rules with recipient allowlists for selectors outside the fixed constrained-selector set.
24. Reject duplicate selector rules for the same `(target, selector)`.
25. Reject duplicate recipients within a selector rule.
26. Reject key authorization when selector rules with recipient allowlists are used on a non-TIP-20 target.

## References

* [AccountKeychain docs](https://docs.tempo.xyz/protocol/transactions/AccountKeychain)
* [Tempo Transactions](https://docs.tempo.xyz/guide/tempo-transaction)
* [IAccountKeychain.sol](https://github.com/tempoxyz/tempo-std/blob/master/src/interfaces/IAccountKeychain.sol)
* [GitHub Issue #1865](https://github.com/tempoxyz/tempo/issues/1865) - Periodic spending limits
* [GitHub Issue #1491](https://github.com/tempoxyz/tempo/issues/1491) - Destination address scoping
