Skip to content

Commit

Permalink
Merge pull request #108 from OffchainLabs/0.4.3
Browse files Browse the repository at this point in the history
SDK 0.4.3
  • Loading branch information
rachel-bousfield authored Feb 23, 2024
2 parents 69fa161 + 5bb4a8d commit bf26cb5
Show file tree
Hide file tree
Showing 19 changed files with 300 additions and 52 deletions.
4 changes: 2 additions & 2 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ Please provide a summary of the changes and any backward incompatibilities.
- [ ] I have read the [DCO][DCO] and ensured that these changes comply.
- [ ] I assign this work under its [open source licensing][terms].

[DCO]: licenses/DCO.txt
[terms]: licenses/COPYRIGHT.md
[DCO]: https://github.com/OffchainLabs/stylus-sdk-rs/blob/stylus/licenses/DCO.txt
[terms]: https://github.com/OffchainLabs/stylus-sdk-rs/blob/stylus/licenses/COPYRIGHT.md
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = ["stylus-sdk", "stylus-proc", "mini-alloc"]
resolver = "2"

[workspace.package]
version = "0.4.2"
version = "0.4.3"
edition = "2021"
authors = ["Offchain Labs"]
license = "MIT OR Apache-2.0"
Expand Down
2 changes: 1 addition & 1 deletion ci/smoke_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ cargo stylus new counter
cd counter
echo "[workspace]" >> Cargo.toml

cargo stylus deploy --private-key $PRIV_KEY
cargo stylus deploy --private-key $PRIV_KEY -e https://stylus-testnet.arbitrum.io/rpc
61 changes: 54 additions & 7 deletions examples/erc20/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 2 additions & 11 deletions examples/erc20/src/erc20.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use alloc::{string::String, vec::Vec};
use alloc::string::String;
use core::marker::PhantomData;
use stylus_sdk::{
alloy_primitives::{Address, U256},
Expand Down Expand Up @@ -36,21 +36,12 @@ sol! {
error InsufficientAllowance(address owner, address spender, uint256 have, uint256 want);
}

#[derive(SolidityError)]
pub enum Erc20Error {
InsufficientBalance(InsufficientBalance),
InsufficientAllowance(InsufficientAllowance),
}

// We will soon provide a #[derive(SolidityError)] to clean this up
impl From<Erc20Error> for Vec<u8> {
fn from(err: Erc20Error) -> Vec<u8> {
match err {
Erc20Error::InsufficientBalance(e) => e.encode(),
Erc20Error::InsufficientAllowance(e) => e.encode(),
}
}
}

// These methods aren't exposed to other contracts
// Note: modifying storage will become much prettier soon
impl<T: Erc20Params> Erc20<T> {
Expand Down
29 changes: 29 additions & 0 deletions stylus-proc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,35 @@ pub fn derive_erase(input: TokenStream) -> TokenStream {
storage::derive_erase(input)
}

/// Allows an error `enum` to be used in method signatures.
///
/// ```ignore
/// sol! {
/// error InsufficientBalance(address from, uint256 have, uint256 want);
/// error InsufficientAllowance(address owner, address spender, uint256 have, uint256 want);
/// }
///
/// #[derive(SolidityError)]
/// pub enum Erc20Error {
/// InsufficientBalance(InsufficientBalance),
/// InsufficientAllowance(InsufficientAllowance),
/// }
///
/// #[external]
/// impl Contract {
/// pub fn fallible_method() -> Result<(), Erc20Error> {
/// // code that might revert
/// }
/// }
/// ```
///
/// Under the hood, the above macro works by implementing `From<Erc20Error>` for `Vec<u8>`
/// along with printing code for abi-export.
#[proc_macro_derive(SolidityError)]
pub fn derive_solidity_error(input: TokenStream) -> TokenStream {
methods::error::derive_solidity_error(input)
}

/// Defines the entrypoint, which is where Stylus execution begins.
/// Without it the contract will fail to pass [`cargo stylus check`][check].
/// Most commonly this macro is used to annotate the top level storage `struct`.
Expand Down
2 changes: 1 addition & 1 deletion stylus-proc/src/methods/entrypoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ pub fn entrypoint(attr: TokenStream, input: TokenStream) -> TokenStream {
output.extend(quote! {
#[no_mangle]
pub unsafe fn mark_used() {
stylus_sdk::evm::memory_grow(0);
stylus_sdk::evm::pay_for_memory_grow(0);
panic!();
}

Expand Down
57 changes: 57 additions & 0 deletions stylus-proc/src/methods/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright 2024, Offchain Labs, Inc.
// For licensing, see https://github.com/OffchainLabs/stylus-sdk-rs/blob/stylus/licenses/COPYRIGHT.md

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Fields, ItemEnum};

pub fn derive_solidity_error(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as ItemEnum);
let name = &input.ident;
let mut match_arms = quote!();
let mut errors = vec![];
for variant in input.variants {
let variant_name = variant.ident;
let error = match variant.fields {
Fields::Unnamed(e) if variant.fields.len() == 1 => e.unnamed.first().unwrap().clone(),
_ => error!(variant.fields, "Variant not a 1-tuple"),
};
match_arms.extend(quote! {
#name::#variant_name(e) => stylus_sdk::alloy_sol_types::SolError::encode(&e),
});
errors.push(error);
}
let mut output = quote! {
impl From<#name> for alloc::vec::Vec<u8> {
fn from(err: #name) -> alloc::vec::Vec<u8> {
match err {
#match_arms
}
}
}
};

if cfg!(feature = "export-abi") {
output.extend(quote! {
impl stylus_sdk::abi::export::internal::InnerTypes for #name {
fn inner_types() -> alloc::vec::Vec<stylus_sdk::abi::export::internal::InnerType> {
use alloc::{format, vec};
use core::any::TypeId;
use stylus_sdk::abi::export::internal::InnerType;
use stylus_sdk::alloy_sol_types::SolError;

vec![
#(
InnerType {
name: format!("error {};", <#errors as SolError>::SIGNATURE.replace(',', ", ")),
id: TypeId::of::<#errors>(),
}
),*
]
}
}
});
}

output.into()
}
32 changes: 25 additions & 7 deletions stylus-proc/src/methods/external.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2022-2023, Offchain Labs, Inc.
// Copyright 2022-2024, Offchain Labs, Inc.
// For licensing, see https://github.com/OffchainLabs/stylus-sdk-rs/blob/stylus/licenses/COPYRIGHT.md

use crate::types::{self, Purity};
Expand All @@ -20,6 +20,7 @@ pub fn external(_attr: TokenStream, input: TokenStream) -> TokenStream {
let mut selectors = quote!();
let mut match_selectors = quote!();
let mut abi = quote!();
let mut types = vec![];

for item in input.items.iter_mut() {
let ImplItem::Method(method) = item else {
Expand Down Expand Up @@ -155,10 +156,7 @@ pub fn external(_attr: TokenStream, input: TokenStream) -> TokenStream {
}
};
let result = Self::#name(#storage #(#expand_args, )* );
match result {
Ok(result) => Some(Ok(internal::encode_return_type(result))),
Err(err) => Some(Err(err.into())),
}
Some(EncodableReturnType::encode(result))
}
});

