diff --git a/CHANGELOG.md b/CHANGELOG.md index abd9f1c28..9f2808451 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +- + +### Changed + +- Use `AddAssignUnchecked` and `SubAssignUnchecked` in `erc20::_update` calculations. #467 + +### Changed (Breaking) + +- `Nonce::use_nonce` now panics on exceeding `U256::MAX`. #467 + +### Fixed + +- + ## [v0.2.0-alpha.2] - 2024-12-18 ### Added diff --git a/contracts/src/access/control.rs b/contracts/src/access/control.rs index 216b794f5..321338bd8 100644 --- a/contracts/src/access/control.rs +++ b/contracts/src/access/control.rs @@ -119,6 +119,7 @@ pub struct RoleData { #[storage] pub struct AccessControl { /// Role identifier -> Role information. + #[allow(clippy::used_underscore_binding)] pub _roles: StorageMap, RoleData>, } diff --git a/contracts/src/access/ownable.rs b/contracts/src/access/ownable.rs index 7caf659d7..2e0cbec87 100644 --- a/contracts/src/access/ownable.rs +++ b/contracts/src/access/ownable.rs @@ -67,6 +67,7 @@ impl MethodError for Error { #[storage] pub struct Ownable { /// The current owner of this contract. + #[allow(clippy::used_underscore_binding)] pub _owner: StorageAddress, } diff --git a/contracts/src/access/ownable_two_step.rs b/contracts/src/access/ownable_two_step.rs index c3f8543ea..0cd9503b7 100644 --- a/contracts/src/access/ownable_two_step.rs +++ b/contracts/src/access/ownable_two_step.rs @@ -59,8 +59,10 @@ pub enum Error { #[storage] pub struct Ownable2Step { /// [`Ownable`] contract. + #[allow(clippy::used_underscore_binding)] pub _ownable: Ownable, /// Pending owner of the contract. + #[allow(clippy::used_underscore_binding)] pub _pending_owner: StorageAddress, } diff --git a/contracts/src/finance/vesting_wallet.rs b/contracts/src/finance/vesting_wallet.rs index e065c7b76..03e25c3b8 100644 --- a/contracts/src/finance/vesting_wallet.rs +++ b/contracts/src/finance/vesting_wallet.rs @@ -107,12 +107,16 @@ pub struct VestingWallet { /// [`Ownable`] contract. pub ownable: Ownable, /// Amount of Ether already released. + #[allow(clippy::used_underscore_binding)] pub _released: StorageU256, /// Amount of ERC-20 tokens already released. + #[allow(clippy::used_underscore_binding)] pub _erc20_released: StorageMap, /// Start timestamp. + #[allow(clippy::used_underscore_binding)] pub _start: StorageU64, /// Vesting duration. + #[allow(clippy::used_underscore_binding)] pub _duration: StorageU64, /// [`SafeErc20`] contract. pub safe_erc20: SafeErc20, diff --git a/contracts/src/token/erc1155/extensions/metadata_uri.rs b/contracts/src/token/erc1155/extensions/metadata_uri.rs index a77eaa0ff..44c92685f 100644 --- a/contracts/src/token/erc1155/extensions/metadata_uri.rs +++ b/contracts/src/token/erc1155/extensions/metadata_uri.rs @@ -36,6 +36,7 @@ mod sol { pub struct Erc1155MetadataUri { /// Used as the URI for all token types by relying on ID substitution, /// e.g. https://token-cdn-domain/{id}.json. + #[allow(clippy::used_underscore_binding)] pub _uri: StorageString, } diff --git a/contracts/src/token/erc1155/extensions/supply.rs b/contracts/src/token/erc1155/extensions/supply.rs index 80e9fd61f..5ae0e5b83 100644 --- a/contracts/src/token/erc1155/extensions/supply.rs +++ b/contracts/src/token/erc1155/extensions/supply.rs @@ -33,8 +33,10 @@ pub struct Erc1155Supply { /// ERC-1155 contract storage. pub erc1155: Erc1155, /// Mapping from token id to total supply. + #[allow(clippy::used_underscore_binding)] pub _total_supply: StorageMap, /// Total supply of all token ids. + #[allow(clippy::used_underscore_binding)] pub _total_supply_all: StorageU256, } diff --git a/contracts/src/token/erc1155/extensions/uri_storage.rs b/contracts/src/token/erc1155/extensions/uri_storage.rs index 442c260fa..f1d90ea67 100644 --- a/contracts/src/token/erc1155/extensions/uri_storage.rs +++ b/contracts/src/token/erc1155/extensions/uri_storage.rs @@ -16,8 +16,10 @@ use super::metadata_uri::{IErc1155MetadataUri, URI}; #[storage] pub struct Erc1155UriStorage { /// Optional base URI. + #[allow(clippy::used_underscore_binding)] pub _base_uri: StorageString, /// Optional mapping for token URIs. + #[allow(clippy::used_underscore_binding)] pub _token_uris: StorageMap, } diff --git a/contracts/src/token/erc1155/mod.rs b/contracts/src/token/erc1155/mod.rs index c0dd5dcfd..4b6545219 100644 --- a/contracts/src/token/erc1155/mod.rs +++ b/contracts/src/token/erc1155/mod.rs @@ -188,8 +188,10 @@ impl MethodError for Error { #[storage] pub struct Erc1155 { /// Maps users to balances. + #[allow(clippy::used_underscore_binding)] pub _balances: StorageMap>, /// Maps owners to a mapping of operator approvals. + #[allow(clippy::used_underscore_binding)] pub _operator_approvals: StorageMap>, } diff --git a/contracts/src/token/erc20/extensions/capped.rs b/contracts/src/token/erc20/extensions/capped.rs index 1e38e7f40..f8d58d990 100644 --- a/contracts/src/token/erc20/extensions/capped.rs +++ b/contracts/src/token/erc20/extensions/capped.rs @@ -47,6 +47,7 @@ pub enum Error { #[storage] pub struct Capped { /// A cap to the supply of tokens. + #[allow(clippy::used_underscore_binding)] pub _cap: StorageU256, } diff --git a/contracts/src/token/erc20/extensions/metadata.rs b/contracts/src/token/erc20/extensions/metadata.rs index 20f8f99fc..813a2e548 100644 --- a/contracts/src/token/erc20/extensions/metadata.rs +++ b/contracts/src/token/erc20/extensions/metadata.rs @@ -19,6 +19,7 @@ use crate::utils::Metadata; #[storage] pub struct Erc20Metadata { /// Common Metadata. + #[allow(clippy::used_underscore_binding)] pub _metadata: Metadata, } diff --git a/contracts/src/token/erc20/mod.rs b/contracts/src/token/erc20/mod.rs index dca8fd755..92bc99f55 100644 --- a/contracts/src/token/erc20/mod.rs +++ b/contracts/src/token/erc20/mod.rs @@ -14,7 +14,10 @@ use stylus_sdk::{ stylus_proc::{public, SolidityError}, }; -use crate::utils::introspection::erc165::{Erc165, IErc165}; +use crate::utils::{ + introspection::erc165::{Erc165, IErc165}, + math::storage::{AddAssignUnchecked, SubAssignUnchecked}, +}; pub mod extensions; pub mod utils; @@ -123,10 +126,13 @@ impl MethodError for Error { #[storage] pub struct Erc20 { /// Maps users to balances. + #[allow(clippy::used_underscore_binding)] pub _balances: StorageMap, /// Maps users to a mapping of each spender's allowance. + #[allow(clippy::used_underscore_binding)] pub _allowances: StorageMap>, /// The total supply of the token. + #[allow(clippy::used_underscore_binding)] pub _total_supply: StorageU256, } @@ -489,17 +495,15 @@ impl Erc20 { } if to.is_zero() { - let total_supply = self.total_supply(); // Overflow not possible: // `value` <= `_total_supply` or // `value` <= `from_balance` <= `_total_supply`. - self._total_supply.set(total_supply - value); + self._total_supply.sub_assign_unchecked(value); } else { - let balance_to = self._balances.get(to); // Overflow not possible: // `balance_to` + `value` is at most `total_supply`, // which fits into a `U256`. - self._balances.setter(to).set(balance_to + value); + self._balances.setter(to).add_assign_unchecked(value); } evm::log(Transfer { from, to, value }); diff --git a/contracts/src/token/erc721/extensions/consecutive.rs b/contracts/src/token/erc721/extensions/consecutive.rs index c0c458fa6..3ed58a3f7 100644 --- a/contracts/src/token/erc721/extensions/consecutive.rs +++ b/contracts/src/token/erc721/extensions/consecutive.rs @@ -62,16 +62,20 @@ pub struct Erc721Consecutive { /// Erc721 contract storage. pub erc721: Erc721, /// Checkpoint library contract for sequential ownership. + #[allow(clippy::used_underscore_binding)] pub _sequential_ownership: Trace, /// BitMap library contract for sequential burn of tokens. + #[allow(clippy::used_underscore_binding)] pub _sequential_burn: BitMap, /// Used to offset the first token id in /// [`Erc721Consecutive::_next_consecutive_id`]. + #[allow(clippy::used_underscore_binding)] 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. + #[allow(clippy::used_underscore_binding)] pub _max_batch_size: StorageU96, } diff --git a/contracts/src/token/erc721/extensions/enumerable.rs b/contracts/src/token/erc721/extensions/enumerable.rs index 936009e12..25ba7618a 100644 --- a/contracts/src/token/erc721/extensions/enumerable.rs +++ b/contracts/src/token/erc721/extensions/enumerable.rs @@ -62,12 +62,16 @@ pub enum Error { #[storage] pub struct Erc721Enumerable { /// Maps owners to a mapping of indices to tokens ids. + #[allow(clippy::used_underscore_binding)] pub _owned_tokens: StorageMap>, /// Maps tokens ids to indices in `_owned_tokens`. + #[allow(clippy::used_underscore_binding)] pub _owned_tokens_index: StorageMap, /// Stores all tokens ids. + #[allow(clippy::used_underscore_binding)] pub _all_tokens: StorageVec, /// Maps indices at `_all_tokens` to tokens ids. + #[allow(clippy::used_underscore_binding)] pub _all_tokens_index: StorageMap, } diff --git a/contracts/src/token/erc721/extensions/metadata.rs b/contracts/src/token/erc721/extensions/metadata.rs index f85efb331..e1a50ad8e 100644 --- a/contracts/src/token/erc721/extensions/metadata.rs +++ b/contracts/src/token/erc721/extensions/metadata.rs @@ -17,8 +17,10 @@ use crate::{ #[storage] pub struct Erc721Metadata { /// Common Metadata. + #[allow(clippy::used_underscore_binding)] pub _metadata: Metadata, /// Base URI for tokens. + #[allow(clippy::used_underscore_binding)] pub _base_uri: StorageString, } diff --git a/contracts/src/token/erc721/extensions/uri_storage.rs b/contracts/src/token/erc721/extensions/uri_storage.rs index 704f4e64b..90065c162 100644 --- a/contracts/src/token/erc721/extensions/uri_storage.rs +++ b/contracts/src/token/erc721/extensions/uri_storage.rs @@ -37,6 +37,7 @@ mod sol { #[storage] pub struct Erc721UriStorage { /// Optional mapping for token URIs. + #[allow(clippy::used_underscore_binding)] pub _token_uris: StorageMap, } diff --git a/contracts/src/token/erc721/mod.rs b/contracts/src/token/erc721/mod.rs index 2747366cd..1057e7fce 100644 --- a/contracts/src/token/erc721/mod.rs +++ b/contracts/src/token/erc721/mod.rs @@ -197,12 +197,16 @@ mod receiver { #[storage] pub struct Erc721 { /// Maps tokens to owners. + #[allow(clippy::used_underscore_binding)] pub _owners: StorageMap, /// Maps users to balances. + #[allow(clippy::used_underscore_binding)] pub _balances: StorageMap, /// Maps tokens to approvals. + #[allow(clippy::used_underscore_binding)] pub _token_approvals: StorageMap, /// Maps owners to a mapping of operator approvals. + #[allow(clippy::used_underscore_binding)] pub _operator_approvals: StorageMap>, } diff --git a/contracts/src/utils/metadata.rs b/contracts/src/utils/metadata.rs index fe42e017c..b20497858 100644 --- a/contracts/src/utils/metadata.rs +++ b/contracts/src/utils/metadata.rs @@ -9,8 +9,10 @@ use stylus_sdk::{ #[storage] pub struct Metadata { /// Token name. + #[allow(clippy::used_underscore_binding)] pub _name: StorageString, /// Token symbol. + #[allow(clippy::used_underscore_binding)] pub _symbol: StorageString, } diff --git a/contracts/src/utils/nonces.rs b/contracts/src/utils/nonces.rs index 4627b4ea5..8406cd81e 100644 --- a/contracts/src/utils/nonces.rs +++ b/contracts/src/utils/nonces.rs @@ -20,7 +20,7 @@ mod sol { /// The nonce used for an `account` is not the expected current nonce. #[derive(Debug)] #[allow(missing_docs)] - error InvalidAccountNonce(address account, uint256 currentNonce); + error InvalidAccountNonce(address account, uint256 current_nonce); } } @@ -35,6 +35,7 @@ pub enum Error { #[storage] pub struct Nonces { /// Mapping from address to its nonce. + #[allow(clippy::used_underscore_binding)] pub _nonces: StorageMap, } @@ -62,14 +63,13 @@ impl Nonces { /// /// # Panics /// - /// This function will panic if the nonce for the given `owner` has reached - /// the maximum value representable by `U256`, causing the `checked_add` - /// method to return `None`. + /// If the nonce for the given `owner` exceeds `U256::MAX`. pub fn use_nonce(&mut self, owner: Address) -> U256 { let nonce = self._nonces.get(owner); - self._nonces - .setter(owner) - .set(unsafe { nonce.checked_add(ONE).unwrap_unchecked() }); + let updated_nonce = nonce + .checked_add(ONE) + .expect("nonce should not exceed `U256::MAX`"); + self._nonces.setter(owner).set(updated_nonce); nonce } @@ -83,34 +83,28 @@ impl Nonces { /// * `owner` - The address for which to consume the nonce. /// * `nonce` - The nonce to consume. /// - /// # Panics - /// - /// This function will panic if the nonce for the given `owner` has reached - /// the maximum value representable by `U256`, causing the `checked_add` - /// method to return `None`. - /// /// # Errors /// /// Returns an error if the `nonce` is not the next valid nonce for the /// owner. + /// + /// # Panics + /// + /// If the nonce for the given `owner` exceeds `U256::MAX`. pub fn use_checked_nonce( &mut self, owner: Address, nonce: U256, ) -> Result<(), Error> { - let current_nonce = self._nonces.get(owner); + let current_nonce = self.use_nonce(owner); if nonce != current_nonce { return Err(Error::InvalidAccountNonce(InvalidAccountNonce { account: owner, - currentNonce: current_nonce, + current_nonce, })); } - self._nonces - .setter(owner) - .set(unsafe { nonce.checked_add(ONE).unwrap_unchecked() }); - Ok(()) } } diff --git a/contracts/src/utils/pausable.rs b/contracts/src/utils/pausable.rs index 33e0421c6..5e48649cb 100644 --- a/contracts/src/utils/pausable.rs +++ b/contracts/src/utils/pausable.rs @@ -66,6 +66,7 @@ pub enum Error { #[storage] pub struct Pausable { /// Indicates whether the contract is `Paused`. + #[allow(clippy::used_underscore_binding)] pub _paused: StorageBool, } diff --git a/contracts/src/utils/structs/bitmap.rs b/contracts/src/utils/structs/bitmap.rs index 821bf9b19..40f808a18 100644 --- a/contracts/src/utils/structs/bitmap.rs +++ b/contracts/src/utils/structs/bitmap.rs @@ -26,6 +26,7 @@ const HEX_FF: U256 = uint!(0xff_U256); #[storage] pub struct BitMap { /// Inner laying mapping. + #[allow(clippy::used_underscore_binding)] pub _data: StorageMap, } diff --git a/contracts/src/utils/structs/checkpoints/mod.rs b/contracts/src/utils/structs/checkpoints/mod.rs index ee99b586f..81d522f58 100644 --- a/contracts/src/utils/structs/checkpoints/mod.rs +++ b/contracts/src/utils/structs/checkpoints/mod.rs @@ -51,6 +51,7 @@ impl MethodError for Error { #[storage] pub struct Trace { /// Stores checkpoints in a dynamic array sorted by key. + #[allow(clippy::used_underscore_binding)] pub _checkpoints: StorageVec>, } @@ -58,8 +59,10 @@ pub struct Trace { #[storage] pub struct Checkpoint { /// The key of the checkpoint. Used as a sorting key. + #[allow(clippy::used_underscore_binding)] pub _key: S::KeyStorage, /// The value corresponding to the key. + #[allow(clippy::used_underscore_binding)] pub _value: S::ValueStorage, } diff --git a/examples/erc1155/src/lib.rs b/examples/erc1155/src/lib.rs index 8666b4004..540f31532 100644 --- a/examples/erc1155/src/lib.rs +++ b/examples/erc1155/src/lib.rs @@ -101,6 +101,10 @@ impl Erc1155Example { Erc1155::supports_interface(interface_id) } + /// WARNING: These functions are intended for **testing purposes** only. In + /// **production**, ensure strict access control to prevent unauthorized + /// pausing or unpausing, which can disrupt contract functionality. Remove + /// or secure these functions before deployment. fn pause(&mut self) -> Result<(), Vec> { self.pausable.pause().map_err(|e| e.into()) } diff --git a/examples/erc20/src/lib.rs b/examples/erc20/src/lib.rs index 1f4d01db1..5a1ee4ae3 100644 --- a/examples/erc20/src/lib.rs +++ b/examples/erc20/src/lib.rs @@ -110,6 +110,10 @@ impl Erc20Example { || Erc20Metadata::supports_interface(interface_id) } + /// WARNING: These functions are intended for **testing purposes** only. In + /// **production**, ensure strict access control to prevent unauthorized + /// pausing or unpausing, which can disrupt contract functionality. Remove + /// or secure these functions before deployment. pub fn pause(&mut self) -> Result<(), Vec> { self.pausable.pause().map_err(|e| e.into()) } diff --git a/examples/erc721/src/lib.rs b/examples/erc721/src/lib.rs index cb71c5ff8..d4e675f2a 100644 --- a/examples/erc721/src/lib.rs +++ b/examples/erc721/src/lib.rs @@ -156,6 +156,10 @@ impl Erc721Example { || Enumerable::supports_interface(interface_id) } + /// WARNING: These functions are intended for **testing purposes** only. In + /// **production**, ensure strict access control to prevent unauthorized + /// pausing or unpausing, which can disrupt contract functionality. Remove + /// or secure these functions before deployment. pub fn pause(&mut self) -> Result<(), Vec> { self.pausable.pause().map_err(|e| e.into()) } diff --git a/lib/crypto/src/poseidon2/mod.rs b/lib/crypto/src/poseidon2/mod.rs index 6933f9299..ade136014 100644 --- a/lib/crypto/src/poseidon2/mod.rs +++ b/lib/crypto/src/poseidon2/mod.rs @@ -23,8 +23,8 @@ enum Mode { Squeezing, } -/// Poseidon2 sponge that can absorb any number of [`F`] field elements and be -/// squeezed to a finite number of [`F`] field elements. +/// Poseidon2 sponge that can absorb any number of `F` field elements and be +/// squeezed to a finite number of `F` field elements. #[derive(Clone, Debug)] pub struct Poseidon2, F: PrimeField> { phantom: core::marker::PhantomData

