diff --git a/.github/codecov.yml b/.github/codecov.yml index e4802e57a..9359748ed 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -14,10 +14,15 @@ coverage: patch: default: informational: true -# Test files aren't important for coverage. +# Docs and examples are not relevant to coverage. +# Motsu and e2e should be moved outside of this repo. ignore: - - "tests" - "docs" + - "examples" + - "lib/e2e" + - "lib/e2e-proc" + - "lib/motsu" + - "lib/motsu-proc" # Make comments less noisy. comment: layout: "files" diff --git a/.github/workflows/nostd.yml b/.github/workflows/nostd.yml index 409b76f5c..67de59ab3 100644 --- a/.github/workflows/nostd.yml +++ b/.github/workflows/nostd.yml @@ -6,7 +6,7 @@ permissions: contents: read on: push: - branches: [ main, release/* ] + branches: [ main, release/* ] pull_request: concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ae3c2fdfa..f22146905 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ permissions: contents: read on: push: - branches: [main, release/*] + branches: [ main, release/* ] paths-ignore: - "**.md" - "**.adoc" @@ -32,7 +32,7 @@ jobs: matrix: # Run on stable and beta to ensure that tests won't break on the next # version of the rust toolchain. - toolchain: [stable, beta] + toolchain: [ stable, beta ] steps: - uses: actions/checkout@v4 with: @@ -69,7 +69,7 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-latest] + os: [ macos-latest ] # Windows fails because of `stylus-proc`. # os: [macos-latest, windows-latest] steps: @@ -117,16 +117,19 @@ jobs: # # for lots of more discussion. runs-on: ubuntu-latest - name: ubuntu / stable / coverage + name: ubuntu / nightly / coverage steps: - uses: actions/checkout@v4 with: submodules: true - name: Install rust + # We run in nightly to make use of the `#[coverage(off)]` attribute (see _contracts/src/lib.rs_) + # We'll return to stable release after the tracking issue is merged (and the feature is stable) + # See: https://github.com/rust-lang/rust/issues/84605 uses: actions-rust-lang/setup-rust-toolchain@v1 with: - toolchain: stable + toolchain: nightly components: llvm-tools-preview rustflags: "" diff --git a/Cargo.toml b/Cargo.toml index 413dba717..66270beb7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,6 +67,7 @@ version = "0.2.0-alpha.1" missing_docs = "warn" unreachable_pub = "warn" rust_2021_compatibility = { level = "warn", priority = -1 } +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] } [workspace.lints.clippy] pedantic = "warn" diff --git a/README.md b/README.md index 4eeb8d7bd..dbdc27521 100644 --- a/README.md +++ b/README.md @@ -64,14 +64,14 @@ Once defined as a dependency, use one of our pre-defined implementations by importing them: ```rust +use stylus_sdk::prelude::*; use openzeppelin_stylus::token::erc20::Erc20; -sol_storage! { - #[entrypoint] - struct Erc20Example { - #[borrow] - Erc20 erc20; - } +#[entrypoint] +#[storage] +struct Erc20Example { + #[borrow] + pub erc20: Erc20, } #[public] diff --git a/contracts/src/access/control.rs b/contracts/src/access/control.rs index 1a73cf023..216b794f5 100644 --- a/contracts/src/access/control.rs +++ b/contracts/src/access/control.rs @@ -41,52 +41,59 @@ //! accounts that have been granted it. We recommend using //! `AccessControlDefaultAdminRules` to enforce additional security measures for //! this role. -use alloy_primitives::{Address, B256}; -use alloy_sol_types::sol; +use alloy_primitives::{Address, FixedBytes, B256}; +pub use sol::*; use stylus_sdk::{ evm, msg, - stylus_proc::{public, sol_storage, SolidityError}, + prelude::storage, + storage::{StorageBool, StorageFixedBytes, StorageMap}, + stylus_proc::{public, SolidityError}, }; -sol! { - /// Emitted when `new_admin_role` is set as `role`'s admin role, replacing - /// `previous_admin_role`. - /// - /// `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - /// `RoleAdminChanged` not being emitted signaling this. - #[allow(missing_docs)] - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previous_admin_role, bytes32 indexed new_admin_role); - /// Emitted when `account` is granted `role`. - /// - /// `sender` is the account that originated the contract call. This account - /// bears the admin role (for the granted role). - /// Expected in cases where the role was granted using the internal - /// [`AccessControl::grant_role`]. - #[allow(missing_docs)] - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - /// Emitted when `account` is revoked `role`. - /// - /// `sender` is the account that originated the contract call: - /// - if using `revoke_role`, it is the admin role bearer. - /// - if using `renounce_role`, it is the role bearer (i.e. `account`). - #[allow(missing_docs)] - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); -} - -sol! { - /// The `account` is missing a role. - /// - /// * `account` - Account that was found to not be authorized. - /// * `needed_role` - The missing role. - #[derive(Debug)] - #[allow(missing_docs)] - error AccessControlUnauthorizedAccount(address account, bytes32 needed_role); - /// The caller of a function is not the expected one. - /// - /// NOTE: Don't confuse with [`AccessControlUnauthorizedAccount`]. - #[derive(Debug)] - #[allow(missing_docs)] - error AccessControlBadConfirmation(); +#[cfg_attr(coverage_nightly, coverage(off))] +mod sol { + use alloy_sol_macro::sol; + + sol! { + /// Emitted when `new_admin_role` is set as `role`'s admin role, replacing + /// `previous_admin_role`. + /// + /// `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite + /// `RoleAdminChanged` not being emitted signaling this. + #[allow(missing_docs)] + event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previous_admin_role, bytes32 indexed new_admin_role); + /// Emitted when `account` is granted `role`. + /// + /// `sender` is the account that originated the contract call. This account + /// bears the admin role (for the granted role). + /// Expected in cases where the role was granted using the internal + /// [`AccessControl::grant_role`]. + #[allow(missing_docs)] + event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); + /// Emitted when `account` is revoked `role`. + /// + /// `sender` is the account that originated the contract call: + /// - if using `revoke_role`, it is the admin role bearer. + /// - if using `renounce_role`, it is the role bearer (i.e. `account`). + #[allow(missing_docs)] + event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); + } + + sol! { + /// The `account` is missing a role. + /// + /// * `account` - Account that was found to not be authorized. + /// * `needed_role` - The missing role. + #[derive(Debug)] + #[allow(missing_docs)] + error AccessControlUnauthorizedAccount(address account, bytes32 needed_role); + /// The caller of a function is not the expected one. + /// + /// NOTE: Don't confuse with [`AccessControlUnauthorizedAccount`]. + #[derive(Debug)] + #[allow(missing_docs)] + error AccessControlBadConfirmation(); + } } /// An error that occurred in the implementation of an [`AccessControl`] @@ -99,20 +106,20 @@ pub enum Error { BadConfirmation(AccessControlBadConfirmation), } -sol_storage! { - /// Information about a specific role. - pub struct RoleData { - /// Whether an account is member of a certain role. - mapping(address => bool) has_role; - /// The admin role for this role. - bytes32 admin_role; - } +/// Information about a specific role. +#[storage] +pub struct RoleData { + /// Whether an account is member of a certain role. + pub has_role: StorageMap, + /// The admin role for this role. + pub admin_role: StorageFixedBytes<32>, +} - /// State of an `AccessControl` contract. - pub struct AccessControl { - /// Role identifier -> Role information. - mapping(bytes32 => RoleData) _roles; - } +/// State of an `AccessControl` contract. +#[storage] +pub struct AccessControl { + /// Role identifier -> Role information. + pub _roles: StorageMap, RoleData>, } #[public] diff --git a/contracts/src/access/ownable.rs b/contracts/src/access/ownable.rs index 50692973f..7caf659d7 100644 --- a/contracts/src/access/ownable.rs +++ b/contracts/src/access/ownable.rs @@ -9,36 +9,43 @@ //! [`Ownable::only_owner`] function, which can be called to restrict operations //! to the owner. use alloy_primitives::Address; -use alloy_sol_types::sol; use openzeppelin_stylus_proc::interface_id; +pub use sol::*; use stylus_sdk::{ call::MethodError, evm, msg, - stylus_proc::{public, sol_storage, SolidityError}, + prelude::storage, + storage::StorageAddress, + stylus_proc::{public, SolidityError}, }; -sol! { - /// Emitted when ownership gets transferred between accounts. - /// - /// * `previous_owner` - Address of the previous owner. - /// * `new_owner` - Address of the new owner. - #[allow(missing_docs)] - event OwnershipTransferred(address indexed previous_owner, address indexed new_owner); -} +#[cfg_attr(coverage_nightly, coverage(off))] +mod sol { + use alloy_sol_macro::sol; + + sol! { + /// Emitted when ownership gets transferred between accounts. + /// + /// * `previous_owner` - Address of the previous owner. + /// * `new_owner` - Address of the new owner. + #[allow(missing_docs)] + event OwnershipTransferred(address indexed previous_owner, address indexed new_owner); + } -sol! { - /// The caller account is not authorized to perform an operation. - /// - /// * `account` - Account that was found to not be authorized. - #[derive(Debug)] - #[allow(missing_docs)] - error OwnableUnauthorizedAccount(address account); - /// The owner is not a valid owner account. (eg. `Address::ZERO`) - /// - /// * `owner` - Account that's not allowed to become the owner. - #[derive(Debug)] - #[allow(missing_docs)] - error OwnableInvalidOwner(address owner); + sol! { + /// The caller account is not authorized to perform an operation. + /// + /// * `account` - Account that was found to not be authorized. + #[derive(Debug)] + #[allow(missing_docs)] + error OwnableUnauthorizedAccount(address account); + /// The owner is not a valid owner account. (eg. `Address::ZERO`) + /// + /// * `owner` - Account that's not allowed to become the owner. + #[derive(Debug)] + #[allow(missing_docs)] + error OwnableInvalidOwner(address owner); + } } /// An error that occurred in the implementation of an [`Ownable`] contract. @@ -56,12 +63,11 @@ impl MethodError for Error { } } -sol_storage! { - /// State of an `Ownable` contract. - pub struct Ownable { - /// The current owner of this contract. - address _owner; - } +/// State of an `Ownable` contract. +#[storage] +pub struct Ownable { + /// The current owner of this contract. + pub _owner: StorageAddress, } /// Interface for an [`Ownable`] contract. diff --git a/contracts/src/access/ownable_two_step.rs b/contracts/src/access/ownable_two_step.rs index f5d9a2be1..c3f8543ea 100644 --- a/contracts/src/access/ownable_two_step.rs +++ b/contracts/src/access/ownable_two_step.rs @@ -17,27 +17,34 @@ //! available. use alloy_primitives::Address; -use alloy_sol_types::sol; +pub use sol::*; use stylus_sdk::{ evm, msg, - stylus_proc::{public, sol_storage, SolidityError}, + prelude::storage, + storage::StorageAddress, + stylus_proc::{public, SolidityError}, }; use crate::access::ownable::{ Error as OwnableError, IOwnable, Ownable, OwnableUnauthorizedAccount, }; -sol! { - /// Emitted when ownership transfer starts. - /// - /// * `previous_owner` - Address of the previous owner. - /// * `new_owner` - Address of the new owner, to which the ownership - /// will be transferred. - event OwnershipTransferStarted( - address indexed previous_owner, - address indexed new_owner - ); +#[cfg_attr(coverage_nightly, coverage(off))] +mod sol { + use alloy_sol_macro::sol; + + sol! { + /// Emitted when ownership transfer starts. + /// + /// * `previous_owner` - Address of the previous owner. + /// * `new_owner` - Address of the new owner, to which the ownership + /// will be transferred. + event OwnershipTransferStarted( + address indexed previous_owner, + address indexed new_owner + ); + } } /// An error that occurred in the implementation of an [`Ownable2Step`] @@ -48,14 +55,13 @@ pub enum Error { Ownable(OwnableError), } -sol_storage! { - /// State of an `Ownable2Step` contract. - pub struct Ownable2Step { - /// [`Ownable`] contract. - Ownable _ownable; - /// Pending owner of the contract. - address _pending_owner; - } +/// State of an `Ownable2Step` contract. +#[storage] +pub struct Ownable2Step { + /// [`Ownable`] contract. + pub _ownable: Ownable, + /// Pending owner of the contract. + pub _pending_owner: StorageAddress, } /// Interface for an [`Ownable2Step`] contract. diff --git a/contracts/src/finance/vesting_wallet.rs b/contracts/src/finance/vesting_wallet.rs index 3870fba2d..e065c7b76 100644 --- a/contracts/src/finance/vesting_wallet.rs +++ b/contracts/src/finance/vesting_wallet.rs @@ -26,14 +26,15 @@ //! intended. use alloy_primitives::{Address, U256, U64}; -use alloy_sol_types::sol; use openzeppelin_stylus_proc::interface_id; +pub use sol::*; use stylus_sdk::{ block, call::{self, call, Call}, contract, evm, function_selector, - storage::TopLevelStorage, - stylus_proc::{public, sol_storage, SolidityError}, + prelude::storage, + storage::{StorageMap, StorageU256, StorageU64, TopLevelStorage}, + stylus_proc::{public, SolidityError}, }; use crate::{ @@ -41,33 +42,38 @@ use crate::{ token::erc20::utils::safe_erc20::{self, ISafeErc20, SafeErc20}, }; -sol! { - /// Emitted when `amount` of Ether has been released. - /// - /// * `amount` - Total Ether released. - #[allow(missing_docs)] - event EtherReleased(uint256 amount); - - /// Emitted when `amount` of ERC-20 `token` has been released. - /// - /// * `token` - Address of the token being released. - /// * `amount` - Number of tokens released. - #[allow(missing_docs)] - event ERC20Released(address indexed token, uint256 amount); -} - -sol! { - /// Indicates an error related to the underlying Ether transfer. - #[derive(Debug)] - #[allow(missing_docs)] - error ReleaseEtherFailed(); +#[cfg_attr(coverage_nightly, coverage(off))] +mod sol { + use alloy_sol_macro::sol; + + sol! { + /// Emitted when `amount` of Ether has been released. + /// + /// * `amount` - Total Ether released. + #[allow(missing_docs)] + event EtherReleased(uint256 amount); + + /// Emitted when `amount` of ERC-20 `token` has been released. + /// + /// * `token` - Address of the token being released. + /// * `amount` - Number of tokens released. + #[allow(missing_docs)] + event ERC20Released(address indexed token, uint256 amount); + } - /// The token address is not valid (eg. `Address::ZERO`). - /// - /// * `token` - Address of the token being released. - #[derive(Debug)] - #[allow(missing_docs)] - error InvalidToken(address token); + sol! { + /// Indicates an error related to the underlying Ether transfer. + #[derive(Debug)] + #[allow(missing_docs)] + error ReleaseEtherFailed(); + + /// The token address is not valid (eg. `Address::ZERO`). + /// + /// * `token` - Address of the token being released. + #[derive(Debug)] + #[allow(missing_docs)] + error InvalidToken(address token); + } } /// An error that occurred in the [`VestingWallet`] contract. @@ -84,8 +90,9 @@ pub enum Error { } pub use token::IErc20; -#[allow(missing_docs)] mod token { + #![allow(missing_docs)] + #![cfg_attr(coverage_nightly, coverage(off))] stylus_sdk::stylus_proc::sol_interface! { /// Interface of the ERC-20 token. interface IErc20 { @@ -94,22 +101,21 @@ mod token { } } -sol_storage! { - /// State of the [`VestingWallet`] Contract. - pub struct VestingWallet { - /// [`Ownable`] contract. - Ownable ownable; - /// Amount of Ether already released. - uint256 _released; - /// Amount of ERC-20 tokens already released. - mapping(address => uint256) _erc20_released; - /// Start timestamp. - uint64 _start; - /// Vesting duration. - uint64 _duration; - /// [`SafeErc20`] contract. - SafeErc20 safe_erc20; - } +/// State of the [`VestingWallet`] Contract. +#[storage] +pub struct VestingWallet { + /// [`Ownable`] contract. + pub ownable: Ownable, + /// Amount of Ether already released. + pub _released: StorageU256, + /// Amount of ERC-20 tokens already released. + pub _erc20_released: StorageMap, + /// Start timestamp. + pub _start: StorageU64, + /// Vesting duration. + pub _duration: StorageU64, + /// [`SafeErc20`] contract. + pub safe_erc20: SafeErc20, } /// NOTE: Implementation of [`TopLevelStorage`] to be able use `&mut self` when diff --git a/contracts/src/lib.rs b/contracts/src/lib.rs index aa0055308..266a150f9 100644 --- a/contracts/src/lib.rs +++ b/contracts/src/lib.rs @@ -25,12 +25,11 @@ importing them: use stylus_sdk::prelude::*; use openzeppelin_stylus::token::erc20::Erc20; -sol_storage! { - #[entrypoint] - struct MyContract { - #[borrow] - Erc20 erc20; - } +#[entrypoint] +#[storage] +struct MyContract { + #[borrow] + pub erc20: Erc20, } #[public] @@ -45,6 +44,7 @@ impl MyContract { } clippy::used_underscore_items )] #![cfg_attr(not(feature = "std"), no_std, no_main)] +#![cfg_attr(coverage_nightly, feature(coverage_attribute))] #![deny(rustdoc::broken_intra_doc_links)] extern crate alloc; diff --git a/contracts/src/token/erc1155/extensions/metadata_uri.rs b/contracts/src/token/erc1155/extensions/metadata_uri.rs index 6f822a7df..a77eaa0ff 100644 --- a/contracts/src/token/erc1155/extensions/metadata_uri.rs +++ b/contracts/src/token/erc1155/extensions/metadata_uri.rs @@ -6,31 +6,37 @@ use alloc::string::String; use alloy_primitives::{FixedBytes, U256}; -use alloy_sol_macro::sol; use openzeppelin_stylus_proc::interface_id; -use stylus_sdk::stylus_proc::{public, sol_storage}; +pub use sol::*; +use stylus_sdk::{ + prelude::storage, storage::StorageString, stylus_proc::public, +}; use crate::utils::introspection::erc165::{Erc165, IErc165}; -sol! { - /// Emitted when the URI for token type `id` changes to `value`, if it is - /// a non-programmatic URI. - /// - /// If a [`URI`] event was emitted for `id`, the standard [guarantees] that - /// `value` will equal the value returned by [`IErc1155MetadataUri::uri`]. - /// - /// [guarantees]: https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions - #[allow(missing_docs)] - event URI(string value, uint256 indexed id); +#[cfg_attr(coverage_nightly, coverage(off))] +mod sol { + use alloy_sol_macro::sol; + + sol! { + /// Emitted when the URI for token type `id` changes to `value`, if it is + /// a non-programmatic URI. + /// + /// If a [`URI`] event was emitted for `id`, the standard [guarantees] that + /// `value` will equal the value returned by [`IErc1155MetadataUri::uri`]. + /// + /// [guarantees]: https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions + #[allow(missing_docs)] + event URI(string value, uint256 indexed id); + } } -sol_storage! { - /// URI Metadata of an [`crate::token::erc1155::Erc1155`] token. - pub struct Erc1155MetadataUri { - /// Used as the URI for all token types by relying on ID substitution, - /// e.g. https://token-cdn-domain/{id}.json. - string _uri; - } +/// URI Metadata of an [`crate::token::erc1155::Erc1155`] token. +#[storage] +pub struct Erc1155MetadataUri { + /// Used as the URI for all token types by relying on ID substitution, + /// e.g. https://token-cdn-domain/{id}.json. + pub _uri: StorageString, } /// Interface for the optional metadata functions from the ERC-1155 standard. diff --git a/contracts/src/token/erc1155/extensions/supply.rs b/contracts/src/token/erc1155/extensions/supply.rs index 0c5e218c3..80e9fd61f 100644 --- a/contracts/src/token/erc1155/extensions/supply.rs +++ b/contracts/src/token/erc1155/extensions/supply.rs @@ -18,7 +18,8 @@ use openzeppelin_stylus_proc::interface_id; use stylus_sdk::{ abi::Bytes, msg, - prelude::{public, sol_storage}, + prelude::{public, storage}, + storage::{StorageMap, StorageU256}, }; use crate::{ @@ -26,16 +27,15 @@ use crate::{ utils::math::storage::SubAssignUnchecked, }; -sol_storage! { - /// State of an [`Erc1155Supply`] token. - pub struct Erc1155Supply { - /// ERC-1155 contract storage. - Erc1155 erc1155; - /// Mapping from token id to total supply. - mapping(uint256 => uint256) _total_supply; - /// Total supply of all token ids. - uint256 _total_supply_all; - } +/// State of an [`Erc1155Supply`] token. +#[storage] +pub struct Erc1155Supply { + /// ERC-1155 contract storage. + pub erc1155: Erc1155, + /// Mapping from token id to total supply. + pub _total_supply: StorageMap, + /// Total supply of all token ids. + pub _total_supply_all: StorageU256, } /// Required interface of a [`Erc1155Supply`] contract. diff --git a/contracts/src/token/erc1155/extensions/uri_storage.rs b/contracts/src/token/erc1155/extensions/uri_storage.rs index fd0052ab3..442c260fa 100644 --- a/contracts/src/token/erc1155/extensions/uri_storage.rs +++ b/contracts/src/token/erc1155/extensions/uri_storage.rs @@ -4,18 +4,21 @@ use alloc::string::String; use alloy_primitives::U256; -use stylus_sdk::{evm, stylus_proc::sol_storage}; +use stylus_sdk::{ + evm, + prelude::storage, + storage::{StorageMap, StorageString}, +}; use super::metadata_uri::{IErc1155MetadataUri, URI}; -sol_storage! { - /// Uri Storage. - pub struct Erc1155UriStorage { - /// Optional base URI - string _base_uri; - /// Optional mapping for token URIs. - mapping(uint256 => string) _token_uris; - } +/// Uri Storage. +#[storage] +pub struct Erc1155UriStorage { + /// Optional base URI. + pub _base_uri: StorageString, + /// Optional mapping for token URIs. + pub _token_uris: StorageMap, } impl Erc1155UriStorage { @@ -88,7 +91,7 @@ impl Erc1155UriStorage { #[cfg(all(test, feature = "std"))] mod tests { use alloy_primitives::U256; - use stylus_sdk::stylus_proc::sol_storage; + use stylus_sdk::prelude::storage; use super::Erc1155UriStorage; use crate::token::erc1155::{extensions::Erc1155MetadataUri, Erc1155}; @@ -98,17 +101,15 @@ mod tests { U256::from(num) } - sol_storage! { - struct Erc1155Example { - Erc1155 erc1155; - Erc1155MetadataUri metadata_uri; - Erc1155UriStorage uri_storage; - } + #[storage] + struct Erc1155MetadataExample { + pub metadata_uri: Erc1155MetadataUri, + pub uri_storage: Erc1155UriStorage, } #[motsu::test] fn uri_returns_metadata_uri_when_token_uri_is_not_set( - contract: Erc1155Example, + contract: Erc1155MetadataExample, ) { let token_id = random_token_id(); let uri = "https://some.metadata/token/uri"; @@ -122,7 +123,9 @@ mod tests { } #[motsu::test] - fn uri_returns_empty_string_when_no_uri_is_set(contract: Erc1155Example) { + fn uri_returns_empty_string_when_no_uri_is_set( + contract: Erc1155MetadataExample, + ) { let token_id = random_token_id(); assert!(contract @@ -132,7 +135,9 @@ mod tests { } #[motsu::test] - fn uri_returns_token_uri_when_base_uri_is_empty(contract: Erc1155Example) { + fn uri_returns_token_uri_when_base_uri_is_empty( + contract: Erc1155MetadataExample, + ) { let token_id = random_token_id(); let token_uri = "https://some.short/token/uri"; @@ -150,7 +155,7 @@ mod tests { #[motsu::test] fn uri_returns_concatenated_base_uri_and_token_uri( - contract: Erc1155Example, + contract: Erc1155MetadataExample, ) { let token_id = random_token_id(); let base_uri = "https://some.base.uri"; @@ -171,7 +176,7 @@ mod tests { #[motsu::test] fn uri_ignores_metadata_uri_when_token_uri_is_set( - contract: Erc1155Example, + contract: Erc1155MetadataExample, ) { let token_id = random_token_id(); let uri = "https://some.metadata/token/uri"; @@ -191,7 +196,7 @@ mod tests { } #[motsu::test] - fn test_set_uri(contract: Erc1155Example) { + fn test_set_uri(contract: Erc1155MetadataExample) { let token_id = random_token_id(); let uri = "https://some.metadata/token/uri"; let token_uri = "https://some.short/token/uri".to_string(); diff --git a/contracts/src/token/erc1155/mod.rs b/contracts/src/token/erc1155/mod.rs index 521e92655..c0dd5dcfd 100644 --- a/contracts/src/token/erc1155/mod.rs +++ b/contracts/src/token/erc1155/mod.rs @@ -5,11 +5,10 @@ use alloy_primitives::{Address, FixedBytes, U256}; use openzeppelin_stylus_proc::interface_id; use stylus_sdk::{ abi::Bytes, - alloy_sol_types::sol, call::{self, Call, MethodError}, evm, function_selector, msg, - prelude::{public, sol_storage, AddressVM, SolidityError}, - storage::TopLevelStorage, + prelude::{public, storage, AddressVM, SolidityError}, + storage::{StorageBool, StorageMap, StorageU256, TopLevelStorage}, }; use crate::utils::{ @@ -40,108 +39,114 @@ const BATCH_TRANSFER_FN_SELECTOR: [u8; 4] = function_selector!( Bytes ); -sol! { - /// Emitted when `value` amount of tokens of type `id` are - /// transferred from `from` to `to` by `operator`. - #[allow(missing_docs)] - event TransferSingle( - address indexed operator, - address indexed from, - address indexed to, - uint256 id, - uint256 value - ); - - /// Equivalent to multiple [`TransferSingle`] events, where `operator` - /// `from` and `to` are the same for all transfers. - #[allow(missing_docs)] - event TransferBatch( - address indexed operator, - address indexed from, - address indexed to, - uint256[] ids, - uint256[] values - ); - - /// Emitted when `account` grants or revokes permission to `operator` - /// to transfer their tokens, according to `approved`. - #[allow(missing_docs)] - event ApprovalForAll( - address indexed account, - address indexed operator, - bool approved - ); -} - -sol! { - /// Indicates an error related to the current `balance` of a `sender`. - /// Used in transfers. - /// - /// * `sender` - Address whose tokens are being transferred. - /// * `balance` - Current balance for the interacting account. - /// * `needed` - Minimum amount required to perform a transfer. - /// * `token_id` - Identifier number of a token. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC1155InsufficientBalance( - address sender, - uint256 balance, - uint256 needed, - uint256 token_id - ); - - /// Indicates a failure with the token `sender`. - /// Used in transfers. - /// - /// * `sender` - Address whose tokens are being transferred. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC1155InvalidSender(address sender); - - /// Indicates a failure with the token `receiver`. - /// Used in transfers. - /// - /// * `receiver` - Address to which tokens are being transferred. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC1155InvalidReceiver(address receiver); +pub use sol::*; +#[cfg_attr(coverage_nightly, coverage(off))] +mod sol { + use alloy_sol_macro::sol; + + sol! { + /// Emitted when `value` amount of tokens of type `id` are + /// transferred from `from` to `to` by `operator`. + #[allow(missing_docs)] + event TransferSingle( + address indexed operator, + address indexed from, + address indexed to, + uint256 id, + uint256 value + ); - /// Indicates a failure with the `operator`’s approval. - /// Used in transfers. - /// - /// * `operator` - Address that may be allowed to operate on tokens - /// without being their owner. - /// * `owner` - Address of the current owner of a token. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC1155MissingApprovalForAll(address operator, address owner); + /// Equivalent to multiple [`TransferSingle`] events, where `operator` + /// `from` and `to` are the same for all transfers. + #[allow(missing_docs)] + event TransferBatch( + address indexed operator, + address indexed from, + address indexed to, + uint256[] ids, + uint256[] values + ); - /// Indicates a failure with the `approver` of a token to be approved. - /// Used in approvals. - /// - /// * `approver` - Address initiating an approval operation. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC1155InvalidApprover(address approver); + /// Emitted when `account` grants or revokes permission to `operator` + /// to transfer their tokens, according to `approved`. + #[allow(missing_docs)] + event ApprovalForAll( + address indexed account, + address indexed operator, + bool approved + ); + } - /// Indicates a failure with the `operator` to be approved. - /// Used in approvals. - /// - /// * `operator` - Address that may be allowed to operate on tokens - /// without being their owner. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC1155InvalidOperator(address operator); + sol! { + /// Indicates an error related to the current `balance` of a `sender`. + /// Used in transfers. + /// + /// * `sender` - Address whose tokens are being transferred. + /// * `balance` - Current balance for the interacting account. + /// * `needed` - Minimum amount required to perform a transfer. + /// * `token_id` - Identifier number of a token. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC1155InsufficientBalance( + address sender, + uint256 balance, + uint256 needed, + uint256 token_id + ); - /// Indicates an array length mismatch between token ids and values in a - /// [`IErc1155::safe_batch_transfer_from`] operation. - /// Used in batch transfers. - /// - /// * `ids_length` - Length of the array of token identifiers. - /// * `values_length` - Length of the array of token amounts. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC1155InvalidArrayLength(uint256 ids_length, uint256 values_length); + /// Indicates a failure with the token `sender`. + /// Used in transfers. + /// + /// * `sender` - Address whose tokens are being transferred. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC1155InvalidSender(address sender); + + /// Indicates a failure with the token `receiver`. + /// Used in transfers. + /// + /// * `receiver` - Address to which tokens are being transferred. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC1155InvalidReceiver(address receiver); + + /// Indicates a failure with the `operator`’s approval. + /// Used in transfers. + /// + /// * `operator` - Address that may be allowed to operate on tokens + /// without being their owner. + /// * `owner` - Address of the current owner of a token. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC1155MissingApprovalForAll(address operator, address owner); + + /// Indicates a failure with the `approver` of a token to be approved. + /// Used in approvals. + /// + /// * `approver` - Address initiating an approval operation. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC1155InvalidApprover(address approver); + + /// Indicates a failure with the `operator` to be approved. + /// Used in approvals. + /// + /// * `operator` - Address that may be allowed to operate on tokens + /// without being their owner. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC1155InvalidOperator(address operator); + + /// Indicates an array length mismatch between token ids and values in a + /// [`IErc1155::safe_batch_transfer_from`] operation. + /// Used in batch transfers. + /// + /// * `ids_length` - Length of the array of token identifiers. + /// * `values_length` - Length of the array of token amounts. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC1155InvalidArrayLength(uint256 ids_length, uint256 values_length); + } } /// An [`Erc1155`] error defined as described in [ERC-6093]. @@ -179,14 +184,14 @@ impl MethodError for Error { } } -sol_storage! { - /// State of an [`Erc1155`] token. - pub struct Erc1155 { - /// Maps users to balances. - mapping(uint256 => mapping(address => uint256)) _balances; - /// Maps owners to a mapping of operator approvals. - mapping(address => mapping(address => bool)) _operator_approvals; - } +/// State of an [`Erc1155`] token. +#[storage] +pub struct Erc1155 { + /// Maps users to balances. + pub _balances: StorageMap>, + /// Maps owners to a mapping of operator approvals. + pub _operator_approvals: + StorageMap>, } /// NOTE: Implementation of [`TopLevelStorage`] to be able use `&mut self` when diff --git a/contracts/src/token/erc1155/receiver.rs b/contracts/src/token/erc1155/receiver.rs index 49da4798f..0a78dc556 100644 --- a/contracts/src/token/erc1155/receiver.rs +++ b/contracts/src/token/erc1155/receiver.rs @@ -1,4 +1,5 @@ #![allow(missing_docs)] +#![cfg_attr(coverage_nightly, coverage(off))] //! Module with an interface required for smart contract //! in order to receive ERC-1155 token transfers. diff --git a/contracts/src/token/erc20/extensions/capped.rs b/contracts/src/token/erc20/extensions/capped.rs index a971900b5..1e38e7f40 100644 --- a/contracts/src/token/erc20/extensions/capped.rs +++ b/contracts/src/token/erc20/extensions/capped.rs @@ -6,21 +6,30 @@ //! but only once the checks are put in place. use alloy_primitives::U256; -use alloy_sol_types::sol; -use stylus_sdk::stylus_proc::{public, sol_storage, SolidityError}; +pub use sol::*; +use stylus_sdk::{ + prelude::storage, + storage::StorageU256, + stylus_proc::{public, SolidityError}, +}; -sol! { - /// Indicates an error related to the operation that failed - /// because `total_supply` exceeded the `_cap`. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC20ExceededCap(uint256 increased_supply, uint256 cap); +#[cfg_attr(coverage_nightly, coverage(off))] +mod sol { + use alloy_sol_macro::sol; - /// Indicates an error related to the operation that failed - /// because the supplied `cap` is not a valid cap value. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC20InvalidCap(uint256 cap); + sol! { + /// Indicates an error related to the operation that failed + /// because `total_supply` exceeded the `_cap`. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC20ExceededCap(uint256 increased_supply, uint256 cap); + + /// Indicates an error related to the operation that failed + /// because the supplied `cap` is not a valid cap value. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC20InvalidCap(uint256 cap); + } } /// A Capped error. @@ -34,13 +43,11 @@ pub enum Error { InvalidCap(ERC20InvalidCap), } -sol_storage! { - /// State of a Capped Contract. - #[allow(clippy::pub_underscore_fields)] - pub struct Capped { - /// A cap to the supply of tokens. - uint256 _cap; - } +/// State of a Capped Contract. +#[storage] +pub struct Capped { + /// A cap to the supply of tokens. + pub _cap: StorageU256, } #[public] diff --git a/contracts/src/token/erc20/extensions/metadata.rs b/contracts/src/token/erc20/extensions/metadata.rs index 6cc10141f..20f8f99fc 100644 --- a/contracts/src/token/erc20/extensions/metadata.rs +++ b/contracts/src/token/erc20/extensions/metadata.rs @@ -4,7 +4,7 @@ use alloc::string::String; use alloy_primitives::FixedBytes; use openzeppelin_stylus_proc::interface_id; -use stylus_sdk::stylus_proc::{public, sol_storage}; +use stylus_sdk::stylus_proc::{public, storage}; use crate::utils::introspection::erc165::IErc165; @@ -13,14 +13,13 @@ pub const DEFAULT_DECIMALS: u8 = 18; use crate::utils::Metadata; -sol_storage! { - /// Metadata of the [`super::super::Erc20`] token. - /// - /// It has hardcoded `decimals` to [`DEFAULT_DECIMALS`]. - pub struct Erc20Metadata { - /// Common Metadata. - Metadata _metadata - } +/// Metadata of the [`super::super::Erc20`] token. +/// +/// It has hardcoded `decimals` to [`DEFAULT_DECIMALS`]. +#[storage] +pub struct Erc20Metadata { + /// Common Metadata. + pub _metadata: Metadata, } /// Interface for the optional metadata functions from the ERC-20 standard. diff --git a/contracts/src/token/erc20/extensions/permit.rs b/contracts/src/token/erc20/extensions/permit.rs index 64af6492a..28d7bcf46 100644 --- a/contracts/src/token/erc20/extensions/permit.rs +++ b/contracts/src/token/erc20/extensions/permit.rs @@ -13,12 +13,12 @@ //! [ERC]: https://eips.ethereum.org/EIPS/eip-2612 use alloy_primitives::{b256, keccak256, Address, B256, U256}; -use alloy_sol_types::{sol, SolType}; +use alloy_sol_types::SolType; use stylus_sdk::{ block, - prelude::StorageType, + prelude::{storage, StorageType}, storage::TopLevelStorage, - stylus_proc::{public, sol_storage, SolidityError}, + stylus_proc::{public, SolidityError}, }; use crate::{ @@ -34,21 +34,27 @@ use crate::{ const PERMIT_TYPEHASH: B256 = b256!("6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9"); -type StructHashTuple = sol! { - tuple(bytes32, address, address, uint256, uint256, uint256) -}; +pub use sol::*; +#[cfg_attr(coverage_nightly, coverage(off))] +mod sol { + use alloy_sol_macro::sol; -sol! { - /// Indicates an error related to the fact that - /// permit deadline has expired. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC2612ExpiredSignature(uint256 deadline); + pub(crate) type StructHashTuple = sol! { + tuple(bytes32, address, address, uint256, uint256, uint256) + }; - /// Indicates an error related to the issue about mismatched signature. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC2612InvalidSigner(address signer, address owner); + sol! { + /// Indicates an error related to the fact that + /// permit deadline has expired. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC2612ExpiredSignature(uint256 deadline); + + /// Indicates an error related to the issue about mismatched signature. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC2612InvalidSigner(address signer, address owner); + } } /// A Permit error. @@ -65,18 +71,15 @@ pub enum Error { ECDSA(ecdsa::Error), } -sol_storage! { - /// State of a Permit Contract. - pub struct Erc20Permit{ - /// ERC-20 contract. - Erc20 erc20; - - /// Nonces contract. - Nonces nonces; - - /// EIP-712 contract. Must implement [`IEip712`] trait. - T eip712; - } +/// State of a Permit Contract. +#[storage] +pub struct Erc20Permit { + /// ERC-20 contract. + pub erc20: Erc20, + /// Nonces contract. + pub nonces: Nonces, + /// EIP-712 contract. Must implement [`IEip712`] trait. + pub eip712: T, } /// NOTE: Implementation of [`TopLevelStorage`] to be able use `&mut self` when diff --git a/contracts/src/token/erc20/mod.rs b/contracts/src/token/erc20/mod.rs index 7d4c46ecf..dca8fd755 100644 --- a/contracts/src/token/erc20/mod.rs +++ b/contracts/src/token/erc20/mod.rs @@ -5,12 +5,13 @@ //! nonetheless conventional and does not conflict with the expectations of //! [`Erc20`] applications. use alloy_primitives::{Address, FixedBytes, U256}; -use alloy_sol_types::sol; use openzeppelin_stylus_proc::interface_id; use stylus_sdk::{ call::MethodError, evm, msg, - stylus_proc::{public, sol_storage, SolidityError}, + prelude::storage, + storage::{StorageMap, StorageU256}, + stylus_proc::{public, SolidityError}, }; use crate::utils::introspection::erc165::{Erc165, IErc165}; @@ -18,69 +19,75 @@ use crate::utils::introspection::erc165::{Erc165, IErc165}; pub mod extensions; pub mod utils; -sol! { - /// Emitted when `value` tokens are moved from one account (`from`) to - /// another (`to`). - /// - /// Note that `value` may be zero. - #[allow(missing_docs)] - event Transfer(address indexed from, address indexed to, uint256 value); - /// Emitted when the allowance of a `spender` for an `owner` is set by a - /// call to `approve`. `value` is the new allowance. - #[allow(missing_docs)] - event Approval(address indexed owner, address indexed spender, uint256 value); -} - -sol! { - /// Indicates an error related to the current `balance` of `sender`. Used - /// in transfers. - /// - /// * `sender` - Address whose tokens are being transferred. - /// * `balance` - Current balance for the interacting account. - /// * `needed` - Minimum amount required to perform a transfer. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed); - /// Indicates a failure with the token `sender`. Used in transfers. - /// - /// * `sender` - Address whose tokens are being transferred. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC20InvalidSender(address sender); - /// Indicates a failure with the token `receiver`. Used in transfers. - /// - /// * `receiver` - Address to which the tokens are being transferred. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC20InvalidReceiver(address receiver); - /// Indicates a failure with the `spender`’s `allowance`. Used in - /// transfers. - /// - /// * `spender` - Address that may be allowed to operate on tokens without - /// being their owner. - /// * `allowance` - Amount of tokens a `spender` is allowed to operate - /// with. - /// * `needed` - Minimum amount required to perform a transfer. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed); - /// Indicates a failure with the `spender` to be approved. Used in - /// approvals. - /// - /// * `spender` - Address that may be allowed to operate on tokens without - /// being their owner. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC20InvalidSpender(address spender); - - /// Indicates a failure with the `approver` of a token to be approved. Used in approvals. - /// approver Address initiating an approval operation. - /// - /// * `approver` - Address initiating an approval operation. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC20InvalidApprover(address approver); +pub use sol::*; +#[cfg_attr(coverage_nightly, coverage(off))] +mod sol { + use alloy_sol_macro::sol; + + sol! { + /// Emitted when `value` tokens are moved from one account (`from`) to + /// another (`to`). + /// + /// Note that `value` may be zero. + #[allow(missing_docs)] + event Transfer(address indexed from, address indexed to, uint256 value); + /// Emitted when the allowance of a `spender` for an `owner` is set by a + /// call to `approve`. `value` is the new allowance. + #[allow(missing_docs)] + event Approval(address indexed owner, address indexed spender, uint256 value); + } + + sol! { + /// Indicates an error related to the current `balance` of `sender`. Used + /// in transfers. + /// + /// * `sender` - Address whose tokens are being transferred. + /// * `balance` - Current balance for the interacting account. + /// * `needed` - Minimum amount required to perform a transfer. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed); + /// Indicates a failure with the token `sender`. Used in transfers. + /// + /// * `sender` - Address whose tokens are being transferred. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC20InvalidSender(address sender); + /// Indicates a failure with the token `receiver`. Used in transfers. + /// + /// * `receiver` - Address to which the tokens are being transferred. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC20InvalidReceiver(address receiver); + /// Indicates a failure with the `spender`’s `allowance`. Used in + /// transfers. + /// + /// * `spender` - Address that may be allowed to operate on tokens without + /// being their owner. + /// * `allowance` - Amount of tokens a `spender` is allowed to operate + /// with. + /// * `needed` - Minimum amount required to perform a transfer. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed); + /// Indicates a failure with the `spender` to be approved. Used in + /// approvals. + /// + /// * `spender` - Address that may be allowed to operate on tokens without + /// being their owner. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC20InvalidSpender(address spender); + + /// Indicates a failure with the `approver` of a token to be approved. Used in approvals. + /// approver Address initiating an approval operation. + /// + /// * `approver` - Address initiating an approval operation. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC20InvalidApprover(address approver); + } } /// An [`Erc20`] error defined as described in [ERC-6093]. @@ -112,16 +119,15 @@ impl MethodError for Error { } } -sol_storage! { - /// State of an `Erc20` token. - pub struct Erc20 { - /// Maps users to balances. - mapping(address => uint256) _balances; - /// Maps users to a mapping of each spender's allowance. - mapping(address => mapping(address => uint256)) _allowances; - /// The total supply of the token. - uint256 _total_supply; - } +/// State of an `Erc20` token. +#[storage] +pub struct Erc20 { + /// Maps users to balances. + pub _balances: StorageMap, + /// Maps users to a mapping of each spender's allowance. + pub _allowances: StorageMap>, + /// The total supply of the token. + pub _total_supply: StorageU256, } /// Required interface of an [`Erc20`] compliant contract. diff --git a/contracts/src/token/erc20/utils/safe_erc20.rs b/contracts/src/token/erc20/utils/safe_erc20.rs index d64f18661..20bb84e9e 100644 --- a/contracts/src/token/erc20/utils/safe_erc20.rs +++ b/contracts/src/token/erc20/utils/safe_erc20.rs @@ -10,39 +10,46 @@ //! `contract.safe_transfer(token_addr, ...)`, etc. use alloy_primitives::{Address, U256}; -use alloy_sol_types::{sol, SolCall}; +use alloy_sol_types::SolCall; +pub use sol::*; use stylus_sdk::{ call::{MethodError, RawCall}, contract::address, evm::gas_left, function_selector, + prelude::storage, storage::TopLevelStorage, - stylus_proc::{public, sol_storage, SolidityError}, + stylus_proc::{public, SolidityError}, types::AddressVM, }; use crate::token::erc20; -sol! { - /// An operation with an ERC-20 token failed. - /// - /// * `token` - Address of the ERC-20 token. - #[derive(Debug)] - #[allow(missing_docs)] - error SafeErc20FailedOperation(address token); - - /// Indicates a failed [`ISafeErc20::safe_decrease_allowance`] request. - /// - /// * `spender` - Address of future tokens' spender. - /// * `current_allowance` - Current allowance of the `spender`. - /// * `requested_decrease` - Requested decrease in allowance for `spender`. - #[derive(Debug)] - #[allow(missing_docs)] - error SafeErc20FailedDecreaseAllowance( - address spender, - uint256 current_allowance, - uint256 requested_decrease - ); +#[cfg_attr(coverage_nightly, coverage(off))] +mod sol { + use alloy_sol_macro::sol; + + sol! { + /// An operation with an ERC-20 token failed. + /// + /// * `token` - Address of the ERC-20 token. + #[derive(Debug)] + #[allow(missing_docs)] + error SafeErc20FailedOperation(address token); + + /// Indicates a failed [`ISafeErc20::safe_decrease_allowance`] request. + /// + /// * `spender` - Address of future tokens' spender. + /// * `current_allowance` - Current allowance of the `spender`. + /// * `requested_decrease` - Requested decrease in allowance for `spender`. + #[derive(Debug)] + #[allow(missing_docs)] + error SafeErc20FailedDecreaseAllowance( + address spender, + uint256 current_allowance, + uint256 requested_decrease + ); + } } /// A [`SafeErc20`] error. @@ -63,8 +70,9 @@ impl MethodError for Error { } pub use token::*; -#[allow(missing_docs)] mod token { + #![allow(missing_docs)] + #![cfg_attr(coverage_nightly, coverage(off))] alloy_sol_types::sol! { /// Interface of the ERC-20 token. interface IErc20 { @@ -75,10 +83,10 @@ mod token { } } } -sol_storage! { - /// State of the [`SafeErc20`] Contract. - pub struct SafeErc20 {} -} + +/// State of the [`SafeErc20`] Contract. +#[storage] +pub struct SafeErc20 {} /// NOTE: Implementation of [`TopLevelStorage`] to be able use `&mut self` when /// calling other contracts and not `&mut (impl TopLevelStorage + diff --git a/contracts/src/token/erc721/extensions/consecutive.rs b/contracts/src/token/erc721/extensions/consecutive.rs index e457d0de2..c0c458fa6 100644 --- a/contracts/src/token/erc721/extensions/consecutive.rs +++ b/contracts/src/token/erc721/extensions/consecutive.rs @@ -27,12 +27,11 @@ use alloc::vec; use alloy_primitives::{uint, Address, U256}; -use alloy_sol_types::sol; use stylus_sdk::{ abi::Bytes, evm, msg, - prelude::TopLevelStorage, - stylus_proc::{public, sol_storage, SolidityError}, + prelude::{storage, TopLevelStorage}, + stylus_proc::{public, SolidityError}, }; use crate::{ @@ -55,64 +54,71 @@ use crate::{ }; type U96 = ::Key; - -sol_storage! { - /// State of an [`Erc721Consecutive`] token. - pub struct Erc721Consecutive { - /// Erc721 contract storage. - Erc721 erc721; - /// Checkpoint library contract for sequential ownership. - Trace _sequential_ownership; - /// BitMap library contract for sequential burn of tokens. - BitMap _sequential_burn; - /// Used to offset the first token id. - uint96 _first_consecutive_id; - /// Maximum size of a batch of consecutive tokens. This is designed to limit - /// stress on off-chain indexing services that have to record one entry per - /// token, and have protections against "unreasonably large" batches of - /// tokens. - uint96 _max_batch_size; - } -} - -sol! { - /// Emitted when the tokens from `from_token_id` to `to_token_id` are transferred from `from_address` to `to_address`. - /// - /// * `from_token_id` - First token being transferred. - /// * `to_token_id` - Last token being transferred. - /// * `from_address` - Address from which tokens will be transferred. - /// * `to_address` - Address where the tokens will be transferred to. - #[allow(missing_docs)] - event ConsecutiveTransfer( - uint256 indexed from_token_id, - uint256 to_token_id, - address indexed from_address, - address indexed to_address - ); +type StorageU96 = ::KeyStorage; + +/// State of an [`Erc721Consecutive`] token. +#[storage] +pub struct Erc721Consecutive { + /// Erc721 contract storage. + pub erc721: Erc721, + /// Checkpoint library contract for sequential ownership. + pub _sequential_ownership: Trace, + /// BitMap library contract for sequential burn of tokens. + pub _sequential_burn: BitMap, + /// Used to offset the first token id in + /// [`Erc721Consecutive::_next_consecutive_id`]. + pub _first_consecutive_id: StorageU96, + /// Maximum size of a batch of consecutive tokens. This is designed to + /// limit stress on off-chain indexing services that have to record one + /// entry per token, and have protections against "unreasonably large" + /// batches of tokens. + pub _max_batch_size: StorageU96, } -sol! { - /// Batch mint is restricted to the constructor. - /// Any batch mint not emitting the [`Transfer`] event outside of the constructor - /// is non ERC-721 compliant. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC721ForbiddenBatchMint(); - - /// Exceeds the max number of mints per batch. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC721ExceededMaxBatchMint(uint256 batch_size, uint256 max_batch); - - /// Individual minting is not allowed. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC721ForbiddenMint(); +pub use sol::*; +#[cfg_attr(coverage_nightly, coverage(off))] +mod sol { + use alloy_sol_macro::sol; + + sol! { + /// Emitted when the tokens from `from_token_id` to `to_token_id` are transferred from `from_address` to `to_address`. + /// + /// * `from_token_id` - First token being transferred. + /// * `to_token_id` - Last token being transferred. + /// * `from_address` - Address from which tokens will be transferred. + /// * `to_address` - Address where the tokens will be transferred to. + #[allow(missing_docs)] + event ConsecutiveTransfer( + uint256 indexed from_token_id, + uint256 to_token_id, + address indexed from_address, + address indexed to_address + ); + } - /// Batch burn is not supported. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC721ForbiddenBatchBurn(); + sol! { + /// Batch mint is restricted to the constructor. + /// Any batch mint not emitting the [`Transfer`] event outside of the constructor + /// is non ERC-721 compliant. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC721ForbiddenBatchMint(); + + /// Exceeds the max number of mints per batch. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC721ExceededMaxBatchMint(uint256 batch_size, uint256 max_batch); + + /// Individual minting is not allowed. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC721ForbiddenMint(); + + /// Batch burn is not supported. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC721ForbiddenBatchBurn(); + } } /// An [`Erc721Consecutive`] error. diff --git a/contracts/src/token/erc721/extensions/enumerable.rs b/contracts/src/token/erc721/extensions/enumerable.rs index e8d1223c8..936009e12 100644 --- a/contracts/src/token/erc721/extensions/enumerable.rs +++ b/contracts/src/token/erc721/extensions/enumerable.rs @@ -10,29 +10,38 @@ //! [`Erc721Enumerable`]. use alloy_primitives::{uint, Address, FixedBytes, U256}; -use alloy_sol_types::sol; use openzeppelin_stylus_proc::interface_id; -use stylus_sdk::stylus_proc::{public, sol_storage, SolidityError}; +pub use sol::*; +use stylus_sdk::{ + prelude::storage, + storage::{StorageMap, StorageU256, StorageVec}, + stylus_proc::{public, SolidityError}, +}; use crate::{ token::{erc721, erc721::IErc721}, utils::introspection::erc165::IErc165, }; -sol! { - /// Indicates an error when an `owner`'s token query - /// was out of bounds for `index`. - /// - /// NOTE: The owner being `Address::ZERO` - /// indicates a global out of bounds index. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC721OutOfBoundsIndex(address owner, uint256 index); - - /// Indicates an error related to batch minting not allowed. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC721EnumerableForbiddenBatchMint(); +#[cfg_attr(coverage_nightly, coverage(off))] +mod sol { + use alloy_sol_macro::sol; + + sol! { + /// Indicates an error when an `owner`'s token query + /// was out of bounds for `index`. + /// + /// NOTE: The owner being `Address::ZERO` + /// indicates a global out of bounds index. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC721OutOfBoundsIndex(address owner, uint256 index); + + /// Indicates an error related to batch minting not allowed. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC721EnumerableForbiddenBatchMint(); + } } /// An [`Erc721Enumerable`] extension error. @@ -49,19 +58,17 @@ pub enum Error { EnumerableForbiddenBatchMint(ERC721EnumerableForbiddenBatchMint), } -sol_storage! { - /// State of an Enumerable extension. - pub struct Erc721Enumerable { - /// Maps owners to a mapping of indices to tokens ids. - mapping(address => mapping(uint256 => uint256)) _owned_tokens; - /// Maps tokens ids to indices in `_owned_tokens`. - mapping(uint256 => uint256) _owned_tokens_index; - /// Stores all tokens ids. - uint256[] _all_tokens; - /// Maps indices at `_all_tokens` to tokens ids. - mapping(uint256 => uint256) _all_tokens_index; - } - +/// State of an Enumerable extension. +#[storage] +pub struct Erc721Enumerable { + /// Maps owners to a mapping of indices to tokens ids. + pub _owned_tokens: StorageMap>, + /// Maps tokens ids to indices in `_owned_tokens`. + pub _owned_tokens_index: StorageMap, + /// Stores all tokens ids. + pub _all_tokens: StorageVec, + /// Maps indices at `_all_tokens` to tokens ids. + pub _all_tokens_index: StorageMap, } /// This is the interface of the optional `Enumerable` extension diff --git a/contracts/src/token/erc721/extensions/metadata.rs b/contracts/src/token/erc721/extensions/metadata.rs index ff19adbd9..f85efb331 100644 --- a/contracts/src/token/erc721/extensions/metadata.rs +++ b/contracts/src/token/erc721/extensions/metadata.rs @@ -4,21 +4,22 @@ use alloc::string::{String, ToString}; use alloy_primitives::{FixedBytes, U256}; use openzeppelin_stylus_proc::interface_id; -use stylus_sdk::stylus_proc::{public, sol_storage}; +use stylus_sdk::{ + prelude::storage, storage::StorageString, stylus_proc::public, +}; use crate::{ token::erc721::{Error, IErc721}, utils::{introspection::erc165::IErc165, Metadata}, }; -sol_storage! { - /// Metadata of an [`crate::token::erc721::Erc721`] token. - pub struct Erc721Metadata { - /// Common Metadata. - Metadata _metadata; - /// Base URI for tokens. - string _base_uri; - } +/// Metadata of an [`crate::token::erc721::Erc721`] token. +#[storage] +pub struct Erc721Metadata { + /// Common Metadata. + pub _metadata: Metadata, + /// Base URI for tokens. + pub _base_uri: StorageString, } /// Interface for the optional metadata functions from the ERC-721 standard. diff --git a/contracts/src/token/erc721/extensions/uri_storage.rs b/contracts/src/token/erc721/extensions/uri_storage.rs index 527a3983e..704f4e64b 100644 --- a/contracts/src/token/erc721/extensions/uri_storage.rs +++ b/contracts/src/token/erc721/extensions/uri_storage.rs @@ -4,32 +4,40 @@ use alloc::string::String; use alloy_primitives::U256; -use alloy_sol_types::sol; -use stylus_sdk::{evm, stylus_proc::sol_storage}; +pub use sol::*; +use stylus_sdk::{ + evm, + prelude::storage, + storage::{StorageMap, StorageString}, +}; use crate::token::erc721::{extensions::Erc721Metadata, Error, IErc721}; -sol! { - /// This event gets emitted when the metadata of a token is changed. - /// - /// The event comes from IERC4096. - #[allow(missing_docs)] - event MetadataUpdate(uint256 token_id); - - /// This event gets emitted when the metadata of a range of tokens - /// is changed. - /// - /// The event comes from IERC4096. - #[allow(missing_docs)] - event BatchMetadataUpdate(uint256 from_token_id, uint256 to_token_id); +#[cfg_attr(coverage_nightly, coverage(off))] +mod sol { + use alloy_sol_macro::sol; + + sol! { + /// This event gets emitted when the metadata of a token is changed. + /// + /// The event comes from IERC4096. + #[allow(missing_docs)] + event MetadataUpdate(uint256 token_id); + + /// This event gets emitted when the metadata of a range of tokens + /// is changed. + /// + /// The event comes from IERC4096. + #[allow(missing_docs)] + event BatchMetadataUpdate(uint256 from_token_id, uint256 to_token_id); + } } -sol_storage! { - /// Uri Storage. - pub struct Erc721UriStorage { - /// Optional mapping for token URIs. - mapping(uint256 => string) _token_uris; - } +/// Uri Storage. +#[storage] +pub struct Erc721UriStorage { + /// Optional mapping for token URIs. + pub _token_uris: StorageMap, } impl Erc721UriStorage { @@ -106,7 +114,7 @@ impl Erc721UriStorage { #[cfg(all(test, feature = "std"))] mod tests { use alloy_primitives::U256; - use stylus_sdk::{msg, stylus_proc::sol_storage}; + use stylus_sdk::{msg, prelude::storage}; use super::Erc721UriStorage; use crate::token::erc721::{extensions::Erc721Metadata, Erc721}; @@ -116,12 +124,11 @@ mod tests { U256::from(num) } - sol_storage! { - struct Erc721MetadataExample { - Erc721 erc721; - Erc721Metadata metadata; - Erc721UriStorage uri_storage; - } + #[storage] + struct Erc721MetadataExample { + pub erc721: Erc721, + pub metadata: Erc721Metadata, + pub uri_storage: Erc721UriStorage, } #[motsu::test] diff --git a/contracts/src/token/erc721/mod.rs b/contracts/src/token/erc721/mod.rs index da61a97d0..2747366cd 100644 --- a/contracts/src/token/erc721/mod.rs +++ b/contracts/src/token/erc721/mod.rs @@ -5,10 +5,10 @@ use alloy_primitives::{uint, Address, FixedBytes, U128, U256}; use openzeppelin_stylus_proc::interface_id; use stylus_sdk::{ abi::Bytes, - alloy_sol_types::sol, call::{self, Call, MethodError}, evm, function_selector, msg, prelude::*, + storage::{StorageAddress, StorageBool, StorageMap, StorageU256}, }; use crate::utils::{ @@ -18,109 +18,115 @@ use crate::utils::{ pub mod extensions; -sol! { - /// Emitted when the `token_id` token is transferred from `from` to `to`. - /// - /// * `from` - Address from which the token will be transferred. - /// * `to` - Address where the token will be transferred to. - /// * `token_id` - Token id as a number. - #[allow(missing_docs)] - event Transfer( - address indexed from, - address indexed to, - uint256 indexed token_id - ); +pub use sol::*; +#[cfg_attr(coverage_nightly, coverage(off))] +mod sol { + use alloy_sol_macro::sol; - /// Emitted when `owner` enables `approved` to manage the `token_id` token. - /// - /// * `owner` - Address of the owner of the token. - /// * `approved` - Address of the approver. - /// * `token_id` - Token id as a number. - #[allow(missing_docs)] - event Approval( - address indexed owner, - address indexed approved, - uint256 indexed token_id - ); - - /// Emitted when `owner` enables or disables (`approved`) `operator` - /// to manage all of its assets. - /// - /// * `owner` - Address of the owner of the token. - /// * `operator` - Address of an operator that - /// will manage operations on the token. - /// * `approved` - Whether or not permission has been granted. If true, - /// this means `operator` will be allowed to manage `owner`'s assets. - #[allow(missing_docs)] - event ApprovalForAll(address indexed owner, address indexed operator, bool approved); -} + sol! { + /// Emitted when the `token_id` token is transferred from `from` to `to`. + /// + /// * `from` - Address from which the token will be transferred. + /// * `to` - Address where the token will be transferred to. + /// * `token_id` - Token id as a number. + #[allow(missing_docs)] + event Transfer( + address indexed from, + address indexed to, + uint256 indexed token_id + ); -sol! { - /// Indicates that an address can't be an owner. - /// For example, `Address::ZERO` is a forbidden owner in [`Erc721`]. - /// Used in balance queries. - /// - /// * `owner` - The address deemed to be an invalid owner. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC721InvalidOwner(address owner); + /// Emitted when `owner` enables `approved` to manage the `token_id` token. + /// + /// * `owner` - Address of the owner of the token. + /// * `approved` - Address of the approver. + /// * `token_id` - Token id as a number. + #[allow(missing_docs)] + event Approval( + address indexed owner, + address indexed approved, + uint256 indexed token_id + ); - /// Indicates a `token_id` whose `owner` is the zero address. - /// - /// * `token_id` - Token id as a number. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC721NonexistentToken(uint256 token_id); + /// Emitted when `owner` enables or disables (`approved`) `operator` + /// to manage all of its assets. + /// + /// * `owner` - Address of the owner of the token. + /// * `operator` - Address of an operator that + /// will manage operations on the token. + /// * `approved` - Whether or not permission has been granted. If true, + /// this means `operator` will be allowed to manage `owner`'s assets. + #[allow(missing_docs)] + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + } + + sol! { + /// Indicates that an address can't be an owner. + /// For example, `Address::ZERO` is a forbidden owner in [`Erc721`]. + /// Used in balance queries. + /// + /// * `owner` - The address deemed to be an invalid owner. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC721InvalidOwner(address owner); - /// Indicates an error related to the ownership over a particular token. - /// Used in transfers. - /// - /// * `sender` - Address whose tokens are being transferred. - /// * `token_id` - Token id as a number. - /// * `owner` - Address of the owner of the token. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC721IncorrectOwner(address sender, uint256 token_id, address owner); + /// Indicates a `token_id` whose `owner` is the zero address. + /// + /// * `token_id` - Token id as a number. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC721NonexistentToken(uint256 token_id); - /// Indicates a failure with the token `sender`. Used in transfers. - /// - /// * `sender` - An address whose token is being transferred. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC721InvalidSender(address sender); + /// Indicates an error related to the ownership over a particular token. + /// Used in transfers. + /// + /// * `sender` - Address whose tokens are being transferred. + /// * `token_id` - Token id as a number. + /// * `owner` - Address of the owner of the token. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC721IncorrectOwner(address sender, uint256 token_id, address owner); + + /// Indicates a failure with the token `sender`. Used in transfers. + /// + /// * `sender` - An address whose token is being transferred. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC721InvalidSender(address sender); - /// Indicates a failure with the token `receiver`. Used in transfers. - /// - /// * `receiver` - Address that receives the token. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC721InvalidReceiver(address receiver); + /// Indicates a failure with the token `receiver`. Used in transfers. + /// + /// * `receiver` - Address that receives the token. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC721InvalidReceiver(address receiver); - /// Indicates a failure with the `operator`’s approval. Used in transfers. - /// - /// * `operator` - Address that may be allowed to operate on tokens - /// without being their owner. - /// * `token_id` - Token id as a number. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC721InsufficientApproval(address operator, uint256 token_id); - - /// Indicates a failure with the `approver` of a token to be approved. - /// Used in approvals. - /// - /// * `approver` - Address initiating an approval operation. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC721InvalidApprover(address approver); - - /// Indicates a failure with the `operator` to be approved. - /// Used in approvals. - /// - /// * `operator` - Address that may be allowed to operate on tokens - /// without being their owner. - #[derive(Debug)] - #[allow(missing_docs)] - error ERC721InvalidOperator(address operator); + /// Indicates a failure with the `operator`’s approval. Used in transfers. + /// + /// * `operator` - Address that may be allowed to operate on tokens + /// without being their owner. + /// * `token_id` - Token id as a number. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC721InsufficientApproval(address operator, uint256 token_id); + + /// Indicates a failure with the `approver` of a token to be approved. + /// Used in approvals. + /// + /// * `approver` - Address initiating an approval operation. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC721InvalidApprover(address approver); + + /// Indicates a failure with the `operator` to be approved. + /// Used in approvals. + /// + /// * `operator` - Address that may be allowed to operate on tokens + /// without being their owner. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC721InvalidOperator(address operator); + } } /// An [`Erc721`] error defined as described in [ERC-6093]. @@ -161,8 +167,9 @@ impl MethodError for Error { } pub use receiver::IERC721Receiver; -#[allow(missing_docs)] mod receiver { + #![allow(missing_docs)] + #![cfg_attr(coverage_nightly, coverage(off))] stylus_sdk::stylus_proc::sol_interface! { /// [`Erc721`] token receiver interface. /// @@ -186,18 +193,18 @@ mod receiver { } } -sol_storage! { - /// State of an [`Erc721`] token. - pub struct Erc721 { - /// Maps tokens to owners. - mapping(uint256 => address) _owners; - /// Maps users to balances. - mapping(address => uint256) _balances; - /// Maps tokens to approvals. - mapping(uint256 => address) _token_approvals; - /// Maps owners to a mapping of operator approvals. - mapping(address => mapping(address => bool)) _operator_approvals; - } +/// State of an [`Erc721`] token. +#[storage] +pub struct Erc721 { + /// Maps tokens to owners. + pub _owners: StorageMap, + /// Maps users to balances. + pub _balances: StorageMap, + /// Maps tokens to approvals. + pub _token_approvals: StorageMap, + /// Maps owners to a mapping of operator approvals. + pub _operator_approvals: + StorageMap>, } /// NOTE: Implementation of [`TopLevelStorage`] to be able use `&mut self` when diff --git a/contracts/src/utils/cryptography/ecdsa.rs b/contracts/src/utils/cryptography/ecdsa.rs index eeffca3e8..5f8c6bf6a 100644 --- a/contracts/src/utils/cryptography/ecdsa.rs +++ b/contracts/src/utils/cryptography/ecdsa.rs @@ -5,7 +5,7 @@ use alloc::vec::Vec; use alloy_primitives::{address, uint, Address, B256, U256}; -use alloy_sol_types::{sol, SolType}; +use alloy_sol_types::SolType; use stylus_sdk::{ call::{self, Call, MethodError}, storage::TopLevelStorage, @@ -23,18 +23,39 @@ pub const SIGNATURE_S_UPPER_BOUND: U256 = uint!( 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0_U256 ); -sol! { - /// The signature derives the `Address::ZERO`. - #[derive(Debug)] - #[allow(missing_docs)] - error ECDSAInvalidSignature(); +pub use sol::*; +#[cfg_attr(coverage_nightly, coverage(off))] +mod sol { + use alloy_sol_macro::sol; - /// The signature has an `S` value that is in the upper half order. - /// - /// * `s` - Invalid `S` value. - #[derive(Debug)] - #[allow(missing_docs)] - error ECDSAInvalidSignatureS(bytes32 s); + sol! { + /// The signature derives the `Address::ZERO`. + #[derive(Debug)] + #[allow(missing_docs)] + error ECDSAInvalidSignature(); + + /// The signature has an `S` value that is in the upper half order. + /// + /// * `s` - Invalid `S` value. + #[derive(Debug)] + #[allow(missing_docs)] + error ECDSAInvalidSignatureS(bytes32 s); + } + + sol! { + /// Struct with callable data to the `ecrecover` precompile. + #[allow(missing_docs)] + struct EcRecoverData { + /// EIP-191 Hash of the message. + bytes32 hash; + /// `v` value from the signature. + uint8 v; + /// `r` value from the signature. + bytes32 r; + /// `s` value from the signature. + bytes32 s; + } + } } /// An error that occurred in the implementation of an `ECDSA` library. @@ -52,21 +73,6 @@ impl MethodError for ecdsa::Error { } } -sol! { - /// Struct with callable data to the `ecrecover` precompile. - #[allow(missing_docs)] - struct EcRecoverData { - /// EIP-191 Hash of the message. - bytes32 hash; - /// `v` value from the signature. - uint8 v; - /// `r` value from the signature. - bytes32 r; - /// `s` value from the signature. - bytes32 s; - } -} - /// Returns the address that signed a hashed message (`hash`). /// /// # Arguments diff --git a/contracts/src/utils/metadata.rs b/contracts/src/utils/metadata.rs index 7d904dbb0..fe42e017c 100644 --- a/contracts/src/utils/metadata.rs +++ b/contracts/src/utils/metadata.rs @@ -1,16 +1,17 @@ //! Common Metadata Smart Contract. use alloc::string::String; -use stylus_sdk::stylus_proc::{public, sol_storage}; +use stylus_sdk::{ + prelude::storage, storage::StorageString, stylus_proc::public, +}; -sol_storage! { - /// Metadata of the token. - pub struct Metadata { - /// Token name. - string _name; - /// Token symbol. - string _symbol; - } +/// Metadata of the token. +#[storage] +pub struct Metadata { + /// Token name. + pub _name: StorageString, + /// Token symbol. + pub _symbol: StorageString, } #[public] diff --git a/contracts/src/utils/nonces.rs b/contracts/src/utils/nonces.rs index 13678fcc3..4627b4ea5 100644 --- a/contracts/src/utils/nonces.rs +++ b/contracts/src/utils/nonces.rs @@ -3,16 +3,25 @@ //! Nonces will only increment. use alloy_primitives::{uint, Address, U256}; -use alloy_sol_types::sol; -use stylus_sdk::stylus_proc::{public, sol_storage, SolidityError}; +use stylus_sdk::{ + prelude::storage, + storage::{StorageMap, StorageU256}, + stylus_proc::{public, SolidityError}, +}; const ONE: U256 = uint!(1_U256); -sol! { - /// The nonce used for an `account` is not the expected current nonce. - #[derive(Debug)] - #[allow(missing_docs)] - error InvalidAccountNonce(address account, uint256 currentNonce); +pub use sol::*; +#[cfg_attr(coverage_nightly, coverage(off))] +mod sol { + use alloy_sol_macro::sol; + + sol! { + /// The nonce used for an `account` is not the expected current nonce. + #[derive(Debug)] + #[allow(missing_docs)] + error InvalidAccountNonce(address account, uint256 currentNonce); + } } /// A Nonces error. @@ -22,12 +31,11 @@ pub enum Error { InvalidAccountNonce(InvalidAccountNonce), } -sol_storage! { - /// State of a Nonces Contract. - pub struct Nonces { - /// Mapping from address to its nonce. - mapping(address => uint256) _nonces; - } +/// State of a Nonces Contract. +#[storage] +pub struct Nonces { + /// Mapping from address to its nonce. + pub _nonces: StorageMap, } #[public] diff --git a/contracts/src/utils/pausable.rs b/contracts/src/utils/pausable.rs index 638e78d8b..33e0421c6 100644 --- a/contracts/src/utils/pausable.rs +++ b/contracts/src/utils/pausable.rs @@ -14,34 +14,41 @@ //! exposed by default. //! You should expose them manually in your contract's abi. -use alloy_sol_types::sol; +pub use sol::*; use stylus_sdk::{ evm, msg, - stylus_proc::{public, sol_storage, SolidityError}, + prelude::storage, + storage::StorageBool, + stylus_proc::{public, SolidityError}, }; -sol! { - /// Emitted when pause is triggered by `account`. - #[allow(missing_docs)] - event Paused(address account); +#[cfg_attr(coverage_nightly, coverage(off))] +mod sol { + use alloy_sol_macro::sol; - /// Emitted when the pause is lifted by `account`. - #[allow(missing_docs)] - event Unpaused(address account); -} + sol! { + /// Emitted when pause is triggered by `account`. + #[allow(missing_docs)] + event Paused(address account); -sol! { - /// Indicates an error related to the operation that failed - /// because the contract is paused. - #[derive(Debug)] - #[allow(missing_docs)] - error EnforcedPause(); + /// Emitted when the pause is lifted by `account`. + #[allow(missing_docs)] + event Unpaused(address account); + } - /// Indicates an error related to the operation that failed - /// because the contract is not paused. - #[derive(Debug)] - #[allow(missing_docs)] - error ExpectedPause(); + sol! { + /// Indicates an error related to the operation that failed + /// because the contract is paused. + #[derive(Debug)] + #[allow(missing_docs)] + error EnforcedPause(); + + /// Indicates an error related to the operation that failed + /// because the contract is not paused. + #[derive(Debug)] + #[allow(missing_docs)] + error ExpectedPause(); + } } /// A Pausable error. @@ -55,12 +62,11 @@ pub enum Error { ExpectedPause(ExpectedPause), } -sol_storage! { - /// State of a Pausable Contract. - pub struct Pausable { - /// Indicates whether the contract is `Paused`. - bool _paused; - } +/// State of a Pausable Contract. +#[storage] +pub struct Pausable { + /// Indicates whether the contract is `Paused`. + pub _paused: StorageBool, } #[public] diff --git a/contracts/src/utils/structs/bitmap.rs b/contracts/src/utils/structs/bitmap.rs index 55fdedec0..821bf9b19 100644 --- a/contracts/src/utils/structs/bitmap.rs +++ b/contracts/src/utils/structs/bitmap.rs @@ -14,17 +14,19 @@ //! //! [merkle-distributor]: https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol use alloy_primitives::{uint, U256}; -use stylus_sdk::stylus_proc::sol_storage; +use stylus_sdk::{ + prelude::storage, + storage::{StorageMap, StorageU256}, +}; const ONE: U256 = uint!(0x1_U256); const HEX_FF: U256 = uint!(0xff_U256); -sol_storage! { - /// State of bit map. - pub struct BitMap { - /// Inner laying mapping. - mapping(uint256 => uint256) _data; - } +/// State of bit map. +#[storage] +pub struct BitMap { + /// Inner laying mapping. + pub _data: StorageMap, } impl BitMap { diff --git a/contracts/src/utils/structs/checkpoints/mod.rs b/contracts/src/utils/structs/checkpoints/mod.rs index 88b1eb22c..ee99b586f 100644 --- a/contracts/src/utils/structs/checkpoints/mod.rs +++ b/contracts/src/utils/structs/checkpoints/mod.rs @@ -10,8 +10,8 @@ pub mod generic_size; use alloy_primitives::{uint, U256, U32}; -use alloy_sol_types::sol; pub use generic_size::{Size, S160, S208, S224}; +pub use sol::*; use stylus_sdk::{ call::MethodError, prelude::*, @@ -23,10 +23,15 @@ use crate::utils::{ structs::checkpoints::generic_size::{Accessor, Num}, }; -sol! { - /// A value was attempted to be inserted into a past checkpoint. - #[derive(Debug)] - error CheckpointUnorderedInsertion(); +#[cfg_attr(coverage_nightly, coverage(off))] +mod sol { + use alloy_sol_macro::sol; + + sol! { + /// A value was attempted to be inserted into a past checkpoint. + #[derive(Debug)] + error CheckpointUnorderedInsertion(); + } } /// An error that occurred while calling the [`Trace`] checkpoint contract. @@ -42,20 +47,20 @@ impl MethodError for Error { } } -sol_storage! { - /// State of the checkpoint library contract. - pub struct Trace { - /// Stores checkpoints in a dynamic array sorted by key. - StorageVec> _checkpoints; - } +/// State of the checkpoint library contract. +#[storage] +pub struct Trace { + /// Stores checkpoints in a dynamic array sorted by key. + pub _checkpoints: StorageVec>, +} - /// State of a single checkpoint. - pub struct Checkpoint { - /// The key of the checkpoint. Used as a sorting key. - S::KeyStorage _key; - /// The value corresponding to the key. - S::ValueStorage _value; - } +/// State of a single checkpoint. +#[storage] +pub struct Checkpoint { + /// The key of the checkpoint. Used as a sorting key. + pub _key: S::KeyStorage, + /// The value corresponding to the key. + pub _value: S::ValueStorage, } impl Trace { diff --git a/docs/modules/ROOT/pages/access-control.adoc b/docs/modules/ROOT/pages/access-control.adoc index 0e3bfb914..b819af28e 100644 --- a/docs/modules/ROOT/pages/access-control.adoc +++ b/docs/modules/ROOT/pages/access-control.adoc @@ -13,12 +13,11 @@ OpenZeppelin Contracts for Stylus provides https://docs.rs/openzeppelin-stylus/0 ---- use openzeppelin_stylus::access::ownable::Ownable; -sol_storage! { - #[entrypoint] - struct OwnableExample { - #[borrow] - Ownable ownable; - } +#[entrypoint] +#[storage] +struct OwnableExample { + #[borrow] + pub ownable: Ownable, } #[public] @@ -73,14 +72,13 @@ Here's a simple example of using `AccessControl` in an xref:erc20.adoc[ERC-20 to [source,rust] ---- -sol_storage! { - #[entrypoint] - struct Example { - #[borrow] - Erc20 erc20; - #[borrow] - AccessControl access; - } +#[entrypoint] +#[storage] +struct Example { + #[borrow] + pub erc20: Erc20, + #[borrow] + pub access: AccessControl, } // `keccak256("MINTER_ROLE")` @@ -113,14 +111,13 @@ Let's augment our ERC-20 token example by also defining a 'burner' role, which l [source,rust] ---- -sol_storage! { - #[entrypoint] - struct Example { - #[borrow] - Erc20 erc20; - #[borrow] - AccessControl access; - } +#[entrypoint] +#[storage] +struct Example { + #[borrow] + pub erc20: Erc20, + #[borrow] + pub access: AccessControl, } // `keccak256("MINTER_ROLE")` diff --git a/docs/modules/ROOT/pages/deploy.adoc b/docs/modules/ROOT/pages/deploy.adoc index 3cff7ea0f..4ac25e5d7 100644 --- a/docs/modules/ROOT/pages/deploy.adoc +++ b/docs/modules/ROOT/pages/deploy.adoc @@ -30,11 +30,10 @@ For a contract like this: [source,rust] ---- -sol_storage! { - #[entrypoint] - pub struct Counter { - uint256 number; - } +#[entrypoint] +#[storage] +struct Counter { + pub number: StorageU256, } #[public] diff --git a/docs/modules/ROOT/pages/erc1155-burnable.adoc b/docs/modules/ROOT/pages/erc1155-burnable.adoc index 546b2e813..f824afc43 100644 --- a/docs/modules/ROOT/pages/erc1155-burnable.adoc +++ b/docs/modules/ROOT/pages/erc1155-burnable.adoc @@ -14,12 +14,11 @@ use openzeppelin_stylus::token::erc1155::{ extensions::IErc1155Burnable, Erc1155, }; -sol_storage! { - #[entrypoint] - struct Erc1155Example { - #[borrow] - Erc1155 erc1155; - } +#[entrypoint] +#[storage] +struct Erc1155Example { + #[borrow] + pub erc1155: Erc1155, } #[public] diff --git a/docs/modules/ROOT/pages/erc1155-metadata-uri.adoc b/docs/modules/ROOT/pages/erc1155-metadata-uri.adoc index 71bdbf845..fcccbcb40 100644 --- a/docs/modules/ROOT/pages/erc1155-metadata-uri.adoc +++ b/docs/modules/ROOT/pages/erc1155-metadata-uri.adoc @@ -16,14 +16,12 @@ use openzeppelin_stylus::token::erc1155::{ extensions::Erc1155MetadataUri, Erc1155, }; -sol_storage! { - #[entrypoint] - struct Erc1155Example { - #[borrow] - Erc1155 erc1155; - #[borrow] - Erc1155MetadataUri metadata_uri; - } +#[entrypoint] +#[storage] +struct Erc1155Example { + #[borrow] + pub erc1155: Erc1155, + pub metadata_uri: Erc1155MetadataUri, } #[public] diff --git a/docs/modules/ROOT/pages/erc1155-pausable.adoc b/docs/modules/ROOT/pages/erc1155-pausable.adoc index 4d9dcd633..528ca5557 100644 --- a/docs/modules/ROOT/pages/erc1155-pausable.adoc +++ b/docs/modules/ROOT/pages/erc1155-pausable.adoc @@ -16,14 +16,13 @@ use openzeppelin_stylus::{ utils::Pausable, }; -sol_storage! { - #[entrypoint] - struct Erc1155Example { - #[borrow] - Erc1155 erc1155; - #[borrow] - Pausable pausable; - } +#[entrypoint] +#[storage] +struct Erc1155Example { + #[borrow] + pub erc1155: Erc1155, + #[borrow] + pub pausable: Pausable, } #[public] diff --git a/docs/modules/ROOT/pages/erc1155-supply.adoc b/docs/modules/ROOT/pages/erc1155-supply.adoc index ea9ba4405..900d945c7 100644 --- a/docs/modules/ROOT/pages/erc1155-supply.adoc +++ b/docs/modules/ROOT/pages/erc1155-supply.adoc @@ -17,12 +17,11 @@ use openzeppelin_stylus::token::erc1155::extensions::{ Erc1155Supply, IErc1155Supply, }; -sol_storage! { - #[entrypoint] - struct Erc1155Example { - #[borrow] - Erc1155Supply erc1155_supply; - } +#[entrypoint] +#[storage] +struct Erc1155Example { + #[borrow] + pub erc1155_supply: Erc1155Supply, } #[public] diff --git a/docs/modules/ROOT/pages/erc1155-uri-storage.adoc b/docs/modules/ROOT/pages/erc1155-uri-storage.adoc index 22a994e00..5878893a3 100644 --- a/docs/modules/ROOT/pages/erc1155-uri-storage.adoc +++ b/docs/modules/ROOT/pages/erc1155-uri-storage.adoc @@ -17,16 +17,14 @@ use openzeppelin_stylus::token::erc1155::{ extensions::{Erc1155MetadataUri, Erc1155UriStorage}, Erc1155, }; -use stylus_sdk::prelude::{entrypoint, public, sol_storage}; - -sol_storage! { - #[entrypoint] - struct Erc1155MetadataUriExample { - #[borrow] - Erc1155 erc1155; - Erc1155MetadataUri metadata_uri; - Erc1155UriStorage uri_storage; - } + +#[entrypoint] +#[storage] +struct Erc1155MetadataUriExample { + #[borrow] + pub erc1155: Erc1155, + pub metadata_uri: Erc1155MetadataUri, + pub uri_storage: Erc1155UriStorage, } #[public] diff --git a/docs/modules/ROOT/pages/erc20-burnable.adoc b/docs/modules/ROOT/pages/erc20-burnable.adoc index dd0d3337c..75f4c63c2 100644 --- a/docs/modules/ROOT/pages/erc20-burnable.adoc +++ b/docs/modules/ROOT/pages/erc20-burnable.adoc @@ -16,12 +16,11 @@ use openzeppelin_stylus::{ }, }; -sol_storage! { - #[entrypoint] - struct Erc20Example { - #[borrow] - Erc20 erc20; - } +#[entrypoint] +#[storage] +struct Erc20Example { + #[borrow] + pub erc20: Erc20, } #[public] diff --git a/docs/modules/ROOT/pages/erc20-capped.adoc b/docs/modules/ROOT/pages/erc20-capped.adoc index 350f46c6a..3b1621441 100644 --- a/docs/modules/ROOT/pages/erc20-capped.adoc +++ b/docs/modules/ROOT/pages/erc20-capped.adoc @@ -16,14 +16,13 @@ use openzeppelin_stylus::{ } }; -sol_storage! { - #[entrypoint] - struct Erc20Example { - #[borrow] - Erc20 erc20; - #[borrow] - Capped capped; - } +#[entrypoint] +#[storage] +struct Erc20Example { + #[borrow] + pub erc20: Erc20, + #[borrow] + pub capped: Capped, } #[public] diff --git a/docs/modules/ROOT/pages/erc20-metadata.adoc b/docs/modules/ROOT/pages/erc20-metadata.adoc index 05e91cabe..5d2329887 100644 --- a/docs/modules/ROOT/pages/erc20-metadata.adoc +++ b/docs/modules/ROOT/pages/erc20-metadata.adoc @@ -16,14 +16,13 @@ use openzeppelin_stylus::{ }, }; -sol_storage! { - #[entrypoint] - struct Erc20Example { - #[borrow] - Erc20 erc20; - #[borrow] - Erc20Metadata metadata; - } +#[entrypoint] +#[storage] +struct Erc20Example { + #[borrow] + pub erc20: Erc20, + #[borrow] + pub metadata: Erc20Metadata, } #[public] diff --git a/docs/modules/ROOT/pages/erc20-pausable.adoc b/docs/modules/ROOT/pages/erc20-pausable.adoc index ecfc16283..6bc954ff6 100644 --- a/docs/modules/ROOT/pages/erc20-pausable.adoc +++ b/docs/modules/ROOT/pages/erc20-pausable.adoc @@ -16,14 +16,13 @@ use openzeppelin_stylus::{ utils::Pausable, }; -sol_storage! { - #[entrypoint] - struct Erc20Example { - #[borrow] - Erc20 erc20; - #[borrow] - Pausable pausable; - } +#[entrypoint] +#[storage] +struct Erc20Example { + #[borrow] + pub erc20: Erc20, + #[borrow] + pub pausable: Pausable, } #[public] diff --git a/docs/modules/ROOT/pages/erc20-permit.adoc b/docs/modules/ROOT/pages/erc20-permit.adoc index c9bfa1dde..bf69b2ed5 100644 --- a/docs/modules/ROOT/pages/erc20-permit.adoc +++ b/docs/modules/ROOT/pages/erc20-permit.adoc @@ -15,16 +15,16 @@ use openzeppelin_stylus::{ token::erc20::extensions::Erc20Permit, utils::cryptography::eip712::IEip712, }; -sol_storage! { - #[entrypoint] - struct Erc20PermitExample { - #[borrow] - Erc20Permit erc20_permit; - } - - struct Eip712 {} +#[entrypoint] +#[storage] +struct Erc20PermitExample { + #[borrow] + pub erc20_permit: Erc20Permit, } +#[storage] +struct Eip712 {} + // Define `NAME` and `VERSION` for your contract. impl IEip712 for Eip712 { const NAME: &'static str = "ERC-20 Permit Example"; diff --git a/docs/modules/ROOT/pages/erc20.adoc b/docs/modules/ROOT/pages/erc20.adoc index 5a15c3720..6294fefd5 100644 --- a/docs/modules/ROOT/pages/erc20.adoc +++ b/docs/modules/ROOT/pages/erc20.adoc @@ -15,14 +15,13 @@ Here's what our GLD token might look like. [source,rust] ---- -sol_storage! { - #[entrypoint] - struct GLDToken { - #[borrow] - Erc20 erc20; - #[borrow] - Erc20Metadata metadata; - } +#[entrypoint] +#[storage] +struct GLDToken { + #[borrow] + pub erc20: Erc20, + #[borrow] + pub metadata: Erc20Metadata, } #[public] diff --git a/docs/modules/ROOT/pages/erc721-burnable.adoc b/docs/modules/ROOT/pages/erc721-burnable.adoc index b02a719b7..c058b747a 100644 --- a/docs/modules/ROOT/pages/erc721-burnable.adoc +++ b/docs/modules/ROOT/pages/erc721-burnable.adoc @@ -16,12 +16,11 @@ use openzeppelin_stylus::{ }, }; -sol_storage! { - #[entrypoint] - struct Erc721Example { - #[borrow] - Erc721 erc721; - } +#[entrypoint] +#[storage] +struct Erc721Example { + #[borrow] + pub erc721: Erc721, } #[public] diff --git a/docs/modules/ROOT/pages/erc721-consecutive.adoc b/docs/modules/ROOT/pages/erc721-consecutive.adoc index 2ddaa74ad..b327fefc5 100644 --- a/docs/modules/ROOT/pages/erc721-consecutive.adoc +++ b/docs/modules/ROOT/pages/erc721-consecutive.adoc @@ -13,12 +13,11 @@ use openzeppelin_stylus::token::erc721::extensions::consecutive::{ Erc721Consecutive, Error, }; -sol_storage! { - #[entrypoint] - struct Erc721ConsecutiveExample { - #[borrow] - Erc721Consecutive erc721_consecutive; - } +#[entrypoint] +#[storage] +struct Erc721ConsecutiveExample { + #[borrow] + pub erc721_consecutive: Erc721Consecutive, } #[public] diff --git a/docs/modules/ROOT/pages/erc721-enumerable.adoc b/docs/modules/ROOT/pages/erc721-enumerable.adoc index 35f27d6c2..9069ac578 100644 --- a/docs/modules/ROOT/pages/erc721-enumerable.adoc +++ b/docs/modules/ROOT/pages/erc721-enumerable.adoc @@ -15,14 +15,13 @@ use openzeppelin_stylus::token::erc721::{ Erc721, IErc721, }; -sol_storage! { - #[entrypoint] - struct Erc721Example { - #[borrow] - Erc721 erc721; - #[borrow] - Erc721Enumerable enumerable; - } +#[entrypoint] +#[storage] +struct Erc721Example { + #[borrow] + pub erc721: Erc721, + #[borrow] + pub enumerable: Enumerable, } #[public] diff --git a/docs/modules/ROOT/pages/erc721-metadata.adoc b/docs/modules/ROOT/pages/erc721-metadata.adoc index 5f7eea358..c209cb153 100644 --- a/docs/modules/ROOT/pages/erc721-metadata.adoc +++ b/docs/modules/ROOT/pages/erc721-metadata.adoc @@ -16,14 +16,13 @@ use openzeppelin_stylus::{ }, }; -sol_storage! { - #[entrypoint] - struct Erc721Example { - #[borrow] - Erc721 erc721; - #[borrow] - Erc721Metadata metadata; - } +#[entrypoint] +#[storage] +struct Erc721Example { + #[borrow] + pub erc721: Erc721, + #[borrow] + pub metadata: Metadata, } #[public] diff --git a/docs/modules/ROOT/pages/erc721-pausable.adoc b/docs/modules/ROOT/pages/erc721-pausable.adoc index 593ce1038..ffb450e40 100644 --- a/docs/modules/ROOT/pages/erc721-pausable.adoc +++ b/docs/modules/ROOT/pages/erc721-pausable.adoc @@ -16,14 +16,13 @@ use openzeppelin_stylus::{ utils::Pausable, }; -sol_storage! { - #[entrypoint] - struct Erc721Example { - #[borrow] - Erc721 erc721; - #[borrow] - Pausable pausable; - } +#[entrypoint] +#[storage] +struct Erc721Example { + #[borrow] + pub erc721: Erc721, + #[borrow] + pub pausable: Pausable, } #[public] diff --git a/docs/modules/ROOT/pages/erc721-uri-storage.adoc b/docs/modules/ROOT/pages/erc721-uri-storage.adoc index 99c363201..efa4344e6 100644 --- a/docs/modules/ROOT/pages/erc721-uri-storage.adoc +++ b/docs/modules/ROOT/pages/erc721-uri-storage.adoc @@ -21,15 +21,14 @@ use openzeppelin_stylus::token::erc721::{ Erc721, IErc721, }; -sol_storage! { - #[entrypoint] - struct Erc721MetadataExample { - #[borrow] - Erc721 erc721; - #[borrow] - Erc721Metadata metadata; - Erc721UriStorage uri_storage; - } +#[entrypoint] +#[storage] +struct Erc721MetadataExample { + #[borrow] + pub erc721: Erc721, + #[borrow] + pub metadata: Metadata, + pub uri_storage: UriStorage, } #[public] diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 1f5b9e636..f6ce79384 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -21,15 +21,14 @@ Here's what a contract for tokenized items might look like: [source,rust] ---- -sol_storage! { - #[entrypoint] - struct GameItem { - #[borrow] - Erc721 erc721; - #[borrow] - Metadata metadata; - uint256 _next_token_id; - } +#[entrypoint] +#[storage] +struct GameItem { + #[borrow] + pub erc721: Erc721, + #[borrow] + pub metadata: Metadata, + pub next_token_id: StorageU256, } #[public] @@ -40,7 +39,7 @@ impl GameItem { player: Address, ) -> Result> { let token_id = self._next_token_id.get() + uint!(1_U256); - self._next_token_id.set(token_id); + self.next_token_id.set(token_id); self.erc721._mint(player, token_id)?; diff --git a/docs/modules/ROOT/pages/utilities.adoc b/docs/modules/ROOT/pages/utilities.adoc index ff7ab20ca..066d590ad 100644 --- a/docs/modules/ROOT/pages/utilities.adoc +++ b/docs/modules/ROOT/pages/utilities.adoc @@ -15,12 +15,11 @@ Contracts for Stylus provides helpers for implementing ERC-165 in your contracts [source,rust] ---- -sol_storage! { - #[entrypoint] - struct Erc721Example { - #[borrow] - Erc721 erc721; - } +#[entrypoint] +#[storage] +struct Erc721Example { + #[borrow] + pub erc721: Erc721, } #[public] diff --git a/examples/access-control/src/lib.rs b/examples/access-control/src/lib.rs index 0b4590d1f..81d8777e0 100644 --- a/examples/access-control/src/lib.rs +++ b/examples/access-control/src/lib.rs @@ -8,16 +8,15 @@ use openzeppelin_stylus::{ access::control::AccessControl, token::erc20::{Erc20, IErc20}, }; -use stylus_sdk::prelude::{entrypoint, public, sol_storage}; - -sol_storage! { - #[entrypoint] - struct AccessControlExample { - #[borrow] - Erc20 erc20; - #[borrow] - AccessControl access; - } +use stylus_sdk::prelude::{entrypoint, public, storage}; + +#[entrypoint] +#[storage] +struct AccessControlExample { + #[borrow] + pub erc20: Erc20, + #[borrow] + pub access: AccessControl, } // `keccak256("TRANSFER_ROLE")` diff --git a/examples/basic/token/src/lib.rs b/examples/basic/token/src/lib.rs index 9976743ab..aaa6efe1f 100644 --- a/examples/basic/token/src/lib.rs +++ b/examples/basic/token/src/lib.rs @@ -5,16 +5,15 @@ use alloc::vec::Vec; use alloy_primitives::{Address, U256}; use openzeppelin_stylus::token::erc20::{extensions::Erc20Metadata, Erc20}; -use stylus_sdk::prelude::{entrypoint, public, sol_storage}; +use stylus_sdk::prelude::{entrypoint, public, storage}; -sol_storage! { - #[entrypoint] - struct Erc20Example { - #[borrow] - Erc20 erc20; - #[borrow] - Erc20Metadata metadata; - } +#[entrypoint] +#[storage] +struct Erc20Example { + #[borrow] + pub erc20: Erc20, + #[borrow] + pub metadata: Erc20Metadata, } #[public] diff --git a/examples/ecdsa/src/lib.rs b/examples/ecdsa/src/lib.rs index de6bf0576..f94e0a5da 100644 --- a/examples/ecdsa/src/lib.rs +++ b/examples/ecdsa/src/lib.rs @@ -5,12 +5,11 @@ use alloc::vec::Vec; use alloy_primitives::{Address, B256}; use openzeppelin_stylus::utils::cryptography::ecdsa; -use stylus_sdk::prelude::{entrypoint, public, sol_storage}; +use stylus_sdk::prelude::{entrypoint, public, storage}; -sol_storage! { - #[entrypoint] - struct ECDSAExample {} -} +#[entrypoint] +#[storage] +struct ECDSAExample {} #[public] impl ECDSAExample { diff --git a/examples/erc1155-metadata-uri/src/lib.rs b/examples/erc1155-metadata-uri/src/lib.rs index bb7cb97bc..b460a383e 100644 --- a/examples/erc1155-metadata-uri/src/lib.rs +++ b/examples/erc1155-metadata-uri/src/lib.rs @@ -9,16 +9,15 @@ use openzeppelin_stylus::{ }, utils::introspection::erc165::IErc165, }; -use stylus_sdk::prelude::{entrypoint, public, sol_storage}; +use stylus_sdk::prelude::{entrypoint, public, storage}; -sol_storage! { - #[entrypoint] - struct Erc1155MetadataUriExample { - #[borrow] - Erc1155 erc1155; - Erc1155MetadataUri metadata_uri; - Erc1155UriStorage uri_storage; - } +#[entrypoint] +#[storage] +struct Erc1155MetadataUriExample { + #[borrow] + pub erc1155: Erc1155, + pub metadata_uri: Erc1155MetadataUri, + pub uri_storage: Erc1155UriStorage, } #[public] diff --git a/examples/erc1155-supply/src/lib.rs b/examples/erc1155-supply/src/lib.rs index dd18816ed..845c81d01 100644 --- a/examples/erc1155-supply/src/lib.rs +++ b/examples/erc1155-supply/src/lib.rs @@ -9,16 +9,16 @@ use openzeppelin_stylus::token::erc1155::extensions::{ }; use stylus_sdk::{ abi::Bytes, - prelude::{entrypoint, public, sol_storage}, + prelude::{entrypoint, public, storage}, }; -sol_storage! { - #[entrypoint] - struct Erc1155Example { - #[borrow] - Erc1155Supply erc1155_supply; - } +#[entrypoint] +#[storage] +struct Erc1155Example { + #[borrow] + pub erc1155_supply: Erc1155Supply, } + #[public] #[inherit(Erc1155Supply)] impl Erc1155Example { diff --git a/examples/erc1155/src/lib.rs b/examples/erc1155/src/lib.rs index 778a66c71..8666b4004 100644 --- a/examples/erc1155/src/lib.rs +++ b/examples/erc1155/src/lib.rs @@ -10,17 +10,16 @@ use openzeppelin_stylus::{ }; use stylus_sdk::{ abi::Bytes, - prelude::{entrypoint, public, sol_storage}, + prelude::{entrypoint, public, storage}, }; -sol_storage! { - #[entrypoint] - struct Erc1155Example { - #[borrow] - Erc1155 erc1155; - #[borrow] - Pausable pausable; - } +#[entrypoint] +#[storage] +struct Erc1155Example { + #[borrow] + pub erc1155: Erc1155, + #[borrow] + pub pausable: Pausable, } #[public] diff --git a/examples/erc20-permit/src/lib.rs b/examples/erc20-permit/src/lib.rs index b4e9ef425..81f3c80db 100644 --- a/examples/erc20-permit/src/lib.rs +++ b/examples/erc20-permit/src/lib.rs @@ -7,17 +7,16 @@ use alloy_primitives::{Address, U256}; use openzeppelin_stylus::{ token::erc20::extensions::Erc20Permit, utils::cryptography::eip712::IEip712, }; -use stylus_sdk::prelude::{entrypoint, public, sol_storage}; +use stylus_sdk::prelude::{entrypoint, public, storage}; -sol_storage! { - #[entrypoint] - struct Erc20PermitExample { - #[borrow] - Erc20Permit erc20_permit; - } - - struct Eip712 {} +#[entrypoint] +#[storage] +struct Erc20PermitExample { + #[borrow] + pub erc20_permit: Erc20Permit, } +#[storage] +struct Eip712 {} impl IEip712 for Eip712 { const NAME: &'static str = "ERC-20 Permit Example"; diff --git a/examples/erc20/src/lib.rs b/examples/erc20/src/lib.rs index 28ec60b25..1f4d01db1 100644 --- a/examples/erc20/src/lib.rs +++ b/examples/erc20/src/lib.rs @@ -11,22 +11,21 @@ use openzeppelin_stylus::{ }, utils::{introspection::erc165::IErc165, Pausable}, }; -use stylus_sdk::prelude::{entrypoint, public, sol_storage}; +use stylus_sdk::prelude::{entrypoint, public, storage}; const DECIMALS: u8 = 10; -sol_storage! { - #[entrypoint] - struct Erc20Example { - #[borrow] - Erc20 erc20; - #[borrow] - Erc20Metadata metadata; - #[borrow] - Capped capped; - #[borrow] - Pausable pausable; - } +#[entrypoint] +#[storage] +struct Erc20Example { + #[borrow] + pub erc20: Erc20, + #[borrow] + pub metadata: Erc20Metadata, + #[borrow] + pub capped: Capped, + #[borrow] + pub pausable: Pausable, } #[public] diff --git a/examples/erc721-consecutive/src/lib.rs b/examples/erc721-consecutive/src/lib.rs index 798850905..6e13be0ff 100644 --- a/examples/erc721-consecutive/src/lib.rs +++ b/examples/erc721-consecutive/src/lib.rs @@ -7,12 +7,11 @@ use openzeppelin_stylus::token::erc721::extensions::consecutive::{ }; use stylus_sdk::prelude::*; -sol_storage! { - #[entrypoint] - struct Erc721ConsecutiveExample { - #[borrow] - Erc721Consecutive erc721_consecutive; - } +#[entrypoint] +#[storage] +struct Erc721ConsecutiveExample { + #[borrow] + pub erc721_consecutive: Erc721Consecutive, } #[public] diff --git a/examples/erc721-metadata/src/lib.rs b/examples/erc721-metadata/src/lib.rs index f5e7237ca..7d72c4e29 100644 --- a/examples/erc721-metadata/src/lib.rs +++ b/examples/erc721-metadata/src/lib.rs @@ -14,17 +14,16 @@ use openzeppelin_stylus::{ }, utils::introspection::erc165::IErc165, }; -use stylus_sdk::prelude::{entrypoint, public, sol_storage}; - -sol_storage! { - #[entrypoint] - struct Erc721MetadataExample { - #[borrow] - Erc721 erc721; - #[borrow] - Metadata metadata; - UriStorage uri_storage; - } +use stylus_sdk::prelude::{entrypoint, public, storage}; + +#[entrypoint] +#[storage] +struct Erc721MetadataExample { + #[borrow] + pub erc721: Erc721, + #[borrow] + pub metadata: Metadata, + pub uri_storage: UriStorage, } #[public] diff --git a/examples/erc721/src/lib.rs b/examples/erc721/src/lib.rs index 5be6fb439..cb71c5ff8 100644 --- a/examples/erc721/src/lib.rs +++ b/examples/erc721/src/lib.rs @@ -13,19 +13,18 @@ use openzeppelin_stylus::{ }; use stylus_sdk::{ abi::Bytes, - prelude::{entrypoint, public, sol_storage}, + prelude::{entrypoint, public, storage}, }; -sol_storage! { - #[entrypoint] - struct Erc721Example { - #[borrow] - Erc721 erc721; - #[borrow] - Enumerable enumerable; - #[borrow] - Pausable pausable; - } +#[entrypoint] +#[storage] +struct Erc721Example { + #[borrow] + pub erc721: Erc721, + #[borrow] + pub enumerable: Enumerable, + #[borrow] + pub pausable: Pausable, } #[public] diff --git a/examples/merkle-proofs/src/lib.rs b/examples/merkle-proofs/src/lib.rs index a1db588d2..d0dd8fcef 100644 --- a/examples/merkle-proofs/src/lib.rs +++ b/examples/merkle-proofs/src/lib.rs @@ -10,7 +10,7 @@ use openzeppelin_crypto::{ }; use stylus_sdk::{ alloy_sol_types::sol, - prelude::{entrypoint, public, sol_storage}, + prelude::{entrypoint, public, storage}, stylus_proc::SolidityError, }; @@ -47,10 +47,9 @@ impl core::convert::From for VerifierError { } } -sol_storage! { - #[entrypoint] - struct VerifierContract { } -} +#[entrypoint] +#[storage] +struct VerifierContract {} #[public] impl VerifierContract { diff --git a/examples/ownable-two-step/src/lib.rs b/examples/ownable-two-step/src/lib.rs index 11d9583ea..79f4a9a77 100644 --- a/examples/ownable-two-step/src/lib.rs +++ b/examples/ownable-two-step/src/lib.rs @@ -8,16 +8,15 @@ use openzeppelin_stylus::{ access::ownable_two_step::Ownable2Step, token::erc20::{Erc20, IErc20}, }; -use stylus_sdk::prelude::{entrypoint, public, sol_storage}; +use stylus_sdk::prelude::{entrypoint, public, storage}; -sol_storage! { - #[entrypoint] - struct Ownable2StepExample { - #[borrow] - Erc20 erc20; - #[borrow] - Ownable2Step ownable; - } +#[entrypoint] +#[storage] +struct Ownable2StepExample { + #[borrow] + pub erc20: Erc20, + #[borrow] + pub ownable: Ownable2Step, } #[public] diff --git a/examples/ownable/src/lib.rs b/examples/ownable/src/lib.rs index b7654c423..864d2ee5d 100644 --- a/examples/ownable/src/lib.rs +++ b/examples/ownable/src/lib.rs @@ -8,16 +8,15 @@ use openzeppelin_stylus::{ access::ownable::Ownable, token::erc20::{Erc20, IErc20}, }; -use stylus_sdk::prelude::{entrypoint, public, sol_storage}; +use stylus_sdk::prelude::{entrypoint, public, storage}; -sol_storage! { - #[entrypoint] - struct OwnableExample { - #[borrow] - Erc20 erc20; - #[borrow] - Ownable ownable; - } +#[entrypoint] +#[storage] +struct OwnableExample { + #[borrow] + pub erc20: Erc20, + #[borrow] + pub ownable: Ownable, } #[public] diff --git a/examples/safe-erc20/src/lib.rs b/examples/safe-erc20/src/lib.rs index fcc64a9cc..a93915453 100644 --- a/examples/safe-erc20/src/lib.rs +++ b/examples/safe-erc20/src/lib.rs @@ -2,14 +2,13 @@ extern crate alloc; use openzeppelin_stylus::token::erc20::utils::safe_erc20::SafeErc20; -use stylus_sdk::prelude::{entrypoint, public, sol_storage}; +use stylus_sdk::prelude::{entrypoint, public, storage}; -sol_storage! { - #[entrypoint] - struct SafeErc20Example { - #[borrow] - SafeErc20 safe_erc20; - } +#[entrypoint] +#[storage] +struct SafeErc20Example { + #[borrow] + pub safe_erc20: SafeErc20, } #[public] diff --git a/examples/vesting-wallet/src/lib.rs b/examples/vesting-wallet/src/lib.rs index 6e74ebe28..372f2f398 100644 --- a/examples/vesting-wallet/src/lib.rs +++ b/examples/vesting-wallet/src/lib.rs @@ -2,14 +2,13 @@ extern crate alloc; use openzeppelin_stylus::finance::vesting_wallet::VestingWallet; -use stylus_sdk::prelude::{entrypoint, public, sol_storage}; +use stylus_sdk::prelude::{entrypoint, public, storage}; -sol_storage! { - #[entrypoint] - struct VestingWalletExample { - #[borrow] - VestingWallet vesting_wallet; - } +#[entrypoint] +#[storage] +struct VestingWalletExample { + #[borrow] + pub vesting_wallet: VestingWallet, } #[public] diff --git a/lib/motsu-proc/src/test.rs b/lib/motsu-proc/src/test.rs index 2e2425d1e..2e88961cd 100644 --- a/lib/motsu-proc/src/test.rs +++ b/lib/motsu-proc/src/test.rs @@ -3,7 +3,7 @@ use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, FnArg}; -/// Defines a unit test that provides access to Stylus' execution context. +/// Defines a unit test that provides access to Stylus execution context. /// /// For more information see [`crate::test`]. pub(crate) fn test(_attr: &TokenStream, input: TokenStream) -> TokenStream { @@ -21,31 +21,49 @@ pub(crate) fn test(_attr: &TokenStream, input: TokenStream) -> TokenStream { } // Whether 1 or none contracts will be declared. - let contract_declarations = fn_args.into_iter().map(|arg| { - let FnArg::Typed(arg) = arg else { - error!(arg, "unexpected receiver argument in test signature"); - }; - let contract_arg_binding = &arg.pat; - let contract_ty = &arg.ty; + let arg_binding_and_ty = match fn_args + .into_iter() + .map(|arg| { + let FnArg::Typed(arg) = arg else { + error!(@arg, "unexpected receiver argument in test signature"); + }; + let contract_arg_binding = &arg.pat; + let contract_ty = &arg.ty; + Ok((contract_arg_binding, contract_ty)) + }) + .collect::, _>>() + { + Ok(res) => res, + Err(err) => return err.to_compile_error().into(), + }; - // Test case assumes, that contract's variable has `&mut` reference - // to contract's type. - quote! { - let mut #contract_arg_binding = <#contract_ty>::default(); - let #contract_arg_binding = &mut #contract_arg_binding; - } - }); + let contract_arg_defs = + arg_binding_and_ty.iter().map(|(arg_binding, contract_ty)| { + // Test case assumes, that contract's variable has `&mut` reference + // to contract's type. + quote! { + #arg_binding: &mut #contract_ty + } + }); + + let contract_args = + arg_binding_and_ty.iter().map(|(_arg_binding, contract_ty)| { + // Pass mutable reference to the contract. + quote! { + &mut <#contract_ty>::default() + } + }); - // Output full testcase function. - // Declare contract. - // And in the end, reset storage for test context. + // Declare test case closure. + // Pass mut ref to the test closure and call it. + // Reset storage for the test context and return test's output. quote! { #( #attrs )* #[test] fn #fn_name() #fn_return_type { use ::motsu::prelude::DefaultStorage; - #( #contract_declarations )* - let res = #fn_block; + let test = | #( #contract_arg_defs ),* | #fn_block; + let res = test( #( #contract_args ),* ); ::motsu::prelude::Context::current().reset_storage(); res } diff --git a/lib/motsu/README.md b/lib/motsu/README.md index f7e8c074c..3356e2038 100644 --- a/lib/motsu/README.md +++ b/lib/motsu/README.md @@ -13,7 +13,8 @@ Annotate tests with `#[motsu::test]` instead of `#[test]` to get access to VM affordances. Note that we require contracts to implement `stylus_sdk::prelude::StorageType`. -This trait is typically implemented by default with `stylus_proc::sol_storage` macro. +This trait is typically implemented by default with `stylus_proc::sol_storage` +or `stylus_proc::storage` macros. ```rust #[cfg(test)] diff --git a/lib/motsu/src/lib.rs b/lib/motsu/src/lib.rs index 88eb49707..7636b7541 100644 --- a/lib/motsu/src/lib.rs +++ b/lib/motsu/src/lib.rs @@ -14,7 +14,7 @@ //! //! Note that we require contracts to implement //! `stylus_sdk::prelude::StorageType`. This trait is typically implemented by -//! default with `stylus_proc::sol_storage` macro. +//! default with `stylus_proc::sol_storage` or `stylus_proc::storage` macros. //! //! ```rust //! #[cfg(test)]