Skip to content

Commit

Permalink
fix(json-abi): normalize $ to _ in identifiers in to_sol (#747)
Browse files Browse the repository at this point in the history
* fix(json-abi): normalize $ to _ in identifiers in to_sol

* chore: clippy
  • Loading branch information
DaniPopes authored Sep 24, 2024
1 parent 9183606 commit eb522be
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 14 deletions.
54 changes: 46 additions & 8 deletions crates/json-abi/src/to_sol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::{
EventParam, InternalType, JsonAbi, Param, StateMutability,
};
use alloc::{
borrow::Cow,
collections::{BTreeMap, BTreeSet},
string::String,
vec::Vec,
Expand All @@ -18,6 +19,7 @@ use core::{
pub struct ToSolConfig {
print_constructors: bool,
enums_as_udvt: bool,
for_sol_macro: bool,
}

impl Default for ToSolConfig {
Expand All @@ -31,7 +33,7 @@ impl ToSolConfig {
/// Creates a new configuration with default settings.
#[inline]
pub const fn new() -> Self {
Self { print_constructors: false, enums_as_udvt: true }
Self { print_constructors: false, enums_as_udvt: true, for_sol_macro: false }
}

/// Sets whether to print constructors. Default: `false`.
Expand All @@ -48,6 +50,14 @@ impl ToSolConfig {
self.enums_as_udvt = yes;
self
}

/// Sets whether to normalize the output for the [`sol!`] macro. Default: `false`.
///
/// [`sol!`]: https://docs.rs/alloy-sol-macro/latest/alloy_sol_macro/macro.sol.html
pub const fn for_sol_macro(mut self, yes: bool) -> Self {
self.for_sol_macro = yes;
self
}
}

pub(crate) trait ToSol {
Expand Down Expand Up @@ -97,6 +107,27 @@ impl<'a> SolPrinter<'a> {
fn indent(&mut self) {
self.push_str(" ");
}

/// Normalizes `s` as a Rust identifier and pushes it to the buffer.
///
/// See [`Self::normalize_ident`] for more details.
fn push_ident(&mut self, s: &str) {
let s = self.normalize_ident(s);
self.push_str(&s);
}

/// Normalizes `s` as a Rust identifier.
///
/// All Solidity identifiers are also valid Rust identifiers, except for `$`.
/// This function replaces `$` with `_` if the configuration is set to normalize for the `sol!`
/// macro.
fn normalize_ident<'b>(&self, s: &'b str) -> Cow<'b, str> {
if self.config.for_sol_macro && s.contains('$') {
Cow::Owned(s.replace('$', "_"))
} else {
Cow::Borrowed(s)
}
}
}

impl JsonAbi {
Expand Down Expand Up @@ -311,19 +342,19 @@ impl ToSol for It<'_> {
match self.kind {
ItKind::Enum => {
out.push_str("type ");
out.push_str(self.name);
out.push_ident(self.name);
out.push_str(" is uint8;");
}
ItKind::Udvt(ty) => {
out.push_str("type ");
out.push_str(self.name);
out.push_ident(self.name);
out.push_str(" is ");
out.push_str(ty);
out.push(';');
}
ItKind::Struct(components) => {
out.push_str("struct ");
out.push_str(self.name);
out.push_ident(self.name);
out.push_str(" {\n");
for component in components {
out.indent();
Expand Down Expand Up @@ -483,10 +514,17 @@ impl<IN: ToSol> ToSol for AbiFunction<'_, IN> {
out.print_param_location = true;
}

// TODO: Enable once `#[sol(rename)]` is implemented.
// if let Some(name) = self.name {
// if out.config.for_sol_macro && name.contains('$') {
// write!(out, "#[sol(rename = \"{name}\")]").unwrap();
// }
// }

out.push_str(self.kw.as_str());
if let Some(name) = self.name {
out.push(' ');
out.push_str(name);
out.push_ident(name);
}

out.push('(');
Expand Down Expand Up @@ -616,11 +654,11 @@ fn param(
_ => {
if let Some(contract_name) = contract_name {
if contract_name != out.name {
out.push_str(contract_name);
out.push_ident(contract_name);
out.push('.');
}
}
out.push_str(type_name);
out.push_ident(type_name);
}
}

Expand All @@ -639,6 +677,6 @@ fn param(
}
if !name.is_empty() {
out.push(' ');
out.push_str(name);
out.push_ident(name);
}
}
6 changes: 4 additions & 2 deletions crates/json-abi/tests/abi.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use alloy_json_abi::{AbiItem, EventParam, JsonAbi, Param};
use alloy_json_abi::{AbiItem, EventParam, JsonAbi, Param, ToSolConfig};
use pretty_assertions::assert_eq;
use std::{
collections::HashMap,
Expand Down Expand Up @@ -86,7 +86,7 @@ fn to_sol_test(path: &str, abi: &JsonAbi, run_solc: bool) {
// Ignore constructors for Solc tests.
abi.constructor = None;
abi.dedup();
let actual = abi.to_sol(name, None);
let actual = abi.to_sol(name, Some(ToSolConfig::new().for_sol_macro(true)));

ensure_file_contents(&sol_path, &actual);

Expand All @@ -98,6 +98,8 @@ fn to_sol_test(path: &str, abi: &JsonAbi, run_solc: bool) {
| "UniswapV1Exchange"
// https://github.com/alloy-rs/core/issues/744
| "DelegationManager"
// https://github.com/alloy-rs/core/issues/746
| "Bootstrap"
) {
return;
}
Expand Down
1 change: 1 addition & 0 deletions crates/json-abi/tests/abi/Bootstrap.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"type":"fallback","stateMutability":"payable"},{"type":"receive","stateMutability":"payable"},{"type":"function","name":"_getInitMSACalldata","inputs":[{"name":"$valdiators","type":"tuple[]","internalType":"struct BootstrapConfig[]","components":[{"name":"module","type":"address","internalType":"address"},{"name":"data","type":"bytes","internalType":"bytes"}]},{"name":"$executors","type":"tuple[]","internalType":"struct BootstrapConfig[]","components":[{"name":"module","type":"address","internalType":"address"},{"name":"data","type":"bytes","internalType":"bytes"}]},{"name":"_hook","type":"tuple","internalType":"struct BootstrapConfig","components":[{"name":"module","type":"address","internalType":"address"},{"name":"data","type":"bytes","internalType":"bytes"}]},{"name":"_fallbacks","type":"tuple[]","internalType":"struct BootstrapConfig[]","components":[{"name":"module","type":"address","internalType":"address"},{"name":"data","type":"bytes","internalType":"bytes"}]}],"outputs":[{"name":"init","type":"bytes","internalType":"bytes"}],"stateMutability":"view"},{"type":"function","name":"entryPoint","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"getActiveFallbackHandler","inputs":[{"name":"functionSig","type":"bytes4","internalType":"bytes4"}],"outputs":[{"name":"","type":"tuple","internalType":"struct ModuleManager.FallbackHandler","components":[{"name":"handler","type":"address","internalType":"address"},{"name":"calltype","type":"bytes1","internalType":"CallType"}]}],"stateMutability":"view"},{"type":"function","name":"getActiveHook","inputs":[],"outputs":[{"name":"hook","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"getExecutorsPaginated","inputs":[{"name":"cursor","type":"address","internalType":"address"},{"name":"size","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"array","type":"address[]","internalType":"address[]"},{"name":"next","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"getValidatorsPaginated","inputs":[{"name":"cursor","type":"address","internalType":"address"},{"name":"size","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"array","type":"address[]","internalType":"address[]"},{"name":"next","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"initMSA","inputs":[{"name":"$valdiators","type":"tuple[]","internalType":"struct BootstrapConfig[]","components":[{"name":"module","type":"address","internalType":"address"},{"name":"data","type":"bytes","internalType":"bytes"}]},{"name":"$executors","type":"tuple[]","internalType":"struct BootstrapConfig[]","components":[{"name":"module","type":"address","internalType":"address"},{"name":"data","type":"bytes","internalType":"bytes"}]},{"name":"_hook","type":"tuple","internalType":"struct BootstrapConfig","components":[{"name":"module","type":"address","internalType":"address"},{"name":"data","type":"bytes","internalType":"bytes"}]},{"name":"_fallbacks","type":"tuple[]","internalType":"struct BootstrapConfig[]","components":[{"name":"module","type":"address","internalType":"address"},{"name":"data","type":"bytes","internalType":"bytes"}]}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"singleInitMSA","inputs":[{"name":"validator","type":"address","internalType":"contract IModule"},{"name":"data","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"error","name":"AccountAccessUnauthorized","inputs":[]},{"type":"error","name":"CannotRemoveLastValidator","inputs":[]},{"type":"error","name":"HookAlreadyInstalled","inputs":[{"name":"currentHook","type":"address","internalType":"address"}]},{"type":"error","name":"HookPostCheckFailed","inputs":[]},{"type":"error","name":"InvalidModule","inputs":[{"name":"module","type":"address","internalType":"address"}]},{"type":"error","name":"LinkedList_EntryAlreadyInList","inputs":[{"name":"entry","type":"address","internalType":"address"}]},{"type":"error","name":"LinkedList_InvalidEntry","inputs":[{"name":"entry","type":"address","internalType":"address"}]},{"type":"error","name":"LinkedList_InvalidPage","inputs":[]},{"type":"error","name":"NoFallbackHandler","inputs":[{"name":"selector","type":"bytes4","internalType":"bytes4"}]}]
37 changes: 37 additions & 0 deletions crates/json-abi/tests/abi/Bootstrap.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
library ModuleManager {
struct FallbackHandler {
address handler;
CallType calltype;
}
}

interface Bootstrap {
type CallType is bytes1;
struct BootstrapConfig {
address module;
bytes data;
}

error AccountAccessUnauthorized();
error CannotRemoveLastValidator();
error HookAlreadyInstalled(address currentHook);
error HookPostCheckFailed();
error InvalidModule(address module);
error LinkedList_EntryAlreadyInList(address entry);
error LinkedList_InvalidEntry(address entry);
error LinkedList_InvalidPage();
error NoFallbackHandler(bytes4 selector);

fallback() external payable;

receive() external payable;

function _getInitMSACalldata(BootstrapConfig[] memory _valdiators, BootstrapConfig[] memory _executors, BootstrapConfig memory _hook, BootstrapConfig[] memory _fallbacks) external view returns (bytes memory init);
function entryPoint() external view returns (address);
function getActiveFallbackHandler(bytes4 functionSig) external view returns (ModuleManager.FallbackHandler memory);
function getActiveHook() external view returns (address hook);
function getExecutorsPaginated(address cursor, uint256 size) external view returns (address[] memory array, address next);
function getValidatorsPaginated(address cursor, uint256 size) external view returns (address[] memory array, address next);
function initMSA(BootstrapConfig[] memory _valdiators, BootstrapConfig[] memory _executors, BootstrapConfig memory _hook, BootstrapConfig[] memory _fallbacks) external;
function singleInitMSA(address validator, bytes memory data) external;
}
1 change: 1 addition & 0 deletions crates/json-abi/tests/abi/DollarIdentifiers.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"inputs":[{"components":[{"internalType":"uint256","name":"$dField","type":"uint256"}],"internalType":"struct DollarIdentifiers.$dStruct","name":"$dStructArg","type":"tuple"},{"internalType":"enum DollarIdentifiers.$dEnum","name":"$dEnumArg","type":"uint8"},{"internalType":"DollarIdentifiers.$dUDVT","name":"$dUDVTArg","type":"uint256"}],"name":"$dFunction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"$dPublicVariable","outputs":[{"internalType":"uint256","name":"$dField","type":"uint256"}],"stateMutability":"view","type":"function"}]
10 changes: 10 additions & 0 deletions crates/json-abi/tests/abi/DollarIdentifiers.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
interface DollarIdentifiers {
type _dEnum is uint8;
type _dUDVT is uint256;
struct _dStruct {
uint256 _dField;
}

function _dFunction(_dStruct memory _dStructArg, _dEnum _dEnumArg, _dUDVT _dUDVTArg) external;
function _dPublicVariable() external view returns (uint256 _dField);
}
7 changes: 4 additions & 3 deletions crates/sol-macro-input/src/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ Generated by the following Solidity interface...

let ast: ast::File = syn::parse2(tokens).map_err(|e| {
let msg = format!(
"failed to parse ABI-generated tokens into a Solidity AST: {e}.\n\
"failed to parse ABI-generated tokens into a Solidity AST for `{name}`: {e}.\n\
This is a bug. We would appreciate a bug report: \
https://github.com/alloy-rs/core/issues/new/choose"
);
Expand All @@ -105,14 +105,15 @@ Generated by the following Solidity interface...

fn abi_to_sol(name: &Ident, abi: &mut JsonAbi) -> String {
abi.dedup();
abi.to_sol(&name.to_string(), Some(ToSolConfig::new().print_constructors(true)))
let config = ToSolConfig::new().print_constructors(true).for_sol_macro(true);
abi.to_sol(&name.to_string(), Some(config))
}

/// Returns `sol!` tokens.
pub fn tokens_for_sol(name: &Ident, sol: &str) -> Result<TokenStream> {
let mk_err = |s: &str| {
let msg = format!(
"`JsonAbi::to_sol` generated invalid Rust tokens: {s}\n\
"`JsonAbi::to_sol` generated invalid Rust tokens for `{name}`: {s}\n\
This is a bug. We would appreciate a bug report: \
https://github.com/alloy-rs/core/issues/new/choose"
);
Expand Down
6 changes: 6 additions & 0 deletions crates/sol-types/tests/macros/sol/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,3 +233,9 @@ fn balancer_v2_vault() {
// fn eigenlayer_delegation_manager() {
// sol!(DelegationManager, "../json-abi/tests/abi/DelegationManager.json");
// }

// TODO: https://github.com/alloy-rs/core/issues/746
// #[test]
// fn smartsession_bootstrap() {
// sol!(Bootstrap, "../json-abi/tests/abi/Bootstrap.json");
// }
1 change: 0 additions & 1 deletion scripts/check_no_std.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ crates=(
alloy-core-sol-test
alloy-dyn-abi
alloy-json-abi
alloy-json-abi
alloy-primitives
# alloy-sol-macro
# alloy-sol-macro-expander
Expand Down

0 comments on commit eb522be

Please sign in to comment.