diff --git a/examples/revert.rs b/examples/revert.rs index 620e1197f..ff1a574d7 100644 --- a/examples/revert.rs +++ b/examples/revert.rs @@ -1,33 +1,77 @@ -//! Example of revert #![cfg_attr(target_arch = "wasm32", no_std)] #![cfg_attr(target_arch = "wasm32", no_main)] +use zink::{ + primitives::{Address, U256}, + Error, +}; + extern crate zink; +// Define custom errors +#[derive(Error)] +pub enum ContractError { + Unauthorized, + InsufficientBalance(U256, U256), + InvalidAddress(Address), +} + #[cfg(not(target_arch = "wasm32"))] fn main() {} -/// check if the passing address is owner #[zink::external] -pub fn revert() { - zink::revert!("revert works") +pub fn simple_revert() { + zink::revert!("simple revert message"); +} + +#[zink::external] +pub fn assert_example() { + zink::assert!(false, "assertion failed"); +} + +#[zink::external] +pub fn custom_error() { + ContractError::Unauthorized; +} + +#[zink::external] +pub fn insufficient_balance(amount: U256) { + let balance = U256::empty(); + ContractError::InsufficientBalance(balance, amount); } -/// check if the passing address is owner #[zink::external] -pub fn assert() { - zink::assert!(false, "assert works"); +pub fn check_address(addr: Address) { + ContractError::InvalidAddress(addr); } -#[test] -fn test_revert() -> anyhow::Result<()> { +#[cfg(test)] +mod tests { + use super::*; use zint::Contract; - let mut contract = Contract::search("revert")?.compile()?; - let info = contract.execute(["revert()".as_bytes()])?; - assert_eq!(info.revert, Some("revert works".into())); + // #[test] + // fn test_errors() -> anyhow::Result<()> { + // let mut contract = Contract::search("errors")?.compile()?; + + // // Test string revert + // let info = contract.execute(["simple_revert()".as_bytes()])?; + // assert_eq!(info.revert, Some("simple revert message".into())); + + // // Test assertion + // let info = contract.execute(["assert_example()".as_bytes()])?; + // assert_eq!(info.revert, Some("assertion failed".into())); + + // // Test custom error + // let info = contract.execute(["custom_error()".as_bytes()])?; + // assert!(info.revert.is_some()); + // // In practice, you'd decode the custom error from the revert data + + // // Test custom error with data + // let info = contract.execute(["insufficient_balance(uint256)".as_bytes(), &[100u8; 32]])?; + // assert!(info.revert.is_some()); + // // You'd decode the balance values from the revert data - let info = contract.execute(["assert()".as_bytes()])?; - assert_eq!(info.revert, Some("assert works".into())); - Ok(()) + // Ok(()) + // } } diff --git a/zink/codegen/src/lib.rs b/zink/codegen/src/lib.rs index 776ddaa54..36c462356 100644 --- a/zink/codegen/src/lib.rs +++ b/zink/codegen/src/lib.rs @@ -17,9 +17,14 @@ mod utils; /// Revert with the input message /// /// Only raw string is supported, formatter currently doesn't work. +#[proc_macro_derive(Error)] +pub fn error(input: TokenStream) -> TokenStream { + revert::derive_error(input) +} + #[proc_macro] pub fn revert(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as LitStr); + let input = parse_macro_input!(input); revert::parse(input) } @@ -33,6 +38,8 @@ pub fn assert(input: TokenStream) -> TokenStream { revert::parse_assert(input) } + + /// Event logging interface /// /// ```ignore diff --git a/zink/codegen/src/revert.rs b/zink/codegen/src/revert.rs index e8fc1305c..33cfa1c01 100644 --- a/zink/codegen/src/revert.rs +++ b/zink/codegen/src/revert.rs @@ -4,9 +4,69 @@ use proc_macro::TokenStream; use proc_macro2::{Literal, Span}; use quote::{quote, ToTokens}; use syn::{ - parse::{Parse, ParseStream}, - parse2, Expr, Ident, LitStr, Token, + parse::{Parse, ParseStream}, parse2, parse_macro_input, Data, DeriveInput, Expr, Fields, Ident, LitStr, Token, Type }; +use super::selector; + +pub fn derive_error(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let name = input.ident; + + let error_variants = match input.data { + Data::Enum(enum_data) => enum_data.variants, + _ => panic!("Error derive macro only works on enums"), + }; + + let mut error_matches = vec![]; + let mut error_names = vec![]; + let mut error_signatures = vec![]; + + for variant in error_variants { + let variant_name = variant.ident; + let signature = match variant.fields { + Fields::Unnamed(fields) => { + let types: Vec = fields.unnamed.iter() + .map(|f| f.ty.clone()) + .collect(); + quote! { (#(#types),*) } + }, + Fields::Unit => quote! {}, + Fields::Named(_) => panic!("Named fields are not supported in error variants"), + }; + + let selector = generate_error_selector(&variant_name.to_string()); + error_matches.push(quote! { + #name::#variant_name #signature => #selector + }); + error_names.push(variant_name.to_string()); + error_signatures.push(signature); + } + + quote! { + impl #name { + pub fn selector(&self) -> [u8; 4] { + match self { + #(#error_matches,)* + } + } + + pub fn get_abi() -> Vec { + vec![ + // #(zink::abi::Error { + // name: #error_names.into(), + // inputs: #error_signatures, + // },)* + ] + } + } + }.into() +} + +fn generate_error_selector(name: &str) -> proc_macro2::TokenStream { + quote! { + + } +} /// Revert with message pub fn parse(input: LitStr) -> TokenStream { diff --git a/zink/src/lib.rs b/zink/src/lib.rs index 870538e3a..d935d8e3b 100644 --- a/zink/src/lib.rs +++ b/zink/src/lib.rs @@ -12,7 +12,7 @@ pub mod primitives; pub mod storage; pub use self::{asm::Asm, event::Event}; pub use storage::{DoubleKeyMapping, Mapping, Storage, TransientStorage}; -pub use zink_codegen::{assert, external, revert, storage, transient_storage, Event}; +pub use zink_codegen::{assert, external, storage, transient_storage, Event, revert, Error}; /// Generate a keccak hash of the input (sha3) #[cfg(not(target_family = "wasm"))]