Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
## v1.0.4-beta - 2023-05-12

**Features:**
- NFT integration [#900](https://github.com/KomodoPlatform/atomicDEX-API/issues/900)
- Proxy support was added [#1775](https://github.com/KomodoPlatform/atomicDEX-API/pull/1775)

**Enhancements/Fixes:**
- Some enhancements were done for `enable_bch_with_tokens`,`enable_eth_with_tokens`,`enable_tendermint_with_assets` RPCs in [#1762](https://github.com/KomodoPlatform/atomicDEX-API/pull/1762)
Expand Down
1 change: 0 additions & 1 deletion mm2src/coins/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ enable-solana = [
"dep:spl-token",
"dep:spl-associated-token-account"
]
enable-nft-integration = []
default = []

[lib]
Expand Down
80 changes: 48 additions & 32 deletions mm2src/coins/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
// Copyright © 2022 AtomicDEX. All rights reserved.
//
use super::eth::Action::{Call, Create};
#[cfg(feature = "enable-nft-integration")]
use crate::nft::nft_structs::{ContractType, ConvertChain, NftListReq, TransactionNftDetails, WithdrawErc1155,
WithdrawErc721};
use async_trait::async_trait;
Expand Down Expand Up @@ -100,9 +99,7 @@ pub use rlp;
mod web3_transport;

#[path = "eth/v2_activation.rs"] pub mod v2_activation;
#[cfg(feature = "enable-nft-integration")]
use crate::nft::{find_wallet_amount, WithdrawNftResult};
#[cfg(feature = "enable-nft-integration")]
use crate::{lp_coinfind_or_err, MmCoinEnum, TransactionType};
use v2_activation::{build_address_and_priv_key_policy, EthActivationV2Error};

Expand Down Expand Up @@ -867,31 +864,38 @@ async fn withdraw_impl(coin: EthCoin, req: WithdrawRequest) -> WithdrawResult {
})
}

#[cfg(feature = "enable-nft-integration")]
/// `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, req: WithdrawErc1155) -> WithdrawNftResult {
let coin = lp_coinfind_or_err(&ctx, &req.chain.to_ticker()).await?;
let (to_addr, token_addr, eth_coin) = get_valid_nft_add_to_withdraw(coin, &req.to, &req.token_address)?;
pub async fn withdraw_erc1155(ctx: MmArc, withdraw_type: WithdrawErc1155, url: String) -> 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)?;
let my_address = eth_coin.my_address()?;

// todo check amount in nft cache, instead of sending new moralis req
// dont use `get_nft_metadata` for erc1155, it can return info related to other owner.
let nft_req = NftListReq {
chains: vec![req.chain],
chains: vec![withdraw_type.chain],
url,
};
let wallet_amount = find_wallet_amount(ctx, nft_req, req.token_address.clone(), req.token_id.clone()).await?;

