Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: function type #224

Merged
merged 1 commit into from
Aug 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
1 change: 1 addition & 0 deletions crates/dyn-abi/src/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ impl ResolveSolType for RootType<'_> {
fn resolve(&self) -> DynAbiResult<DynSolType> {
match self.span() {
"address" => Ok(DynSolType::Address),
"function" => Ok(DynSolType::Function),
"bool" => Ok(DynSolType::Bool),
"string" => Ok(DynSolType::String),
"bytes" => Ok(DynSolType::Bytes),
Expand Down
25 changes: 20 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 @@ -272,6 +275,9 @@ impl DynSolType {
(Self::Address, DynToken::Word(word)) => Ok(DynSolValue::Address(
sol_data::Address::detokenize(word.into()),
)),
(Self::Function, DynToken::Word(word)) => Ok(DynSolValue::Function(
sol_data::Function::detokenize(word.into()),
)),
(Self::Bool, DynToken::Word(word)) => {
Ok(DynSolValue::Bool(sol_data::Bool::detokenize(word.into())))
}
Expand Down Expand Up @@ -362,6 +368,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 +382,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 +462,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
30 changes: 25 additions & 5 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 @@ -326,6 +335,7 @@ impl DynSolValue {
pub fn as_word(&self) -> Option<Word> {
match *self {
Self::Address(a) => Some(a.into_word()),
Self::Function(f) => Some(f.into_word()),
Self::Bool(b) => Some(Word::with_last_byte(b as u8)),
Self::FixedBytes(w, _) => Some(w),
Self::Int(i, _) => Some(i.into()),
Expand Down Expand Up @@ -490,6 +500,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 +538,7 @@ impl DynSolValue {
match self {
// `self.is_word()`
Self::Address(_)
| Self::Function(_)
| Self::Bool(_)
| Self::FixedBytes(..)
| Self::Int(..)
Expand Down Expand Up @@ -570,6 +582,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 +605,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 +631,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 +668,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 All @@ -669,11 +685,15 @@ impl DynSolValue {
let head_words = contents.iter().map(Self::head_words).sum::<usize>();
enc.push_offset(head_words as u32);

contents.iter().for_each(|t| {
for t in contents {
t.head_append(enc);
enc.bump_offset(t.tail_words() as u32);
});
contents.iter().for_each(|t| t.tail_append(enc));
}

for t in contents {
t.tail_append(enc);
}

enc.pop_offset();
}

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
Loading