feat: add uint8 type (#1639)

* feat: add uint8 type

* update changelog

* derive default

* fix: failing test
This commit is contained in:
Matthias Seitz 2022-08-28 21:17:48 +02:00 committed by GitHub
parent 6c017990a1
commit 0b04ffe787
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 178 additions and 8 deletions

View File

@ -4,6 +4,7 @@
### Unreleased ### 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) - 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) - 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) - Add support for Geth `debug_traceTransaction` [#1469](https://github.com/gakonst/ethers-rs/pull/1469)

View File

@ -356,6 +356,7 @@ impl Context {
param: &str, param: &str,
kind: &ParamType, kind: &ParamType,
) -> Result<TokenStream> { ) -> Result<TokenStream> {
let ethers_core = ethers_core_crate();
match kind { match kind {
ParamType::Array(ty) => { ParamType::Array(ty) => {
let ty = self.expand_input_param_type(fun, param, ty)?; let ty = self.expand_input_param_type(fun, param, ty)?;
@ -364,7 +365,18 @@ impl Context {
}) })
} }
ParamType::FixedArray(ty, size) => { 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; let size = *size;
Ok(quote! {[#ty; #size]}) Ok(quote! {[#ty; #size]})
} }

View File

@ -41,7 +41,17 @@ pub(crate) fn expand(kind: &ParamType) -> Result<TokenStream> {
} }
ParamType::FixedArray(t, n) => { ParamType::FixedArray(t, n) => {
// TODO(nlordell): see above // 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); let size = Literal::usize_unsuffixed(*n);
Ok(quote! { [#inner; #size] }) Ok(quote! { [#inner; #size] })
} }

View File

@ -679,3 +679,13 @@ fn can_generate_large_output_struct() {
let r = GetByIdReturn(Info::default()); 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
]"#,
);
}

View File

@ -2,7 +2,7 @@ use crate::{
abi::{ abi::{
AbiArrayType, AbiError, AbiType, Detokenize, Token, Tokenizable, TokenizableItem, Tokenize, 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 /// Trait for ABI encoding
@ -65,6 +65,7 @@ impl_abi_codec!(
U128, U128,
U256, U256,
I256, I256,
Uint8,
u8, u8,
u16, u16,
u32, u32,
@ -279,4 +280,24 @@ mod tests {
let encoded = value.encode(); let encoded = value.encode();
assert_eq!(value, String::decode(encoded).unwrap()); 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);
}
} }

View File

@ -1218,4 +1218,11 @@ mod tests {
event 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();
}
} }

View File

@ -1,10 +1,9 @@
//! This module implements extensions to the [`ethabi`](https://docs.rs/ethabi) API. //! 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) // Adapted from [Gnosis' ethcontract](https://github.com/gnosis/ethcontract-rs/blob/master/common/src/abiext.rs)
use crate::{ use crate::{
types::{Bytes, Selector}, types::{Bytes, Selector, Uint8, H256, H512, I256, U128, U256, U64},
utils::id, utils::id,
}; };
pub use ethabi::{self, Contract as Abi, *}; pub use ethabi::{self, Contract as Abi, *};
mod tokens; mod tokens;
@ -23,9 +22,6 @@ mod human_readable;
pub use human_readable::{ pub use human_readable::{
lexer::HumanReadableParser, parse as parse_abi, parse_str as parse_abi_str, AbiParser, lexer::HumanReadableParser, parse as parse_abi, parse_str as parse_abi_str, AbiParser,
}; };
use crate::types::{H256, H512, I256, U128, U256, U64};
mod sealed { mod sealed {
use ethabi::{Event, Function}; use ethabi::{Event, Function};
@ -166,6 +162,7 @@ impl_abi_type!(
str => String, str => String,
H256 => FixedBytes(32), H256 => FixedBytes(32),
H512 => FixedBytes(64), H512 => FixedBytes(64),
Uint8 => Uint(8),
U64 => Uint(64), U64 => Uint(64),
U128 => Uint(128), U128 => Uint(128),
U256 => Uint(256), U256 => Uint(256),

View File

@ -24,6 +24,9 @@ pub use path_or_string::PathOrString;
mod u256; mod u256;
pub use u256::*; pub use u256::*;
mod uint8;
pub use uint8::*;
mod i256; mod i256;
pub use i256::{Sign, I256}; pub use i256::{Sign, I256};

View File

@ -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)
);
}
}