alloy_primitives/
common.rs

1use crate::Address;
2
3#[cfg(feature = "rlp")]
4use alloy_rlp::{Buf, BufMut, Decodable, EMPTY_STRING_CODE, Encodable};
5
6/// The `to` field of a transaction. Either a target address, or empty for a
7/// contract creation.
8#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
9#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary, proptest_derive::Arbitrary))]
10#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
11#[doc(alias = "TransactionKind")]
12pub enum TxKind {
13    /// A transaction that creates a contract.
14    #[default]
15    Create,
16    /// A transaction that calls a contract or transfer.
17    Call(Address),
18}
19
20impl From<Option<Address>> for TxKind {
21    /// Creates a `TxKind::Call` with the `Some` address, `None` otherwise.
22    #[inline]
23    fn from(value: Option<Address>) -> Self {
24        match value {
25            None => Self::Create,
26            Some(addr) => Self::Call(addr),
27        }
28    }
29}
30
31impl From<Address> for TxKind {
32    /// Creates a `TxKind::Call` with the given address.
33    #[inline]
34    fn from(value: Address) -> Self {
35        Self::Call(value)
36    }
37}
38
39impl From<TxKind> for Option<Address> {
40    /// Returns the address of the contract that will be called or will receive the transfer.
41    #[inline]
42    fn from(value: TxKind) -> Self {
43        value.to().copied()
44    }
45}
46
47impl TxKind {
48    /// Returns the address of the contract that will be called or will receive the transfer.
49    pub const fn to(&self) -> Option<&Address> {
50        match self {
51            Self::Create => None,
52            Self::Call(to) => Some(to),
53        }
54    }
55
56    /// Consumes the type and returns the address of the contract that will be called or will
57    /// receive the transfer.
58    pub const fn into_to(self) -> Option<Address> {
59        match self {
60            Self::Create => None,
61            Self::Call(to) => Some(to),
62        }
63    }
64
65    /// Returns true if the transaction is a contract creation.
66    #[inline]
67    pub const fn is_create(&self) -> bool {
68        matches!(self, Self::Create)
69    }
70
71    /// Returns true if the transaction is a contract call.
72    #[inline]
73    pub const fn is_call(&self) -> bool {
74        matches!(self, Self::Call(_))
75    }
76
77    /// Calculates a heuristic for the in-memory size of this object.
78    #[inline]
79    pub const fn size(&self) -> usize {
80        core::mem::size_of::<Self>()
81    }
82}
83
84#[cfg(feature = "rlp")]
85impl Encodable for TxKind {
86    fn encode(&self, out: &mut dyn BufMut) {
87        match self {
88            Self::Call(to) => to.encode(out),
89            Self::Create => out.put_u8(EMPTY_STRING_CODE),
90        }
91    }
92
93    fn length(&self) -> usize {
94        match self {
95            Self::Call(to) => to.length(),
96            Self::Create => 1, // EMPTY_STRING_CODE is a single byte
97        }
98    }
99}
100
101#[cfg(feature = "rlp")]
102impl Decodable for TxKind {
103    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
104        if let Some(&first) = buf.first() {
105            if first == EMPTY_STRING_CODE {
106                buf.advance(1);
107                Ok(Self::Create)
108            } else {
109                let addr = <Address as Decodable>::decode(buf)?;
110                Ok(Self::Call(addr))
111            }
112        } else {
113            Err(alloy_rlp::Error::InputTooShort)
114        }
115    }
116}
117
118#[cfg(feature = "serde")]
119impl serde::Serialize for TxKind {
120    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
121        self.to().serialize(serializer)
122    }
123}
124
125#[cfg(feature = "serde")]
126impl<'de> serde::Deserialize<'de> for TxKind {
127    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
128        Ok(Option::<Address>::deserialize(deserializer)?.into())
129    }
130}