tempo_contracts/precompiles/
account_keychain.rs

1pub use IAccountKeychain::{
2    IAccountKeychainErrors as AccountKeychainError, IAccountKeychainEvents as AccountKeychainEvent,
3};
4
5use alloy::sol;
6
7sol! {
8    /// Account Keychain interface for managing authorized keys
9    ///
10    /// This precompile allows accounts to authorize secondary keys with:
11    /// - Different signature types (secp256k1, P256, WebAuthn)
12    /// - Expiry times for key rotation
13    /// - Per-token spending limits for security
14    ///
15    /// Only the main account key can authorize/revoke keys, while secondary keys
16    /// can be used for regular transactions within their spending limits.
17    #[derive(Debug, PartialEq, Eq)]
18    #[sol(rpc, abi)]
19    interface IAccountKeychain {
20        enum SignatureType {
21            Secp256k1,
22            P256,
23            WebAuthn,
24        }
25
26        /// Token spending limit structure
27        struct TokenLimit {
28            address token;
29            uint256 amount;
30        }
31
32        /// Key information structure
33        struct KeyInfo {
34            SignatureType signatureType;
35            address keyId;
36            uint64 expiry;
37            bool enforceLimits;
38            bool isRevoked;
39        }
40        /// Emitted when a new key is authorized
41        event KeyAuthorized(address indexed account, address indexed publicKey, uint8 signatureType, uint64 expiry);
42
43        /// Legacy event for backwards compatibility
44        event KeyAuthorized(address indexed account, bytes32 indexed publicKey, uint8 signatureType, uint64 expiry);
45
46        /// Emitted when a key is revoked
47        event KeyRevoked(address indexed account, address indexed publicKey);
48
49        /// Legacy event for backwards compatibility
50        event KeyRevoked(address indexed account, bytes32 indexed publicKey);
51
52        /// Emitted when a spending limit is updated
53        event SpendingLimitUpdated(address indexed account, address indexed publicKey, address indexed token, uint256 newLimit);
54
55        /// Legacy event for backwards compatibility
56        event SpendingLimitUpdated(address indexed account, bytes32 indexed publicKey, address indexed token, uint256 newLimit);
57
58        /// Authorize a new key for the caller's account
59        /// @param keyId The key identifier (address derived from public key)
60        /// @param signatureType 0: secp256k1, 1: P256, 2: WebAuthn
61        /// @param expiry Block timestamp when the key expires (u64::MAX for never expires)
62        /// @param enforceLimits Whether to enforce spending limits for this key
63        /// @param limits Initial spending limits for tokens (only used if enforceLimits is true)
64        function authorizeKey(
65            address keyId,
66            SignatureType signatureType,
67            uint64 expiry,
68            bool enforceLimits,
69            TokenLimit[] calldata limits
70        ) external;
71
72        /// Revoke an authorized key
73        /// @param publicKey The public key to revoke
74        function revokeKey(address keyId) external;
75
76        /// Update spending limit for a key-token pair
77        /// @param publicKey The public key
78        /// @param token The token address
79        /// @param newLimit The new spending limit
80        function updateSpendingLimit(
81            address keyId,
82            address token,
83            uint256 newLimit
84        ) external;
85
86        /// Get key information
87        /// @param account The account address
88        /// @param publicKey The public key
89        /// @return Key information
90        function getKey(address account, address keyId) external view returns (KeyInfo memory);
91
92        /// Get remaining spending limit
93        /// @param account The account address
94        /// @param publicKey The public key
95        /// @param token The token address
96        /// @return Remaining spending amount
97        function getRemainingLimit(
98            address account,
99            address keyId,
100            address token
101        ) external view returns (uint256);
102
103        /// Get the key used in the current transaction
104        /// @return The keyId used in the current transaction
105        function getTransactionKey() external view returns (address);
106
107        // Errors
108        error UnauthorizedCaller();
109        error KeyAlreadyExists();
110        error KeyNotFound();
111        error KeyExpired();
112        error SpendingLimitExceeded();
113        error InvalidSignatureType();
114        error ZeroPublicKey();
115        error ExpiryInPast();
116        error KeyAlreadyRevoked();
117    }
118}
119
120impl AccountKeychainError {
121    /// Creates an error for unauthorized caller.
122    pub const fn unauthorized_caller() -> Self {
123        Self::UnauthorizedCaller(IAccountKeychain::UnauthorizedCaller {})
124    }
125
126    /// Creates an error for key already exists.
127    pub const fn key_already_exists() -> Self {
128        Self::KeyAlreadyExists(IAccountKeychain::KeyAlreadyExists {})
129    }
130
131    /// Creates an error for key not found.
132    pub const fn key_not_found() -> Self {
133        Self::KeyNotFound(IAccountKeychain::KeyNotFound {})
134    }
135
136    /// Creates an error for key expired.
137    pub const fn key_expired() -> Self {
138        Self::KeyExpired(IAccountKeychain::KeyExpired {})
139    }
140
141    /// Creates an error for spending limit exceeded.
142    pub const fn spending_limit_exceeded() -> Self {
143        Self::SpendingLimitExceeded(IAccountKeychain::SpendingLimitExceeded {})
144    }
145
146    /// Creates an error for invalid signature type.
147    pub const fn invalid_signature_type() -> Self {
148        Self::InvalidSignatureType(IAccountKeychain::InvalidSignatureType {})
149    }
150
151    /// Creates an error for zero public key.
152    pub const fn zero_public_key() -> Self {
153        Self::ZeroPublicKey(IAccountKeychain::ZeroPublicKey {})
154    }
155
156    /// Creates an error for expiry timestamp in the past.
157    pub const fn expiry_in_past() -> Self {
158        Self::ExpiryInPast(IAccountKeychain::ExpiryInPast {})
159    }
160
161    /// Creates an error for when a key_id has already been revoked.
162    /// Once revoked, a key_id can never be re-authorized for the same account.
163    /// This prevents replay attacks where a revoked key's authorization is reused.
164    pub const fn key_already_revoked() -> Self {
165        Self::KeyAlreadyRevoked(IAccountKeychain::KeyAlreadyRevoked {})
166    }
167}