Skip to main content

neo_devpack_solidity/neo/
method_token.rs

1use super::constants::MAX_TOKEN_METHOD_LENGTH;
2use super::encoding::write_varbytes;
3
4/// Method token for cross-contract calls in NEF format.
5///
6/// Method tokens are used to optimize calls to other contracts by caching
7/// the target contract hash and method information in the NEF header.
8#[derive(Debug, Clone)]
9pub struct MethodToken {
10    /// Target contract hash (20 bytes, Script Hash)
11    pub hash: [u8; 20],
12    /// Method name to call
13    pub method: String,
14    /// Number of parameters the method accepts
15    pub parameters_count: u16,
16    /// Whether the method returns a value
17    pub has_return_value: bool,
18    /// Call flags (Neo N3 `CallFlags` bitmask; `All` = 0x0F)
19    pub call_flags: u8,
20}
21
22impl MethodToken {
23    /// Create a new method token
24    pub fn new(hash: [u8; 20], method: &str, params: u16, has_return: bool, flags: u8) -> Self {
25        Self {
26            hash,
27            method: method.to_string(),
28            parameters_count: params,
29            has_return_value: has_return,
30            call_flags: flags,
31        }
32    }
33
34    /// Get the contract hash as hex string
35    pub fn hash_hex(&self) -> String {
36        hex::encode(self.hash)
37    }
38
39    /// Check if call flags allow state changes
40    pub fn allows_state_changes(&self) -> bool {
41        self.call_flags & 0x01 != 0
42    }
43
44    /// Serialize the method token to bytes.
45    ///
46    /// M-BC3 fix — returns `Err` (rather than `assert!`-panicking) when the
47    /// method name exceeds the Neo N3 NEF3 spec cap (`MAX_TOKEN_METHOD_LENGTH`
48    /// = 32 bytes). `build_nef_with_tokens` validates up-front, but
49    /// `serialize` is `pub(super)` and any future direct caller would have
50    /// panicked instead of returning a clean error.
51    pub(super) fn serialize(&self, buffer: &mut Vec<u8>) -> Result<(), String> {
52        // Contract hash (20 bytes)
53        buffer.extend_from_slice(&self.hash);
54
55        // Method name (length-prefixed string, max 32 bytes per Neo spec)
56        let bytes = self.method.as_bytes();
57        if bytes.len() > MAX_TOKEN_METHOD_LENGTH {
58            return Err(format!(
59                "method token '{}' exceeds {MAX_TOKEN_METHOD_LENGTH} bytes (Neo N3 NEF3 cap)",
60                self.method
61            ));
62        }
63        write_varbytes(buffer, bytes);
64
65        // Parameters count (2 bytes, little-endian)
66        buffer.extend_from_slice(&self.parameters_count.to_le_bytes());
67
68        // Has return value (1 byte)
69        buffer.push(if self.has_return_value { 1 } else { 0 });
70
71        // Call flags (1 byte)
72        buffer.push(self.call_flags);
73
74        Ok(())
75    }
76}