diff --git a/ethers-contract/ethers-contract-abigen/src/contract/common.rs b/ethers-contract/ethers-contract-abigen/src/contract/common.rs index 5868f19d..afea146a 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/common.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/common.rs @@ -1,11 +1,9 @@ -use super::{util, Context}; - -use crate::contract::types; +use super::{types, util, Context}; use ethers_core::{ abi::{Param, ParamType}, macros::{ethers_contract_crate, ethers_core_crate, ethers_providers_crate}, }; -use proc_macro2::{Ident, TokenStream}; +use proc_macro2::{Ident, Literal, TokenStream}; use quote::quote; /// Expands to the `name : type` pairs for the params @@ -87,49 +85,56 @@ pub(crate) fn imports(name: &str) -> TokenStream { } } -/// Generates the static `Abi` constants and the contract struct +/// Generates the token stream for the contract's ABI, bytecode and struct declarations. pub(crate) fn struct_declaration(cx: &Context) -> TokenStream { let name = &cx.contract_ident; - let abi = &cx.abi_str; - - let abi_name = cx.inline_abi_ident(); let ethers_core = ethers_core_crate(); let ethers_contract = ethers_contract_crate(); - let abi_parse = if !cx.human_readable { + let abi = { + let abi_name = cx.inline_abi_ident(); + let abi = &cx.abi_str; + let (doc_str, parse) = if cx.human_readable { + // Human readable: use abi::parse_abi_str + let doc_str = "The parsed human-readable ABI of the contract."; + let parse = quote!(#ethers_core::abi::parse_abi_str(__ABI)); + (doc_str, parse) + } else { + // JSON ABI: use serde_json::from_str + let doc_str = "The parsed JSON ABI of the contract."; + let parse = quote!(#ethers_core::utils::__serde_json::from_str(__ABI)); + (doc_str, parse) + }; + quote! { #[rustfmt::skip] const __ABI: &str = #abi; - /// The parsed JSON-ABI of the contract. - pub static #abi_name: #ethers_contract::Lazy<#ethers_core::abi::Abi> = #ethers_contract::Lazy::new(|| #ethers_core::utils::__serde_json::from_str(__ABI) - .expect("invalid abi")); - } - } else { - quote! { - /// The parsed human readable ABI of the contract. - pub static #abi_name: #ethers_contract::Lazy<#ethers_core::abi::Abi> = #ethers_contract::Lazy::new(|| #ethers_core::abi::parse_abi_str(#abi) - .expect("invalid abi")); + // This never fails as we are parsing the ABI in this macro + #[doc = #doc_str] + pub static #abi_name: #ethers_contract::Lazy<#ethers_core::abi::Abi> = + #ethers_contract::Lazy::new(|| #parse.expect("ABI is always valid")); } }; - let bytecode = if let Some(ref bytecode) = cx.contract_bytecode { + let bytecode = cx.contract_bytecode.as_ref().map(|bytecode| { + let bytecode = bytecode.iter().copied().map(Literal::u8_unsuffixed); let bytecode_name = cx.inline_bytecode_ident(); - let hex_bytecode = format!("{bytecode}"); quote! { - /// Bytecode of the #name contract - pub static #bytecode_name: #ethers_contract::Lazy<#ethers_core::types::Bytes> = #ethers_contract::Lazy::new(|| #hex_bytecode.parse() - .expect("invalid bytecode")); + #[rustfmt::skip] + const __BYTECODE: &[u8] = &[ #( #bytecode ),* ]; + + #[doc = "The bytecode of the contract."] + pub static #bytecode_name: #ethers_core::types::Bytes = #ethers_core::types::Bytes::from_static(__BYTECODE); } - } else { - quote! {} - }; + }); quote! { - // Inline ABI declaration - #abi_parse + // The `Lazy` ABI + #abi + // The static Bytecode, if present #bytecode // Struct declaration diff --git a/ethers-core/src/types/bytes.rs b/ethers-core/src/types/bytes.rs index b6d8833c..f09c9170 100644 --- a/ethers-core/src/types/bytes.rs +++ b/ethers-core/src/types/bytes.rs @@ -16,31 +16,62 @@ pub struct Bytes( pub bytes::Bytes, ); -fn bytes_to_hex(b: &Bytes) -> String { - hex::encode(b.0.as_ref()) +impl Bytes { + /// Creates a new empty `Bytes`. + /// + /// This will not allocate and the returned `Bytes` handle will be empty. + /// + /// # Examples + /// + /// ``` + /// use ethers_core::types::Bytes; + /// + /// let b = Bytes::new(); + /// assert_eq!(&b[..], b""); + /// ``` + #[inline] + pub const fn new() -> Self { + Self(bytes::Bytes::new()) + } + + /// Creates a new `Bytes` from a static slice. + /// + /// The returned `Bytes` will point directly to the static slice. There is + /// no allocating or copying. + /// + /// # Examples + /// + /// ``` + /// use ethers_core::types::Bytes; + /// + /// let b = Bytes::from_static(b"hello"); + /// assert_eq!(&b[..], b"hello"); + /// ``` + #[inline] + pub const fn from_static(bytes: &'static [u8]) -> Self { + Self(bytes::Bytes::from_static(bytes)) + } + + fn hex_encode(&self) -> String { + hex::encode(self.0.as_ref()) + } } impl Debug for Bytes { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "Bytes(0x{})", bytes_to_hex(self)) + write!(f, "Bytes(0x{})", self.hex_encode()) } } impl Display for Bytes { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "0x{}", bytes_to_hex(self)) + write!(f, "0x{}", self.hex_encode()) } } impl LowerHex for Bytes { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "0x{}", bytes_to_hex(self)) - } -} - -impl Bytes { - pub fn to_vec(&self) -> Vec { - self.as_ref().to_vec() + write!(f, "0x{}", self.hex_encode()) } }