|
1 | 1 | use super::ExpCtxt; |
2 | 2 | use crate::verbatim::Verbatim; |
3 | 3 | use alloy_json_abi::{ |
4 | | - Constructor, Error, Event, EventParam, Fallback, Function, Param, Receive, StateMutability, |
| 4 | + Constructor, Error, Event, EventParam, Fallback, Function, InternalType, Param, Receive, |
| 5 | + StateMutability, |
5 | 6 | }; |
6 | 7 | use ast::{ItemError, ItemEvent, ItemFunction}; |
7 | 8 | use proc_macro2::TokenStream; |
@@ -105,38 +106,101 @@ fn ty_to_param(name: Option<String>, ty: &ast::Type, cx: &ExpCtxt<'_>) -> Param |
105 | 106 | ty_name = format!("tuple{suffix}"); |
106 | 107 | } |
107 | 108 |
|
108 | | - let mut component_names = vec![]; |
109 | | - let resolved = match ty.peel_arrays() { |
| 109 | + // For struct types, get the original fields to preserve type information (like UDVTs) |
| 110 | + let original_fields = match ty.peel_arrays() { |
110 | 111 | ast::Type::Custom(name) => { |
111 | 112 | if let ast::Item::Struct(s) = cx.item(name) { |
112 | | - component_names = s |
113 | | - .fields |
114 | | - .names() |
115 | | - .map(|n| n.map(|i| i.as_string()).unwrap_or_default()) |
116 | | - .collect(); |
| 113 | + Some(s.fields.clone()) |
| 114 | + } else { |
| 115 | + None |
117 | 116 | } |
118 | | - cx.custom_type(name) |
119 | 117 | } |
| 118 | + _ => None, |
| 119 | + }; |
| 120 | + |
| 121 | + let resolved = match ty.peel_arrays() { |
| 122 | + ast::Type::Custom(name) => cx.custom_type(name), |
120 | 123 | ty => ty, |
121 | 124 | }; |
122 | 125 |
|
123 | | - let components = if let ast::Type::Tuple(tuple) = resolved { |
124 | | - tuple |
125 | | - .types |
| 126 | + let components = if let Some(fields) = original_fields { |
| 127 | + // Use original struct fields to preserve UDVT and other custom type names |
| 128 | + fields |
126 | 129 | .iter() |
127 | | - .enumerate() |
128 | | - .map(|(i, ty)| ty_to_param(component_names.get(i).cloned(), ty, cx)) |
| 130 | + .map(|field| ty_to_param(field.name.as_ref().map(|n| n.as_string()), &field.ty, cx)) |
129 | 131 | .collect() |
| 132 | + } else if let ast::Type::Tuple(tuple) = resolved { |
| 133 | + // For non-struct tuples, use the resolved types |
| 134 | + tuple.types.iter().map(|ty| ty_to_param(None, ty, cx)).collect() |
130 | 135 | } else { |
131 | 136 | vec![] |
132 | 137 | }; |
133 | 138 |
|
134 | | - // TODO: internal_type |
135 | | - let internal_type = None; |
| 139 | + let internal_type = ty_to_internal_type(ty, cx); |
136 | 140 |
|
137 | 141 | Param { ty: ty_name, name: name.unwrap_or_default(), internal_type, components } |
138 | 142 | } |
139 | 143 |
|
| 144 | +/// Generates the internal type for a given Solidity type. |
| 145 | +/// This represents the source-level type as it appears in the Solidity code. |
| 146 | +fn ty_to_internal_type(ty: &ast::Type, cx: &ExpCtxt<'_>) -> Option<InternalType> { |
| 147 | + // Collect array suffixes |
| 148 | + let mut array_suffix = String::new(); |
| 149 | + rec_ty_abi_string_suffix(cx, ty, &mut array_suffix); |
| 150 | + |
| 151 | + // Peel arrays to get the base type |
| 152 | + let base_ty = ty.peel_arrays(); |
| 153 | + |
| 154 | + match base_ty { |
| 155 | + ast::Type::Address(_, Some(_)) => { |
| 156 | + // Address payable |
| 157 | + Some(InternalType::AddressPayable(format!("address payable{array_suffix}"))) |
| 158 | + } |
| 159 | + ast::Type::Custom(path) => { |
| 160 | + // Determine the contract qualifier. |
| 161 | + let contract = if path.len() == 2 { |
| 162 | + // Explicit namespace: MyContract.MyStruct |
| 163 | + Some(path.first().as_string()) |
| 164 | + } else if path.len() == 1 { |
| 165 | + // Single component: check if we're in a contract namespace |
| 166 | + // and if the item is defined in that namespace |
| 167 | + cx.current_namespace.as_ref().map(|ns| ns.as_string()) |
| 168 | + } else { |
| 169 | + None |
| 170 | + }; |
| 171 | + |
| 172 | + // Get the type name (last component of the path) |
| 173 | + let type_name = path.last().as_string(); |
| 174 | + |
| 175 | + // Look up what kind of item this is |
| 176 | + match cx.try_item(path) { |
| 177 | + Some(ast::Item::Struct(_)) => Some(InternalType::Struct { |
| 178 | + contract, |
| 179 | + ty: format!("{type_name}{array_suffix}"), |
| 180 | + }), |
| 181 | + Some(ast::Item::Enum(_)) => { |
| 182 | + Some(InternalType::Enum { contract, ty: format!("{type_name}{array_suffix}") }) |
| 183 | + } |
| 184 | + Some(ast::Item::Contract(_)) => { |
| 185 | + Some(InternalType::Contract(format!("{type_name}{array_suffix}"))) |
| 186 | + } |
| 187 | + Some(ast::Item::Udt(_)) => { |
| 188 | + Some(InternalType::Other { contract, ty: format!("{type_name}{array_suffix}") }) |
| 189 | + } |
| 190 | + _ => { |
| 191 | + // Fallback for unresolved custom types |
| 192 | + Some(InternalType::Other { contract, ty: format!("{type_name}{array_suffix}") }) |
| 193 | + } |
| 194 | + } |
| 195 | + } |
| 196 | + _ => { |
| 197 | + // For built-in types, generate the internal type string |
| 198 | + let ty_str = format!("{}{array_suffix}", super::ty::TypePrinter::new(cx, base_ty)); |
| 199 | + Some(InternalType::Other { contract: None, ty: ty_str }) |
| 200 | + } |
| 201 | + } |
| 202 | +} |
| 203 | + |
140 | 204 | fn ty_abi_string(ty: &ast::Type, cx: &ExpCtxt<'_>) -> String { |
141 | 205 | let mut suffix = String::new(); |
142 | 206 | rec_ty_abi_string_suffix(cx, ty, &mut suffix); |
|
0 commit comments