diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs
index a61e4f4384..b5f5b0ecde 100644
--- a/mm2src/coins/eth.rs
+++ b/mm2src/coins/eth.rs
@@ -79,7 +79,7 @@ use instant::Instant;
use keys::Public as HtlcPubKey;
use mm2_core::mm_ctx::{MmArc, MmWeak};
use mm2_event_stream::behaviour::{EventBehaviour, EventInitStatus};
-use mm2_net::transport::{GuiAuthValidation, GuiAuthValidationGenerator};
+use mm2_net::transport::{KomodefiProxyAuthValidation, ProxyAuthValidationGenerator};
use mm2_number::bigdecimal_custom::CheckedDivision;
use mm2_number::{BigDecimal, BigUint, MmNumber};
#[cfg(test)] use mocktopus::macros::*;
@@ -172,6 +172,7 @@ const ERC721_ABI: &str = include_str!("eth/erc721_abi.json");
/// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md
const ERC1155_ABI: &str = include_str!("eth/erc1155_abi.json");
const NFT_SWAP_CONTRACT_ABI: &str = include_str!("eth/nft_swap_contract_abi.json");
+const NFT_MAKER_SWAP_V2_ABI: &str = include_str!("eth/nft_maker_swap_v2_abi.json");
/// Payment states from etomic swap smart contract: https://github.com/artemii235/etomic-swap/blob/master/contracts/EtomicSwap.sol#L5
pub enum PaymentState {
@@ -289,8 +290,8 @@ impl Default for EthGasLimit {
}
}
-/// Lifetime of generated signed message for gui-auth requests
-const GUI_AUTH_SIGNED_MESSAGE_LIFETIME_SEC: i64 = 90;
+/// Lifetime of generated signed message for proxy-auth requests
+const PROXY_AUTH_SIGNED_MESSAGE_LIFETIME_SEC: i64 = 90;
/// Max transaction type according to EIP-2718
const ETH_MAX_TX_TYPE: u64 = 0x7f;
@@ -301,6 +302,7 @@ lazy_static! {
pub static ref ERC721_CONTRACT: Contract = Contract::load(ERC721_ABI.as_bytes()).unwrap();
pub static ref ERC1155_CONTRACT: Contract = Contract::load(ERC1155_ABI.as_bytes()).unwrap();
pub static ref NFT_SWAP_CONTRACT: Contract = Contract::load(NFT_SWAP_CONTRACT_ABI.as_bytes()).unwrap();
+ pub static ref NFT_MAKER_SWAP_V2: Contract = Contract::load(NFT_MAKER_SWAP_V2_ABI.as_bytes()).unwrap();
}
pub type EthDerivationMethod = DerivationMethod
;
@@ -639,7 +641,7 @@ pub(crate) enum FeeEstimatorState {
pub struct EthCoinImpl {
ticker: String,
pub coin_type: EthCoinType,
- priv_key_policy: EthPrivKeyPolicy,
+ pub(crate) priv_key_policy: EthPrivKeyPolicy,
/// Either an Iguana address or a 'EthHDWallet' instance.
/// Arc is used to use the same hd wallet from platform coin if we need to.
/// This allows the reuse of the same derived accounts/addresses of the
@@ -3593,7 +3595,7 @@ impl EthCoin {
impl EthCoin {
/// Sign and send eth transaction.
/// This function is primarily for swap transactions so internally it relies on the swap tx fee policy
- pub(crate) fn sign_and_send_transaction(&self, value: U256, action: Action, data: Vec, gas: U256) -> EthTxFut {
+ pub fn sign_and_send_transaction(&self, value: U256, action: Action, data: Vec, gas: U256) -> EthTxFut {
let coin = self.clone();
let fut = async move {
match coin.priv_key_policy {
@@ -5776,14 +5778,15 @@ impl TryToAddress for Option {
}
}
-pub trait GuiAuthMessages {
- fn gui_auth_sign_message_hash(message: String) -> Option<[u8; 32]>;
- fn generate_gui_auth_signed_validation(generator: GuiAuthValidationGenerator)
- -> SignatureResult;
+pub trait KomodoDefiAuthMessages {
+ fn proxy_auth_sign_message_hash(message: String) -> Option<[u8; 32]>;
+ fn generate_proxy_auth_signed_validation(
+ generator: ProxyAuthValidationGenerator,
+ ) -> SignatureResult;
}
-impl GuiAuthMessages for EthCoin {
- fn gui_auth_sign_message_hash(message: String) -> Option<[u8; 32]> {
+impl KomodoDefiAuthMessages for EthCoin {
+ fn proxy_auth_sign_message_hash(message: String) -> Option<[u8; 32]> {
let message_prefix = "atomicDEX Auth Ethereum Signed Message:\n";
let prefix_len = CompactInteger::from(message_prefix.len());
@@ -5796,16 +5799,16 @@ impl GuiAuthMessages for EthCoin {
Some(keccak256(&stream.out()).take())
}
- fn generate_gui_auth_signed_validation(
- generator: GuiAuthValidationGenerator,
- ) -> SignatureResult {
- let timestamp_message = get_utc_timestamp() + GUI_AUTH_SIGNED_MESSAGE_LIFETIME_SEC;
+ fn generate_proxy_auth_signed_validation(
+ generator: ProxyAuthValidationGenerator,
+ ) -> SignatureResult {
+ let timestamp_message = get_utc_timestamp() + PROXY_AUTH_SIGNED_MESSAGE_LIFETIME_SEC;
- let message_hash =
- EthCoin::gui_auth_sign_message_hash(timestamp_message.to_string()).ok_or(SignatureError::PrefixNotFound)?;
+ let message_hash = EthCoin::proxy_auth_sign_message_hash(timestamp_message.to_string())
+ .ok_or(SignatureError::PrefixNotFound)?;
let signature = sign(&generator.secret, &H256::from(message_hash))?;
- Ok(GuiAuthValidation {
+ Ok(KomodefiProxyAuthValidation {
coin_ticker: generator.coin_ticker,
address: generator.address,
timestamp_message,
diff --git a/mm2src/coins/eth/nft_maker_swap_v2_abi.json b/mm2src/coins/eth/nft_maker_swap_v2_abi.json
new file mode 100644
index 0000000000..95def23766
--- /dev/null
+++ b/mm2src/coins/eth/nft_maker_swap_v2_abi.json
@@ -0,0 +1,462 @@
+[
+ {
+ "inputs": [],
+ "stateMutability": "nonpayable",
+ "type": "constructor"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "bytes32",
+ "name": "id",
+ "type": "bytes32"
+ }
+ ],
+ "name": "MakerPaymentRefundedSecret",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "bytes32",
+ "name": "id",
+ "type": "bytes32"
+ }
+ ],
+ "name": "MakerPaymentRefundedTimelock",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "bytes32",
+ "name": "id",
+ "type": "bytes32"
+ }
+ ],
+ "name": "MakerPaymentSent",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "bytes32",
+ "name": "id",
+ "type": "bytes32"
+ }
+ ],
+ "name": "MakerPaymentSpent",
+ "type": "event"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "bytes32",
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "name": "makerPayments",
+ "outputs": [
+ {
+ "internalType": "bytes20",
+ "name": "paymentHash",
+ "type": "bytes20"
+ },
+ {
+ "internalType": "uint32",
+ "name": "paymentLockTime",
+ "type": "uint32"
+ },
+ {
+ "internalType": "enum EtomicSwapMakerNftV2.MakerPaymentState",
+ "name": "state",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256[]",
+ "name": "",
+ "type": "uint256[]"
+ },
+ {
+ "internalType": "uint256[]",
+ "name": "",
+ "type": "uint256[]"
+ },
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "name": "onERC1155BatchReceived",
+ "outputs": [
+ {
+ "internalType": "bytes4",
+ "name": "",
+ "type": "bytes4"
+ }
+ ],
+ "stateMutability": "pure",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "operator",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "from",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "tokenId",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes",
+ "name": "data",
+ "type": "bytes"
+ }
+ ],
+ "name": "onERC1155Received",
+ "outputs": [
+ {
+ "internalType": "bytes4",
+ "name": "",
+ "type": "bytes4"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "operator",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "from",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "tokenId",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes",
+ "name": "data",
+ "type": "bytes"
+ }
+ ],
+ "name": "onERC721Received",
+ "outputs": [
+ {
+ "internalType": "bytes4",
+ "name": "",
+ "type": "bytes4"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "bytes32",
+ "name": "id",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "address",
+ "name": "taker",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "takerSecret",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "makerSecretHash",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "address",
+ "name": "tokenAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "tokenId",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "refundErc1155MakerPaymentSecret",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "bytes32",
+ "name": "id",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "address",
+ "name": "taker",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "takerSecretHash",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "makerSecretHash",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "address",
+ "name": "tokenAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "tokenId",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "refundErc1155MakerPaymentTimelock",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "bytes32",
+ "name": "id",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "address",
+ "name": "taker",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "takerSecret",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "makerSecretHash",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "address",
+ "name": "tokenAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "tokenId",
+ "type": "uint256"
+ }
+ ],
+ "name": "refundErc721MakerPaymentSecret",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "bytes32",
+ "name": "id",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "address",
+ "name": "taker",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "takerSecretHash",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "makerSecretHash",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "address",
+ "name": "tokenAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "tokenId",
+ "type": "uint256"
+ }
+ ],
+ "name": "refundErc721MakerPaymentTimelock",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "bytes32",
+ "name": "id",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "address",
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "takerSecretHash",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "makerSecret",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "address",
+ "name": "tokenAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "tokenId",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "spendErc1155MakerPayment",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "bytes32",
+ "name": "id",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "address",
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "takerSecretHash",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "makerSecret",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "address",
+ "name": "tokenAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "tokenId",
+ "type": "uint256"
+ }
+ ],
+ "name": "spendErc721MakerPayment",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "bytes4",
+ "name": "interfaceId",
+ "type": "bytes4"
+ }
+ ],
+ "name": "supportsInterface",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ }
+]
\ No newline at end of file
diff --git a/mm2src/coins/eth/nft_swap_v2/mod.rs b/mm2src/coins/eth/nft_swap_v2/mod.rs
index 9e6afcbbcd..19f5caca7f 100644
--- a/mm2src/coins/eth/nft_swap_v2/mod.rs
+++ b/mm2src/coins/eth/nft_swap_v2/mod.rs
@@ -15,7 +15,7 @@ use structs::{ExpectedHtlcParams, PaymentType, ValidationParams};
use super::ContractType;
use crate::eth::{addr_from_raw_pubkey, decode_contract_call, EthCoin, EthCoinType, MakerPaymentStateV2, SignedEthTx,
- TryToAddress, ERC1155_CONTRACT, ERC721_CONTRACT, NFT_SWAP_CONTRACT};
+ TryToAddress, ERC1155_CONTRACT, ERC721_CONTRACT, NFT_MAKER_SWAP_V2};
use crate::{ParseCoinAssocTypes, RefundPaymentArgs, SendNftMakerPaymentArgs, SpendNftMakerPaymentArgs, TransactionErr,
ValidateNftMakerPaymentArgs};
@@ -74,7 +74,7 @@ impl EthCoin {
.payment_status_v2(
*etomic_swap_contract,
Token::FixedBytes(swap_id.clone()),
- &NFT_SWAP_CONTRACT,
+ &NFT_MAKER_SWAP_V2,
PaymentType::MakerPayments,
)
.await?;
@@ -144,7 +144,7 @@ impl EthCoin {
let (state, htlc_params) = try_tx_s!(
self.status_and_htlc_params_from_tx_data(
*etomic_swap_contract,
- &NFT_SWAP_CONTRACT,
+ &NFT_MAKER_SWAP_V2,
&decoded,
index_bytes,
PaymentType::MakerPayments,
@@ -269,8 +269,8 @@ impl EthCoin {
state: U256,
) -> Result, PrepareTxDataError> {
let spend_func = match args.contract_type {
- ContractType::Erc1155 => NFT_SWAP_CONTRACT.function("spendErc1155MakerPayment")?,
- ContractType::Erc721 => NFT_SWAP_CONTRACT.function("spendErc721MakerPayment")?,
+ ContractType::Erc1155 => NFT_MAKER_SWAP_V2.function("spendErc1155MakerPayment")?,
+ ContractType::Erc721 => NFT_MAKER_SWAP_V2.function("spendErc721MakerPayment")?,
};
if state != U256::from(MakerPaymentStateV2::PaymentSent as u8) {
diff --git a/mm2src/coins/eth/v2_activation.rs b/mm2src/coins/eth/v2_activation.rs
index 2cf680b66f..e8bb6cebeb 100644
--- a/mm2src/coins/eth/v2_activation.rs
+++ b/mm2src/coins/eth/v2_activation.rs
@@ -89,6 +89,7 @@ impl From for EthActivationV2Error {
EthTokenActivationError::UnexpectedDerivationMethod(err) => {
EthActivationV2Error::UnexpectedDerivationMethod(err)
},
+ EthTokenActivationError::PrivKeyPolicyNotAllowed(e) => EthActivationV2Error::PrivKeyPolicyNotAllowed(e),
}
}
}
@@ -204,6 +205,7 @@ pub enum EthTokenActivationError {
InvalidPayload(String),
Transport(String),
UnexpectedDerivationMethod(UnexpectedDerivationMethod),
+ PrivKeyPolicyNotAllowed(PrivKeyPolicyNotAllowed),
}
impl From for EthTokenActivationError {
@@ -254,6 +256,36 @@ impl From for EthTokenActivationError {
fn from(e: String) -> Self { EthTokenActivationError::InternalError(e) }
}
+impl From for EthTokenActivationError {
+ fn from(e: PrivKeyPolicyNotAllowed) -> Self { EthTokenActivationError::PrivKeyPolicyNotAllowed(e) }
+}
+
+impl From for EthTokenActivationError {
+ fn from(e: GenerateSignedMessageError) -> Self {
+ match e {
+ GenerateSignedMessageError::InternalError(e) => EthTokenActivationError::InternalError(e),
+ GenerateSignedMessageError::PrivKeyPolicyNotAllowed(e) => {
+ EthTokenActivationError::PrivKeyPolicyNotAllowed(e)
+ },
+ }
+ }
+}
+
+#[derive(Display, Serialize)]
+pub enum GenerateSignedMessageError {
+ #[display(fmt = "Internal: {}", _0)]
+ InternalError(String),
+ PrivKeyPolicyNotAllowed(PrivKeyPolicyNotAllowed),
+}
+
+impl From for GenerateSignedMessageError {
+ fn from(e: PrivKeyPolicyNotAllowed) -> Self { GenerateSignedMessageError::PrivKeyPolicyNotAllowed(e) }
+}
+
+impl From for GenerateSignedMessageError {
+ fn from(e: SignatureError) -> Self { GenerateSignedMessageError::InternalError(e.to_string()) }
+}
+
/// Represents the parameters required for activating either an ERC-20 token or an NFT on the Ethereum platform.
#[derive(Clone, Deserialize)]
#[serde(untagged)]
@@ -300,7 +332,11 @@ pub struct NftActivationRequest {
#[derive(Clone, Deserialize)]
#[serde(tag = "type", content = "info")]
pub enum NftProviderEnum {
- Moralis { url: Url },
+ Moralis {
+ url: Url,
+ #[serde(default)]
+ proxy_auth: bool,
+ },
}
/// Represents the protocol type for an Ethereum-based token, distinguishing between ERC-20 tokens and NFTs.
@@ -368,7 +404,7 @@ impl EthCoin {
.iter()
.map(|node| {
let mut transport = node.web3.transport().clone();
- if let Some(auth) = transport.gui_auth_validation_generator_as_mut() {
+ if let Some(auth) = transport.proxy_auth_validation_generator_as_mut() {
auth.coin_ticker = ticker.clone();
}
let web3 = Web3::new(transport);
@@ -438,7 +474,11 @@ impl EthCoin {
/// It fetches NFT details from a given URL to populate the `nfts_infos` field, which stores information about the user's NFTs.
///
/// This setup allows the Global NFT to function like a coin, supporting swap operations and providing easy access to NFT details via `nfts_infos`.
- pub async fn global_nft_from_platform_coin(&self, url: &Url) -> MmResult {
+ pub async fn global_nft_from_platform_coin(
+ &self,
+ original_url: &Url,
+ proxy_auth: &bool,
+ ) -> MmResult {
let chain = Chain::from_ticker(self.ticker())?;
let ticker = chain.to_nft_ticker().to_string();
@@ -454,7 +494,12 @@ impl EthCoin {
// Todo: support HD wallet for NFTs, currently we get nfts for enabled address only and there might be some issues when activating NFTs while ETH is activated with HD wallet
let my_address = self.derivation_method.single_addr_or_err().await?;
- let nft_infos = get_nfts_for_activation(&chain, &my_address, url).await?;
+
+ let my_address_str = display_eth_address(&my_address);
+ let signed_message =
+ generate_signed_message(*proxy_auth, &chain, my_address_str, self.priv_key_policy()).await?;
+
+ let nft_infos = get_nfts_for_activation(&chain, &my_address, original_url, signed_message.as_ref()).await?;
let coin_type = EthCoinType::Nft {
platform: self.ticker.clone(),
};
@@ -493,6 +538,28 @@ impl EthCoin {
}
}
+pub(crate) async fn generate_signed_message(
+ proxy_auth: bool,
+ chain: &Chain,
+ my_address: String,
+ priv_key_policy: &EthPrivKeyPolicy,
+) -> MmResult