Expand Down Expand Up @@ -258,7 +256,7 @@ pub fn external(_attr: TokenStream, input: TokenStream) -> TokenStream {
#[inline(always)]
fn route(storage: &mut S, selector: u32, input: &[u8]) -> Option<stylus_sdk::ArbResult> {
use stylus_sdk::{function_selector, alloy_sol_types::SolType};
use stylus_sdk::abi::{internal, AbiType, Router};
use stylus_sdk::abi::{internal, internal::EncodableReturnType, AbiType, Router};
use alloc::vec;

#[cfg(feature = "export-abi")]
Expand All @@ -281,6 +279,24 @@ pub fn external(_attr: TokenStream, input: TokenStream) -> TokenStream {
return router.into();
}

for item in input.items.iter_mut() {
let ImplItem::Method(method) = item else {
continue;
};
if let ReturnType::Type(_, ty) = &method.sig.output {
types.push(ty);
}
}

let type_decls = quote! {
let mut seen = HashSet::new();
for item in [].iter() #(.chain(&<#types as InnerTypes>::inner_types()))* {
if seen.insert(item.id) {
writeln!(f, "\n {}", item.name)?;
}
}
};

let name = match *self_ty.clone() {
Type::Path(path) => path.path.segments.last().unwrap().ident.clone().to_string(),
_ => error!(self_ty, "Can't generate ABI for unnamed type"),
Expand Down Expand Up @@ -312,12 +328,14 @@ pub fn external(_attr: TokenStream, input: TokenStream) -> TokenStream {
fn fmt_abi(f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
use stylus_sdk::abi::{AbiType, GenerateAbi};
use stylus_sdk::abi::internal::write_solidity_returns;
use stylus_sdk::abi::export::{underscore_if_sol};
use stylus_sdk::abi::export::{underscore_if_sol, internal::InnerTypes};
use std::collections::HashSet;
#(#inherited_abis)*
write!(f, "interface I{}", #name)?;
#is_clause
write!(f, " {{")?;
#abi
#type_decls
writeln!(f, "}}")?;
Ok(())
}
Expand Down
1 change: 1 addition & 0 deletions stylus-proc/src/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
// For licensing, see https://github.com/OffchainLabs/stylus-sdk-rs/blob/stylus/licenses/COPYRIGHT.md

pub mod entrypoint;
pub mod error;
pub mod external;
Loading

0 comments on commit bf26cb5

Please sign in to comment.