From 0660e75d0772a2c92b8e438cff83176c36afe996 Mon Sep 17 00:00:00 2001 From: shamardy <39480341+shamardy@users.noreply.github.com> Date: Wed, 17 May 2023 19:49:09 +0300 Subject: [PATCH] fix: Minor fixes for v1.0.4-beta release (#1827) * lock SCP github action to v0.8.0 * use strict types for requests in nft_structs.rs * Remove unneeded comment in block_header_utxo_loop * use try_into with expect instead of type casting in now_sec_u32/now_sec_i64 * fix nft_tests * use Url methods to construct the moralis requests urls --------- Reviewed-by: ozkanonur , laruh --- .github/workflows/dev-build.yml | 16 ++-- .github/workflows/release-build.yml | 16 ++-- Cargo.lock | 2 + mm2src/coins/Cargo.toml | 1 + mm2src/coins/eth.rs | 3 +- mm2src/coins/nft.rs | 88 +++++++++++-------- mm2src/coins/nft/nft_structs.rs | 37 ++++---- mm2src/coins/nft/nft_tests.rs | 26 +++--- .../utxo/utxo_builder/utxo_arc_builder.rs | 2 - mm2src/common/common.rs | 13 ++- 10 files changed, 114 insertions(+), 90 deletions(-) diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml index 82818d2e95..d7a4c2f959 100644 --- a/.github/workflows/dev-build.yml +++ b/.github/workflows/dev-build.yml @@ -61,7 +61,7 @@ jobs: env: AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} if: ${{ env.AVAILABLE != '' }} - uses: garygrossgarten/github-action-scp@release + uses: garygrossgarten/github-action-scp@v0.8.0 with: host: ${{ secrets.FILE_SERVER_HOST }} username: ${{ secrets.FILE_SERVER_USERNAME }} @@ -120,7 +120,7 @@ jobs: env: AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} if: ${{ env.AVAILABLE != '' }} - uses: garygrossgarten/github-action-scp@release + uses: garygrossgarten/github-action-scp@v0.8.0 with: host: ${{ secrets.FILE_SERVER_HOST }} username: ${{ secrets.FILE_SERVER_USERNAME }} @@ -169,7 +169,7 @@ jobs: env: AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} if: ${{ env.AVAILABLE != '' }} - uses: garygrossgarten/github-action-scp@release + uses: garygrossgarten/github-action-scp@v0.8.0 with: host: ${{ secrets.FILE_SERVER_HOST }} username: ${{ secrets.FILE_SERVER_USERNAME }} @@ -217,7 +217,7 @@ jobs: env: AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} if: ${{ env.AVAILABLE != '' }} - uses: garygrossgarten/github-action-scp@release + uses: garygrossgarten/github-action-scp@v0.8.0 with: host: ${{ secrets.FILE_SERVER_HOST }} username: ${{ secrets.FILE_SERVER_USERNAME }} @@ -277,7 +277,7 @@ jobs: env: AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} if: ${{ env.AVAILABLE != '' }} - uses: garygrossgarten/github-action-scp@release + uses: garygrossgarten/github-action-scp@v0.8.0 with: host: ${{ secrets.FILE_SERVER_HOST }} username: ${{ secrets.FILE_SERVER_USERNAME }} @@ -326,7 +326,7 @@ jobs: env: AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} if: ${{ env.AVAILABLE != '' }} - uses: garygrossgarten/github-action-scp@release + uses: garygrossgarten/github-action-scp@v0.8.0 with: host: ${{ secrets.FILE_SERVER_HOST }} username: ${{ secrets.FILE_SERVER_USERNAME }} @@ -389,7 +389,7 @@ jobs: env: AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} if: ${{ env.AVAILABLE != '' }} - uses: garygrossgarten/github-action-scp@release + uses: garygrossgarten/github-action-scp@v0.8.0 with: host: ${{ secrets.FILE_SERVER_HOST }} username: ${{ secrets.FILE_SERVER_USERNAME }} @@ -452,7 +452,7 @@ jobs: env: AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} if: ${{ env.AVAILABLE != '' }} - uses: garygrossgarten/github-action-scp@release + uses: garygrossgarten/github-action-scp@v0.8.0 with: host: ${{ secrets.FILE_SERVER_HOST }} username: ${{ secrets.FILE_SERVER_USERNAME }} diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index c97b4d2f1c..381b51fc08 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -55,7 +55,7 @@ jobs: mv $NAME ./$BRANCH_NAME/ - name: Upload output - uses: garygrossgarten/github-action-scp@release + uses: garygrossgarten/github-action-scp@v0.8.0 with: host: ${{ secrets.FILE_SERVER_HOST }} username: ${{ secrets.FILE_SERVER_USERNAME }} @@ -106,7 +106,7 @@ jobs: mv $NAME ./$BRANCH_NAME/ - name: Upload output - uses: garygrossgarten/github-action-scp@release + uses: garygrossgarten/github-action-scp@v0.8.0 with: host: ${{ secrets.FILE_SERVER_HOST }} username: ${{ secrets.FILE_SERVER_USERNAME }} @@ -149,7 +149,7 @@ jobs: mv $NAME ./$Env:BRANCH_NAME/ - name: Upload output - uses: garygrossgarten/github-action-scp@release + uses: garygrossgarten/github-action-scp@v0.8.0 with: host: ${{ secrets.FILE_SERVER_HOST }} username: ${{ secrets.FILE_SERVER_USERNAME }} @@ -191,7 +191,7 @@ jobs: mv $NAME ./$BRANCH_NAME/ - name: Upload output - uses: garygrossgarten/github-action-scp@release + uses: garygrossgarten/github-action-scp@v0.8.0 with: host: ${{ secrets.FILE_SERVER_HOST }} username: ${{ secrets.FILE_SERVER_USERNAME }} @@ -245,7 +245,7 @@ jobs: mv $NAME ./$BRANCH_NAME/ - name: Upload output - uses: garygrossgarten/github-action-scp@release + uses: garygrossgarten/github-action-scp@v0.8.0 with: host: ${{ secrets.FILE_SERVER_HOST }} username: ${{ secrets.FILE_SERVER_USERNAME }} @@ -288,7 +288,7 @@ jobs: mv $NAME ./$BRANCH_NAME/ - name: Upload output - uses: garygrossgarten/github-action-scp@release + uses: garygrossgarten/github-action-scp@v0.8.0 with: host: ${{ secrets.FILE_SERVER_HOST }} username: ${{ secrets.FILE_SERVER_USERNAME }} @@ -345,7 +345,7 @@ jobs: mv $NAME ./$BRANCH_NAME/ - name: Upload output - uses: garygrossgarten/github-action-scp@release + uses: garygrossgarten/github-action-scp@v0.8.0 with: host: ${{ secrets.FILE_SERVER_HOST }} username: ${{ secrets.FILE_SERVER_USERNAME }} @@ -402,7 +402,7 @@ jobs: mv $NAME ./$BRANCH_NAME/ - name: Upload output - uses: garygrossgarten/github-action-scp@release + uses: garygrossgarten/github-action-scp@v0.8.0 with: host: ${{ secrets.FILE_SERVER_HOST }} username: ${{ secrets.FILE_SERVER_USERNAME }} diff --git a/Cargo.lock b/Cargo.lock index 05b11e68df..8bc475a346 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1116,6 +1116,7 @@ dependencies = [ "tokio-rustls", "tonic", "tonic-build", + "url", "utxo_signer", "uuid 1.2.2", "wasm-bindgen", @@ -8483,6 +8484,7 @@ dependencies = [ "idna", "matches", "percent-encoding", + "serde", ] [[package]] diff --git a/mm2src/coins/Cargo.toml b/mm2src/coins/Cargo.toml index 42dc962670..4fd7a4e69b 100644 --- a/mm2src/coins/Cargo.toml +++ b/mm2src/coins/Cargo.toml @@ -95,6 +95,7 @@ utxo_signer = { path = "utxo_signer" } # using the same version as cosmrs tendermint-rpc = { version = "=0.23.7", default-features = false } tiny-bip39 = "0.8.0" +url = { version = "2.2.2", features = ["serde"] } uuid = { version = "1.2.2", features = ["fast-rng", "serde", "v4"] } # One of web3 dependencies is the old `tokio-uds 0.1.7` which fails cross-compiling to ARM. # We don't need the default web3 features at all since we added our own web3 transport using shared HYPER instance. diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index c4b29a7ff3..9a39e47bda 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -67,6 +67,7 @@ use std::ops::Deref; use std::str::FromStr; use std::sync::atomic::{AtomicU64, Ordering as AtomicOrdering}; use std::sync::{Arc, Mutex}; +use url::Url; use web3::types::{Action as TraceAction, BlockId, BlockNumber, Bytes, CallRequest, FilterBuilder, Log, Trace, TraceFilterBuilder, Transaction as Web3Transaction, TransactionId, U64}; use web3::{self, Web3}; @@ -874,7 +875,7 @@ async fn withdraw_impl(coin: EthCoin, req: WithdrawRequest) -> WithdrawResult { /// `withdraw_erc1155` function returns details of `ERC-1155` transaction including tx hex, /// which should be sent to`send_raw_transaction` RPC to broadcast the transaction. -pub async fn withdraw_erc1155(ctx: MmArc, withdraw_type: WithdrawErc1155, url: String) -> WithdrawNftResult { +pub async fn withdraw_erc1155(ctx: MmArc, withdraw_type: WithdrawErc1155, url: Url) -> 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)?; diff --git a/mm2src/coins/nft.rs b/mm2src/coins/nft.rs index e5e7ec96cd..e6482bd1c8 100644 --- a/mm2src/coins/nft.rs +++ b/mm2src/coins/nft.rs @@ -7,7 +7,7 @@ pub(crate) mod nft_structs; use crate::WithdrawError; use nft_errors::GetNftInfoError; -use nft_structs::{Chain, ConvertChain, Nft, NftList, NftListReq, NftMetadataReq, NftTransferHistory, +use nft_structs::{ConvertChain, Nft, NftList, NftListReq, NftMetadataReq, NftTransferHistory, NftTransferHistoryWrapper, NftTransfersReq, NftWrapper, NftsTransferHistoryList, TransactionNftDetails, WithdrawNftReq}; @@ -15,14 +15,17 @@ use crate::eth::{get_eth_address, withdraw_erc1155, withdraw_erc721}; use crate::nft::nft_structs::WithdrawNftType; use common::APPLICATION_JSON; use http::header::ACCEPT; +use mm2_err_handle::map_to_mm::MapToMmResult; use mm2_number::BigDecimal; use serde_json::Value as Json; -const MORALIS_API_ENDPOINT: &str = "/api/v2/"; -/// query parameter for moralis request: The format of the token ID -const FORMAT_DECIMAL_MORALIS: &str = "format=decimal"; -/// query parameter for moralis request: The transfer direction -const DIRECTION_BOTH_MORALIS: &str = "direction=both"; +const MORALIS_API_ENDPOINT: &str = "api/v2"; +/// query parameters for moralis request: The format of the token ID +const MORALIS_FORMAT_QUERY_NAME: &str = "format"; +const MORALIS_FORMAT_QUERY_VALUE: &str = "decimal"; +/// query parameters for moralis request: The transfer direction +const MORALIS_DIRECTION_QUERY_NAME: &str = "direction"; +const MORALIS_DIRECTION_QUERY_VALUE: &str = "both"; pub type WithdrawNftResult = Result>; @@ -30,12 +33,20 @@ pub type WithdrawNftResult = Result MmResult { let mut res_list = Vec::new(); for chain in req.chains { - let (coin_str, chain_str) = chain.to_ticker_chain(); - let my_address = get_eth_address(&ctx, &coin_str).await?; - let req_url = &req.url; - let wallet_address = my_address.wallet_address; - let uri_without_cursor = - format!("{req_url}{MORALIS_API_ENDPOINT}{wallet_address}/nft?chain={chain_str}&{FORMAT_DECIMAL_MORALIS}"); + let my_address = get_eth_address(&ctx, &chain.to_ticker()).await?; + + let mut uri_without_cursor = req.url.clone(); + uri_without_cursor.set_path(MORALIS_API_ENDPOINT); + uri_without_cursor + .path_segments_mut() + .map_to_mm(|_| GetNftInfoError::Internal("Invalid URI".to_string()))? + .push(&my_address.wallet_address) + .push("nft"); + uri_without_cursor + .query_pairs_mut() + .append_pair("chain", &chain.to_string()) + .append_pair(MORALIS_FORMAT_QUERY_NAME, MORALIS_FORMAT_QUERY_VALUE); + drop_mutability!(uri_without_cursor); // The cursor returned in the previous response (used for getting the next page). let mut cursor = String::new(); @@ -92,19 +103,18 @@ pub async fn get_nft_list(ctx: MmArc, req: NftListReq) -> MmResult MmResult { - let chain_str = match req.chain { - Chain::Avalanche => "avalanche", - Chain::Bsc => "bsc", - Chain::Eth => "eth", - Chain::Fantom => "fantom", - Chain::Polygon => "polygon", - }; - let req_url = &req.url; - let token_address = &req.token_address; - let token_id = &req.token_id; - let uri = format!( - "{req_url}{MORALIS_API_ENDPOINT}nft/{token_address}/{token_id}?chain={chain_str}&{FORMAT_DECIMAL_MORALIS}" - ); + let mut uri = req.url; + uri.set_path(MORALIS_API_ENDPOINT); + uri.path_segments_mut() + .map_to_mm(|_| GetNftInfoError::Internal("Invalid URI".to_string()))? + .push("nft") + .push(&format!("{:#02x}", &req.token_address)) + .push(&req.token_id.to_string()); + uri.query_pairs_mut() + .append_pair("chain", &req.chain.to_string()) + .append_pair(MORALIS_FORMAT_QUERY_NAME, MORALIS_FORMAT_QUERY_VALUE); + drop_mutability!(uri); + let response = send_moralis_request(uri.as_str()).await?; let nft_wrapper: NftWrapper = serde_json::from_str(&response.to_string())?; let nft_metadata = Nft { @@ -135,20 +145,22 @@ pub async fn get_nft_transfers(ctx: MmArc, req: NftTransfersReq) -> MmResult ("AVAX", "avalanche"), - Chain::Bsc => ("BNB", "bsc"), - Chain::Eth => ("ETH", "eth"), - Chain::Fantom => ("FTM", "fantom"), - Chain::Polygon => ("MATIC", "polygon"), - }; - let my_address = get_eth_address(&ctx, coin_str).await?; - let req_url = &req.url; - let wallet_address = my_address.wallet_address; - let uri_without_cursor = format!( - "{req_url}{MORALIS_API_ENDPOINT}{wallet_address}/nft/transfers?chain={chain_str}&{FORMAT_DECIMAL_MORALIS}&{DIRECTION_BOTH_MORALIS}", + let my_address = get_eth_address(&ctx, &chain.to_ticker()).await?; - ); + let mut uri_without_cursor = req.url.clone(); + uri_without_cursor.set_path(MORALIS_API_ENDPOINT); + uri_without_cursor + .path_segments_mut() + .map_to_mm(|_| GetNftInfoError::Internal("Invalid URI".to_string()))? + .push(&my_address.wallet_address) + .push("nft") + .push("transfers"); + uri_without_cursor + .query_pairs_mut() + .append_pair("chain", &chain.to_string()) + .append_pair(MORALIS_FORMAT_QUERY_NAME, MORALIS_FORMAT_QUERY_VALUE) + .append_pair(MORALIS_DIRECTION_QUERY_NAME, MORALIS_DIRECTION_QUERY_VALUE); + drop_mutability!(uri_without_cursor); // The cursor returned in the previous response (used for getting the next page). let mut cursor = String::new(); diff --git a/mm2src/coins/nft/nft_structs.rs b/mm2src/coins/nft/nft_structs.rs index 435cc22b7e..f50ac7f379 100644 --- a/mm2src/coins/nft/nft_structs.rs +++ b/mm2src/coins/nft/nft_structs.rs @@ -1,21 +1,24 @@ use crate::{TransactionType, TxFeeDetails, WithdrawFee}; +use ethereum_types::Address; use mm2_number::BigDecimal; use rpc::v1::types::Bytes as BytesJson; use serde::Deserialize; +use std::fmt; use std::str::FromStr; +use url::Url; #[derive(Debug, Deserialize)] pub struct NftListReq { pub(crate) chains: Vec, - pub(crate) url: String, + pub(crate) url: Url, } #[derive(Debug, Deserialize)] pub struct NftMetadataReq { - pub(crate) token_address: String, + pub(crate) token_address: Address, pub(crate) token_id: BigDecimal, pub(crate) chain: Chain, - pub(crate) url: String, + pub(crate) url: Url, } #[derive(Clone, Copy, Debug, Deserialize, Serialize)] @@ -28,10 +31,20 @@ pub(crate) enum Chain { Polygon, } +impl fmt::Display for Chain { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self { + Chain::Avalanche => write!(f, "avalanche"), + Chain::Bsc => write!(f, "bsc"), + Chain::Eth => write!(f, "eth"), + Chain::Fantom => write!(f, "fantom"), + Chain::Polygon => write!(f, "polygon"), + } + } +} + pub(crate) trait ConvertChain { fn to_ticker(&self) -> String; - - fn to_ticker_chain(&self) -> (String, String); } impl ConvertChain for Chain { @@ -44,16 +57,6 @@ impl ConvertChain for Chain { Chain::Polygon => "MATIC".to_owned(), } } - - fn to_ticker_chain(&self) -> (String, String) { - match self { - Chain::Avalanche => ("AVAX".to_owned(), "avalanche".to_owned()), - Chain::Bsc => ("BNB".to_owned(), "bsc".to_owned()), - Chain::Eth => ("ETH".to_owned(), "eth".to_owned()), - Chain::Fantom => ("FTM".to_owned(), "fantom".to_owned()), - Chain::Polygon => ("MATIC".to_owned(), "polygon".to_owned()), - } - } } #[derive(Debug, Display)] @@ -175,7 +178,7 @@ pub struct WithdrawErc721 { #[derive(Clone, Deserialize)] pub struct WithdrawNftReq { - pub(crate) url: String, + pub(crate) url: Url, pub(crate) withdraw_type: WithdrawNftType, } @@ -217,7 +220,7 @@ pub struct TransactionNftDetails { #[derive(Debug, Deserialize)] pub struct NftTransfersReq { pub(crate) chains: Vec, - pub(crate) url: String, + pub(crate) url: Url, } #[derive(Debug, Serialize)] diff --git a/mm2src/coins/nft/nft_tests.rs b/mm2src/coins/nft/nft_tests.rs index 304511e19d..6c6640cb99 100644 --- a/mm2src/coins/nft/nft_tests.rs +++ b/mm2src/coins/nft/nft_tests.rs @@ -17,19 +17,18 @@ mod native_tests { assert_eq!(2, nfts_list.len()); for nft_json in nfts_list { let nft_wrapper: NftWrapper = serde_json::from_str(&nft_json.to_string()).unwrap(); - assert_eq!(TEST_WALLET_ADDR_EVM, nft_wrapper.owner_of) + assert_eq!(TEST_WALLET_ADDR_EVM, nft_wrapper.owner_of); } } #[test] fn test_moralis_nft_transfer_history() { let response = block_on(send_moralis_request(NFT_HISTORY_URL_TEST)).unwrap(); - let transfer_list = response["result"].as_array().unwrap(); - assert_eq!(2, transfer_list.len()); - for transfer in transfer_list { - let transfer_wrapper: NftTransferHistoryWrapper = serde_json::from_str(&transfer.to_string()).unwrap(); - assert_eq!(TEST_WALLET_ADDR_EVM, transfer_wrapper.to_address); - } + let mut transfer_list = response["result"].as_array().unwrap().clone(); + assert_eq!(4, transfer_list.len()); + let last_tx = transfer_list.remove(0); + let transfer_wrapper: NftTransferHistoryWrapper = serde_json::from_str(&last_tx.to_string()).unwrap(); + assert_eq!(TEST_WALLET_ADDR_EVM, transfer_wrapper.from_address); } #[test] @@ -56,19 +55,18 @@ mod wasm_tests { assert_eq!(2, nfts_list.len()); for nft_json in nfts_list { let nft_wrapper: NftWrapper = serde_json::from_str(&nft_json.to_string()).unwrap(); - assert_eq!(TEST_WALLET_ADDR_EVM, nft_wrapper.owner_of) + assert_eq!(TEST_WALLET_ADDR_EVM, nft_wrapper.owner_of); } } #[wasm_bindgen_test] async fn test_moralis_nft_transfer_history() { let response = send_moralis_request(NFT_HISTORY_URL_TEST).await.unwrap(); - let transfer_list = response["result"].as_array().unwrap(); - assert_eq!(2, transfer_list.len()); - for transfer in transfer_list { - let transfer_wrapper: NftTransferHistoryWrapper = serde_json::from_str(&transfer.to_string()).unwrap(); - assert_eq!(TEST_WALLET_ADDR_EVM, transfer_wrapper.to_address); - } + let mut transfer_list = response["result"].as_array().unwrap().clone(); + assert_eq!(4, transfer_list.len()); + let last_tx = transfer_list.remove(0); + let transfer_wrapper: NftTransferHistoryWrapper = serde_json::from_str(&last_tx.to_string()).unwrap(); + assert_eq!(TEST_WALLET_ADDR_EVM, transfer_wrapper.from_address); } #[wasm_bindgen_test] diff --git a/mm2src/coins/utxo/utxo_builder/utxo_arc_builder.rs b/mm2src/coins/utxo/utxo_builder/utxo_arc_builder.rs index e9927d77fb..b97c1aa94e 100644 --- a/mm2src/coins/utxo/utxo_builder/utxo_arc_builder.rs +++ b/mm2src/coins/utxo/utxo_builder/utxo_arc_builder.rs @@ -319,8 +319,6 @@ pub(crate) async fn block_header_utxo_loop( }, }; - // More than `chunk_size` blocks could have appeared since the last `get_block_count` RPC. - // So reset `to_block_height` if only `from_block_height + chunk_size > actual_block_count`. if retrieve_to > block_count { retrieve_to = block_count; } diff --git a/mm2src/common/common.rs b/mm2src/common/common.rs index 064ed6428d..8670eb3d87 100644 --- a/mm2src/common/common.rs +++ b/mm2src/common/common.rs @@ -144,6 +144,7 @@ use serde::{de, ser}; use serde_json::{self as json, Value as Json}; use sha2::{Digest, Sha256}; use std::alloc::Allocator; +use std::convert::TryInto; use std::fmt::Write as FmtWrite; use std::fs::File; use std::future::Future as Future03; @@ -667,9 +668,17 @@ pub fn wait_until_ms(milliseconds: u64) -> u64 { now_ms() + milliseconds } pub fn now_sec() -> u64 { now_ms() / 1000 } -pub fn now_sec_u32() -> u32 { (now_ms() / 1000) as u32 } +pub fn now_sec_u32() -> u32 { + (now_ms() / 1000) + .try_into() + .expect("current time in seconds should fit into u32 until 2106!") +} -pub fn now_sec_i64() -> i64 { (now_ms() / 1000) as i64 } +pub fn now_sec_i64() -> i64 { + (now_ms() / 1000) + .try_into() + .expect("current time in seconds should fit into i64 for the foreseeable future!") +} #[cfg(not(target_arch = "wasm32"))] pub fn temp_dir() -> PathBuf { env::temp_dir() }