let amount_dec = if req.max {
let wallet_amount = find_wallet_amount(
ctx,
nft_req,
withdraw_type.token_address.clone(),
withdraw_type.token_id.clone(),
)
.await?;

let amount_dec = if withdraw_type.max {
wallet_amount.clone()
} else {
req.amount.unwrap_or_else(|| 1.into())
withdraw_type.amount.unwrap_or_else(|| 1.into())
};

if amount_dec > wallet_amount {
return MmError::err(WithdrawError::NotEnoughNftsAmount {
token_address: req.token_address,
token_id: req.token_id.to_string(),
token_address: withdraw_type.token_address,
token_id: withdraw_type.token_id.to_string(),
available: wallet_amount,
required: amount_dec,
});
Expand All @@ -900,7 +904,7 @@ pub async fn withdraw_erc1155(ctx: MmArc, req: WithdrawErc1155) -> WithdrawNftRe
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(&req.token_id.to_string())
let token_id_u256 = U256::from_dec_str(&withdraw_type.token_id.to_string())
.map_err(|e| format!("{:?}", e))
.map_to_mm(NumConversError::new)?;
let amount_u256 = U256::from_dec_str(&amount_dec.to_string())
Expand All @@ -921,8 +925,15 @@ pub async fn withdraw_erc1155(ctx: MmArc, req: WithdrawErc1155) -> WithdrawNftRe
))
},
};
let (gas, gas_price) =
get_eth_gas_details(&eth_coin, req.fee, eth_value, data.clone().into(), call_addr, false).await?;
let (gas, gas_price) = get_eth_gas_details(
&eth_coin,
withdraw_type.fee,
eth_value,
data.clone().into(),
call_addr,
false,
)
.await?;
let _nonce_lock = eth_coin.nonce_lock.lock().await;
let (nonce, _) = get_addr_nonce(eth_coin.my_address, eth_coin.web3_instances.clone())
.compat()
Expand All @@ -948,10 +959,10 @@ pub async fn withdraw_erc1155(ctx: MmArc, req: WithdrawErc1155) -> WithdrawNftRe
tx_hex: BytesJson::from(signed_bytes.to_vec()),
tx_hash: format!("{:02x}", signed.tx_hash()),
from: vec![my_address],
to: vec![req.to],
to: vec![withdraw_type.to],
contract_type: ContractType::Erc1155,
token_address: req.token_address,
token_id: req.token_id,
token_address: withdraw_type.token_address,
token_id: withdraw_type.token_id,
amount: amount_dec,
fee_details: Some(fee_details.into()),
coin: eth_coin.ticker.clone(),
Expand All @@ -962,18 +973,18 @@ pub async fn withdraw_erc1155(ctx: MmArc, req: WithdrawErc1155) -> WithdrawNftRe
})
}

#[cfg(feature = "enable-nft-integration")]
/// `withdraw_erc721` function returns details of `ERC-721` transaction including tx hex,
/// which should be sent to`send_raw_transaction` RPC to broadcast the transaction.
pub async fn withdraw_erc721(ctx: MmArc, req: WithdrawErc721) -> WithdrawNftResult {
let coin = lp_coinfind_or_err(&ctx, &req.chain.to_ticker()).await?;
let (to_addr, token_addr, eth_coin) = get_valid_nft_add_to_withdraw(coin, &req.to, &req.token_address)?;
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)?;
let my_address = eth_coin.my_address()?;

let (eth_value, data, call_addr, fee_coin) = match eth_coin.coin_type {
EthCoinType::Eth => {
let function = ERC721_CONTRACT.function("safeTransferFrom")?;
let token_id_u256 = U256::from_dec_str(&req.token_id.to_string())
let token_id_u256 = U256::from_dec_str(&withdraw_type.token_id.to_string())
.map_err(|e| format!("{:?}", e))
.map_to_mm(NumConversError::new)?;
let data = function.encode_input(&[
Expand All @@ -989,8 +1000,15 @@ pub async fn withdraw_erc721(ctx: MmArc, req: WithdrawErc721) -> WithdrawNftResu
))
},
};
let (gas, gas_price) =
get_eth_gas_details(&eth_coin, req.fee, eth_value, data.clone().into(), call_addr, false).await?;
let (gas, gas_price) = get_eth_gas_details(
&eth_coin,
withdraw_type.fee,
eth_value,
data.clone().into(),
call_addr,
false,
)
.await?;
let _nonce_lock = eth_coin.nonce_lock.lock().await;
let (nonce, _) = get_addr_nonce(eth_coin.my_address, eth_coin.web3_instances.clone())
.compat()
Expand All @@ -1016,10 +1034,10 @@ pub async fn withdraw_erc721(ctx: MmArc, req: WithdrawErc721) -> WithdrawNftResu
tx_hex: BytesJson::from(signed_bytes.to_vec()),
tx_hash: format!("{:02x}", signed.tx_hash()),
from: vec![my_address],
to: vec![req.to],
to: vec![withdraw_type.to],
contract_type: ContractType::Erc721,
token_address: req.token_address,
token_id: req.token_id,
token_address: withdraw_type.token_address,
token_id: withdraw_type.token_id,
amount: 1.into(),
fee_details: Some(fee_details.into()),
coin: eth_coin.ticker.clone(),
Expand Down Expand Up @@ -5136,7 +5154,6 @@ pub async fn get_eth_address(ctx: &MmArc, ticker: &str) -> MmResult<MyWalletAddr
})
}

