Skip to main content

xrpl_common_stdlib/types/
transaction_type.rs

1use crate::fields::decoder::{FieldDecoder, FromCurrentTx, decode_exact};
2use crate::types::decode_error::DecodeError;
3
4/// The type of any given XRPL transaction.
5///
6/// This enum maps to the transaction type codes used in the XRPL protocol.
7/// Each variant corresponds to a specific transaction type with its associated
8/// numeric code.
9///
10/// ## Derived Traits
11///
12/// - `Debug`: Useful for development and debugging
13/// - `Clone`: Automatically derived with Copy for consistency
14/// - `Copy`: Efficient for this enum (2 bytes due to `#[repr(i16)]`)
15/// - `PartialEq, Eq`: Enable transaction type comparisons
16///
17/// The `Copy` trait is appropriate here because:
18/// - The enum is only 2 bytes, making copies extremely cheap
19/// - Transaction types are frequently checked and compared
20/// - Implicit copying improves ergonomics
21#[repr(i16)]
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum TransactionType {
24    Invalid = -1,
25    Payment = 0,
26    EscrowCreate = 1,
27    EscrowFinish = 2,
28    AccountSet = 3,
29    EscrowCancel = 4,
30    SetRegularKey = 5,
31    NickNameSet = 6,
32    OfferCreate = 7,
33    OfferCancel = 8,
34    Contract = 9,
35    TicketCreate = 10,
36    TicketCancel = 11,
37    SignerListSet = 12,
38    PaymentChannelCreate = 13,
39    PaymentChannelFund = 14,
40    PaymentChannelClaim = 15,
41    CheckCreate = 16,
42    CheckCash = 17,
43    CheckCancel = 18,
44    DepositPreauth = 19,
45    TrustSet = 20,
46    AccountDelete = 21,
47    SetHook = 22,
48    NFTokenMint = 25,
49    NFTokenBurn = 26,
50    NFTokenCreateOffer = 27,
51    NFTokenCancelOffer = 28,
52    NFTokenAcceptOffer = 29,
53    Clawback = 30,
54    AMMCreate = 35,
55    AMMDeposit = 36,
56    AMMWithdraw = 37,
57    AMMVote = 38,
58    AMMBid = 39,
59    AMMDelete = 40,
60    XChainCreateClaimID = 41,
61    XChainCommit = 42,
62    XChainClaim = 43,
63    XChainAccountCreateCommit = 44,
64    XChainAddClaimAttestation = 45,
65    XChainAddAccountCreateAttestation = 46,
66    XChainModifyBridge = 47,
67    XChainCreateBridge = 48,
68    DIDSet = 49,
69    DIDDelete = 50,
70    OracleSet = 51,
71    OracleDelete = 52,
72    EnableAmendment = 100,
73    SetFee = 101,
74    UNLModify = 102,
75}
76
77impl From<[u8; 2]> for TransactionType {
78    fn from(value: [u8; 2]) -> Self {
79        let value_16 = i16::from_le_bytes(value.as_slice().try_into().expect("Incorrect length"));
80        value_16.into()
81    }
82}
83
84impl From<i16> for TransactionType {
85    fn from(value: i16) -> Self {
86        match value {
87            // List every single variant and its corresponding i16 value
88            -1 => TransactionType::Invalid,
89            0 => TransactionType::Payment,
90            1 => TransactionType::EscrowCreate,
91            2 => TransactionType::EscrowFinish,
92            3 => TransactionType::AccountSet,
93            4 => TransactionType::EscrowCancel,
94            5 => TransactionType::SetRegularKey,
95            6 => TransactionType::NickNameSet,
96            7 => TransactionType::OfferCreate,
97            8 => TransactionType::OfferCancel,
98            9 => TransactionType::Contract,
99            10 => TransactionType::TicketCreate,
100            11 => TransactionType::TicketCancel,
101            12 => TransactionType::SignerListSet,
102            13 => TransactionType::PaymentChannelCreate,
103            14 => TransactionType::PaymentChannelFund,
104            15 => TransactionType::PaymentChannelClaim,
105            16 => TransactionType::CheckCreate,
106            17 => TransactionType::CheckCash,
107            18 => TransactionType::CheckCancel,
108            19 => TransactionType::DepositPreauth,
109            20 => TransactionType::TrustSet,
110            21 => TransactionType::AccountDelete,
111            22 => TransactionType::SetHook,
112            25 => TransactionType::NFTokenMint,
113            26 => TransactionType::NFTokenBurn,
114            27 => TransactionType::NFTokenCreateOffer,
115            28 => TransactionType::NFTokenCancelOffer,
116            29 => TransactionType::NFTokenAcceptOffer,
117            30 => TransactionType::Clawback,
118            35 => TransactionType::AMMCreate,
119            36 => TransactionType::AMMDeposit,
120            37 => TransactionType::AMMWithdraw,
121            38 => TransactionType::AMMVote,
122            39 => TransactionType::AMMBid,
123            40 => TransactionType::AMMDelete,
124            41 => TransactionType::XChainCreateClaimID,
125            42 => TransactionType::XChainCommit,
126            43 => TransactionType::XChainClaim,
127            44 => TransactionType::XChainAccountCreateCommit,
128            45 => TransactionType::XChainAddClaimAttestation,
129            46 => TransactionType::XChainAddAccountCreateAttestation,
130            47 => TransactionType::XChainModifyBridge,
131            48 => TransactionType::XChainCreateBridge,
132            49 => TransactionType::DIDSet,
133            50 => TransactionType::DIDDelete,
134            51 => TransactionType::OracleSet,
135            52 => TransactionType::OracleDelete,
136            100 => TransactionType::EnableAmendment,
137            101 => TransactionType::SetFee,
138            102 => TransactionType::UNLModify,
139
140            // If the value doesn't match any known variant, return an error
141            _ => TransactionType::Invalid,
142        }
143    }
144}
145
146impl From<TransactionType> for [u8; 2] {
147    fn from(value: TransactionType) -> Self {
148        // 1. Cast the enum variant `self` to its underlying i16 value.
149        let value_i16: i16 = value as i16;
150
151        // 2. Convert the i16 value into a fixed-size byte array ([u8; 2]).
152        let bytes_array: [u8; 2] = value_i16.to_le_bytes();
153
154        bytes_array
155    }
156}
157
158/// `FieldDecoder` for XRPL transaction types: decodes a 2-byte buffer via the existing
159/// (infallible) `i16` mapping, failing only if the host wrote a different number of bytes.
160impl FieldDecoder for TransactionType {
161    type Buffer = [u8; 2];
162
163    #[inline]
164    fn empty_buffer() -> Self::Buffer {
165        [0u8; 2]
166    }
167
168    #[inline]
169    fn decode(buf: Self::Buffer, bytes_written: usize) -> core::result::Result<Self, DecodeError> {
170        decode_exact(buf, bytes_written)
171    }
172}
173
174// Tx-only: the transaction type is a transaction field. Ledger objects carry `LedgerEntryType`
175// as `SField<u16, _>`, not `SField<TransactionType, _>`, so there is no `FromLedger` impl here.
176impl FromCurrentTx for TransactionType {}
177
178#[cfg(test)]
179mod tests {
180    use super::*;
181
182    #[test]
183    fn test_transaction_type_from_i16_valid() {
184        // Test all valid transaction types - documents the i16 -> enum mapping
185        assert_eq!(TransactionType::from(-1i16), TransactionType::Invalid);
186        assert_eq!(TransactionType::from(0i16), TransactionType::Payment);
187        assert_eq!(TransactionType::from(1i16), TransactionType::EscrowCreate);
188        assert_eq!(TransactionType::from(2i16), TransactionType::EscrowFinish);
189        assert_eq!(TransactionType::from(3i16), TransactionType::AccountSet);
190        assert_eq!(TransactionType::from(4i16), TransactionType::EscrowCancel);
191        assert_eq!(TransactionType::from(5i16), TransactionType::SetRegularKey);
192        assert_eq!(TransactionType::from(6i16), TransactionType::NickNameSet);
193        assert_eq!(TransactionType::from(7i16), TransactionType::OfferCreate);
194        assert_eq!(TransactionType::from(8i16), TransactionType::OfferCancel);
195        assert_eq!(TransactionType::from(9i16), TransactionType::Contract);
196        assert_eq!(TransactionType::from(10i16), TransactionType::TicketCreate);
197        assert_eq!(TransactionType::from(11i16), TransactionType::TicketCancel);
198        assert_eq!(TransactionType::from(12i16), TransactionType::SignerListSet);
199        assert_eq!(
200            TransactionType::from(13i16),
201            TransactionType::PaymentChannelCreate
202        );
203        assert_eq!(
204            TransactionType::from(14i16),
205            TransactionType::PaymentChannelFund
206        );
207        assert_eq!(
208            TransactionType::from(15i16),
209            TransactionType::PaymentChannelClaim
210        );
211        assert_eq!(TransactionType::from(16i16), TransactionType::CheckCreate);
212        assert_eq!(TransactionType::from(17i16), TransactionType::CheckCash);
213        assert_eq!(TransactionType::from(18i16), TransactionType::CheckCancel);
214        assert_eq!(
215            TransactionType::from(19i16),
216            TransactionType::DepositPreauth
217        );
218        assert_eq!(TransactionType::from(20i16), TransactionType::TrustSet);
219        assert_eq!(TransactionType::from(21i16), TransactionType::AccountDelete);
220        assert_eq!(TransactionType::from(22i16), TransactionType::SetHook);
221        assert_eq!(TransactionType::from(25i16), TransactionType::NFTokenMint);
222        assert_eq!(TransactionType::from(26i16), TransactionType::NFTokenBurn);
223        assert_eq!(
224            TransactionType::from(27i16),
225            TransactionType::NFTokenCreateOffer
226        );
227        assert_eq!(
228            TransactionType::from(28i16),
229            TransactionType::NFTokenCancelOffer
230        );
231        assert_eq!(
232            TransactionType::from(29i16),
233            TransactionType::NFTokenAcceptOffer
234        );
235        assert_eq!(TransactionType::from(30i16), TransactionType::Clawback);
236        assert_eq!(TransactionType::from(35i16), TransactionType::AMMCreate);
237        assert_eq!(TransactionType::from(36i16), TransactionType::AMMDeposit);
238        assert_eq!(TransactionType::from(37i16), TransactionType::AMMWithdraw);
239        assert_eq!(TransactionType::from(38i16), TransactionType::AMMVote);
240        assert_eq!(TransactionType::from(39i16), TransactionType::AMMBid);
241        assert_eq!(TransactionType::from(40i16), TransactionType::AMMDelete);
242        assert_eq!(
243            TransactionType::from(41i16),
244            TransactionType::XChainCreateClaimID
245        );
246        assert_eq!(TransactionType::from(42i16), TransactionType::XChainCommit);
247        assert_eq!(TransactionType::from(43i16), TransactionType::XChainClaim);
248        assert_eq!(
249            TransactionType::from(44i16),
250            TransactionType::XChainAccountCreateCommit
251        );
252        assert_eq!(
253            TransactionType::from(45i16),
254            TransactionType::XChainAddClaimAttestation
255        );
256        assert_eq!(
257            TransactionType::from(46i16),
258            TransactionType::XChainAddAccountCreateAttestation
259        );
260        assert_eq!(
261            TransactionType::from(47i16),
262            TransactionType::XChainModifyBridge
263        );
264        assert_eq!(
265            TransactionType::from(48i16),
266            TransactionType::XChainCreateBridge
267        );
268        assert_eq!(TransactionType::from(49i16), TransactionType::DIDSet);
269        assert_eq!(TransactionType::from(50i16), TransactionType::DIDDelete);
270        assert_eq!(TransactionType::from(51i16), TransactionType::OracleSet);
271        assert_eq!(TransactionType::from(52i16), TransactionType::OracleDelete);
272        assert_eq!(
273            TransactionType::from(100i16),
274            TransactionType::EnableAmendment
275        );
276        assert_eq!(TransactionType::from(101i16), TransactionType::SetFee);
277        assert_eq!(TransactionType::from(102i16), TransactionType::UNLModify);
278    }
279
280    #[test]
281    fn test_transaction_type_from_i16_invalid() {
282        // Test that gaps in the enum and out-of-range values return Invalid
283        assert_eq!(TransactionType::from(999i16), TransactionType::Invalid);
284        assert_eq!(TransactionType::from(-100i16), TransactionType::Invalid);
285        assert_eq!(TransactionType::from(23i16), TransactionType::Invalid); // Gap in enum
286        assert_eq!(TransactionType::from(24i16), TransactionType::Invalid); // Gap in enum
287        assert_eq!(TransactionType::from(31i16), TransactionType::Invalid); // Gap in enum
288    }
289
290    #[test]
291    fn test_transaction_type_to_bytes() {
292        // Test serialization to little-endian bytes
293        let bytes: [u8; 2] = TransactionType::Payment.into();
294        assert_eq!(bytes, [0, 0]); // 0 in little-endian
295
296        let bytes: [u8; 2] = TransactionType::EscrowFinish.into();
297        assert_eq!(bytes, [2, 0]); // 2 in little-endian
298
299        let bytes: [u8; 2] = TransactionType::Invalid.into();
300        assert_eq!(bytes, [0xff, 0xff]); // -1 in little-endian (two's complement)
301
302        let bytes: [u8; 2] = TransactionType::EnableAmendment.into();
303        assert_eq!(bytes, [100, 0]); // 100 in little-endian
304    }
305
306    #[test]
307    fn test_transaction_type_from_bytes() {
308        // Test deserialization from little-endian bytes
309        let tx_type = TransactionType::from([0, 0]);
310        assert_eq!(tx_type, TransactionType::Payment);
311
312        let tx_type = TransactionType::from([2, 0]);
313        assert_eq!(tx_type, TransactionType::EscrowFinish);
314
315        let tx_type = TransactionType::from([0xff, 0xff]);
316        assert_eq!(tx_type, TransactionType::Invalid);
317
318        let tx_type = TransactionType::from([100, 0]);
319        assert_eq!(tx_type, TransactionType::EnableAmendment);
320    }
321
322    #[test]
323    fn test_transaction_type_bytes_roundtrip() {
324        // Test that converting to bytes and back gives the same result
325        let types = [
326            TransactionType::Payment,
327            TransactionType::EscrowCreate,
328            TransactionType::EscrowFinish,
329            TransactionType::AccountSet,
330            TransactionType::TrustSet,
331            TransactionType::AMMCreate,
332            TransactionType::NFTokenMint,
333            TransactionType::Clawback,
334            TransactionType::EnableAmendment,
335        ];
336
337        for tx_type in types {
338            let bytes: [u8; 2] = tx_type.into();
339            let recovered = TransactionType::from(bytes);
340            assert_eq!(tx_type, recovered);
341        }
342    }
343}