diff --git a/Cargo.toml b/Cargo.toml index 646d66a..35ad200 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,8 @@ pretty_assertions = "1" tokio = "1" [workspace.lints.clippy] -# pedantic = "warn" +pedantic = "warn" +module-name-repetitions = "allow" [workspace.lints.rust] missing-docs = "warn" diff --git a/macros/src/escrow.rs b/macros/src/escrow.rs index 6cbf326..6669d90 100644 --- a/macros/src/escrow.rs +++ b/macros/src/escrow.rs @@ -43,9 +43,7 @@ pub fn expand(meta: EscrowMeta) -> Result { } }); - let state = state - .map(|state| quote! { #state }) - .unwrap_or_else(|| quote! { () }); + let state = state.map_or_else(|| quote! { () }, |state| quote! { #state }); Ok(quote! { impl #imp #me::escrow::EscrowInternal for #ident #ty #wher { diff --git a/macros/src/lib.rs b/macros/src/lib.rs index b4c6790..86b43a7 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,3 +1,4 @@ +#![allow(clippy::too_many_lines, clippy::unnecessary_wraps)] //! Macros for near-sdk-contract-tools. use darling::{ast::NestedMeta, FromDeriveInput, FromMeta}; @@ -45,8 +46,7 @@ where FromDeriveInput::from_derive_input(&input) .and_then(expand) - .map(Into::into) - .unwrap_or_else(|e| e.write_errors().into()) + .map_or_else(|e| e.write_errors().into(), Into::into) } /// Use on a struct to emit NEP-297 event strings. @@ -257,9 +257,8 @@ pub fn event(attr: TokenStream, item: TokenStream) -> TokenStream { let item = parse_macro_input!(item as Item); standard::event::EventAttributeMeta::from_list(&attr) - .and_then(|meta| standard::event::event_attribute(meta, item)) - .map(Into::into) - .unwrap_or_else(|e| e.write_errors().into()) + .and_then(|meta| standard::event::event_attribute(meta, &item)) + .map_or_else(|e| e.write_errors().into(), Into::into) } /// Create an upgrade component. Does not expose any functions to the diff --git a/macros/src/migrate.rs b/macros/src/migrate.rs index ec7a38a..6ea1b36 100644 --- a/macros/src/migrate.rs +++ b/macros/src/migrate.rs @@ -32,9 +32,10 @@ pub fn expand(meta: MigrateMeta) -> Result { let (imp, ty, wh) = generics.split_for_impl(); - let to = to - .map(|t| t.to_token_stream()) - .unwrap_or_else(|| quote! { Self }.to_token_stream()); + let to = to.map_or_else( + || quote! { Self }.to_token_stream(), + |t| t.to_token_stream(), + ); Ok(quote! { impl #imp #me::migrate::MigrateController for #ident #ty #wh { diff --git a/macros/src/rename.rs b/macros/src/rename.rs index 52355ec..1c47af9 100644 --- a/macros/src/rename.rs +++ b/macros/src/rename.rs @@ -36,7 +36,7 @@ impl RenameStrategy { impl FromMeta for RenameStrategy { fn from_string(value: &str) -> darling::Result { RenameStrategy::try_from(value) - .map_err(|_| darling::Error::custom("Invalid rename strategy")) + .map_err(|()| darling::Error::custom("Invalid rename strategy")) } } diff --git a/macros/src/standard/event.rs b/macros/src/standard/event.rs index 2fcf2a4..627f12f 100644 --- a/macros/src/standard/event.rs +++ b/macros/src/standard/event.rs @@ -23,7 +23,7 @@ pub struct EventAttributeMeta { pub fn event_attribute( attr: EventAttributeMeta, - item: Item, + item: &Item, ) -> Result { let EventAttributeMeta { standard, diff --git a/macros/src/standard/nep141.rs b/macros/src/standard/nep141.rs index b04dea9..bc1778e 100644 --- a/macros/src/standard/nep141.rs +++ b/macros/src/standard/nep141.rs @@ -45,19 +45,11 @@ pub fn expand(meta: Nep141Meta) -> Result { } }); - let mint_hook = mint_hook - .map(|h| quote! { #h }) - .unwrap_or_else(|| quote! { () }); - let transfer_hook = transfer_hook - .map(|h| quote! { #h }) - .unwrap_or_else(|| quote! { () }); - let burn_hook = burn_hook - .map(|h| quote! { #h }) - .unwrap_or_else(|| quote! { () }); - - let default_hook = all_hooks - .map(|h| quote! { #h }) - .unwrap_or_else(|| quote! { () }); + let mint_hook = mint_hook.map_or_else(|| quote! { () }, |h| quote! { #h }); + let transfer_hook = transfer_hook.map_or_else(|| quote! { () }, |h| quote! { #h }); + let burn_hook = burn_hook.map_or_else(|| quote! { () }, |h| quote! { #h }); + + let default_hook = all_hooks.map_or_else(|| quote! { () }, |h| quote! { #h }); Ok(quote! { impl #imp #me::standard::nep141::Nep141ControllerInternal for #ident #ty #wher { diff --git a/macros/src/standard/nep145.rs b/macros/src/standard/nep145.rs index 5ff7230..aa6b42c 100644 --- a/macros/src/standard/nep145.rs +++ b/macros/src/standard/nep145.rs @@ -41,12 +41,9 @@ pub fn expand(meta: Nep145Meta) -> Result { } }); - let all_hooks = all_hooks - .map(|h| quote! { #h }) - .unwrap_or_else(|| quote! { () }); - let force_unregister_hook = force_unregister_hook - .map(|h| quote! { #h }) - .unwrap_or_else(|| quote! { () }); + let all_hooks = all_hooks.map_or_else(|| quote! { () }, |h| quote! { #h }); + let force_unregister_hook = + force_unregister_hook.map_or_else(|| quote! { () }, |h| quote! { #h }); Ok(quote! { impl #imp #me::standard::nep145::Nep145ControllerInternal for #ident #ty #wher { diff --git a/macros/src/standard/nep297.rs b/macros/src/standard/nep297.rs index dd1997a..b2af413 100644 --- a/macros/src/standard/nep297.rs +++ b/macros/src/standard/nep297.rs @@ -140,12 +140,12 @@ pub fn expand(meta: Nep297Meta) -> Result { let mut e = darling::Error::accumulator(); let mut no_duplicate_names = HashSet::<&String>::new(); - for used_name in used_names.iter() { + for used_name in &used_names { let fresh_insertion = no_duplicate_names.insert(used_name); if !fresh_insertion { e.push(darling::Error::custom(format!( "Event name collision: `{used_name}`", - ))) + ))); } } diff --git a/macros/src/upgrade.rs b/macros/src/upgrade.rs index 2a02a30..a7fc77d 100644 --- a/macros/src/upgrade.rs +++ b/macros/src/upgrade.rs @@ -93,15 +93,18 @@ pub fn expand(meta: UpgradeMeta) -> Result { // Defaults are defined in main crate. // I don't think these defaults can be easily defined using // #[darling(default = "...")] because they are different types. - let migrate_method_name = migrate_method_name - .map(|e| quote! { #e }) - .unwrap_or_else(|| quote! { #me::upgrade::DEFAULT_POST_UPGRADE_METHOD_NAME }); - let migrate_method_args = migrate_method_args - .map(|e| quote! { #e }) - .unwrap_or_else(|| quote! { #me::upgrade::DEFAULT_POST_UPGRADE_METHOD_ARGS }); - let migrate_minimum_gas = migrate_minimum_gas - .map(|e| quote! { #e }) - .unwrap_or_else(|| quote! { #me::upgrade::DEFAULT_POST_UPGRADE_MINIMUM_GAS }); + let migrate_method_name = migrate_method_name.map_or_else( + || quote! { #me::upgrade::DEFAULT_POST_UPGRADE_METHOD_NAME }, + |e| quote! { #e }, + ); + let migrate_method_args = migrate_method_args.map_or_else( + || quote! { #me::upgrade::DEFAULT_POST_UPGRADE_METHOD_ARGS }, + |e| quote! { #e }, + ); + let migrate_minimum_gas = migrate_minimum_gas.map_or_else( + || quote! { #me::upgrade::DEFAULT_POST_UPGRADE_MINIMUM_GAS }, + |e| quote! { #e }, + ); let hook_implementation = match &hook { // Should we generate an UpgradeHook implementation with body? diff --git a/src/approval/mod.rs b/src/approval/mod.rs index 2684764..0a287f0 100644 --- a/src/approval/mod.rs +++ b/src/approval/mod.rs @@ -26,35 +26,51 @@ pub trait Action { fn execute(self, contract: &mut Cont) -> Self::Output; } -/// Defines the operating parameters for an ApprovalManager and performs -/// approvals +/// Defines the operating parameters for an `ApprovalManager` and performs +/// approvals. pub trait ApprovalConfiguration { - /// Errors when approving a request + /// Errors when approving a request. type ApprovalError; - /// Errors when removing a request + /// Errors when removing a request. type RemovalError; - /// Errors when authorizing an account + /// Errors when authorizing an account. type AuthorizationError; - /// Errors when evaluating a request for execution candidacy + /// Errors when evaluating a request for execution candidacy. type ExecutionEligibilityError; /// Has the request reached full approval? + /// + /// # Errors + /// + /// Returns an error if the request is not approved for execution. fn is_approved_for_execution( &self, action_request: &ActionRequest, ) -> Result<(), Self::ExecutionEligibilityError>; /// Can this request be removed by an allowed account? + /// + /// # Errors + /// + /// Returns an error if the request cannot be removed. fn is_removable(&self, action_request: &ActionRequest) -> Result<(), Self::RemovalError>; /// Is the account allowed to execute, approve, or remove this request? + /// + /// # Errors + /// + /// Returns an error if the account is not allowed to perform such an action. fn is_account_authorized( &self, account_id: &AccountId, action_request: &ActionRequest, ) -> Result<(), Self::AuthorizationError>; - /// Modify action_request.approval_state in-place to increase approval + /// Modify `action_request.approval_state` in-place to increase approval. + /// + /// # Errors + /// + /// Returns an error if the request cannot be approved. fn try_approve_with_authorized_account( &self, account_id: AccountId, @@ -135,22 +151,26 @@ where C: ApprovalConfiguration + BorshDeserialize + BorshSerialize, { /// Storage root + #[must_use] fn root() -> Slot<()> { Slot::new(DefaultStorageKey::ApprovalManager) } /// Because requests will be deleted from the requests collection, /// maintain a simple counter to guarantee unique IDs + #[must_use] fn slot_next_request_id() -> Slot { Self::root().field(ApprovalStorageKey::NextRequestId) } /// Approval context included in relevant approval-related calls + #[must_use] fn slot_config() -> Slot { Self::root().field(ApprovalStorageKey::Config) } /// Current list of pending action requests. + #[must_use] fn slot_request(request_id: u32) -> Slot> { Self::root().field(ApprovalStorageKey::Request(request_id)) } @@ -175,7 +195,11 @@ where /// once. fn init(config: C); - /// Creates a new action request initialized with the given approval state + /// Creates a new action request initialized with the given approval state. + /// + /// # Errors + /// + /// - If the acting account is unauthorized. fn create_request( &mut self, action: A, @@ -184,6 +208,11 @@ where /// Executes an action request and removes it from the collection if the /// approval state of the request is fulfilled. + /// + /// # Errors + /// + /// - If the acting account is unauthorized. + /// - If the request is ineligible for execution. fn execute_request( &mut self, request_id: u32, @@ -191,16 +220,30 @@ where /// Is the given request ID able to be executed if such a request were to /// be initiated by an authorized account? + /// + /// # Errors + /// + /// - If the request is ineligible for execution. fn is_approved_for_execution(request_id: u32) -> Result<(), C::ExecutionEligibilityError>; /// Tries to approve the action request designated by the given request ID /// with the given arguments. Panics if the request ID does not exist. + /// + /// # Errors + /// + /// - If the acting account is unauthorized. + /// - If another error was encountered when approving the request. fn approve_request( &mut self, request_id: u32, ) -> Result<(), ApprovalError>; - /// Tries to remove the action request indicated by request_id. + /// Tries to remove the action request indicated by `request_id`. + /// + /// # Errors + /// + /// - If the acting account is unauthorized. + /// - If the request cannot be removed. fn remove_request( &mut self, request_id: u32, @@ -495,7 +538,7 @@ mod tests { predecessor(&alice); let request_id = contract - .create_request(MyAction::SayHello, Default::default()) + .create_request(MyAction::SayHello, MultisigApprovalState::default()) .unwrap(); assert_eq!(request_id, 0); @@ -524,7 +567,7 @@ mod tests { predecessor(&alice); let request_id = contract - .create_request(MyAction::SayHello, Default::default()) + .create_request(MyAction::SayHello, MultisigApprovalState::default()) .unwrap(); contract.approve_request(request_id).unwrap(); @@ -544,7 +587,7 @@ mod tests { predecessor(&alice); let request_id = contract - .create_request(MyAction::SayHello, Default::default()) + .create_request(MyAction::SayHello, MultisigApprovalState::default()) .unwrap(); contract.approve_request(request_id).unwrap(); @@ -565,7 +608,7 @@ mod tests { predecessor(&alice); let request_id = contract - .create_request(MyAction::SayHello, Default::default()) + .create_request(MyAction::SayHello, MultisigApprovalState::default()) .unwrap(); contract.approve_request(request_id).unwrap(); @@ -589,7 +632,7 @@ mod tests { predecessor(&alice); let request_id = contract - .create_request(MyAction::SayGoodbye, Default::default()) + .create_request(MyAction::SayGoodbye, MultisigApprovalState::default()) .unwrap(); contract.approve_request(request_id).unwrap(); diff --git a/src/approval/native_transaction_action.rs b/src/approval/native_transaction_action.rs index ddc96db..34edd4a 100644 --- a/src/approval/native_transaction_action.rs +++ b/src/approval/native_transaction_action.rs @@ -76,7 +76,7 @@ pub enum PromiseAction { } /// A native protocol-level transaction that (de)serializes into many different -/// formats +/// formats. #[derive(Eq, PartialEq, Clone, Debug)] #[near(serializers = [borsh, json])] pub struct NativeTransactionAction { @@ -107,12 +107,12 @@ impl super::Action for NativeTransactionAction { .unwrap_or(near_sdk::Allowance::Unlimited), receiver_id, function_names.join(","), - nonce.map(Into::into).unwrap_or(0), + nonce.map_or(0, Into::into), ), PromiseAction::AddFullAccessKey { public_key, nonce } => promise .add_full_access_key_with_nonce( public_key.parse().unwrap(), - nonce.map(Into::into).unwrap_or(0), + nonce.map_or(0, Into::into), ), PromiseAction::CreateAccount => promise.create_account(), PromiseAction::DeployContract { code } => promise.deploy_contract(code.0), diff --git a/src/approval/simple_multisig.rs b/src/approval/simple_multisig.rs index a939084..c370e1b 100644 --- a/src/approval/simple_multisig.rs +++ b/src/approval/simple_multisig.rs @@ -1,5 +1,7 @@ //! Simple multi-signature wallet component. Generic over approvable actions. -//! Use with NativeTransactionAction for multisig over native transactions. +//! Use with +//! [`NativeTransactionAction`](super::native_transaction_action::NativeTransactionAction) +//! for multisig over native transactions. use std::marker::PhantomData; @@ -9,12 +11,16 @@ use thiserror::Error; use super::{ActionRequest, ApprovalConfiguration}; /// Check which accounts are eligible to submit approvals to an -/// [ApprovalManager](super::ApprovalManager) +/// [`ApprovalManager`](super::ApprovalManager) pub trait AccountAuthorizer { /// Why can this account not be authorized? type AuthorizationError; /// Determines whether an account ID is allowed to submit an approval + /// + /// # Errors + /// + /// Returns an error if the account is not authorized. fn is_account_authorized(account_id: &AccountId) -> Result<(), Self::AuthorizationError>; } @@ -35,6 +41,7 @@ pub struct Configuration { impl Configuration { /// Create an approval scheme with the given threshold + #[must_use] pub fn new(threshold: u8, validity_period_nanoseconds: u64) -> Self { Self { threshold, @@ -44,6 +51,11 @@ impl Configuration { } /// Is the given approval state still considered valid? + /// + /// # Panics + /// + /// - If the request timestamp is in the future. + #[must_use] pub fn is_within_validity_period(&self, approval_state: &ApprovalState) -> bool { if self.validity_period_nanoseconds == 0 { true @@ -73,7 +85,8 @@ impl Default for ApprovalState { } impl ApprovalState { - /// Creates an ApprovalState with the current network timestamp + /// Creates an [`ApprovalState`] with the current network timestamp. + #[must_use] pub fn new() -> Self { Self { approved_by: Vec::new(), @@ -82,7 +95,7 @@ impl ApprovalState { } } -/// If a request has expired, some actions may not be performed +/// If a request has expired, some actions may not be performed. #[derive(Error, Clone, Debug)] #[error("Validity period exceeded")] pub struct RequestExpiredError; @@ -90,10 +103,10 @@ pub struct RequestExpiredError; /// Why might a simple multisig approval attempt fail? #[derive(Error, Clone, Debug)] pub enum ApprovalError { - /// The account has already approved this action request + /// The account has already approved this action request. #[error("Already approved by this account")] AlreadyApprovedByAccount, - /// The request has expired and cannot be approved or executed + /// The request has expired and cannot be approved or executed. #[error(transparent)] RequestExpired(#[from] RequestExpiredError), } @@ -300,7 +313,7 @@ mod tests { } pub fn remove(&mut self, request_id: u32) { - self.remove_request(request_id).unwrap() + self.remove_request(request_id).unwrap(); } } diff --git a/src/escrow.rs b/src/escrow.rs index ff09777..3ead08b 100644 --- a/src/escrow.rs +++ b/src/escrow.rs @@ -1,15 +1,19 @@ -//! Escrow pattern implements locking functionality over some arbitrary storage key. +//! Escrow pattern implements locking functionality over some arbitrary storage +//! key. //! -//! Upon locking something, it adds a flag in the store that some item on some `id` is locked with some `state`. -//! This allows you to verify if an item is locked, and add some additional functionality to unlock the item. +//! Upon locking something, it adds a flag in the store that some item on some +//! `id` is locked with some `state`. This allows you to verify if an item is +//! locked, and add some additional functionality to unlock the item. //! //! The crate exports a [derive macro](near_sdk_contract_tools_macros::Escrow) //! that derives a default implementation for escrow. //! //! # Safety -//! The state for this contract is stored under the [root][EscrowInternal::root], make sure you dont -//! accidentally collide these storage entries in your contract. -//! You can change the key this is stored under by providing [storage_key] to the macro. +//! +//! The state for this contract is stored under the +//! [`root`][EscrowInternal::root], make sure you don't accidentally collide +//! these storage entries in your contract. You can change the key this is +//! stored under by providing `storage_key` to the macro. use crate::{event, standard::nep297::Event}; use crate::{slot::Slot, DefaultStorageKey}; use near_sdk::{ @@ -30,7 +34,7 @@ enum StorageKey<'a, T> { Locked(&'a T), } -/// Emit the state of an escrow lock and whether it was locked or unlocked +/// Emit the state of an escrow lock and whether it was locked or unlocked. #[event( standard = "x-escrow", version = "1.0.0", @@ -38,59 +42,65 @@ enum StorageKey<'a, T> { macros = "crate" )] pub struct Lock { - /// The identifier for a lock + /// The identifier for a lock. pub id: Id, - /// If the lock was locked or unlocked, and any state along with it + /// If the lock was locked or unlocked, and any state along with it. pub locked: Option, } -/// Inner storage modifiers and functionality required for escrow to succeed +/// Inner storage modifiers and functionality required for escrow to succeed. pub trait EscrowInternal { - /// Identifier over which the escrow exists + /// Identifier over which the escrow exists. type Id: BorshSerialize; - /// State stored inside the lock + /// State stored inside the lock. type State: BorshSerialize + BorshDeserialize; - /// Retrieve the state root + /// Retrieve the state root. + #[must_use] fn root() -> Slot<()> { Slot::root(DefaultStorageKey::Escrow) } - /// Inner function to retrieve the slot keyed by it's `Self::Id` + /// Inner function to retrieve the slot keyed by its [`Id`](EscrowInternal::Id). fn locked_slot(&self, id: &Self::Id) -> Slot { Self::root().field(StorageKey::Locked(id)) } - /// Read the state from the slot + /// Read the state from the slot. fn get_locked(&self, id: &Self::Id) -> Option { self.locked_slot(id).read() } - /// Set the state at `id` to `locked` + /// Set the state at `id` to `locked`. fn set_locked(&mut self, id: &Self::Id, locked: &Self::State) { self.locked_slot(id).write(locked); } - /// Clear the state at `id` + /// Clear the state at `id`. fn set_unlocked(&mut self, id: &Self::Id) { self.locked_slot(id).remove(); } } -/// Some escrowable capabilities, with a simple locking/unlocking mechanism -/// If you add additional `Approve` capabilities here, you can make use of a step-wise locking system. +/// Some escrowable capabilities, with a simple locking/unlocking mechanism. +/// +/// If you add additional `Approve` capabilities here, you can make use of a +/// step-wise locking system. pub trait Escrow { - /// Identifier over which the escrow exists + /// Identifier over which the escrow exists. type Id: BorshSerialize; - /// State stored inside the lock + /// State stored inside the lock. type State: BorshSerialize + BorshDeserialize; - /// Lock some `Self::State` by it's `Self::Id` within the store + /// Lock some [`State`](Escrow::State) by its [`Id`](Escrow::Id) within the + /// store. fn lock(&mut self, id: &Self::Id, state: &Self::State); - /// Unlock and release some `Self::State` by it's `Self::Id` + /// Unlock and release some [`State`](Escrow::State) by its + /// [`Id`](Escrow::Id). /// - /// Optionally, you can provide a handler which would allow you to inject logic if you should unlock or not. + /// Optionally, you can provide a handler which would allow you to inject + /// logic if you should unlock or not. fn unlock(&mut self, id: &Self::Id, unlock_handler: impl FnOnce(&Self::State) -> bool); /// Check if the item is locked @@ -127,8 +137,8 @@ where } } -/// A wrapper trait allowing all implementations of `State` and `Id` that implement [`serde::Serialize`] -/// to emit an event on success if they want to. +/// A wrapper trait allowing all implementations of `State` and `Id` that +/// implement [`serde::Serialize`] to emit an event on success if they want to. pub trait EventEmittedOnEscrow { /// Optionally implement an event on success of lock fn lock_emit(&mut self, id: &Id, state: &State); diff --git a/src/fast_account_id.rs b/src/fast_account_id.rs index f4681d8..4843e5b 100644 --- a/src/fast_account_id.rs +++ b/src/fast_account_id.rs @@ -28,6 +28,7 @@ pub struct FastAccountId(Rc); impl FastAccountId { /// Creates a new `FastAccountId` from a `&str` without performing any checks. + #[must_use] pub fn new_unchecked(account_id: &str) -> Self { Self(Rc::from(account_id)) } @@ -77,6 +78,8 @@ impl TryFrom<&str> for FastAccountId { impl BorshSerialize for FastAccountId { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + // A valid NEAR account ID cannot be longer than 64. + #[allow(clippy::cast_possible_truncation)] let len: u8 = self.0.len() as u8; writer.write_all(&[len])?; let compressed = compress_account_id(&self.0).ok_or(std::io::ErrorKind::InvalidData)?; @@ -161,6 +164,8 @@ fn compress_account_id(account_id: &str) -> Option> { let mut i = 0; for c in account_id.as_bytes() { + // ALPHABET is not that long. + #[allow(clippy::cast_possible_truncation)] let index = char_index(*c)? as u8; append_sub_byte(&mut v, i, index, 6); i += 6; @@ -191,18 +196,18 @@ mod tests { append_sub_byte(&mut v, 0, 0b111, 3); append_sub_byte(&mut v, 3, 0b010, 3); append_sub_byte(&mut v, 6, 0b110, 3); - append_sub_byte(&mut v, 9, 0b1110101, 7); + append_sub_byte(&mut v, 9, 0b111_0101, 7); - assert_eq!(v, vec![0b10010111, 0b11101011]); + assert_eq!(v, vec![0b1001_0111, 0b1110_1011]); } #[test] fn test_read_sub_byte() { - let v = vec![0b10010111, 0b11101011]; + let v = vec![0b1001_0111, 0b1110_1011]; assert_eq!(read_sub_byte(&v, 0, 3), 0b111); assert_eq!(read_sub_byte(&v, 3, 3), 0b010); assert_eq!(read_sub_byte(&v, 6, 3), 0b110); - assert_eq!(read_sub_byte(&v, 9, 7), 0b1110101); + assert_eq!(read_sub_byte(&v, 9, 7), 0b111_0101); } #[test] diff --git a/src/lib.rs b/src/lib.rs index f94a4aa..0fdfe69 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +#![allow(clippy::wildcard_imports)] #![doc = include_str!("../README.md")] #![cfg_attr(docsrs, feature(doc_auto_cfg))] diff --git a/src/migrate.rs b/src/migrate.rs index 950e23d..051354e 100644 --- a/src/migrate.rs +++ b/src/migrate.rs @@ -37,6 +37,7 @@ pub trait MigrateController { /// Deserializes the old schema from storage. /// /// It is probably not necessary to override this function. + #[must_use] fn deserialize_old_schema() -> Self::OldSchema { env::state_read::() .unwrap_or_else(|| env::panic_str("Failed to deserialize old state")) diff --git a/src/owner.rs b/src/owner.rs index 2bee8c5..db31afb 100644 --- a/src/owner.rs +++ b/src/owner.rs @@ -78,21 +78,25 @@ enum StorageKey { /// Internal functions for [`Owner`]. Using these methods may result in unexpected behavior. pub trait OwnerInternal { /// Storage root + #[must_use] fn root() -> Slot<()> { Slot::new(DefaultStorageKey::Owner) } /// Storage slot for initialization state + #[must_use] fn slot_is_initialized() -> Slot { Self::root().field(StorageKey::IsInitialized) } /// Storage slot for owner account ID + #[must_use] fn slot_owner() -> Slot { Self::root().field(StorageKey::Owner) } /// Storage slot for proposed owner account ID + #[must_use] fn slot_proposed_owner() -> Slot { Self::root().field(StorageKey::ProposedOwner) } @@ -112,7 +116,7 @@ pub trait Owner { /// Updates proposed owner without any checks or emitting events. fn update_proposed_unchecked(&mut self, new: Option); - /// Same as require_owner but as a method. + /// Same as [`require_owner`](Owner::require_owner) but as a method. fn assert_owner(&self); /// Initializes the contract owner. Can only be called once. @@ -365,6 +369,7 @@ mod tests { #[near(contract_state)] struct Contract {} + #[allow(clippy::unused_self, clippy::needless_pass_by_value)] #[near] impl Contract { #[init] diff --git a/src/pause.rs b/src/pause.rs index 7090f68..9306e80 100644 --- a/src/pause.rs +++ b/src/pause.rs @@ -47,11 +47,13 @@ pub enum PauseEvent { /// Internal functions for [`Pause`]. Using these methods may result in unexpected behavior. pub trait PauseInternal { /// Storage root + #[must_use] fn root() -> Slot<()> { Slot::new(DefaultStorageKey::Pause) } /// Storage slot for pause state + #[must_use] fn slot_paused() -> Slot { Self::root().transmute() } diff --git a/src/rbac.rs b/src/rbac.rs index 01d8127..76bb233 100644 --- a/src/rbac.rs +++ b/src/rbac.rs @@ -49,6 +49,7 @@ pub trait RbacInternal { type Role: BorshSerialize + IntoStorageKey; /// Storage slot namespace for items. + #[must_use] fn root() -> Slot<()> { Slot::new(DefaultStorageKey::Rbac) } @@ -130,8 +131,7 @@ impl Rbac for I { fn has_role(account_id: &AccountId, role: &Self::Role) -> bool { Self::slot_members_of(role) .read() - .map(|set| set.contains(account_id)) - .unwrap_or(false) + .is_some_and(|set| set.contains(account_id)) } fn add_role(&mut self, account_id: &AccountId, role: &Self::Role) { @@ -167,6 +167,7 @@ pub struct Iter { impl Iter { /// Creates a new iterator from an `UnorderedSet`. + #[must_use] pub fn new(s: UnorderedSet) -> Self { Self { inner_collection: s, @@ -175,6 +176,10 @@ impl Iter { } } +// iter.nth always takes a usize, so we truncation unavoidable. +// However, it is vanishingly unlikely that someone will have over u32::MAX +// different roles. +#[allow(clippy::cast_possible_truncation)] impl Iterator for Iter { type Item = AccountId; diff --git a/src/slot.rs b/src/slot.rs index a15d74c..a0a6996 100644 --- a/src/slot.rs +++ b/src/slot.rs @@ -15,7 +15,7 @@ use crate::utils::prefix_key; #[near] /// A storage slot, composed of a storage location (key) and a data type pub struct Slot { - /// The storage key this slot controls + /// The storage key this slot controls. pub key: Vec, #[borsh(skip)] _marker: PhantomData, @@ -65,6 +65,7 @@ impl Slot { /// /// If the data in the slot is not parsable into the new type, methods like /// [`Slot::read`] and [`Slot::take`] will panic. + #[must_use] pub fn transmute(&self) -> Slot { Slot { key: self.key.clone(), @@ -78,17 +79,19 @@ impl Slot { } /// Read raw bytes from the slot. No type checking or parsing. + #[must_use] pub fn read_raw(&self) -> Option> { env::storage_read(&self.key) } /// Returns `true` if this slot's key is currently present in the smart - /// contract storage, `false` otherwise + /// contract storage, `false` otherwise. + #[must_use] pub fn exists(&self) -> bool { env::storage_has_key(&self.key) } - /// Removes the managed key from storage + /// Removes the managed key from storage. pub fn remove(&mut self) -> bool { env::storage_remove(&self.key) } @@ -96,12 +99,20 @@ impl Slot { impl Slot { /// Writes a value to the managed storage slot. + /// + /// # Panics + /// + /// If Borsh serialization fails. pub fn write(&mut self, value: &T) -> bool { self.write_raw(&borsh::to_vec(value).unwrap()) } /// Writes a value to the managed storage slot which is dereferenced from /// the target type. + /// + /// # Panics + /// + /// If Borsh serialization fails. pub fn write_deref(&mut self, value: &U) -> bool where T: Deref, @@ -121,11 +132,21 @@ impl Slot { impl Slot { /// Reads a value from storage, if present. + /// + /// # Panics + /// + /// If Borsh deserialization fails. + #[must_use] pub fn read(&self) -> Option { self.read_raw().map(|v| T::try_from_slice(&v).unwrap()) } /// Removes a value from storage and returns it if present. + /// + /// # Panics + /// + /// If Borsh deserialization fails. + #[must_use] pub fn take(&mut self) -> Option { if self.remove() { // unwrap should be safe if remove returns true @@ -138,6 +159,11 @@ impl Slot { impl Slot { /// Writes a value to storage and returns the evicted value, if present. + /// + /// # Panics + /// + /// If Borsh serialization fails. + #[must_use] pub fn swap(&mut self, value: &T) -> Option { let v = borsh::to_vec(&value).unwrap(); diff --git a/src/standard/nep141/mod.rs b/src/standard/nep141/mod.rs index 73059e9..1d56bd0 100644 --- a/src/standard/nep141/mod.rs +++ b/src/standard/nep141/mod.rs @@ -70,6 +70,7 @@ impl<'a> Nep141Transfer<'a> { } /// Add a memo string. + #[must_use] pub fn memo(self, memo: impl Into>) -> Self { Self { memo: Some(memo.into()), @@ -78,6 +79,7 @@ impl<'a> Nep141Transfer<'a> { } /// Add a message string. + #[must_use] pub fn msg(self, msg: impl Into>) -> Self { Self { msg: Some(msg.into()), @@ -87,6 +89,7 @@ impl<'a> Nep141Transfer<'a> { /// Returns `true` if this transfer comes from a `ft_transfer_call` /// call, `false` otherwise. + #[must_use] pub fn is_transfer_call(&self) -> bool { self.msg.is_some() } @@ -115,6 +118,7 @@ impl<'a> Nep141Mint<'a> { } /// Add a memo string. + #[must_use] pub fn memo(self, memo: impl Into>) -> Self { Self { memo: Some(memo.into()), @@ -146,6 +150,7 @@ impl<'a> Nep141Burn<'a> { } /// Add a memo string. + #[must_use] pub fn memo(self, memo: impl Into>) -> Self { Self { memo: Some(memo.into()), @@ -170,16 +175,19 @@ pub trait Nep141ControllerInternal { Self: Sized; /// Root storage slot. + #[must_use] fn root() -> Slot<()> { Slot::new(DefaultStorageKey::Nep141) } /// Slot for account data. + #[must_use] fn slot_account(account_id: &AccountIdRef) -> Slot { Self::root().field(StorageKey::Account(account_id)) } /// Slot for storing total supply. + #[must_use] fn slot_total_supply() -> Slot { Self::root().field(StorageKey::TotalSupply) } @@ -208,6 +216,11 @@ pub trait Nep141Controller { /// Removes tokens from an account and decreases total supply. No event /// emission or hook invocation. + /// + /// # Errors + /// + /// - Account balance underflow. + /// - Total supply underflow. fn withdraw_unchecked( &mut self, account_id: &AccountIdRef, @@ -216,6 +229,11 @@ pub trait Nep141Controller { /// Increases the token balance of an account. Updates total supply. No /// event emission or hook invocation. + /// + /// # Errors + /// + /// - Account balance overflow. + /// - Total supply overflow. fn deposit_unchecked( &mut self, account_id: &AccountIdRef, @@ -225,6 +243,11 @@ pub trait Nep141Controller { /// Decreases the balance of `sender_account_id` by `amount` and increases /// the balance of `receiver_account_id` by the same. No change to total /// supply. No event emission or hook invocation. + /// + /// # Errors + /// + /// - Receiver balance overflow. + /// - Sender balance underflow. fn transfer_unchecked( &mut self, sender_account_id: &AccountIdRef, @@ -234,14 +257,29 @@ pub trait Nep141Controller { /// Performs an NEP-141 token transfer, with event emission. Invokes /// [`Nep141Controller::TransferHook`]. + /// + /// # Errors + /// + /// - Receiver balance overflow. + /// - Sender balance underflow. fn transfer(&mut self, transfer: &Nep141Transfer<'_>) -> Result<(), TransferError>; /// Performs an NEP-141 token mint, with event emission. Invokes /// [`Nep141Controller::MintHook`]. + /// + /// # Errors + /// + /// - Account balance overflow. + /// - Total supply overflow. fn mint(&mut self, mint: &Nep141Mint<'_>) -> Result<(), DepositError>; /// Performs an NEP-141 token burn, with event emission. Invokes /// [`Nep141Controller::BurnHook`]. + /// + /// # Errors + /// + /// - Account balance underflow. + /// - Total supply underflow. fn burn(&mut self, burn: &Nep141Burn<'_>) -> Result<(), WithdrawError>; } diff --git a/src/standard/nep145/hooks.rs b/src/standard/nep145/hooks.rs index 13506e4..69ca414 100644 --- a/src/standard/nep145/hooks.rs +++ b/src/standard/nep145/hooks.rs @@ -30,7 +30,7 @@ fn apply_storage_accounting_hook( contract .storage_accounting(account_id, storage_usage_start) - .unwrap_or_else(|e| env::panic_str(&format!("Storage accounting error: {}", e))); + .unwrap_or_else(|e| env::panic_str(&format!("Storage accounting error: {e}"))); r } diff --git a/src/standard/nep145/mod.rs b/src/standard/nep145/mod.rs index 6f6ad39..095daaf 100644 --- a/src/standard/nep145/mod.rs +++ b/src/standard/nep145/mod.rs @@ -53,6 +53,7 @@ pub struct StorageBalanceBounds { impl StorageBalanceBounds { /// Restricts a balance to be within the bounds. + #[must_use] pub fn bound(&self, balance: NearToken, registration_only: bool) -> NearToken { if registration_only { self.min @@ -98,16 +99,19 @@ pub trait Nep145ControllerInternal { Self: Sized; /// Root storage slot. + #[must_use] fn root() -> Slot<()> { Slot::new(DefaultStorageKey::Nep145) } /// Storage slot for balance bounds. + #[must_use] fn slot_balance_bounds() -> Slot { Slot::new(StorageKey::BalanceBounds) } /// Storage slot for individual account balance. + #[must_use] fn slot_account(account_id: &AccountIdRef) -> Slot { Slot::new(StorageKey::Account(account_id)) } @@ -122,12 +126,21 @@ pub trait Nep145Controller { Self: Sized; /// Returns the storage balance of the given account. + /// + /// # Errors + /// + /// - If the account is not registered. fn get_storage_balance( &self, account_id: &AccountIdRef, ) -> Result; /// Locks the given amount of storage balance for the given account. + /// + /// # Errors + /// + /// - If the account is not registered. + /// - If the account's balance is too low. fn lock_storage( &mut self, account_id: &AccountIdRef, @@ -135,6 +148,11 @@ pub trait Nep145Controller { ) -> Result; /// Unlocks the given amount of storage balance for the given account. + /// + /// # Errors + /// + /// - If the account is not registered. + /// - If the account attempts to unlock more tokens than are deposited. fn unlock_storage( &mut self, account_id: &AccountIdRef, @@ -142,6 +160,11 @@ pub trait Nep145Controller { ) -> Result; /// Deposits the given amount of storage balance for the given account. + /// + /// # Errors + /// + /// - If the account balance would be less than the minimum balance. + /// - If the account balance would be greater than the maximum balance. fn deposit_to_storage_account( &mut self, account_id: &AccountIdRef, @@ -149,6 +172,12 @@ pub trait Nep145Controller { ) -> Result; /// Withdraws the given amount of storage balance for the given account. + /// + /// # Errors + /// + /// - If the account is not registered. + /// - If the account attempts to withdraw more tokens than are deposited. + /// - If the account balance would be less than the minimum balance. fn withdraw_from_storage_account( &mut self, account_id: &AccountIdRef, @@ -157,6 +186,11 @@ pub trait Nep145Controller { /// Unregisters the given account, returning the amount of storage balance /// that should be refunded. + /// + /// # Errors + /// + /// - If the account is not registered. + /// - If the account still has a locked balance. fn unregister_storage_account( &mut self, account_id: &AccountIdRef, @@ -164,6 +198,10 @@ pub trait Nep145Controller { /// Force unregisters the given account, returning the amount of storage balance /// that should be refunded. + /// + /// # Errors + /// + /// - If the account is not registered. fn force_unregister_storage_account( &mut self, account_id: &AccountIdRef, @@ -177,6 +215,10 @@ pub trait Nep145Controller { /// Convenience method for performing storage accounting, to be used after /// storage writes that are to be debited from the account's balance. + /// + /// # Errors + /// + /// See: [`lock_storage`](Nep145Controller::lock_storage) and [`unlock_storage`](Nep145Controller::unlock_storage). fn storage_accounting( &mut self, account_id: &AccountIdRef, diff --git a/src/standard/nep148.rs b/src/standard/nep148.rs index 0672732..9cab78a 100644 --- a/src/standard/nep148.rs +++ b/src/standard/nep148.rs @@ -37,6 +37,7 @@ pub struct ContractMetadata { impl ContractMetadata { /// Creates a new metadata struct. + #[must_use] pub fn new(name: String, symbol: String, decimals: u8) -> Self { Self { spec: FT_METADATA_SPEC.into(), @@ -50,42 +51,49 @@ impl ContractMetadata { } /// Sets the spec field. + #[must_use] pub fn spec(mut self, spec: String) -> Self { self.spec = spec; self } /// Sets the name field. + #[must_use] pub fn name(mut self, name: String) -> Self { self.name = name; self } /// Sets the symbol field. + #[must_use] pub fn symbol(mut self, symbol: String) -> Self { self.symbol = symbol; self } /// Sets the icon field. + #[must_use] pub fn icon(mut self, icon: String) -> Self { self.icon = Some(icon); self } /// Sets the reference field. + #[must_use] pub fn reference(mut self, reference: String) -> Self { self.reference = Some(reference); self } - /// Sets the reference_hash field. + /// Sets the `reference_hash` field. + #[must_use] pub fn reference_hash(mut self, reference_hash: Base64VecU8) -> Self { self.reference_hash = Some(reference_hash); self } /// Sets the decimals field. + #[must_use] pub fn decimals(mut self, decimals: u8) -> Self { self.decimals = decimals; self @@ -101,11 +109,13 @@ enum StorageKey { /// Internal functions for [`Nep148Controller`]. pub trait Nep148ControllerInternal { /// Returns the root storage slot for NEP-148. + #[must_use] fn root() -> Slot<()> { Slot::new(DefaultStorageKey::Nep148) } /// Returns the storage slot for NEP-148 metadata. + #[must_use] fn metadata() -> Slot { Self::root().field(StorageKey::Metadata) } diff --git a/src/standard/nep171/action.rs b/src/standard/nep171/action.rs index 6f6d055..17b9c50 100644 --- a/src/standard/nep171/action.rs +++ b/src/standard/nep171/action.rs @@ -30,6 +30,7 @@ impl<'a> Nep171Mint<'a> { } /// Add a memo string. + #[must_use] pub fn memo(self, memo: impl Into>) -> Self { Self { memo: Some(memo.into()), @@ -61,6 +62,7 @@ impl<'a> Nep171Burn<'a> { } /// Add a memo string. + #[must_use] pub fn memo(self, memo: impl Into>) -> Self { Self { memo: Some(memo.into()), @@ -110,6 +112,7 @@ impl<'a> Nep171Transfer<'a> { } /// Add a memo string. + #[must_use] pub fn memo(self, memo: impl Into>) -> Self { Self { memo: Some(memo.into()), @@ -118,6 +121,7 @@ impl<'a> Nep171Transfer<'a> { } /// Add a message string. + #[must_use] pub fn msg(self, msg: impl Into>) -> Self { Self { msg: Some(msg.into()), diff --git a/src/standard/nep171/mod.rs b/src/standard/nep171/mod.rs index 6183801..28990fa 100644 --- a/src/standard/nep171/mod.rs +++ b/src/standard/nep171/mod.rs @@ -103,11 +103,13 @@ pub trait Nep171ControllerInternal { Self: Sized; /// Root storage slot. + #[must_use] fn root() -> Slot<()> { Slot::root(DefaultStorageKey::Nep171) } /// Storage slot for the owner of a token. + #[must_use] fn slot_token_owner(token_id: &TokenId) -> Slot { Self::root().field(StorageKey::TokenOwner(token_id)) } @@ -142,6 +144,13 @@ pub trait Nep171Controller { /// call to `nft_transfer`. Checks that the transfer is valid using /// [`CheckExternalTransfer::check_external_transfer`] before performing /// the transfer. Emits events and runs relevant hooks. + /// + /// # Errors + /// + /// - If the token does not exist. + /// - If the sender is not approved. + /// - If the sender is the receiver. + /// - If the correct account does not own the token. fn external_transfer(&mut self, transfer: &Nep171Transfer) -> Result<(), Nep171TransferError> where Self: Sized; @@ -162,6 +171,10 @@ pub trait Nep171Controller { /// Mints a new token `token_id` to `owner_id`. Emits events and runs /// relevant hooks. + /// + /// # Errors + /// + /// - If the token ID already exists. fn mint(&mut self, action: &Nep171Mint<'_>) -> Result<(), Nep171MintError>; /// Mints a new token `token_id` to `owner_id` without checking if the @@ -170,6 +183,11 @@ pub trait Nep171Controller { /// Burns tokens `token_ids` owned by `current_owner_id`. Emits events and /// runs relevant hooks. + /// + /// # Errors + /// + /// - If the token does not exist. + /// - If the token is not owned by the expected owner. fn burn(&mut self, action: &Nep171Burn<'_>) -> Result<(), Nep171BurnError>; /// Burns tokens `token_ids` without checking the owners. Does not emit @@ -197,6 +215,10 @@ pub enum Nep171TransferAuthorization { pub trait CheckExternalTransfer { /// Checks if a transfer is valid. Returns the account ID of the current /// owner of the token. + /// + /// # Errors + /// + /// Returns an error if the external transfer should not be performed. fn check_external_transfer( contract: &C, transfer: &Nep171Transfer, @@ -294,10 +316,10 @@ impl Nep171Controller for T { } fn mint_unchecked(&mut self, token_ids: &[TokenId], owner_id: &AccountIdRef) { - token_ids.iter().for_each(|token_id| { + for token_id in token_ids { let mut slot = Self::slot_token_owner(token_id); slot.write_deref(owner_id); - }); + } } fn mint(&mut self, action: &Nep171Mint<'_>) -> Result<(), Nep171MintError> { @@ -408,6 +430,10 @@ pub struct Token { /// Trait for NFT extensions to load token metadata. pub trait LoadTokenMetadata { /// Load token metadata into `metadata`. + /// + /// # Errors + /// + /// If the token metadata could not be loaded. fn load( contract: &C, token_id: &TokenId, diff --git a/src/standard/nep177.rs b/src/standard/nep177.rs index bda1770..c443faf 100644 --- a/src/standard/nep177.rs +++ b/src/standard/nep177.rs @@ -52,6 +52,7 @@ impl ContractMetadata { /// Creates a new contract metadata, specifying the name, symbol, and /// optional base URI. Other fields are set to `None`. + #[must_use] pub fn new(name: String, symbol: String, base_uri: Option) -> Self { Self { spec: Self::SPEC.to_string(), @@ -98,77 +99,90 @@ pub struct TokenMetadata { // Builder pattern for TokenMetadata. impl TokenMetadata { /// Create a new `TokenMetadata` with all fields set to `None`. + #[must_use] pub fn new() -> Self { Self::default() } /// Set the title. + #[must_use] pub fn title(mut self, title: impl Into) -> Self { self.title = Some(title.into()); self } /// Set the description. + #[must_use] pub fn description(mut self, description: impl Into) -> Self { self.description = Some(description.into()); self } /// Set the media. + #[must_use] pub fn media(mut self, media: impl Into) -> Self { self.media = Some(media.into()); self } /// Set the media hash. + #[must_use] pub fn media_hash(mut self, media_hash: impl Into) -> Self { self.media_hash = Some(media_hash.into()); self } /// Set the copies. + #[must_use] pub fn copies(mut self, copies: impl Into) -> Self { self.copies = Some(copies.into()); self } /// Set the time the token was issued. + #[must_use] pub fn issued_at(mut self, issued_at: impl Into) -> Self { self.issued_at = Some(issued_at.into()); self } /// Set the time the token expires. + #[must_use] pub fn expires_at(mut self, expires_at: impl Into) -> Self { self.expires_at = Some(expires_at.into()); self } /// Set the time the token starts being valid. + #[must_use] pub fn starts_at(mut self, starts_at: impl Into) -> Self { self.starts_at = Some(starts_at.into()); self } /// Set the time the token was last updated. + #[must_use] pub fn updated_at(mut self, updated_at: impl Into) -> Self { self.updated_at = Some(updated_at.into()); self } /// Set the extra data. + #[must_use] pub fn extra(mut self, extra: impl Into) -> Self { self.extra = Some(extra.into()); self } /// Set the reference. + #[must_use] pub fn reference(mut self, reference: impl Into) -> Self { self.reference = Some(reference.into()); self } /// Set the reference hash. + #[must_use] pub fn reference_hash(mut self, reference_hash: impl Into) -> Self { self.reference_hash = Some(reference_hash.into()); self @@ -208,16 +222,19 @@ enum StorageKey<'a> { /// Internal functions for [`Nep177Controller`]. pub trait Nep177ControllerInternal { /// Storage root. + #[must_use] fn root() -> Slot<()> { Slot::root(DefaultStorageKey::Nep177) } /// Storage slot for contract metadata. + #[must_use] fn slot_contract_metadata() -> Slot { Self::root().field(StorageKey::ContractMetadata) } /// Storage slot for token metadata. + #[must_use] fn slot_token_metadata(token_id: &TokenId) -> Slot { Self::root().field(StorageKey::TokenMetadata(token_id)) } @@ -226,6 +243,10 @@ pub trait Nep177ControllerInternal { /// Functions for managing non-fungible tokens with attached metadata, NEP-177. pub trait Nep177Controller { /// Mint a new token with metadata. + /// + /// # Errors + /// + /// - If the token ID already exists. fn mint_with_metadata( &mut self, token_id: &TokenId, @@ -234,6 +255,11 @@ pub trait Nep177Controller { ) -> Result<(), Nep171MintError>; /// Burn a token with metadata. + /// + /// # Errors + /// + /// - If the token ID does not exist. + /// - If the token is not owned by the expected owner. fn burn_with_metadata( &mut self, token_id: &TokenId, @@ -249,6 +275,10 @@ pub trait Nep177Controller { ); /// Sets the metadata for a token ID and emits an [`Nep171Event::NftMetadataUpdate`] event. + /// + /// # Errors + /// + /// - If the token does not exist. fn set_token_metadata( &mut self, token_id: &TokenId, diff --git a/src/standard/nep178/mod.rs b/src/standard/nep178/mod.rs index 7e8d9c7..cb371a4 100644 --- a/src/standard/nep178/mod.rs +++ b/src/standard/nep178/mod.rs @@ -129,16 +129,19 @@ pub trait Nep178ControllerInternal { Self: Sized; /// Storage root. + #[must_use] fn root() -> Slot<()> { Slot::root(DefaultStorageKey::Nep178) } /// Storage slot for token approvals. + #[must_use] fn slot_token_approvals(token_id: &TokenId) -> Slot { Self::root().field(StorageKey::TokenApprovals(token_id)) } /// Storage slot for token approvals `UnorderedMap`. + #[must_use] fn slot_token_approvals_unordered_map( token_id: &TokenId, ) -> Slot> { @@ -162,6 +165,12 @@ pub trait Nep178Controller { Self: Sized; /// Approve a token for transfer by a delegated account. + /// + /// # Errors + /// + /// - If the acting account is not authorized to create approvals for the token. + /// - If the target account is already approved for the token. + /// - If the token exceeds the maximum number of approvals. fn approve(&mut self, action: &Nep178Approve<'_>) -> Result; /// Approve a token without checking if the account is already approved or @@ -169,6 +178,11 @@ pub trait Nep178Controller { fn approve_unchecked(&mut self, token_id: &TokenId, account_id: &AccountIdRef) -> ApprovalId; /// Revoke approval for an account to transfer token. + /// + /// # Errors + /// + /// - If the acting account is not authorized to revoke approvals for the token. + /// - If the target account is not approved for the token. fn revoke(&mut self, action: &Nep178Revoke<'_>) -> Result<(), Nep178RevokeError>; /// Revoke approval for an account to transfer token without checking if @@ -176,6 +190,10 @@ pub trait Nep178Controller { fn revoke_unchecked(&mut self, token_id: &TokenId, account_id: &AccountIdRef); /// Revoke all approvals for a token. + /// + /// # Errors + /// + /// - If the acting account is not authorized to revoke approvals for the token. fn revoke_all(&mut self, action: &Nep178RevokeAll<'_>) -> Result<(), Nep178RevokeAllError>; /// Revoke all approvals for a token without checking current owner. @@ -260,9 +278,8 @@ impl Nep178Controller for T { fn revoke_unchecked(&mut self, token_id: &TokenId, account_id: &AccountIdRef) { let mut slot = Self::slot_token_approvals(token_id); - let mut approvals = match slot.read() { - Some(approvals) => approvals, - None => return, + let Some(mut approvals) = slot.read() else { + return; }; let old = approvals.accounts.remove(&account_id.into()); @@ -329,9 +346,8 @@ impl Nep178Controller for T { fn revoke_all_unchecked(&mut self, token_id: &TokenId) { let mut slot = Self::slot_token_approvals(token_id); - let mut approvals = match slot.read() { - Some(approvals) => approvals, - None => return, + let Some(mut approvals) = slot.read() else { + return; }; if !approvals.accounts.is_empty() { @@ -353,9 +369,8 @@ impl Nep178Controller for T { fn get_approvals_for(&self, token_id: &TokenId) -> HashMap { let slot = Self::slot_token_approvals(token_id); - let approvals = match slot.read() { - Some(approvals) => approvals, - None => return HashMap::default(), + let Some(approvals) = slot.read() else { + return HashMap::default(); }; approvals.accounts.into_iter().collect() diff --git a/src/standard/nep181.rs b/src/standard/nep181.rs index 85e25e4..8d8574e 100644 --- a/src/standard/nep181.rs +++ b/src/standard/nep181.rs @@ -65,16 +65,19 @@ enum StorageKey<'a> { /// Internal functions for [`Nep181Controller`]. pub trait Nep181ControllerInternal { /// Storage root. + #[must_use] fn root() -> Slot<()> { Slot::root(DefaultStorageKey::Nep181) } /// Storage slot for all tokens. + #[must_use] fn slot_tokens() -> Slot> { Self::root().field(StorageKey::Tokens) } /// Storage slot for tokens owned by an account. + #[must_use] fn slot_owner_tokens(owner_id: &AccountIdRef) -> Slot> { Self::root().field(StorageKey::OwnerTokens(owner_id)) } diff --git a/src/upgrade/raw.rs b/src/upgrade/raw.rs index 364e3bc..c2eefcd 100644 --- a/src/upgrade/raw.rs +++ b/src/upgrade/raw.rs @@ -25,6 +25,7 @@ use super::PostUpgrade; /// /// Requires that `near_sdk::env::input()` contains the plain, raw bytes of a /// valid WebAssembly smart contract. +#[allow(clippy::needless_pass_by_value)] pub unsafe fn upgrade(post_upgrade: PostUpgrade) { // Create a promise batch let promise_id = sys::promise_batch_create( diff --git a/src/upgrade/serialized.rs b/src/upgrade/serialized.rs index 421ecfd..c172d46 100644 --- a/src/upgrade/serialized.rs +++ b/src/upgrade/serialized.rs @@ -1,3 +1,4 @@ +#![allow(clippy::must_use_candidate)] //! Contract upgrade functions that work as expected in conjunction with //! `#[near]`. diff --git a/src/utils.rs b/src/utils.rs index dd3a168..6446acd 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -11,6 +11,7 @@ use near_sdk::{env, require, NearToken, Promise}; /// /// assert_eq!(prefix_key(b"p", b"key"), b"pkey"); /// ``` +#[must_use] pub fn prefix_key(prefix: &[u8], key: &[u8]) -> Vec { [prefix, key].concat() } @@ -44,6 +45,7 @@ pub fn prefix_key(prefix: &[u8], key: &[u8]) -> Vec { /// // Attached deposit must cover storage fee or this function will panic /// apply_storage_fee_and_refund(initial_storage_usage, additional_fees); /// ``` +#[must_use] pub fn apply_storage_fee_and_refund( initial_storage_usage: u64, additional_fees: u128, @@ -76,10 +78,10 @@ pub fn apply_storage_fee_and_refund( let refund = attached_deposit.saturating_sub(total_required_deposit); // Send refund transfer if required - if !refund.is_zero() { - Some(Promise::new(env::predecessor_account_id()).transfer(refund)) - } else { + if refund.is_zero() { None + } else { + Some(Promise::new(env::predecessor_account_id()).transfer(refund)) } } diff --git a/tests/macros/mod.rs b/tests/macros/mod.rs index 33c777e..b9ce758 100644 --- a/tests/macros/mod.rs +++ b/tests/macros/mod.rs @@ -62,6 +62,7 @@ mod integration { pub value: u32, } + #[allow(clippy::needless_pass_by_value)] #[near] impl Integration { #[init] diff --git a/tests/macros/standard/nep145.rs b/tests/macros/standard/nep145.rs index 28e5f0a..2d7f136 100644 --- a/tests/macros/standard/nep145.rs +++ b/tests/macros/standard/nep145.rs @@ -55,7 +55,7 @@ impl Contract { let storage_fee = env::storage_byte_cost().saturating_mul(u128::from(storage_usage)); Nep145Controller::lock_storage(self, &predecessor, storage_fee) - .unwrap_or_else(|e| env::panic_str(&format!("Storage lock error: {}", e))); + .unwrap_or_else(|e| env::panic_str(&format!("Storage lock error: {e}"))); } } @@ -158,6 +158,7 @@ mod tests { .predecessor_account_id(alice()) .build()); + #[allow(clippy::cast_possible_truncation)] contract.use_storage( one_near .as_yoctonear()