Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(nft): nft abi in withdraw_nft RPC, clear_nft_db RPC #2039

Merged
merged 16 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from 14 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
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion mm2src/coins/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand Down
116 changes: 91 additions & 25 deletions mm2src/coins/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -107,10 +107,11 @@ 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;
use crate::nft::nft_errors::GetNftInfoError;
use crate::{PrivKeyPolicy, TransactionResult, WithdrawFrom};
use nonce::ParityNonce;

Expand Down Expand Up @@ -877,16 +878,12 @@ 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)?;
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?;
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();
let wallet_amount = eth_coin.erc1155_balance(token_addr, token_id_str).await?;

let amount_dec = if withdraw_type.max {
wallet_amount.clone()
} else {
Expand All @@ -905,7 +902,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())
Expand Down Expand Up @@ -959,7 +956,7 @@ pub async fn withdraw_erc1155(ctx: MmArc, withdraw_type: WithdrawErc1155) -> Wit
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,
Expand All @@ -979,8 +976,18 @@ 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)?;
let my_address = eth_coin.my_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();
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 => {
Expand All @@ -989,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),
])?;
Expand All @@ -1011,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?
Expand All @@ -1034,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,
Expand Down Expand Up @@ -4011,6 +4018,61 @@ impl EthCoin {
}
}

async fn erc1155_balance(&self, token_addr: Address, token_id: &str) -> MmResult<BigDecimal, BalanceError> {
let wallet_amount_uint = match self.coin_type {
EthCoinType::Eth => {
let function = ERC1155_CONTRACT.function("balanceOf")?;
borngraced marked this conversation as resolved.
Show resolved Hide resolved
let token_id_u256 = U256::from_dec_str(token_id)
.map_err(|e| format!("{:?}", e))
.map_to_mm(NumConversError::new)?;
shamardy marked this conversation as resolved.
Show resolved Hide resolved
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 = u256_to_big_decimal(wallet_amount_uint, self.decimals)?;
Ok(wallet_amount)
}

async fn erc721_owner(&self, token_addr: Address, token_id: &str) -> MmResult<Address, GetNftInfoError> {
let owner_address = match self.coin_type {
EthCoinType::Eth => {
let function = ERC721_CONTRACT.function("ownerOf")?;
borngraced marked this conversation as resolved.
Show resolved Hide resolved
let token_id_u256 = U256::from_dec_str(token_id)
.map_err(|e| format!("{:?}", e))
.map_to_mm(NumConversError::new)?;
shamardy marked this conversation as resolved.
Show resolved Hide resolved
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<dyn Future<Item = U256, Error = web3::Error> + 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())
Expand Down Expand Up @@ -5726,6 +5788,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),
Expand Down Expand Up @@ -5766,21 +5829,24 @@ 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.
shamardy marked this conversation as resolved.
Show resolved Hide resolved
#[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,
},
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,
Expand Down
2 changes: 1 addition & 1 deletion mm2src/coins/eth/v2_activation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
2 changes: 1 addition & 1 deletion mm2src/coins/hd_confirm_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
19 changes: 8 additions & 11 deletions mm2src/coins/lp_coins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -2402,11 +2402,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)]
Expand All @@ -2427,6 +2422,11 @@ pub enum WithdrawError {
},
#[display(fmt = "DB error {}", _0)]
DbError(String),
#[display(fmt = "My address is {}, while current Nft owner is {}", my_address, token_owner)]
MyAddressNotNftOwner {
my_address: String,
token_owner: String,
},
}

impl HttpStatusCode for WithdrawError {
Expand All @@ -2449,10 +2449,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,
Expand Down Expand Up @@ -2496,9 +2496,6 @@ impl From<TimeoutError> for WithdrawError {
impl From<GetValidEthWithdrawAddError> 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 }
},
Expand Down
Loading
Loading