1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
fn emit_abi_encode(
bytecode: &mut Vec<u8>,
arg_count: usize,
use_callt: bool,
token_patches: &mut Vec<MethodTokenPatch>,
) {
if arg_count == 0 {
// `abi.encode()` is the empty byte string. Lower it directly rather
// than routing through the pseudo-native StdLib helper, which does
// not exist on real Neo N3 / Neo-Express.
push_data(bytecode, &[]);
return;
}
let total_bigint = BigInt::from(arg_count);
push_integer_bigint(bytecode, &total_bigint);
bytecode.push(0xC0); // PACK
// PACK pops values from the stack and therefore reverses their order.
// For `abi.encode(a, b, c)` we must preserve the original argument order.
if arg_count > 1 {
bytecode.push(0x4A); // DUP (keep a reference to the array on the stack)
bytecode.push(0xD1); // REVERSEITEMS (consumes one reference, reverses in-place)
}
// Production Neo N3 has no StdLib.abiEncode helper. The high-level IR
// lowers many static ABI shapes directly before bytecode emission; this
// fallback keeps remaining shapes executable on chain by using real Neo
// StdLib serialization instead of a pseudo-native test-runtime method.
emit_native_contract_call(
bytecode,
ir::NativeContract::StdLib,
"serialize",
1,
use_callt,
token_patches,
);
}
fn emit_abi_encode_packed(
bytecode: &mut Vec<u8>,
arg_count: usize,
use_callt: bool,
token_patches: &mut Vec<MethodTokenPatch>,
) {
if arg_count == 0 {
// `abi.encodePacked()` is also the empty byte string.
push_data(bytecode, &[]);
return;
}
let total_bigint = BigInt::from(arg_count);
push_integer_bigint(bytecode, &total_bigint);
bytecode.push(0xC0); // PACK
if arg_count > 1 {
bytecode.push(0x4A); // DUP
bytecode.push(0xD1); // REVERSEITEMS
}
// Production Neo N3 has no StdLib.abiEncodePacked helper. Static/narrow
// packed forms are lowered before this point; use real StdLib.serialize
// for the remaining fallback instead of emitting a nonexistent method.
emit_native_contract_call(
bytecode,
ir::NativeContract::StdLib,
"serialize",
1,
use_callt,
token_patches,
);
}
fn emit_abi_decode(
bytecode: &mut Vec<u8>,
use_callt: bool,
token_patches: &mut Vec<MethodTokenPatch>,
) {
// Production Neo N3 has no StdLib.abiDecode helper. Decode the fallback
// Neo-serialized ABI payloads with real StdLib.deserialize; static
// canonical ABI decode fast paths are handled in IR where target types are
// still available.
emit_native_contract_call(
bytecode,
ir::NativeContract::StdLib,
"deserialize",
1,
use_callt,
token_patches,
);
}
fn emit_notify_serialized(
bytecode: &mut Vec<u8>,
use_callt: bool,
token_patches: &mut Vec<MethodTokenPatch>,
) {
// Syscalls.notify expects a serialized payload: [eventName, stateArray]
emit_native_contract_call(
bytecode,
ir::NativeContract::StdLib,
"deserialize",
1,
use_callt,
token_patches,
);
// Extract eventName/state via PICKITEM and call Runtime.Notify.
bytecode.push(0x4A); // DUP (payload)
bytecode.push(0x10); // PUSH0
bytecode.push(0xCE); // PICKITEM -> eventName
bytecode.push(0x50); // SWAP -> [eventName, payload]
bytecode.push(0x4A); // DUP (payload)
bytecode.push(0x11); // PUSH1
bytecode.push(0xCE); // PICKITEM -> state
bytecode.push(0x46); // NIP (drop payload) -> [eventName, state]
bytecode.push(0x50); // SWAP -> [state, eventName]
emit_syscall(bytecode, "System.Runtime.Notify");
bytecode.push(0x11); // PUSH1
}