avm_rs/
types.rs

1//! Core data types for the Algorand Virtual Machine
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5
6/// Stack value type that can hold different types of data
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
8pub enum StackValue {
9    /// Unsigned 64-bit integer
10    Uint(u64),
11    /// Byte array (used for addresses, hashes, strings, etc.)
12    Bytes(Vec<u8>),
13}
14
15impl StackValue {
16    /// Create a new uint value
17    pub fn uint(value: u64) -> Self {
18        Self::Uint(value)
19    }
20
21    /// Create a new bytes value
22    pub fn bytes(value: Vec<u8>) -> Self {
23        Self::Bytes(value)
24    }
25
26    /// Create a new bytes value from a string
27    pub fn from_string(s: &str) -> Self {
28        Self::Bytes(s.as_bytes().to_vec())
29    }
30
31    /// Get the uint value, returning an error if not a uint
32    pub fn as_uint(&self) -> Result<u64, crate::error::AvmError> {
33        match self {
34            Self::Uint(val) => Ok(*val),
35            Self::Bytes(_) => Err(crate::error::AvmError::TypeError {
36                expected: "uint".to_string(),
37                actual: "bytes".to_string(),
38            }),
39        }
40    }
41
42    /// Get the bytes value, returning an error if not bytes
43    pub fn as_bytes(&self) -> Result<&[u8], crate::error::AvmError> {
44        match self {
45            Self::Bytes(bytes) => Ok(bytes),
46            Self::Uint(_) => Err(crate::error::AvmError::TypeError {
47                expected: "bytes".to_string(),
48                actual: "uint".to_string(),
49            }),
50        }
51    }
52
53    /// Get the bytes value as a mutable reference
54    pub fn as_bytes_mut(&mut self) -> Result<&mut Vec<u8>, crate::error::AvmError> {
55        match self {
56            Self::Bytes(bytes) => Ok(bytes),
57            Self::Uint(_) => Err(crate::error::AvmError::TypeError {
58                expected: "bytes".to_string(),
59                actual: "uint".to_string(),
60            }),
61        }
62    }
63
64    /// Convert to bool (0 is false, non-zero is true)
65    pub fn as_bool(&self) -> Result<bool, crate::error::AvmError> {
66        match self {
67            Self::Uint(val) => Ok(*val != 0),
68            Self::Bytes(bytes) => {
69                // Empty bytes or all zeros is false
70                Ok(!bytes.is_empty() && bytes.iter().any(|&b| b != 0))
71            }
72        }
73    }
74
75    /// Get the type name as a string
76    pub fn type_name(&self) -> &'static str {
77        match self {
78            Self::Uint(_) => "uint",
79            Self::Bytes(_) => "bytes",
80        }
81    }
82
83    /// Check if this is a uint value
84    pub fn is_uint(&self) -> bool {
85        matches!(self, Self::Uint(_))
86    }
87
88    /// Check if this is a bytes value
89    pub fn is_bytes(&self) -> bool {
90        matches!(self, Self::Bytes(_))
91    }
92
93    /// Convert to uint if possible, otherwise return 0
94    pub fn uint_or_zero(&self) -> u64 {
95        match self {
96            Self::Uint(val) => *val,
97            Self::Bytes(_) => 0,
98        }
99    }
100}
101
102impl fmt::Display for StackValue {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        match self {
105            Self::Uint(val) => write!(f, "{val}"),
106            Self::Bytes(bytes) => {
107                if bytes.iter().all(|&b| b.is_ascii() && !b.is_ascii_control()) {
108                    write!(f, "\"{}\"", String::from_utf8_lossy(bytes))
109                } else {
110                    write!(f, "0x{}", hex::encode(bytes))
111                }
112            }
113        }
114    }
115}
116
117impl From<u64> for StackValue {
118    fn from(value: u64) -> Self {
119        Self::Uint(value)
120    }
121}
122
123impl From<Vec<u8>> for StackValue {
124    fn from(value: Vec<u8>) -> Self {
125        Self::Bytes(value)
126    }
127}
128
129impl From<&[u8]> for StackValue {
130    fn from(value: &[u8]) -> Self {
131        Self::Bytes(value.to_vec())
132    }
133}
134
135impl From<String> for StackValue {
136    fn from(value: String) -> Self {
137        Self::Bytes(value.into_bytes())
138    }
139}
140
141impl From<&str> for StackValue {
142    fn from(value: &str) -> Self {
143        Self::Bytes(value.as_bytes().to_vec())
144    }
145}
146
147/// TEAL value type used in global and local state
148#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
149pub enum TealValue {
150    /// Unsigned 64-bit integer
151    Uint(u64),
152    /// Byte array
153    Bytes(Vec<u8>),
154}
155
156impl TealValue {
157    /// Create a new uint value
158    pub fn uint(value: u64) -> Self {
159        Self::Uint(value)
160    }
161
162    /// Create a new bytes value
163    pub fn bytes(value: Vec<u8>) -> Self {
164        Self::Bytes(value)
165    }
166
167    /// Convert to StackValue
168    pub fn to_stack_value(&self) -> StackValue {
169        match self {
170            Self::Uint(val) => StackValue::Uint(*val),
171            Self::Bytes(bytes) => StackValue::Bytes(bytes.clone()),
172        }
173    }
174
175    /// Create from StackValue
176    pub fn from_stack_value(value: &StackValue) -> Self {
177        match value {
178            StackValue::Uint(val) => Self::Uint(*val),
179            StackValue::Bytes(bytes) => Self::Bytes(bytes.clone()),
180        }
181    }
182}
183
184impl fmt::Display for TealValue {
185    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186        match self {
187            Self::Uint(val) => write!(f, "{val}"),
188            Self::Bytes(bytes) => {
189                if bytes.iter().all(|&b| b.is_ascii() && !b.is_ascii_control()) {
190                    write!(f, "\"{}\"", String::from_utf8_lossy(bytes))
191                } else {
192                    write!(f, "0x{}", hex::encode(bytes))
193                }
194            }
195        }
196    }
197}
198
199/// TEAL version enum for type-safe version handling
200#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
201pub enum TealVersion {
202    /// TEAL version 1 (initial version)
203    V1 = 1,
204    /// TEAL version 2 (added more opcodes)
205    V2 = 2,
206    /// TEAL version 3 (added asset opcodes)
207    V3 = 3,
208    /// TEAL version 4 (added more crypto opcodes)
209    V4 = 4,
210    /// TEAL version 5 (added application opcodes)
211    V5 = 5,
212    /// TEAL version 6 (added more opcodes)
213    V6 = 6,
214    /// TEAL version 7 (added inner transactions)
215    V7 = 7,
216    /// TEAL version 8 (added box storage)
217    V8 = 8,
218    /// TEAL version 9 (added more box operations)
219    V9 = 9,
220    /// TEAL version 10 (added elliptic curve operations)
221    V10 = 10,
222    /// TEAL version 11 (added MIMC hash and block opcode)
223    V11 = 11,
224}
225
226impl TealVersion {
227    /// Convert from u8 to TealVersion
228    pub fn from_u8(version: u8) -> Result<Self, crate::error::AvmError> {
229        match version {
230            1 => Ok(Self::V1),
231            2 => Ok(Self::V2),
232            3 => Ok(Self::V3),
233            4 => Ok(Self::V4),
234            5 => Ok(Self::V5),
235            6 => Ok(Self::V6),
236            7 => Ok(Self::V7),
237            8 => Ok(Self::V8),
238            9 => Ok(Self::V9),
239            10 => Ok(Self::V10),
240            11 => Ok(Self::V11),
241            _ => Err(crate::error::AvmError::UnsupportedVersion(version)),
242        }
243    }
244
245    /// Convert to u8
246    pub fn as_u8(self) -> u8 {
247        self as u8
248    }
249
250    /// Get the latest supported version
251    pub const fn latest() -> Self {
252        Self::V11
253    }
254
255    /// Check if this version supports a specific feature
256    pub fn supports_subroutines(self) -> bool {
257        self >= Self::V4
258    }
259
260    /// Check if this version supports inner transactions
261    pub fn supports_inner_transactions(self) -> bool {
262        self >= Self::V5
263    }
264
265    /// Check if this version supports box operations
266    pub fn supports_boxes(self) -> bool {
267        self >= Self::V8
268    }
269
270    /// Check if this version supports advanced crypto operations
271    pub fn supports_advanced_crypto(self) -> bool {
272        self >= Self::V5
273    }
274
275    /// Check if this version supports extended box operations (splice, resize)
276    pub fn supports_extended_box_ops(self) -> bool {
277        self >= Self::V9
278    }
279
280    /// Check if this version supports elliptic curve operations
281    pub fn supports_elliptic_curve_ops(self) -> bool {
282        self >= Self::V10
283    }
284
285    /// Check if this version supports MIMC hash and block randomness
286    pub fn supports_mimc_and_block(self) -> bool {
287        self >= Self::V11
288    }
289
290    /// Get all available versions
291    pub const fn all() -> &'static [Self] {
292        &[
293            Self::V1,
294            Self::V2,
295            Self::V3,
296            Self::V4,
297            Self::V5,
298            Self::V6,
299            Self::V7,
300            Self::V8,
301            Self::V9,
302            Self::V10,
303            Self::V11,
304        ]
305    }
306}
307
308impl fmt::Display for TealVersion {
309    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310        write!(f, "{}", self.as_u8())
311    }
312}
313
314impl Default for TealVersion {
315    fn default() -> Self {
316        Self::latest()
317    }
318}
319
320/// Run mode for the AVM
321#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
322pub enum RunMode {
323    /// Signature verification mode (stateless)
324    Signature,
325    /// Application mode (stateful)
326    Application,
327}
328
329/// Transaction field identifiers
330#[derive(Debug, Clone, Copy, PartialEq, Eq)]
331pub enum TxnField {
332    Sender,
333    Fee,
334    FirstValid,
335    FirstValidTime,
336    LastValid,
337    Note,
338    Lease,
339    Receiver,
340    Amount,
341    CloseRemainderTo,
342    VotePK,
343    SelectionPK,
344    VoteFirst,
345    VoteLast,
346    VoteKeyDilution,
347    Type,
348    TypeEnum,
349    XferAsset,
350    AssetAmount,
351    AssetSender,
352    AssetReceiver,
353    AssetCloseTo,
354    GroupIndex,
355    TxID,
356    ApplicationID,
357    OnCompletion,
358    ApplicationArgs,
359    NumAppArgs,
360    Accounts,
361    NumAccounts,
362    ApprovalProgram,
363    ClearStateProgram,
364    RekeyTo,
365    ConfigAsset,
366    ConfigAssetTotal,
367    ConfigAssetDecimals,
368    ConfigAssetDefaultFrozen,
369    ConfigAssetName,
370    ConfigAssetURL,
371    ConfigAssetMetadataHash,
372    ConfigAssetManager,
373    ConfigAssetReserve,
374    ConfigAssetFreeze,
375    ConfigAssetClawback,
376    FreezeAsset,
377    FreezeAssetAccount,
378    FreezeAssetFrozen,
379    Assets,
380    NumAssets,
381    Applications,
382    NumApplications,
383    GlobalNumUint,
384    GlobalNumByteSlice,
385    LocalNumUint,
386    LocalNumByteSlice,
387    ExtraProgramPages,
388    Nonparticipation,
389    Logs,
390    NumLogs,
391    CreatedAssetID,
392    CreatedApplicationID,
393    LastLog,
394    StateProofPK,
395    ApprovalProgramPages,
396    NumApprovalProgramPages,
397    ClearStateProgramPages,
398    NumClearStateProgramPages,
399}
400
401/// Global field identifiers
402#[derive(Debug, Clone, Copy, PartialEq, Eq)]
403pub enum GlobalField {
404    MinTxnFee,
405    MinBalance,
406    MaxTxnLife,
407    ZeroAddress,
408    GroupSize,
409    LogicSigVersion,
410    Round,
411    LatestTimestamp,
412    CurrentApplicationID,
413    CreatorAddress,
414    CurrentApplicationAddress,
415    GroupID,
416    OpcodeBudget,
417    CallerApplicationID,
418    CallerApplicationAddress,
419    AssetCreateMinBalance,
420    AssetOptInMinBalance,
421    GenesisHash,
422}