use super::parser::{
Contract, Event, Function, Mutability, SolidityAST, StateVariable, Visibility,
};
use super::types::TypeMapper;
#[derive(Debug, Clone)]
pub struct DALAST {
pub services: Vec<Service>,
}
#[derive(Debug, Clone)]
pub struct Service {
pub name: String,
pub attributes: Vec<String>,
pub fields: Vec<Field>,
pub functions: Vec<DALFunction>,
pub events: Vec<DALEvent>,
}
#[derive(Debug, Clone)]
pub struct Field {
pub name: String,
pub field_type: String,
pub initial_value: Option<String>,
}
#[derive(Debug, Clone)]
pub struct DALFunction {
pub name: String,
pub parameters: Vec<DALParameter>,
pub return_type: Option<String>,
pub attributes: Vec<String>,
pub body: Option<String>,
pub comment: Option<String>,
}
#[derive(Debug, Clone)]
pub struct DALParameter {
pub name: String,
pub param_type: String,
}
#[derive(Debug, Clone)]
pub struct DALEvent {
pub name: String,
pub parameters: Vec<DALParameter>,
}
pub struct SolidityConverter {
type_mapper: TypeMapper,
}
impl SolidityConverter {
pub fn new() -> Self {
Self {
type_mapper: TypeMapper::new(),
}
}
pub fn convert(&self, solidity_ast: SolidityAST) -> Result<DALAST, String> {
let mut services = Vec::new();
for contract in solidity_ast.contracts {
services.extend(self.convert_contract_and_nested(contract, None)?);
}
Ok(DALAST { services })
}
fn convert_contract_and_nested(
&self,
contract: Contract,
name_prefix: Option<String>,
) -> Result<Vec<Service>, String> {
let name = match &name_prefix {
None => contract.name.clone(),
Some(p) => format!("{}_{}", p, contract.name),
};
let service = self.convert_contract(&contract, Some(name))?;
let mut out = vec![service];
let prefix = name_prefix
.map(|p| format!("{}_{}", p, contract.name))
.unwrap_or(contract.name.clone());
for nested in contract.nested_contracts {
out.extend(self.convert_contract_and_nested(nested, Some(prefix.clone()))?);
}
Ok(out)
}
fn convert_contract(
&self,
contract: &Contract,
name_override: Option<String>,
) -> Result<Service, String> {
let mut attributes = Vec::new();
attributes.push("@trust(\"hybrid\")".to_string());
attributes.push("@chain(\"ethereum\")".to_string());
if super::security::SecurityConverter::new().has_reentrancy_risk(&contract) {
attributes.push("@secure".to_string());
}
let fields = contract
.state_variables
.iter()
.map(|v| self.convert_state_variable(v))
.collect::<Result<Vec<_>, _>>()?;
let functions = contract
.functions
.iter()
.map(|f| self.convert_function(f))
.collect::<Result<Vec<_>, _>>()?;
let events = contract
.events
.iter()
.map(|e| self.convert_event(e))
.collect::<Result<Vec<_>, _>>()?;
Ok(Service {
name: name_override.unwrap_or_else(|| contract.name.clone()),
attributes,
fields,
functions,
events,
})
}
fn convert_state_variable(&self, var: &StateVariable) -> Result<Field, String> {
let dal_type = self.type_mapper.convert_type(&var.var_type);
Ok(Field {
name: var.name.clone(),
field_type: dal_type,
initial_value: var.initial_value.clone(),
})
}
fn convert_function(&self, func: &Function) -> Result<DALFunction, String> {
let mut attributes = Vec::new();
match func.visibility {
Visibility::Public => attributes.push("@public".to_string()),
Visibility::External => attributes.push("@public".to_string()), Visibility::Private => attributes.push("@private".to_string()),
Visibility::Internal => {} }
match func.mutability {
Mutability::View => attributes.push("@view".to_string()),
Mutability::Pure => attributes.push("@pure".to_string()),
Mutability::Payable => {
attributes.push("// @payable (value handling in DAL may differ)".to_string());
}
Mutability::NonPayable => {}
}
let parameters = func
.parameters
.iter()
.map(|p| DALParameter {
name: p.name.clone(),
param_type: self.type_mapper.convert_type(&p.param_type),
})
.collect();
let (return_type, comment) = if func.returns.is_empty() {
(None, None)
} else if func.returns.len() == 1 {
(
Some(self.type_mapper.convert_type(&func.returns[0].param_type)),
None,
)
} else {
let first = self.type_mapper.convert_type(&func.returns[0].param_type);
let original = func
.returns
.iter()
.map(|p| self.type_mapper.convert_type(&p.param_type))
.collect::<Vec<_>>()
.join(", ");
(
Some(first),
Some(format!(
"Multiple returns collapsed to first; original returns ({})",
original
)),
)
};
Ok(DALFunction {
name: func.name.clone(),
parameters,
return_type,
attributes,
body: func.body.clone(),
comment,
})
}
fn convert_event(&self, event: &Event) -> Result<DALEvent, String> {
let parameters = event
.parameters
.iter()
.map(|p| DALParameter {
name: p.name.clone(),
param_type: self.type_mapper.convert_type(&p.param_type),
})
.collect();
Ok(DALEvent {
name: event.name.clone(),
parameters,
})
}
}
impl Default for SolidityConverter {
fn default() -> Self {
Self::new()
}
}