, diff --git a/lib/e2e/README.md b/lib/e2e/README.md index 79a222399..2e7775d5b 100644 --- a/lib/e2e/README.md +++ b/lib/e2e/README.md @@ -108,7 +108,7 @@ Then altogether, your first test case can look like this: sol!("src/constructor.sol") #[e2e::test] -async fn constructs(alice: Account) -> Result<()> { +async fn constructs(alice: Account) -> eyre::Result<()> { let ctr = Example::constructorCall { name_: "Token".to_owned(), symbol_: "TKN".to_owned(), diff --git a/lib/e2e/src/account.rs b/lib/e2e/src/account.rs index 96c3485fb..6d1f0a748 100644 --- a/lib/e2e/src/account.rs +++ b/lib/e2e/src/account.rs @@ -4,7 +4,6 @@ use alloy::{ providers::{Provider, ProviderBuilder}, signers::{local::PrivateKeySigner, Signature, Signer}, }; -use eyre::Result; use once_cell::sync::Lazy; use tokio::sync::{Mutex, MutexGuard}; @@ -30,7 +29,7 @@ impl Account { /// # Errors /// /// May fail if funding the newly created account fails. - pub async fn new() -> Result { + pub async fn new() -> eyre::Result { AccountFactory::create().await } diff --git a/scripts/e2e-tests.sh b/scripts/e2e-tests.sh index aaa7e5731..71bf41234 100755 --- a/scripts/e2e-tests.sh +++ b/scripts/e2e-tests.sh @@ -1,14 +1,16 @@ #!/bin/bash set -e -TEST_ARG="${1:-*}" - -MYDIR=$(realpath "$(dirname "$0")") -cd "$MYDIR" -cd .. +# Navigate to project root +cd "$(dirname "$(realpath "$0")")/.." cargo build --release --target wasm32-unknown-unknown -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort export RPC_URL=http://localhost:8547 -cargo test --features std,e2e --test "$TEST_ARG" +# If any arguments are set, just pass them as-is to the cargo test command +if [[ $# -eq 0 ]]; then + cargo test --features e2e --test "*" +else + cargo test --features e2e "$@" +fi