neo_solidity/ir/expressions/calls/builtins/
member_nativecalls.rs

1// Native Contract Call Handling
2//
3// This module handles lowering of Solidity native contract calls to NeoVM bytecode.
4// See the source code for full implementation of NeoToken, GasToken, ContractManagement,
5// PolicyContract, OracleContract, RoleManagement, Notary, Treasury, LedgerContract,
6// CryptoLib, and StdLib native contract operations.
7//
8// Note: This file is large (~1300 lines) due to the complexity of native contract handling.
9// Consider splitting by contract type in future refactoring.
10
11struct NativeContractDescriptor {
12    hash: [u8; 20],
13    name: &'static str,
14}
15
16const NATIVE_CONTRACTS: [NativeContractDescriptor; 11] = [
17    NativeContractDescriptor {
18        hash: [
19            0xf5, 0x63, 0xea, 0x40, 0xbc, 0x28, 0x3d, 0x4d, 0x0e, 0x05, 0xc4, 0x8e, 0xa3, 0x05,
20            0xb3, 0xf2, 0xa0, 0x73, 0x40, 0xef,
21        ],
22        name: "NeoToken",
23    },
24    NativeContractDescriptor {
25        hash: [
26            0xcf, 0x76, 0xe2, 0x8b, 0xd0, 0x06, 0x2c, 0x4a, 0x47, 0x8e, 0xe3, 0x55, 0x61, 0x01,
27            0x13, 0x19, 0xf3, 0xcf, 0xa4, 0xd2,
28        ],
29        name: "GasToken",
30    },
31    NativeContractDescriptor {
32        hash: [
33            0xfd, 0xa3, 0xfa, 0x43, 0x46, 0xea, 0x53, 0x2a, 0x25, 0x8f, 0xc4, 0x97, 0xdd, 0xad,
34            0xdb, 0x64, 0x37, 0xc9, 0xfd, 0xff,
35        ],
36        name: "ContractManagement",
37    },
38    NativeContractDescriptor {
39        hash: [
40            0x7b, 0xc6, 0x81, 0xc0, 0xa1, 0xf7, 0x1d, 0x54, 0x34, 0x57, 0xb6, 0x8b, 0xba, 0x8d,
41            0x5f, 0x9f, 0xdd, 0x4e, 0x5e, 0xcc,
42        ],
43        name: "PolicyContract",
44    },
45    NativeContractDescriptor {
46        hash: [
47            0x58, 0x87, 0x17, 0x11, 0x7e, 0x0a, 0xa8, 0x10, 0x72, 0xaf, 0xab, 0x71, 0xd2, 0xdd,
48            0x89, 0xfe, 0x7c, 0x4b, 0x92, 0xfe,
49        ],
50        name: "OracleContract",
51    },
52    NativeContractDescriptor {
53        hash: [
54            0xe2, 0x95, 0xe3, 0x91, 0x54, 0x4c, 0x17, 0x8a, 0xd9, 0x4f, 0x03, 0xec, 0x4d, 0xcd,
55            0xff, 0x78, 0x53, 0x4e, 0xcf, 0x49,
56        ],
57        name: "RoleManagement",
58    },
59    NativeContractDescriptor {
60        hash: [
61            0x3b, 0xec, 0x35, 0x31, 0x11, 0x9b, 0xba, 0xd7, 0x6d, 0xd0, 0x44, 0x92, 0x0b, 0x0d,
62            0xe6, 0xc3, 0x19, 0x4f, 0xe1, 0xc1,
63        ],
64        name: "Notary",
65    },
66    NativeContractDescriptor {
67        hash: [
68            0xc1, 0x3a, 0x56, 0xc9, 0x83, 0x53, 0xa7, 0xea, 0x6a, 0x32, 0x4d, 0x9a, 0x83, 0x5d,
69            0x1b, 0x5b, 0xf2, 0x26, 0x63, 0x15,
70        ],
71        name: "Treasury",
72    },
73    NativeContractDescriptor {
74        hash: [
75            0xbe, 0xf2, 0x04, 0x31, 0x40, 0x36, 0x2a, 0x77, 0xc1, 0x50, 0x99, 0xc7, 0xe6, 0x4c,
76            0x12, 0xf7, 0x00, 0xb6, 0x65, 0xda,
77        ],
78        name: "LedgerContract",
79    },
80    NativeContractDescriptor {
81        hash: [
82            0x1b, 0xf5, 0x75, 0xab, 0x11, 0x89, 0x68, 0x84, 0x13, 0x61, 0x0a, 0x35, 0xa1, 0x28,
83            0x86, 0xcd, 0xe0, 0xb6, 0x6c, 0x72,
84        ],
85        name: "CryptoLib",
86    },
87    NativeContractDescriptor {
88        hash: [
89            0xc0, 0xef, 0x39, 0xce, 0xe0, 0xe4, 0xe9, 0x25, 0xc6, 0xc2, 0xa0, 0x6a, 0x79, 0xe1,
90            0x44, 0x0d, 0xd8, 0x6f, 0xce, 0xac,
91        ],
92        name: "StdLib",
93    },
94];
95
96fn emit_throw_with_message(instructions: &mut Vec<Instruction>, message: &str) {
97    instructions.push(Instruction::PushLiteral(LiteralValue::String(
98        message.as_bytes().to_vec(),
99    )));
100    instructions.push(Instruction::Throw);
101}
102
103fn emit_is_native_contract_check(
104    ctx: &mut LoweringContext,
105    instructions: &mut Vec<Instruction>,
106    contract_slot: usize,
107) {
108    let done_label = ctx.next_label();
109
110    for contract in NATIVE_CONTRACTS.iter() {
111        let next_label = ctx.next_label();
112        instructions.push(Instruction::LoadLocal(contract_slot));
113        instructions.push(Instruction::PushLiteral(LiteralValue::Address(
114            contract.hash.to_vec(),
115        )));
116        instructions.push(Instruction::BinaryOp(BinaryOperator::Eq));
117        instructions.push(Instruction::JumpIf { target: next_label });
118        instructions.push(Instruction::PushLiteral(LiteralValue::Boolean(true)));
119        instructions.push(Instruction::Jump { target: done_label });
120        instructions.push(Instruction::Label(next_label));
121    }
122
123    instructions.push(Instruction::PushLiteral(LiteralValue::Boolean(false)));
124    instructions.push(Instruction::Label(done_label));
125}
126
127fn emit_native_contract_name(
128    ctx: &mut LoweringContext,
129    instructions: &mut Vec<Instruction>,
130    contract_slot: usize,
131) {
132    let done_label = ctx.next_label();
133
134    for contract in NATIVE_CONTRACTS.iter() {
135        let next_label = ctx.next_label();
136        instructions.push(Instruction::LoadLocal(contract_slot));
137        instructions.push(Instruction::PushLiteral(LiteralValue::Address(
138            contract.hash.to_vec(),
139        )));
140        instructions.push(Instruction::BinaryOp(BinaryOperator::Eq));
141        instructions.push(Instruction::JumpIf { target: next_label });
142        instructions.push(Instruction::PushLiteral(LiteralValue::String(
143            contract.name.as_bytes().to_vec(),
144        )));
145        instructions.push(Instruction::Jump { target: done_label });
146        instructions.push(Instruction::Label(next_label));
147    }
148
149    instructions.push(Instruction::PushLiteral(LiteralValue::String(
150        b"Unknown".to_vec(),
151    )));
152    instructions.push(Instruction::Label(done_label));
153}
154
155fn try_lower_nativecalls_member_builtin(
156    base: &Identifier,
157    member: &Identifier,
158    args: &[Expression],
159    ctx: &mut LoweringContext,
160    instructions: &mut Vec<Instruction>,
161) -> Option<bool> {
162    if base.name != "NativeCalls" {
163        return None;
164    }
165
166    match member.name.as_str() {
167        "getCommittee" => {
168            if !args.is_empty() {
169                ctx.record_error(format!(
170                    "NativeCalls.getCommittee requires 0 argument(s), got {}",
171                    args.len()
172                ));
173                return Some(false);
174            }
175
176            // Neo native contract exposes committee members as ECPoint public keys.
177            // The NativeCalls devpack returns `address[]`, so convert to UInt160 standard
178            // accounts via System.Contract.CreateStandardAccount.
179            let tmp_id = ctx.next_label();
180            let committee_keys_slot = ctx.allocate_local(
181                format!("__native_calls_committee_keys_{tmp_id}"),
182                Some(ValueType::Any),
183            );
184            let committee_addrs_slot = ctx.allocate_local(
185                format!("__native_calls_committee_addrs_{tmp_id}"),
186                Some(ValueType::Array(Box::new(ValueType::Address))),
187            );
188            let index_slot = ctx.allocate_local(
189                format!("__native_calls_committee_index_{tmp_id}"),
190                Some(ValueType::Integer {
191                    signed: false,
192                    bits: 256,
193                }),
194            );
195
196            instructions.push(Instruction::CallBuiltin {
197                builtin: BuiltinCall::NativeCall {
198                    contract: NativeContract::Neo,
199                    method: "getCommittee".to_string(),
200                },
201                arg_count: 0,
202            });
203            instructions.push(Instruction::StoreLocal(committee_keys_slot));
204
205            instructions.push(Instruction::LoadLocal(committee_keys_slot));
206            instructions.push(Instruction::GetSize);
207            instructions.push(Instruction::NewArray {
208                element_type: ValueType::Address,
209            });
210            instructions.push(Instruction::StoreLocal(committee_addrs_slot));
211
212            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
213                BigInt::zero(),
214            )));
215            instructions.push(Instruction::StoreLocal(index_slot));
216
217            let loop_label = ctx.next_label();
218            let done_label = ctx.next_label();
219            instructions.push(Instruction::Label(loop_label));
220            instructions.push(Instruction::LoadLocal(index_slot));
221            instructions.push(Instruction::LoadLocal(committee_keys_slot));
222            instructions.push(Instruction::GetSize);
223            instructions.push(Instruction::BinaryOp(BinaryOperator::Lt));
224            instructions.push(Instruction::JumpIf { target: done_label });
225
226            // committee_addrs[index] = CreateStandardAccount(committee_keys[index])
227            instructions.push(Instruction::LoadLocal(committee_addrs_slot));
228            instructions.push(Instruction::LoadLocal(index_slot));
229            instructions.push(Instruction::LoadLocal(committee_keys_slot));
230            instructions.push(Instruction::LoadLocal(index_slot));
231            instructions.push(Instruction::ArrayGet);
232            instructions.push(Instruction::CallBuiltin {
233                builtin: BuiltinCall::Syscall("System.Contract.CreateStandardAccount".to_string()),
234                arg_count: 1,
235            });
236            instructions.push(Instruction::ArraySet);
237
238            instructions.push(Instruction::LoadLocal(index_slot));
239            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
240                BigInt::one(),
241            )));
242            instructions.push(Instruction::BinaryOp(BinaryOperator::Add));
243            instructions.push(Instruction::StoreLocal(index_slot));
244            instructions.push(Instruction::Jump { target: loop_label });
245
246            instructions.push(Instruction::Label(done_label));
247            instructions.push(Instruction::LoadLocal(committee_addrs_slot));
248            Some(true)
249        }
250        "isCommittee" => {
251            if args.len() != 1 {
252                ctx.record_error(format!(
253                    "NativeCalls.isCommittee requires 1 argument(s), got {}",
254                    args.len()
255                ));
256                return Some(false);
257            }
258
259            // Neo native contract exposes committee members as ECPoint public keys. Derive
260            // standard accounts via System.Contract.CreateStandardAccount and compare with
261            // the supplied address.
262            let tmp_id = ctx.next_label();
263            let account_slot = ctx.allocate_local(
264                format!("__native_calls_is_committee_account_{tmp_id}"),
265                Some(ValueType::Address),
266            );
267            let committee_slot = ctx.allocate_local(
268                format!("__native_calls_is_committee_committee_{tmp_id}"),
269                Some(ValueType::Any),
270            );
271            let index_slot = ctx.allocate_local(
272                format!("__native_calls_is_committee_index_{tmp_id}"),
273                Some(ValueType::Integer {
274                    signed: false,
275                    bits: 256,
276                }),
277            );
278
279            if !lower_expression(&args[0], ctx, instructions) {
280                return Some(false);
281            }
282            instructions.push(Instruction::StoreLocal(account_slot));
283
284            instructions.push(Instruction::CallBuiltin {
285                builtin: BuiltinCall::NativeCall {
286                    contract: NativeContract::Neo,
287                    method: "getCommittee".to_string(),
288                },
289                arg_count: 0,
290            });
291            instructions.push(Instruction::StoreLocal(committee_slot));
292
293            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
294                BigInt::zero(),
295            )));
296            instructions.push(Instruction::StoreLocal(index_slot));
297
298            let loop_label = ctx.next_label();
299            let advance_label = ctx.next_label();
300            let done_label = ctx.next_label();
301            let end_label = ctx.next_label();
302
303            instructions.push(Instruction::Label(loop_label));
304            instructions.push(Instruction::LoadLocal(index_slot));
305            instructions.push(Instruction::LoadLocal(committee_slot));
306            instructions.push(Instruction::GetSize);
307            instructions.push(Instruction::BinaryOp(BinaryOperator::Lt));
308            instructions.push(Instruction::JumpIf { target: done_label });
309
310            instructions.push(Instruction::LoadLocal(committee_slot));
311            instructions.push(Instruction::LoadLocal(index_slot));
312            instructions.push(Instruction::ArrayGet);
313            instructions.push(Instruction::CallBuiltin {
314                builtin: BuiltinCall::Syscall("System.Contract.CreateStandardAccount".to_string()),
315                arg_count: 1,
316            });
317            instructions.push(Instruction::LoadLocal(account_slot));
318            instructions.push(Instruction::BinaryOp(BinaryOperator::Eq));
319            instructions.push(Instruction::JumpIf {
320                target: advance_label,
321            });
322
323            instructions.push(Instruction::PushLiteral(LiteralValue::Boolean(true)));
324            instructions.push(Instruction::Jump { target: end_label });
325
326            instructions.push(Instruction::Label(advance_label));
327            instructions.push(Instruction::LoadLocal(index_slot));
328            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
329                BigInt::one(),
330            )));
331            instructions.push(Instruction::BinaryOp(BinaryOperator::Add));
332            instructions.push(Instruction::StoreLocal(index_slot));
333            instructions.push(Instruction::Jump { target: loop_label });
334
335            instructions.push(Instruction::Label(done_label));
336            instructions.push(Instruction::PushLiteral(LiteralValue::Boolean(false)));
337            instructions.push(Instruction::Label(end_label));
338            Some(true)
339        }
340        "getNextBlockValidators" => {
341            if !args.is_empty() {
342                ctx.record_error(format!(
343                    "NativeCalls.getNextBlockValidators requires 0 argument(s), got {}",
344                    args.len()
345                ));
346                return Some(false);
347            }
348
349            // Neo native contract exposes validators as ECPoint public keys.
350            // The NativeCalls devpack returns `address[]`, so convert to UInt160 standard
351            // accounts via System.Contract.CreateStandardAccount.
352            let tmp_id = ctx.next_label();
353            let validator_keys_slot = ctx.allocate_local(
354                format!("__native_calls_validator_keys_{tmp_id}"),
355                Some(ValueType::Any),
356            );
357            let validator_addrs_slot = ctx.allocate_local(
358                format!("__native_calls_validator_addrs_{tmp_id}"),
359                Some(ValueType::Array(Box::new(ValueType::Address))),
360            );
361            let index_slot = ctx.allocate_local(
362                format!("__native_calls_validator_index_{tmp_id}"),
363                Some(ValueType::Integer {
364                    signed: false,
365                    bits: 256,
366                }),
367            );
368
369            instructions.push(Instruction::CallBuiltin {
370                builtin: BuiltinCall::NativeCall {
371                    contract: NativeContract::Neo,
372                    method: "getNextBlockValidators".to_string(),
373                },
374                arg_count: 0,
375            });
376            instructions.push(Instruction::StoreLocal(validator_keys_slot));
377
378            instructions.push(Instruction::LoadLocal(validator_keys_slot));
379            instructions.push(Instruction::GetSize);
380            instructions.push(Instruction::NewArray {
381                element_type: ValueType::Address,
382            });
383            instructions.push(Instruction::StoreLocal(validator_addrs_slot));
384
385            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
386                BigInt::zero(),
387            )));
388            instructions.push(Instruction::StoreLocal(index_slot));
389
390            let loop_label = ctx.next_label();
391            let done_label = ctx.next_label();
392            instructions.push(Instruction::Label(loop_label));
393            instructions.push(Instruction::LoadLocal(index_slot));
394            instructions.push(Instruction::LoadLocal(validator_keys_slot));
395            instructions.push(Instruction::GetSize);
396            instructions.push(Instruction::BinaryOp(BinaryOperator::Lt));
397            instructions.push(Instruction::JumpIf { target: done_label });
398
399            // validator_addrs[index] = CreateStandardAccount(validator_keys[index])
400            instructions.push(Instruction::LoadLocal(validator_addrs_slot));
401            instructions.push(Instruction::LoadLocal(index_slot));
402            instructions.push(Instruction::LoadLocal(validator_keys_slot));
403            instructions.push(Instruction::LoadLocal(index_slot));
404            instructions.push(Instruction::ArrayGet);
405            instructions.push(Instruction::CallBuiltin {
406                builtin: BuiltinCall::Syscall("System.Contract.CreateStandardAccount".to_string()),
407                arg_count: 1,
408            });
409            instructions.push(Instruction::ArraySet);
410
411            instructions.push(Instruction::LoadLocal(index_slot));
412            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
413                BigInt::one(),
414            )));
415            instructions.push(Instruction::BinaryOp(BinaryOperator::Add));
416            instructions.push(Instruction::StoreLocal(index_slot));
417            instructions.push(Instruction::Jump { target: loop_label });
418
419            instructions.push(Instruction::Label(done_label));
420            instructions.push(Instruction::LoadLocal(validator_addrs_slot));
421            Some(true)
422        }
423        "isValidator" => {
424            if args.len() != 1 {
425                ctx.record_error(format!(
426                    "NativeCalls.isValidator requires 1 argument(s), got {}",
427                    args.len()
428                ));
429                return Some(false);
430            }
431
432            let tmp_id = ctx.next_label();
433            let account_slot = ctx.allocate_local(
434                format!("__native_calls_is_validator_account_{tmp_id}"),
435                Some(ValueType::Address),
436            );
437            let validators_slot = ctx.allocate_local(
438                format!("__native_calls_is_validator_keys_{tmp_id}"),
439                Some(ValueType::Any),
440            );
441            let index_slot = ctx.allocate_local(
442                format!("__native_calls_is_validator_index_{tmp_id}"),
443                Some(ValueType::Integer {
444                    signed: false,
445                    bits: 256,
446                }),
447            );
448
449            if !lower_expression(&args[0], ctx, instructions) {
450                return Some(false);
451            }
452            instructions.push(Instruction::StoreLocal(account_slot));
453
454            instructions.push(Instruction::CallBuiltin {
455                builtin: BuiltinCall::NativeCall {
456                    contract: NativeContract::Neo,
457                    method: "getNextBlockValidators".to_string(),
458                },
459                arg_count: 0,
460            });
461            instructions.push(Instruction::StoreLocal(validators_slot));
462
463            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
464                BigInt::zero(),
465            )));
466            instructions.push(Instruction::StoreLocal(index_slot));
467
468            let loop_label = ctx.next_label();
469            let advance_label = ctx.next_label();
470            let done_label = ctx.next_label();
471            let end_label = ctx.next_label();
472
473            instructions.push(Instruction::Label(loop_label));
474            instructions.push(Instruction::LoadLocal(index_slot));
475            instructions.push(Instruction::LoadLocal(validators_slot));
476            instructions.push(Instruction::GetSize);
477            instructions.push(Instruction::BinaryOp(BinaryOperator::Lt));
478            instructions.push(Instruction::JumpIf { target: done_label });
479
480            instructions.push(Instruction::LoadLocal(validators_slot));
481            instructions.push(Instruction::LoadLocal(index_slot));
482            instructions.push(Instruction::ArrayGet);
483            instructions.push(Instruction::CallBuiltin {
484                builtin: BuiltinCall::Syscall("System.Contract.CreateStandardAccount".to_string()),
485                arg_count: 1,
486            });
487            instructions.push(Instruction::LoadLocal(account_slot));
488            instructions.push(Instruction::BinaryOp(BinaryOperator::Eq));
489            instructions.push(Instruction::JumpIf {
490                target: advance_label,
491            });
492
493            instructions.push(Instruction::PushLiteral(LiteralValue::Boolean(true)));
494            instructions.push(Instruction::Jump { target: end_label });
495
496            instructions.push(Instruction::Label(advance_label));
497            instructions.push(Instruction::LoadLocal(index_slot));
498            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
499                BigInt::one(),
500            )));
501            instructions.push(Instruction::BinaryOp(BinaryOperator::Add));
502            instructions.push(Instruction::StoreLocal(index_slot));
503            instructions.push(Instruction::Jump { target: loop_label });
504
505            instructions.push(Instruction::Label(done_label));
506            instructions.push(Instruction::PushLiteral(LiteralValue::Boolean(false)));
507            instructions.push(Instruction::Label(end_label));
508            Some(true)
509        }
510        "isNativeContract" => {
511            if args.len() != 1 {
512                ctx.record_error(format!(
513                    "NativeCalls.isNativeContract requires 1 argument(s), got {}",
514                    args.len()
515                ));
516                return Some(false);
517            }
518
519            let tmp_id = ctx.next_label();
520            let contract_slot = ctx.allocate_local(
521                format!("__native_calls_is_native_contract_{tmp_id}"),
522                Some(ValueType::Address),
523            );
524
525            if !lower_expression(&args[0], ctx, instructions) {
526                return Some(false);
527            }
528            instructions.push(Instruction::StoreLocal(contract_slot));
529
530            emit_is_native_contract_check(ctx, instructions, contract_slot);
531            Some(true)
532        }
533        "getNativeContractName" => {
534            if args.len() != 1 {
535                ctx.record_error(format!(
536                    "NativeCalls.getNativeContractName requires 1 argument(s), got {}",
537                    args.len()
538                ));
539                return Some(false);
540            }
541
542            let tmp_id = ctx.next_label();
543            let contract_slot = ctx.allocate_local(
544                format!("__native_calls_contract_name_{tmp_id}"),
545                Some(ValueType::Address),
546            );
547
548            if !lower_expression(&args[0], ctx, instructions) {
549                return Some(false);
550            }
551            instructions.push(Instruction::StoreLocal(contract_slot));
552
553            emit_native_contract_name(ctx, instructions, contract_slot);
554            Some(true)
555        }
556        "getAllNativeContracts" => {
557            if !args.is_empty() {
558                ctx.record_error(format!(
559                    "NativeCalls.getAllNativeContracts requires 0 argument(s), got {}",
560                    args.len()
561                ));
562                return Some(false);
563            }
564
565            let tmp_id = ctx.next_label();
566            let contracts_slot = ctx.allocate_local(
567                format!("__native_calls_all_contracts_{tmp_id}"),
568                Some(ValueType::Array(Box::new(ValueType::Address))),
569            );
570
571            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
572                BigInt::from(NATIVE_CONTRACTS.len() as u64),
573            )));
574            instructions.push(Instruction::NewArray {
575                element_type: ValueType::Address,
576            });
577            instructions.push(Instruction::StoreLocal(contracts_slot));
578
579            for (index, contract) in NATIVE_CONTRACTS.iter().enumerate() {
580                instructions.push(Instruction::LoadLocal(contracts_slot));
581                instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
582                    BigInt::from(index as u64),
583                )));
584                instructions.push(Instruction::PushLiteral(LiteralValue::Address(
585                    contract.hash.to_vec(),
586                )));
587                instructions.push(Instruction::ArraySet);
588            }
589
590            instructions.push(Instruction::LoadLocal(contracts_slot));
591            Some(true)
592        }
593        "estimateNativeCallGas" => {
594            if args.len() != 3 {
595                ctx.record_error(format!(
596                    "NativeCalls.estimateNativeCallGas requires 3 argument(s), got {}",
597                    args.len()
598                ));
599                return Some(false);
600            }
601
602            let tmp_id = ctx.next_label();
603            let contract_slot = ctx.allocate_local(
604                format!("__native_calls_estimate_contract_{tmp_id}"),
605                Some(ValueType::Address),
606            );
607            let method_slot = ctx.allocate_local(
608                format!("__native_calls_estimate_method_{tmp_id}"),
609                Some(ValueType::String),
610            );
611            let params_slot = ctx.allocate_local(
612                format!("__native_calls_estimate_params_{tmp_id}"),
613                Some(ValueType::ByteArray { fixed_len: None }),
614            );
615            let result_slot = ctx.allocate_local(
616                format!("__native_calls_estimate_result_{tmp_id}"),
617                Some(ValueType::Integer {
618                    signed: false,
619                    bits: 256,
620                }),
621            );
622
623            if !lower_expression(&args[0], ctx, instructions) {
624                return Some(false);
625            }
626            instructions.push(Instruction::StoreLocal(contract_slot));
627
628            if !lower_expression(&args[1], ctx, instructions) {
629                return Some(false);
630            }
631            instructions.push(Instruction::StoreLocal(method_slot));
632
633            if !lower_expression(&args[2], ctx, instructions) {
634                return Some(false);
635            }
636            instructions.push(Instruction::StoreLocal(params_slot));
637
638            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
639                BigInt::from(1_000_000u64),
640            )));
641            instructions.push(Instruction::StoreLocal(result_slot));
642
643            let end_label = ctx.next_label();
644
645            // NEO contract heuristics
646            let neo_skip_label = ctx.next_label();
647            let neo_register_label = ctx.next_label();
648            instructions.push(Instruction::LoadLocal(contract_slot));
649            instructions.push(Instruction::PushLiteral(LiteralValue::Address(
650                NATIVE_CONTRACTS[0].hash.to_vec(),
651            )));
652            instructions.push(Instruction::BinaryOp(BinaryOperator::Eq));
653            instructions.push(Instruction::JumpIf {
654                target: neo_skip_label,
655            });
656
657            instructions.push(Instruction::LoadLocal(method_slot));
658            instructions.push(Instruction::PushLiteral(LiteralValue::String(
659                b"vote".to_vec(),
660            )));
661            instructions.push(Instruction::BinaryOp(BinaryOperator::Eq));
662            instructions.push(Instruction::JumpIf {
663                target: neo_register_label,
664            });
665            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
666                BigInt::from(100_000_000u64),
667            )));
668            instructions.push(Instruction::StoreLocal(result_slot));
669            instructions.push(Instruction::Jump { target: end_label });
670
671            instructions.push(Instruction::Label(neo_register_label));
672            instructions.push(Instruction::LoadLocal(method_slot));
673            instructions.push(Instruction::PushLiteral(LiteralValue::String(
674                b"registerCandidate".to_vec(),
675            )));
676            instructions.push(Instruction::BinaryOp(BinaryOperator::Eq));
677            instructions.push(Instruction::JumpIf {
678                target: neo_skip_label,
679            });
680            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
681                BigInt::from(1_000_000_000u64),
682            )));
683            instructions.push(Instruction::StoreLocal(result_slot));
684            instructions.push(Instruction::Jump { target: end_label });
685
686            instructions.push(Instruction::Label(neo_skip_label));
687
688            // ContractManagement heuristics
689            let cm_skip_label = ctx.next_label();
690            let cm_update_label = ctx.next_label();
691            instructions.push(Instruction::LoadLocal(contract_slot));
692            instructions.push(Instruction::PushLiteral(LiteralValue::Address(
693                NATIVE_CONTRACTS[2].hash.to_vec(),
694            )));
695            instructions.push(Instruction::BinaryOp(BinaryOperator::Eq));
696            instructions.push(Instruction::JumpIf {
697                target: cm_skip_label,
698            });
699
700            instructions.push(Instruction::LoadLocal(method_slot));
701            instructions.push(Instruction::PushLiteral(LiteralValue::String(
702                b"deploy".to_vec(),
703            )));
704            instructions.push(Instruction::BinaryOp(BinaryOperator::Eq));
705            instructions.push(Instruction::JumpIf {
706                target: cm_update_label,
707            });
708            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
709                BigInt::from(500_000_000u64),
710            )));
711            instructions.push(Instruction::StoreLocal(result_slot));
712            instructions.push(Instruction::Jump { target: end_label });
713
714            instructions.push(Instruction::Label(cm_update_label));
715            instructions.push(Instruction::LoadLocal(method_slot));
716            instructions.push(Instruction::PushLiteral(LiteralValue::String(
717                b"update".to_vec(),
718            )));
719            instructions.push(Instruction::BinaryOp(BinaryOperator::Eq));
720            instructions.push(Instruction::JumpIf {
721                target: cm_skip_label,
722            });
723            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
724                BigInt::from(300_000_000u64),
725            )));
726            instructions.push(Instruction::StoreLocal(result_slot));
727            instructions.push(Instruction::Jump { target: end_label });
728
729            instructions.push(Instruction::Label(cm_skip_label));
730
731            // Oracle heuristics
732            let oracle_skip_label = ctx.next_label();
733            instructions.push(Instruction::LoadLocal(contract_slot));
734            instructions.push(Instruction::PushLiteral(LiteralValue::Address(
735                NATIVE_CONTRACTS[4].hash.to_vec(),
736            )));
737            instructions.push(Instruction::BinaryOp(BinaryOperator::Eq));
738            instructions.push(Instruction::JumpIf {
739                target: oracle_skip_label,
740            });
741
742            instructions.push(Instruction::LoadLocal(method_slot));
743            instructions.push(Instruction::PushLiteral(LiteralValue::String(
744                b"request".to_vec(),
745            )));
746            instructions.push(Instruction::BinaryOp(BinaryOperator::Eq));
747            instructions.push(Instruction::JumpIf {
748                target: oracle_skip_label,
749            });
750            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
751                BigInt::from(50_000_000u64),
752            )));
753            instructions.push(Instruction::StoreLocal(result_slot));
754            instructions.push(Instruction::Jump { target: end_label });
755
756            instructions.push(Instruction::Label(oracle_skip_label));
757            instructions.push(Instruction::Label(end_label));
758            instructions.push(Instruction::LoadLocal(result_slot));
759            Some(true)
760        }
761        "batchNativeCalls" => {
762            if args.len() != 3 {
763                ctx.record_error(format!(
764                    "NativeCalls.batchNativeCalls requires 3 argument(s), got {}",
765                    args.len()
766                ));
767                return Some(false);
768            }
769
770            let tmp_id = ctx.next_label();
771            let contracts_slot = ctx.allocate_local(
772                format!("__native_calls_batch_contracts_{tmp_id}"),
773                Some(ValueType::Any),
774            );
775            let methods_slot = ctx.allocate_local(
776                format!("__native_calls_batch_methods_{tmp_id}"),
777                Some(ValueType::Any),
778            );
779            let params_slot = ctx.allocate_local(
780                format!("__native_calls_batch_params_{tmp_id}"),
781                Some(ValueType::Any),
782            );
783            let length_slot = ctx.allocate_local(
784                format!("__native_calls_batch_length_{tmp_id}"),
785                Some(ValueType::Integer {
786                    signed: false,
787                    bits: 256,
788                }),
789            );
790
791            if !lower_expression(&args[0], ctx, instructions) {
792                return Some(false);
793            }
794            instructions.push(Instruction::StoreLocal(contracts_slot));
795
796            if !lower_expression(&args[1], ctx, instructions) {
797                return Some(false);
798            }
799            instructions.push(Instruction::StoreLocal(methods_slot));
800
801            if !lower_expression(&args[2], ctx, instructions) {
802                return Some(false);
803            }
804            instructions.push(Instruction::StoreLocal(params_slot));
805
806            instructions.push(Instruction::LoadLocal(contracts_slot));
807            instructions.push(Instruction::GetSize);
808            instructions.push(Instruction::StoreLocal(length_slot));
809
810            // contracts.length == methods.length
811            let methods_fail = ctx.next_label();
812            let methods_ok = ctx.next_label();
813            instructions.push(Instruction::LoadLocal(methods_slot));
814            instructions.push(Instruction::GetSize);
815            instructions.push(Instruction::LoadLocal(length_slot));
816            instructions.push(Instruction::BinaryOp(BinaryOperator::Eq));
817            instructions.push(Instruction::JumpIf {
818                target: methods_fail,
819            });
820            instructions.push(Instruction::Jump { target: methods_ok });
821            instructions.push(Instruction::Label(methods_fail));
822            emit_throw_with_message(instructions, "NativeCalls: array length mismatch");
823            instructions.push(Instruction::Label(methods_ok));
824
825            // contracts.length == params.length
826            let params_fail = ctx.next_label();
827            let params_ok = ctx.next_label();
828            instructions.push(Instruction::LoadLocal(params_slot));
829            instructions.push(Instruction::GetSize);
830            instructions.push(Instruction::LoadLocal(length_slot));
831            instructions.push(Instruction::BinaryOp(BinaryOperator::Eq));
832            instructions.push(Instruction::JumpIf {
833                target: params_fail,
834            });
835            instructions.push(Instruction::Jump { target: params_ok });
836            instructions.push(Instruction::Label(params_fail));
837            emit_throw_with_message(instructions, "NativeCalls: array length mismatch");
838            instructions.push(Instruction::Label(params_ok));
839
840            // contracts.length > 0
841            let non_empty_label = ctx.next_label();
842            instructions.push(Instruction::LoadLocal(length_slot));
843            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
844                BigInt::zero(),
845            )));
846            instructions.push(Instruction::BinaryOp(BinaryOperator::Eq));
847            instructions.push(Instruction::JumpIf {
848                target: non_empty_label,
849            });
850            emit_throw_with_message(instructions, "NativeCalls: empty arrays");
851            instructions.push(Instruction::Label(non_empty_label));
852
853            // contracts.length <= 10
854            let length_fail = ctx.next_label();
855            let length_ok_label = ctx.next_label();
856            instructions.push(Instruction::LoadLocal(length_slot));
857            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
858                BigInt::from(10u8),
859            )));
860            instructions.push(Instruction::BinaryOp(BinaryOperator::Le));
861            instructions.push(Instruction::JumpIf {
862                target: length_fail,
863            });
864            instructions.push(Instruction::Jump {
865                target: length_ok_label,
866            });
867            instructions.push(Instruction::Label(length_fail));
868            emit_throw_with_message(instructions, "NativeCalls: too many calls");
869            instructions.push(Instruction::Label(length_ok_label));
870
871            let results_slot = ctx.allocate_local(
872                format!("__native_calls_batch_results_{tmp_id}"),
873                Some(ValueType::Array(Box::new(ValueType::ByteArray {
874                    fixed_len: None,
875                }))),
876            );
877            let index_slot = ctx.allocate_local(
878                format!("__native_calls_batch_index_{tmp_id}"),
879                Some(ValueType::Integer {
880                    signed: false,
881                    bits: 256,
882                }),
883            );
884            let contract_slot = ctx.allocate_local(
885                format!("__native_calls_batch_contract_{tmp_id}"),
886                Some(ValueType::Address),
887            );
888
889            instructions.push(Instruction::LoadLocal(length_slot));
890            instructions.push(Instruction::NewArray {
891                element_type: ValueType::ByteArray { fixed_len: None },
892            });
893            instructions.push(Instruction::StoreLocal(results_slot));
894
895            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
896                BigInt::zero(),
897            )));
898            instructions.push(Instruction::StoreLocal(index_slot));
899
900            let loop_label = ctx.next_label();
901            let done_label = ctx.next_label();
902            instructions.push(Instruction::Label(loop_label));
903            instructions.push(Instruction::LoadLocal(index_slot));
904            instructions.push(Instruction::LoadLocal(length_slot));
905            instructions.push(Instruction::BinaryOp(BinaryOperator::Lt));
906            instructions.push(Instruction::JumpIf { target: done_label });
907
908            // contract = contracts[index]
909            instructions.push(Instruction::LoadLocal(contracts_slot));
910            instructions.push(Instruction::LoadLocal(index_slot));
911            instructions.push(Instruction::ArrayGet);
912            instructions.push(Instruction::StoreLocal(contract_slot));
913
914            // require isNativeContract(contract)
915            let native_ok = ctx.next_label();
916            emit_is_native_contract_check(ctx, instructions, contract_slot);
917            instructions.push(Instruction::JumpIf { target: native_ok });
918            emit_throw_with_message(instructions, "NativeCalls: not a native contract");
919            instructions.push(Instruction::Label(native_ok));
920
921            // results[index] = contractCall(contract, method, params)
922            instructions.push(Instruction::LoadLocal(results_slot));
923            instructions.push(Instruction::LoadLocal(index_slot));
924            instructions.push(Instruction::LoadLocal(contract_slot));
925            instructions.push(Instruction::LoadLocal(methods_slot));
926            instructions.push(Instruction::LoadLocal(index_slot));
927            instructions.push(Instruction::ArrayGet);
928            instructions.push(Instruction::LoadLocal(params_slot));
929            instructions.push(Instruction::LoadLocal(index_slot));
930            instructions.push(Instruction::ArrayGet);
931
932            if ctx.is_safe {
933                instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
934                    BigInt::from(0x05u8),
935                )));
936                instructions.push(Instruction::CallBuiltin {
937                    builtin: BuiltinCall::ContractCallWithFlags,
938                    arg_count: 4,
939                });
940            } else {
941                instructions.push(Instruction::CallBuiltin {
942                    builtin: BuiltinCall::ContractCall,
943                    arg_count: 3,
944                });
945            }
946
947            instructions.push(Instruction::ArraySet);
948
949            instructions.push(Instruction::LoadLocal(index_slot));
950            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
951                BigInt::one(),
952            )));
953            instructions.push(Instruction::BinaryOp(BinaryOperator::Add));
954            instructions.push(Instruction::StoreLocal(index_slot));
955            instructions.push(Instruction::Jump { target: loop_label });
956
957            instructions.push(Instruction::Label(done_label));
958            instructions.push(Instruction::LoadLocal(results_slot));
959            Some(true)
960        }
961        "getContractById" => {
962            if args.len() != 1 {
963                ctx.record_error(format!(
964                    "NativeCalls.getContractById requires 1 argument(s), got {}",
965                    args.len()
966                ));
967                return Some(false);
968            }
969
970            let tmp_id = ctx.next_label();
971            let state_slot = ctx.allocate_local(
972                format!("__native_calls_contract_state_{tmp_id}"),
973                Some(ValueType::Any),
974            );
975            let result_slot = ctx.allocate_local(
976                format!("__native_calls_contract_state_result_{tmp_id}"),
977                Some(ValueType::Array(Box::new(ValueType::Any))),
978            );
979
980            if !lower_expression(&args[0], ctx, instructions) {
981                return Some(false);
982            }
983
984            instructions.push(Instruction::CallBuiltin {
985                builtin: BuiltinCall::NativeCall {
986                    contract: NativeContract::ContractManagement,
987                    method: "getContractById".to_string(),
988                },
989                arg_count: 1,
990            });
991            instructions.push(Instruction::StoreLocal(state_slot));
992
993            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
994                BigInt::from(4u8),
995            )));
996            instructions.push(Instruction::NewArray {
997                element_type: ValueType::Any,
998            });
999            instructions.push(Instruction::StoreLocal(result_slot));
1000
1001            // hash (index 2)
1002            instructions.push(Instruction::LoadLocal(result_slot));
1003            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1004                BigInt::zero(),
1005            )));
1006            instructions.push(Instruction::LoadLocal(state_slot));
1007            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1008                BigInt::from(2u8),
1009            )));
1010            instructions.push(Instruction::ArrayGet);
1011            instructions.push(Instruction::ArraySet);
1012
1013            // nef (index 3)
1014            instructions.push(Instruction::LoadLocal(result_slot));
1015            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1016                BigInt::one(),
1017            )));
1018            instructions.push(Instruction::LoadLocal(state_slot));
1019            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1020                BigInt::from(3u8),
1021            )));
1022            instructions.push(Instruction::ArrayGet);
1023            instructions.push(Instruction::ArraySet);
1024
1025            // manifest (index 4) serialized
1026            instructions.push(Instruction::LoadLocal(result_slot));
1027            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1028                BigInt::from(2u8),
1029            )));
1030            instructions.push(Instruction::LoadLocal(state_slot));
1031            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1032                BigInt::from(4u8),
1033            )));
1034            instructions.push(Instruction::ArrayGet);
1035            instructions.push(Instruction::CallBuiltin {
1036                builtin: BuiltinCall::NativeCall {
1037                    contract: NativeContract::StdLib,
1038                    method: "serialize".to_string(),
1039                },
1040                arg_count: 1,
1041            });
1042            instructions.push(Instruction::ArraySet);
1043
1044            // updateCounter (index 1)
1045            instructions.push(Instruction::LoadLocal(result_slot));
1046            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1047                BigInt::from(3u8),
1048            )));
1049            instructions.push(Instruction::LoadLocal(state_slot));
1050            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1051                BigInt::one(),
1052            )));
1053            instructions.push(Instruction::ArrayGet);
1054            instructions.push(Instruction::ArraySet);
1055
1056            instructions.push(Instruction::LoadLocal(result_slot));
1057            Some(true)
1058        }
1059        "getNetworkConfiguration" => {
1060            if !args.is_empty() {
1061                ctx.record_error(format!(
1062                    "NativeCalls.getNetworkConfiguration requires 0 argument(s), got {}",
1063                    args.len()
1064                ));
1065                return Some(false);
1066            }
1067
1068            let tmp_id = ctx.next_label();
1069            let config_slot = ctx.allocate_local(
1070                format!("__native_calls_network_config_{tmp_id}"),
1071                Some(ValueType::Array(Box::new(ValueType::Any))),
1072            );
1073
1074            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1075                BigInt::from(6u8),
1076            )));
1077            instructions.push(Instruction::NewArray {
1078                element_type: ValueType::Any,
1079            });
1080            instructions.push(Instruction::StoreLocal(config_slot));
1081
1082            // feePerByte
1083            instructions.push(Instruction::LoadLocal(config_slot));
1084            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1085                BigInt::zero(),
1086            )));
1087            instructions.push(Instruction::CallBuiltin {
1088                builtin: BuiltinCall::NativeCall {
1089                    contract: NativeContract::Policy,
1090                    method: "getFeePerByte".to_string(),
1091                },
1092                arg_count: 0,
1093            });
1094            instructions.push(Instruction::ArraySet);
1095
1096            // execFeeFactor
1097            instructions.push(Instruction::LoadLocal(config_slot));
1098            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1099                BigInt::one(),
1100            )));
1101            instructions.push(Instruction::CallBuiltin {
1102                builtin: BuiltinCall::NativeCall {
1103                    contract: NativeContract::Policy,
1104                    method: "getExecFeeFactor".to_string(),
1105                },
1106                arg_count: 0,
1107            });
1108            instructions.push(Instruction::ArraySet);
1109
1110            // storagePrice
1111            instructions.push(Instruction::LoadLocal(config_slot));
1112            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1113                BigInt::from(2u8),
1114            )));
1115            instructions.push(Instruction::CallBuiltin {
1116                builtin: BuiltinCall::NativeCall {
1117                    contract: NativeContract::Policy,
1118                    method: "getStoragePrice".to_string(),
1119                },
1120                arg_count: 0,
1121            });
1122            instructions.push(Instruction::ArraySet);
1123
1124            // gasPerBlock
1125            instructions.push(Instruction::LoadLocal(config_slot));
1126            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1127                BigInt::from(3u8),
1128            )));
1129            instructions.push(Instruction::CallBuiltin {
1130                builtin: BuiltinCall::NativeCall {
1131                    contract: NativeContract::Neo,
1132                    method: "getGasPerBlock".to_string(),
1133                },
1134                arg_count: 0,
1135            });
1136            instructions.push(Instruction::ArraySet);
1137
1138            // oraclePrice
1139            instructions.push(Instruction::LoadLocal(config_slot));
1140            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1141                BigInt::from(4u8),
1142            )));
1143            instructions.push(Instruction::CallBuiltin {
1144                builtin: BuiltinCall::NativeCall {
1145                    contract: NativeContract::Oracle,
1146                    method: "getPrice".to_string(),
1147                },
1148                arg_count: 0,
1149            });
1150            instructions.push(Instruction::ArraySet);
1151
1152            // minimumDeploymentFee
1153            instructions.push(Instruction::LoadLocal(config_slot));
1154            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1155                BigInt::from(5u8),
1156            )));
1157            instructions.push(Instruction::CallBuiltin {
1158                builtin: BuiltinCall::NativeCall {
1159                    contract: NativeContract::ContractManagement,
1160                    method: "getMinimumDeploymentFee".to_string(),
1161                },
1162                arg_count: 0,
1163            });
1164            instructions.push(Instruction::ArraySet);
1165
1166            instructions.push(Instruction::LoadLocal(config_slot));
1167            Some(true)
1168        }
1169        "getNativeContractManifest" => {
1170            if args.len() != 1 {
1171                ctx.record_error(format!(
1172                    "NativeCalls.getNativeContractManifest requires 1 argument(s), got {}",
1173                    args.len()
1174                ));
1175                return Some(false);
1176            }
1177
1178            if !lower_expression(&args[0], ctx, instructions) {
1179                return Some(false);
1180            }
1181            instructions.push(Instruction::CallBuiltin {
1182                builtin: BuiltinCall::GetContract,
1183                arg_count: 1,
1184            });
1185            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1186                BigInt::from(2u8),
1187            )));
1188            instructions.push(Instruction::ArrayGet);
1189            Some(true)
1190        }
1191        "safeNativeCall" => {
1192            if args.len() != 3 {
1193                ctx.record_error(format!(
1194                    "NativeCalls.safeNativeCall requires 3 argument(s), got {}",
1195                    args.len()
1196                ));
1197                return Some(false);
1198            }
1199
1200            let tmp_id = ctx.next_label();
1201            let contract_slot = ctx.allocate_local(
1202                format!("__native_calls_safe_contract_{tmp_id}"),
1203                Some(ValueType::Address),
1204            );
1205            let method_slot = ctx.allocate_local(
1206                format!("__native_calls_safe_method_{tmp_id}"),
1207                Some(ValueType::String),
1208            );
1209            let params_slot = ctx.allocate_local(
1210                format!("__native_calls_safe_params_{tmp_id}"),
1211                Some(ValueType::ByteArray { fixed_len: None }),
1212            );
1213            let data_slot = ctx.allocate_local(
1214                format!("__native_calls_safe_data_{tmp_id}"),
1215                Some(ValueType::ByteArray { fixed_len: None }),
1216            );
1217            let tuple_slot = ctx.allocate_local(
1218                format!("__native_calls_safe_tuple_{tmp_id}"),
1219                Some(ValueType::Any),
1220            );
1221
1222            if !lower_expression(&args[0], ctx, instructions) {
1223                return Some(false);
1224            }
1225            instructions.push(Instruction::StoreLocal(contract_slot));
1226
1227            if !lower_expression(&args[1], ctx, instructions) {
1228                return Some(false);
1229            }
1230            instructions.push(Instruction::StoreLocal(method_slot));
1231
1232            if !lower_expression(&args[2], ctx, instructions) {
1233                return Some(false);
1234            }
1235            instructions.push(Instruction::StoreLocal(params_slot));
1236
1237            // require(isNativeContract(contract))
1238            let native_ok = ctx.next_label();
1239            emit_is_native_contract_check(ctx, instructions, contract_slot);
1240            instructions.push(Instruction::JumpIf { target: native_ok });
1241            emit_throw_with_message(instructions, "NativeCalls: not a native contract");
1242            instructions.push(Instruction::Label(native_ok));
1243
1244            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1245                BigInt::from(2u8),
1246            )));
1247            instructions.push(Instruction::NewArray {
1248                element_type: ValueType::Any,
1249            });
1250            instructions.push(Instruction::StoreLocal(tuple_slot));
1251
1252            let catch_label = ctx.next_label();
1253            let end_label = ctx.next_label();
1254            instructions.push(Instruction::Try {
1255                catch_target: catch_label,
1256            });
1257
1258            instructions.push(Instruction::LoadLocal(contract_slot));
1259            instructions.push(Instruction::LoadLocal(method_slot));
1260            instructions.push(Instruction::LoadLocal(params_slot));
1261
1262            if ctx.is_safe {
1263                instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1264                    BigInt::from(0x05u8),
1265                )));
1266                instructions.push(Instruction::CallBuiltin {
1267                    builtin: BuiltinCall::ContractCallWithFlags,
1268                    arg_count: 4,
1269                });
1270            } else {
1271                instructions.push(Instruction::CallBuiltin {
1272                    builtin: BuiltinCall::ContractCall,
1273                    arg_count: 3,
1274                });
1275            }
1276
1277            instructions.push(Instruction::StoreLocal(data_slot));
1278
1279            instructions.push(Instruction::LoadLocal(tuple_slot));
1280            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1281                BigInt::zero(),
1282            )));
1283            instructions.push(Instruction::PushLiteral(LiteralValue::Boolean(true)));
1284            instructions.push(Instruction::ArraySet);
1285
1286            instructions.push(Instruction::LoadLocal(tuple_slot));
1287            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1288                BigInt::one(),
1289            )));
1290            instructions.push(Instruction::LoadLocal(data_slot));
1291            instructions.push(Instruction::ArraySet);
1292
1293            instructions.push(Instruction::EndTry { target: end_label });
1294
1295            instructions.push(Instruction::Label(catch_label));
1296            instructions.push(Instruction::Drop(ValueType::Any));
1297
1298            instructions.push(Instruction::LoadLocal(tuple_slot));
1299            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1300                BigInt::zero(),
1301            )));
1302            instructions.push(Instruction::PushLiteral(LiteralValue::Boolean(false)));
1303            instructions.push(Instruction::ArraySet);
1304
1305            instructions.push(Instruction::LoadLocal(tuple_slot));
1306            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
1307                BigInt::one(),
1308            )));
1309            instructions.push(Instruction::PushLiteral(
1310                LiteralValue::ByteArray(Vec::new()),
1311            ));
1312            instructions.push(Instruction::ArraySet);
1313
1314            instructions.push(Instruction::EndTry { target: end_label });
1315            instructions.push(Instruction::Label(end_label));
1316            instructions.push(Instruction::LoadLocal(tuple_slot));
1317            Some(true)
1318        }
1319        _ => None,
1320    }
1321}