neo_solidity/cli/bytecode/bytecode_builtins/builtin_call/
native_wrappers.rs

1fn emit_native_call(
2    bytecode: &mut Vec<u8>,
3    contract: ir::NativeContract,
4    method: &str,
5    arg_count: usize,
6    use_callt: bool,
7    token_patches: &mut Vec<MethodTokenPatch>,
8) {
9    emit_native_contract_call(
10        bytecode,
11        contract,
12        method,
13        arg_count,
14        use_callt,
15        token_patches,
16    );
17}
18
19fn emit_deploy_contract(
20    bytecode: &mut Vec<u8>,
21    arg_count: usize,
22    use_callt: bool,
23    token_patches: &mut Vec<MethodTokenPatch>,
24) {
25    // ContractManagement.deploy(nef, manifest[, data]) returns a ContractState.
26    // NativeCalls.deployContract is a devpack convenience wrapper that returns
27    // the deployed contract hash (UInt160) only.
28    emit_native_contract_call(
29        bytecode,
30        ir::NativeContract::ContractManagement,
31        "deploy",
32        arg_count,
33        use_callt,
34        token_patches,
35    );
36    bytecode.push(0x12); // PUSH2 (ContractState.Hash field index)
37    bytecode.push(0xCE); // PICKITEM
38}
39
40fn emit_get_contract(
41    bytecode: &mut Vec<u8>,
42    use_callt: bool,
43    token_patches: &mut Vec<MethodTokenPatch>,
44) {
45    // ContractManagement.getContract(UInt160) returns a ContractState struct:
46    // [id, updateCounter, hash, nef, manifestStruct]
47    //
48    // NativeCalls.getContract reshapes it into:
49    // [hash, nef, serialize(manifestStruct), updateCounter]
50    emit_native_contract_call(
51        bytecode,
52        ir::NativeContract::ContractManagement,
53        "getContract",
54        1,
55        use_callt,
56        token_patches,
57    );
58
59    // Extract hash (index 2)
60    bytecode.push(0x4A); // DUP
61    bytecode.push(0x12); // PUSH2
62    bytecode.push(0xCE); // PICKITEM -> [state, hash]
63
64    // Extract nef (index 3)
65    bytecode.push(0x4B); // OVER
66    bytecode.push(0x13); // PUSH3
67    bytecode.push(0xCE); // PICKITEM -> [state, hash, nef]
68
69    // Extract manifest (index 4) and serialize it
70    bytecode.push(0x4B); // OVER
71    bytecode.push(0x14); // PUSH4
72    bytecode.push(0xCE); // PICKITEM -> [state, hash, nef, manifestStruct]
73    emit_native_contract_call(
74        bytecode,
75        ir::NativeContract::StdLib,
76        "serialize",
77        1,
78        use_callt,
79        token_patches,
80    ); // -> [state, hash, nef, manifestBytes]
81
82    // Extract updateCounter (index 1)
83    bytecode.push(0x13); // PUSH3 (duplicate state from depth 3)
84    bytecode.push(0x4D); // PICK -> [state, hash, nef, manifestBytes, state]
85    bytecode.push(0x11); // PUSH1
86    bytecode.push(0xCE); // PICKITEM -> [state, hash, nef, manifestBytes, updateCounter]
87
88    // Drop original state and pack struct
89    bytecode.push(0x14); // PUSH4
90    bytecode.push(0x52); // ROLL -> [..., state]
91    bytecode.push(0x45); // DROP
92
93    bytecode.push(0x14); // PUSH4
94    bytecode.push(0xC0); // PACK
95    // PACK reverses stack order; restore the field order expected by the devpack helper:
96    // [hash, nef, manifestBytes, updateCounter]
97    bytecode.push(0x4A); // DUP
98    bytecode.push(0xD1); // REVERSEITEMS
99}
100
101fn emit_get_contract_script(
102    bytecode: &mut Vec<u8>,
103    use_callt: bool,
104    token_patches: &mut Vec<MethodTokenPatch>,
105) {
106    // Syscalls.getContractScript is implemented as a convenience wrapper
107    // that fetches the NEF file from a contract state.
108    emit_native_contract_call(
109        bytecode,
110        ir::NativeContract::ContractManagement,
111        "getContract",
112        1,
113        use_callt,
114        token_patches,
115    );
116    bytecode.push(0x13); // PUSH3 (ContractState.Nef field index)
117    bytecode.push(0xCE); // PICKITEM
118}
119
120fn emit_get_neo_account_state(
121    bytecode: &mut Vec<u8>,
122    use_callt: bool,
123    token_patches: &mut Vec<MethodTokenPatch>,
124) {
125    // NeoToken.getAccountState(UInt160) returns NeoAccountState? (nullable) with shape:
126    //   [balance, balanceHeight, voteTo (ECPoint bytes or null), lastGasPerVote]
127    //
128    // The devpack helper expects a non-null struct, so we replace `null` with a
129    // default struct and normalize `voteTo` to an empty byte string.
130    emit_native_contract_call(
131        bytecode,
132        ir::NativeContract::Neo,
133        "getAccountState",
134        1,
135        use_callt,
136        token_patches,
137    );
138
139    // If result is null, replace with [0, 0, "", 0].
140    bytecode.push(0x4A); // DUP
141    bytecode.push(0xD8); // ISNULL
142    let jmp_if_not_null_pos = bytecode.len();
143    bytecode.push(0x27); // JMPIFNOT_L
144    let jmp_if_not_null_operand = bytecode.len();
145    bytecode.extend_from_slice(&[0, 0, 0, 0]);
146
147    // null path
148    bytecode.push(0x45); // DROP
149    bytecode.push(0x10); // PUSH0 (balance)
150    bytecode.push(0x10); // PUSH0 (balanceHeight)
151    push_data(bytecode, &[]); // empty voteTo
152    bytecode.push(0x10); // PUSH0 (lastGasPerVote)
153    bytecode.push(0x14); // PUSH4
154    bytecode.push(0xC0); // PACK
155    // PACK reverses stack order; restore [balance, balanceHeight, voteTo, lastGasPerVote].
156    bytecode.push(0x4A); // DUP
157    bytecode.push(0xD1); // REVERSEITEMS
158
159    let jmp_end_pos = bytecode.len();
160    bytecode.push(0x23); // JMP_L
161    let jmp_end_operand = bytecode.len();
162    bytecode.extend_from_slice(&[0, 0, 0, 0]);
163
164    // not-null path
165    let not_null_pos = bytecode.len();
166
167    // If voteTo (index 2) is null, set it to "".
168    bytecode.push(0x4A); // DUP
169    bytecode.push(0x12); // PUSH2
170    bytecode.push(0xCE); // PICKITEM -> voteTo
171    bytecode.push(0xD8); // ISNULL
172    let jmp_vote_non_null_pos = bytecode.len();
173    bytecode.push(0x27); // JMPIFNOT_L
174    let jmp_vote_non_null_operand = bytecode.len();
175    bytecode.extend_from_slice(&[0, 0, 0, 0]);
176
177    // voteTo is null path: state[2] = ""
178    bytecode.push(0x4A); // DUP
179    bytecode.push(0x12); // PUSH2
180    push_data(bytecode, &[]);
181    bytecode.push(0xD0); // SETITEM
182
183    let vote_non_null_pos = bytecode.len();
184
185    let rel_vote_non_null = (vote_non_null_pos as i32)
186        .checked_sub(jmp_vote_non_null_pos as i32)
187        .unwrap_or(0);
188    bytecode[jmp_vote_non_null_operand..jmp_vote_non_null_operand + 4]
189        .copy_from_slice(&rel_vote_non_null.to_le_bytes());
190
191    let end_pos = bytecode.len();
192
193    let rel_not_null = (not_null_pos as i32)
194        .checked_sub(jmp_if_not_null_pos as i32)
195        .unwrap_or(0);
196    bytecode[jmp_if_not_null_operand..jmp_if_not_null_operand + 4]
197        .copy_from_slice(&rel_not_null.to_le_bytes());
198
199    let rel_end = (end_pos as i32)
200        .checked_sub(jmp_end_pos as i32)
201        .unwrap_or(0);
202    bytecode[jmp_end_operand..jmp_end_operand + 4].copy_from_slice(&rel_end.to_le_bytes());
203}