#[cfg(feature = "enable-nft-integration")]
#[derive(Display)]
pub enum GetValidEthWithdrawAddError {
#[display(fmt = "My address {} and from address {} mismatch", my_address, from)]
Expand All @@ -5151,7 +5168,6 @@ pub enum GetValidEthWithdrawAddError {
InvalidAddress(String),
}

#[cfg(feature = "enable-nft-integration")]
fn get_valid_nft_add_to_withdraw(
coin_enum: MmCoinEnum,
to: &str,
Expand Down
54 changes: 30 additions & 24 deletions mm2src/coins/lp_coins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,6 @@ use coin_errors::{MyAddressError, ValidatePaymentError, ValidatePaymentFut};
pub mod coins_tests;

pub mod eth;
#[cfg(feature = "enable-nft-integration")]
use eth::GetValidEthWithdrawAddError;
use eth::{eth_coin_from_conf_and_request, get_eth_address, EthCoin, EthGasDetailsErr, EthTxFeeDetails,
GetEthAddressError, SignedEthTx};
Expand Down Expand Up @@ -281,8 +280,7 @@ use utxo::utxo_standard::{utxo_standard_coin_with_policy, UtxoStandardCoin};
use utxo::UtxoActivationParams;
use utxo::{BlockchainNetwork, GenerateTxError, UtxoFeeDetails, UtxoTx};

#[cfg(feature = "enable-nft-integration")] pub mod nft;
#[cfg(feature = "enable-nft-integration")]
pub mod nft;
use nft::nft_errors::GetNftInfoError;

#[cfg(not(target_arch = "wasm32"))] pub mod z_coin;
Expand Down Expand Up @@ -1782,7 +1780,9 @@ pub enum WithdrawError {
fmt = "'{}' coin doesn't support 'init_withdraw' yet. Consider using 'withdraw' request instead",
coin
)]
CoinDoesntSupportInitWithdraw { coin: String },
CoinDoesntSupportInitWithdraw {
coin: String,
},
#[display(
fmt = "Not enough {} to withdraw: available {}, required at least {}",
coin,
Expand All @@ -1808,15 +1808,20 @@ pub enum WithdrawError {
#[display(fmt = "Balance is zero")]
ZeroBalanceToWithdrawMax,
#[display(fmt = "The amount {} is too small, required at least {}", amount, threshold)]
AmountTooLow { amount: BigDecimal, threshold: BigDecimal },
AmountTooLow {
amount: BigDecimal,
threshold: BigDecimal,
},
#[display(fmt = "Invalid address: {}", _0)]
InvalidAddress(String),
#[display(fmt = "Invalid fee policy: {}", _0)]
InvalidFeePolicy(String),
#[display(fmt = "Invalid memo field: {}", _0)]
InvalidMemo(String),
#[display(fmt = "No such coin {}", coin)]
NoSuchCoin { coin: String },
NoSuchCoin {
coin: String,
},
Comment thread
onur-ozkan marked this conversation as resolved.
#[from_trait(WithTimeout::timeout)]
#[display(fmt = "Withdraw timed out {:?}", _0)]
Timeout(Duration),
Expand All @@ -1825,9 +1830,13 @@ pub enum WithdrawError {
#[display(fmt = "Unexpected 'from' address: {}", _0)]
UnexpectedFromAddress(String),
#[display(fmt = "Unknown '{}' account", account_id)]
UnknownAccount { account_id: u32 },
UnknownAccount {
account_id: u32,
},
#[display(fmt = "RPC 'task' is awaiting '{}' user action", expected)]
UnexpectedUserAction { expected: String },
UnexpectedUserAction {
expected: String,
},
#[from_trait(WithHwRpcError::hw_rpc_error)]
HwError(HwRpcError),
#[cfg(target_arch = "wasm32")]
Expand All @@ -1838,20 +1847,20 @@ pub enum WithdrawError {
#[from_stringify("NumConversError", "UnexpectedDerivationMethod", "PrivKeyPolicyNotAllowed")]
#[display(fmt = "Internal error: {}", _0)]
InternalError(String),
#[cfg(feature = "enable-nft-integration")]
#[display(fmt = "{} coin doesn't support NFT withdrawing", coin)]
CoinDoesntSupportNftWithdraw { coin: String },
#[cfg(feature = "enable-nft-integration")]
CoinDoesntSupportNftWithdraw {
coin: String,
},
#[display(fmt = "My address {} and from address {} mismatch", my_address, from)]
AddressMismatchError { my_address: String, from: String },
#[cfg(feature = "enable-nft-integration")]
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)]
ActionNotAllowed(String),
#[cfg(feature = "enable-nft-integration")]
GetNftInfoError(GetNftInfoError),
#[cfg(feature = "enable-nft-integration")]
#[display(
fmt = "Not enough NFTs amount with token_address: {} and token_id {}. Available {}, required {}",
token_address,
Expand All @@ -1867,7 +1876,6 @@ pub enum WithdrawError {
},
}

#[cfg(feature = "enable-nft-integration")]
impl From<GetNftInfoError> for WithdrawError {
fn from(e: GetNftInfoError) -> Self { WithdrawError::GetNftInfoError(e) }
}
Expand All @@ -1889,17 +1897,16 @@ impl HttpStatusCode for WithdrawError {
| WithdrawError::UnexpectedFromAddress(_)
| WithdrawError::UnknownAccount { .. }
| WithdrawError::UnexpectedUserAction { .. }
| WithdrawError::ActionNotAllowed(_) => StatusCode::BAD_REQUEST,
WithdrawError::HwError(_) => StatusCode::GONE,
#[cfg(target_arch = "wasm32")]
WithdrawError::BroadcastExpected(_) => StatusCode::BAD_REQUEST,
WithdrawError::Transport(_) | WithdrawError::InternalError(_) => StatusCode::INTERNAL_SERVER_ERROR,
#[cfg(feature = "enable-nft-integration")]
WithdrawError::GetNftInfoError(_)
| WithdrawError::ActionNotAllowed(_)
| WithdrawError::GetNftInfoError(_)
| WithdrawError::AddressMismatchError { .. }
| WithdrawError::ContractTypeDoesntSupportNftWithdrawing(_)
| WithdrawError::CoinDoesntSupportNftWithdraw { .. }
| WithdrawError::NotEnoughNftsAmount { .. } => StatusCode::BAD_REQUEST,
WithdrawError::HwError(_) => StatusCode::GONE,
#[cfg(target_arch = "wasm32")]
WithdrawError::BroadcastExpected(_) => StatusCode::BAD_REQUEST,
WithdrawError::Transport(_) | WithdrawError::InternalError(_) => StatusCode::INTERNAL_SERVER_ERROR,
}
}
}
Expand Down Expand Up @@ -1934,7 +1941,6 @@ impl From<TimeoutError> for WithdrawError {
fn from(e: TimeoutError) -> Self { WithdrawError::Timeout(e.duration) }
}

#[cfg(feature = "enable-nft-integration")]
impl From<GetValidEthWithdrawAddError> for WithdrawError {
fn from(e: GetValidEthWithdrawAddError) -> Self {
match e {
Expand Down
Loading