diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index d96c98ccca..c825efd2c6 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -1,5 +1,5 @@ /****************************************************************************** - * Copyright © 2022 Atomic Private Limited and its contributors * + * Copyright © 2023 Atomic Private Limited and its contributors * * * * See the CONTRIBUTOR-LICENSE-AGREEMENT, COPYING, LICENSE-COPYRIGHT-NOTICE * * and DEVELOPER-CERTIFICATE-OF-ORIGIN files in the LEGAL directory in * @@ -98,7 +98,7 @@ use super::{coin_conf, lp_coinfind_or_err, AsyncMutex, BalanceError, BalanceFut, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult, EARLY_CONFIRMATION_ERR_LOG, INVALID_CONTRACT_ADDRESS_ERR_LOG, INVALID_PAYMENT_STATE_ERR_LOG, INVALID_RECEIVER_ERR_LOG, - INVALID_SENDER_ERR_LOG, INVALID_SWAP_ID_ERR_LOG}; + INVALID_SENDER_ERR_LOG, INVALID_SWAP_ID_ERR_LOG, SignEthTransactionRequest, SignEthTransactionResponse, SignEthTransactionResult}; pub use rlp; #[cfg(test)] mod eth_tests; @@ -1915,6 +1915,39 @@ impl MarketCoinOps for EthCoin { }) } + /// Stub for sign eth tx + #[inline(always)] + async fn sign_eth_tx(&self, args: &SignEthTransactionRequest) -> SignEthTransactionResult { + let ctx = MmArc::from_weak(&self.ctx).ok_or("!ctx") + .map_to_mm(|err| RawTransactionError::TransactionError(err.to_string()))?; + let coin = self.clone(); + let value = if let Some(value) = args.value { value } else { U256::from(0) }; + + let action = if let Some(to) = &args.to { + Action::Call( + Address::from_str(to).map_to_mm(|err| RawTransactionError::InvalidParam(err.to_string()))? + ) + } else { + Action::Create + }; + let data = if let Some(data) = &args.data { data.clone() } else { vec![] }; + let gas_limit = args.gas_limit; + match coin.priv_key_policy { + // TODO: use zeroise for privkey + EthPrivKeyPolicy::KeyPair(ref key_pair) => { + return sign_transaction_with_keypair(ctx, &coin, key_pair, value, action, data, gas_limit).await + .map(|(signed_tx, _)| SignEthTransactionResponse { + tx_hex: signed_tx.tx_hex().into(), + }) + .map_to_mm(|err| RawTransactionError::TransactionError(err.get_plain_text_format())); + }, + #[cfg(target_arch = "wasm32")] + EthPrivKeyPolicy::Metamask(_) => { + unimplemented!() + }, + } + } + fn wait_for_confirmations(&self, input: ConfirmPaymentInput) -> Box + Send> { macro_rules! update_status_with_error { ($status: ident, $error: ident) => { @@ -2145,7 +2178,7 @@ lazy_static! { type EthTxFut = Box + Send + 'static>; -async fn sign_and_send_transaction_with_keypair( +async fn sign_transaction_with_keypair( ctx: MmArc, coin: &EthCoin, key_pair: &KeyPair, @@ -2153,11 +2186,11 @@ async fn sign_and_send_transaction_with_keypair( action: Action, data: Vec, gas: U256, -) -> Result { +) -> Result<(SignedEthTx, Vec), TransactionErr> { let mut status = ctx.log.status_handle(); macro_rules! tags { () => { - &[&"sign-and-send"] + &[&"sign"] }; } let _nonce_lock = coin.nonce_lock.lock().await; @@ -2179,7 +2212,25 @@ async fn sign_and_send_transaction_with_keypair( data, }; - let signed = tx.sign(key_pair.secret(), coin.chain_id); + Ok((tx.sign(key_pair.secret(), coin.chain_id), web3_instances_with_latest_nonce)) +} + +async fn sign_and_send_transaction_with_keypair( + ctx: MmArc, + coin: &EthCoin, + key_pair: &KeyPair, + value: U256, + action: Action, + data: Vec, + gas: U256, +) -> Result { + let mut status = ctx.log.status_handle(); + macro_rules! tags { + () => { + &[&"sign-and-send"] + }; + } + let (signed, web3_instances_with_latest_nonce) = sign_transaction_with_keypair(ctx, coin, key_pair, value, action, data, gas).await?; let bytes = Bytes(rlp::encode(&signed).to_vec()); status.status(tags!(), "send_raw_transaction…"); @@ -2189,7 +2240,7 @@ async fn sign_and_send_transaction_with_keypair( try_tx_s!(select_ok(futures).await.map_err(|e| ERRL!("{}", e)), signed); status.status(tags!(), "get_addr_nonce…"); - coin.wait_for_addr_nonce_increase(coin.my_address, nonce).await; + coin.wait_for_addr_nonce_increase(coin.my_address, signed.transaction.unsigned.nonce).await; Ok(signed) } diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index d25431a664..dc26a98112 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -26,7 +26,8 @@ use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, C UtxoStandardCoin, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationError, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, - WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest}; + WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest, + SignEthTransactionRequest, SignEthTransactionResult}; use async_trait::async_trait; use bitcoin::bech32::ToBase32; use bitcoin::hashes::Hash; @@ -1103,6 +1104,14 @@ impl MarketCoinOps for LightningCoin { }) } + /// Stub for sign eth tx + #[inline(always)] + async fn sign_eth_tx(&self, _args: &SignEthTransactionRequest) -> SignEthTransactionResult { + MmError::err(RawTransactionError::NotImplemented { + coin: self.ticker().to_string(), + }) + } + // Todo: Add waiting for confirmations logic for the case of if the channel is closed and the htlc can be claimed on-chain // Todo: The above is postponed and might not be needed after this issue is resolved https://github.com/lightningdevkit/rust-lightning/issues/2017 fn wait_for_confirmations(&self, input: ConfirmPaymentInput) -> Box + Send> { diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 058aeb3205..69f3b48c64 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -215,6 +215,7 @@ pub mod eth; use eth::GetValidEthWithdrawAddError; use eth::{eth_coin_from_conf_and_request, get_eth_address, EthCoin, EthGasDetailsErr, EthTxFeeDetails, GetEthAddressError, SignedEthTx}; +use ethereum_types::U256; pub mod hd_confirm_address; pub mod hd_pubkey; @@ -310,6 +311,7 @@ pub type RawTransactionResult = Result = Box> + Send + 'a>; pub type SignRawTransactionResult = Result>; +pub type SignEthTransactionResult = Result>; pub type RefundResult = Result>; pub type IguanaPrivKey = Secp256k1Secret; @@ -348,6 +350,8 @@ pub enum RawTransactionError { SigningError(String), #[display(fmt = "Not implemented for this coin {}", coin)] NotImplemented { coin: String }, + #[display(fmt = "Transaction error {}", _0)] + TransactionError(String), } impl HttpStatusCode for RawTransactionError { @@ -361,7 +365,8 @@ impl HttpStatusCode for RawTransactionError { | RawTransactionError::HashNotExist(_) | RawTransactionError::DecodeError(_) | RawTransactionError::InvalidParam(_) - | RawTransactionError::NonExistentPrevOutputError(_) => StatusCode::BAD_REQUEST, + | RawTransactionError::NonExistentPrevOutputError(_) + | RawTransactionError::TransactionError(_) => StatusCode::BAD_REQUEST, RawTransactionError::NotImplemented { .. } => StatusCode::NOT_IMPLEMENTED, } } @@ -455,6 +460,30 @@ pub struct SignRawTransactionResponse { pub tx_hex: BytesJson, } +/// RPC request with unsigned eth transaction and params for signing +#[derive(Clone, Debug, Deserialize)] +pub struct SignEthTransactionRequest { + /// Coin ticker name + pub coin: String, + /// Eth transfer value + value: Option, + /// Eth to address + to: Option, + /// Eth contract data + data: Option>, + /// Eth gas use limit + gas_limit: U256, +} + +/// RPC response with signed eth transaction +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub struct SignEthTransactionResponse { + /// Raw bytes of signed transaction in hexadecimal string which is the response from the sign_eth_transaction request + pub tx_hex: BytesJson, +} + + + #[derive(Debug, Deserialize)] pub struct MyAddressReq { coin: String, @@ -1086,6 +1115,9 @@ pub trait MarketCoinOps { /// Signs raw utxo transaction in hexadecimal format as input and returns signed transaction in hexadecimal format async fn sign_raw_tx(&self, args: &SignRawTransactionRequest) -> SignRawTransactionResult; + /// Signs ethereum transaction in hexadecimal format as input and returns signed transaction in hexadecimal format + async fn sign_eth_tx(&self, args: &SignEthTransactionRequest) -> SignEthTransactionResult; + fn wait_for_confirmations(&self, input: ConfirmPaymentInput) -> Box + Send>; fn wait_for_htlc_tx_spend(&self, args: WaitForHTLCTxSpendArgs<'_>) -> TransactionFut; @@ -3370,6 +3402,11 @@ pub async fn sign_raw_transaction(ctx: MmArc, req: SignRawTransactionRequest) -> coin.sign_raw_tx(&req).await } +pub async fn sign_eth_transaction(ctx: MmArc, req: SignEthTransactionRequest) -> SignEthTransactionResult { + let coin = lp_coinfind_or_err(&ctx, &req.coin).await?; + coin.sign_eth_tx(&req).await +} + pub async fn remove_delegation(ctx: MmArc, req: RemoveDelegateRequest) -> DelegationResult { let coin = lp_coinfind_or_err(&ctx, &req.coin).await?; match coin { diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index 0b323c9365..c5e4b4c059 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -27,7 +27,8 @@ use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, Coi ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentFut, ValidatePaymentInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, - WatcherValidateTakerFeeInput, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult}; + WatcherValidateTakerFeeInput, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult, + RawTransactionError, SignEthTransactionRequest, SignEthTransactionResult}; use async_trait::async_trait; use bitcrypto::{dhash160, sha256}; use chain::TransactionOutput; @@ -1238,6 +1239,14 @@ impl MarketCoinOps for Qrc20Coin { async fn sign_raw_tx(&self, args: &SignRawTransactionRequest) -> SignRawTransactionResult { utxo_common::sign_raw_tx(self, args).await } + + /// Stub for sign eth tx + #[inline(always)] + async fn sign_eth_tx(&self, _args: &SignEthTransactionRequest) -> SignEthTransactionResult { + MmError::err(RawTransactionError::NotImplemented { + coin: self.ticker().to_string(), + }) + } fn wait_for_confirmations(&self, input: ConfirmPaymentInput) -> Box + Send> { let tx: UtxoTx = try_fus!(deserialize(input.payment_tx.as_slice()).map_err(|e| ERRL!("{:?}", e))); diff --git a/mm2src/coins/solana.rs b/mm2src/coins/solana.rs index 0811d9c91d..903b0854a2 100644 --- a/mm2src/coins/solana.rs +++ b/mm2src/coins/solana.rs @@ -13,7 +13,8 @@ use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinFutSpawner, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, - WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult}; + WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult, + SignEthTransactionRequest, SignEthTransactionResult}; use async_trait::async_trait; use base58::ToBase58; use bincode::{deserialize, serialize}; @@ -447,6 +448,14 @@ impl MarketCoinOps for SolanaCoin { }) } + /// Stub for sign eth tx + #[inline(always)] + async fn sign_eth_tx(&self, _args: &SignEthTransactionRequest) -> SignEthTransactionResult { + MmError::err(RawTransactionError::NotImplemented { + coin: self.ticker().to_string(), + }) + } + fn wait_for_confirmations(&self, _input: ConfirmPaymentInput) -> Box + Send> { unimplemented!() } diff --git a/mm2src/coins/solana/spl.rs b/mm2src/coins/solana/spl.rs index ddc4397d61..f9d97ee69d 100644 --- a/mm2src/coins/solana/spl.rs +++ b/mm2src/coins/solana/spl.rs @@ -13,7 +13,8 @@ use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinFutSpawner, ConfirmPayment ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, - WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult}; + WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult, + SignEthTransactionRequest, SignEthTransactionResult}; use async_trait::async_trait; use bincode::serialize; use common::executor::{abortable_queue::AbortableQueue, AbortableSystem, AbortedError}; @@ -274,6 +275,14 @@ impl MarketCoinOps for SplToken { coin: self.ticker().to_string(), }) } + + /// Stub for sign eth tx + #[inline(always)] + async fn sign_eth_tx(&self, _args: &SignEthTransactionRequest) -> SignEthTransactionResult { + MmError::err(RawTransactionError::NotImplemented { + coin: self.ticker().to_string(), + }) + } fn wait_for_confirmations(&self, _input: ConfirmPaymentInput) -> Box + Send> { unimplemented!() diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index 9c2ba68c58..fd4d0ed041 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -28,7 +28,7 @@ use crate::{big_decimal_from_sat_unsigned, BalanceError, BalanceFut, BigDecimal, ValidateOtherPubKeyErr, ValidatePaymentFut, ValidatePaymentInput, VerificationError, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFee, WithdrawFut, - WithdrawRequest}; + WithdrawRequest, SignEthTransactionRequest, SignEthTransactionResult}; use async_std::prelude::FutureExt as AsyncStdFutureExt; use async_trait::async_trait; use bitcrypto::{dhash160, sha256}; @@ -2192,6 +2192,14 @@ impl MarketCoinOps for TendermintCoin { coin: self.ticker().to_string(), }) } + + /// Stub for sign eth tx + #[inline(always)] + async fn sign_eth_tx(&self, _args: &SignEthTransactionRequest) -> SignEthTransactionResult { + MmError::err(RawTransactionError::NotImplemented { + coin: self.ticker().to_string(), + }) + } fn wait_for_confirmations(&self, input: ConfirmPaymentInput) -> Box + Send> { // Sanity check diff --git a/mm2src/coins/tendermint/tendermint_token.rs b/mm2src/coins/tendermint/tendermint_token.rs index 65e7ecb36f..c6da5032d3 100644 --- a/mm2src/coins/tendermint/tendermint_token.rs +++ b/mm2src/coins/tendermint/tendermint_token.rs @@ -18,8 +18,9 @@ use crate::{big_decimal_from_sat_unsigned, utxo::sat_from_big_decimal, BalanceFu ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, - WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest}; -use crate::{MmCoinEnum, PaymentInstructionArgs, RawTransactionError, WatcherReward, WatcherRewardError}; + WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest, + SignEthTransactionRequest, SignEthTransactionResult, MmCoinEnum, PaymentInstructionArgs, + RawTransactionError, WatcherReward, WatcherRewardError}; use async_trait::async_trait; use bitcrypto::sha256; use common::executor::abortable_queue::AbortableQueue; @@ -545,6 +546,14 @@ impl MarketCoinOps for TendermintToken { coin: self.ticker().to_string(), }) } + + /// Stub for sign eth tx + #[inline(always)] + async fn sign_eth_tx(&self, _args: &SignEthTransactionRequest) -> SignEthTransactionResult { + MmError::err(RawTransactionError::NotImplemented { + coin: self.ticker().to_string(), + }) + } fn wait_for_confirmations(&self, input: ConfirmPaymentInput) -> Box + Send> { self.platform_coin.wait_for_confirmations(input) diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index d7ea970443..ba50e3fbb4 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -12,7 +12,7 @@ use crate::{coin_errors::MyAddressError, BalanceFut, CanRefundHtlc, CheckIfMyPay ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut, - WithdrawRequest}; + WithdrawRequest, SignEthTransactionRequest, SignEthTransactionResult}; use async_trait::async_trait; use common::executor::AbortedError; use futures01::Future; @@ -80,6 +80,9 @@ impl MarketCoinOps for TestCoin { #[inline(always)] async fn sign_raw_tx(&self, _args: &SignRawTransactionRequest) -> SignRawTransactionResult { unimplemented!() } + + #[inline(always)] + async fn sign_eth_tx(&self, _args: &SignEthTransactionRequest) -> SignEthTransactionResult { unimplemented!() } fn wait_for_confirmations(&self, _input: ConfirmPaymentInput) -> Box + Send> { unimplemented!() diff --git a/mm2src/coins/utxo/bch.rs b/mm2src/coins/utxo/bch.rs index 5578778e48..6a584026e4 100644 --- a/mm2src/coins/utxo/bch.rs +++ b/mm2src/coins/utxo/bch.rs @@ -19,7 +19,8 @@ use crate::{BlockHeightAndTime, CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinBal UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, - WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut}; + WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut, + RawTransactionError, SignEthTransactionRequest, SignEthTransactionResult}; use common::executor::{AbortableSystem, AbortedError}; use common::log::warn; use derive_more::Display; @@ -1182,6 +1183,14 @@ impl MarketCoinOps for BchCoin { async fn sign_raw_tx(&self, args: &SignRawTransactionRequest) -> SignRawTransactionResult { utxo_common::sign_raw_tx(self, args).await } + + /// Stub for sign eth tx + #[inline(always)] + async fn sign_eth_tx(&self, _args: &SignEthTransactionRequest) -> SignEthTransactionResult { + MmError::err(RawTransactionError::NotImplemented { + coin: self.ticker().to_string(), + }) + } fn wait_for_confirmations(&self, input: ConfirmPaymentInput) -> Box + Send> { utxo_common::wait_for_confirmations(&self.utxo_arc, input) diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index 7554be620f..e3fbf1776e 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -34,7 +34,8 @@ use crate::{eth, CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinBalance, CoinWithD ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, - WatcherValidateTakerFeeInput, WithdrawFut, WithdrawSenderAddress}; + WatcherValidateTakerFeeInput, WithdrawFut, WithdrawSenderAddress, + RawTransactionError, SignEthTransactionRequest, SignEthTransactionResult}; use common::executor::{AbortableSystem, AbortedError}; use crypto::Bip44Chain; use ethereum_types::H160; @@ -848,6 +849,14 @@ impl MarketCoinOps for QtumCoin { async fn sign_raw_tx(&self, args: &SignRawTransactionRequest) -> SignRawTransactionResult { utxo_common::sign_raw_tx(self, args).await } + + /// Stub for sign eth tx + #[inline(always)] + async fn sign_eth_tx(&self, _args: &SignEthTransactionRequest) -> SignEthTransactionResult { + MmError::err(RawTransactionError::NotImplemented { + coin: self.ticker().to_string(), + }) + } fn wait_for_confirmations(&self, input: ConfirmPaymentInput) -> Box + Send> { utxo_common::wait_for_confirmations(&self.utxo_arc, input) diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index 0177ec02cb..a11e8fac2f 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -25,7 +25,8 @@ use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, C ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentInput, VerificationError, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, - WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest}; + WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, + RawTransactionError, SignEthTransactionRequest, SignEthTransactionResult}; use async_trait::async_trait; use bitcrypto::dhash160; use chain::constants::SEQUENCE_FINAL; @@ -1163,6 +1164,14 @@ impl MarketCoinOps for SlpToken { async fn sign_raw_tx(&self, args: &SignRawTransactionRequest) -> SignRawTransactionResult { utxo_common::sign_raw_tx(self, args).await } + + /// Stub for sign eth tx + #[inline(always)] + async fn sign_eth_tx(&self, _args: &SignEthTransactionRequest) -> SignEthTransactionResult { + MmError::err(RawTransactionError::NotImplemented { + coin: self.ticker().to_string(), + }) + } fn wait_for_confirmations(&self, input: ConfirmPaymentInput) -> Box + Send> { self.platform_coin.wait_for_confirmations(input) diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index 148d1f7702..b0e15a56fc 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -31,7 +31,8 @@ use crate::{CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinBalance, CoinWithDeriva ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, - WatcherValidateTakerFeeInput, WithdrawFut, WithdrawSenderAddress}; + WatcherValidateTakerFeeInput, WithdrawFut, WithdrawSenderAddress, + RawTransactionError, SignEthTransactionRequest, SignEthTransactionResult}; use common::executor::{AbortableSystem, AbortedError}; use crypto::Bip44Chain; use futures::{FutureExt, TryFutureExt}; @@ -622,6 +623,14 @@ impl MarketCoinOps for UtxoStandardCoin { async fn sign_raw_tx(&self, args: &SignRawTransactionRequest) -> SignRawTransactionResult { utxo_common::sign_raw_tx(self, args).await } + + /// Stub for sign eth tx + #[inline(always)] + async fn sign_eth_tx(&self, _args: &SignEthTransactionRequest) -> SignEthTransactionResult { + MmError::err(RawTransactionError::NotImplemented { + coin: self.ticker().to_string(), + }) + } fn wait_for_confirmations(&self, input: ConfirmPaymentInput) -> Box + Send> { utxo_common::wait_for_confirmations(&self.utxo_arc, input) diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index 635be01a04..a0ee86e68a 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -25,7 +25,8 @@ use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, Coi ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationError, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, - WatcherValidateTakerFeeInput, WithdrawFut, WithdrawRequest}; + WatcherValidateTakerFeeInput, WithdrawFut, WithdrawRequest, + RawTransactionError, SignEthTransactionRequest, SignEthTransactionResult}; use crate::{Transaction, WithdrawError}; use async_trait::async_trait; use bitcrypto::dhash256; @@ -1122,6 +1123,14 @@ impl MarketCoinOps for ZCoin { async fn sign_raw_tx(&self, args: &SignRawTransactionRequest) -> SignRawTransactionResult { utxo_common::sign_raw_tx(self, args).await } + + /// Stub for sign eth tx + #[inline(always)] + async fn sign_eth_tx(&self, _args: &SignEthTransactionRequest) -> SignEthTransactionResult { + MmError::err(RawTransactionError::NotImplemented { + coin: self.ticker().to_string(), + }) + } fn wait_for_confirmations(&self, input: ConfirmPaymentInput) -> Box + Send> { utxo_common::wait_for_confirmations(self.as_ref(), input) diff --git a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs index cf3c43ea3d..c9fa62d73d 100644 --- a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs +++ b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs @@ -30,7 +30,7 @@ use coins::utxo::qtum::QtumCoin; use coins::utxo::slp::SlpToken; use coins::utxo::utxo_standard::UtxoStandardCoin; use coins::{add_delegation, get_my_address, get_raw_transaction, get_staking_infos, nft, remove_delegation, - sign_message, sign_raw_transaction, verify_message, withdraw}; + sign_message, sign_raw_transaction, sign_eth_transaction, verify_message, withdraw}; #[cfg(all( feature = "enable-solana", not(target_os = "ios"), @@ -185,6 +185,7 @@ async fn dispatcher_v2(request: MmRpcRequest, ctx: MmArc) -> DispatcherResult handle_mmrpc(ctx, request, remove_node_from_version_stat).await, "sign_message" => handle_mmrpc(ctx, request, sign_message).await, "sign_raw_transaction" => handle_mmrpc(ctx, request, sign_raw_transaction).await, + "sign_eth_transaction" => handle_mmrpc(ctx, request, sign_eth_transaction).await, "start_simple_market_maker_bot" => handle_mmrpc(ctx, request, start_simple_market_maker_bot).await, "start_version_stat_collection" => handle_mmrpc(ctx, request, start_version_stat_collection).await, "stop_simple_market_maker_bot" => handle_mmrpc(ctx, request, stop_simple_market_maker_bot).await, diff --git a/mm2src/mm2_main/tests/mm2_tests/eth_tests.rs b/mm2src/mm2_main/tests/mm2_tests/eth_tests.rs index 676145fc84..efcf4e7c7c 100644 --- a/mm2src/mm2_main/tests/mm2_tests/eth_tests.rs +++ b/mm2src/mm2_main/tests/mm2_tests/eth_tests.rs @@ -158,3 +158,65 @@ fn test_disable_eth_coin_with_token_without_balance() { HashSet::from_iter(vec!["JST".to_string()]) ); } + +#[test] +#[cfg(not(target_arch = "wasm32"))] +fn test_sign_eth_transaction() { + let passphrase = get_passphrase(&".env.client", "BOB_PASSPHRASE").unwrap(); + let coins = json!([eth_testnet_conf()]); + let conf = Mm2TestConf::seednode(&passphrase, &coins); + let mm = block_on(MarketMakerIt::start_async(conf.conf, conf.rpc_password, None)).unwrap(); + block_on(enable_eth(&mm, "ETH", ETH_DEV_NODES)); + let signed_tx = block_on(call_sign_eth_transaction(&mm, "ETH")); + assert!(signed_tx["result"]["tx_hex"].is_string()); +} + +#[cfg(not(target_arch = "wasm32"))] +async fn enable_eth(mm: &MarketMakerIt, platform_coin: &str, nodes: &[&str]) -> Json { + let enable = mm + .rpc(&json!({ + "userpass": mm.userpass, + "method": "enable", + "coin": platform_coin, + "urls": nodes, + "swap_contract_address": ETH_DEV_SWAP_CONTRACT, + "mm2": 1, + })) + .await + .unwrap(); + assert_eq!( + enable.0, + StatusCode::OK, + "'enable {platform_coin:?}' failed: {}", + enable.1 + ); + Json::from_str(&enable.1).unwrap() +} + +#[cfg(not(target_arch = "wasm32"))] +async fn call_sign_eth_transaction( + mm: &MarketMakerIt, + platform_coin: &str, +) -> Json { + let signed_tx = mm + .rpc(&json!({ + "userpass": mm.userpass, + "method": "sign_eth_transaction", + "mmrpc": "2.0", + "params": { + "coin": platform_coin, + "to": "0x7Bc1bBDD6A0a722fC9bffC49c921B685ECB84b94".to_string(), + "value": "0x1000", + "gas_limit": "21000" + } + })) + .await + .unwrap(); + assert_eq!( + signed_tx.0, + StatusCode::OK, + "'sign_eth_transaction' failed: {}", + signed_tx.1 + ); + Json::from_str(&signed_tx.1).unwrap() +}