Skip to main content

tempo_primitives/transaction/
mod.rs

1pub mod envelope;
2pub mod key_authorization;
3pub mod tempo_transaction;
4pub mod tt_authorization;
5pub mod tt_signature;
6pub mod tt_signed;
7
8pub use tt_authorization::{MAGIC, RecoveredTempoAuthorization, TempoSignedAuthorization};
9// Re-export Authorization from alloy for convenience
10pub use tt_signature::{
11    KeychainSignature, KeychainVersion, KeychainVersionError, PrimitiveSignature, TempoSignature,
12    derive_p256_address,
13};
14
15pub use crate::address::TIP20_TOKEN_PREFIX as TIP20_PAYMENT_PREFIX;
16pub use alloy_eips::eip7702::Authorization;
17pub use envelope::{TempoTxEnvelope, TempoTxType, TempoTypedTransaction};
18pub use key_authorization::{
19    CallScope, KeyAuthorization, KeyAuthorizationChainIdError, SelectorRule,
20    SignedKeyAuthorization, TokenLimit,
21};
22pub use tempo_transaction::{
23    Call, MAX_WEBAUTHN_SIGNATURE_LENGTH, P256_SIGNATURE_LENGTH, SECP256K1_SIGNATURE_LENGTH,
24    SignatureType, TEMPO_EXPIRING_NONCE_KEY, TEMPO_EXPIRING_NONCE_MAX_EXPIRY_SECS,
25    TEMPO_TX_TYPE_ID, TempoTransaction, validate_calls,
26};
27pub use tt_signed::AASigned;
28
29use alloc::vec::Vec;
30use alloy_consensus::SignableTransaction;
31use alloy_primitives::{Address, B256, Signature, U256, uint};
32
33/// Computes the sender-scoped transaction identifier used for replay-sensitive features.
34///
35/// The identifier is `keccak256(encode_for_signing || sender)`, making it unique per recovered
36/// sender while remaining invariant to signatures that do not change the signed payload.
37pub(crate) fn unique_tx_identifier_from_signable<T>(tx: &T, sender: Address) -> B256
38where
39    T: SignableTransaction<Signature>,
40{
41    let mut buf = Vec::with_capacity(tx.payload_len_for_signature() + sender.as_slice().len());
42    tx.encode_for_signing(&mut buf);
43    buf.extend_from_slice(sender.as_slice());
44    alloy_primitives::keccak256(buf)
45}
46
47/// Scaling factor for converting gas prices (attodollars) to TIP-20 token amounts (microdollars).
48///
49/// This factor is 10^12, which converts from attodollars (10^-18 USD) to microdollars (10^-6 USD):
50/// - Gas prices are in attodollars at 10^-18 USD precision
51/// - TIP-20 tokens use 6 decimals (microdollars at 10^-6 USD precision)
52/// - Conversion: attodollars / 10^12 = microdollars
53pub const TEMPO_GAS_PRICE_SCALING_FACTOR: U256 = uint!(1_000_000_000_000_U256);
54
55/// Calculates gas balance spending in TIP-20 token units (microdollars).
56///
57/// Takes gas parameters in attodollars and converts to microdollars (TIP-20 token units).
58/// Formula: (gas_limit × gas_price) / 10^12 = microdollars
59pub fn calc_gas_balance_spending(gas_limit: u64, gas_price: u128) -> U256 {
60    U256::from(gas_limit)
61        .saturating_mul(U256::from(gas_price))
62        .div_ceil(TEMPO_GAS_PRICE_SCALING_FACTOR)
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn calc_gas_balance_spending_variations() {
71        // zero gas → zero spending
72        assert_eq!(calc_gas_balance_spending(0, 1_000_000_000), U256::ZERO);
73
74        // zero price → zero spending
75        assert_eq!(calc_gas_balance_spending(21000, 0), U256::ZERO);
76
77        // both zero
78        assert_eq!(calc_gas_balance_spending(0, 0), U256::ZERO);
79
80        // exact division: 1 gas * 10^12 attodollars = 1 microdollar
81        assert_eq!(
82            calc_gas_balance_spending(1, 1_000_000_000_000),
83            U256::from(1)
84        );
85
86        // rounds up via div_ceil: 1 gas * 1 attodollar → ceil(1 / 10^12) = 1
87        assert_eq!(calc_gas_balance_spending(1, 1), U256::from(1));
88
89        // typical tx: 21000 gas * 1 gwei (10^9 attodollars)
90        // = 21000 * 10^9 / 10^12 = 21000 / 1000 = 21
91        assert_eq!(
92            calc_gas_balance_spending(21000, 1_000_000_000),
93            U256::from(21)
94        );
95
96        // large values don't overflow (saturating_mul)
97        let result = calc_gas_balance_spending(u64::MAX, u128::MAX);
98        assert!(result > U256::ZERO);
99    }
100}