Skip to content

Commit

Permalink
feat: add uint8 type (#1639)
Browse files Browse the repository at this point in the history
* feat: add uint8 type

* update changelog

* derive default

* fix: failing test
  • Loading branch information
mattsse committed Aug 28, 2022
1 parent 6c01799 commit 0b04ffe
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Unreleased

- Add `Unit8` helper type [#1639](https://github.com/gakonst/ethers-rs/pull/1639)
- Add `evm.deployedBytecode.immutableReferences` output selector [#1523](https://github.com/gakonst/ethers-rs/pull/1523)
- Added `get_erc1155_token_transfer_events` function for etherscan client [#1503](https://github.com/gakonst/ethers-rs/pull/1503)
- Add support for Geth `debug_traceTransaction` [#1469](https://github.com/gakonst/ethers-rs/pull/1469)
Expand Down
14 changes: 13 additions & 1 deletion ethers-contract/ethers-contract-abigen/src/contract/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ impl Context {
param: &str,
kind: &ParamType,
) -> Result<TokenStream> {
let ethers_core = ethers_core_crate();
match kind {
ParamType::Array(ty) => {
let ty = self.expand_input_param_type(fun, param, ty)?;
Expand All @@ -364,7 +365,18 @@ impl Context {
})
}
ParamType::FixedArray(ty, size) => {
let ty = self.expand_input_param_type(fun, param, ty)?;
let ty = match **ty {
ParamType::Uint(size) => {
if size / 8 == 1 {
// this prevents type ambiguity with `FixedBytes`
quote! { #ethers_core::types::Uint8}
} else {
self.expand_input_param_type(fun, param, ty)?
}
}
_ => self.expand_input_param_type(fun, param, ty)?,
};

let size = *size;
Ok(quote! {[#ty; #size]})
}
Expand Down
12 changes: 11 additions & 1 deletion ethers-contract/ethers-contract-abigen/src/contract/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,17 @@ pub(crate) fn expand(kind: &ParamType) -> Result<TokenStream> {
}
ParamType::FixedArray(t, n) => {
// TODO(nlordell): see above
let inner = expand(t)?;
let inner = match **t {
ParamType::Uint(size) => {
if size / 8 == 1 {
// this prevents type ambiguity with `FixedBytes`
quote! { #ethers_core::types::Uint8}
} else {
expand(t)?
}
}
_ => expand(t)?,
};
let size = Literal::usize_unsuffixed(*n);
Ok(quote! { [#inner; #size] })
}
Expand Down
10 changes: 10 additions & 0 deletions ethers-contract/tests/it/abigen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -679,3 +679,13 @@ fn can_generate_large_output_struct() {

let r = GetByIdReturn(Info::default());
}

#[test]
fn gen_complex_function() {
abigen!(
WyvernExchangeV1,
r#"[
function atomicMatch_(address[14] addrs, uint[18] uints, uint8[8] feeMethodsSidesKindsHowToCalls, bytes calldataBuy, bytes calldataSell, bytes replacementPatternBuy, bytes replacementPatternSell, bytes staticExtradataBuy, bytes staticExtradataSell, uint8[2] vs, bytes32[5] rssMetadata) public payable
]"#,
);
}
23 changes: 22 additions & 1 deletion ethers-core/src/abi/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
abi::{
AbiArrayType, AbiError, AbiType, Detokenize, Token, Tokenizable, TokenizableItem, Tokenize,
},
types::{Address, Bytes, H256, I256, U128, U256},
types::{Address, Bytes, Uint8, H256, I256, U128, U256},
};

/// Trait for ABI encoding
Expand Down Expand Up @@ -65,6 +65,7 @@ impl_abi_codec!(
U128,
U256,
I256,
Uint8,
u8,
u16,
u32,
Expand Down Expand Up @@ -279,4 +280,24 @@ mod tests {
let encoded = value.encode();
assert_eq!(value, String::decode(encoded).unwrap());
}

#[test]
fn should_decode_array_of_fixed_uint8() {
// uint8[8]
let tokens = vec![Token::FixedArray(vec![
Token::Uint(1.into()),
Token::Uint(2.into()),
Token::Uint(3.into()),
Token::Uint(4.into()),
Token::Uint(5.into()),
Token::Uint(6.into()),
Token::Uint(7.into()),
Token::Uint(8.into()),
])];
let data: [Uint8; 8] = Detokenize::from_tokens(tokens).unwrap();
assert_eq!(data[0], 1);
assert_eq!(data[1], 2);
assert_eq!(data[2], 3);
assert_eq!(data[7], 8);
}
}
7 changes: 7 additions & 0 deletions ethers-core/src/abi/human_readable/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1218,4 +1218,11 @@ mod tests {
event
);
}

#[test]
fn parse_large_function() {
let f = "function atomicMatch_(address[14] addrs, uint[18] uints, uint8[8] feeMethodsSidesKindsHowToCalls, bytes calldataBuy, bytes calldataSell, bytes replacementPatternBuy, bytes replacementPatternSell, bytes staticExtradataBuy, bytes staticExtradataSell, uint8[2] vs, bytes32[5] rssMetadata) public payable";

let _fun = HumanReadableParser::parse_function(f).unwrap();
}
}
7 changes: 2 additions & 5 deletions ethers-core/src/abi/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
//! This module implements extensions to the [`ethabi`](https://docs.rs/ethabi) API.
// Adapted from [Gnosis' ethcontract](https://github.com/gnosis/ethcontract-rs/blob/master/common/src/abiext.rs)
use crate::{
types::{Bytes, Selector},
types::{Bytes, Selector, Uint8, H256, H512, I256, U128, U256, U64},
utils::id,
};

pub use ethabi::{self, Contract as Abi, *};

mod tokens;
Expand All @@ -23,9 +22,6 @@ mod human_readable;
pub use human_readable::{
lexer::HumanReadableParser, parse as parse_abi, parse_str as parse_abi_str, AbiParser,
};

use crate::types::{H256, H512, I256, U128, U256, U64};

mod sealed {
use ethabi::{Event, Function};

Expand Down Expand Up @@ -166,6 +162,7 @@ impl_abi_type!(
str => String,
H256 => FixedBytes(32),
H512 => FixedBytes(64),
Uint8 => Uint(8),
U64 => Uint(64),
U128 => Uint(128),
U256 => Uint(256),
Expand Down
3 changes: 3 additions & 0 deletions ethers-core/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ pub use path_or_string::PathOrString;
mod u256;
pub use u256::*;

mod uint8;
pub use uint8::*;

mod i256;
pub use i256::{Sign, I256};

Expand Down
109 changes: 109 additions & 0 deletions ethers-core/src/types/uint8.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//! This module contains a helper type for `uint8`
//!
//! The reason this exists is to circumvent ambiguity with fixed bytes arrays

use crate::abi::{InvalidOutputType, Tokenizable, TokenizableItem};
use ethabi::{ethereum_types::U256, Token};
use serde::{Deserialize, Serialize};
use std::ops::{Add, Sub};

/// A wrapper for `u8`
///
/// Note: this type is only necessary in conjunction with `FixedBytes` so that `[Uint8; 8]` is
/// recognized as `uint8[8]` and not fixed bytes.
///
/// See also <https://github.com/gakonst/ethers-rs/issues/1636>
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Default, Ord, PartialOrd)]
#[repr(transparent)]
#[serde(transparent)]
pub struct Uint8(u8);

impl From<u8> for Uint8 {
fn from(val: u8) -> Self {
Uint8(val)
}
}

impl From<Uint8> for u8 {
fn from(val: Uint8) -> Self {
val.0
}
}

impl From<Uint8> for U256 {
fn from(val: Uint8) -> Self {
U256::from(val.0)
}
}

impl PartialEq<u8> for Uint8 {
fn eq(&self, other: &u8) -> bool {
self.0 == *other
}
}

impl Add for Uint8 {
type Output = Self;

fn add(self, rhs: Self) -> Self::Output {
Uint8(self.0 + rhs.0)
}
}

impl Sub for Uint8 {
type Output = Self;

fn sub(self, rhs: Self) -> Self::Output {
Uint8(self.0 - rhs.0)
}
}

impl Add<u8> for Uint8 {
type Output = Self;

fn add(self, rhs: u8) -> Self::Output {
Uint8(self.0 + rhs)
}
}

impl Sub<u8> for Uint8 {
type Output = Self;

fn sub(self, rhs: u8) -> Self::Output {
Uint8(self.0 - rhs)
}
}

impl Tokenizable for Uint8 {
fn from_token(token: Token) -> Result<Self, InvalidOutputType> {
match token {
Token::Int(data) | Token::Uint(data) => {
if data > U256::from(u8::MAX) {
return Err(InvalidOutputType("Integer overflow when casting to u8".to_string()))
}
Ok(Uint8(data.low_u32() as u8))
}
other => Err(InvalidOutputType(format!("Expected `uint8`, got {:?}", other))),
}
}
fn into_token(self) -> Token {
Token::Uint(self.into())
}
}

impl TokenizableItem for Uint8 {}

#[cfg(test)]
mod tests {
use super::*;
use crate::abi::AbiType;
use ethabi::ParamType;

#[test]
fn uint8_array() {
assert_eq!(
<[Uint8; 8usize]>::param_type(),
ParamType::FixedArray(Box::new(ParamType::Uint(8),), 8)
);
}
}

0 comments on commit 0b04ffe

Please sign in to comment.