#![macro_use]
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(not(feature = "std"))]
extern crate alloc;
extern crate self as evm_coder;
pub use evm_coder_procedural::{event_topic, fn_selector};
pub mod abi;
pub use events::{ToLog, ToTopic};
#[macro_use]
pub mod custom_signature;
#[doc(hidden)]
pub use ethereum;
pub use evm_coder_procedural::solidity_interface;
pub use evm_coder_procedural::AbiCoder;
#[cfg(feature = "bondrewd")]
pub use evm_coder_procedural::AbiCoderFlags;
pub use evm_coder_procedural::ToLog;
#[doc(hidden)]
pub use sha3_const;
pub use self::abi::{AbiDecode, AbiDecoder, AbiEncode, AbiEncoder};
use self::{abi::Error, types::*};
#[doc(hidden)]
pub mod events;
#[doc(hidden)]
#[cfg(feature = "stubgen")]
pub mod solidity;
pub mod types {
#![allow(non_camel_case_types, missing_docs)]
#[cfg(not(feature = "std"))]
pub use alloc::{vec, vec::Vec};
use core::marker::PhantomData;
#[cfg(feature = "std")]
pub use std::vec::Vec;
use primitive_types::{H160, H256, U256};
use crate::abi::AbiDecodeZero;
pub type Address = H160;
pub type Topic = H256;
#[cfg(not(feature = "std"))]
pub type String = ::alloc::string::String;
#[cfg(feature = "std")]
pub type String = ::std::string::String;
#[derive(Default, Debug, PartialEq, Eq, Clone)]
pub struct Bytes(pub Vec<u8>);
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct BytesFixed<const S: usize>(pub [u8; S]);
pub type Bytes4 = BytesFixed<4>;
#[derive(Debug, PartialEq, Clone)]
pub struct Zero<T>(PhantomData<T>);
impl<T> Zero<T> {
pub fn new() -> Self {
Self(PhantomData)
}
}
impl<T> Default for Zero<T> {
fn default() -> Self {
Self::new()
}
}
pub enum MaybeZero<T: AbiDecodeZero> {
Zero(Zero<T>),
NonZero(T),
}
pub type Value = U256;
pub type Caller = Address;
pub struct Msg<C> {
pub call: C,
pub caller: H160,
pub value: U256,
}
impl From<Vec<u8>> for Bytes {
fn from(src: Vec<u8>) -> Self {
Self(src)
}
}
#[allow(clippy::from_over_into)]
impl Into<Vec<u8>> for Bytes {
fn into(self) -> Vec<u8> {
self.0
}
}
impl Bytes {
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<const S: usize> Default for BytesFixed<S> {
fn default() -> Self {
Self([0; S])
}
}
#[allow(clippy::from_over_into)]
impl<const S: usize> Into<Vec<u8>> for BytesFixed<S> {
fn into(self) -> Vec<u8> {
self.0.into()
}
}
}
pub trait Call: Sized {
fn parse(selector: Bytes4, input: &[u8]) -> abi::Result<Option<Self>>;
fn parse_full(input: &[u8]) -> abi::Result<Option<Self>> {
if input.len() < 4 {
return Err(Error::OutOfOffset);
}
let mut selector = [0; 4];
selector.copy_from_slice(&input[..4]);
Self::parse(BytesFixed(selector), &input[4..])
}
}
pub trait Callable<C: Call>: Contract {
fn call(&mut self, call: types::Msg<C>) -> ResultWithPostInfoOf<Self, Vec<u8>>;
}
pub type ResultOf<C, R> = <C as Contract>::Result<R, <C as Contract>::Error>;
pub type ResultWithPostInfoOf<C, R> = <C as Contract>::Result<
<C as Contract>::WithPostInfo<R>,
<C as Contract>::WithPostInfo<<C as Contract>::Error>,
>;
pub trait Contract {
type Error: From<&'static str>;
type WithPostInfo<T>;
type Result<T, E>;
fn map_post<I, O>(
v: Self::WithPostInfo<I>,
mapper: impl FnOnce(I) -> O,
) -> Self::WithPostInfo<O>;
fn with_default_post<T>(v: T) -> Self::WithPostInfo<T>;
}
pub struct DummyPost<T>(pub T);
#[macro_export]
macro_rules! dummy_contract {
(
macro_rules! $res:ident {...}
impl$(<$($gen:ident),+ $(,)?>)? Contract for $ty:ty {...}
) => {
macro_rules! $res {
($i:expr) => {{
use ::evm_coder::DummyPost;
struct Wrapper<T>(core::cell::Cell<Option<T>>);
type O<T> = ::core::result::Result<DummyPost<T>, DummyPost<String>>;
trait Matcher<T> {
fn convert(&self) -> O<T>;
}
impl<T> Matcher<T> for &Wrapper<::core::result::Result<T, String>> {
fn convert(&self) -> O<T> {
let i = self.0.take().unwrap();
i.map(DummyPost).map_err(DummyPost)
}
}
impl<T> Matcher<T> for Wrapper<T> {
fn convert(&self) -> O<T> {
let i = self.0.take().unwrap();
Ok(DummyPost(i))
}
}
(&&Wrapper(core::cell::Cell::new(Some($i)))).convert()
}};
}
impl $(<$($gen),+>)? $crate::Contract for $ty {
type Error = String;
type WithPostInfo<RR> = $crate::DummyPost<RR>;
type Result<RR, EE> = core::result::Result<RR, EE>;
fn map_post<II, OO>(v: Self::WithPostInfo<II>, mapper: impl FnOnce(II) -> OO) -> Self::WithPostInfo<OO> {
$crate::DummyPost(mapper(v.0))
}
fn with_default_post<TT>(v: TT) -> Self::WithPostInfo<TT> {
$crate::DummyPost(v)
}
}
};
}
#[derive(Debug, PartialEq)]
pub enum ERC165Call {
SupportsInterface {
interface_id: Bytes4,
},
}
impl ERC165Call {
pub const INTERFACE_ID: Bytes4 = BytesFixed(u32::to_be_bytes(0x01ff_c9a7));
}
impl Call for ERC165Call {
fn parse(selector: Bytes4, input: &[u8]) -> abi::Result<Option<Self>> {
if selector != Self::INTERFACE_ID {
return Ok(None);
}
Ok(Some(Self::SupportsInterface {
interface_id: Bytes4::abi_decode(input)?,
}))
}
}
#[macro_export]
macro_rules! generate_stubgen {
($name:ident, $decl:ty, $is_impl:literal) => {
#[cfg(feature = "stubgen")]
#[test]
#[ignore]
fn $name() {
use evm_coder::solidity::TypeCollector;
let mut out = TypeCollector::new();
<$decl>::generate_solidity_interface(&mut out, $is_impl);
println!("=== SNIP START ===");
println!("// SPDX-License-Identifier: OTHER");
println!("// This code is automatically generated");
println!();
println!("pragma solidity >=0.8.0 <0.9.0;");
println!();
for b in out.finish() {
println!("{}", b);
}
println!("=== SNIP END ===");
}
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn function_selector_generation() {
assert_eq!(
fn_selector!(transfer(address, uint256)),
BytesFixed(u32::to_be_bytes(0xa9059cbb))
);
}
#[test]
fn event_topic_generation() {
assert_eq!(
hex::encode(&event_topic!(Transfer(address, address, uint256))[..]),
"ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
);
}
}