neo_solidity/ir/expressions/calls/builtins/
resolved.rs1fn try_lower_resolved_builtin_call(
2 func: &Expression,
3 args: &[Expression],
4 ctx: &mut LoweringContext,
5 instructions: &mut Vec<Instruction>,
6) -> Option<bool> {
7 let builtin = resolve_builtin_call(func)?;
8
9 if matches!(&builtin, BuiltinCall::RuntimeNotify) {
10 validate_runtime_notify_call(args, ctx);
11 }
12
13 if matches!(&builtin, BuiltinCall::TypeOf) {
14 ctx.record_error(
15 "`type(...)` is only supported via members like `type(uint256).max`, `type(int256).min`, or `type(IInterface).interfaceId`",
16 );
17 return Some(false);
18 }
19
20 let (min_args, max_args) = match &builtin {
21 BuiltinCall::RuntimeNotify => (2, Some(2)),
22 BuiltinCall::RuntimeCheckWitness => (1, Some(1)),
23 BuiltinCall::AbiEncode | BuiltinCall::AbiEncodePacked | BuiltinCall::AbiEncodeCall => (0, None),
25 BuiltinCall::AbiEncodeWithSignature => (1, None),
26 BuiltinCall::AbiDecode => (2, None),
27 BuiltinCall::Keccak256 => (1, Some(1)),
28 BuiltinCall::Ecrecover => (4, Some(4)),
29 BuiltinCall::StorageFind => (1, None),
30 BuiltinCall::StoragePut => (2, Some(2)),
31 BuiltinCall::StorageGet | BuiltinCall::StorageDelete => (1, Some(1)),
32 BuiltinCall::ContractCall => (3, Some(3)),
33 BuiltinCall::ContractCallWithFlags => (4, Some(4)),
34 BuiltinCall::NotifySerialized => (1, Some(1)),
35 BuiltinCall::VerifySignature => (3, Some(3)),
36 BuiltinCall::DeployContract => (2, Some(3)),
37 BuiltinCall::GetContract | BuiltinCall::GetContractScript => (1, Some(1)),
38 BuiltinCall::GetNeoAccountState => (1, Some(1)),
39 BuiltinCall::NativeCall { .. } => (0, None),
40 BuiltinCall::Syscall(_) => (0, None),
41 BuiltinCall::TypeOf => (1, Some(1)),
42 BuiltinCall::BytesConcat => (0, None),
43 };
44
45 if args.len() < min_args || max_args.is_some_and(|max| args.len() > max) {
46 ctx.record_error(format!(
47 "builtin call requires between {} and {} argument(s), got {}",
48 min_args,
49 max_args
50 .map(|v| v.to_string())
51 .unwrap_or_else(|| "∞".to_string()),
52 args.len()
53 ));
54 return Some(false);
55 }
56
57 let mut success = true;
58 match &builtin {
59 BuiltinCall::AbiDecode => {
60 if let Some(first) = args.first() {
61 if !lower_expression(first, ctx, instructions) {
62 success = false;
63 }
64 }
65 }
66 _ => {
67 for arg in args {
68 if !lower_expression(arg, ctx, instructions) {
69 success = false;
70 }
71 }
72 }
73 }
74
75 if success {
76 match builtin {
77 BuiltinCall::RuntimeNotify
78 | BuiltinCall::RuntimeCheckWitness
79 | BuiltinCall::AbiEncode
80 | BuiltinCall::AbiEncodePacked
81 | BuiltinCall::AbiEncodeCall
82 | BuiltinCall::AbiDecode
83 | BuiltinCall::Keccak256
84 | BuiltinCall::Ecrecover
85 | BuiltinCall::StorageFind
86 | BuiltinCall::StoragePut
87 | BuiltinCall::StorageGet
88 | BuiltinCall::StorageDelete
89 | BuiltinCall::ContractCallWithFlags
90 | BuiltinCall::NotifySerialized
91 | BuiltinCall::VerifySignature
92 | BuiltinCall::DeployContract
93 | BuiltinCall::GetContract
94 | BuiltinCall::GetContractScript
95 | BuiltinCall::GetNeoAccountState
96 | BuiltinCall::NativeCall { .. }
97 | BuiltinCall::Syscall(_)
98 | BuiltinCall::BytesConcat => {
99 instructions.push(Instruction::CallBuiltin {
100 builtin,
101 arg_count: args.len(),
102 });
103 }
104 BuiltinCall::ContractCall => {
105 if ctx.is_safe {
106 instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
108 BigInt::from(0x05u8),
109 )));
110 instructions.push(Instruction::CallBuiltin {
111 builtin: BuiltinCall::ContractCallWithFlags,
112 arg_count: 4,
113 });
114 } else {
115 instructions.push(Instruction::CallBuiltin {
116 builtin: BuiltinCall::ContractCall,
117 arg_count: args.len(),
118 });
119 }
120 }
121 BuiltinCall::AbiEncodeWithSignature => {
122 let mut selector = Vec::new();
123 if let Some(Expression::StringLiteral(parts)) = args.first() {
124 let bytes = string_literal_bytes(parts);
125 let mut hasher = Keccak256::new();
126 hasher.update(&bytes);
127 let digest = hasher.finalize();
128 selector.extend_from_slice(&digest[..4]);
129 }
130 for _ in args {
131 instructions.push(Instruction::Drop(ValueType::Any));
132 }
133 instructions.push(Instruction::PushLiteral(LiteralValue::ByteArray(selector)));
134 }
135 BuiltinCall::TypeOf => {}
136 }
137 }
138
139 Some(success)
140}