Skip to content

Commit bcd37f9

Browse files
committed
feat: add internalType generation
1 parent 3625aa0 commit bcd37f9

File tree

4 files changed

+583
-71
lines changed

4 files changed

+583
-71
lines changed

crates/sol-macro-expander/src/expand/to_abi.rs

Lines changed: 80 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use super::ExpCtxt;
22
use crate::verbatim::Verbatim;
33
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,
56
};
67
use ast::{ItemError, ItemEvent, ItemFunction};
78
use proc_macro2::TokenStream;
@@ -105,38 +106,101 @@ fn ty_to_param(name: Option<String>, ty: &ast::Type, cx: &ExpCtxt<'_>) -> Param
105106
ty_name = format!("tuple{suffix}");
106107
}
107108

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() {
110111
ast::Type::Custom(name) => {
111112
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
117116
}
118-
cx.custom_type(name)
119117
}
118+
_ => None,
119+
};
120+
121+
let resolved = match ty.peel_arrays() {
122+
ast::Type::Custom(name) => cx.custom_type(name),
120123
ty => ty,
121124
};
122125

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
126129
.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))
129131
.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()
130135
} else {
131136
vec![]
132137
};
133138

134-
// TODO: internal_type
135-
let internal_type = None;
139+
let internal_type = ty_to_internal_type(ty, cx);
136140

137141
Param { ty: ty_name, name: name.unwrap_or_default(), internal_type, components }
138142
}
139143

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+
140204
fn ty_abi_string(ty: &ast::Type, cx: &ExpCtxt<'_>) -> String {
141205
let mut suffix = String::new();
142206
rec_ty_abi_string_suffix(cx, ty, &mut suffix);

0 commit comments

Comments
 (0)