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}