Skip to content

Commit

Permalink
feat: function type
Browse files Browse the repository at this point in the history
  • Loading branch information
DaniPopes committed Aug 3, 2023
1 parent 4f7a72c commit 2a4f282
Show file tree
Hide file tree
Showing 16 changed files with 224 additions and 44 deletions.
6 changes: 5 additions & 1 deletion crates/dyn-abi/src/arbitrary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#![allow(clippy::arc_with_non_send_sync)]

use crate::{DynSolType, DynSolValue};
use alloy_primitives::{Address, B256, I256, U256};
use alloy_primitives::{Address, Function, B256, I256, U256};
use arbitrary::{size_hint, Unstructured};
use core::ops::RangeInclusive;
use proptest::{
Expand Down Expand Up @@ -133,6 +133,7 @@ enum Choice {
Int,
Uint,
Address,
Function,
FixedBytes,
Bytes,
String,
Expand All @@ -151,6 +152,7 @@ impl<'a> arbitrary::Arbitrary<'a> for DynSolType {
Choice::Int => u.arbitrary().map(int_size).map(Self::Int),
Choice::Uint => u.arbitrary().map(int_size).map(Self::Uint),
Choice::Address => Ok(Self::Address),
Choice::Function => Ok(Self::Function),
Choice::FixedBytes => Ok(Self::FixedBytes(u.int_in_range(1..=32)?)),
Choice::Bytes => Ok(Self::Bytes),
Choice::String => Ok(Self::String),
Expand Down Expand Up @@ -359,6 +361,7 @@ impl DynSolValue {
match ty {
DynSolType::Bool => u.arbitrary().map(Self::Bool),
DynSolType::Address => u.arbitrary().map(Self::Address),
DynSolType::Function => u.arbitrary().map(Self::Function),
&DynSolType::Int(sz) => u.arbitrary().map(|x| Self::Int(x, sz)),
&DynSolType::Uint(sz) => u.arbitrary().map(|x| Self::Uint(x, sz)),
&DynSolType::FixedBytes(sz) => u.arbitrary().map(|x| Self::FixedBytes(x, sz)),
Expand Down Expand Up @@ -410,6 +413,7 @@ impl DynSolValue {
match ty {
DynSolType::Bool => any::<bool>().prop_map(Self::Bool).sboxed(),
DynSolType::Address => any::<Address>().prop_map(Self::Address).sboxed(),
DynSolType::Function => any::<Function>().prop_map(Self::Function).sboxed(),
&DynSolType::Int(sz) => any::<I256>().prop_map(move |x| Self::Int(x, sz)).sboxed(),
&DynSolType::Uint(sz) => any::<U256>().prop_map(move |x| Self::Uint(x, sz)).sboxed(),
&DynSolType::FixedBytes(sz) => any::<B256>()
Expand Down
15 changes: 14 additions & 1 deletion crates/dyn-abi/src/eip712/coerce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ use alloc::{
string::{String, ToString},
vec::Vec,
};
use alloy_primitives::{Address, I256, U256};
use alloy_primitives::{Address, Function, I256, U256};

impl DynSolType {
/// Coerce a [`serde_json::Value`] to a [`DynSolValue`] via this type.
pub fn coerce(&self, value: &serde_json::Value) -> DynAbiResult<DynSolValue> {
match self {
DynSolType::Address => address(value),
DynSolType::Function => function(value),
DynSolType::Bool => bool(value),
DynSolType::Int(n) => int(*n, value),
DynSolType::Uint(n) => uint(*n, value),
Expand Down Expand Up @@ -41,6 +42,18 @@ fn address(value: &serde_json::Value) -> DynAbiResult<DynSolValue> {
Ok(DynSolValue::Address(address))
}

fn function(value: &serde_json::Value) -> DynAbiResult<DynSolValue> {
let function = value
.as_str()
.map(|s| {
s.parse::<Function>()
.map_err(|_| DynAbiError::type_mismatch(DynSolType::Function, value))
})
.ok_or_else(|| DynAbiError::type_mismatch(DynSolType::Function, value))??;

Ok(DynSolValue::Function(function))
}

fn bool(value: &serde_json::Value) -> DynAbiResult<DynSolValue> {
if let Some(bool) = value.as_bool() {
return Ok(DynSolValue::Bool(bool))
Expand Down
22 changes: 17 additions & 5 deletions crates/dyn-abi/src/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ struct StructProp {
pub enum DynSolType {
/// Address.
Address,
/// Function.
Function,
/// Boolean.
Bool,
/// Signed Integer.
Expand Down Expand Up @@ -222,6 +224,7 @@ impl DynSolType {
pub fn matches(&self, value: &DynSolValue) -> bool {
match self {
Self::Address => matches!(value, DynSolValue::Address(_)),
Self::Function => matches!(value, DynSolValue::Function(_)),
Self::Bytes => matches!(value, DynSolValue::Bytes(_)),
Self::Int(size) => matches!(value, DynSolValue::Int(_, s) if s == size),
Self::Uint(size) => matches!(value, DynSolValue::Uint(_, s) if s == size),
Expand Down Expand Up @@ -362,6 +365,7 @@ impl DynSolType {
fn sol_type_name_simple(&self) -> Option<&str> {
match self {
Self::Address => Some("address"),
Self::Function => Some("function"),
Self::Bool => Some("bool"),
Self::Bytes => Some("bytes"),
Self::String => Some("string"),
Expand All @@ -375,11 +379,16 @@ impl DynSolType {
fn sol_type_name_raw(&self, out: &mut String) {
match self {
#[cfg(feature = "eip712")]
Self::Address | Self::Bool | Self::Bytes | Self::String | Self::CustomStruct { .. } => {
Self::Address
| Self::Function
| Self::Bool
| Self::Bytes
| Self::String
| Self::CustomStruct { .. } => {
out.push_str(unsafe { self.sol_type_name_simple().unwrap_unchecked() });
}
#[cfg(not(feature = "eip712"))]
Self::Address | Self::Bool | Self::Bytes | Self::String => {
Self::Address | Self::Function | Self::Bool | Self::Bytes | Self::String => {
out.push_str(unsafe { self.sol_type_name_simple().unwrap_unchecked() });
}

Expand Down Expand Up @@ -450,9 +459,12 @@ impl DynSolType {
/// Instantiate an empty dyn token, to be decoded into.
pub(crate) fn empty_dyn_token(&self) -> DynToken<'_> {
match self {
Self::Address | Self::Bool | Self::FixedBytes(_) | Self::Int(_) | Self::Uint(_) => {
DynToken::Word(Word::ZERO)
}
Self::Address
| Self::Function
| Self::Bool
| Self::FixedBytes(_)
| Self::Int(_)
| Self::Uint(_) => DynToken::Word(Word::ZERO),

Self::Bytes | Self::String => DynToken::PackedSeq(&[]),

Expand Down
19 changes: 17 additions & 2 deletions crates/dyn-abi/src/value.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{DynSolType, DynToken, Word};
use alloc::{borrow::Cow, boxed::Box, string::String, vec::Vec};
use alloy_primitives::{Address, I256, U256};
use alloy_primitives::{Address, Function, I256, U256};
use alloy_sol_types::{utils::words_for_len, Encoder};

#[cfg(feature = "eip712")]
Expand Down Expand Up @@ -39,6 +39,8 @@ macro_rules! as_fixed_seq {
pub enum DynSolValue {
/// An address.
Address(Address),
/// A function pointer.
Function(Function),
/// A boolean.
Bool(bool),
/// A signed integer.
Expand Down Expand Up @@ -172,6 +174,7 @@ impl DynSolValue {
pub fn as_type(&self) -> Option<DynSolType> {
let ty = match self {
Self::Address(_) => DynSolType::Address,
Self::Function(_) => DynSolType::Function,
Self::Bool(_) => DynSolType::Bool,
Self::Bytes(_) => DynSolType::Bytes,
Self::FixedBytes(_, size) => DynSolType::FixedBytes(*size),
Expand Down Expand Up @@ -211,6 +214,7 @@ impl DynSolValue {
fn sol_type_name_simple(&self) -> Option<&str> {
match self {
Self::Address(_) => Some("address"),
Self::Function(_) => Some("function"),
Self::Bool(_) => Some("bool"),
Self::Bytes(_) => Some("bytes"),
Self::String(_) => Some("string"),
Expand All @@ -224,11 +228,16 @@ impl DynSolValue {
fn sol_type_name_raw(&self, out: &mut String) -> bool {
match self {
#[cfg(not(feature = "eip712"))]
Self::Address(_) | Self::Bool(_) | Self::Bytes(_) | Self::String(_) => {
Self::Address(_)
| Self::Function(_)
| Self::Bool(_)
| Self::Bytes(_)
| Self::String(_) => {
out.push_str(unsafe { self.sol_type_name_simple().unwrap_unchecked() });
}
#[cfg(feature = "eip712")]
Self::Address(_)
| Self::Function(_)
| Self::Bool(_)
| Self::Bytes(_)
| Self::String(_)
Expand Down Expand Up @@ -490,6 +499,7 @@ impl DynSolValue {
pub fn is_dynamic(&self) -> bool {
match self {
Self::Address(_)
| Self::Function(_)
| Self::Bool(_)
| Self::Int(..)
| Self::Uint(..)
Expand Down Expand Up @@ -527,6 +537,7 @@ impl DynSolValue {
match self {
// `self.is_word()`
Self::Address(_)
| Self::Function(_)
| Self::Bool(_)
| Self::FixedBytes(..)
| Self::Int(..)
Expand Down Expand Up @@ -570,6 +581,7 @@ impl DynSolValue {
pub fn head_append(&self, enc: &mut Encoder) {
match self {
Self::Address(_)
| Self::Function(_)
| Self::Bool(_)
| Self::FixedBytes(..)
| Self::Int(..)
Expand All @@ -592,6 +604,7 @@ impl DynSolValue {
pub fn tail_append(&self, enc: &mut Encoder) {
match self {
Self::Address(_)
| Self::Function(_)
| Self::Bool(_)
| Self::FixedBytes(..)
| Self::Int(..)
Expand All @@ -617,6 +630,7 @@ impl DynSolValue {
pub fn encode_packed_to(&self, buf: &mut Vec<u8>) {
match self {
Self::Address(addr) => buf.extend_from_slice(addr.as_slice()),
Self::Function(func) => buf.extend_from_slice(func.as_slice()),
Self::Bool(b) => buf.push(*b as u8),
Self::String(s) => buf.extend_from_slice(s.as_bytes()),
Self::Bytes(bytes) => buf.extend_from_slice(bytes),
Expand Down Expand Up @@ -653,6 +667,7 @@ impl DynSolValue {
pub fn tokenize(&self) -> DynToken<'_> {
match self {
Self::Address(a) => a.into_word().into(),
Self::Function(f) => f.into_word().into(),
Self::Bool(b) => Word::with_last_byte(*b as u8).into(),
Self::Bytes(buf) => DynToken::PackedSeq(buf),
Self::FixedBytes(buf, _) => (*buf).into(),
Expand Down
22 changes: 14 additions & 8 deletions crates/primitives/src/bits/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,22 @@ pub enum AddressError {
}

impl From<hex::FromHexError> for AddressError {
#[inline]
fn from(value: hex::FromHexError) -> Self {
Self::Hex(value)
}
}

#[cfg(feature = "std")]
impl std::error::Error for AddressError {}
impl std::error::Error for AddressError {
#[inline]
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Hex(err) => Some(err),
Self::InvalidChecksum => None,
}
}
}

impl fmt::Display for AddressError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand Down Expand Up @@ -173,23 +182,21 @@ impl Address {
s: S,
chain_id: Option<u64>,
) -> Result<Self, AddressError> {
fn inner(s: &str, chain_id: Option<u64>) -> Result<Address, AddressError> {
fn parse_checksummed(s: &str, chain_id: Option<u64>) -> Result<Address, AddressError> {
// checksummed addresses always start with the "0x" prefix
if !s.starts_with("0x") {
return Err(AddressError::Hex(hex::FromHexError::InvalidStringLength))
}

let address: Address = s.parse()?;
let buf = &mut [0; 42];
let expected = address.to_checksum_raw(buf, chain_id);
if s == expected {
if s == address.to_checksum_raw(&mut [0; 42], chain_id) {
Ok(address)
} else {
Err(AddressError::InvalidChecksum)
}
}

inner(s.as_ref(), chain_id)
parse_checksummed(s.as_ref(), chain_id)
}

/// Encodes an Ethereum address to its [EIP-55] checksum.
Expand Down Expand Up @@ -292,8 +299,7 @@ impl Address {
#[inline]
#[must_use]
pub fn to_checksum(&self, chain_id: Option<u64>) -> String {
let mut buf = [0u8; 42];
self.to_checksum_raw(&mut buf, chain_id).to_string()
self.to_checksum_raw(&mut [0u8; 42], chain_id).to_string()
}

/// Computes the `create` address for this address and nonce:
Expand Down
28 changes: 28 additions & 0 deletions crates/primitives/src/bits/function.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use crate::FixedBytes;

wrap_fixed_bytes! {
/// An Ethereum ABI function pointer, 24 bytes in length.
///
/// An address (20 bytes), followed by a function selector (4 bytes).
/// Encoded identical to `bytes24`.
pub struct Function<24>;
}

impl Function {
/// Creates an Ethereum function from an EVM word's upper 24 bytes
/// (`word[8..]`).
#[inline]
#[must_use]
pub fn from_word(word: FixedBytes<32>) -> Self {
Self(FixedBytes(word[8..].try_into().unwrap()))
}

/// Left-pads the function to 32 bytes (EVM word size).
#[inline]
#[must_use]
pub fn into_word(&self) -> FixedBytes<32> {
let mut word = [0; 32];
word[8..].copy_from_slice(self.as_slice());
FixedBytes(word)
}
}
3 changes: 3 additions & 0 deletions crates/primitives/src/bits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ pub use bloom::{Bloom, BloomInput, BLOOM_BITS_PER_ITEM, BLOOM_SIZE_BITS, BLOOM_S
mod fixed;
pub use fixed::FixedBytes;

mod function;
pub use function::Function;

#[cfg(feature = "rlp")]
mod rlp;

Expand Down
4 changes: 2 additions & 2 deletions crates/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ pub use aliases::{

mod bits;
pub use bits::{
Address, AddressError, Bloom, BloomInput, FixedBytes, BLOOM_BITS_PER_ITEM, BLOOM_SIZE_BITS,
BLOOM_SIZE_BYTES,
Address, AddressError, Bloom, BloomInput, FixedBytes, Function, BLOOM_BITS_PER_ITEM,
BLOOM_SIZE_BITS, BLOOM_SIZE_BYTES,
};

mod bytes;
Expand Down
9 changes: 7 additions & 2 deletions crates/sol-macro/src/expand/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,13 @@ fn rec_expand_type(ty: &Type, tokens: &mut TokenStream) {
}
}
}
Type::Function(ref function) => todo!("function types: {function:?}"),
Type::Mapping(ref mapping) => todo!("mapping types: {mapping:?}"),
Type::Function(ref function) => {
let span = function.span();
quote_spanned! {span=>
::alloy_sol_types::sol_data::Function
}
}
Type::Mapping(ref mapping) => panic!("mapping types are unsupported: {mapping:?}"),
Type::Custom(ref custom) => return custom.to_tokens(tokens),
};
tokens.extend(tts);
Expand Down
8 changes: 4 additions & 4 deletions crates/sol-type-parser/src/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ impl<'a> RootType<'a> {
#[inline]
pub fn try_basic_solidity(self) -> Result<()> {
match self.0 {
"address" | "bool" | "string" | "bytes" | "uint" | "int" => Ok(()),
"address" | "bool" | "string" | "bytes" | "uint" | "int" | "function" => Ok(()),
name => {
if let Some(sz) = name.strip_prefix("bytes") {
if let Ok(sz) = sz.parse::<usize>() {
Expand All @@ -96,10 +96,10 @@ impl<'a> RootType<'a> {
return Ok(())
}
}
Err(Error::invalid_size(name))
} else {
Err(Error::invalid_type_string(name))
return Err(Error::invalid_size(name))
}

Err(Error::invalid_type_string(name))
}
}
}
Expand Down
Loading

0 comments on commit 2a4f282

Please sign in to comment.