From 696c25f5978085741f21f568b7d99c27412fa2a2 Mon Sep 17 00:00:00 2001 From: laruh Date: Thu, 7 Dec 2023 20:16:02 +0700 Subject: [PATCH 01/13] wip, `fn erc1155_balance` added --- mm2src/coins/eth.rs | 46 +++++++++++++++++++++++++-------- mm2src/coins/nft.rs | 26 +------------------ mm2src/coins/nft/storage/mod.rs | 5 ---- 3 files changed, 36 insertions(+), 41 deletions(-) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 90e2f90ffb..1d08e27a5a 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -107,7 +107,7 @@ pub use rlp; mod web3_transport; #[path = "eth/v2_activation.rs"] pub mod v2_activation; -use crate::nft::{find_wallet_nft_amount, WithdrawNftResult}; +use crate::nft::WithdrawNftResult; use v2_activation::{build_address_and_priv_key_policy, EthActivationV2Error}; mod nonce; @@ -878,15 +878,10 @@ pub async fn withdraw_erc1155(ctx: MmArc, withdraw_type: WithdrawErc1155) -> Wit let coin = lp_coinfind_or_err(&ctx, withdraw_type.chain.to_ticker()).await?; let (to_addr, token_addr, eth_coin) = get_valid_nft_add_to_withdraw(coin, &withdraw_type.to, &withdraw_type.token_address)?; - let my_address = eth_coin.my_address()?; - let wallet_amount = find_wallet_nft_amount( - &ctx, - &withdraw_type.chain, - withdraw_type.token_address.to_lowercase(), - withdraw_type.token_id.clone(), - ) - .await?; + let token_id_str = &withdraw_type.token_id.to_string(); + let wallet_amount = eth_coin.erc1155_balance(token_addr, token_id_str).await?; + let amount_dec = if withdraw_type.max { wallet_amount.clone() } else { @@ -905,7 +900,7 @@ pub async fn withdraw_erc1155(ctx: MmArc, withdraw_type: WithdrawErc1155) -> Wit let (eth_value, data, call_addr, fee_coin) = match eth_coin.coin_type { EthCoinType::Eth => { let function = ERC1155_CONTRACT.function("safeTransferFrom")?; - let token_id_u256 = U256::from_dec_str(&withdraw_type.token_id.to_string()) + let token_id_u256 = U256::from_dec_str(token_id_str) .map_err(|e| format!("{:?}", e)) .map_to_mm(NumConversError::new)?; let amount_u256 = U256::from_dec_str(&amount_dec.to_string()) @@ -956,10 +951,11 @@ pub async fn withdraw_erc1155(ctx: MmArc, withdraw_type: WithdrawErc1155) -> Wit let signed_bytes = rlp::encode(&signed); let fee_details = EthTxFeeDetails::new(gas, gas_price, fee_coin)?; + let my_address_str = eth_coin.my_address()?; Ok(TransactionNftDetails { tx_hex: BytesJson::from(signed_bytes.to_vec()), tx_hash: format!("{:02x}", signed.tx_hash()), - from: vec![my_address], + from: vec![my_address_str], to: vec![withdraw_type.to], contract_type: ContractType::Erc1155, token_address: withdraw_type.token_address, @@ -3943,6 +3939,34 @@ impl EthCoin { } } + async fn erc1155_balance(&self, token_addr: Address, token_id: &str) -> MmResult { + let wallet_amount_uint = match self.coin_type { + EthCoinType::Eth => { + let function = ERC1155_CONTRACT.function("balanceOf")?; + let token_id_u256 = U256::from_dec_str(token_id) + .map_err(|e| format!("{:?}", e)) + .map_to_mm(NumConversError::new)?; + let data = function.encode_input(&[Token::Address(self.my_address), Token::Uint(token_id_u256)])?; + let result = self.call_request(token_addr, None, Some(data.into())).await?; + let decoded = function.decode_output(&result.0)?; + match decoded[0] { + Token::Uint(number) => number, + _ => { + let error = format!("Expected U256 as balanceOf result but got {:?}", decoded); + return MmError::err(BalanceError::InvalidResponse(error)); + }, + } + }, + EthCoinType::Erc20 { .. } => { + return MmError::err(BalanceError::Internal( + "Erc20 coin type doesnt support Erc1155 standard".to_owned(), + )) + }, + }; + let wallet_amount = BigDecimal::from_str(&wallet_amount_uint.to_string()).map_err(NumConversError::from)?; + Ok(wallet_amount) + } + fn estimate_gas(&self, req: CallRequest) -> Box + Send> { // always using None block number as old Geth version accept only single argument in this RPC Box::new(self.web3.eth().estimate_gas(req, None).compat()) diff --git a/mm2src/coins/nft.rs b/mm2src/coins/nft.rs index 40736a6414..7b5d2933c9 100644 --- a/mm2src/coins/nft.rs +++ b/mm2src/coins/nft.rs @@ -29,7 +29,7 @@ use futures::compat::Future01CompatExt; use futures::future::try_join_all; use mm2_err_handle::map_to_mm::MapToMmResult; use mm2_net::transport::send_post_request_to_uri; -use mm2_number::{BigDecimal, BigUint}; +use mm2_number::BigUint; use regex::Regex; use serde_json::Value as Json; use std::cmp::Ordering; @@ -1160,30 +1160,6 @@ async fn mark_as_spam_and_build_empty_meta MmResult { - let nft_ctx = NftCtx::from_ctx(ctx).map_to_mm(GetNftInfoError::Internal)?; - - let storage = nft_ctx.lock_db().await?; - if !NftListStorageOps::is_initialized(&storage, chain).await? { - NftListStorageOps::init(&storage, chain).await?; - } - let nft_meta = storage - .get_nft(chain, token_address.to_lowercase(), token_id.clone()) - .await? - .ok_or_else(|| GetNftInfoError::TokenNotFoundInWallet { - token_address, - token_id: token_id.to_string(), - })?; - Ok(nft_meta.common.amount) -} - async fn cache_nfts_from_moralis( ctx: &MmArc, storage: &T, diff --git a/mm2src/coins/nft/storage/mod.rs b/mm2src/coins/nft/storage/mod.rs index c28c33ea54..eaee9649da 100644 --- a/mm2src/coins/nft/storage/mod.rs +++ b/mm2src/coins/nft/storage/mod.rs @@ -1,7 +1,6 @@ use crate::eth::EthTxFeeDetails; use crate::nft::nft_structs::{Chain, Nft, NftList, NftListFilters, NftTokenAddrId, NftTransferHistory, NftTransferHistoryFilters, NftsTransferHistoryList, TransferMeta}; -use crate::WithdrawError; use async_trait::async_trait; use ethereum_types::Address; use mm2_err_handle::mm_error::MmResult; @@ -28,10 +27,6 @@ pub enum RemoveNftResult { /// Defines the standard errors that can occur in NFT storage operations pub trait NftStorageError: std::fmt::Debug + NotMmError + NotEqual + Send {} -impl From for WithdrawError { - fn from(err: T) -> Self { WithdrawError::DbError(format!("{:?}", err)) } -} - /// Provides asynchronous operations for handling and querying NFT listings. #[async_trait] pub trait NftListStorageOps { From c19e0072813efe8c746343e22da3feaecf119ef6 Mon Sep 17 00:00:00 2001 From: laruh Date: Fri, 8 Dec 2023 16:21:56 +0700 Subject: [PATCH 02/13] impl erc721_owner --- mm2src/coins/eth.rs | 53 +++++++++++++++++++++++++++------- mm2src/coins/lp_coins.rs | 17 +++++------ mm2src/coins/nft/nft_errors.rs | 18 ++++++++++-- 3 files changed, 66 insertions(+), 22 deletions(-) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 1d08e27a5a..0a8c78e6b6 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -111,6 +111,7 @@ use crate::nft::WithdrawNftResult; use v2_activation::{build_address_and_priv_key_policy, EthActivationV2Error}; mod nonce; +use crate::nft::nft_errors::GetNftInfoError; use crate::{PrivKeyPolicy, TransactionResult, WithdrawFrom}; use nonce::ParityNonce; @@ -878,6 +879,7 @@ pub async fn withdraw_erc1155(ctx: MmArc, withdraw_type: WithdrawErc1155) -> Wit let coin = lp_coinfind_or_err(&ctx, withdraw_type.chain.to_ticker()).await?; let (to_addr, token_addr, eth_coin) = get_valid_nft_add_to_withdraw(coin, &withdraw_type.to, &withdraw_type.token_address)?; + let my_address_str = eth_coin.my_address()?; let token_id_str = &withdraw_type.token_id.to_string(); let wallet_amount = eth_coin.erc1155_balance(token_addr, token_id_str).await?; @@ -951,7 +953,6 @@ pub async fn withdraw_erc1155(ctx: MmArc, withdraw_type: WithdrawErc1155) -> Wit let signed_bytes = rlp::encode(&signed); let fee_details = EthTxFeeDetails::new(gas, gas_price, fee_coin)?; - let my_address_str = eth_coin.my_address()?; Ok(TransactionNftDetails { tx_hex: BytesJson::from(signed_bytes.to_vec()), tx_hash: format!("{:02x}", signed.tx_hash()), @@ -976,7 +977,17 @@ pub async fn withdraw_erc721(ctx: MmArc, withdraw_type: WithdrawErc721) -> Withd let coin = lp_coinfind_or_err(&ctx, withdraw_type.chain.to_ticker()).await?; let (to_addr, token_addr, eth_coin) = get_valid_nft_add_to_withdraw(coin, &withdraw_type.to, &withdraw_type.token_address)?; - let my_address = eth_coin.my_address()?; + let my_address_str = eth_coin.my_address()?; + + let token_id_str = &withdraw_type.token_id.to_string(); + let token_owner = eth_coin.erc721_owner(token_addr, token_id_str).await?; + let my_address = eth_coin.my_address; + if token_owner != my_address { + return MmError::err(WithdrawError::MyAddressNotNftOwner { + my_address: eth_addr_to_hex(&my_address), + token_owner: eth_addr_to_hex(&token_owner), + }); + } let (eth_value, data, call_addr, fee_coin) = match eth_coin.coin_type { EthCoinType::Eth => { @@ -985,7 +996,7 @@ pub async fn withdraw_erc721(ctx: MmArc, withdraw_type: WithdrawErc721) -> Withd .map_err(|e| format!("{:?}", e)) .map_to_mm(NumConversError::new)?; let data = function.encode_input(&[ - Token::Address(eth_coin.my_address), + Token::Address(my_address), Token::Address(to_addr), Token::Uint(token_id_u256), ])?; @@ -1007,7 +1018,7 @@ pub async fn withdraw_erc721(ctx: MmArc, withdraw_type: WithdrawErc721) -> Withd ) .await?; let _nonce_lock = eth_coin.nonce_lock.lock().await; - let (nonce, _) = get_addr_nonce(eth_coin.my_address, eth_coin.web3_instances.clone()) + let (nonce, _) = get_addr_nonce(my_address, eth_coin.web3_instances.clone()) .compat() .timeout_secs(30.) .await? @@ -1030,7 +1041,7 @@ pub async fn withdraw_erc721(ctx: MmArc, withdraw_type: WithdrawErc721) -> Withd Ok(TransactionNftDetails { tx_hex: BytesJson::from(signed_bytes.to_vec()), tx_hash: format!("{:02x}", signed.tx_hash()), - from: vec![my_address], + from: vec![my_address_str], to: vec![withdraw_type.to], contract_type: ContractType::Erc721, token_address: withdraw_type.token_address, @@ -3967,6 +3978,33 @@ impl EthCoin { Ok(wallet_amount) } + async fn erc721_owner(&self, token_addr: Address, token_id: &str) -> MmResult { + let owner_address = match self.coin_type { + EthCoinType::Eth => { + let function = ERC721_CONTRACT.function("ownerOf")?; + let token_id_u256 = U256::from_dec_str(token_id) + .map_err(|e| format!("{:?}", e)) + .map_to_mm(NumConversError::new)?; + let data = function.encode_input(&[Token::Uint(token_id_u256)])?; + let result = self.call_request(token_addr, None, Some(data.into())).await?; + let decoded = function.decode_output(&result.0)?; + match decoded[0] { + Token::Address(owner) => owner, + _ => { + let error = format!("Expected Address as ownerOf result but got {:?}", decoded); + return MmError::err(GetNftInfoError::InvalidResponse(error)); + }, + } + }, + EthCoinType::Erc20 { .. } => { + return MmError::err(GetNftInfoError::Internal( + "Erc20 coin type doesnt support Erc721 standard".to_owned(), + )) + }, + }; + Ok(owner_address) + } + fn estimate_gas(&self, req: CallRequest) -> Box + Send> { // always using None block number as old Geth version accept only single argument in this RPC Box::new(self.web3.eth().estimate_gas(req, None).compat()) @@ -5724,11 +5762,6 @@ pub async fn get_eth_address( #[derive(Display)] pub enum GetValidEthWithdrawAddError { - #[display(fmt = "My address {} and from address {} mismatch", my_address, from)] - AddressMismatchError { - my_address: String, - from: String, - }, #[display(fmt = "{} coin doesn't support NFT withdrawing", coin)] CoinDoesntSupportNftWithdraw { coin: String, diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index ad33246977..c01d599d33 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -2315,11 +2315,6 @@ pub enum WithdrawError { CoinDoesntSupportNftWithdraw { coin: String, }, - #[display(fmt = "My address {} and from address {} mismatch", my_address, from)] - AddressMismatchError { - my_address: String, - from: String, - }, #[display(fmt = "Contract type {} doesnt support 'withdraw_nft' yet", _0)] ContractTypeDoesntSupportNftWithdrawing(String), #[display(fmt = "Action not allowed for coin: {}", _0)] @@ -2340,6 +2335,11 @@ pub enum WithdrawError { }, #[display(fmt = "DB error {}", _0)] DbError(String), + #[display(fmt = "My address:= {}, while current Nft owner:= {}", my_address, token_owner)] + MyAddressNotNftOwner { + my_address: String, + token_owner: String, + }, } impl HttpStatusCode for WithdrawError { @@ -2362,10 +2362,10 @@ impl HttpStatusCode for WithdrawError { | WithdrawError::UnsupportedError(_) | WithdrawError::ActionNotAllowed(_) | WithdrawError::GetNftInfoError(_) - | WithdrawError::AddressMismatchError { .. } | WithdrawError::ContractTypeDoesntSupportNftWithdrawing(_) | WithdrawError::CoinDoesntSupportNftWithdraw { .. } - | WithdrawError::NotEnoughNftsAmount { .. } => StatusCode::BAD_REQUEST, + | WithdrawError::NotEnoughNftsAmount { .. } + | WithdrawError::MyAddressNotNftOwner { .. } => StatusCode::BAD_REQUEST, WithdrawError::HwError(_) => StatusCode::GONE, #[cfg(target_arch = "wasm32")] WithdrawError::BroadcastExpected(_) => StatusCode::BAD_REQUEST, @@ -2409,9 +2409,6 @@ impl From for WithdrawError { impl From for WithdrawError { fn from(e: GetValidEthWithdrawAddError) -> Self { match e { - GetValidEthWithdrawAddError::AddressMismatchError { my_address, from } => { - WithdrawError::AddressMismatchError { my_address, from } - }, GetValidEthWithdrawAddError::CoinDoesntSupportNftWithdraw { coin } => { WithdrawError::CoinDoesntSupportNftWithdraw { coin } }, diff --git a/mm2src/coins/nft/nft_errors.rs b/mm2src/coins/nft/nft_errors.rs index f5dd5adaba..1c61545b7f 100644 --- a/mm2src/coins/nft/nft_errors.rs +++ b/mm2src/coins/nft/nft_errors.rs @@ -2,7 +2,7 @@ use crate::eth::GetEthAddressError; #[cfg(target_arch = "wasm32")] use crate::nft::storage::wasm::WasmNftCacheError; use crate::nft::storage::NftStorageError; -use crate::{CoinFindError, GetMyAddressError, WithdrawError}; +use crate::{CoinFindError, GetMyAddressError, NumConversError, WithdrawError}; use common::{HttpStatusCode, ParseRfc3339Err}; #[cfg(not(target_arch = "wasm32"))] use db_common::sqlite::rusqlite::Error as SqlError; @@ -43,6 +43,7 @@ pub enum GetNftInfoError { ContractTypeIsNull, ProtectFromSpamError(ProtectFromSpamError), TransferConfirmationsError(TransferConfirmationsError), + NumConversError(String), } impl From for WithdrawError { @@ -109,6 +110,18 @@ impl From for GetNftInfoError { fn from(e: TransferConfirmationsError) -> Self { GetNftInfoError::TransferConfirmationsError(e) } } +impl From for GetNftInfoError { + fn from(e: ethabi::Error) -> Self { + // Currently, we use the `ethabi` crate to work with a smart contract ABI known at compile time. + // It's an internal error if there are any issues during working with a smart contract ABI. + GetNftInfoError::Internal(e.to_string()) + } +} + +impl From for GetNftInfoError { + fn from(e: NumConversError) -> Self { GetNftInfoError::NumConversError(e.to_string()) } +} + impl HttpStatusCode for GetNftInfoError { fn status_code(&self) -> StatusCode { match self { @@ -121,7 +134,8 @@ impl HttpStatusCode for GetNftInfoError { | GetNftInfoError::TokenNotFoundInWallet { .. } | GetNftInfoError::DbError(_) | GetNftInfoError::ProtectFromSpamError(_) - | GetNftInfoError::TransferConfirmationsError(_) => StatusCode::INTERNAL_SERVER_ERROR, + | GetNftInfoError::TransferConfirmationsError(_) + | GetNftInfoError::NumConversError(_) => StatusCode::INTERNAL_SERVER_ERROR, } } } From 1586b711f069d9f09fe5154c0bc814a695c549d0 Mon Sep 17 00:00:00 2001 From: laruh Date: Wed, 13 Dec 2023 15:00:54 +0700 Subject: [PATCH 03/13] clear_nft_db RPC --- mm2src/coins/lp_coins.rs | 2 +- mm2src/coins/nft.rs | 34 +++++++++-- mm2src/coins/nft/nft_errors.rs | 25 ++++++++ mm2src/coins/nft/nft_structs.rs | 7 +++ mm2src/coins/nft/nft_tests.rs | 61 ++++++++++++++++++- mm2src/coins/nft/storage/mod.rs | 4 ++ mm2src/coins/nft/storage/sql_storage.rs | 39 ++++++++++++ mm2src/coins/nft/storage/wasm/wasm_storage.rs | 19 ++++++ .../mm2_main/src/rpc/dispatcher/dispatcher.rs | 4 +- 9 files changed, 188 insertions(+), 7 deletions(-) diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index c01d599d33..9137c71903 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -2335,7 +2335,7 @@ pub enum WithdrawError { }, #[display(fmt = "DB error {}", _0)] DbError(String), - #[display(fmt = "My address:= {}, while current Nft owner:= {}", my_address, token_owner)] + #[display(fmt = "My address is {}, while current Nft owner is {}", my_address, token_owner)] MyAddressNotNftOwner { my_address: String, token_owner: String, diff --git a/mm2src/coins/nft.rs b/mm2src/coins/nft.rs index 7b5d2933c9..1b4da067a0 100644 --- a/mm2src/coins/nft.rs +++ b/mm2src/coins/nft.rs @@ -16,11 +16,11 @@ use nft_structs::{Chain, ContractType, ConvertChain, Nft, NftFromMoralis, NftLis use crate::eth::{eth_addr_to_hex, get_eth_address, withdraw_erc1155, withdraw_erc721, EthCoin, EthCoinType, EthTxFeeDetails}; -use crate::nft::nft_errors::{MetaFromUrlError, ProtectFromSpamError, TransferConfirmationsError, +use crate::nft::nft_errors::{ClearNftDbError, MetaFromUrlError, ProtectFromSpamError, TransferConfirmationsError, UpdateSpamPhishingError}; -use crate::nft::nft_structs::{build_nft_with_empty_meta, BuildNftFields, NftCommon, NftCtx, NftTransferCommon, - PhishingDomainReq, PhishingDomainRes, RefreshMetadataReq, SpamContractReq, - SpamContractRes, TransferMeta, TransferStatus, UriMeta}; +use crate::nft::nft_structs::{build_nft_with_empty_meta, BuildNftFields, ClearNftDbReq, NftCommon, NftCtx, + NftTransferCommon, PhishingDomainReq, PhishingDomainRes, RefreshMetadataReq, + SpamContractReq, SpamContractRes, TransferMeta, TransferStatus, UriMeta}; use crate::nft::storage::{NftListStorageOps, NftTransferHistoryStorageOps}; use common::parse_rfc3339_to_timestamp; use crypto::StandardHDCoinAddress; @@ -1372,3 +1372,29 @@ pub(crate) fn get_domain_from_url(url: Option<&str>) -> Option { url.and_then(|uri| Url::parse(uri).ok()) .and_then(|url| url.domain().map(String::from)) } + +/// todo add doc com +pub async fn clear_nft_db(ctx: MmArc, req: ClearNftDbReq) -> MmResult<(), ClearNftDbError> { + let chains = if req.clear_all { + vec![Chain::Avalanche, Chain::Bsc, Chain::Eth, Chain::Fantom, Chain::Polygon] + } else { + req.chains + }; + clear_nft_data_for_chains(&ctx, chains).await +} + +async fn clear_nft_data_for_chains(ctx: &MmArc, chains: Vec) -> MmResult<(), ClearNftDbError> { + let nft_ctx = NftCtx::from_ctx(ctx).map_to_mm(ClearNftDbError::Internal)?; + + let storage = nft_ctx.lock_db().await?; + for chain in &chains { + let is_nft_list_init = NftListStorageOps::is_initialized(&storage, chain).await?; + let is_history_init = NftTransferHistoryStorageOps::is_initialized(&storage, chain).await?; + if !is_nft_list_init && !is_history_init { + continue; + } + storage.clear_nft_data(chain).await?; + storage.clear_history_data(chain).await?; + } + Ok(()) +} diff --git a/mm2src/coins/nft/nft_errors.rs b/mm2src/coins/nft/nft_errors.rs index 1c61545b7f..14ab71c9ef 100644 --- a/mm2src/coins/nft/nft_errors.rs +++ b/mm2src/coins/nft/nft_errors.rs @@ -380,3 +380,28 @@ impl From for TransferConfirmationsError { } } } + +#[derive(Clone, Debug, Deserialize, Display, PartialEq, Serialize, SerializeErrorType)] +#[serde(tag = "error_type", content = "error_data")] +pub enum ClearNftDbError { + #[display(fmt = "DB error {}", _0)] + DbError(String), + #[display(fmt = "Internal: {}", _0)] + Internal(String), +} + +impl From for ClearNftDbError { + fn from(err: T) -> Self { ClearNftDbError::DbError(format!("{:?}", err)) } +} + +impl From for ClearNftDbError { + fn from(e: LockDBError) -> Self { ClearNftDbError::DbError(e.to_string()) } +} + +impl HttpStatusCode for ClearNftDbError { + fn status_code(&self) -> StatusCode { + match self { + ClearNftDbError::DbError(_) | ClearNftDbError::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR, + } + } +} diff --git a/mm2src/coins/nft/nft_structs.rs b/mm2src/coins/nft/nft_structs.rs index 9173f3bd5b..ff514f14de 100644 --- a/mm2src/coins/nft/nft_structs.rs +++ b/mm2src/coins/nft/nft_structs.rs @@ -732,3 +732,10 @@ where let s = String::deserialize(deserializer)?; BigUint::from_str(&s).map_err(serde::de::Error::custom) } + +#[derive(Debug, Deserialize)] +pub struct ClearNftDbReq { + pub(crate) chains: Vec, + #[serde(default)] + pub(crate) clear_all: bool, +} diff --git a/mm2src/coins/nft/nft_tests.rs b/mm2src/coins/nft/nft_tests.rs index 99ec3925de..d2e3ff26c9 100644 --- a/mm2src/coins/nft/nft_tests.rs +++ b/mm2src/coins/nft/nft_tests.rs @@ -395,6 +395,36 @@ cross_test!(test_exclude_nft_phishing_spam, { assert_eq!(nfts.len(), 2); }); +cross_test!(test_clear_nft, { + let chain = Chain::Bsc; + let nft_ctx = get_nft_ctx(&chain).await; + let storage = nft_ctx.lock_db().await.unwrap(); + NftListStorageOps::init(&storage, &chain).await.unwrap(); + let nft = nft(); + storage.add_nfts_to_list(chain, vec![nft], 28056726).await.unwrap(); + + storage.clear_nft_data(&chain).await.unwrap(); + test_clear_nft_target(&storage, &chain).await; +}); + +#[cfg(not(target_arch = "wasm32"))] +async fn test_clear_nft_target(storage: &S, chain: &Chain) { + let is_initialized = NftListStorageOps::is_initialized(storage, chain).await.unwrap(); + assert!(!is_initialized); + + let is_err = storage.get_nft_list(vec![*chain], false, 10, None, None).await.is_err(); + assert!(is_err); + + let is_err = storage.get_last_scanned_block(chain).await.is_err(); + assert!(is_err); +} + +#[cfg(target_arch = "wasm32")] +async fn test_clear_nft_target(storage: &S, chain: &Chain) { + let nft_list = storage.get_nft_list(vec![*chain], true, 1, None, None).await.unwrap(); + assert!(nft_list.nfts.is_empty()); +} + cross_test!(test_add_get_transfers, { let chain = Chain::Bsc; let nft_ctx = get_nft_ctx(&chain).await; @@ -527,7 +557,7 @@ cross_test!(test_get_update_transfer_meta, { storage.add_transfers_to_history(chain, transfers).await.unwrap(); let vec_token_add_id = storage.get_transfers_with_empty_meta(chain).await.unwrap(); - assert_eq!(vec_token_add_id.len(), 3); + assert_eq!(vec_token_add_id.len(), 2); let token_add = "0x5c7d6712dfaf0cb079d48981781c8705e8417ca0".to_string(); let transfer_meta = TransferMeta { @@ -693,3 +723,32 @@ cross_test!(test_exclude_transfer_phishing_spam, { .transfer_history; assert_eq!(transfers.len(), 1); }); + +cross_test!(test_clear_history, { + let chain = Chain::Bsc; + let nft_ctx = get_nft_ctx(&chain).await; + let storage = nft_ctx.lock_db().await.unwrap(); + NftTransferHistoryStorageOps::init(&storage, &chain).await.unwrap(); + let transfers = nft_transfer_history(); + storage.add_transfers_to_history(chain, transfers).await.unwrap(); + + storage.clear_history_data(&chain).await.unwrap(); + test_clear_history_target(&storage, &chain).await; +}); + +#[cfg(not(target_arch = "wasm32"))] +async fn test_clear_history_target(storage: &S, chain: &Chain) { + let is_init = NftTransferHistoryStorageOps::is_initialized(storage, chain) + .await + .unwrap(); + assert!(!is_init); +} + +#[cfg(target_arch = "wasm32")] +async fn test_clear_history_target(storage: &S, chain: &Chain) { + let transfer_list = storage + .get_transfer_history(vec![*chain], true, 1, None, None) + .await + .unwrap(); + assert!(transfer_list.transfer_history.is_empty()); +} diff --git a/mm2src/coins/nft/storage/mod.rs b/mm2src/coins/nft/storage/mod.rs index eaee9649da..196041d962 100644 --- a/mm2src/coins/nft/storage/mod.rs +++ b/mm2src/coins/nft/storage/mod.rs @@ -107,6 +107,8 @@ pub trait NftListStorageOps { domain: String, possible_phishing: bool, ) -> MmResult<(), Self::Error>; + + async fn clear_nft_data(&self, chain: &Chain) -> MmResult<(), Self::Error>; } /// Provides asynchronous operations related to the history of NFT transfers. @@ -196,6 +198,8 @@ pub trait NftTransferHistoryStorageOps { domain: String, possible_phishing: bool, ) -> MmResult<(), Self::Error>; + + async fn clear_history_data(&self, chain: &Chain) -> MmResult<(), Self::Error>; } /// `get_offset_limit` function calculates offset and limit for final result if we use pagination. diff --git a/mm2src/coins/nft/storage/sql_storage.rs b/mm2src/coins/nft/storage/sql_storage.rs index 86166a4793..aa513251b1 100644 --- a/mm2src/coins/nft/storage/sql_storage.rs +++ b/mm2src/coins/nft/storage/sql_storage.rs @@ -898,6 +898,33 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { .await .map_to_mm(AsyncConnError::from) } + + async fn clear_nft_data(&self, chain: &Chain) -> MmResult<(), Self::Error> { + let table_nft_name = chain.nft_list_table_name()?; + let sql_nft = format!("DROP TABLE {};", table_nft_name); + let table_scanned_blocks_name = scanned_nft_blocks_table_name()?; + let sql_scanned_block = format!("DELETE from {} where chain=?1", table_scanned_blocks_name); + let scanned_block_param = [chain.to_ticker()]; + self.call(move |conn| { + let sql_transaction = conn.transaction()?; + sql_transaction.execute(&sql_nft, [])?; + sql_transaction.execute(&sql_scanned_block, scanned_block_param)?; + sql_transaction.commit()?; + if is_table_empty(conn, table_scanned_blocks_name.as_str())? { + conn.execute(&format!("DROP TABLE {};", table_scanned_blocks_name), []) + .map(|_| ())?; + } + Ok(()) + }) + .await + .map_to_mm(AsyncConnError::from) + } +} + +fn is_table_empty(conn: &Connection, table_name: &str) -> Result { + let mut stmt = conn.prepare(&format!("SELECT COUNT(*) FROM {}", table_name))?; + let count: i64 = stmt.query_row([], |row| row.get(0))?; + Ok(count == 0) } #[async_trait] @@ -1215,4 +1242,16 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { .await .map_to_mm(AsyncConnError::from) } + + async fn clear_history_data(&self, chain: &Chain) -> MmResult<(), Self::Error> { + let table_name = chain.transfer_history_table_name()?; + self.call(move |conn| { + let sql_transaction = conn.transaction()?; + sql_transaction.execute(&format!("DROP TABLE {};", table_name), [])?; + sql_transaction.commit()?; + Ok(()) + }) + .await + .map_to_mm(AsyncConnError::from) + } } diff --git a/mm2src/coins/nft/storage/wasm/wasm_storage.rs b/mm2src/coins/nft/storage/wasm/wasm_storage.rs index faf79f663a..55facb398b 100644 --- a/mm2src/coins/nft/storage/wasm/wasm_storage.rs +++ b/mm2src/coins/nft/storage/wasm/wasm_storage.rs @@ -421,6 +421,18 @@ impl NftListStorageOps for NftCacheIDBLocked<'_> { update_nft_phishing_for_index(&table, &chain_str, external_index, &domain, possible_phishing).await?; Ok(()) } + + async fn clear_nft_data(&self, chain: &Chain) -> MmResult<(), Self::Error> { + let db_transaction = self.get_inner().transaction().await?; + let nft_table = db_transaction.table::().await?; + let last_scanned_block_table = db_transaction.table::().await?; + + nft_table.delete_items_by_index("chain", chain.to_string()).await?; + last_scanned_block_table + .delete_item_by_unique_index("chain", chain.to_string()) + .await?; + Ok(()) + } } #[async_trait] @@ -722,6 +734,13 @@ impl NftTransferHistoryStorageOps for NftCacheIDBLocked<'_> { .await?; Ok(()) } + + async fn clear_history_data(&self, chain: &Chain) -> MmResult<(), Self::Error> { + let db_transaction = self.get_inner().transaction().await?; + let table = db_transaction.table::().await?; + table.delete_items_by_index("chain", chain.to_string()).await?; + Ok(()) + } } async fn update_transfer_phishing_for_index( diff --git a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs index 5645b9a68f..cb3d6cf75b 100644 --- a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs +++ b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs @@ -49,7 +49,8 @@ use http::Response; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_rpc::mm_protocol::{MmRpcBuilder, MmRpcRequest, MmRpcVersion}; -use nft::{get_nft_list, get_nft_metadata, get_nft_transfers, refresh_nft_metadata, update_nft, withdraw_nft}; +use nft::{clear_nft_db, get_nft_list, get_nft_metadata, get_nft_transfers, refresh_nft_metadata, update_nft, + withdraw_nft}; use serde::de::DeserializeOwned; use serde_json::{self as json, Value as Json}; use std::net::SocketAddr; @@ -156,6 +157,7 @@ async fn dispatcher_v2(request: MmRpcRequest, ctx: MmArc) -> DispatcherResult handle_mmrpc(ctx, request, add_delegation).await, "add_node_to_version_stat" => handle_mmrpc(ctx, request, add_node_to_version_stat).await, "best_orders" => handle_mmrpc(ctx, request, best_orders_rpc_v2).await, + "clear_nft_db" => handle_mmrpc(ctx, request, clear_nft_db).await, "enable_bch_with_tokens" => handle_mmrpc(ctx, request, enable_platform_coin_with_tokens::).await, "enable_slp" => handle_mmrpc(ctx, request, enable_token::).await, "enable_eth_with_tokens" => handle_mmrpc(ctx, request, enable_platform_coin_with_tokens::).await, From 50d2ed48bb3f371a976bbb74e060f13626cd401a Mon Sep 17 00:00:00 2001 From: laruh Date: Wed, 13 Dec 2023 18:53:58 +0700 Subject: [PATCH 04/13] `EnumVariantList` macro derive, clear all methods --- mm2src/coins/nft.rs | 44 +++++++----- mm2src/coins/nft/nft_structs.rs | 3 +- mm2src/coins/nft/storage/mod.rs | 6 ++ mm2src/coins/nft/storage/sql_storage.rs | 46 ++++++++++--- mm2src/coins/nft/storage/wasm/wasm_storage.rs | 4 ++ mm2src/derives/enum_from/src/lib.rs | 67 +++++++++++++++++-- 6 files changed, 138 insertions(+), 32 deletions(-) diff --git a/mm2src/coins/nft.rs b/mm2src/coins/nft.rs index 1b4da067a0..02dc6e6ec6 100644 --- a/mm2src/coins/nft.rs +++ b/mm2src/coins/nft.rs @@ -22,7 +22,7 @@ use crate::nft::nft_structs::{build_nft_with_empty_meta, BuildNftFields, ClearNf NftTransferCommon, PhishingDomainReq, PhishingDomainRes, RefreshMetadataReq, SpamContractReq, SpamContractRes, TransferMeta, TransferStatus, UriMeta}; use crate::nft::storage::{NftListStorageOps, NftTransferHistoryStorageOps}; -use common::parse_rfc3339_to_timestamp; +use common::{log::error, parse_rfc3339_to_timestamp}; use crypto::StandardHDCoinAddress; use ethereum_types::{Address, H256}; use futures::compat::Future01CompatExt; @@ -1373,27 +1373,37 @@ pub(crate) fn get_domain_from_url(url: Option<&str>) -> Option { .and_then(|url| url.domain().map(String::from)) } -/// todo add doc com +/// Clears NFT data from the database for specified chains. pub async fn clear_nft_db(ctx: MmArc, req: ClearNftDbReq) -> MmResult<(), ClearNftDbError> { - let chains = if req.clear_all { - vec![Chain::Avalanche, Chain::Bsc, Chain::Eth, Chain::Fantom, Chain::Polygon] - } else { - req.chains - }; - clear_nft_data_for_chains(&ctx, chains).await -} - -async fn clear_nft_data_for_chains(ctx: &MmArc, chains: Vec) -> MmResult<(), ClearNftDbError> { - let nft_ctx = NftCtx::from_ctx(ctx).map_to_mm(ClearNftDbError::Internal)?; + let nft_ctx = NftCtx::from_ctx(&ctx).map_to_mm(ClearNftDbError::Internal)?; let storage = nft_ctx.lock_db().await?; - for chain in &chains { - let is_nft_list_init = NftListStorageOps::is_initialized(&storage, chain).await?; - let is_history_init = NftTransferHistoryStorageOps::is_initialized(&storage, chain).await?; - if !is_nft_list_init && !is_history_init { - continue; + if req.clear_all { + storage.clear_all_nft_data().await?; + storage.clear_all_history_data().await?; + } else { + for chain in req.chains.iter() { + if let Err(e) = clear_data_for_chain(&storage, chain).await { + error!("Failed to clear data for chain {}: {}", chain, e); + } } + } + + Ok(()) +} + +async fn clear_data_for_chain(storage: &T, chain: &Chain) -> MmResult<(), ClearNftDbError> +where + T: NftListStorageOps + NftTransferHistoryStorageOps, +{ + let (is_nft_list_init, is_history_init) = ( + NftListStorageOps::is_initialized(storage, chain).await?, + NftTransferHistoryStorageOps::is_initialized(storage, chain).await?, + ); + if is_nft_list_init { storage.clear_nft_data(chain).await?; + } + if is_history_init { storage.clear_history_data(chain).await?; } Ok(()) diff --git a/mm2src/coins/nft/nft_structs.rs b/mm2src/coins/nft/nft_structs.rs index ff514f14de..b114b1eb69 100644 --- a/mm2src/coins/nft/nft_structs.rs +++ b/mm2src/coins/nft/nft_structs.rs @@ -1,4 +1,5 @@ use common::ten; +use enum_from::EnumVariantList; use ethereum_types::Address; use mm2_core::mm_ctx::{from_ctx, MmArc}; use mm2_err_handle::prelude::*; @@ -103,7 +104,7 @@ pub struct RefreshMetadataReq { /// Represents blockchains which are supported by NFT feature. /// Currently there are only EVM based chains. -#[derive(Clone, Copy, Debug, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, EnumVariantList, PartialEq, Serialize)] #[serde(rename_all = "UPPERCASE")] pub enum Chain { Avalanche, diff --git a/mm2src/coins/nft/storage/mod.rs b/mm2src/coins/nft/storage/mod.rs index 196041d962..ad255100c3 100644 --- a/mm2src/coins/nft/storage/mod.rs +++ b/mm2src/coins/nft/storage/mod.rs @@ -109,6 +109,9 @@ pub trait NftListStorageOps { ) -> MmResult<(), Self::Error>; async fn clear_nft_data(&self, chain: &Chain) -> MmResult<(), Self::Error>; + + /// Clears all nft list tables related to each chain. + async fn clear_all_nft_data(&self) -> MmResult<(), Self::Error>; } /// Provides asynchronous operations related to the history of NFT transfers. @@ -200,6 +203,9 @@ pub trait NftTransferHistoryStorageOps { ) -> MmResult<(), Self::Error>; async fn clear_history_data(&self, chain: &Chain) -> MmResult<(), Self::Error>; + + /// Clears all nft history tables related to each chain. + async fn clear_all_history_data(&self) -> MmResult<(), Self::Error>; } /// `get_offset_limit` function calculates offset and limit for final result if we use pagination. diff --git a/mm2src/coins/nft/storage/sql_storage.rs b/mm2src/coins/nft/storage/sql_storage.rs index aa513251b1..8a20ad2f39 100644 --- a/mm2src/coins/nft/storage/sql_storage.rs +++ b/mm2src/coins/nft/storage/sql_storage.rs @@ -528,6 +528,12 @@ fn get_transfers_with_empty_meta_builder<'a>(conn: &'a Connection, chain: &'a Ch Ok(sql_builder) } +fn is_table_empty(conn: &Connection, table_name: &str) -> Result { + let mut stmt = conn.prepare(&format!("SELECT COUNT(*) FROM {}", table_name))?; + let count: i64 = stmt.query_row([], |row| row.get(0))?; + Ok(count == 0) +} + #[async_trait] impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { type Error = AsyncConnError; @@ -901,7 +907,7 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { async fn clear_nft_data(&self, chain: &Chain) -> MmResult<(), Self::Error> { let table_nft_name = chain.nft_list_table_name()?; - let sql_nft = format!("DROP TABLE {};", table_nft_name); + let sql_nft = format!("DROP TABLE IF EXISTS {};", table_nft_name); let table_scanned_blocks_name = scanned_nft_blocks_table_name()?; let sql_scanned_block = format!("DELETE from {} where chain=?1", table_scanned_blocks_name); let scanned_block_param = [chain.to_ticker()]; @@ -911,7 +917,7 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { sql_transaction.execute(&sql_scanned_block, scanned_block_param)?; sql_transaction.commit()?; if is_table_empty(conn, table_scanned_blocks_name.as_str())? { - conn.execute(&format!("DROP TABLE {};", table_scanned_blocks_name), []) + conn.execute(&format!("DROP TABLE IF EXISTS {};", table_scanned_blocks_name), []) .map(|_| ())?; } Ok(()) @@ -919,12 +925,22 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { .await .map_to_mm(AsyncConnError::from) } -} -fn is_table_empty(conn: &Connection, table_name: &str) -> Result { - let mut stmt = conn.prepare(&format!("SELECT COUNT(*) FROM {}", table_name))?; - let count: i64 = stmt.query_row([], |row| row.get(0))?; - Ok(count == 0) + async fn clear_all_nft_data(&self) -> MmResult<(), Self::Error> { + self.call(move |conn| { + let sql_transaction = conn.transaction()?; + for chain in Chain::variant_list().into_iter() { + let table_name = chain.nft_list_table_name()?; + sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_name), [])?; + } + let table_scanned_blocks_name = scanned_nft_blocks_table_name()?; + sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_scanned_blocks_name), [])?; + sql_transaction.commit()?; + Ok(()) + }) + .await + .map_to_mm(AsyncConnError::from) + } } #[async_trait] @@ -1247,7 +1263,21 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.transfer_history_table_name()?; self.call(move |conn| { let sql_transaction = conn.transaction()?; - sql_transaction.execute(&format!("DROP TABLE {};", table_name), [])?; + sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_name), [])?; + sql_transaction.commit()?; + Ok(()) + }) + .await + .map_to_mm(AsyncConnError::from) + } + + async fn clear_all_history_data(&self) -> MmResult<(), Self::Error> { + self.call(move |conn| { + let sql_transaction = conn.transaction()?; + for chain in Chain::variant_list().into_iter() { + let table_name = chain.transfer_history_table_name()?; + sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_name), [])?; + } sql_transaction.commit()?; Ok(()) }) diff --git a/mm2src/coins/nft/storage/wasm/wasm_storage.rs b/mm2src/coins/nft/storage/wasm/wasm_storage.rs index 55facb398b..678021840a 100644 --- a/mm2src/coins/nft/storage/wasm/wasm_storage.rs +++ b/mm2src/coins/nft/storage/wasm/wasm_storage.rs @@ -433,6 +433,8 @@ impl NftListStorageOps for NftCacheIDBLocked<'_> { .await?; Ok(()) } + + async fn clear_all_nft_data(&self) -> MmResult<(), Self::Error> { todo!() } } #[async_trait] @@ -741,6 +743,8 @@ impl NftTransferHistoryStorageOps for NftCacheIDBLocked<'_> { table.delete_items_by_index("chain", chain.to_string()).await?; Ok(()) } + + async fn clear_all_history_data(&self) -> MmResult<(), Self::Error> { todo!() } } async fn update_transfer_phishing_for_index( diff --git a/mm2src/derives/enum_from/src/lib.rs b/mm2src/derives/enum_from/src/lib.rs index 83aef6f6f0..7ebc27d36a 100644 --- a/mm2src/derives/enum_from/src/lib.rs +++ b/mm2src/derives/enum_from/src/lib.rs @@ -3,14 +3,16 @@ use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::quote; use std::fmt; use syn::Meta::List; -use syn::{parse_macro_input, Data, DeriveInput, Error, Field, Fields, ImplGenerics, Type, TypeGenerics, WhereClause}; +use syn::{parse_macro_input, Data, DataEnum, DeriveInput, Error, Field, Fields, ImplGenerics, Type, TypeGenerics, + WhereClause}; use syn::{Attribute, NestedMeta, Variant}; mod from_inner; mod from_stringify; mod from_trait; -const MACRO_IDENT: &str = "EnumFromInner"; +const ENUM_FROM_INNER_IDENT: &str = "EnumFromInner"; +const ENUM_VARIANT_LIST_IDENT: &str = "EnumVariantList"; /// Implements `From` trait for the given enumeration. /// @@ -124,6 +126,59 @@ pub fn derive(input: TokenStream) -> TokenStream { } } +/// `EnumVariantList` is a procedural macro used to generate a method that returns a vector containing all variants of an enum. +/// This macro is intended for use with simple enums (enums without associated data or complex structures). +/// +/// ### USAGE: +/// +/// ```rust +/// use enum_from::EnumVariantList; +/// +/// #[derive(EnumVariantList)] +/// enum Chain { +/// Avalanche, +/// Bsc, +/// Eth, +/// Fantom, +/// Polygon, +/// } +/// +///#[test] +///fn test_enum_variant_list() { +/// let all_chains = Chain::variant_list(); +/// assert_eq!(all_chains, vec![ +/// Chain::Avalanche, +/// Chain::Bsc, +/// Chain::Eth, +/// Chain::Fantom, +/// Chain::Polygon +/// ]); +///} +/// ``` +#[proc_macro_derive(EnumVariantList)] +pub fn enum_variant_list(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let name = input.ident; + + let variants = match input.data { + Data::Enum(DataEnum { variants, .. }) => variants, + Data::Struct(_) => return CompileError::expected_enum(ENUM_VARIANT_LIST_IDENT, "struct").into(), + Data::Union(_) => return CompileError::expected_enum(ENUM_VARIANT_LIST_IDENT, "union").into(), + }; + + let variant_list: Vec<_> = variants.iter().map(|v| &v.ident).collect(); + + let expanded = quote! { + impl #name { + pub fn variant_list() -> Vec<#name> { + vec![ #( #name::#variant_list ),* ] + } + } + }; + + TokenStream::from(expanded) +} + #[allow(clippy::enum_variant_names)] #[derive(Clone, Copy)] enum MacroAttr { @@ -148,8 +203,8 @@ impl fmt::Display for MacroAttr { struct CompileError(String); impl CompileError { - fn expected_enum(found: &str) -> CompileError { - CompileError(format!("'{}' cannot be implement for a {}", MACRO_IDENT, found)) + fn expected_enum(macro_ident: &str, found: &str) -> CompileError { + CompileError(format!("'{}' cannot be implement for a {}", macro_ident, found)) } fn expected_unnamed_inner(attr: MacroAttr) -> CompileError { @@ -229,8 +284,8 @@ impl<'a> UnnamedInnerField<'a> { fn derive_enum_from_macro(input: DeriveInput, attr: MacroAttr) -> Result { let enumeration = match input.data { Data::Enum(ref enumeration) => enumeration, - Data::Struct(_) => return Err(CompileError::expected_enum("struct")), - Data::Union(_) => return Err(CompileError::expected_enum("union")), + Data::Struct(_) => return Err(CompileError::expected_enum(ENUM_FROM_INNER_IDENT, "struct")), + Data::Union(_) => return Err(CompileError::expected_enum(ENUM_FROM_INNER_IDENT, "union")), }; let ctx = IdentCtx::from(&input); From a9a68f086208a380a1957235237d5df08968c1dc Mon Sep 17 00:00:00 2001 From: laruh Date: Thu, 14 Dec 2023 13:04:32 +0700 Subject: [PATCH 05/13] finish todo methods, add more tests --- mm2src/coins/nft/nft_errors.rs | 6 +++++ mm2src/coins/nft/nft_structs.rs | 6 +++++ mm2src/coins/nft/nft_tests.rs | 24 +++++++++++++++++++ mm2src/coins/nft/storage/wasm/wasm_storage.rs | 16 +++++++++++-- 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/mm2src/coins/nft/nft_errors.rs b/mm2src/coins/nft/nft_errors.rs index 14ab71c9ef..2bc2147188 100644 --- a/mm2src/coins/nft/nft_errors.rs +++ b/mm2src/coins/nft/nft_errors.rs @@ -381,6 +381,12 @@ impl From for TransferConfirmationsError { } } +/// Enumerates errors that can occur while clearing NFT data from the database. +/// +/// Variants: +/// - `DbError`: Represents errors related to database operations. +/// - `Internal`: Indicates internal errors not directly associated with database +/// operations. #[derive(Clone, Debug, Deserialize, Display, PartialEq, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] pub enum ClearNftDbError { diff --git a/mm2src/coins/nft/nft_structs.rs b/mm2src/coins/nft/nft_structs.rs index b114b1eb69..19880c7fa2 100644 --- a/mm2src/coins/nft/nft_structs.rs +++ b/mm2src/coins/nft/nft_structs.rs @@ -734,6 +734,12 @@ where BigUint::from_str(&s).map_err(serde::de::Error::custom) } +/// Request parameters for clearing NFT data from the database. +/// +/// - `chains`: Specifies the blockchain networks (e.g., Ethereum, BSC) for which +/// to clear NFT data. Used only when `clear_all` is `false`. +/// - `clear_all`: If `true`, clears NFT data for all chains, ignoring the `chains` field. +/// Defaults to `false`. #[derive(Debug, Deserialize)] pub struct ClearNftDbReq { pub(crate) chains: Vec, diff --git a/mm2src/coins/nft/nft_tests.rs b/mm2src/coins/nft/nft_tests.rs index d2e3ff26c9..151d3af02d 100644 --- a/mm2src/coins/nft/nft_tests.rs +++ b/mm2src/coins/nft/nft_tests.rs @@ -407,6 +407,18 @@ cross_test!(test_clear_nft, { test_clear_nft_target(&storage, &chain).await; }); +cross_test!(test_clear_all_nft, { + let chain = Chain::Bsc; + let nft_ctx = get_nft_ctx(&chain).await; + let storage = nft_ctx.lock_db().await.unwrap(); + NftListStorageOps::init(&storage, &chain).await.unwrap(); + let nft = nft(); + storage.add_nfts_to_list(chain, vec![nft], 28056726).await.unwrap(); + + storage.clear_all_nft_data().await.unwrap(); + test_clear_nft_target(&storage, &chain).await; +}); + #[cfg(not(target_arch = "wasm32"))] async fn test_clear_nft_target(storage: &S, chain: &Chain) { let is_initialized = NftListStorageOps::is_initialized(storage, chain).await.unwrap(); @@ -736,6 +748,18 @@ cross_test!(test_clear_history, { test_clear_history_target(&storage, &chain).await; }); +cross_test!(test_clear_all_history, { + let chain = Chain::Bsc; + let nft_ctx = get_nft_ctx(&chain).await; + let storage = nft_ctx.lock_db().await.unwrap(); + NftTransferHistoryStorageOps::init(&storage, &chain).await.unwrap(); + let transfers = nft_transfer_history(); + storage.add_transfers_to_history(chain, transfers).await.unwrap(); + + storage.clear_all_history_data().await.unwrap(); + test_clear_history_target(&storage, &chain).await; +}); + #[cfg(not(target_arch = "wasm32"))] async fn test_clear_history_target(storage: &S, chain: &Chain) { let is_init = NftTransferHistoryStorageOps::is_initialized(storage, chain) diff --git a/mm2src/coins/nft/storage/wasm/wasm_storage.rs b/mm2src/coins/nft/storage/wasm/wasm_storage.rs index 678021840a..7cb1c9b998 100644 --- a/mm2src/coins/nft/storage/wasm/wasm_storage.rs +++ b/mm2src/coins/nft/storage/wasm/wasm_storage.rs @@ -434,7 +434,14 @@ impl NftListStorageOps for NftCacheIDBLocked<'_> { Ok(()) } - async fn clear_all_nft_data(&self) -> MmResult<(), Self::Error> { todo!() } + async fn clear_all_nft_data(&self) -> MmResult<(), Self::Error> { + let db_transaction = self.get_inner().transaction().await?; + let nft_table = db_transaction.table::().await?; + let last_scanned_block_table = db_transaction.table::().await?; + nft_table.clear().await?; + last_scanned_block_table.clear().await?; + Ok(()) + } } #[async_trait] @@ -744,7 +751,12 @@ impl NftTransferHistoryStorageOps for NftCacheIDBLocked<'_> { Ok(()) } - async fn clear_all_history_data(&self) -> MmResult<(), Self::Error> { todo!() } + async fn clear_all_history_data(&self) -> MmResult<(), Self::Error> { + let db_transaction = self.get_inner().transaction().await?; + let table = db_transaction.table::().await?; + table.clear().await?; + Ok(()) + } } async fn update_transfer_phishing_for_index( From 087f5da72c45d6b978d57a691c79fea5714ba311 Mon Sep 17 00:00:00 2001 From: laruh Date: Thu, 14 Dec 2023 18:06:55 +0700 Subject: [PATCH 06/13] return InvalidRequest at the begging --- mm2src/coins/nft.rs | 11 ++++++++--- mm2src/coins/nft/nft_errors.rs | 3 +++ mm2src/coins/nft/nft_structs.rs | 8 +++++--- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/mm2src/coins/nft.rs b/mm2src/coins/nft.rs index 02dc6e6ec6..ca5a651680 100644 --- a/mm2src/coins/nft.rs +++ b/mm2src/coins/nft.rs @@ -1375,14 +1375,19 @@ pub(crate) fn get_domain_from_url(url: Option<&str>) -> Option { /// Clears NFT data from the database for specified chains. pub async fn clear_nft_db(ctx: MmArc, req: ClearNftDbReq) -> MmResult<(), ClearNftDbError> { + if req.chains.is_none() && !req.clear_all { + return MmError::err(ClearNftDbError::InvalidRequest( + "Nothing to clear was specified".to_string(), + )); + } let nft_ctx = NftCtx::from_ctx(&ctx).map_to_mm(ClearNftDbError::Internal)?; - let storage = nft_ctx.lock_db().await?; + if req.clear_all { storage.clear_all_nft_data().await?; storage.clear_all_history_data().await?; - } else { - for chain in req.chains.iter() { + } else if let Some(chains) = req.chains { + for chain in chains.iter() { if let Err(e) = clear_data_for_chain(&storage, chain).await { error!("Failed to clear data for chain {}: {}", chain, e); } diff --git a/mm2src/coins/nft/nft_errors.rs b/mm2src/coins/nft/nft_errors.rs index 2bc2147188..954dcc13db 100644 --- a/mm2src/coins/nft/nft_errors.rs +++ b/mm2src/coins/nft/nft_errors.rs @@ -394,6 +394,8 @@ pub enum ClearNftDbError { DbError(String), #[display(fmt = "Internal: {}", _0)] Internal(String), + #[display(fmt = "Invalid request: {}", _0)] + InvalidRequest(String), } impl From for ClearNftDbError { @@ -407,6 +409,7 @@ impl From for ClearNftDbError { impl HttpStatusCode for ClearNftDbError { fn status_code(&self) -> StatusCode { match self { + ClearNftDbError::InvalidRequest(_) => StatusCode::BAD_REQUEST, ClearNftDbError::DbError(_) | ClearNftDbError::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR, } } diff --git a/mm2src/coins/nft/nft_structs.rs b/mm2src/coins/nft/nft_structs.rs index 19880c7fa2..d5f91a7cb7 100644 --- a/mm2src/coins/nft/nft_structs.rs +++ b/mm2src/coins/nft/nft_structs.rs @@ -736,13 +736,15 @@ where /// Request parameters for clearing NFT data from the database. /// -/// - `chains`: Specifies the blockchain networks (e.g., Ethereum, BSC) for which -/// to clear NFT data. Used only when `clear_all` is `false`. +/// - `chains`: (Optional) Specifies the blockchain networks (e.g., Ethereum, BSC) to clear NFT data. +/// This field is used and required only when `clear_all` is `false`. /// - `clear_all`: If `true`, clears NFT data for all chains, ignoring the `chains` field. /// Defaults to `false`. +/// +/// Note: If `clear_all` is `false` and `chains` is `None`, the request will be considered invalid and result in an error. #[derive(Debug, Deserialize)] pub struct ClearNftDbReq { - pub(crate) chains: Vec, + pub(crate) chains: Option>, #[serde(default)] pub(crate) clear_all: bool, } From ab0ddc9f5ecb5dce489fa749edd1f40c1a7110a7 Mon Sep 17 00:00:00 2001 From: laruh Date: Thu, 14 Dec 2023 19:05:56 +0700 Subject: [PATCH 07/13] doc comms --- mm2src/coins/eth.rs | 15 ++++++++++++--- mm2src/coins/nft/nft_errors.rs | 11 +++++++++++ mm2src/coins/nft/nft_structs.rs | 26 ++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 0a8c78e6b6..9165fe5471 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -878,7 +878,7 @@ async fn withdraw_impl(coin: EthCoin, req: WithdrawRequest) -> WithdrawResult { pub async fn withdraw_erc1155(ctx: MmArc, withdraw_type: WithdrawErc1155) -> WithdrawNftResult { let coin = lp_coinfind_or_err(&ctx, withdraw_type.chain.to_ticker()).await?; let (to_addr, token_addr, eth_coin) = - get_valid_nft_add_to_withdraw(coin, &withdraw_type.to, &withdraw_type.token_address)?; + get_valid_nft_addr_to_withdraw(coin, &withdraw_type.to, &withdraw_type.token_address)?; let my_address_str = eth_coin.my_address()?; let token_id_str = &withdraw_type.token_id.to_string(); @@ -976,7 +976,7 @@ pub async fn withdraw_erc1155(ctx: MmArc, withdraw_type: WithdrawErc1155) -> Wit pub async fn withdraw_erc721(ctx: MmArc, withdraw_type: WithdrawErc721) -> WithdrawNftResult { let coin = lp_coinfind_or_err(&ctx, withdraw_type.chain.to_ticker()).await?; let (to_addr, token_addr, eth_coin) = - get_valid_nft_add_to_withdraw(coin, &withdraw_type.to, &withdraw_type.token_address)?; + get_valid_nft_addr_to_withdraw(coin, &withdraw_type.to, &withdraw_type.token_address)?; let my_address_str = eth_coin.my_address()?; let token_id_str = &withdraw_type.token_id.to_string(); @@ -5720,6 +5720,7 @@ fn increase_gas_price_by_stage(gas_price: U256, level: &FeeApproxStage) -> U256 } } +/// Represents errors that can occur while retrieving an Ethereum address. #[derive(Clone, Debug, Deserialize, Display, PartialEq, Serialize)] pub enum GetEthAddressError { PrivKeyPolicyNotAllowed(PrivKeyPolicyNotAllowed), @@ -5760,6 +5761,11 @@ pub async fn get_eth_address( }) } +/// Errors encountered while validating Ethereum addresses for NFT withdrawal. +/// +/// Variants: +/// - `CoinDoesntSupportNftWithdraw`: The specified coin does not support NFT withdrawal. +/// - `InvalidAddress`: The provided address is invalid. #[derive(Display)] pub enum GetValidEthWithdrawAddError { #[display(fmt = "{} coin doesn't support NFT withdrawing", coin)] @@ -5769,7 +5775,10 @@ pub enum GetValidEthWithdrawAddError { InvalidAddress(String), } -fn get_valid_nft_add_to_withdraw( +/// Validates Ethereum addresses for NFT withdrawal. +/// Returns a tuple of valid `to` address, `token` address, and `EthCoin` instance on success. +/// Errors if the coin doesn't support NFT withdrawal or if the addresses are invalid. +fn get_valid_nft_addr_to_withdraw( coin_enum: MmCoinEnum, to: &str, token_add: &str, diff --git a/mm2src/coins/nft/nft_errors.rs b/mm2src/coins/nft/nft_errors.rs index 954dcc13db..95ff07185d 100644 --- a/mm2src/coins/nft/nft_errors.rs +++ b/mm2src/coins/nft/nft_errors.rs @@ -345,6 +345,11 @@ impl From for MetaFromUrlError { fn from(e: GetInfoFromUriError) -> Self { MetaFromUrlError::GetInfoFromUriError(e) } } +/// Represents errors that can occur while locking the NFT database. +/// +/// Variants: +/// - `WasmNftCacheError`: Errors specific to the WebAssembly (WASM) environment's NFT cache. +/// - `SqlError`: Errors related to SQL operations in non-WASM environments. #[derive(Debug, Display)] pub enum LockDBError { #[cfg(target_arch = "wasm32")] @@ -363,6 +368,12 @@ impl From for LockDBError { fn from(e: WasmNftCacheError) -> Self { LockDBError::WasmNftCacheError(e) } } +/// Errors related to calculating transfer confirmations for NFTs. +/// +/// Variants: +/// - `NoSuchCoin`: Occurs when the specified coin does not exist. +/// - `CoinDoesntSupportNft`: Triggered when the specified coin does not support NFT operations. +/// - `GetCurrentBlockErr`: Represents errors encountered while retrieving the current block number. #[derive(Clone, Debug, Deserialize, Display, PartialEq, Serialize)] pub enum TransferConfirmationsError { #[display(fmt = "No such coin {}", coin)] diff --git a/mm2src/coins/nft/nft_structs.rs b/mm2src/coins/nft/nft_structs.rs index d5f91a7cb7..260898bd0c 100644 --- a/mm2src/coins/nft/nft_structs.rs +++ b/mm2src/coins/nft/nft_structs.rs @@ -386,6 +386,16 @@ pub struct NftList { pub(crate) total: usize, } +/// Parameters for withdrawing an ERC-1155 token. +/// +/// Fields: +/// - `chain`: The blockchain network to perform the withdrawal on. +/// - `to`: The address to send the NFT to. +/// - `token_address`: The address of the ERC-1155 token contract. +/// - `token_id`: The unique identifier of the NFT to withdraw. +/// - `amount`: Optional amount of the token to withdraw. Defaults to 1 if not specified. +/// - `max`: If set to `true`, withdraws the maximum amount available. Overrides the `amount` field. +/// - `fee`: Optional details for the withdrawal fee. #[derive(Clone, Deserialize)] pub struct WithdrawErc1155 { pub(crate) chain: Chain, @@ -399,6 +409,14 @@ pub struct WithdrawErc1155 { pub(crate) fee: Option, } +/// Parameters for withdrawing an ERC-721 token. +/// +/// Fields: +/// - `chain`: The blockchain network to perform the withdrawal on. +/// - `to`: The address to send the NFT to. +/// - `token_address`: The address of the ERC-721 token contract. +/// - `token_id`: The unique identifier of the NFT to withdraw. +/// - `fee`: Optional details for the withdrawal fee. #[derive(Clone, Deserialize)] pub struct WithdrawErc721 { pub(crate) chain: Chain, @@ -409,6 +427,11 @@ pub struct WithdrawErc721 { pub(crate) fee: Option, } +/// Represents a request for withdrawing an NFT, supporting different ERC standards. +/// +/// Variants: +/// - `WithdrawErc1155`: Parameters for withdrawing an ERC-1155 token. +/// - `WithdrawErc721`: Parameters for withdrawing an ERC-721 token. #[derive(Clone, Deserialize)] #[serde(tag = "type", content = "withdraw_data")] #[serde(rename_all = "snake_case")] @@ -417,6 +440,9 @@ pub enum WithdrawNftReq { WithdrawErc721(WithdrawErc721), } +/// Details of NFT transaction. +/// +/// Includes the raw transaction hex for broadcasting, along with additional information about the transaction. #[derive(Debug, Deserialize, Serialize)] pub struct TransactionNftDetails { /// Raw bytes of signed transaction, this should be sent as is to `send_raw_transaction` RPC to broadcast the transaction From 20f305758441f190330572b64d618adb51ce589c Mon Sep 17 00:00:00 2001 From: laruh Date: Fri, 15 Dec 2023 15:06:25 +0700 Subject: [PATCH 08/13] update doc com --- mm2src/coins/nft/nft_errors.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mm2src/coins/nft/nft_errors.rs b/mm2src/coins/nft/nft_errors.rs index 95ff07185d..a199bb3542 100644 --- a/mm2src/coins/nft/nft_errors.rs +++ b/mm2src/coins/nft/nft_errors.rs @@ -396,8 +396,8 @@ impl From for TransferConfirmationsError { /// /// Variants: /// - `DbError`: Represents errors related to database operations. -/// - `Internal`: Indicates internal errors not directly associated with database -/// operations. +/// - `Internal`: Indicates internal errors not directly associated with database operations. +/// - `InvalidRequest`: Used for various types of invalid requests, such as missing or contradictory parameters. #[derive(Clone, Debug, Deserialize, Display, PartialEq, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] pub enum ClearNftDbError { From d505c5619df67aeb58e8b494847b0c2d76e63f05 Mon Sep 17 00:00:00 2001 From: laruh Date: Mon, 15 Jan 2024 18:48:13 +0700 Subject: [PATCH 09/13] use `u256_to_big_decimal`, impl struct SafeTableName, remove prepared statement, use if/else --- mm2src/coins/eth.rs | 2 +- mm2src/coins/nft.rs | 16 +-- mm2src/coins/nft/storage/sql_storage.rs | 138 +++++++++++++----------- mm2src/db_common/src/sqlite.rs | 21 ++++ 4 files changed, 105 insertions(+), 72 deletions(-) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 5b0d21f4a9..1c579c22b0 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -4042,7 +4042,7 @@ impl EthCoin { )) }, }; - let wallet_amount = BigDecimal::from_str(&wallet_amount_uint.to_string()).map_err(NumConversError::from)?; + let wallet_amount = u256_to_big_decimal(wallet_amount_uint, self.decimals)?; Ok(wallet_amount) } diff --git a/mm2src/coins/nft.rs b/mm2src/coins/nft.rs index ca5a651680..cc543bfc4f 100644 --- a/mm2src/coins/nft.rs +++ b/mm2src/coins/nft.rs @@ -1375,23 +1375,23 @@ pub(crate) fn get_domain_from_url(url: Option<&str>) -> Option { /// Clears NFT data from the database for specified chains. pub async fn clear_nft_db(ctx: MmArc, req: ClearNftDbReq) -> MmResult<(), ClearNftDbError> { - if req.chains.is_none() && !req.clear_all { - return MmError::err(ClearNftDbError::InvalidRequest( - "Nothing to clear was specified".to_string(), - )); - } - let nft_ctx = NftCtx::from_ctx(&ctx).map_to_mm(ClearNftDbError::Internal)?; - let storage = nft_ctx.lock_db().await?; - if req.clear_all { + let nft_ctx = NftCtx::from_ctx(&ctx).map_to_mm(ClearNftDbError::Internal)?; + let storage = nft_ctx.lock_db().await?; storage.clear_all_nft_data().await?; storage.clear_all_history_data().await?; } else if let Some(chains) = req.chains { + let nft_ctx = NftCtx::from_ctx(&ctx).map_to_mm(ClearNftDbError::Internal)?; + let storage = nft_ctx.lock_db().await?; for chain in chains.iter() { if let Err(e) = clear_data_for_chain(&storage, chain).await { error!("Failed to clear data for chain {}: {}", chain, e); } } + } else { + return MmError::err(ClearNftDbError::InvalidRequest( + "Nothing to clear was specified".to_string(), + )); } Ok(()) diff --git a/mm2src/coins/nft/storage/sql_storage.rs b/mm2src/coins/nft/storage/sql_storage.rs index 8a20ad2f39..d169755e40 100644 --- a/mm2src/coins/nft/storage/sql_storage.rs +++ b/mm2src/coins/nft/storage/sql_storage.rs @@ -10,7 +10,7 @@ use db_common::sql_build::{SqlCondition, SqlQuery}; use db_common::sqlite::rusqlite::types::{FromSqlError, Type}; use db_common::sqlite::rusqlite::{Connection, Error as SqlError, Result as SqlResult, Row, Statement}; use db_common::sqlite::sql_builder::SqlBuilder; -use db_common::sqlite::{query_single_row, string_from_row, validate_table_name, CHECK_TABLE_EXISTS_SQL}; +use db_common::sqlite::{query_single_row, string_from_row, SafeTableName, CHECK_TABLE_EXISTS_SQL}; use ethereum_types::Address; use futures::lock::MutexGuard as AsyncMutexGuard; use mm2_err_handle::prelude::*; @@ -23,23 +23,23 @@ use std::num::NonZeroUsize; use std::str::FromStr; impl Chain { - fn nft_list_table_name(&self) -> SqlResult { + fn nft_list_table_name(&self) -> SqlResult { let name = self.to_ticker().to_owned() + "_nft_list"; - validate_table_name(&name)?; - Ok(name) + let safe_name = SafeTableName::new(&name)?; + Ok(safe_name) } - fn transfer_history_table_name(&self) -> SqlResult { + fn transfer_history_table_name(&self) -> SqlResult { let name = self.to_ticker().to_owned() + "_nft_transfer_history"; - validate_table_name(&name)?; - Ok(name) + let safe_name = SafeTableName::new(&name)?; + Ok(safe_name) } } -fn scanned_nft_blocks_table_name() -> SqlResult { +fn scanned_nft_blocks_table_name() -> SqlResult { let name = "scanned_nft_blocks".to_string(); - validate_table_name(&name)?; - Ok(name) + let safe_name = SafeTableName::new(&name)?; + Ok(safe_name) } fn create_nft_list_table_sql(chain: &Chain) -> MmResult { @@ -75,7 +75,7 @@ fn create_nft_list_table_sql(chain: &Chain) -> MmResult { details_json TEXT, PRIMARY KEY (token_address, token_id) );", - table_name + table_name.0 ); Ok(sql) } @@ -105,7 +105,7 @@ fn create_transfer_history_table_sql(chain: &Chain) -> Result details_json TEXT, PRIMARY KEY (transaction_hash, log_index) );", - table_name + table_name.0 ); Ok(sql) } @@ -117,7 +117,7 @@ fn create_scanned_nft_blocks_sql() -> Result { chain TEXT PRIMARY KEY, last_scanned_block INTEGER DEFAULT 0 );", - table_name + table_name.0 ); Ok(sql) } @@ -129,7 +129,7 @@ fn get_nft_list_builder_preimage(chains: Vec, filters: Option, filters: Option) -> Result { - let mut sql_builder = SqlBuilder::select_from(table_name); +fn nft_list_builder_preimage( + table_name: SafeTableName, + filters: Option, +) -> Result { + let mut sql_builder = SqlBuilder::select_from(table_name.0); if let Some(filters) = filters { if filters.exclude_spam { sql_builder.and_where("possible_spam == 0"); @@ -167,7 +170,7 @@ fn get_nft_transfer_builder_preimage( .into_iter() .map(|chain| { let table_name = chain.transfer_history_table_name()?; - let sql_builder = nft_history_table_builder_preimage(table_name.as_str(), filters)?; + let sql_builder = nft_history_table_builder_preimage(table_name, filters)?; let sql_string = sql_builder .sql() .map_err(|e| SqlError::ToSqlConversionFailure(e.into()))? @@ -184,10 +187,10 @@ fn get_nft_transfer_builder_preimage( } fn nft_history_table_builder_preimage( - table_name: &str, + table_name: SafeTableName, filters: Option, ) -> Result { - let mut sql_builder = SqlBuilder::select_from(table_name); + let mut sql_builder = SqlBuilder::select_from(table_name.0); if let Some(filters) = filters { if filters.send && !filters.receive { sql_builder.and_where_eq("status", "'Send'"); @@ -400,7 +403,7 @@ fn insert_nft_in_list_sql(chain: &Chain) -> Result { ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23, ?24, ?25, ?26, ?27 );", - table_name + table_name.0 ); Ok(sql) } @@ -415,7 +418,7 @@ fn insert_transfer_in_history_sql(chain: &Chain) -> Result { ) VALUES ( ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19 );", - table_name + table_name.0 ); Ok(sql) } @@ -424,7 +427,7 @@ fn upsert_last_scanned_block_sql() -> Result { let table_name = scanned_nft_blocks_table_name()?; let sql = format!( "INSERT OR REPLACE INTO {} (chain, last_scanned_block) VALUES (?1, ?2);", - table_name + table_name.0 ); Ok(sql) } @@ -435,7 +438,7 @@ fn refresh_nft_metadata_sql(chain: &Chain) -> Result { "UPDATE {} SET possible_spam = ?1, possible_phishing = ?2, collection_name = ?3, symbol = ?4, token_uri = ?5, token_domain = ?6, metadata = ?7, \ last_token_uri_sync = ?8, last_metadata_sync = ?9, raw_image_url = ?10, image_url = ?11, image_domain = ?12, token_name = ?13, description = ?14, \ attributes = ?15, animation_url = ?16, animation_domain = ?17, external_url = ?18, external_domain = ?19, image_details = ?20 WHERE token_address = ?21 AND token_id = ?22;", - table_name + table_name.0 ); Ok(sql) } @@ -445,7 +448,7 @@ fn update_transfers_meta_by_token_addr_id_sql(chain: &Chain) -> Result Result Result { +fn select_last_block_number_sql(table_name: SafeTableName) -> Result { let sql = format!( "SELECT block_number FROM {} ORDER BY block_number DESC LIMIT 1", - table_name + table_name.0 ); Ok(sql) } fn select_last_scanned_block_sql() -> MmResult { let table_name = scanned_nft_blocks_table_name()?; - let sql = format!("SELECT last_scanned_block FROM {} WHERE chain=?1", table_name,); + let sql = format!("SELECT last_scanned_block FROM {} WHERE chain=?1", table_name.0,); Ok(sql) } -fn delete_nft_sql(table_name: String) -> Result { - let sql = format!("DELETE FROM {} WHERE token_address=?1 AND token_id=?2", table_name); +fn delete_nft_sql(table_name: SafeTableName) -> Result { + let sql = format!("DELETE FROM {} WHERE token_address=?1 AND token_id=?2", table_name.0); Ok(sql) } @@ -482,14 +485,14 @@ fn block_number_from_row(row: &Row<'_>) -> Result { row.get::<_, fn nft_amount_from_row(row: &Row<'_>) -> Result { row.get(0) } -fn get_nfts_by_token_address_statement(conn: &Connection, table_name: String) -> Result { - let sql_query = format!("SELECT * FROM {} WHERE token_address = ?", table_name); +fn get_nfts_by_token_address_statement(conn: &Connection, table_name: SafeTableName) -> Result { + let sql_query = format!("SELECT * FROM {} WHERE token_address = ?", table_name.0); let stmt = conn.prepare(&sql_query)?; Ok(stmt) } -fn get_token_addresses_statement(conn: &Connection, table_name: String) -> Result { - let sql_query = format!("SELECT DISTINCT token_address FROM {}", table_name); +fn get_token_addresses_statement(conn: &Connection, table_name: SafeTableName) -> Result { + let sql_query = format!("SELECT DISTINCT token_address FROM {}", table_name.0); let stmt = conn.prepare(&sql_query)?; Ok(stmt) } @@ -498,7 +501,7 @@ fn get_transfers_from_block_statement<'a>(conn: &'a Connection, chain: &'a Chain let table_name = chain.transfer_history_table_name()?; let sql_query = format!( "SELECT * FROM {} WHERE block_number >= ? ORDER BY block_number ASC", - table_name + table_name.0 ); let stmt = conn.prepare(&sql_query)?; Ok(stmt) @@ -506,14 +509,17 @@ fn get_transfers_from_block_statement<'a>(conn: &'a Connection, chain: &'a Chain fn get_transfers_by_token_addr_id_statement(conn: &Connection, chain: Chain) -> Result { let table_name = chain.transfer_history_table_name()?; - let sql_query = format!("SELECT * FROM {} WHERE token_address = ? AND token_id = ?", table_name); + let sql_query = format!( + "SELECT * FROM {} WHERE token_address = ? AND token_id = ?", + table_name.0 + ); let stmt = conn.prepare(&sql_query)?; Ok(stmt) } fn get_transfers_with_empty_meta_builder<'a>(conn: &'a Connection, chain: &'a Chain) -> Result, SqlError> { let table_name = chain.transfer_history_table_name()?; - let mut sql_builder = SqlQuery::select_from(conn, table_name.as_str())?; + let mut sql_builder = SqlQuery::select_from(conn, table_name.0.as_str())?; sql_builder .sql_builder() .distinct() @@ -528,10 +534,10 @@ fn get_transfers_with_empty_meta_builder<'a>(conn: &'a Connection, chain: &'a Ch Ok(sql_builder) } -fn is_table_empty(conn: &Connection, table_name: &str) -> Result { - let mut stmt = conn.prepare(&format!("SELECT COUNT(*) FROM {}", table_name))?; - let count: i64 = stmt.query_row([], |row| row.get(0))?; - Ok(count == 0) +fn is_table_empty(conn: &Connection, table_name: SafeTableName) -> Result { + let query = format!("SELECT COUNT(*) FROM {}", table_name.0); + conn.query_row(&query, [], |row| row.get::<_, i64>(0)) + .map(|count| count == 0) } #[async_trait] @@ -552,11 +558,11 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { async fn is_initialized(&self, chain: &Chain) -> MmResult { let table_name = chain.nft_list_table_name()?; self.call(move |conn| { - let nft_list_initialized = query_single_row(conn, CHECK_TABLE_EXISTS_SQL, [table_name], string_from_row)?; + let nft_list_initialized = query_single_row(conn, CHECK_TABLE_EXISTS_SQL, [table_name.0], string_from_row)?; let scanned_nft_blocks_initialized = query_single_row( conn, CHECK_TABLE_EXISTS_SQL, - [scanned_nft_blocks_table_name()?], + [scanned_nft_blocks_table_name()?.0], string_from_row, )?; Ok(nft_list_initialized.is_some() && scanned_nft_blocks_initialized.is_some()) @@ -666,7 +672,7 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { ) -> MmResult, Self::Error> { let table_name = chain.nft_list_table_name()?; self.call(move |conn| { - let sql = format!("SELECT * FROM {} WHERE token_address=?1 AND token_id=?2", table_name); + let sql = format!("SELECT * FROM {} WHERE token_address=?1 AND token_id=?2", table_name.0); let params = [token_address, token_id.to_string()]; let nft = query_single_row(conn, &sql, params, nft_from_row)?; Ok(nft) @@ -712,7 +718,7 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.nft_list_table_name()?; let sql = format!( "SELECT amount FROM {} WHERE token_address=?1 AND token_id=?2", - table_name + table_name.0 ); let params = [token_address, token_id.to_string()]; self.call(move |conn| { @@ -789,7 +795,7 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.nft_list_table_name()?; let sql = format!( "UPDATE {} SET amount = ?1 WHERE token_address = ?2 AND token_id = ?3;", - table_name + table_name.0 ); let scanned_block_params = [chain.to_ticker().to_string(), scanned_block.to_string()]; self.call(move |conn| { @@ -812,7 +818,7 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.nft_list_table_name()?; let sql = format!( "UPDATE {} SET amount = ?1, block_number = ?2 WHERE token_address = ?3 AND token_id = ?4;", - table_name + table_name.0 ); let scanned_block_params = [chain.to_ticker().to_string(), nft.block_number.to_string()]; self.call(move |conn| { @@ -852,7 +858,10 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { possible_spam: bool, ) -> MmResult<(), Self::Error> { let table_name = chain.nft_list_table_name()?; - let sql = format!("UPDATE {} SET possible_spam = ?1 WHERE token_address = ?2;", table_name); + let sql = format!( + "UPDATE {} SET possible_spam = ?1 WHERE token_address = ?2;", + table_name.0 + ); self.call(move |conn| { let sql_transaction = conn.transaction()?; let params = [Some(i32::from(possible_spam).to_string()), Some(token_address.clone())]; @@ -869,7 +878,7 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { self.call(move |conn| { let sql_query = format!( "SELECT DISTINCT animation_domain FROM {} UNION SELECT DISTINCT external_domain FROM {}", - table_name, table_name + table_name.0, table_name.0 ); let mut stmt = conn.prepare(&sql_query)?; let domains = stmt @@ -892,7 +901,7 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let sql = format!( "UPDATE {} SET possible_phishing = ?1 WHERE token_domain = ?2 OR image_domain = ?2 OR animation_domain = ?2 OR external_domain = ?2;", - table_name + table_name.0 ); self.call(move |conn| { let sql_transaction = conn.transaction()?; @@ -907,17 +916,17 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { async fn clear_nft_data(&self, chain: &Chain) -> MmResult<(), Self::Error> { let table_nft_name = chain.nft_list_table_name()?; - let sql_nft = format!("DROP TABLE IF EXISTS {};", table_nft_name); + let sql_nft = format!("DROP TABLE IF EXISTS {};", table_nft_name.0); let table_scanned_blocks_name = scanned_nft_blocks_table_name()?; - let sql_scanned_block = format!("DELETE from {} where chain=?1", table_scanned_blocks_name); + let sql_scanned_block = format!("DELETE from {} where chain=?1", table_scanned_blocks_name.0); let scanned_block_param = [chain.to_ticker()]; self.call(move |conn| { let sql_transaction = conn.transaction()?; sql_transaction.execute(&sql_nft, [])?; sql_transaction.execute(&sql_scanned_block, scanned_block_param)?; sql_transaction.commit()?; - if is_table_empty(conn, table_scanned_blocks_name.as_str())? { - conn.execute(&format!("DROP TABLE IF EXISTS {};", table_scanned_blocks_name), []) + if is_table_empty(conn, table_scanned_blocks_name.clone())? { + conn.execute(&format!("DROP TABLE IF EXISTS {};", table_scanned_blocks_name.0), []) .map(|_| ())?; } Ok(()) @@ -931,10 +940,10 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let sql_transaction = conn.transaction()?; for chain in Chain::variant_list().into_iter() { let table_name = chain.nft_list_table_name()?; - sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_name), [])?; + sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_name.0), [])?; } let table_scanned_blocks_name = scanned_nft_blocks_table_name()?; - sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_scanned_blocks_name), [])?; + sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_scanned_blocks_name.0), [])?; sql_transaction.commit()?; Ok(()) }) @@ -960,7 +969,7 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { async fn is_initialized(&self, chain: &Chain) -> MmResult { let table_name = chain.transfer_history_table_name()?; self.call(move |conn| { - let nft_list_initialized = query_single_row(conn, CHECK_TABLE_EXISTS_SQL, [table_name], string_from_row)?; + let nft_list_initialized = query_single_row(conn, CHECK_TABLE_EXISTS_SQL, [table_name.0], string_from_row)?; Ok(nft_list_initialized.is_some()) }) .await @@ -1109,7 +1118,7 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.transfer_history_table_name()?; let sql = format!( "SELECT * FROM {} WHERE transaction_hash=?1 AND log_index = ?2", - table_name + table_name.0 ); self.call(move |conn| { let transfer = query_single_row( @@ -1194,7 +1203,10 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { possible_spam: bool, ) -> MmResult<(), Self::Error> { let table_name = chain.transfer_history_table_name()?; - let sql = format!("UPDATE {} SET possible_spam = ?1 WHERE token_address = ?2;", table_name); + let sql = format!( + "UPDATE {} SET possible_spam = ?1 WHERE token_address = ?2;", + table_name.0 + ); self.call(move |conn| { let sql_transaction = conn.transaction()?; let params = [Some(i32::from(possible_spam).to_string()), Some(token_address.clone())]; @@ -1224,7 +1236,7 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { self.call(move |conn| { let sql_query = format!( "SELECT DISTINCT token_domain FROM {} UNION SELECT DISTINCT image_domain FROM {}", - table_name, table_name + table_name.0, table_name.0 ); let mut stmt = conn.prepare(&sql_query)?; let domains = stmt @@ -1246,7 +1258,7 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.transfer_history_table_name()?; let sql = format!( "UPDATE {} SET possible_phishing = ?1 WHERE token_domain = ?2 OR image_domain = ?2;", - table_name + table_name.0 ); self.call(move |conn| { let sql_transaction = conn.transaction()?; @@ -1263,7 +1275,7 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.transfer_history_table_name()?; self.call(move |conn| { let sql_transaction = conn.transaction()?; - sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_name), [])?; + sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_name.0), [])?; sql_transaction.commit()?; Ok(()) }) @@ -1276,7 +1288,7 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let sql_transaction = conn.transaction()?; for chain in Chain::variant_list().into_iter() { let table_name = chain.transfer_history_table_name()?; - sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_name), [])?; + sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_name.0), [])?; } sql_transaction.commit()?; Ok(()) diff --git a/mm2src/db_common/src/sqlite.rs b/mm2src/db_common/src/sqlite.rs index 5da327c0fd..dc057d507d 100644 --- a/mm2src/db_common/src/sqlite.rs +++ b/mm2src/db_common/src/sqlite.rs @@ -86,12 +86,29 @@ pub fn validate_ident(ident: &str) -> SqlResult<()> { validate_ident_impl(ident, |c| c.is_alphanumeric() || c == '_' || c == '.') } +/// Validates a table name against SQL injection risks. +/// +/// This function checks if the provided `table_name` is safe for use in SQL queries. +/// It disallows any characters in the table name that may lead to SQL injection, only +/// allowing alphanumeric characters and underscores. pub fn validate_table_name(table_name: &str) -> SqlResult<()> { // As per https://stackoverflow.com/a/3247553, tables can't be the target of parameter substitution. // So we have to use a plain concatenation disallowing any characters in the table name that may lead to SQL injection. validate_ident_impl(table_name, |c| c.is_alphanumeric() || c == '_') } +/// Represents a SQL table name that has been validated for safety. +#[derive(Clone, Debug)] +pub struct SafeTableName(pub String); + +impl SafeTableName { + /// Creates a new SafeTableName, validating the provided table name. + pub fn new(table_name: &str) -> SqlResult { + validate_table_name(table_name)?; + Ok(SafeTableName(table_name.to_owned())) + } +} + /// Calculates the offset to skip records by uuid. /// Expects `query_builder` to have where clauses applied *before* calling this fn. pub fn offset_by_uuid( @@ -317,6 +334,10 @@ impl StringError { pub fn into_boxed(self) -> Box { Box::new(self) } } +/// Internal function to validate identifiers such as table names. +/// +/// This function is a general-purpose identifier validator. It uses a closure to determine +/// the validity of each character in the provided identifier. fn validate_ident_impl(ident: &str, is_valid: F) -> SqlResult<()> where F: Fn(char) -> bool, From 5aadd7c431fc59cb9a6448c0947cabd5bcef9ef4 Mon Sep 17 00:00:00 2001 From: laruh Date: Tue, 16 Jan 2024 16:27:38 +0700 Subject: [PATCH 10/13] rename enum_from to enum_utilities, doc above the item it documents, get_name func --- Cargo.lock | 10 +- Cargo.toml | 2 +- mm2src/coins/Cargo.toml | 2 +- mm2src/coins/eth.rs | 2 +- mm2src/coins/eth/v2_activation.rs | 2 +- mm2src/coins/hd_confirm_address.rs | 2 +- mm2src/coins/lp_coins.rs | 2 +- mm2src/coins/nft/nft_errors.rs | 10 +- mm2src/coins/nft/nft_structs.rs | 79 +++++---- mm2src/coins/nft/storage/sql_storage.rs | 152 ++++++++++-------- mm2src/coins/rpc_command/get_new_address.rs | 2 +- .../coins/rpc_command/init_create_account.rs | 2 +- mm2src/crypto/Cargo.toml | 2 +- mm2src/crypto/src/shared_db_id.rs | 2 +- mm2src/db_common/src/sqlite.rs | 5 +- .../{enum_from => enum_utilities}/Cargo.toml | 2 +- .../src/from_inner.rs | 0 .../src/from_stringify.rs | 0 .../src/from_trait.rs | 0 .../{enum_from => enum_utilities}/src/lib.rs | 8 +- mm2src/mm2_db/Cargo.toml | 2 +- .../src/indexed_db/drivers/cursor/cursor.rs | 2 +- .../src/indexed_db/drivers/transaction.rs | 2 +- mm2src/mm2_main/Cargo.toml | 2 +- mm2src/mm2_main/src/lp_init/init_hw.rs | 2 +- mm2src/mm2_main/src/lp_init/init_metamask.rs | 2 +- mm2src/mm2_main/src/lp_native_dex.rs | 2 +- 27 files changed, 159 insertions(+), 141 deletions(-) rename mm2src/derives/{enum_from => enum_utilities}/Cargo.toml (89%) rename mm2src/derives/{enum_from => enum_utilities}/src/from_inner.rs (100%) rename mm2src/derives/{enum_from => enum_utilities}/src/from_stringify.rs (100%) rename mm2src/derives/{enum_from => enum_utilities}/src/from_trait.rs (100%) rename mm2src/derives/{enum_from => enum_utilities}/src/lib.rs (98%) diff --git a/Cargo.lock b/Cargo.lock index 21fddc9af4..7fb0d565d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1013,7 +1013,7 @@ dependencies = [ "dirs", "ed25519-dalek", "ed25519-dalek-bip32 0.2.0", - "enum_from", + "enum_utilities", "ethabi", "ethcore-transaction", "ethereum-types", @@ -1501,7 +1501,7 @@ dependencies = [ "common", "derive_more", "enum-primitive-derive", - "enum_from", + "enum_utilities", "futures 0.3.28", "hex 0.4.3", "http 0.2.7", @@ -2072,7 +2072,7 @@ dependencies = [ ] [[package]] -name = "enum_from" +name = "enum_utilities" version = "0.1.0" dependencies = [ "itertools", @@ -4284,7 +4284,7 @@ dependencies = [ "async-trait", "common", "derive_more", - "enum_from", + "enum_utilities", "futures 0.3.28", "hex 0.4.3", "itertools", @@ -4422,7 +4422,7 @@ dependencies = [ "dirs", "either", "enum-primitive-derive", - "enum_from", + "enum_utilities", "ethereum-types", "futures 0.1.29", "futures 0.3.28", diff --git a/Cargo.toml b/Cargo.toml index ac0439b17b..7bba94fe9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ members = [ "mm2src/common/shared_ref_counter", "mm2src/crypto", "mm2src/db_common", - "mm2src/derives/enum_from", + "mm2src/derives/enum_utilities", "mm2src/derives/ser_error_derive", "mm2src/derives/ser_error", "mm2src/hw_common", diff --git a/mm2src/coins/Cargo.toml b/mm2src/coins/Cargo.toml index b2ccc8c227..d185efcbcf 100644 --- a/mm2src/coins/Cargo.toml +++ b/mm2src/coins/Cargo.toml @@ -42,7 +42,7 @@ crypto = { path = "../crypto" } db_common = { path = "../db_common" } derive_more = "0.99" ed25519-dalek = "1.0.1" -enum_from = { path = "../derives/enum_from" } +enum_utilities = { path = "../derives/enum_utilities" } ethabi = { version = "17.0.0" } ethcore-transaction = { git = "https://github.com/KomodoPlatform/mm2-parity-ethereum.git" } ethereum-types = { version = "0.13", default-features = false, features = ["std", "serialize"] } diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 1c579c22b0..093beef328 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -37,7 +37,7 @@ use common::{now_ms, wait_until_ms}; use crypto::privkey::key_pair_from_secret; use crypto::{CryptoCtx, CryptoCtxError, GlobalHDAccountArc, KeyPairPolicy, StandardHDCoinAddress}; use derive_more::Display; -use enum_from::EnumFromStringify; +use enum_utilities::EnumFromStringify; use ethabi::{Contract, Function, Token}; pub use ethcore_transaction::SignedTransaction as SignedEthTx; use ethcore_transaction::{Action, Transaction as UnSignedEthTx, UnverifiedTransaction}; diff --git a/mm2src/coins/eth/v2_activation.rs b/mm2src/coins/eth/v2_activation.rs index fddf8da03f..d424d96606 100644 --- a/mm2src/coins/eth/v2_activation.rs +++ b/mm2src/coins/eth/v2_activation.rs @@ -2,7 +2,7 @@ use super::*; #[cfg(target_arch = "wasm32")] use crate::EthMetamaskPolicy; use common::executor::AbortedError; use crypto::{CryptoCtxError, StandardHDCoinAddress}; -use enum_from::EnumFromTrait; +use enum_utilities::EnumFromTrait; use mm2_err_handle::common_errors::WithInternal; #[cfg(target_arch = "wasm32")] use mm2_metamask::{from_metamask_error, MetamaskError, MetamaskRpcError, WithMetamaskRpcError}; diff --git a/mm2src/coins/hd_confirm_address.rs b/mm2src/coins/hd_confirm_address.rs index b028f9fd9e..69b7eca4c2 100644 --- a/mm2src/coins/hd_confirm_address.rs +++ b/mm2src/coins/hd_confirm_address.rs @@ -4,7 +4,7 @@ use crypto::hw_rpc_task::HwConnectStatuses; use crypto::trezor::trezor_rpc_task::{TrezorRequestStatuses, TrezorRpcTaskProcessor, TryIntoUserAction}; use crypto::trezor::{ProcessTrezorResponse, TrezorError, TrezorProcessingError}; use crypto::{CryptoCtx, CryptoCtxError, HardwareWalletArc, HwError, HwProcessingError}; -use enum_from::{EnumFromInner, EnumFromStringify}; +use enum_utilities::{EnumFromInner, EnumFromStringify}; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use rpc_task::{RpcTask, RpcTaskError, RpcTaskHandleShared}; diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 306fd5f6bd..70620a7f7d 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -52,7 +52,7 @@ use common::{calc_total_pages, now_sec, ten, HttpStatusCode}; use crypto::{derive_secp256k1_secret, Bip32Error, CryptoCtx, CryptoCtxError, DerivationPath, GlobalHDAccountArc, HwRpcError, KeyPairPolicy, Secp256k1Secret, StandardHDCoinAddress, StandardHDPathToCoin, WithHwRpcError}; use derive_more::Display; -use enum_from::{EnumFromStringify, EnumFromTrait}; +use enum_utilities::{EnumFromStringify, EnumFromTrait}; use ethereum_types::H256; use futures::compat::Future01CompatExt; use futures::lock::Mutex as AsyncMutex; diff --git a/mm2src/coins/nft/nft_errors.rs b/mm2src/coins/nft/nft_errors.rs index a199bb3542..9c59135415 100644 --- a/mm2src/coins/nft/nft_errors.rs +++ b/mm2src/coins/nft/nft_errors.rs @@ -7,7 +7,7 @@ use common::{HttpStatusCode, ParseRfc3339Err}; #[cfg(not(target_arch = "wasm32"))] use db_common::sqlite::rusqlite::Error as SqlError; use derive_more::Display; -use enum_from::EnumFromStringify; +use enum_utilities::EnumFromStringify; use http::StatusCode; use mm2_net::transport::{GetInfoFromUriError, SlurpError}; use serde::{Deserialize, Serialize}; @@ -393,18 +393,16 @@ impl From for TransferConfirmationsError { } /// Enumerates errors that can occur while clearing NFT data from the database. -/// -/// Variants: -/// - `DbError`: Represents errors related to database operations. -/// - `Internal`: Indicates internal errors not directly associated with database operations. -/// - `InvalidRequest`: Used for various types of invalid requests, such as missing or contradictory parameters. #[derive(Clone, Debug, Deserialize, Display, PartialEq, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] pub enum ClearNftDbError { + /// Represents errors related to database operations. #[display(fmt = "DB error {}", _0)] DbError(String), + /// Indicates internal errors not directly associated with database operations. #[display(fmt = "Internal: {}", _0)] Internal(String), + /// Used for various types of invalid requests, such as missing or contradictory parameters. #[display(fmt = "Invalid request: {}", _0)] InvalidRequest(String), } diff --git a/mm2src/coins/nft/nft_structs.rs b/mm2src/coins/nft/nft_structs.rs index 260898bd0c..d3fd8dbe65 100644 --- a/mm2src/coins/nft/nft_structs.rs +++ b/mm2src/coins/nft/nft_structs.rs @@ -1,5 +1,5 @@ use common::ten; -use enum_from::EnumVariantList; +use enum_utilities::EnumVariantList; use ethereum_types::Address; use mm2_core::mm_ctx::{from_ctx, MmArc}; use mm2_err_handle::prelude::*; @@ -85,20 +85,19 @@ pub struct NftMetadataReq { } /// Contains parameters required to refresh metadata for a specified NFT. -/// # Fields -/// * `token_address`: The address of the NFT token whose metadata needs to be refreshed. -/// * `token_id`: The ID of the NFT token. -/// * `chain`: The blockchain where the NFT exists. -/// * `url`: URL to fetch the metadata. -/// * `url_antispam`: URL used to validate if the fetched contract addresses are associated -/// with spam contracts or if domain fields in the fetched metadata match known phishing domains. #[derive(Debug, Deserialize)] pub struct RefreshMetadataReq { + /// The address of the NFT token whose metadata needs to be refreshed. pub(crate) token_address: Address, + /// The ID of the NFT token. #[serde(deserialize_with = "deserialize_token_id")] pub(crate) token_id: BigUint, + /// The blockchain where the NFT exists. pub(crate) chain: Chain, + /// URL to fetch the metadata. pub(crate) url: Url, + /// URL used to validate if the fetched contract addresses are associated + /// with spam contracts or if domain fields in the fetched metadata match known phishing domains. pub(crate) url_antispam: Url, } @@ -387,56 +386,50 @@ pub struct NftList { } /// Parameters for withdrawing an ERC-1155 token. -/// -/// Fields: -/// - `chain`: The blockchain network to perform the withdrawal on. -/// - `to`: The address to send the NFT to. -/// - `token_address`: The address of the ERC-1155 token contract. -/// - `token_id`: The unique identifier of the NFT to withdraw. -/// - `amount`: Optional amount of the token to withdraw. Defaults to 1 if not specified. -/// - `max`: If set to `true`, withdraws the maximum amount available. Overrides the `amount` field. -/// - `fee`: Optional details for the withdrawal fee. #[derive(Clone, Deserialize)] pub struct WithdrawErc1155 { + /// The blockchain network to perform the withdrawal on. pub(crate) chain: Chain, + /// The address to send the NFT to. pub(crate) to: String, + /// The address of the ERC-1155 token contract. pub(crate) token_address: String, + /// The unique identifier of the NFT to withdraw. #[serde(deserialize_with = "deserialize_token_id")] pub(crate) token_id: BigUint, + /// Optional amount of the token to withdraw. Defaults to 1 if not specified. pub(crate) amount: Option, + /// If set to `true`, withdraws the maximum amount available. Overrides the `amount` field. #[serde(default)] pub(crate) max: bool, + /// Optional details for the withdrawal fee. pub(crate) fee: Option, } /// Parameters for withdrawing an ERC-721 token. -/// -/// Fields: -/// - `chain`: The blockchain network to perform the withdrawal on. -/// - `to`: The address to send the NFT to. -/// - `token_address`: The address of the ERC-721 token contract. -/// - `token_id`: The unique identifier of the NFT to withdraw. -/// - `fee`: Optional details for the withdrawal fee. #[derive(Clone, Deserialize)] pub struct WithdrawErc721 { + /// The blockchain network to perform the withdrawal on. pub(crate) chain: Chain, + /// The address to send the NFT to. pub(crate) to: String, + /// The address of the ERC-721 token contract. pub(crate) token_address: String, + /// The unique identifier of the NFT to withdraw. #[serde(deserialize_with = "deserialize_token_id")] pub(crate) token_id: BigUint, + /// Optional details for the withdrawal fee. pub(crate) fee: Option, } /// Represents a request for withdrawing an NFT, supporting different ERC standards. -/// -/// Variants: -/// - `WithdrawErc1155`: Parameters for withdrawing an ERC-1155 token. -/// - `WithdrawErc721`: Parameters for withdrawing an ERC-721 token. #[derive(Clone, Deserialize)] #[serde(tag = "type", content = "withdraw_data")] #[serde(rename_all = "snake_case")] pub enum WithdrawNftReq { + /// Parameters for withdrawing an ERC-1155 token. WithdrawErc1155(WithdrawErc1155), + /// Parameters for withdrawing an ERC-721 token. WithdrawErc721(WithdrawErc721), } @@ -620,33 +613,44 @@ pub struct NftTransferHistoryFilters { } /// Contains parameters required to update NFT transfer history and NFT list. -/// # Fields -/// * `chains`: A list of blockchains for which the NFTs need to be updated. -/// * `url`: URL to fetch the NFT data. -/// * `url_antispam`: URL used to validate if the fetched contract addresses are associated -/// with spam contracts or if domain fields in the fetched metadata match known phishing domains. #[derive(Debug, Deserialize)] pub struct UpdateNftReq { + /// A list of blockchains for which the NFTs need to be updated. pub(crate) chains: Vec, + /// URL to fetch the NFT data. pub(crate) url: Url, + /// URL used to validate if the fetched contract addresses are associated + /// with spam contracts or if domain fields in the fetched metadata match known phishing domains. pub(crate) url_antispam: Url, } +/// Represents a unique identifier for an NFT, consisting of its token address and token ID. #[derive(Debug, Deserialize, Eq, Hash, PartialEq)] pub struct NftTokenAddrId { + /// The address of the NFT token contract. pub(crate) token_address: String, + /// The unique identifier of the NFT within its contract. pub(crate) token_id: BigUint, } +/// Holds metadata information for an NFT transfer. #[derive(Debug)] pub struct TransferMeta { + /// The address of the NFT token contract. pub(crate) token_address: String, + /// The unique identifier of the NFT. pub(crate) token_id: BigUint, + /// Optional URI for the NFT's metadata. pub(crate) token_uri: Option, + /// Optional domain associated with the NFT's metadata. pub(crate) token_domain: Option, + /// Optional name of the NFT's collection. pub(crate) collection_name: Option, + /// Optional URL for the NFT's image. pub(crate) image_url: Option, + /// Optional domain for the NFT's image. pub(crate) image_domain: Option, + /// Optional name of the NFT. pub(crate) token_name: Option, } @@ -761,16 +765,11 @@ where } /// Request parameters for clearing NFT data from the database. -/// -/// - `chains`: (Optional) Specifies the blockchain networks (e.g., Ethereum, BSC) to clear NFT data. -/// This field is used and required only when `clear_all` is `false`. -/// - `clear_all`: If `true`, clears NFT data for all chains, ignoring the `chains` field. -/// Defaults to `false`. -/// -/// Note: If `clear_all` is `false` and `chains` is `None`, the request will be considered invalid and result in an error. #[derive(Debug, Deserialize)] pub struct ClearNftDbReq { + /// (Optional) Specifies the blockchain networks (e.g., Ethereum, BSC) to clear NFT data. pub(crate) chains: Option>, + /// If `true`, clears NFT data for all chains, ignoring the `chains` field. Defaults to `false`. #[serde(default)] pub(crate) clear_all: bool, } diff --git a/mm2src/coins/nft/storage/sql_storage.rs b/mm2src/coins/nft/storage/sql_storage.rs index d169755e40..2a1525c899 100644 --- a/mm2src/coins/nft/storage/sql_storage.rs +++ b/mm2src/coins/nft/storage/sql_storage.rs @@ -43,7 +43,7 @@ fn scanned_nft_blocks_table_name() -> SqlResult { } fn create_nft_list_table_sql(chain: &Chain) -> MmResult { - let table_name = chain.nft_list_table_name()?; + let safe_table_name = chain.nft_list_table_name()?; let sql = format!( "CREATE TABLE IF NOT EXISTS {} ( token_address VARCHAR(256) NOT NULL, @@ -75,13 +75,13 @@ fn create_nft_list_table_sql(chain: &Chain) -> MmResult { details_json TEXT, PRIMARY KEY (token_address, token_id) );", - table_name.0 + safe_table_name.get_name() ); Ok(sql) } fn create_transfer_history_table_sql(chain: &Chain) -> Result { - let table_name = chain.transfer_history_table_name()?; + let safe_table_name = chain.transfer_history_table_name()?; let sql = format!( "CREATE TABLE IF NOT EXISTS {} ( transaction_hash VARCHAR(256) NOT NULL, @@ -105,19 +105,19 @@ fn create_transfer_history_table_sql(chain: &Chain) -> Result details_json TEXT, PRIMARY KEY (transaction_hash, log_index) );", - table_name.0 + safe_table_name.get_name() ); Ok(sql) } fn create_scanned_nft_blocks_sql() -> Result { - let table_name = scanned_nft_blocks_table_name()?; + let safe_table_name = scanned_nft_blocks_table_name()?; let sql = format!( "CREATE TABLE IF NOT EXISTS {} ( chain TEXT PRIMARY KEY, last_scanned_block INTEGER DEFAULT 0 );", - table_name.0 + safe_table_name.get_name() ); Ok(sql) } @@ -146,10 +146,10 @@ fn get_nft_list_builder_preimage(chains: Vec, filters: Option, ) -> Result { - let mut sql_builder = SqlBuilder::select_from(table_name.0); + let mut sql_builder = SqlBuilder::select_from(safe_table_name.get_name()); if let Some(filters) = filters { if filters.exclude_spam { sql_builder.and_where("possible_spam == 0"); @@ -187,10 +187,10 @@ fn get_nft_transfer_builder_preimage( } fn nft_history_table_builder_preimage( - table_name: SafeTableName, + safe_table_name: SafeTableName, filters: Option, ) -> Result { - let mut sql_builder = SqlBuilder::select_from(table_name.0); + let mut sql_builder = SqlBuilder::select_from(safe_table_name.get_name()); if let Some(filters) = filters { if filters.send && !filters.receive { sql_builder.and_where_eq("status", "'Send'"); @@ -391,7 +391,7 @@ fn token_address_id_from_row(row: &Row<'_>) -> Result } fn insert_nft_in_list_sql(chain: &Chain) -> Result { - let table_name = chain.nft_list_table_name()?; + let safe_table_name = chain.nft_list_table_name()?; let sql = format!( "INSERT INTO {} ( token_address, token_id, chain, amount, block_number, contract_type, possible_spam, @@ -403,13 +403,13 @@ fn insert_nft_in_list_sql(chain: &Chain) -> Result { ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23, ?24, ?25, ?26, ?27 );", - table_name.0 + safe_table_name.get_name() ); Ok(sql) } fn insert_transfer_in_history_sql(chain: &Chain) -> Result { - let table_name = chain.transfer_history_table_name()?; + let safe_table_name = chain.transfer_history_table_name()?; let sql = format!( "INSERT INTO {} ( transaction_hash, log_index, chain, block_number, block_timestamp, contract_type, @@ -418,66 +418,72 @@ fn insert_transfer_in_history_sql(chain: &Chain) -> Result { ) VALUES ( ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19 );", - table_name.0 + safe_table_name.get_name() ); Ok(sql) } fn upsert_last_scanned_block_sql() -> Result { - let table_name = scanned_nft_blocks_table_name()?; + let safe_table_name = scanned_nft_blocks_table_name()?; let sql = format!( "INSERT OR REPLACE INTO {} (chain, last_scanned_block) VALUES (?1, ?2);", - table_name.0 + safe_table_name.get_name() ); Ok(sql) } fn refresh_nft_metadata_sql(chain: &Chain) -> Result { - let table_name = chain.nft_list_table_name()?; + let safe_table_name = chain.nft_list_table_name()?; let sql = format!( "UPDATE {} SET possible_spam = ?1, possible_phishing = ?2, collection_name = ?3, symbol = ?4, token_uri = ?5, token_domain = ?6, metadata = ?7, \ last_token_uri_sync = ?8, last_metadata_sync = ?9, raw_image_url = ?10, image_url = ?11, image_domain = ?12, token_name = ?13, description = ?14, \ attributes = ?15, animation_url = ?16, animation_domain = ?17, external_url = ?18, external_domain = ?19, image_details = ?20 WHERE token_address = ?21 AND token_id = ?22;", - table_name.0 + safe_table_name.get_name() ); Ok(sql) } fn update_transfers_meta_by_token_addr_id_sql(chain: &Chain) -> Result { - let table_name = chain.transfer_history_table_name()?; + let safe_table_name = chain.transfer_history_table_name()?; let sql = format!( "UPDATE {} SET token_uri = ?1, token_domain = ?2, collection_name = ?3, image_url = ?4, image_domain = ?5, \ token_name = ?6 WHERE token_address = ?7 AND token_id = ?8;", - table_name.0 + safe_table_name.get_name() ); Ok(sql) } fn update_transfer_spam_by_token_addr_id(chain: &Chain) -> Result { - let table_name = chain.transfer_history_table_name()?; + let safe_table_name = chain.transfer_history_table_name()?; let sql = format!( "UPDATE {} SET possible_spam = ?1 WHERE token_address = ?2 AND token_id = ?3;", - table_name.0 + safe_table_name.get_name() ); Ok(sql) } -fn select_last_block_number_sql(table_name: SafeTableName) -> Result { +fn select_last_block_number_sql(safe_table_name: SafeTableName) -> Result { let sql = format!( "SELECT block_number FROM {} ORDER BY block_number DESC LIMIT 1", - table_name.0 + safe_table_name.get_name() ); Ok(sql) } fn select_last_scanned_block_sql() -> MmResult { let table_name = scanned_nft_blocks_table_name()?; - let sql = format!("SELECT last_scanned_block FROM {} WHERE chain=?1", table_name.0,); + let sql = format!( + "SELECT last_scanned_block FROM {} WHERE chain=?1", + table_name.get_name() + ); Ok(sql) } -fn delete_nft_sql(table_name: SafeTableName) -> Result { - let sql = format!("DELETE FROM {} WHERE token_address=?1 AND token_id=?2", table_name.0); +fn delete_nft_sql(safe_table_name: SafeTableName) -> Result { + let sql = format!( + "DELETE FROM {} WHERE token_address=?1 AND token_id=?2", + safe_table_name.get_name() + ); Ok(sql) } @@ -485,41 +491,44 @@ fn block_number_from_row(row: &Row<'_>) -> Result { row.get::<_, fn nft_amount_from_row(row: &Row<'_>) -> Result { row.get(0) } -fn get_nfts_by_token_address_statement(conn: &Connection, table_name: SafeTableName) -> Result { - let sql_query = format!("SELECT * FROM {} WHERE token_address = ?", table_name.0); +fn get_nfts_by_token_address_statement( + conn: &Connection, + safe_table_name: SafeTableName, +) -> Result { + let sql_query = format!("SELECT * FROM {} WHERE token_address = ?", safe_table_name.get_name()); let stmt = conn.prepare(&sql_query)?; Ok(stmt) } -fn get_token_addresses_statement(conn: &Connection, table_name: SafeTableName) -> Result { - let sql_query = format!("SELECT DISTINCT token_address FROM {}", table_name.0); +fn get_token_addresses_statement(conn: &Connection, safe_table_name: SafeTableName) -> Result { + let sql_query = format!("SELECT DISTINCT token_address FROM {}", safe_table_name.get_name()); let stmt = conn.prepare(&sql_query)?; Ok(stmt) } fn get_transfers_from_block_statement<'a>(conn: &'a Connection, chain: &'a Chain) -> Result, SqlError> { - let table_name = chain.transfer_history_table_name()?; + let safe_table_name = chain.transfer_history_table_name()?; let sql_query = format!( "SELECT * FROM {} WHERE block_number >= ? ORDER BY block_number ASC", - table_name.0 + safe_table_name.get_name() ); let stmt = conn.prepare(&sql_query)?; Ok(stmt) } fn get_transfers_by_token_addr_id_statement(conn: &Connection, chain: Chain) -> Result { - let table_name = chain.transfer_history_table_name()?; + let safe_table_name = chain.transfer_history_table_name()?; let sql_query = format!( "SELECT * FROM {} WHERE token_address = ? AND token_id = ?", - table_name.0 + safe_table_name.get_name() ); let stmt = conn.prepare(&sql_query)?; Ok(stmt) } fn get_transfers_with_empty_meta_builder<'a>(conn: &'a Connection, chain: &'a Chain) -> Result, SqlError> { - let table_name = chain.transfer_history_table_name()?; - let mut sql_builder = SqlQuery::select_from(conn, table_name.0.as_str())?; + let safe_table_name = chain.transfer_history_table_name()?; + let mut sql_builder = SqlQuery::select_from(conn, safe_table_name.get_name())?; sql_builder .sql_builder() .distinct() @@ -534,8 +543,8 @@ fn get_transfers_with_empty_meta_builder<'a>(conn: &'a Connection, chain: &'a Ch Ok(sql_builder) } -fn is_table_empty(conn: &Connection, table_name: SafeTableName) -> Result { - let query = format!("SELECT COUNT(*) FROM {}", table_name.0); +fn is_table_empty(conn: &Connection, safe_table_name: SafeTableName) -> Result { + let query = format!("SELECT COUNT(*) FROM {}", safe_table_name.get_name()); conn.query_row(&query, [], |row| row.get::<_, i64>(0)) .map(|count| count == 0) } @@ -558,11 +567,12 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { async fn is_initialized(&self, chain: &Chain) -> MmResult { let table_name = chain.nft_list_table_name()?; self.call(move |conn| { - let nft_list_initialized = query_single_row(conn, CHECK_TABLE_EXISTS_SQL, [table_name.0], string_from_row)?; + let nft_list_initialized = + query_single_row(conn, CHECK_TABLE_EXISTS_SQL, [table_name.get_name()], string_from_row)?; let scanned_nft_blocks_initialized = query_single_row( conn, CHECK_TABLE_EXISTS_SQL, - [scanned_nft_blocks_table_name()?.0], + [scanned_nft_blocks_table_name()?.get_name()], string_from_row, )?; Ok(nft_list_initialized.is_some() && scanned_nft_blocks_initialized.is_some()) @@ -672,7 +682,10 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { ) -> MmResult, Self::Error> { let table_name = chain.nft_list_table_name()?; self.call(move |conn| { - let sql = format!("SELECT * FROM {} WHERE token_address=?1 AND token_id=?2", table_name.0); + let sql = format!( + "SELECT * FROM {} WHERE token_address=?1 AND token_id=?2", + table_name.get_name() + ); let params = [token_address, token_id.to_string()]; let nft = query_single_row(conn, &sql, params, nft_from_row)?; Ok(nft) @@ -718,7 +731,7 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.nft_list_table_name()?; let sql = format!( "SELECT amount FROM {} WHERE token_address=?1 AND token_id=?2", - table_name.0 + table_name.get_name() ); let params = [token_address, token_id.to_string()]; self.call(move |conn| { @@ -795,7 +808,7 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.nft_list_table_name()?; let sql = format!( "UPDATE {} SET amount = ?1 WHERE token_address = ?2 AND token_id = ?3;", - table_name.0 + table_name.get_name() ); let scanned_block_params = [chain.to_ticker().to_string(), scanned_block.to_string()]; self.call(move |conn| { @@ -818,7 +831,7 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.nft_list_table_name()?; let sql = format!( "UPDATE {} SET amount = ?1, block_number = ?2 WHERE token_address = ?3 AND token_id = ?4;", - table_name.0 + table_name.get_name() ); let scanned_block_params = [chain.to_ticker().to_string(), nft.block_number.to_string()]; self.call(move |conn| { @@ -860,7 +873,7 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.nft_list_table_name()?; let sql = format!( "UPDATE {} SET possible_spam = ?1 WHERE token_address = ?2;", - table_name.0 + table_name.get_name() ); self.call(move |conn| { let sql_transaction = conn.transaction()?; @@ -874,11 +887,12 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { } async fn get_animation_external_domains(&self, chain: &Chain) -> MmResult, Self::Error> { - let table_name = chain.nft_list_table_name()?; + let safe_table_name = chain.nft_list_table_name()?; self.call(move |conn| { + let table_name = safe_table_name.get_name(); let sql_query = format!( "SELECT DISTINCT animation_domain FROM {} UNION SELECT DISTINCT external_domain FROM {}", - table_name.0, table_name.0 + table_name, table_name ); let mut stmt = conn.prepare(&sql_query)?; let domains = stmt @@ -901,7 +915,7 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let sql = format!( "UPDATE {} SET possible_phishing = ?1 WHERE token_domain = ?2 OR image_domain = ?2 OR animation_domain = ?2 OR external_domain = ?2;", - table_name.0 + table_name.get_name() ); self.call(move |conn| { let sql_transaction = conn.transaction()?; @@ -916,18 +930,19 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { async fn clear_nft_data(&self, chain: &Chain) -> MmResult<(), Self::Error> { let table_nft_name = chain.nft_list_table_name()?; - let sql_nft = format!("DROP TABLE IF EXISTS {};", table_nft_name.0); - let table_scanned_blocks_name = scanned_nft_blocks_table_name()?; - let sql_scanned_block = format!("DELETE from {} where chain=?1", table_scanned_blocks_name.0); + let sql_nft = format!("DROP TABLE IF EXISTS {};", table_nft_name.get_name()); + let table_scanned_blocks = scanned_nft_blocks_table_name()?; + let sql_scanned_block = format!("DELETE from {} where chain=?1", table_scanned_blocks.get_name()); let scanned_block_param = [chain.to_ticker()]; self.call(move |conn| { let sql_transaction = conn.transaction()?; sql_transaction.execute(&sql_nft, [])?; sql_transaction.execute(&sql_scanned_block, scanned_block_param)?; sql_transaction.commit()?; - if is_table_empty(conn, table_scanned_blocks_name.clone())? { - conn.execute(&format!("DROP TABLE IF EXISTS {};", table_scanned_blocks_name.0), []) - .map(|_| ())?; + if is_table_empty(conn, table_scanned_blocks.clone())? { + conn.execute(&format!("DROP TABLE IF EXISTS {};", table_scanned_blocks.get_name()), [ + ]) + .map(|_| ())?; } Ok(()) }) @@ -940,10 +955,11 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let sql_transaction = conn.transaction()?; for chain in Chain::variant_list().into_iter() { let table_name = chain.nft_list_table_name()?; - sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_name.0), [])?; + sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_name.get_name()), [])?; } - let table_scanned_blocks_name = scanned_nft_blocks_table_name()?; - sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_scanned_blocks_name.0), [])?; + let table_scanned_blocks = scanned_nft_blocks_table_name()?; + sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_scanned_blocks.get_name()), [ + ])?; sql_transaction.commit()?; Ok(()) }) @@ -969,7 +985,8 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { async fn is_initialized(&self, chain: &Chain) -> MmResult { let table_name = chain.transfer_history_table_name()?; self.call(move |conn| { - let nft_list_initialized = query_single_row(conn, CHECK_TABLE_EXISTS_SQL, [table_name.0], string_from_row)?; + let nft_list_initialized = + query_single_row(conn, CHECK_TABLE_EXISTS_SQL, [table_name.get_name()], string_from_row)?; Ok(nft_list_initialized.is_some()) }) .await @@ -1118,7 +1135,7 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.transfer_history_table_name()?; let sql = format!( "SELECT * FROM {} WHERE transaction_hash=?1 AND log_index = ?2", - table_name.0 + table_name.get_name() ); self.call(move |conn| { let transfer = query_single_row( @@ -1205,7 +1222,7 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.transfer_history_table_name()?; let sql = format!( "UPDATE {} SET possible_spam = ?1 WHERE token_address = ?2;", - table_name.0 + table_name.get_name() ); self.call(move |conn| { let sql_transaction = conn.transaction()?; @@ -1232,11 +1249,12 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { } async fn get_domains(&self, chain: &Chain) -> MmResult, Self::Error> { - let table_name = chain.transfer_history_table_name()?; + let safe_table_name = chain.transfer_history_table_name()?; self.call(move |conn| { + let table_name = safe_table_name.get_name(); let sql_query = format!( "SELECT DISTINCT token_domain FROM {} UNION SELECT DISTINCT image_domain FROM {}", - table_name.0, table_name.0 + table_name, table_name ); let mut stmt = conn.prepare(&sql_query)?; let domains = stmt @@ -1255,10 +1273,10 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { domain: String, possible_phishing: bool, ) -> MmResult<(), Self::Error> { - let table_name = chain.transfer_history_table_name()?; + let safe_table_name = chain.transfer_history_table_name()?; let sql = format!( "UPDATE {} SET possible_phishing = ?1 WHERE token_domain = ?2 OR image_domain = ?2;", - table_name.0 + safe_table_name.get_name() ); self.call(move |conn| { let sql_transaction = conn.transaction()?; @@ -1275,7 +1293,7 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.transfer_history_table_name()?; self.call(move |conn| { let sql_transaction = conn.transaction()?; - sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_name.0), [])?; + sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_name.get_name()), [])?; sql_transaction.commit()?; Ok(()) }) @@ -1288,7 +1306,7 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let sql_transaction = conn.transaction()?; for chain in Chain::variant_list().into_iter() { let table_name = chain.transfer_history_table_name()?; - sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_name.0), [])?; + sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_name.get_name()), [])?; } sql_transaction.commit()?; Ok(()) diff --git a/mm2src/coins/rpc_command/get_new_address.rs b/mm2src/coins/rpc_command/get_new_address.rs index 6d57870ac5..bb6a63a6c9 100644 --- a/mm2src/coins/rpc_command/get_new_address.rs +++ b/mm2src/coins/rpc_command/get_new_address.rs @@ -8,7 +8,7 @@ use common::{HttpStatusCode, SuccessResponse}; use crypto::hw_rpc_task::{HwConnectStatuses, HwRpcTaskAwaitingStatus, HwRpcTaskUserAction, HwRpcTaskUserActionRequest}; use crypto::{from_hw_error, Bip44Chain, HwError, HwRpcError, WithHwRpcError}; use derive_more::Display; -use enum_from::EnumFromTrait; +use enum_utilities::EnumFromTrait; use http::StatusCode; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; diff --git a/mm2src/coins/rpc_command/init_create_account.rs b/mm2src/coins/rpc_command/init_create_account.rs index c67cd8cd3d..4e81037089 100644 --- a/mm2src/coins/rpc_command/init_create_account.rs +++ b/mm2src/coins/rpc_command/init_create_account.rs @@ -8,7 +8,7 @@ use common::{true_f, HttpStatusCode, SuccessResponse}; use crypto::hw_rpc_task::{HwConnectStatuses, HwRpcTaskAwaitingStatus, HwRpcTaskUserAction, HwRpcTaskUserActionRequest}; use crypto::{from_hw_error, Bip44Chain, HwError, HwRpcError, RpcDerivationPath, WithHwRpcError}; use derive_more::Display; -use enum_from::EnumFromTrait; +use enum_utilities::EnumFromTrait; use http::StatusCode; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; diff --git a/mm2src/crypto/Cargo.toml b/mm2src/crypto/Cargo.toml index c4e4a84f92..c892246da2 100644 --- a/mm2src/crypto/Cargo.toml +++ b/mm2src/crypto/Cargo.toml @@ -14,7 +14,7 @@ bitcrypto = { path = "../mm2_bitcoin/crypto" } bs58 = "0.4.0" common = { path = "../common" } derive_more = "0.99" -enum_from = { path = "../derives/enum_from" } +enum_utilities = { path = "../derives/enum_utilities" } enum-primitive-derive = "0.2" futures = "0.3" hex = "0.4.2" diff --git a/mm2src/crypto/src/shared_db_id.rs b/mm2src/crypto/src/shared_db_id.rs index 1aff809ca9..9f98aada73 100644 --- a/mm2src/crypto/src/shared_db_id.rs +++ b/mm2src/crypto/src/shared_db_id.rs @@ -1,6 +1,6 @@ use crate::privkey::private_from_seed_hash; use derive_more::Display; -use enum_from::EnumFromStringify; +use enum_utilities::EnumFromStringify; use keys::{Error as KeysError, KeyPair}; use mm2_err_handle::prelude::*; use primitives::hash::H160; diff --git a/mm2src/db_common/src/sqlite.rs b/mm2src/db_common/src/sqlite.rs index dc057d507d..fcc307dab2 100644 --- a/mm2src/db_common/src/sqlite.rs +++ b/mm2src/db_common/src/sqlite.rs @@ -99,7 +99,7 @@ pub fn validate_table_name(table_name: &str) -> SqlResult<()> { /// Represents a SQL table name that has been validated for safety. #[derive(Clone, Debug)] -pub struct SafeTableName(pub String); +pub struct SafeTableName(String); impl SafeTableName { /// Creates a new SafeTableName, validating the provided table name. @@ -107,6 +107,9 @@ impl SafeTableName { validate_table_name(table_name)?; Ok(SafeTableName(table_name.to_owned())) } + + /// Retrieves the table name. + pub fn get_name(&self) -> &str { &self.0 } } /// Calculates the offset to skip records by uuid. diff --git a/mm2src/derives/enum_from/Cargo.toml b/mm2src/derives/enum_utilities/Cargo.toml similarity index 89% rename from mm2src/derives/enum_from/Cargo.toml rename to mm2src/derives/enum_utilities/Cargo.toml index 5a1c58c1fb..d80c408347 100644 --- a/mm2src/derives/enum_from/Cargo.toml +++ b/mm2src/derives/enum_utilities/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "enum_from" +name = "enum_utilities" version = "0.1.0" edition = "2021" diff --git a/mm2src/derives/enum_from/src/from_inner.rs b/mm2src/derives/enum_utilities/src/from_inner.rs similarity index 100% rename from mm2src/derives/enum_from/src/from_inner.rs rename to mm2src/derives/enum_utilities/src/from_inner.rs diff --git a/mm2src/derives/enum_from/src/from_stringify.rs b/mm2src/derives/enum_utilities/src/from_stringify.rs similarity index 100% rename from mm2src/derives/enum_from/src/from_stringify.rs rename to mm2src/derives/enum_utilities/src/from_stringify.rs diff --git a/mm2src/derives/enum_from/src/from_trait.rs b/mm2src/derives/enum_utilities/src/from_trait.rs similarity index 100% rename from mm2src/derives/enum_from/src/from_trait.rs rename to mm2src/derives/enum_utilities/src/from_trait.rs diff --git a/mm2src/derives/enum_from/src/lib.rs b/mm2src/derives/enum_utilities/src/lib.rs similarity index 98% rename from mm2src/derives/enum_from/src/lib.rs rename to mm2src/derives/enum_utilities/src/lib.rs index 7ebc27d36a..e296b1df4f 100644 --- a/mm2src/derives/enum_from/src/lib.rs +++ b/mm2src/derives/enum_utilities/src/lib.rs @@ -19,7 +19,7 @@ const ENUM_VARIANT_LIST_IDENT: &str = "EnumVariantList"; /// # Usage /// /// ```rust -/// use enum_from::EnumFromInner; +/// use enum_utilities::EnumFromInner; /// /// #[derive(EnumFromInner)] /// enum FooBar { @@ -52,7 +52,7 @@ pub fn enum_from_inner(input: TokenStream) -> TokenStream { /// # Usage /// /// ```rust -/// use enum_from::EnumFromTrait; +/// use enum_utilities::EnumFromTrait; /// /// #[derive(EnumFromTrait)] /// enum FooBar { @@ -94,7 +94,7 @@ pub fn enum_from_trait(input: TokenStream) -> TokenStream { /// ### USAGE: /// /// ```rust -/// use enum_from::EnumFromStringify; +/// use enum_utilities::EnumFromStringify; /// use std::fmt::{Display, Formatter}; /// use std::io::{Error, ErrorKind}; /// @@ -132,7 +132,7 @@ pub fn derive(input: TokenStream) -> TokenStream { /// ### USAGE: /// /// ```rust -/// use enum_from::EnumVariantList; +/// use enum_utilities::EnumVariantList; /// /// #[derive(EnumVariantList)] /// enum Chain { diff --git a/mm2src/mm2_db/Cargo.toml b/mm2src/mm2_db/Cargo.toml index 7f2418159b..47d7c7f756 100644 --- a/mm2src/mm2_db/Cargo.toml +++ b/mm2src/mm2_db/Cargo.toml @@ -10,7 +10,7 @@ doctest = false async-trait = "0.1" common = { path = "../common" } derive_more = "0.99" -enum_from = { path = "../derives/enum_from" } +enum_utilities = { path = "../derives/enum_utilities" } futures = { version = "0.3", package = "futures", features = ["compat", "async-await", "thread-pool"] } itertools = "0.10" hex = "0.4.2" diff --git a/mm2src/mm2_db/src/indexed_db/drivers/cursor/cursor.rs b/mm2src/mm2_db/src/indexed_db/drivers/cursor/cursor.rs index bba549d3bd..d128524951 100644 --- a/mm2src/mm2_db/src/indexed_db/drivers/cursor/cursor.rs +++ b/mm2src/mm2_db/src/indexed_db/drivers/cursor/cursor.rs @@ -3,7 +3,7 @@ use crate::indexed_db::db_driver::{InternalItem, ItemId}; use crate::indexed_db::BeBigUint; use common::wasm::{deserialize_from_js, serialize_to_js, stringify_js_error}; use derive_more::Display; -use enum_from::EnumFromTrait; +use enum_utilities::EnumFromTrait; use futures::channel::mpsc; use futures::StreamExt; use js_sys::Array; diff --git a/mm2src/mm2_db/src/indexed_db/drivers/transaction.rs b/mm2src/mm2_db/src/indexed_db/drivers/transaction.rs index f76794b2de..e4b4d6c83c 100644 --- a/mm2src/mm2_db/src/indexed_db/drivers/transaction.rs +++ b/mm2src/mm2_db/src/indexed_db/drivers/transaction.rs @@ -1,7 +1,7 @@ use super::IdbObjectStoreImpl; use common::wasm::stringify_js_error; use derive_more::Display; -use enum_from::EnumFromTrait; +use enum_utilities::EnumFromTrait; use mm2_err_handle::prelude::*; use serde_json::Value as Json; use std::collections::HashSet; diff --git a/mm2src/mm2_main/Cargo.toml b/mm2src/mm2_main/Cargo.toml index aa68d9469a..64b3772a9e 100644 --- a/mm2src/mm2_main/Cargo.toml +++ b/mm2src/mm2_main/Cargo.toml @@ -41,7 +41,7 @@ db_common = { path = "../db_common" } derive_more = "0.99" either = "1.6" ethereum-types = { version = "0.13", default-features = false, features = ["std", "serialize"] } -enum_from = { path = "../derives/enum_from" } +enum_utilities = { path = "../derives/enum_utilities" } enum-primitive-derive = "0.2" futures01 = { version = "0.1", package = "futures" } futures = { version = "0.3.1", package = "futures", features = ["compat", "async-await"] } diff --git a/mm2src/mm2_main/src/lp_init/init_hw.rs b/mm2src/mm2_main/src/lp_init/init_hw.rs index d9bc45da49..d04f114a63 100644 --- a/mm2src/mm2_main/src/lp_init/init_hw.rs +++ b/mm2src/mm2_main/src/lp_init/init_hw.rs @@ -6,7 +6,7 @@ use crypto::hw_rpc_task::{HwConnectStatuses, HwRpcTaskAwaitingStatus, HwRpcTaskU use crypto::{from_hw_error, CryptoCtx, CryptoCtxError, HwCtxInitError, HwDeviceInfo, HwError, HwPubkey, HwRpcError, HwWalletType, WithHwRpcError}; use derive_more::Display; -use enum_from::EnumFromTrait; +use enum_utilities::EnumFromTrait; use http::StatusCode; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; diff --git a/mm2src/mm2_main/src/lp_init/init_metamask.rs b/mm2src/mm2_main/src/lp_init/init_metamask.rs index f624a7c5c4..f9076cf3f0 100644 --- a/mm2src/mm2_main/src/lp_init/init_metamask.rs +++ b/mm2src/mm2_main/src/lp_init/init_metamask.rs @@ -4,7 +4,7 @@ use common::{HttpStatusCode, SerdeInfallible, SuccessResponse}; use crypto::metamask::{from_metamask_error, MetamaskError, MetamaskRpcError, WithMetamaskRpcError}; use crypto::{CryptoCtx, CryptoCtxError, MetamaskCtxInitError}; use derive_more::Display; -use enum_from::EnumFromTrait; +use enum_utilities::EnumFromTrait; use http::StatusCode; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::common_errors::WithInternal; diff --git a/mm2src/mm2_main/src/lp_native_dex.rs b/mm2src/mm2_main/src/lp_native_dex.rs index 8f98bfc975..a3ade614a9 100644 --- a/mm2src/mm2_main/src/lp_native_dex.rs +++ b/mm2src/mm2_main/src/lp_native_dex.rs @@ -24,7 +24,7 @@ use common::executor::{SpawnFuture, Timer}; use common::log::{info, warn}; use crypto::{from_hw_error, CryptoCtx, CryptoInitError, HwError, HwProcessingError, HwRpcError, WithHwRpcError}; use derive_more::Display; -use enum_from::EnumFromTrait; +use enum_utilities::EnumFromTrait; use mm2_core::mm_ctx::{MmArc, MmCtx}; use mm2_err_handle::common_errors::InternalError; use mm2_err_handle::prelude::*; From 6acfff9c8649c2b8edcf66cc26e68d2fa3c51157 Mon Sep 17 00:00:00 2001 From: laruh Date: Tue, 16 Jan 2024 16:56:18 +0700 Subject: [PATCH 11/13] reformat more doc coms in idiomatic Rust style --- mm2src/coins/nft/nft_errors.rs | 19 +++++++------------ mm2src/coins/nft/nft_structs.rs | 11 +++++------ 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/mm2src/coins/nft/nft_errors.rs b/mm2src/coins/nft/nft_errors.rs index 9c59135415..c042e462c7 100644 --- a/mm2src/coins/nft/nft_errors.rs +++ b/mm2src/coins/nft/nft_errors.rs @@ -274,13 +274,12 @@ impl HttpStatusCode for UpdateNftError { } /// Enumerates the errors that can occur during spam protection operations. -/// -/// This includes issues such as regex failures during text validation and -/// serialization/deserialization problems. #[derive(Clone, Debug, Deserialize, Display, EnumFromStringify, PartialEq, Serialize)] pub enum ProtectFromSpamError { + /// Error related to regular expression operations. #[from_stringify("regex::Error")] RegexError(String), + /// Error related to serialization or deserialization with serde_json. #[from_stringify("serde_json::Error")] SerdeError(String), } @@ -346,14 +345,12 @@ impl From for MetaFromUrlError { } /// Represents errors that can occur while locking the NFT database. -/// -/// Variants: -/// - `WasmNftCacheError`: Errors specific to the WebAssembly (WASM) environment's NFT cache. -/// - `SqlError`: Errors related to SQL operations in non-WASM environments. #[derive(Debug, Display)] pub enum LockDBError { + /// Errors specific to the WebAssembly (WASM) environment's NFT cache. #[cfg(target_arch = "wasm32")] WasmNftCacheError(WasmNftCacheError), + /// Errors related to SQL operations in non-WASM environments. #[cfg(not(target_arch = "wasm32"))] SqlError(SqlError), } @@ -369,17 +366,15 @@ impl From for LockDBError { } /// Errors related to calculating transfer confirmations for NFTs. -/// -/// Variants: -/// - `NoSuchCoin`: Occurs when the specified coin does not exist. -/// - `CoinDoesntSupportNft`: Triggered when the specified coin does not support NFT operations. -/// - `GetCurrentBlockErr`: Represents errors encountered while retrieving the current block number. #[derive(Clone, Debug, Deserialize, Display, PartialEq, Serialize)] pub enum TransferConfirmationsError { + /// Occurs when the specified coin does not exist. #[display(fmt = "No such coin {}", coin)] NoSuchCoin { coin: String }, + /// Triggered when the specified coin does not support NFT operations. #[display(fmt = "{} coin doesn't support NFT", coin)] CoinDoesntSupportNft { coin: String }, + /// Represents errors encountered while retrieving the current block number. #[display(fmt = "Get current block error: {}", _0)] GetCurrentBlockErr(String), } diff --git a/mm2src/coins/nft/nft_structs.rs b/mm2src/coins/nft/nft_structs.rs index d3fd8dbe65..ecdb391f64 100644 --- a/mm2src/coins/nft/nft_structs.rs +++ b/mm2src/coins/nft/nft_structs.rs @@ -68,18 +68,17 @@ pub struct NftListFilters { } /// Contains parameters required to fetch metadata for a specified NFT. -/// # Fields -/// * `token_address`: The address of the NFT token. -/// * `token_id`: The ID of the NFT token. -/// * `chain`: The blockchain where the NFT exists. -/// * `protect_from_spam`: Indicates whether to check and redact potential spam. If set to true, -/// the internal function `protect_from_nft_spam` is utilized. #[derive(Debug, Deserialize)] pub struct NftMetadataReq { + /// The address of the NFT token. pub(crate) token_address: Address, + /// The ID of the NFT token. #[serde(deserialize_with = "deserialize_token_id")] pub(crate) token_id: BigUint, + /// The blockchain where the NFT exists. pub(crate) chain: Chain, + /// Indicates whether to check and redact potential spam. If set to true, + /// the internal function `protect_from_nft_spam` is utilized. #[serde(default)] pub(crate) protect_from_spam: bool, } From 32fc16e10251bb1b34ecc1dc518f39f8ecde5383 Mon Sep 17 00:00:00 2001 From: laruh Date: Wed, 17 Jan 2024 13:57:01 +0700 Subject: [PATCH 12/13] rename to `enum_derives`, fix doc comm, fn inner, vec chains non-optional --- Cargo.lock | 10 +-- Cargo.toml | 2 +- mm2src/coins/Cargo.toml | 2 +- mm2src/coins/eth.rs | 12 +-- mm2src/coins/eth/v2_activation.rs | 2 +- mm2src/coins/hd_confirm_address.rs | 2 +- mm2src/coins/lp_coins.rs | 2 +- mm2src/coins/nft.rs | 4 +- mm2src/coins/nft/nft_errors.rs | 7 +- mm2src/coins/nft/nft_structs.rs | 6 +- mm2src/coins/nft/storage/sql_storage.rs | 89 +++++++++---------- mm2src/coins/rpc_command/get_new_address.rs | 2 +- .../coins/rpc_command/init_create_account.rs | 2 +- mm2src/crypto/Cargo.toml | 2 +- mm2src/crypto/src/shared_db_id.rs | 2 +- mm2src/db_common/src/sqlite.rs | 3 +- .../Cargo.toml | 2 +- .../src/from_inner.rs | 0 .../src/from_stringify.rs | 0 .../src/from_trait.rs | 0 .../src/lib.rs | 8 +- mm2src/mm2_db/Cargo.toml | 2 +- .../src/indexed_db/drivers/cursor/cursor.rs | 2 +- .../src/indexed_db/drivers/transaction.rs | 2 +- mm2src/mm2_main/Cargo.toml | 2 +- mm2src/mm2_main/src/lp_init/init_hw.rs | 2 +- mm2src/mm2_main/src/lp_init/init_metamask.rs | 2 +- mm2src/mm2_main/src/lp_native_dex.rs | 2 +- 28 files changed, 83 insertions(+), 90 deletions(-) rename mm2src/derives/{enum_utilities => enum_derives}/Cargo.toml (89%) rename mm2src/derives/{enum_utilities => enum_derives}/src/from_inner.rs (100%) rename mm2src/derives/{enum_utilities => enum_derives}/src/from_stringify.rs (100%) rename mm2src/derives/{enum_utilities => enum_derives}/src/from_trait.rs (100%) rename mm2src/derives/{enum_utilities => enum_derives}/src/lib.rs (98%) diff --git a/Cargo.lock b/Cargo.lock index 7fb0d565d7..9709c859a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1013,7 +1013,7 @@ dependencies = [ "dirs", "ed25519-dalek", "ed25519-dalek-bip32 0.2.0", - "enum_utilities", + "enum_derives", "ethabi", "ethcore-transaction", "ethereum-types", @@ -1501,7 +1501,7 @@ dependencies = [ "common", "derive_more", "enum-primitive-derive", - "enum_utilities", + "enum_derives", "futures 0.3.28", "hex 0.4.3", "http 0.2.7", @@ -2072,7 +2072,7 @@ dependencies = [ ] [[package]] -name = "enum_utilities" +name = "enum_derives" version = "0.1.0" dependencies = [ "itertools", @@ -4284,7 +4284,7 @@ dependencies = [ "async-trait", "common", "derive_more", - "enum_utilities", + "enum_derives", "futures 0.3.28", "hex 0.4.3", "itertools", @@ -4422,7 +4422,7 @@ dependencies = [ "dirs", "either", "enum-primitive-derive", - "enum_utilities", + "enum_derives", "ethereum-types", "futures 0.1.29", "futures 0.3.28", diff --git a/Cargo.toml b/Cargo.toml index 7bba94fe9f..5868b5b314 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ members = [ "mm2src/common/shared_ref_counter", "mm2src/crypto", "mm2src/db_common", - "mm2src/derives/enum_utilities", + "mm2src/derives/enum_derives", "mm2src/derives/ser_error_derive", "mm2src/derives/ser_error", "mm2src/hw_common", diff --git a/mm2src/coins/Cargo.toml b/mm2src/coins/Cargo.toml index d185efcbcf..77826bdc70 100644 --- a/mm2src/coins/Cargo.toml +++ b/mm2src/coins/Cargo.toml @@ -42,7 +42,7 @@ crypto = { path = "../crypto" } db_common = { path = "../db_common" } derive_more = "0.99" ed25519-dalek = "1.0.1" -enum_utilities = { path = "../derives/enum_utilities" } +enum_derives = { path = "../derives/enum_derives" } ethabi = { version = "17.0.0" } ethcore-transaction = { git = "https://github.com/KomodoPlatform/mm2-parity-ethereum.git" } ethereum-types = { version = "0.13", default-features = false, features = ["std", "serialize"] } diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 093beef328..7ae2caa28e 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -37,7 +37,7 @@ use common::{now_ms, wait_until_ms}; use crypto::privkey::key_pair_from_secret; use crypto::{CryptoCtx, CryptoCtxError, GlobalHDAccountArc, KeyPairPolicy, StandardHDCoinAddress}; use derive_more::Display; -use enum_utilities::EnumFromStringify; +use enum_derives::EnumFromStringify; use ethabi::{Contract, Function, Token}; pub use ethcore_transaction::SignedTransaction as SignedEthTx; use ethcore_transaction::{Action, Transaction as UnSignedEthTx, UnverifiedTransaction}; @@ -5830,16 +5830,12 @@ pub async fn get_eth_address( } /// Errors encountered while validating Ethereum addresses for NFT withdrawal. -/// -/// Variants: -/// - `CoinDoesntSupportNftWithdraw`: The specified coin does not support NFT withdrawal. -/// - `InvalidAddress`: The provided address is invalid. #[derive(Display)] pub enum GetValidEthWithdrawAddError { + /// The specified coin does not support NFT withdrawal. #[display(fmt = "{} coin doesn't support NFT withdrawing", coin)] - CoinDoesntSupportNftWithdraw { - coin: String, - }, + CoinDoesntSupportNftWithdraw { coin: String }, + /// The provided address is invalid. InvalidAddress(String), } diff --git a/mm2src/coins/eth/v2_activation.rs b/mm2src/coins/eth/v2_activation.rs index d424d96606..cd54e472f3 100644 --- a/mm2src/coins/eth/v2_activation.rs +++ b/mm2src/coins/eth/v2_activation.rs @@ -2,7 +2,7 @@ use super::*; #[cfg(target_arch = "wasm32")] use crate::EthMetamaskPolicy; use common::executor::AbortedError; use crypto::{CryptoCtxError, StandardHDCoinAddress}; -use enum_utilities::EnumFromTrait; +use enum_derives::EnumFromTrait; use mm2_err_handle::common_errors::WithInternal; #[cfg(target_arch = "wasm32")] use mm2_metamask::{from_metamask_error, MetamaskError, MetamaskRpcError, WithMetamaskRpcError}; diff --git a/mm2src/coins/hd_confirm_address.rs b/mm2src/coins/hd_confirm_address.rs index 69b7eca4c2..deccbac75b 100644 --- a/mm2src/coins/hd_confirm_address.rs +++ b/mm2src/coins/hd_confirm_address.rs @@ -4,7 +4,7 @@ use crypto::hw_rpc_task::HwConnectStatuses; use crypto::trezor::trezor_rpc_task::{TrezorRequestStatuses, TrezorRpcTaskProcessor, TryIntoUserAction}; use crypto::trezor::{ProcessTrezorResponse, TrezorError, TrezorProcessingError}; use crypto::{CryptoCtx, CryptoCtxError, HardwareWalletArc, HwError, HwProcessingError}; -use enum_utilities::{EnumFromInner, EnumFromStringify}; +use enum_derives::{EnumFromInner, EnumFromStringify}; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use rpc_task::{RpcTask, RpcTaskError, RpcTaskHandleShared}; diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 70620a7f7d..2d1dea7885 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -52,7 +52,7 @@ use common::{calc_total_pages, now_sec, ten, HttpStatusCode}; use crypto::{derive_secp256k1_secret, Bip32Error, CryptoCtx, CryptoCtxError, DerivationPath, GlobalHDAccountArc, HwRpcError, KeyPairPolicy, Secp256k1Secret, StandardHDCoinAddress, StandardHDPathToCoin, WithHwRpcError}; use derive_more::Display; -use enum_utilities::{EnumFromStringify, EnumFromTrait}; +use enum_derives::{EnumFromStringify, EnumFromTrait}; use ethereum_types::H256; use futures::compat::Future01CompatExt; use futures::lock::Mutex as AsyncMutex; diff --git a/mm2src/coins/nft.rs b/mm2src/coins/nft.rs index cc543bfc4f..e243eab9c5 100644 --- a/mm2src/coins/nft.rs +++ b/mm2src/coins/nft.rs @@ -1380,10 +1380,10 @@ pub async fn clear_nft_db(ctx: MmArc, req: ClearNftDbReq) -> MmResult<(), ClearN let storage = nft_ctx.lock_db().await?; storage.clear_all_nft_data().await?; storage.clear_all_history_data().await?; - } else if let Some(chains) = req.chains { + } else if !req.chains.is_empty() { let nft_ctx = NftCtx::from_ctx(&ctx).map_to_mm(ClearNftDbError::Internal)?; let storage = nft_ctx.lock_db().await?; - for chain in chains.iter() { + for chain in req.chains.iter() { if let Err(e) = clear_data_for_chain(&storage, chain).await { error!("Failed to clear data for chain {}: {}", chain, e); } diff --git a/mm2src/coins/nft/nft_errors.rs b/mm2src/coins/nft/nft_errors.rs index c042e462c7..96e520e5cd 100644 --- a/mm2src/coins/nft/nft_errors.rs +++ b/mm2src/coins/nft/nft_errors.rs @@ -7,7 +7,7 @@ use common::{HttpStatusCode, ParseRfc3339Err}; #[cfg(not(target_arch = "wasm32"))] use db_common::sqlite::rusqlite::Error as SqlError; use derive_more::Display; -use enum_utilities::EnumFromStringify; +use enum_derives::EnumFromStringify; use http::StatusCode; use mm2_net::transport::{GetInfoFromUriError, SlurpError}; use serde::{Deserialize, Serialize}; @@ -125,7 +125,9 @@ impl From for GetNftInfoError { impl HttpStatusCode for GetNftInfoError { fn status_code(&self) -> StatusCode { match self { - GetNftInfoError::InvalidRequest(_) => StatusCode::BAD_REQUEST, + GetNftInfoError::InvalidRequest(_) | GetNftInfoError::TransferConfirmationsError(_) => { + StatusCode::BAD_REQUEST + }, GetNftInfoError::InvalidResponse(_) | GetNftInfoError::ParseRfc3339Err(_) => StatusCode::FAILED_DEPENDENCY, GetNftInfoError::ContractTypeIsNull => StatusCode::NOT_FOUND, GetNftInfoError::Transport(_) @@ -134,7 +136,6 @@ impl HttpStatusCode for GetNftInfoError { | GetNftInfoError::TokenNotFoundInWallet { .. } | GetNftInfoError::DbError(_) | GetNftInfoError::ProtectFromSpamError(_) - | GetNftInfoError::TransferConfirmationsError(_) | GetNftInfoError::NumConversError(_) => StatusCode::INTERNAL_SERVER_ERROR, } } diff --git a/mm2src/coins/nft/nft_structs.rs b/mm2src/coins/nft/nft_structs.rs index ecdb391f64..4a4163f633 100644 --- a/mm2src/coins/nft/nft_structs.rs +++ b/mm2src/coins/nft/nft_structs.rs @@ -1,5 +1,5 @@ use common::ten; -use enum_utilities::EnumVariantList; +use enum_derives::EnumVariantList; use ethereum_types::Address; use mm2_core::mm_ctx::{from_ctx, MmArc}; use mm2_err_handle::prelude::*; @@ -766,8 +766,8 @@ where /// Request parameters for clearing NFT data from the database. #[derive(Debug, Deserialize)] pub struct ClearNftDbReq { - /// (Optional) Specifies the blockchain networks (e.g., Ethereum, BSC) to clear NFT data. - pub(crate) chains: Option>, + /// Specifies the blockchain networks (e.g., Ethereum, BSC) to clear NFT data. + pub(crate) chains: Vec, /// If `true`, clears NFT data for all chains, ignoring the `chains` field. Defaults to `false`. #[serde(default)] pub(crate) clear_all: bool, diff --git a/mm2src/coins/nft/storage/sql_storage.rs b/mm2src/coins/nft/storage/sql_storage.rs index 2a1525c899..6844b261d9 100644 --- a/mm2src/coins/nft/storage/sql_storage.rs +++ b/mm2src/coins/nft/storage/sql_storage.rs @@ -75,7 +75,7 @@ fn create_nft_list_table_sql(chain: &Chain) -> MmResult { details_json TEXT, PRIMARY KEY (token_address, token_id) );", - safe_table_name.get_name() + safe_table_name.inner() ); Ok(sql) } @@ -105,7 +105,7 @@ fn create_transfer_history_table_sql(chain: &Chain) -> Result details_json TEXT, PRIMARY KEY (transaction_hash, log_index) );", - safe_table_name.get_name() + safe_table_name.inner() ); Ok(sql) } @@ -117,7 +117,7 @@ fn create_scanned_nft_blocks_sql() -> Result { chain TEXT PRIMARY KEY, last_scanned_block INTEGER DEFAULT 0 );", - safe_table_name.get_name() + safe_table_name.inner() ); Ok(sql) } @@ -149,7 +149,7 @@ fn nft_list_builder_preimage( safe_table_name: SafeTableName, filters: Option, ) -> Result { - let mut sql_builder = SqlBuilder::select_from(safe_table_name.get_name()); + let mut sql_builder = SqlBuilder::select_from(safe_table_name.inner()); if let Some(filters) = filters { if filters.exclude_spam { sql_builder.and_where("possible_spam == 0"); @@ -190,7 +190,7 @@ fn nft_history_table_builder_preimage( safe_table_name: SafeTableName, filters: Option, ) -> Result { - let mut sql_builder = SqlBuilder::select_from(safe_table_name.get_name()); + let mut sql_builder = SqlBuilder::select_from(safe_table_name.inner()); if let Some(filters) = filters { if filters.send && !filters.receive { sql_builder.and_where_eq("status", "'Send'"); @@ -403,7 +403,7 @@ fn insert_nft_in_list_sql(chain: &Chain) -> Result { ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23, ?24, ?25, ?26, ?27 );", - safe_table_name.get_name() + safe_table_name.inner() ); Ok(sql) } @@ -418,7 +418,7 @@ fn insert_transfer_in_history_sql(chain: &Chain) -> Result { ) VALUES ( ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19 );", - safe_table_name.get_name() + safe_table_name.inner() ); Ok(sql) } @@ -427,7 +427,7 @@ fn upsert_last_scanned_block_sql() -> Result { let safe_table_name = scanned_nft_blocks_table_name()?; let sql = format!( "INSERT OR REPLACE INTO {} (chain, last_scanned_block) VALUES (?1, ?2);", - safe_table_name.get_name() + safe_table_name.inner() ); Ok(sql) } @@ -438,7 +438,7 @@ fn refresh_nft_metadata_sql(chain: &Chain) -> Result { "UPDATE {} SET possible_spam = ?1, possible_phishing = ?2, collection_name = ?3, symbol = ?4, token_uri = ?5, token_domain = ?6, metadata = ?7, \ last_token_uri_sync = ?8, last_metadata_sync = ?9, raw_image_url = ?10, image_url = ?11, image_domain = ?12, token_name = ?13, description = ?14, \ attributes = ?15, animation_url = ?16, animation_domain = ?17, external_url = ?18, external_domain = ?19, image_details = ?20 WHERE token_address = ?21 AND token_id = ?22;", - safe_table_name.get_name() + safe_table_name.inner() ); Ok(sql) } @@ -448,7 +448,7 @@ fn update_transfers_meta_by_token_addr_id_sql(chain: &Chain) -> Result Result Result Result { let sql = format!( "SELECT block_number FROM {} ORDER BY block_number DESC LIMIT 1", - safe_table_name.get_name() + safe_table_name.inner() ); Ok(sql) } fn select_last_scanned_block_sql() -> MmResult { let table_name = scanned_nft_blocks_table_name()?; - let sql = format!( - "SELECT last_scanned_block FROM {} WHERE chain=?1", - table_name.get_name() - ); + let sql = format!("SELECT last_scanned_block FROM {} WHERE chain=?1", table_name.inner()); Ok(sql) } fn delete_nft_sql(safe_table_name: SafeTableName) -> Result { let sql = format!( "DELETE FROM {} WHERE token_address=?1 AND token_id=?2", - safe_table_name.get_name() + safe_table_name.inner() ); Ok(sql) } @@ -495,13 +492,13 @@ fn get_nfts_by_token_address_statement( conn: &Connection, safe_table_name: SafeTableName, ) -> Result { - let sql_query = format!("SELECT * FROM {} WHERE token_address = ?", safe_table_name.get_name()); + let sql_query = format!("SELECT * FROM {} WHERE token_address = ?", safe_table_name.inner()); let stmt = conn.prepare(&sql_query)?; Ok(stmt) } fn get_token_addresses_statement(conn: &Connection, safe_table_name: SafeTableName) -> Result { - let sql_query = format!("SELECT DISTINCT token_address FROM {}", safe_table_name.get_name()); + let sql_query = format!("SELECT DISTINCT token_address FROM {}", safe_table_name.inner()); let stmt = conn.prepare(&sql_query)?; Ok(stmt) } @@ -510,7 +507,7 @@ fn get_transfers_from_block_statement<'a>(conn: &'a Connection, chain: &'a Chain let safe_table_name = chain.transfer_history_table_name()?; let sql_query = format!( "SELECT * FROM {} WHERE block_number >= ? ORDER BY block_number ASC", - safe_table_name.get_name() + safe_table_name.inner() ); let stmt = conn.prepare(&sql_query)?; Ok(stmt) @@ -520,7 +517,7 @@ fn get_transfers_by_token_addr_id_statement(conn: &Connection, chain: Chain) -> let safe_table_name = chain.transfer_history_table_name()?; let sql_query = format!( "SELECT * FROM {} WHERE token_address = ? AND token_id = ?", - safe_table_name.get_name() + safe_table_name.inner() ); let stmt = conn.prepare(&sql_query)?; Ok(stmt) @@ -528,7 +525,7 @@ fn get_transfers_by_token_addr_id_statement(conn: &Connection, chain: Chain) -> fn get_transfers_with_empty_meta_builder<'a>(conn: &'a Connection, chain: &'a Chain) -> Result, SqlError> { let safe_table_name = chain.transfer_history_table_name()?; - let mut sql_builder = SqlQuery::select_from(conn, safe_table_name.get_name())?; + let mut sql_builder = SqlQuery::select_from(conn, safe_table_name.inner())?; sql_builder .sql_builder() .distinct() @@ -544,7 +541,7 @@ fn get_transfers_with_empty_meta_builder<'a>(conn: &'a Connection, chain: &'a Ch } fn is_table_empty(conn: &Connection, safe_table_name: SafeTableName) -> Result { - let query = format!("SELECT COUNT(*) FROM {}", safe_table_name.get_name()); + let query = format!("SELECT COUNT(*) FROM {}", safe_table_name.inner()); conn.query_row(&query, [], |row| row.get::<_, i64>(0)) .map(|count| count == 0) } @@ -568,11 +565,11 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.nft_list_table_name()?; self.call(move |conn| { let nft_list_initialized = - query_single_row(conn, CHECK_TABLE_EXISTS_SQL, [table_name.get_name()], string_from_row)?; + query_single_row(conn, CHECK_TABLE_EXISTS_SQL, [table_name.inner()], string_from_row)?; let scanned_nft_blocks_initialized = query_single_row( conn, CHECK_TABLE_EXISTS_SQL, - [scanned_nft_blocks_table_name()?.get_name()], + [scanned_nft_blocks_table_name()?.inner()], string_from_row, )?; Ok(nft_list_initialized.is_some() && scanned_nft_blocks_initialized.is_some()) @@ -684,7 +681,7 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { self.call(move |conn| { let sql = format!( "SELECT * FROM {} WHERE token_address=?1 AND token_id=?2", - table_name.get_name() + table_name.inner() ); let params = [token_address, token_id.to_string()]; let nft = query_single_row(conn, &sql, params, nft_from_row)?; @@ -731,7 +728,7 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.nft_list_table_name()?; let sql = format!( "SELECT amount FROM {} WHERE token_address=?1 AND token_id=?2", - table_name.get_name() + table_name.inner() ); let params = [token_address, token_id.to_string()]; self.call(move |conn| { @@ -808,7 +805,7 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.nft_list_table_name()?; let sql = format!( "UPDATE {} SET amount = ?1 WHERE token_address = ?2 AND token_id = ?3;", - table_name.get_name() + table_name.inner() ); let scanned_block_params = [chain.to_ticker().to_string(), scanned_block.to_string()]; self.call(move |conn| { @@ -831,7 +828,7 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.nft_list_table_name()?; let sql = format!( "UPDATE {} SET amount = ?1, block_number = ?2 WHERE token_address = ?3 AND token_id = ?4;", - table_name.get_name() + table_name.inner() ); let scanned_block_params = [chain.to_ticker().to_string(), nft.block_number.to_string()]; self.call(move |conn| { @@ -873,7 +870,7 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.nft_list_table_name()?; let sql = format!( "UPDATE {} SET possible_spam = ?1 WHERE token_address = ?2;", - table_name.get_name() + table_name.inner() ); self.call(move |conn| { let sql_transaction = conn.transaction()?; @@ -889,7 +886,7 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { async fn get_animation_external_domains(&self, chain: &Chain) -> MmResult, Self::Error> { let safe_table_name = chain.nft_list_table_name()?; self.call(move |conn| { - let table_name = safe_table_name.get_name(); + let table_name = safe_table_name.inner(); let sql_query = format!( "SELECT DISTINCT animation_domain FROM {} UNION SELECT DISTINCT external_domain FROM {}", table_name, table_name @@ -915,7 +912,7 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let sql = format!( "UPDATE {} SET possible_phishing = ?1 WHERE token_domain = ?2 OR image_domain = ?2 OR animation_domain = ?2 OR external_domain = ?2;", - table_name.get_name() + table_name.inner() ); self.call(move |conn| { let sql_transaction = conn.transaction()?; @@ -930,9 +927,9 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { async fn clear_nft_data(&self, chain: &Chain) -> MmResult<(), Self::Error> { let table_nft_name = chain.nft_list_table_name()?; - let sql_nft = format!("DROP TABLE IF EXISTS {};", table_nft_name.get_name()); + let sql_nft = format!("DROP TABLE IF EXISTS {};", table_nft_name.inner()); let table_scanned_blocks = scanned_nft_blocks_table_name()?; - let sql_scanned_block = format!("DELETE from {} where chain=?1", table_scanned_blocks.get_name()); + let sql_scanned_block = format!("DELETE from {} where chain=?1", table_scanned_blocks.inner()); let scanned_block_param = [chain.to_ticker()]; self.call(move |conn| { let sql_transaction = conn.transaction()?; @@ -940,9 +937,8 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { sql_transaction.execute(&sql_scanned_block, scanned_block_param)?; sql_transaction.commit()?; if is_table_empty(conn, table_scanned_blocks.clone())? { - conn.execute(&format!("DROP TABLE IF EXISTS {};", table_scanned_blocks.get_name()), [ - ]) - .map(|_| ())?; + conn.execute(&format!("DROP TABLE IF EXISTS {};", table_scanned_blocks.inner()), []) + .map(|_| ())?; } Ok(()) }) @@ -955,11 +951,10 @@ impl NftListStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let sql_transaction = conn.transaction()?; for chain in Chain::variant_list().into_iter() { let table_name = chain.nft_list_table_name()?; - sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_name.get_name()), [])?; + sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_name.inner()), [])?; } let table_scanned_blocks = scanned_nft_blocks_table_name()?; - sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_scanned_blocks.get_name()), [ - ])?; + sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_scanned_blocks.inner()), [])?; sql_transaction.commit()?; Ok(()) }) @@ -986,7 +981,7 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.transfer_history_table_name()?; self.call(move |conn| { let nft_list_initialized = - query_single_row(conn, CHECK_TABLE_EXISTS_SQL, [table_name.get_name()], string_from_row)?; + query_single_row(conn, CHECK_TABLE_EXISTS_SQL, [table_name.inner()], string_from_row)?; Ok(nft_list_initialized.is_some()) }) .await @@ -1135,7 +1130,7 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.transfer_history_table_name()?; let sql = format!( "SELECT * FROM {} WHERE transaction_hash=?1 AND log_index = ?2", - table_name.get_name() + table_name.inner() ); self.call(move |conn| { let transfer = query_single_row( @@ -1222,7 +1217,7 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.transfer_history_table_name()?; let sql = format!( "UPDATE {} SET possible_spam = ?1 WHERE token_address = ?2;", - table_name.get_name() + table_name.inner() ); self.call(move |conn| { let sql_transaction = conn.transaction()?; @@ -1251,7 +1246,7 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { async fn get_domains(&self, chain: &Chain) -> MmResult, Self::Error> { let safe_table_name = chain.transfer_history_table_name()?; self.call(move |conn| { - let table_name = safe_table_name.get_name(); + let table_name = safe_table_name.inner(); let sql_query = format!( "SELECT DISTINCT token_domain FROM {} UNION SELECT DISTINCT image_domain FROM {}", table_name, table_name @@ -1276,7 +1271,7 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let safe_table_name = chain.transfer_history_table_name()?; let sql = format!( "UPDATE {} SET possible_phishing = ?1 WHERE token_domain = ?2 OR image_domain = ?2;", - safe_table_name.get_name() + safe_table_name.inner() ); self.call(move |conn| { let sql_transaction = conn.transaction()?; @@ -1293,7 +1288,7 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let table_name = chain.transfer_history_table_name()?; self.call(move |conn| { let sql_transaction = conn.transaction()?; - sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_name.get_name()), [])?; + sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_name.inner()), [])?; sql_transaction.commit()?; Ok(()) }) @@ -1306,7 +1301,7 @@ impl NftTransferHistoryStorageOps for AsyncMutexGuard<'_, AsyncConnection> { let sql_transaction = conn.transaction()?; for chain in Chain::variant_list().into_iter() { let table_name = chain.transfer_history_table_name()?; - sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_name.get_name()), [])?; + sql_transaction.execute(&format!("DROP TABLE IF EXISTS {};", table_name.inner()), [])?; } sql_transaction.commit()?; Ok(()) diff --git a/mm2src/coins/rpc_command/get_new_address.rs b/mm2src/coins/rpc_command/get_new_address.rs index bb6a63a6c9..ee8d8ad73d 100644 --- a/mm2src/coins/rpc_command/get_new_address.rs +++ b/mm2src/coins/rpc_command/get_new_address.rs @@ -8,7 +8,7 @@ use common::{HttpStatusCode, SuccessResponse}; use crypto::hw_rpc_task::{HwConnectStatuses, HwRpcTaskAwaitingStatus, HwRpcTaskUserAction, HwRpcTaskUserActionRequest}; use crypto::{from_hw_error, Bip44Chain, HwError, HwRpcError, WithHwRpcError}; use derive_more::Display; -use enum_utilities::EnumFromTrait; +use enum_derives::EnumFromTrait; use http::StatusCode; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; diff --git a/mm2src/coins/rpc_command/init_create_account.rs b/mm2src/coins/rpc_command/init_create_account.rs index 4e81037089..6e8b47047d 100644 --- a/mm2src/coins/rpc_command/init_create_account.rs +++ b/mm2src/coins/rpc_command/init_create_account.rs @@ -8,7 +8,7 @@ use common::{true_f, HttpStatusCode, SuccessResponse}; use crypto::hw_rpc_task::{HwConnectStatuses, HwRpcTaskAwaitingStatus, HwRpcTaskUserAction, HwRpcTaskUserActionRequest}; use crypto::{from_hw_error, Bip44Chain, HwError, HwRpcError, RpcDerivationPath, WithHwRpcError}; use derive_more::Display; -use enum_utilities::EnumFromTrait; +use enum_derives::EnumFromTrait; use http::StatusCode; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; diff --git a/mm2src/crypto/Cargo.toml b/mm2src/crypto/Cargo.toml index c892246da2..80fd38a212 100644 --- a/mm2src/crypto/Cargo.toml +++ b/mm2src/crypto/Cargo.toml @@ -14,7 +14,7 @@ bitcrypto = { path = "../mm2_bitcoin/crypto" } bs58 = "0.4.0" common = { path = "../common" } derive_more = "0.99" -enum_utilities = { path = "../derives/enum_utilities" } +enum_derives = { path = "../derives/enum_derives" } enum-primitive-derive = "0.2" futures = "0.3" hex = "0.4.2" diff --git a/mm2src/crypto/src/shared_db_id.rs b/mm2src/crypto/src/shared_db_id.rs index 9f98aada73..8c78baaaf3 100644 --- a/mm2src/crypto/src/shared_db_id.rs +++ b/mm2src/crypto/src/shared_db_id.rs @@ -1,6 +1,6 @@ use crate::privkey::private_from_seed_hash; use derive_more::Display; -use enum_utilities::EnumFromStringify; +use enum_derives::EnumFromStringify; use keys::{Error as KeysError, KeyPair}; use mm2_err_handle::prelude::*; use primitives::hash::H160; diff --git a/mm2src/db_common/src/sqlite.rs b/mm2src/db_common/src/sqlite.rs index fcc307dab2..af9c4905e5 100644 --- a/mm2src/db_common/src/sqlite.rs +++ b/mm2src/db_common/src/sqlite.rs @@ -109,7 +109,8 @@ impl SafeTableName { } /// Retrieves the table name. - pub fn get_name(&self) -> &str { &self.0 } + #[inline(always)] + pub fn inner(&self) -> &str { &self.0 } } /// Calculates the offset to skip records by uuid. diff --git a/mm2src/derives/enum_utilities/Cargo.toml b/mm2src/derives/enum_derives/Cargo.toml similarity index 89% rename from mm2src/derives/enum_utilities/Cargo.toml rename to mm2src/derives/enum_derives/Cargo.toml index d80c408347..9518bc6d1a 100644 --- a/mm2src/derives/enum_utilities/Cargo.toml +++ b/mm2src/derives/enum_derives/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "enum_utilities" +name = "enum_derives" version = "0.1.0" edition = "2021" diff --git a/mm2src/derives/enum_utilities/src/from_inner.rs b/mm2src/derives/enum_derives/src/from_inner.rs similarity index 100% rename from mm2src/derives/enum_utilities/src/from_inner.rs rename to mm2src/derives/enum_derives/src/from_inner.rs diff --git a/mm2src/derives/enum_utilities/src/from_stringify.rs b/mm2src/derives/enum_derives/src/from_stringify.rs similarity index 100% rename from mm2src/derives/enum_utilities/src/from_stringify.rs rename to mm2src/derives/enum_derives/src/from_stringify.rs diff --git a/mm2src/derives/enum_utilities/src/from_trait.rs b/mm2src/derives/enum_derives/src/from_trait.rs similarity index 100% rename from mm2src/derives/enum_utilities/src/from_trait.rs rename to mm2src/derives/enum_derives/src/from_trait.rs diff --git a/mm2src/derives/enum_utilities/src/lib.rs b/mm2src/derives/enum_derives/src/lib.rs similarity index 98% rename from mm2src/derives/enum_utilities/src/lib.rs rename to mm2src/derives/enum_derives/src/lib.rs index e296b1df4f..666e95a0e3 100644 --- a/mm2src/derives/enum_utilities/src/lib.rs +++ b/mm2src/derives/enum_derives/src/lib.rs @@ -19,7 +19,7 @@ const ENUM_VARIANT_LIST_IDENT: &str = "EnumVariantList"; /// # Usage /// /// ```rust -/// use enum_utilities::EnumFromInner; +/// use enum_derives::EnumFromInner; /// /// #[derive(EnumFromInner)] /// enum FooBar { @@ -52,7 +52,7 @@ pub fn enum_from_inner(input: TokenStream) -> TokenStream { /// # Usage /// /// ```rust -/// use enum_utilities::EnumFromTrait; +/// use enum_derives::EnumFromTrait; /// /// #[derive(EnumFromTrait)] /// enum FooBar { @@ -94,7 +94,7 @@ pub fn enum_from_trait(input: TokenStream) -> TokenStream { /// ### USAGE: /// /// ```rust -/// use enum_utilities::EnumFromStringify; +/// use enum_derives::EnumFromStringify; /// use std::fmt::{Display, Formatter}; /// use std::io::{Error, ErrorKind}; /// @@ -132,7 +132,7 @@ pub fn derive(input: TokenStream) -> TokenStream { /// ### USAGE: /// /// ```rust -/// use enum_utilities::EnumVariantList; +/// use enum_derives::EnumVariantList; /// /// #[derive(EnumVariantList)] /// enum Chain { diff --git a/mm2src/mm2_db/Cargo.toml b/mm2src/mm2_db/Cargo.toml index 47d7c7f756..5f5374acad 100644 --- a/mm2src/mm2_db/Cargo.toml +++ b/mm2src/mm2_db/Cargo.toml @@ -10,7 +10,7 @@ doctest = false async-trait = "0.1" common = { path = "../common" } derive_more = "0.99" -enum_utilities = { path = "../derives/enum_utilities" } +enum_derives = { path = "../derives/enum_derives" } futures = { version = "0.3", package = "futures", features = ["compat", "async-await", "thread-pool"] } itertools = "0.10" hex = "0.4.2" diff --git a/mm2src/mm2_db/src/indexed_db/drivers/cursor/cursor.rs b/mm2src/mm2_db/src/indexed_db/drivers/cursor/cursor.rs index d128524951..2e7de40aae 100644 --- a/mm2src/mm2_db/src/indexed_db/drivers/cursor/cursor.rs +++ b/mm2src/mm2_db/src/indexed_db/drivers/cursor/cursor.rs @@ -3,7 +3,7 @@ use crate::indexed_db::db_driver::{InternalItem, ItemId}; use crate::indexed_db::BeBigUint; use common::wasm::{deserialize_from_js, serialize_to_js, stringify_js_error}; use derive_more::Display; -use enum_utilities::EnumFromTrait; +use enum_derives::EnumFromTrait; use futures::channel::mpsc; use futures::StreamExt; use js_sys::Array; diff --git a/mm2src/mm2_db/src/indexed_db/drivers/transaction.rs b/mm2src/mm2_db/src/indexed_db/drivers/transaction.rs index e4b4d6c83c..1973e6c9bd 100644 --- a/mm2src/mm2_db/src/indexed_db/drivers/transaction.rs +++ b/mm2src/mm2_db/src/indexed_db/drivers/transaction.rs @@ -1,7 +1,7 @@ use super::IdbObjectStoreImpl; use common::wasm::stringify_js_error; use derive_more::Display; -use enum_utilities::EnumFromTrait; +use enum_derives::EnumFromTrait; use mm2_err_handle::prelude::*; use serde_json::Value as Json; use std::collections::HashSet; diff --git a/mm2src/mm2_main/Cargo.toml b/mm2src/mm2_main/Cargo.toml index 64b3772a9e..2458214e11 100644 --- a/mm2src/mm2_main/Cargo.toml +++ b/mm2src/mm2_main/Cargo.toml @@ -41,7 +41,7 @@ db_common = { path = "../db_common" } derive_more = "0.99" either = "1.6" ethereum-types = { version = "0.13", default-features = false, features = ["std", "serialize"] } -enum_utilities = { path = "../derives/enum_utilities" } +enum_derives = { path = "../derives/enum_derives" } enum-primitive-derive = "0.2" futures01 = { version = "0.1", package = "futures" } futures = { version = "0.3.1", package = "futures", features = ["compat", "async-await"] } diff --git a/mm2src/mm2_main/src/lp_init/init_hw.rs b/mm2src/mm2_main/src/lp_init/init_hw.rs index d04f114a63..18e97bc096 100644 --- a/mm2src/mm2_main/src/lp_init/init_hw.rs +++ b/mm2src/mm2_main/src/lp_init/init_hw.rs @@ -6,7 +6,7 @@ use crypto::hw_rpc_task::{HwConnectStatuses, HwRpcTaskAwaitingStatus, HwRpcTaskU use crypto::{from_hw_error, CryptoCtx, CryptoCtxError, HwCtxInitError, HwDeviceInfo, HwError, HwPubkey, HwRpcError, HwWalletType, WithHwRpcError}; use derive_more::Display; -use enum_utilities::EnumFromTrait; +use enum_derives::EnumFromTrait; use http::StatusCode; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; diff --git a/mm2src/mm2_main/src/lp_init/init_metamask.rs b/mm2src/mm2_main/src/lp_init/init_metamask.rs index f9076cf3f0..f80afe5878 100644 --- a/mm2src/mm2_main/src/lp_init/init_metamask.rs +++ b/mm2src/mm2_main/src/lp_init/init_metamask.rs @@ -4,7 +4,7 @@ use common::{HttpStatusCode, SerdeInfallible, SuccessResponse}; use crypto::metamask::{from_metamask_error, MetamaskError, MetamaskRpcError, WithMetamaskRpcError}; use crypto::{CryptoCtx, CryptoCtxError, MetamaskCtxInitError}; use derive_more::Display; -use enum_utilities::EnumFromTrait; +use enum_derives::EnumFromTrait; use http::StatusCode; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::common_errors::WithInternal; diff --git a/mm2src/mm2_main/src/lp_native_dex.rs b/mm2src/mm2_main/src/lp_native_dex.rs index a3ade614a9..22a6cd3d4e 100644 --- a/mm2src/mm2_main/src/lp_native_dex.rs +++ b/mm2src/mm2_main/src/lp_native_dex.rs @@ -24,7 +24,7 @@ use common::executor::{SpawnFuture, Timer}; use common::log::{info, warn}; use crypto::{from_hw_error, CryptoCtx, CryptoInitError, HwError, HwProcessingError, HwRpcError, WithHwRpcError}; use derive_more::Display; -use enum_utilities::EnumFromTrait; +use enum_derives::EnumFromTrait; use mm2_core::mm_ctx::{MmArc, MmCtx}; use mm2_err_handle::common_errors::InternalError; use mm2_err_handle::prelude::*; From 21f67b485dc087ae05eced1ca79c05d9be80f670 Mon Sep 17 00:00:00 2001 From: laruh Date: Mon, 5 Feb 2024 19:19:12 +0700 Subject: [PATCH 13/13] avoid code nesting in `clear_nft_db`, remove `map_err` for `NumConversError` --- mm2src/coins/eth.rs | 27 ++++++++++----------------- mm2src/coins/nft.rs | 27 +++++++++++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 7ae2caa28e..a98a742b2a 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -902,12 +902,10 @@ pub async fn withdraw_erc1155(ctx: MmArc, withdraw_type: WithdrawErc1155) -> Wit let (eth_value, data, call_addr, fee_coin) = match eth_coin.coin_type { EthCoinType::Eth => { let function = ERC1155_CONTRACT.function("safeTransferFrom")?; - let token_id_u256 = U256::from_dec_str(token_id_str) - .map_err(|e| format!("{:?}", e)) - .map_to_mm(NumConversError::new)?; - let amount_u256 = U256::from_dec_str(&amount_dec.to_string()) - .map_err(|e| format!("{:?}", e)) - .map_to_mm(NumConversError::new)?; + let token_id_u256 = + U256::from_dec_str(token_id_str).map_to_mm(|e| NumConversError::new(format!("{:?}", e)))?; + let amount_u256 = + U256::from_dec_str(&amount_dec.to_string()).map_to_mm(|e| NumConversError::new(format!("{:?}", e)))?; let data = function.encode_input(&[ Token::Address(eth_coin.my_address), Token::Address(to_addr), @@ -993,8 +991,7 @@ pub async fn withdraw_erc721(ctx: MmArc, withdraw_type: WithdrawErc721) -> Withd EthCoinType::Eth => { let function = ERC721_CONTRACT.function("safeTransferFrom")?; let token_id_u256 = U256::from_dec_str(&withdraw_type.token_id.to_string()) - .map_err(|e| format!("{:?}", e)) - .map_to_mm(NumConversError::new)?; + .map_to_mm(|e| NumConversError::new(format!("{:?}", e)))?; let data = function.encode_input(&[ Token::Address(my_address), Token::Address(to_addr), @@ -4022,9 +4019,8 @@ impl EthCoin { let wallet_amount_uint = match self.coin_type { EthCoinType::Eth => { let function = ERC1155_CONTRACT.function("balanceOf")?; - let token_id_u256 = U256::from_dec_str(token_id) - .map_err(|e| format!("{:?}", e)) - .map_to_mm(NumConversError::new)?; + let token_id_u256 = + U256::from_dec_str(token_id).map_to_mm(|e| NumConversError::new(format!("{:?}", e)))?; let data = function.encode_input(&[Token::Address(self.my_address), Token::Uint(token_id_u256)])?; let result = self.call_request(token_addr, None, Some(data.into())).await?; let decoded = function.decode_output(&result.0)?; @@ -4050,9 +4046,8 @@ impl EthCoin { let owner_address = match self.coin_type { EthCoinType::Eth => { let function = ERC721_CONTRACT.function("ownerOf")?; - let token_id_u256 = U256::from_dec_str(token_id) - .map_err(|e| format!("{:?}", e)) - .map_to_mm(NumConversError::new)?; + let token_id_u256 = + U256::from_dec_str(token_id).map_to_mm(|e| NumConversError::new(format!("{:?}", e)))?; let data = function.encode_input(&[Token::Uint(token_id_u256)])?; let result = self.call_request(token_addr, None, Some(data.into())).await?; let decoded = function.decode_output(&result.0)?; @@ -5355,9 +5350,7 @@ pub fn wei_from_big_decimal(amount: &BigDecimal, decimals: u8) -> NumConversResu } else { amount.insert_str(amount.len(), &"0".repeat(decimals)); } - U256::from_dec_str(&amount) - .map_err(|e| format!("{:?}", e)) - .map_to_mm(NumConversError::new) + U256::from_dec_str(&amount).map_to_mm(|e| NumConversError::new(format!("{:?}", e))) } impl Transaction for SignedEthTx { diff --git a/mm2src/coins/nft.rs b/mm2src/coins/nft.rs index e243eab9c5..613603ebd2 100644 --- a/mm2src/coins/nft.rs +++ b/mm2src/coins/nft.rs @@ -22,7 +22,7 @@ use crate::nft::nft_structs::{build_nft_with_empty_meta, BuildNftFields, ClearNf NftTransferCommon, PhishingDomainReq, PhishingDomainRes, RefreshMetadataReq, SpamContractReq, SpamContractRes, TransferMeta, TransferStatus, UriMeta}; use crate::nft::storage::{NftListStorageOps, NftTransferHistoryStorageOps}; -use common::{log::error, parse_rfc3339_to_timestamp}; +use common::parse_rfc3339_to_timestamp; use crypto::StandardHDCoinAddress; use ethereum_types::{Address, H256}; use futures::compat::Future01CompatExt; @@ -1380,20 +1380,27 @@ pub async fn clear_nft_db(ctx: MmArc, req: ClearNftDbReq) -> MmResult<(), ClearN let storage = nft_ctx.lock_db().await?; storage.clear_all_nft_data().await?; storage.clear_all_history_data().await?; - } else if !req.chains.is_empty() { - let nft_ctx = NftCtx::from_ctx(&ctx).map_to_mm(ClearNftDbError::Internal)?; - let storage = nft_ctx.lock_db().await?; - for chain in req.chains.iter() { - if let Err(e) = clear_data_for_chain(&storage, chain).await { - error!("Failed to clear data for chain {}: {}", chain, e); - } - } - } else { + return Ok(()); + } + + if req.chains.is_empty() { return MmError::err(ClearNftDbError::InvalidRequest( "Nothing to clear was specified".to_string(), )); } + let nft_ctx = NftCtx::from_ctx(&ctx).map_to_mm(ClearNftDbError::Internal)?; + let storage = nft_ctx.lock_db().await?; + let mut errors = Vec::new(); + for chain in req.chains.iter() { + if let Err(e) = clear_data_for_chain(&storage, chain).await { + errors.push(e); + } + } + if !errors.is_empty() { + return MmError::err(ClearNftDbError::DbError(format!("{:?}", errors))); + } + Ok(()) }