diff --git a/Cargo.lock b/Cargo.lock index be787cce7b..658f01ea62 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4517,6 +4517,7 @@ dependencies = [ "either", "enum-primitive-derive", "enum_derives", + "ethabi", "ethereum-types", "futures 0.1.29", "futures 0.3.28", diff --git a/mm2src/coins/coin_errors.rs b/mm2src/coins/coin_errors.rs index 6c0d88b452..5c86537a46 100644 --- a/mm2src/coins/coin_errors.rs +++ b/mm2src/coins/coin_errors.rs @@ -1,5 +1,7 @@ -use crate::{eth::Web3RpcError, my_tx_history_v2::MyTxHistoryErrorV2, utxo::rpc_clients::UtxoRpcError, DelegationError, - NumConversError, TxHistoryError, UnexpectedDerivationMethod, WithdrawError}; +use crate::eth::nft_swap_v2::errors::{Erc721FunctionError, HtlcParamsError, PaymentStatusErr, PrepareTxDataError}; +use crate::eth::{EthAssocTypesError, EthNftAssocTypesError, Web3RpcError}; +use crate::{utxo::rpc_clients::UtxoRpcError, NumConversError, UnexpectedDerivationMethod}; +use enum_derives::EnumFromStringify; use futures01::Future; use mm2_err_handle::prelude::MmError; use spv_validation::helpers_validation::SPVError; @@ -11,11 +13,21 @@ pub type ValidatePaymentFut = Box = Result>; /// Enum covering possible error cases of swap payment validation -#[derive(Debug, Display)] +#[derive(Debug, Display, EnumFromStringify)] pub enum ValidatePaymentError { /// Should be used to indicate internal MM2 state problems (e.g., DB errors, etc.). + #[from_stringify( + "EthAssocTypesError", + "Erc721FunctionError", + "EthNftAssocTypesError", + "NumConversError", + "UnexpectedDerivationMethod", + "keys::Error", + "PrepareTxDataError" + )] InternalError(String), /// Problem with deserializing the transaction, or one of the transaction parts is invalid. + #[from_stringify("rlp::DecoderError", "serialization::Error")] TxDeserializationError(String), /// One of the input parameters is invalid. InvalidParameter(String), @@ -28,6 +40,7 @@ pub enum ValidatePaymentError { /// Payment transaction is in unexpected state. E.g., `Uninitialized` instead of `Sent` for ETH payment. UnexpectedPaymentState(String), /// Transport (RPC) error. + #[from_stringify("web3::Error")] Transport(String), /// Transaction has wrong properties, for example, it has been sent to a wrong address. WrongPaymentTx(String), @@ -39,30 +52,10 @@ pub enum ValidatePaymentError { NftProtocolNotSupported, } -impl From for ValidatePaymentError { - fn from(err: rlp::DecoderError) -> Self { Self::TxDeserializationError(err.to_string()) } -} - -impl From for ValidatePaymentError { - fn from(err: web3::Error) -> Self { Self::Transport(err.to_string()) } -} - -impl From for ValidatePaymentError { - fn from(err: NumConversError) -> Self { Self::InternalError(err.to_string()) } -} - impl From for ValidatePaymentError { fn from(err: SPVError) -> Self { Self::SPVError(err) } } -impl From for ValidatePaymentError { - fn from(err: serialization::Error) -> Self { Self::TxDeserializationError(err.to_string()) } -} - -impl From for ValidatePaymentError { - fn from(err: UnexpectedDerivationMethod) -> Self { Self::InternalError(err.to_string()) } -} - impl From for ValidatePaymentError { fn from(err: UtxoRpcError) -> Self { match err { @@ -86,36 +79,29 @@ impl From for ValidatePaymentError { } } -impl From for ValidatePaymentError { - fn from(err: keys::Error) -> Self { Self::InternalError(err.to_string()) } +impl From for ValidatePaymentError { + fn from(err: PaymentStatusErr) -> Self { + match err { + PaymentStatusErr::Transport(e) => Self::Transport(e), + PaymentStatusErr::AbiError(e) + | PaymentStatusErr::Internal(e) + | PaymentStatusErr::TxDeserializationError(e) => Self::InternalError(e), + } + } +} + +impl From for ValidatePaymentError { + fn from(err: HtlcParamsError) -> Self { + match err { + HtlcParamsError::WrongPaymentTx(e) => ValidatePaymentError::WrongPaymentTx(e), + HtlcParamsError::TxDeserializationError(e) => ValidatePaymentError::TxDeserializationError(e), + } + } } -#[derive(Debug, Display)] +#[derive(Debug, Display, EnumFromStringify)] pub enum MyAddressError { + #[from_stringify("UnexpectedDerivationMethod")] UnexpectedDerivationMethod(String), InternalError(String), } - -impl From for MyAddressError { - fn from(err: UnexpectedDerivationMethod) -> Self { Self::UnexpectedDerivationMethod(err.to_string()) } -} - -impl From for WithdrawError { - fn from(err: MyAddressError) -> Self { Self::InternalError(err.to_string()) } -} - -impl From for UtxoRpcError { - fn from(err: MyAddressError) -> Self { Self::Internal(err.to_string()) } -} - -impl From for DelegationError { - fn from(err: MyAddressError) -> Self { Self::InternalError(err.to_string()) } -} - -impl From for TxHistoryError { - fn from(err: MyAddressError) -> Self { Self::InternalError(err.to_string()) } -} - -impl From for MyTxHistoryErrorV2 { - fn from(err: MyAddressError) -> Self { Self::Internal(err.to_string()) } -} diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 83a7f9d77f..84431b7f12 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -26,13 +26,15 @@ use crate::eth::web3_transport::websocket_transport::{WebsocketTransport, Websoc use crate::lp_price::get_base_price_in_rel; use crate::nft::nft_structs::{ContractType, ConvertChain, NftInfo, TransactionNftDetails, WithdrawErc1155, WithdrawErc721}; -use crate::{DexFee, RpcCommonOps, ValidateWatcherSpendInput, WatcherSpendType}; +use crate::{DexFee, MakerNftSwapOpsV2, ParseCoinAssocTypes, ParseNftAssocTypes, RefundMakerPaymentArgs, RpcCommonOps, + SendNftMakerPaymentArgs, SpendNftMakerPaymentArgs, ToBytes, ValidateNftMakerPaymentArgs, + ValidateWatcherSpendInput, WatcherSpendType}; use async_trait::async_trait; use bitcrypto::{dhash160, keccak256, ripemd160, sha256}; use common::custom_futures::repeatable::{Ready, Retry, RetryOnError}; use common::custom_futures::timeout::FutureTimerExt; -use common::executor::{abortable_queue::AbortableQueue, AbortableSystem, AbortedError, Timer}; -use common::executor::{AbortSettings, SpawnAbortable}; +use common::executor::{abortable_queue::AbortableQueue, AbortSettings, AbortableSystem, AbortedError, SpawnAbortable, + Timer}; use common::log::{debug, error, info, warn}; use common::number_type_casting::SafeTypeCastingNumbers; use common::{get_utc_timestamp, now_sec, small_rng, DEX_FEE_ADDR_RAW_PUBKEY}; @@ -46,19 +48,19 @@ use ethabi::{Contract, Function, Token}; pub use ethcore_transaction::SignedTransaction as SignedEthTx; use ethcore_transaction::{Action, Transaction as UnSignedEthTx, UnverifiedTransaction}; use ethereum_types::{Address, H160, H256, U256}; -use ethkey::{public_to_address, KeyPair, Public, Signature}; -use ethkey::{sign, verify_address}; +use ethkey::{public_to_address, sign, verify_address, KeyPair, Public, Signature}; use futures::compat::Future01CompatExt; use futures::future::{join_all, select_ok, try_join_all, Either, FutureExt, TryFutureExt}; use futures01::Future; use http::{StatusCode, Uri}; use instant::Instant; +use keys::Public as HtlcPubKey; use mm2_core::mm_ctx::{MmArc, MmWeak}; use mm2_err_handle::prelude::*; use mm2_event_stream::behaviour::{EventBehaviour, EventInitStatus}; use mm2_net::transport::{slurp_url, GuiAuthValidation, GuiAuthValidationGenerator, SlurpError}; use mm2_number::bigdecimal_custom::CheckedDivision; -use mm2_number::{BigDecimal, MmNumber}; +use mm2_number::{BigDecimal, BigUint, MmNumber}; use mm2_rpc::data::legacy::GasStationPricePolicy; #[cfg(test)] use mocktopus::macros::*; use rand::seq::SliceRandom; @@ -71,6 +73,7 @@ use std::collections::HashMap; use std::convert::{TryFrom, TryInto}; use std::ops::Deref; #[cfg(not(target_arch = "wasm32"))] use std::path::PathBuf; +use std::str::from_utf8; use std::str::FromStr; use std::sync::atomic::{AtomicU64, Ordering as AtomicOrdering}; use std::sync::{Arc, Mutex}; @@ -108,20 +111,22 @@ use super::{coin_conf, lp_coinfind_or_err, AsyncMutex, BalanceError, BalanceFut, 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}; pub use rlp; +use rlp::{DecoderError, Encodable, RlpStream}; mod eth_balance_events; mod eth_rpc; #[cfg(test)] mod eth_tests; #[cfg(target_arch = "wasm32")] mod eth_wasm_tests; +pub(crate) mod nft_swap_v2; mod web3_transport; #[path = "eth/v2_activation.rs"] pub mod v2_activation; -use crate::nft::WithdrawNftResult; use v2_activation::{build_address_and_priv_key_policy, EthActivationV2Error}; mod nonce; use crate::coin_errors::ValidatePaymentResult; -use crate::nft::nft_errors::GetNftInfoError; +use crate::nft::nft_errors::{GetNftInfoError, ParseContractTypeError}; +use crate::nft::WithdrawNftResult; use crate::{PrivKeyPolicy, TransactionResult, WithdrawFrom}; use nonce::ParityNonce; @@ -136,6 +141,8 @@ pub const ERC20_ABI: &str = include_str!("eth/erc20_abi.json"); 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"); + /// Payment states from etomic swap smart contract: https://github.com/artemii235/etomic-swap/blob/master/contracts/EtomicSwap.sol#L5 pub enum PaymentState { Uninitialized, @@ -143,6 +150,24 @@ pub enum PaymentState { Spent, Refunded, } + +#[allow(dead_code)] +pub(crate) enum MakerPaymentStateV2 { + Uninitialized, + PaymentSent, + TakerSpent, + MakerRefunded, +} + +#[allow(dead_code)] +pub(crate) enum TakerPaymentStateV2 { + Uninitialized, + PaymentSent, + TakerApproved, + MakerSpent, + TakerRefunded, +} + // Ethgasstation API returns response in 10^8 wei units. So 10 from their API mean 1 gwei const ETH_GAS_STATION_DECIMALS: u8 = 8; const GAS_PRICE_PERCENT: u64 = 10; @@ -168,7 +193,7 @@ const GAS_PRICE_APPROXIMATION_PERCENT_ON_ORDER_ISSUE: u64 = 5; /// - it may increase by 3% during the swap. const GAS_PRICE_APPROXIMATION_PERCENT_ON_TRADE_PREIMAGE: u64 = 7; -const ETH_GAS: u64 = 150_000; +pub(crate) const ETH_GAS: u64 = 150_000; /// Lifetime of generated signed message for gui-auth requests const GUI_AUTH_SIGNED_MESSAGE_LIFETIME_SEC: i64 = 90; @@ -178,6 +203,7 @@ lazy_static! { pub static ref ERC20_CONTRACT: Contract = Contract::load(ERC20_ABI.as_bytes()).unwrap(); 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 type Web3RpcFut = Box> + Send>; @@ -186,22 +212,19 @@ pub type GasStationResult = Result>; type EthPrivKeyPolicy = PrivKeyPolicy; type GasDetails = (U256, U256); -#[derive(Debug, Display)] +#[derive(Debug, Display, EnumFromStringify)] pub enum GasStationReqErr { #[display(fmt = "Transport '{}' error: {}", uri, error)] Transport { uri: String, error: String, }, + #[from_stringify("serde_json::Error")] #[display(fmt = "Invalid response: {}", _0)] InvalidResponse(String), Internal(String), } -impl From for GasStationReqErr { - fn from(e: serde_json::Error) -> Self { GasStationReqErr::InvalidResponse(e.to_string()) } -} - impl From for GasStationReqErr { fn from(e: SlurpError) -> Self { let error = e.to_string(); @@ -215,10 +238,11 @@ impl From for GasStationReqErr { } } -#[derive(Debug, Display)] +#[derive(Debug, Display, EnumFromStringify)] pub enum Web3RpcError { #[display(fmt = "Transport: {}", _0)] Transport(String), + #[from_stringify("serde_json::Error")] #[display(fmt = "Invalid response: {}", _0)] InvalidResponse(String), #[display(fmt = "Timeout: {}", _0)] @@ -239,10 +263,6 @@ impl From for Web3RpcError { } } -impl From for Web3RpcError { - fn from(e: serde_json::Error) -> Self { Web3RpcError::InvalidResponse(e.to_string()) } -} - impl From for Web3RpcError { fn from(e: web3::Error) -> Self { let error_str = e.to_string(); @@ -258,10 +278,6 @@ impl From for Web3RpcError { } } -impl From for RawTransactionError { - fn from(e: web3::Error) -> Self { RawTransactionError::Transport(e.to_string()) } -} - impl From for RawTransactionError { fn from(e: Web3RpcError) -> Self { match e { @@ -601,7 +617,7 @@ impl EthCoinImpl { } /// The id used to differentiate payments on Etomic swap smart contract - fn etomic_swap_id(&self, time_lock: u32, secret_hash: &[u8]) -> Vec { + pub(crate) fn etomic_swap_id(&self, time_lock: u32, secret_hash: &[u8]) -> Vec { let mut input = vec![]; input.extend_from_slice(&time_lock.to_le_bytes()); input.extend_from_slice(secret_hash); @@ -2260,7 +2276,7 @@ impl MarketCoinOps for EthCoin { EthCoinType::Eth => get_function_name("ethPayment", args.watcher_reward), EthCoinType::Erc20 { .. } => get_function_name("erc20Payment", args.watcher_reward), EthCoinType::Nft { .. } => { - return Box::new(futures01::future::err(TransactionErr::NftProtocolNotSupported(ERRL!( + return Box::new(futures01::future::err(TransactionErr::ProtocolNotSupported(ERRL!( "Nft Protocol is not supported yet!" )))) }, @@ -3420,7 +3436,7 @@ impl EthCoin { #[cfg_attr(test, mockable)] impl EthCoin { - fn sign_and_send_transaction(&self, value: U256, action: Action, data: Vec, gas: U256) -> EthTxFut { + pub(crate) fn sign_and_send_transaction(&self, value: U256, action: Action, data: Vec, gas: U256) -> EthTxFut { let ctx = try_tx_fus!(MmArc::from_weak(&self.ctx).ok_or("!ctx")); let coin = self.clone(); let fut = async move { @@ -3453,7 +3469,7 @@ impl EthCoin { self.sign_and_send_transaction(0.into(), Action::Call(*token_addr), data, U256::from(210_000)) }, EthCoinType::Nft { .. } => { - return Box::new(futures01::future::err(TransactionErr::NftProtocolNotSupported(ERRL!( + return Box::new(futures01::future::err(TransactionErr::ProtocolNotSupported(ERRL!( "Nft Protocol is not supported yet!" )))) }, @@ -3618,7 +3634,7 @@ impl EthCoin { })) }, EthCoinType::Nft { .. } => { - return Box::new(futures01::future::err(TransactionErr::NftProtocolNotSupported(ERRL!( + return Box::new(futures01::future::err(TransactionErr::ProtocolNotSupported(ERRL!( "Nft Protocol is not supported yet!" )))) }, @@ -3739,7 +3755,7 @@ impl EthCoin { ) }, EthCoinType::Nft { .. } => { - return Box::new(futures01::future::err(TransactionErr::NftProtocolNotSupported(ERRL!( + return Box::new(futures01::future::err(TransactionErr::ProtocolNotSupported(ERRL!( "Nft Protocol is not supported yet!" )))) }, @@ -3864,7 +3880,7 @@ impl EthCoin { ) }, EthCoinType::Nft { .. } => { - return Box::new(futures01::future::err(TransactionErr::NftProtocolNotSupported(ERRL!( + return Box::new(futures01::future::err(TransactionErr::ProtocolNotSupported(ERRL!( "Nft Protocol is not supported yet!" )))) }, @@ -3986,8 +4002,8 @@ impl EthCoin { ) }, EthCoinType::Nft { .. } => { - return Box::new(futures01::future::err(TransactionErr::NftProtocolNotSupported(ERRL!( - "Nft Protocol is not supported yet!" + return Box::new(futures01::future::err(TransactionErr::ProtocolNotSupported(ERRL!( + "Nft Protocol is not supported!" )))) }, } @@ -4109,7 +4125,7 @@ impl EthCoin { ) }, EthCoinType::Nft { .. } => { - return Box::new(futures01::future::err(TransactionErr::NftProtocolNotSupported(ERRL!( + return Box::new(futures01::future::err(TransactionErr::ProtocolNotSupported(ERRL!( "Nft Protocol is not supported yet!" )))) }, @@ -4276,7 +4292,12 @@ impl EthCoin { Box::new(fut.boxed().compat().map_to_mm_fut(BalanceError::from)) } - async fn call_request(&self, to: Address, value: Option, data: Option) -> Result { + pub(crate) async fn call_request( + &self, + to: Address, + value: Option, + data: Option, + ) -> Result { let request = CallRequest { from: Some(self.my_address), to: Some(to), @@ -4359,7 +4380,7 @@ impl EthCoin { EthCoinType::Eth => return TX_PLAIN_ERR!("'approve' is expected to be call for ERC20 coins only"), EthCoinType::Erc20 { token_addr, .. } => token_addr, EthCoinType::Nft { .. } => { - return Err(TransactionErr::NftProtocolNotSupported(ERRL!( + return Err(TransactionErr::ProtocolNotSupported(ERRL!( "Nft Protocol is not supported yet!" ))) }, @@ -5895,7 +5916,8 @@ pub async fn eth_coin_from_conf_and_request( }; (EthCoinType::Erc20 { platform, token_addr }, decimals) }, - _ => return ERR!("Expect ETH or ERC20 protocol"), + CoinProtocol::NFT { platform } => (EthCoinType::Nft { platform }, ETH_DECIMALS), + _ => return ERR!("Expect ETH, ERC20 or NFT protocol"), }; // param from request should override the config @@ -5926,8 +5948,7 @@ pub async fn eth_coin_from_conf_and_request( let key_lock = match &coin_type { EthCoinType::Eth => String::from(ticker), - EthCoinType::Erc20 { ref platform, .. } => String::from(platform), - EthCoinType::Nft { .. } => return ERR!("Does not support NFT protocol"), + EthCoinType::Erc20 { platform, .. } | EthCoinType::Nft { platform } => String::from(platform), }; let nonce_lock = { @@ -6003,7 +6024,7 @@ pub fn checksum_address(addr: &str) -> String { /// `eth_addr_to_hex` converts Address to hex format. /// Note: the result will be in lowercase. -pub(crate) fn eth_addr_to_hex(address: &Address) -> String { format!("{:#02x}", address) } +pub fn eth_addr_to_hex(address: &Address) -> String { format!("{:#02x}", address) } /// Checks that input is valid mixed-case checksum form address /// The input must be 0x prefixed hex string @@ -6179,3 +6200,166 @@ async fn get_eth_gas_details( }, } } + +impl ToBytes for Signature { + fn to_bytes(&self) -> Vec { self.to_vec() } +} + +impl ToBytes for SignedEthTx { + fn to_bytes(&self) -> Vec { + let mut stream = RlpStream::new(); + self.rlp_append(&mut stream); + // Handle potential panicking. + if stream.is_finished() { + Vec::from(stream.out()) + } else { + // TODO: Consider returning Result, Error> in future refactoring for better error handling. + warn!("RlpStream was not finished; returning an empty Vec as a fail-safe."); + vec![] + } + } +} + +#[derive(Debug, Display)] +pub enum EthAssocTypesError { + InvalidHexString(String), + TxParseError(String), + ParseSignatureError(String), + KeysError(keys::Error), +} + +impl From for EthAssocTypesError { + fn from(e: DecoderError) -> Self { EthAssocTypesError::TxParseError(e.to_string()) } +} + +impl From for EthAssocTypesError { + fn from(e: keys::Error) -> Self { EthAssocTypesError::KeysError(e) } +} + +#[derive(Debug, Display)] +pub enum EthNftAssocTypesError { + Utf8Error(String), + ParseContractTypeError(ParseContractTypeError), + ParseTokenContractError(String), +} + +impl From for EthNftAssocTypesError { + fn from(e: ParseContractTypeError) -> Self { EthNftAssocTypesError::ParseContractTypeError(e) } +} + +impl ParseCoinAssocTypes for EthCoin { + type Address = Address; + type AddressParseError = MmError; + type Pubkey = HtlcPubKey; + type PubkeyParseError = MmError; + type Tx = SignedEthTx; + type TxParseError = MmError; + type Preimage = SignedEthTx; + type PreimageParseError = MmError; + type Sig = Signature; + type SigParseError = MmError; + + fn my_addr(&self) -> &Self::Address { &self.my_address } + + fn parse_address(&self, address: &str) -> Result { + Address::from_str(address).map_to_mm(|e| EthAssocTypesError::InvalidHexString(e.to_string())) + } + + fn parse_pubkey(&self, pubkey: &[u8]) -> Result { + HtlcPubKey::from_slice(pubkey).map_to_mm(EthAssocTypesError::from) + } + + fn parse_tx(&self, tx: &[u8]) -> Result { + let unverified: UnverifiedTransaction = rlp::decode(tx).map_err(EthAssocTypesError::from)?; + SignedEthTx::new(unverified).map_to_mm(|e| EthAssocTypesError::TxParseError(e.to_string())) + } + + fn parse_preimage(&self, tx: &[u8]) -> Result { self.parse_tx(tx) } + + fn parse_signature(&self, sig: &[u8]) -> Result { + if sig.len() != 65 { + return MmError::err(EthAssocTypesError::ParseSignatureError( + "Signature slice is not 65 bytes long".to_string(), + )); + }; + + let mut arr = [0; 65]; + arr.copy_from_slice(sig); + Ok(Signature::from(arr)) // Assuming `Signature::from([u8; 65])` exists + } +} + +impl ToBytes for Address { + fn to_bytes(&self) -> Vec { self.0.to_vec() } +} + +impl ToBytes for BigUint { + fn to_bytes(&self) -> Vec { self.to_bytes_be() } +} + +impl ToBytes for ContractType { + fn to_bytes(&self) -> Vec { self.to_string().into_bytes() } +} + +impl ParseNftAssocTypes for EthCoin { + type ContractAddress = Address; + type TokenId = BigUint; + type ContractType = ContractType; + type NftAssocTypesError = MmError; + + fn parse_contract_address( + &self, + contract_address: &[u8], + ) -> Result { + contract_address + .try_to_address() + .map_to_mm(EthNftAssocTypesError::ParseTokenContractError) + } + + fn parse_token_id(&self, token_id: &[u8]) -> Result { + Ok(BigUint::from_bytes_be(token_id)) + } + + fn parse_contract_type(&self, contract_type: &[u8]) -> Result { + let contract_str = from_utf8(contract_type).map_err(|e| EthNftAssocTypesError::Utf8Error(e.to_string()))?; + ContractType::from_str(contract_str).map_to_mm(EthNftAssocTypesError::from) + } +} + +#[async_trait] +impl MakerNftSwapOpsV2 for EthCoin { + async fn send_nft_maker_payment_v2( + &self, + args: SendNftMakerPaymentArgs<'_, Self>, + ) -> Result { + self.send_nft_maker_payment_v2_impl(args).await + } + + async fn validate_nft_maker_payment_v2( + &self, + args: ValidateNftMakerPaymentArgs<'_, Self>, + ) -> ValidatePaymentResult<()> { + self.validate_nft_maker_payment_v2_impl(args).await + } + + async fn spend_nft_maker_payment_v2( + &self, + args: SpendNftMakerPaymentArgs<'_, Self>, + ) -> Result { + self.spend_nft_maker_payment_v2_impl(args).await + } + + async fn refund_nft_maker_payment_v2_timelock( + &self, + args: RefundPaymentArgs<'_>, + ) -> Result { + self.refund_nft_maker_payment_v2_timelock_impl(args).await + } + + async fn refund_nft_maker_payment_v2_secret( + &self, + _args: RefundMakerPaymentArgs<'_, Self>, + ) -> Result { + todo!() + } +} diff --git a/mm2src/coins/eth/nft_swap_contract_abi.json b/mm2src/coins/eth/nft_swap_contract_abi.json new file mode 100644 index 0000000000..fd17b4cd8c --- /dev/null +++ b/mm2src/coins/eth/nft_swap_contract_abi.json @@ -0,0 +1,898 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "feeAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "AddressInsufficientBalance", + "type": "error" + }, + { + "inputs": [], + "name": "FailedInnerCall", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "SafeERC20FailedOperation", + "type": "error" + }, + { + "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" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "TakerPaymentApproved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "secret", + "type": "bytes32" + } + ], + "name": "TakerPaymentRefundedSecret", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "TakerPaymentRefundedTimelock", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "TakerPaymentSent", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "secret", + "type": "bytes32" + } + ], + "name": "TakerPaymentSpent", + "type": "event" + }, + { + "inputs": [], + "name": "dexFeeAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dexFee", + "type": "uint256" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "takerSecretHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "makerSecretHash", + "type": "bytes32" + }, + { + "internalType": "uint32", + "name": "preApproveLockTime", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "paymentLockTime", + "type": "uint32" + } + ], + "name": "erc20TakerPayment", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "dexFee", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "takerSecretHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "makerSecretHash", + "type": "bytes32" + }, + { + "internalType": "uint32", + "name": "preApproveLockTime", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "paymentLockTime", + "type": "uint32" + } + ], + "name": "ethTakerPayment", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "makerPayments", + "outputs": [ + { + "internalType": "bytes20", + "name": "paymentHash", + "type": "bytes20" + }, + { + "internalType": "uint32", + "name": "paymentLockTime", + "type": "uint32" + }, + { + "internalType": "enum EtomicSwapNft.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": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dexFee", + "type": "uint256" + }, + { + "internalType": "address", + "name": "maker", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "takerSecret", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "makerSecretHash", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + } + ], + "name": "refundTakerPaymentSecret", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dexFee", + "type": "uint256" + }, + { + "internalType": "address", + "name": "maker", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "takerSecretHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "makerSecretHash", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + } + ], + "name": "refundTakerPaymentTimelock", + "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": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dexFee", + "type": "uint256" + }, + { + "internalType": "address", + "name": "taker", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "takerSecretHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "makerSecret", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + } + ], + "name": "spendTakerPayment", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dexFee", + "type": "uint256" + }, + { + "internalType": "address", + "name": "maker", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "takerSecretHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "makerSecretHash", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + } + ], + "name": "takerPaymentApprove", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "takerPayments", + "outputs": [ + { + "internalType": "bytes20", + "name": "paymentHash", + "type": "bytes20" + }, + { + "internalType": "uint32", + "name": "preApproveLockTime", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "paymentLockTime", + "type": "uint32" + }, + { + "internalType": "enum EtomicSwapNft.TakerPaymentState", + "name": "state", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/mm2src/coins/eth/nft_swap_v2/errors.rs b/mm2src/coins/eth/nft_swap_v2/errors.rs new file mode 100644 index 0000000000..e66cd437d0 --- /dev/null +++ b/mm2src/coins/eth/nft_swap_v2/errors.rs @@ -0,0 +1,41 @@ +use enum_derives::EnumFromStringify; + +#[derive(Debug, Display)] +pub(crate) enum Erc721FunctionError { + AbiError(String), + FunctionNotFound(String), +} + +#[derive(Debug, Display)] +pub(crate) enum HtlcParamsError { + WrongPaymentTx(String), + TxDeserializationError(String), +} + +#[derive(Debug, Display, EnumFromStringify)] +pub(crate) enum PaymentStatusErr { + #[from_stringify("ethabi::Error")] + #[display(fmt = "Abi error: {}", _0)] + AbiError(String), + #[from_stringify("web3::Error")] + #[display(fmt = "Transport error: {}", _0)] + Transport(String), + #[display(fmt = "Internal error: {}", _0)] + Internal(String), + #[display(fmt = "Tx deserialization error: {}", _0)] + TxDeserializationError(String), +} + +#[derive(Debug, Display, EnumFromStringify)] +pub(crate) enum PrepareTxDataError { + #[from_stringify("ethabi::Error")] + #[display(fmt = "Abi error: {}", _0)] + AbiError(String), + #[display(fmt = "Internal error: {}", _0)] + Internal(String), + Erc721FunctionError(Erc721FunctionError), +} + +impl From for PrepareTxDataError { + fn from(e: Erc721FunctionError) -> Self { Self::Erc721FunctionError(e) } +} diff --git a/mm2src/coins/eth/nft_swap_v2/mod.rs b/mm2src/coins/eth/nft_swap_v2/mod.rs new file mode 100644 index 0000000000..83488deada --- /dev/null +++ b/mm2src/coins/eth/nft_swap_v2/mod.rs @@ -0,0 +1,534 @@ +use crate::coin_errors::{ValidatePaymentError, ValidatePaymentResult}; +use ethabi::{Contract, Token}; +use ethcore_transaction::{Action, UnverifiedTransaction}; +use ethereum_types::{Address, U256}; +use futures::compat::Future01CompatExt; +use mm2_err_handle::prelude::{MapToMmResult, MmError, MmResult}; +use mm2_number::BigDecimal; +use std::convert::TryInto; +use web3::types::{Transaction as Web3Tx, TransactionId}; + +pub(crate) mod errors; +use errors::{Erc721FunctionError, HtlcParamsError, PaymentStatusErr, PrepareTxDataError}; +mod structs; +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, ETH_GAS, NFT_SWAP_CONTRACT}; +use crate::{ParseCoinAssocTypes, RefundPaymentArgs, SendNftMakerPaymentArgs, SpendNftMakerPaymentArgs, TransactionErr, + ValidateNftMakerPaymentArgs}; + +impl EthCoin { + pub(crate) async fn send_nft_maker_payment_v2_impl( + &self, + args: SendNftMakerPaymentArgs<'_, Self>, + ) -> Result { + try_tx_s!(validate_payment_args( + args.taker_secret_hash, + args.maker_secret_hash, + &args.amount, + args.nft_swap_info.contract_type + )); + let htlc_data = try_tx_s!(self.prepare_htlc_data(&args)); + + match &self.coin_type { + EthCoinType::Nft { .. } => { + let data = try_tx_s!(self.prepare_nft_maker_payment_v2_data(&args, htlc_data)); + self.sign_and_send_transaction( + 0.into(), + Action::Call(*args.nft_swap_info.token_address), + data, + U256::from(ETH_GAS), + ) + .compat() + .await + }, + EthCoinType::Eth | EthCoinType::Erc20 { .. } => Err(TransactionErr::ProtocolNotSupported( + "ETH and ERC20 Protocols are not supported for NFT Swaps".to_string(), + )), + } + } + + pub(crate) async fn validate_nft_maker_payment_v2_impl( + &self, + args: ValidateNftMakerPaymentArgs<'_, Self>, + ) -> ValidatePaymentResult<()> { + let contract_type = args.nft_swap_info.contract_type; + validate_payment_args( + args.taker_secret_hash, + args.maker_secret_hash, + &args.amount, + contract_type, + ) + .map_err(ValidatePaymentError::InternalError)?; + let etomic_swap_contract = args.nft_swap_info.swap_contract_address; + let token_address = args.nft_swap_info.token_address; + let maker_address = addr_from_raw_pubkey(args.maker_pub).map_to_mm(ValidatePaymentError::InternalError)?; + let time_lock_u32 = args + .time_lock + .try_into() + .map_err(ValidatePaymentError::TimelockOverflow)?; + let swap_id = self.etomic_swap_id(time_lock_u32, args.maker_secret_hash); + let maker_status = self + .payment_status_v2( + *etomic_swap_contract, + Token::FixedBytes(swap_id.clone()), + &NFT_SWAP_CONTRACT, + PaymentType::MakerPayments, + ) + .await?; + let tx_from_rpc = self + .transaction(TransactionId::Hash(args.maker_payment_tx.hash)) + .await?; + let tx_from_rpc = tx_from_rpc.as_ref().ok_or_else(|| { + ValidatePaymentError::TxDoesNotExist(format!( + "Didn't find provided tx {:?} on ETH node", + args.maker_payment_tx.hash + )) + })?; + validate_from_to_and_maker_status(tx_from_rpc, maker_address, *token_address, maker_status).await?; + match self.coin_type { + EthCoinType::Nft { .. } => { + let (decoded, index_bytes) = get_decoded_tx_data_and_index_bytes(contract_type, &tx_from_rpc.input.0)?; + + let amount = if matches!(contract_type, &ContractType::Erc1155) { + Some(args.amount.to_string()) + } else { + None + }; + + let validation_params = ValidationParams { + maker_address, + etomic_swap_contract: *etomic_swap_contract, + token_id: args.nft_swap_info.token_id, + amount, + }; + validate_decoded_data(&decoded, &validation_params)?; + + let taker_address = + addr_from_raw_pubkey(args.taker_pub).map_to_mm(ValidatePaymentError::InternalError)?; + let htlc_params = ExpectedHtlcParams { + swap_id, + taker_address, + token_address: *token_address, + taker_secret_hash: args.taker_secret_hash.to_vec(), + maker_secret_hash: args.maker_secret_hash.to_vec(), + time_lock: U256::from(args.time_lock), + }; + decode_and_validate_htlc_params(decoded, index_bytes, htlc_params)?; + }, + EthCoinType::Eth | EthCoinType::Erc20 { .. } => { + return MmError::err(ValidatePaymentError::InternalError( + "EthCoinType must be Nft".to_string(), + )) + }, + } + Ok(()) + } + + pub(crate) async fn spend_nft_maker_payment_v2_impl( + &self, + args: SpendNftMakerPaymentArgs<'_, Self>, + ) -> Result { + let etomic_swap_contract = args.swap_contract_address; + if args.maker_secret.len() != 32 { + return Err(TransactionErr::Plain(ERRL!("maker_secret must be 32 bytes"))); + } + let contract_type = args.contract_type; + let (decoded, index_bytes) = try_tx_s!(get_decoded_tx_data_and_index_bytes( + contract_type, + &args.maker_payment_tx.data + )); + + let (state, htlc_params) = try_tx_s!( + self.status_and_htlc_params_from_tx_data( + *etomic_swap_contract, + &NFT_SWAP_CONTRACT, + &decoded, + index_bytes, + PaymentType::MakerPayments, + ) + .await + ); + match self.coin_type { + EthCoinType::Nft { .. } => { + let data = try_tx_s!(self.prepare_spend_nft_maker_v2_data(&args, decoded, htlc_params, state)); + self.sign_and_send_transaction(0.into(), Action::Call(*etomic_swap_contract), data, U256::from(ETH_GAS)) + .compat() + .await + }, + EthCoinType::Eth | EthCoinType::Erc20 { .. } => Err(TransactionErr::ProtocolNotSupported( + "ETH and ERC20 Protocols are not supported for NFT Swaps".to_string(), + )), + } + } + + pub(crate) async fn refund_nft_maker_payment_v2_timelock_impl( + &self, + args: RefundPaymentArgs<'_>, + ) -> Result { + let _etomic_swap_contract = try_tx_s!(args.swap_contract_address.try_to_address()); + let tx: UnverifiedTransaction = try_tx_s!(rlp::decode(args.payment_tx)); + let _payment = try_tx_s!(SignedEthTx::new(tx)); + todo!() + } + + fn prepare_nft_maker_payment_v2_data( + &self, + args: &SendNftMakerPaymentArgs<'_, Self>, + htlc_data: Vec, + ) -> Result, PrepareTxDataError> { + match args.nft_swap_info.contract_type { + ContractType::Erc1155 => { + let function = ERC1155_CONTRACT.function("safeTransferFrom")?; + let amount_u256 = U256::from_dec_str(&args.amount.to_string()) + .map_err(|e| PrepareTxDataError::Internal(e.to_string()))?; + let data = function.encode_input(&[ + Token::Address(*self.my_addr()), + Token::Address(*args.nft_swap_info.swap_contract_address), + Token::Uint(U256::from(args.nft_swap_info.token_id)), + Token::Uint(amount_u256), + Token::Bytes(htlc_data), + ])?; + Ok(data) + }, + ContractType::Erc721 => { + let function = erc721_transfer_with_data()?; + let data = function.encode_input(&[ + Token::Address(*self.my_addr()), + Token::Address(*args.nft_swap_info.swap_contract_address), + Token::Uint(U256::from(args.nft_swap_info.token_id)), + Token::Bytes(htlc_data), + ])?; + Ok(data) + }, + } + } + + fn prepare_htlc_data(&self, args: &SendNftMakerPaymentArgs<'_, Self>) -> Result, PrepareTxDataError> { + let taker_address = + addr_from_raw_pubkey(args.taker_pub).map_err(|e| PrepareTxDataError::Internal(ERRL!("{}", e)))?; + let time_lock_u32 = args + .time_lock + .try_into() + .map_err(|e| PrepareTxDataError::Internal(ERRL!("{}", e)))?; + let id = self.etomic_swap_id(time_lock_u32, args.maker_secret_hash); + let encoded = ethabi::encode(&[ + Token::FixedBytes(id), + Token::Address(taker_address), + Token::Address(*args.nft_swap_info.token_address), + Token::FixedBytes(args.taker_secret_hash.to_vec()), + Token::FixedBytes(args.maker_secret_hash.to_vec()), + Token::Uint(U256::from(time_lock_u32)), + ]); + Ok(encoded) + } + + /// Retrieves the payment status from a given smart contract address based on the swap ID and state type. + async fn payment_status_v2( + &self, + swap_address: Address, + swap_id: Token, + contract_abi: &Contract, + state_type: PaymentType, + ) -> Result { + let function_name = state_type.as_str(); + let function = contract_abi.function(function_name)?; + let data = function.encode_input(&[swap_id])?; + let bytes = self.call_request(swap_address, None, Some(data.into())).await?; + let decoded_tokens = function.decode_output(&bytes.0)?; + let state = decoded_tokens + .get(2) + .ok_or_else(|| PaymentStatusErr::Internal(ERRL!("Payment status must contain 'state' as the 2nd token")))?; + match state { + Token::Uint(state) => Ok(*state), + _ => Err(PaymentStatusErr::Internal(ERRL!( + "Payment status must be Uint, got {:?}", + state + ))), + } + } + + /// Prepares the encoded transaction data for spending a maker's NFT payment on the blockchain. + /// + /// This function selects the appropriate contract function based on the NFT's contract type (ERC1155 or ERC721) + /// and encodes the input parameters required for the blockchain transaction. + fn prepare_spend_nft_maker_v2_data( + &self, + args: &SpendNftMakerPaymentArgs<'_, Self>, + decoded: Vec, + htlc_params: Vec, + 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")?, + }; + + if state != U256::from(MakerPaymentStateV2::PaymentSent as u8) { + return Err(PrepareTxDataError::Internal(ERRL!( + "Payment {:?} state is not PAYMENT_STATE_SENT, got {}", + args.maker_payment_tx, + state + ))); + } + + let input_tokens = match args.contract_type { + ContractType::Erc1155 => vec![ + htlc_params[0].clone(), // swap_id + Token::Address(args.maker_payment_tx.sender()), + Token::FixedBytes(args.taker_secret_hash.to_vec()), + Token::FixedBytes(args.maker_secret.to_vec()), + htlc_params[2].clone(), // tokenAddress + decoded[2].clone(), // tokenId + decoded[3].clone(), // amount + ], + ContractType::Erc721 => vec![ + htlc_params[0].clone(), // swap_id + Token::Address(args.maker_payment_tx.sender()), + Token::FixedBytes(args.taker_secret_hash.to_vec()), + Token::FixedBytes(args.maker_secret.to_vec()), + htlc_params[2].clone(), // tokenAddress + decoded[2].clone(), // tokenId + ], + }; + + let data = spend_func.encode_input(&input_tokens)?; + Ok(data) + } + + async fn status_and_htlc_params_from_tx_data( + &self, + swap_address: Address, + contract_abi: &Contract, + decoded_data: &[Token], + index: usize, + state_type: PaymentType, + ) -> Result<(U256, Vec), PaymentStatusErr> { + let data_bytes = match decoded_data.get(index) { + Some(Token::Bytes(data_bytes)) => data_bytes, + _ => { + return Err(PaymentStatusErr::TxDeserializationError(ERRL!( + "Failed to decode HTLCParams from data_bytes" + ))) + }, + }; + + let htlc_params = match ethabi::decode(htlc_params(), data_bytes) { + Ok(htlc_params) => htlc_params, + Err(_) => { + return Err(PaymentStatusErr::TxDeserializationError(ERRL!( + "Failed to decode HTLCParams from data_bytes" + ))) + }, + }; + + let state = self + .payment_status_v2(swap_address, htlc_params[0].clone(), contract_abi, state_type) + .await?; + + Ok((state, htlc_params)) + } +} + +/// Validates decoded data from tx input, related to `safeTransferFrom` contract call +fn validate_decoded_data(decoded: &[Token], params: &ValidationParams) -> Result<(), MmError> { + let checks = vec![ + (0, Token::Address(params.maker_address), "maker_address"), + (1, Token::Address(params.etomic_swap_contract), "etomic_swap_contract"), + (2, Token::Uint(U256::from(params.token_id)), "token_id"), + ]; + + for (index, expected_token, field_name) in checks { + if decoded.get(index) != Some(&expected_token) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "NFT Maker Payment `{}` {:?} is invalid, expected {:?}", + field_name, + decoded.get(index), + expected_token + ))); + } + } + if let Some(amount) = ¶ms.amount { + let value = U256::from_dec_str(amount).map_to_mm(|e| ValidatePaymentError::InternalError(e.to_string()))?; + if decoded[3] != Token::Uint(value) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "NFT Maker Payment `amount` {:?} is invalid, expected {:?}", + decoded[3], + Token::Uint(value) + ))); + } + } + Ok(()) +} + +fn decode_and_validate_htlc_params( + decoded: Vec, + index: usize, + expected_params: ExpectedHtlcParams, +) -> MmResult<(), HtlcParamsError> { + let data_bytes = match decoded.get(index) { + Some(Token::Bytes(bytes)) => bytes, + _ => { + return MmError::err(HtlcParamsError::TxDeserializationError( + "Expected Bytes for HTLCParams data".to_string(), + )) + }, + }; + + let decoded_params = match ethabi::decode(htlc_params(), data_bytes) { + Ok(params) => params, + Err(_) => { + return MmError::err(HtlcParamsError::TxDeserializationError( + "Failed to decode HTLCParams from data_bytes".to_string(), + )) + }, + }; + + let expected_taker_secret_hash = Token::FixedBytes(expected_params.taker_secret_hash.clone()); + let expected_maker_secret_hash = Token::FixedBytes(expected_params.maker_secret_hash.clone()); + + let checks = vec![ + (0, Token::FixedBytes(expected_params.swap_id.clone()), "swap_id"), + (1, Token::Address(expected_params.taker_address), "taker_address"), + (2, Token::Address(expected_params.token_address), "token_address"), + (3, expected_taker_secret_hash, "taker_secret_hash"), + (4, expected_maker_secret_hash, "maker_secret_hash"), + (5, Token::Uint(expected_params.time_lock), "time_lock"), + ]; + + for (index, expected_token, param_name) in checks.into_iter() { + if decoded_params[index] != expected_token { + return MmError::err(HtlcParamsError::WrongPaymentTx(format!( + "Invalid '{}' {:?}, expected {:?}", + param_name, decoded_params[index], expected_token + ))); + } + } + + Ok(()) +} + +/// Representation of the Solidity HTLCParams struct. +/// +/// struct HTLCParams { +/// bytes32 id; +/// address taker; +/// address tokenAddress; +/// bytes32 takerSecretHash; +/// bytes32 makerSecretHash; +/// uint32 paymentLockTime; +/// } +fn htlc_params() -> &'static [ethabi::ParamType] { + &[ + ethabi::ParamType::FixedBytes(32), + ethabi::ParamType::Address, + ethabi::ParamType::Address, + ethabi::ParamType::FixedBytes(32), + ethabi::ParamType::FixedBytes(32), + ethabi::ParamType::Uint(256), + ] +} + +/// function to check if BigDecimal is a positive integer +#[inline(always)] +fn is_positive_integer(amount: &BigDecimal) -> bool { amount == &amount.with_scale(0) && amount > &BigDecimal::from(0) } + +fn validate_payment_args<'a>( + taker_secret_hash: &'a [u8], + maker_secret_hash: &'a [u8], + amount: &BigDecimal, + contract_type: &ContractType, +) -> Result<(), String> { + match contract_type { + ContractType::Erc1155 => { + if !is_positive_integer(amount) { + return Err("ERC-1155 amount must be a positive integer".to_string()); + } + }, + ContractType::Erc721 => { + if amount != &BigDecimal::from(1) { + return Err("ERC-721 amount must be 1".to_string()); + } + }, + } + if taker_secret_hash.len() != 32 { + return Err("taker_secret_hash must be 32 bytes".to_string()); + } + if maker_secret_hash.len() != 32 { + return Err("maker_secret_hash must be 32 bytes".to_string()); + } + + Ok(()) +} + +async fn validate_from_to_and_maker_status( + tx_from_rpc: &Web3Tx, + expected_from: Address, + expected_to: Address, + maker_status: U256, +) -> ValidatePaymentResult<()> { + if maker_status != U256::from(MakerPaymentStateV2::PaymentSent as u8) { + return MmError::err(ValidatePaymentError::UnexpectedPaymentState(format!( + "NFT Maker Payment state is not PAYMENT_STATE_SENT, got {}", + maker_status + ))); + } + if tx_from_rpc.from != Some(expected_from) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "NFT Maker Payment tx {:?} was sent from wrong address, expected {:?}", + tx_from_rpc, expected_from + ))); + } + // As NFT owner calls "safeTransferFrom" directly, then in Transaction 'to' field we expect token_address + if tx_from_rpc.to != Some(expected_to) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "NFT Maker Payment tx {:?} was sent to wrong address, expected {:?}", + tx_from_rpc, expected_to, + ))); + } + Ok(()) +} + +/// Identifies the correct "safeTransferFrom" function based on the contract type (either ERC1155 or ERC721) +/// and decodes the provided contract call bytes using the ABI of the identified function. Additionally, it returns +/// the index position of the "bytes" field within the function's parameters. +pub(crate) fn get_decoded_tx_data_and_index_bytes( + contract_type: &ContractType, + contract_call_bytes: &[u8], +) -> Result<(Vec, usize), PrepareTxDataError> { + let (send_func, index_bytes) = match contract_type { + ContractType::Erc1155 => (ERC1155_CONTRACT.function("safeTransferFrom")?, 4), + ContractType::Erc721 => (erc721_transfer_with_data()?, 3), + }; + let decoded = decode_contract_call(send_func, contract_call_bytes)?; + Ok((decoded, index_bytes)) +} + +/// ERC721 contract has overloaded versions of the `safeTransferFrom` function, +/// but `Contract::function` method returns only the first if there are overloaded versions of the same function. +/// Provided function retrieves the `safeTransferFrom` variant that includes a `bytes` parameter. +/// This variant is specifically used for transferring ERC721 tokens with additional data. +fn erc721_transfer_with_data<'a>() -> Result<&'a ethabi::Function, Erc721FunctionError> { + let functions = ERC721_CONTRACT + .functions_by_name("safeTransferFrom") + .map_err(|e| Erc721FunctionError::AbiError(ERRL!("{}", e)))?; + + // Find the correct function variant by inspecting the input parameters. + let function = functions + .iter() + .find(|f| { + f.inputs.len() == 4 + && matches!( + f.inputs.last().map(|input| &input.kind), + Some(ðabi::ParamType::Bytes) + ) + }) + .ok_or_else(|| { + Erc721FunctionError::FunctionNotFound( + "Failed to find the correct safeTransferFrom function variant".to_string(), + ) + })?; + Ok(function) +} diff --git a/mm2src/coins/eth/nft_swap_v2/structs.rs b/mm2src/coins/eth/nft_swap_v2/structs.rs new file mode 100644 index 0000000000..a0c129bf4b --- /dev/null +++ b/mm2src/coins/eth/nft_swap_v2/structs.rs @@ -0,0 +1,33 @@ +use ethereum_types::{Address, U256}; + +pub(crate) struct ExpectedHtlcParams { + pub(crate) swap_id: Vec, + pub(crate) taker_address: Address, + pub(crate) token_address: Address, + pub(crate) taker_secret_hash: Vec, + pub(crate) maker_secret_hash: Vec, + pub(crate) time_lock: U256, +} + +pub(crate) struct ValidationParams<'a> { + pub(crate) maker_address: Address, + pub(crate) etomic_swap_contract: Address, + pub(crate) token_id: &'a [u8], + // Optional, as it's not needed for ERC721 + pub(crate) amount: Option, +} + +#[allow(dead_code)] +pub(crate) enum PaymentType { + MakerPayments, + TakerPayments, +} + +impl PaymentType { + pub(crate) fn as_str(&self) -> &'static str { + match self { + PaymentType::MakerPayments => "makerPayments", + PaymentType::TakerPayments => "takerPayments", + } + } +} diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 5179a5db80..fd6b20c80b 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -341,13 +341,14 @@ pub const INVALID_SWAP_ID_ERR_LOG: &str = "Invalid swap id"; pub const INVALID_SCRIPT_ERR_LOG: &str = "Invalid script"; pub const INVALID_REFUND_TX_ERR_LOG: &str = "Invalid refund transaction"; -#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] +#[derive(Debug, Deserialize, Display, EnumFromStringify, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] pub enum RawTransactionError { #[display(fmt = "No such coin {}", coin)] NoSuchCoin { coin: String }, #[display(fmt = "Invalid hash: {}", _0)] InvalidHashError(String), + #[from_stringify("web3::Error")] #[display(fmt = "Transport error: {}", _0)] Transport(String), #[display(fmt = "Hash does not exist: {}", _0)] @@ -356,6 +357,7 @@ pub enum RawTransactionError { InternalError(String), #[display(fmt = "Transaction decode error: {}", _0)] DecodeError(String), + #[from_stringify("NumConversError", "FromHexError")] #[display(fmt = "Invalid param: {}", _0)] InvalidParam(String), #[display(fmt = "Non-existent previous output: {}", _0)] @@ -395,14 +397,6 @@ impl From for RawTransactionError { } } -impl From for RawTransactionError { - fn from(e: NumConversError) -> Self { RawTransactionError::InvalidParam(e.to_string()) } -} - -impl From for RawTransactionError { - fn from(e: FromHexError) -> Self { RawTransactionError::InvalidParam(e.to_string()) } -} - #[derive(Clone, Debug, Deserialize, Display, EnumFromStringify, PartialEq, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] pub enum GetMyAddressError { @@ -519,7 +513,7 @@ pub struct MyWalletAddress { pub type SignatureResult = Result>; pub type VerificationResult = Result>; -#[derive(Debug, Display)] +#[derive(Debug, Display, EnumFromStringify)] pub enum TxHistoryError { ErrorSerializing(String), ErrorDeserializing(String), @@ -531,6 +525,7 @@ pub enum TxHistoryError { internal_id: BytesJson, }, NotSupported(String), + #[from_stringify("MyAddressError")] InternalError(String), } @@ -633,14 +628,15 @@ pub enum TxMarshalingErr { Internal(String), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, EnumFromStringify)] #[allow(clippy::large_enum_variant)] pub enum TransactionErr { /// Keeps transactions while throwing errors. TxRecoverable(TransactionEnum, String), /// Simply for plain error messages. + #[from_stringify("keys::Error")] Plain(String), - NftProtocolNotSupported(String), + ProtocolNotSupported(String), } impl TransactionErr { @@ -658,16 +654,11 @@ impl TransactionErr { pub fn get_plain_text_format(&self) -> String { match self { TransactionErr::TxRecoverable(_, err) => err.to_string(), - TransactionErr::Plain(err) => err.to_string(), - TransactionErr::NftProtocolNotSupported(err) => err.to_string(), + TransactionErr::Plain(err) | TransactionErr::ProtocolNotSupported(err) => err.to_string(), } } } -impl From for TransactionErr { - fn from(e: keys::Error) -> Self { TransactionErr::Plain(e.to_string()) } -} - #[derive(Debug, PartialEq)] pub enum FoundSwapTxSpend { Spent(TransactionEnum), @@ -1010,17 +1001,14 @@ pub struct PaymentInstructionArgs<'a> { pub wait_until: u64, } -#[derive(Display)] +#[derive(Display, EnumFromStringify)] pub enum PaymentInstructionsErr { LightningInvoiceErr(String), WatcherRewardErr(String), + #[from_stringify("NumConversError")] InternalError(String), } -impl From for PaymentInstructionsErr { - fn from(e: NumConversError) -> Self { PaymentInstructionsErr::InternalError(e.to_string()) } -} - #[derive(Display)] pub enum ValidateInstructionsErr { ValidateLightningInvoiceErr(String), @@ -1250,7 +1238,7 @@ pub struct SendTakerFundingArgs<'a> { } /// Helper struct wrapping arguments for [TakerCoinSwapOpsV2::refund_taker_funding_secret] -pub struct RefundFundingSecretArgs<'a, Coin: CoinAssocTypes + ?Sized> { +pub struct RefundFundingSecretArgs<'a, Coin: ParseCoinAssocTypes + ?Sized> { pub funding_tx: &'a Coin::Tx, pub time_lock: u64, pub maker_pubkey: &'a Coin::Pubkey, @@ -1262,7 +1250,7 @@ pub struct RefundFundingSecretArgs<'a, Coin: CoinAssocTypes + ?Sized> { } /// Helper struct wrapping arguments for [TakerCoinSwapOpsV2::gen_taker_funding_spend_preimage] -pub struct GenTakerFundingSpendArgs<'a, Coin: CoinAssocTypes + ?Sized> { +pub struct GenTakerFundingSpendArgs<'a, Coin: ParseCoinAssocTypes + ?Sized> { /// Taker payment transaction serialized to raw bytes pub funding_tx: &'a Coin::Tx, /// Maker's pubkey @@ -1280,7 +1268,7 @@ pub struct GenTakerFundingSpendArgs<'a, Coin: CoinAssocTypes + ?Sized> { } /// Helper struct wrapping arguments for [TakerCoinSwapOpsV2::validate_taker_funding] -pub struct ValidateTakerFundingArgs<'a, Coin: CoinAssocTypes + ?Sized> { +pub struct ValidateTakerFundingArgs<'a, Coin: ParseCoinAssocTypes + ?Sized> { /// Taker funding transaction pub funding_tx: &'a Coin::Tx, /// Taker will be able to refund the payment after this timestamp @@ -1302,7 +1290,7 @@ pub struct ValidateTakerFundingArgs<'a, Coin: CoinAssocTypes + ?Sized> { /// Helper struct wrapping arguments for taker payment's spend generation, used in /// [TakerCoinSwapOpsV2::gen_taker_payment_spend_preimage], [TakerCoinSwapOpsV2::validate_taker_payment_spend_preimage] and /// [TakerCoinSwapOpsV2::sign_and_broadcast_taker_payment_spend] -pub struct GenTakerPaymentSpendArgs<'a, Coin: CoinAssocTypes + ?Sized> { +pub struct GenTakerPaymentSpendArgs<'a, Coin: ParseCoinAssocTypes + ?Sized> { /// Taker payment transaction serialized to raw bytes pub taker_tx: &'a Coin::Tx, /// Taker will be able to refund the payment after this timestamp @@ -1326,7 +1314,7 @@ pub struct GenTakerPaymentSpendArgs<'a, Coin: CoinAssocTypes + ?Sized> { } /// Taker payment spend preimage with taker's signature -pub struct TxPreimageWithSig { +pub struct TxPreimageWithSig { /// The preimage, might be () for certain coin types (only signature might be used) pub preimage: Coin::Preimage, /// Taker's signature @@ -1397,7 +1385,7 @@ impl From for ValidateSwapV2TxError { } /// Enum covering error cases that can happen during taker funding spend preimage validation. -#[derive(Debug, Display)] +#[derive(Debug, Display, EnumFromStringify)] pub enum ValidateTakerFundingSpendPreimageError { /// Funding tx has no outputs FundingTxNoOutputs, @@ -1408,37 +1396,30 @@ pub enum ValidateTakerFundingSpendPreimageError { /// Error during preimage comparison to an expected one. InvalidPreimage(String), /// Error during taker's signature check. + #[from_stringify("UtxoSignWithKeyPairError")] SignatureVerificationFailure(String), /// Error during generation of an expected preimage. TxGenError(String), /// Input payment timelock overflows the type used by specific coin. LocktimeOverflow(String), /// Coin's RPC error + #[from_stringify("UtxoRpcError")] Rpc(String), } -impl From for ValidateTakerFundingSpendPreimageError { - fn from(err: UtxoSignWithKeyPairError) -> Self { - ValidateTakerFundingSpendPreimageError::SignatureVerificationFailure(err.to_string()) - } -} - impl From for ValidateTakerFundingSpendPreimageError { fn from(err: TxGenError) -> Self { ValidateTakerFundingSpendPreimageError::TxGenError(format!("{:?}", err)) } } -impl From for ValidateTakerFundingSpendPreimageError { - fn from(err: UtxoRpcError) -> Self { ValidateTakerFundingSpendPreimageError::Rpc(err.to_string()) } -} - /// Enum covering error cases that can happen during taker payment spend preimage validation. -#[derive(Debug, Display)] +#[derive(Debug, Display, EnumFromStringify)] pub enum ValidateTakerPaymentSpendPreimageError { /// Error during signature deserialization. InvalidTakerSignature, /// Error during preimage comparison to an expected one. InvalidPreimage(String), /// Error during taker's signature check. + #[from_stringify("UtxoSignWithKeyPairError")] SignatureVerificationFailure(String), /// Error during generation of an expected preimage. TxGenError(String), @@ -1446,12 +1427,6 @@ pub enum ValidateTakerPaymentSpendPreimageError { LocktimeOverflow(String), } -impl From for ValidateTakerPaymentSpendPreimageError { - fn from(err: UtxoSignWithKeyPairError) -> Self { - ValidateTakerPaymentSpendPreimageError::SignatureVerificationFailure(err.to_string()) - } -} - impl From for ValidateTakerPaymentSpendPreimageError { fn from(err: TxGenError) -> Self { ValidateTakerPaymentSpendPreimageError::TxGenError(format!("{:?}", err)) } } @@ -1462,7 +1437,7 @@ pub trait ToBytes { } /// Defines associated types specific to each coin (Pubkey, Address, etc.) -pub trait CoinAssocTypes { +pub trait ParseCoinAssocTypes { type Address: Send + Sync + fmt::Display; type AddressParseError: fmt::Debug + Send + fmt::Display; type Pubkey: ToBytes + Send + Sync; @@ -1487,7 +1462,51 @@ pub trait CoinAssocTypes { fn parse_signature(&self, sig: &[u8]) -> Result; } -pub struct SendMakerPaymentArgs<'a, Coin: CoinAssocTypes + ?Sized> { +/// Defines associated types specific to Non-Fungible Tokens (Token Address, Token Id, etc.) +pub trait ParseNftAssocTypes { + type ContractAddress: Send + Sync + fmt::Display; + type TokenId: ToBytes + Send + Sync; + type ContractType: ToBytes + Send + Sync; + type NftAssocTypesError: fmt::Debug + Send + fmt::Display; + + fn parse_contract_address( + &self, + contract_address: &[u8], + ) -> Result; + + fn parse_token_id(&self, token_id: &[u8]) -> Result; + + fn parse_contract_type(&self, contract_type: &[u8]) -> Result; +} + +pub struct SendMakerPaymentArgs<'a, Coin: ParseCoinAssocTypes + ?Sized> { + /// Maker will be able to refund the payment after this timestamp + pub time_lock: u64, + /// The hash of the secret generated by taker, this is used for immediate refund + pub taker_secret_hash: &'a [u8], + /// The hash of the secret generated by maker, taker needs it to spend the payment + pub maker_secret_hash: &'a [u8], + /// Payment amount + pub amount: BigDecimal, + /// Taker's HTLC pubkey + pub taker_pub: &'a Coin::Pubkey, + /// Unique data of specific swap + pub swap_unique_data: &'a [u8], +} + +/// Structure representing necessary NFT info for Swap +pub struct NftSwapInfo<'a, Coin: ParseNftAssocTypes + ?Sized> { + /// The address of the NFT token + pub token_address: &'a Coin::ContractAddress, + /// The ID of the NFT token. + pub token_id: &'a [u8], + /// The type of smart contract that governs this NFT + pub contract_type: &'a Coin::ContractType, + /// Etomic swap contract address + pub swap_contract_address: &'a Coin::ContractAddress, +} + +pub struct SendNftMakerPaymentArgs<'a, Coin: ParseCoinAssocTypes + ParseNftAssocTypes + ?Sized> { /// Maker will be able to refund the payment after this timestamp pub time_lock: u64, /// The hash of the secret generated by taker, this is used for immediate refund @@ -1500,9 +1519,11 @@ pub struct SendMakerPaymentArgs<'a, Coin: CoinAssocTypes + ?Sized> { pub taker_pub: &'a Coin::Pubkey, /// Unique data of specific swap pub swap_unique_data: &'a [u8], + /// Structure representing necessary NFT info for Swap + pub nft_swap_info: &'a NftSwapInfo<'a, Coin>, } -pub struct ValidateMakerPaymentArgs<'a, Coin: CoinAssocTypes + ?Sized> { +pub struct ValidateMakerPaymentArgs<'a, Coin: ParseCoinAssocTypes + ?Sized> { /// Maker payment tx pub maker_payment_tx: &'a Coin::Tx, /// Maker will be able to refund the payment after this timestamp @@ -1519,7 +1540,28 @@ pub struct ValidateMakerPaymentArgs<'a, Coin: CoinAssocTypes + ?Sized> { pub swap_unique_data: &'a [u8], } -pub struct RefundMakerPaymentArgs<'a, Coin: CoinAssocTypes + ?Sized> { +pub struct ValidateNftMakerPaymentArgs<'a, Coin: ParseCoinAssocTypes + ParseNftAssocTypes + ?Sized> { + /// Maker payment tx + pub maker_payment_tx: &'a Coin::Tx, + /// Maker will be able to refund the payment after this timestamp + pub time_lock: u64, + /// The hash of the secret generated by taker, this is used for immediate refund + pub taker_secret_hash: &'a [u8], + /// The hash of the secret generated by maker, taker needs it to spend the payment + pub maker_secret_hash: &'a [u8], + /// Payment amount + pub amount: BigDecimal, + /// Taker's HTLC pubkey + pub taker_pub: &'a Coin::Pubkey, + /// Maker's HTLC pubkey + pub maker_pub: &'a Coin::Pubkey, + /// Unique data of specific swap + pub swap_unique_data: &'a [u8], + /// Structure representing necessary NFT info for Swap + pub nft_swap_info: &'a NftSwapInfo<'a, Coin>, +} + +pub struct RefundMakerPaymentArgs<'a, Coin: ParseCoinAssocTypes + ?Sized> { /// Maker payment tx pub maker_payment_tx: &'a Coin::Tx, /// Maker will be able to refund the payment after this timestamp @@ -1536,7 +1578,24 @@ pub struct RefundMakerPaymentArgs<'a, Coin: CoinAssocTypes + ?Sized> { pub swap_unique_data: &'a [u8], } -pub struct SpendMakerPaymentArgs<'a, Coin: CoinAssocTypes + ?Sized> { +pub struct SpendMakerPaymentArgs<'a, Coin: ParseCoinAssocTypes + ?Sized> { + /// Maker payment tx + pub maker_payment_tx: &'a Coin::Tx, + /// Maker will be able to refund the payment after this timestamp + pub time_lock: u64, + /// The hash of the secret generated by taker, this is used for immediate refund + pub taker_secret_hash: &'a [u8], + /// The hash of the secret generated by maker, taker needs it to spend the payment + pub maker_secret_hash: &'a [u8], + /// The secret generated by maker, revealed when maker spends taker's payment + pub maker_secret: &'a [u8], + /// Maker's HTLC pubkey + pub maker_pub: &'a Coin::Pubkey, + /// Unique data of specific swap + pub swap_unique_data: &'a [u8], +} + +pub struct SpendNftMakerPaymentArgs<'a, Coin: ParseCoinAssocTypes + ParseNftAssocTypes + ?Sized> { /// Maker payment tx pub maker_payment_tx: &'a Coin::Tx, /// Maker will be able to refund the payment after this timestamp @@ -1551,11 +1610,15 @@ pub struct SpendMakerPaymentArgs<'a, Coin: CoinAssocTypes + ?Sized> { pub maker_pub: &'a Coin::Pubkey, /// Unique data of specific swap pub swap_unique_data: &'a [u8], + /// The type of smart contract that governs this NFT + pub contract_type: &'a Coin::ContractType, + /// Etomic swap contract address + pub swap_contract_address: &'a Coin::ContractAddress, } /// Operations specific to maker coin in [Trading Protocol Upgrade implementation](https://github.com/KomodoPlatform/komodo-defi-framework/issues/1895) #[async_trait] -pub trait MakerCoinSwapOpsV2: CoinAssocTypes + Send + Sync + 'static { +pub trait MakerCoinSwapOpsV2: ParseCoinAssocTypes + Send + Sync + 'static { /// Generate and broadcast maker payment transaction async fn send_maker_payment_v2(&self, args: SendMakerPaymentArgs<'_, Self>) -> Result; @@ -1575,6 +1638,38 @@ pub trait MakerCoinSwapOpsV2: CoinAssocTypes + Send + Sync + 'static { async fn spend_maker_payment_v2(&self, args: SpendMakerPaymentArgs<'_, Self>) -> Result; } +#[async_trait] +pub trait MakerNftSwapOpsV2: ParseCoinAssocTypes + ParseNftAssocTypes + Send + Sync + 'static { + async fn send_nft_maker_payment_v2( + &self, + args: SendNftMakerPaymentArgs<'_, Self>, + ) -> Result; + + /// Validate NFT maker payment transaction + async fn validate_nft_maker_payment_v2( + &self, + args: ValidateNftMakerPaymentArgs<'_, Self>, + ) -> ValidatePaymentResult<()>; + + /// Spend NFT maker payment transaction + async fn spend_nft_maker_payment_v2( + &self, + args: SpendNftMakerPaymentArgs<'_, Self>, + ) -> Result; + + /// Refund NFT maker payment transaction using timelock path + async fn refund_nft_maker_payment_v2_timelock( + &self, + args: RefundPaymentArgs<'_>, + ) -> Result; + + /// Refund NFT maker payment transaction using immediate refund path + async fn refund_nft_maker_payment_v2_secret( + &self, + args: RefundMakerPaymentArgs<'_, Self>, + ) -> Result; +} + /// Enum representing errors that can occur while waiting for taker payment spend. #[derive(Display)] pub enum WaitForTakerPaymentSpendError { @@ -1610,8 +1705,8 @@ impl From for WaitForTakerPaymentSpendError { /// Enum representing different ways a funding transaction can be spent. /// -/// This enum is generic over types that implement the `CoinAssocTypes` trait. -pub enum FundingTxSpend { +/// This enum is generic over types that implement the `ParseCoinAssocTypes` trait. +pub enum FundingTxSpend { /// Variant indicating that the funding transaction has been spent through a timelock path. RefundedTimelock(T::Tx), /// Variant indicating that the funding transaction has been spent by revealing a taker's secret (immediate refund path). @@ -1626,7 +1721,7 @@ pub enum FundingTxSpend { TransferredToTakerPayment(T::Tx), } -impl fmt::Debug for FundingTxSpend { +impl fmt::Debug for FundingTxSpend { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { FundingTxSpend::RefundedTimelock(tx) => { @@ -1657,7 +1752,7 @@ pub enum SearchForFundingSpendErr { /// Operations specific to taker coin in [Trading Protocol Upgrade implementation](https://github.com/KomodoPlatform/komodo-defi-framework/issues/1895) #[async_trait] -pub trait TakerCoinSwapOpsV2: CoinAssocTypes + Send + Sync + 'static { +pub trait TakerCoinSwapOpsV2: ParseCoinAssocTypes + Send + Sync + 'static { /// Generate and broadcast taker funding transaction that includes dex fee, maker premium and actual trading volume. /// Funding tx can be reclaimed immediately if maker back-outs (doesn't send maker payment) async fn send_taker_funding(&self, args: SendTakerFundingArgs<'_>) -> Result; @@ -2219,7 +2314,7 @@ pub enum TradePreimageValue { UpperBound(BigDecimal), } -#[derive(Debug, Display, PartialEq)] +#[derive(Debug, Display, EnumFromStringify, PartialEq)] pub enum TradePreimageError { #[display( fmt = "Not enough {} to preimage the trade: available {}, required at least {}", @@ -2236,20 +2331,13 @@ pub enum TradePreimageError { AmountIsTooSmall { amount: BigDecimal, threshold: BigDecimal }, #[display(fmt = "Transport error: {}", _0)] Transport(String), + #[from_stringify("NumConversError", "UnexpectedDerivationMethod")] #[display(fmt = "Internal error: {}", _0)] InternalError(String), #[display(fmt = "Nft Protocol is not supported yet!")] NftProtocolNotSupported, } -impl From for TradePreimageError { - fn from(e: NumConversError) -> Self { TradePreimageError::InternalError(e.to_string()) } -} - -impl From for TradePreimageError { - fn from(e: UnexpectedDerivationMethod) -> Self { TradePreimageError::InternalError(e.to_string()) } -} - impl TradePreimageError { /// Construct [`TradePreimageError`] from [`GenerateTxError`] using additional `coin` and `decimals`. pub fn from_generate_tx_error( @@ -2318,7 +2406,7 @@ impl TradePreimageError { } /// The reason of unsuccessful conversion of two internal numbers, e.g. `u64` from `BigNumber`. -#[derive(Debug, Display)] +#[derive(Clone, Debug, Display)] pub struct NumConversError(String); impl From for NumConversError { @@ -2331,7 +2419,7 @@ impl NumConversError { pub fn description(&self) -> &str { &self.0 } } -#[derive(Clone, Debug, Display, PartialEq, Serialize, SerializeErrorType)] +#[derive(Clone, Debug, Display, EnumFromStringify, PartialEq, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] pub enum BalanceError { #[display(fmt = "Transport: {}", _0)] @@ -2341,6 +2429,7 @@ pub enum BalanceError { UnexpectedDerivationMethod(UnexpectedDerivationMethod), #[display(fmt = "Wallet storage error: {}", _0)] WalletStorageError(String), + #[from_stringify("Bip32Error", "NumConversError")] #[display(fmt = "Internal: {}", _0)] Internal(String), } @@ -2357,25 +2446,18 @@ impl From for GetNonZeroBalance { fn from(e: BalanceError) -> Self { GetNonZeroBalance::MyBalanceError(e) } } -impl From for BalanceError { - fn from(e: NumConversError) -> Self { BalanceError::Internal(e.to_string()) } -} - impl From for BalanceError { fn from(e: UnexpectedDerivationMethod) -> Self { BalanceError::UnexpectedDerivationMethod(e) } } -impl From for BalanceError { - fn from(e: Bip32Error) -> Self { BalanceError::Internal(e.to_string()) } -} - -#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] +#[derive(Debug, Deserialize, Display, EnumFromStringify, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] pub enum StakingInfosError { #[display(fmt = "Staking infos not available for: {}", coin)] CoinDoesntSupportStakingInfos { coin: String }, #[display(fmt = "No such coin {}", coin)] NoSuchCoin { coin: String }, + #[from_stringify("UnexpectedDerivationMethod")] #[display(fmt = "Derivation method is not supported: {}", _0)] UnexpectedDerivationMethod(String), #[display(fmt = "Transport error: {}", _0)] @@ -2396,10 +2478,6 @@ impl From for StakingInfosError { } } -impl From for StakingInfosError { - fn from(e: UnexpectedDerivationMethod) -> Self { StakingInfosError::UnexpectedDerivationMethod(e.to_string()) } -} - impl From for StakingInfosError { fn from(e: Qrc20AddressError) -> Self { match e { @@ -2431,7 +2509,7 @@ impl From for StakingInfosError { } } -#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] +#[derive(Debug, Deserialize, Display, EnumFromStringify, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] pub enum DelegationError { #[display( @@ -2453,6 +2531,7 @@ pub enum DelegationError { NoSuchCoin { coin: String }, #[display(fmt = "{}", _0)] CannotInteractWithSmartContract(String), + #[from_stringify("ScriptHashTypeNotSupported")] #[display(fmt = "{}", _0)] AddressError(String), #[display(fmt = "Already delegating to: {}", _0)] @@ -2461,6 +2540,7 @@ pub enum DelegationError { DelegationOpsNotSupported { reason: String }, #[display(fmt = "Transport error: {}", _0)] Transport(String), + #[from_stringify("MyAddressError")] #[display(fmt = "Internal error: {}", _0)] InternalError(String), } @@ -2531,10 +2611,6 @@ impl From for DelegationError { } } -impl From for DelegationError { - fn from(e: ScriptHashTypeNotSupported) -> Self { DelegationError::AddressError(e.to_string()) } -} - impl HttpStatusCode for DelegationError { fn status_code(&self) -> StatusCode { match self { @@ -2659,7 +2735,12 @@ pub enum WithdrawError { #[display(fmt = "Transport error: {}", _0)] Transport(String), #[from_trait(WithInternal::internal)] - #[from_stringify("NumConversError", "UnexpectedDerivationMethod", "PrivKeyPolicyNotAllowed")] + #[from_stringify( + "MyAddressError", + "NumConversError", + "UnexpectedDerivationMethod", + "PrivKeyPolicyNotAllowed" + )] #[display(fmt = "Internal error: {}", _0)] InternalError(String), #[display(fmt = "Unsupported error: {}", _0)] @@ -2860,17 +2941,21 @@ impl HttpStatusCode for SignatureError { } } -#[derive(Debug, Display, Serialize, SerializeErrorType)] +#[derive(Debug, Display, EnumFromStringify, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] pub enum VerificationError { #[display(fmt = "Invalid request: {}", _0)] InvalidRequest(String), + #[from_stringify("ethkey::Error", "keys::Error")] #[display(fmt = "Internal error: {}", _0)] InternalError(String), + #[from_stringify("base64::DecodeError")] #[display(fmt = "Signature decoding error: {}", _0)] SignatureDecodingError(String), + #[from_stringify("hex::FromHexError")] #[display(fmt = "Address decoding error: {}", _0)] AddressDecodingError(String), + #[from_stringify("CoinFindError")] #[display(fmt = "Coin is not found: {}", _0)] CoinIsNotFound(String), #[display(fmt = "sign_message_prefix is not set in coin config")] @@ -2890,14 +2975,6 @@ impl HttpStatusCode for VerificationError { } } -impl From for VerificationError { - fn from(e: base64::DecodeError) -> Self { VerificationError::SignatureDecodingError(e.to_string()) } -} - -impl From for VerificationError { - fn from(e: hex::FromHexError) -> Self { VerificationError::AddressDecodingError(e.to_string()) } -} - impl From for VerificationError { fn from(e: FromBase58Error) -> Self { match e { @@ -2911,18 +2988,6 @@ impl From for VerificationError { } } -impl From for VerificationError { - fn from(e: keys::Error) -> Self { VerificationError::InternalError(e.to_string()) } -} - -impl From for VerificationError { - fn from(e: ethkey::Error) -> Self { VerificationError::InternalError(e.to_string()) } -} - -impl From for VerificationError { - fn from(e: CoinFindError) -> Self { VerificationError::CoinIsNotFound(e.to_string()) } -} - /// NB: Implementations are expected to follow the pImpl idiom, providing cheap reference-counted cloning and garbage collection. #[async_trait] pub trait MmCoin: @@ -3769,7 +3834,7 @@ pub enum CoinProtocol { decimals: u8, }, ZHTLC(ZcoinProtocolInfo), - Nft { + NFT { platform: String, }, } @@ -4024,7 +4089,7 @@ pub async fn lp_coininit(ctx: &MmArc, ticker: &str, req: &Json) -> Result return ERR!("TENDERMINT protocol is not supported by lp_coininit"), CoinProtocol::TENDERMINTTOKEN(_) => return ERR!("TENDERMINTTOKEN protocol is not supported by lp_coininit"), CoinProtocol::ZHTLC { .. } => return ERR!("ZHTLC protocol is not supported by lp_coininit"), - CoinProtocol::Nft { .. } => return ERR!("NFT protocol is not supported by lp_coininit"), + CoinProtocol::NFT { .. } => return ERR!("NFT protocol is not supported by lp_coininit"), #[cfg(not(target_arch = "wasm32"))] CoinProtocol::LIGHTNING { .. } => return ERR!("Lightning protocol is not supported by lp_coininit"), #[cfg(all(feature = "enable-solana", not(target_arch = "wasm32")))] @@ -4568,7 +4633,7 @@ pub fn address_by_coin_conf_and_pubkey_str( ) -> Result { let protocol: CoinProtocol = try_s!(json::from_value(conf["protocol"].clone())); match protocol { - CoinProtocol::ERC20 { .. } | CoinProtocol::ETH | CoinProtocol::Nft { .. } => eth::addr_from_pubkey_str(pubkey), + CoinProtocol::ERC20 { .. } | CoinProtocol::ETH | CoinProtocol::NFT { .. } => eth::addr_from_pubkey_str(pubkey), CoinProtocol::UTXO | CoinProtocol::QTUM | CoinProtocol::QRC20 { .. } | CoinProtocol::BCH { .. } => { utxo::address_by_conf_and_pubkey_str(coin, conf, pubkey, addr_format) }, diff --git a/mm2src/coins/my_tx_history_v2.rs b/mm2src/coins/my_tx_history_v2.rs index 6158829c66..00f25b2701 100644 --- a/mm2src/coins/my_tx_history_v2.rs +++ b/mm2src/coins/my_tx_history_v2.rs @@ -3,6 +3,7 @@ use crate::tendermint::{TENDERMINT_ASSET_PROTOCOL_TYPE, TENDERMINT_COIN_PROTOCOL use crate::tx_history_storage::{CreateTxHistoryStorageError, FilteringAddresses, GetTxHistoryFilters, TxHistoryStorageBuilder, WalletId}; use crate::utxo::utxo_common::big_decimal_from_sat_unsigned; +use crate::MyAddressError; use crate::{coin_conf, lp_coinfind_or_err, BlockHeightAndTime, CoinFindError, HDAccountAddressId, HistorySyncState, MmCoin, MmCoinEnum, Transaction, TransactionDetails, TransactionType, TxFeeDetails, UtxoRpcError}; use async_trait::async_trait; @@ -10,6 +11,7 @@ use bitcrypto::sha256; use common::{calc_total_pages, ten, HttpStatusCode, PagingOptionsEnum, StatusCode}; use crypto::StandardHDPath; use derive_more::Display; +use enum_derives::EnumFromStringify; use futures::compat::Future01CompatExt; use keys::{Address, CashAddress}; use mm2_core::mm_ctx::MmArc; @@ -304,15 +306,19 @@ pub struct MyTxHistoryResponseV2 { pub(crate) paging_options: PagingOptionsEnum, } -#[derive(Debug, Display, Serialize, SerializeErrorType)] +#[derive(Debug, Display, EnumFromStringify, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] pub enum MyTxHistoryErrorV2 { CoinIsNotActive(String), + #[from_stringify("InvalidBip44ChainError")] InvalidTarget(String), StorageIsNotInitialized(String), + #[from_stringify("CreateTxHistoryStorageError")] StorageError(String), + #[from_stringify("UtxoRpcError")] RpcError(String), NotSupportedFor(String), + #[from_stringify("MyAddressError")] Internal(String), } @@ -350,14 +356,6 @@ impl From for MyTxHistoryErrorV2 { } } -impl From for MyTxHistoryErrorV2 { - fn from(e: CreateTxHistoryStorageError) -> Self { MyTxHistoryErrorV2::StorageError(e.to_string()) } -} - -impl From for MyTxHistoryErrorV2 { - fn from(err: UtxoRpcError) -> Self { MyTxHistoryErrorV2::RpcError(err.to_string()) } -} - impl From for MyTxHistoryErrorV2 { fn from(e: AddressDerivingError) -> Self { match e { @@ -368,10 +366,6 @@ impl From for MyTxHistoryErrorV2 { } } -impl From for MyTxHistoryErrorV2 { - fn from(e: InvalidBip44ChainError) -> Self { MyTxHistoryErrorV2::InvalidTarget(e.to_string()) } -} - #[async_trait] pub trait CoinWithTxHistoryV2 { fn history_wallet_id(&self) -> WalletId; diff --git a/mm2src/coins/nft/nft_errors.rs b/mm2src/coins/nft/nft_errors.rs index ba1c31c0d2..8a6fb90f19 100644 --- a/mm2src/coins/nft/nft_errors.rs +++ b/mm2src/coins/nft/nft_errors.rs @@ -36,6 +36,7 @@ pub enum GetNftInfoError { token_address: String, token_id: String, }, + #[from_stringify("LockDBError")] #[display(fmt = "DB error {}", _0)] DbError(String), ParseRfc3339Err(ParseRfc3339Err), @@ -43,6 +44,7 @@ pub enum GetNftInfoError { ContractTypeIsNull, ProtectFromSpamError(ProtectFromSpamError), TransferConfirmationsError(TransferConfirmationsError), + #[from_stringify("NumConversError")] NumConversError(String), } @@ -102,10 +104,6 @@ impl From for GetNftInfoError { fn from(e: ProtectFromSpamError) -> Self { GetNftInfoError::ProtectFromSpamError(e) } } -impl From for GetNftInfoError { - fn from(e: LockDBError) -> Self { GetNftInfoError::DbError(e.to_string()) } -} - impl From for GetNftInfoError { fn from(e: TransferConfirmationsError) -> Self { GetNftInfoError::TransferConfirmationsError(e) } } @@ -118,10 +116,6 @@ impl From for GetNftInfoError { } } -impl From for GetNftInfoError { - fn from(e: NumConversError) -> Self { GetNftInfoError::NumConversError(e.to_string()) } -} - impl HttpStatusCode for GetNftInfoError { fn status_code(&self) -> StatusCode { match self { @@ -154,6 +148,7 @@ impl HttpStatusCode for GetNftInfoError { #[derive(Clone, Debug, Deserialize, Display, EnumFromStringify, PartialEq, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] pub enum UpdateNftError { + #[from_stringify("LockDBError")] #[display(fmt = "DB error {}", _0)] DbError(String), #[display(fmt = "Internal: {}", _0)] @@ -213,10 +208,6 @@ pub enum UpdateNftError { CoinDoesntSupportNft { coin: String, }, - #[display(fmt = "Global NFT type mismatch for token '{}'", token)] - GlobalNftTypeMismatch { - token: String, - }, } impl From for UpdateNftError { @@ -243,10 +234,6 @@ impl From for UpdateNftError { fn from(e: ProtectFromSpamError) -> Self { UpdateNftError::ProtectFromSpamError(e) } } -impl From for UpdateNftError { - fn from(e: LockDBError) -> Self { UpdateNftError::DbError(e.to_string()) } -} - impl From for UpdateNftError { fn from(e: CoinFindError) -> Self { match e { @@ -273,8 +260,7 @@ impl HttpStatusCode for UpdateNftError { | UpdateNftError::SerdeError(_) | UpdateNftError::ProtectFromSpamError(_) | UpdateNftError::NoSuchCoin { .. } - | UpdateNftError::CoinDoesntSupportNft { .. } - | UpdateNftError::GlobalNftTypeMismatch { .. } => StatusCode::INTERNAL_SERVER_ERROR, + | UpdateNftError::CoinDoesntSupportNft { .. } => StatusCode::INTERNAL_SERVER_ERROR, } } } @@ -394,10 +380,11 @@ impl From for TransferConfirmationsError { } /// Enumerates errors that can occur while clearing NFT data from the database. -#[derive(Clone, Debug, Deserialize, Display, PartialEq, Serialize, SerializeErrorType)] +#[derive(Clone, Debug, Deserialize, Display, EnumFromStringify, PartialEq, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] pub enum ClearNftDbError { /// Represents errors related to database operations. + #[from_stringify("LockDBError")] #[display(fmt = "DB error {}", _0)] DbError(String), /// Indicates internal errors not directly associated with database operations. @@ -412,10 +399,6 @@ impl From for ClearNftDbError { fn from(err: T) -> Self { ClearNftDbError::DbError(format!("{:?}", err)) } } -impl From for ClearNftDbError { - fn from(e: LockDBError) -> Self { ClearNftDbError::DbError(e.to_string()) } -} - impl HttpStatusCode for ClearNftDbError { fn status_code(&self) -> StatusCode { match self { diff --git a/mm2src/coins/nft/nft_structs.rs b/mm2src/coins/nft/nft_structs.rs index baf6c0c65d..0096e8f2fe 100644 --- a/mm2src/coins/nft/nft_structs.rs +++ b/mm2src/coins/nft/nft_structs.rs @@ -114,9 +114,9 @@ pub enum Chain { pub trait ConvertChain { fn to_ticker(&self) -> &'static str; - fn from_ticker(s: &str) -> MmResult; + fn from_ticker(s: &str) -> Result; fn to_nft_ticker(&self) -> &'static str; - fn from_nft_ticker(s: &str) -> MmResult; + fn from_nft_ticker(s: &str) -> Result; } impl ConvertChain for Chain { @@ -133,14 +133,14 @@ impl ConvertChain for Chain { /// Converts a coin ticker string to a `Chain` enum. #[inline(always)] - fn from_ticker(s: &str) -> MmResult { + fn from_ticker(s: &str) -> Result { match s { "AVAX" | "avax" => Ok(Chain::Avalanche), "BNB" | "bnb" => Ok(Chain::Bsc), "ETH" | "eth" => Ok(Chain::Eth), "FTM" | "ftm" => Ok(Chain::Fantom), "MATIC" | "matic" => Ok(Chain::Polygon), - _ => MmError::err(ParseChainTypeError::UnsupportedChainType), + _ => Err(ParseChainTypeError::UnsupportedChainType), } } @@ -157,14 +157,14 @@ impl ConvertChain for Chain { /// Converts a NFT ticker string to a `Chain` enum. #[inline(always)] - fn from_nft_ticker(s: &str) -> MmResult { + fn from_nft_ticker(s: &str) -> Result { match s.to_uppercase().as_str() { "NFT_AVAX" => Ok(Chain::Avalanche), "NFT_BNB" => Ok(Chain::Bsc), "NFT_ETH" => Ok(Chain::Eth), "NFT_FTM" => Ok(Chain::Fantom), "NFT_MATIC" => Ok(Chain::Polygon), - _ => MmError::err(ParseChainTypeError::UnsupportedChainType), + _ => Err(ParseChainTypeError::UnsupportedChainType), } } } @@ -817,15 +817,15 @@ pub struct ClearNftDbReq { #[derive(Clone, Debug, Serialize)] pub struct NftInfo { /// The address of the NFT token. - pub(crate) token_address: Address, + pub token_address: Address, /// The ID of the NFT token. #[serde(serialize_with = "serialize_token_id")] - pub(crate) token_id: BigUint, + pub token_id: BigUint, /// The blockchain where the NFT exists. - pub(crate) chain: Chain, + pub chain: Chain, /// The type of smart contract that governs this NFT. - pub(crate) contract_type: ContractType, + pub contract_type: ContractType, /// The quantity of this type of NFT owned. Particularly relevant for ERC-1155 tokens, /// where a single token ID can represent multiple assets. - pub(crate) amount: BigDecimal, + pub amount: BigDecimal, } diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index 5ac52ef31d..3d7ca55ed5 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -4,10 +4,10 @@ use super::{CoinBalance, FundingTxSpend, HistorySyncState, MarketCoinOps, MmCoin RawTransactionRequest, SearchForFundingSpendErr, SwapOps, TradeFee, TransactionEnum, TransactionFut, WaitForTakerPaymentSpendError}; use crate::coin_errors::ValidatePaymentResult; -use crate::{coin_errors::MyAddressError, BalanceFut, CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinAssocTypes, - CoinFutSpawner, ConfirmPaymentInput, FeeApproxStage, FoundSwapTxSpend, GenPreimageResult, - GenTakerFundingSpendArgs, GenTakerPaymentSpendArgs, MakerSwapTakerCoin, MmCoinEnum, - NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, +use crate::{coin_errors::MyAddressError, BalanceFut, CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinFutSpawner, + ConfirmPaymentInput, FeeApproxStage, FoundSwapTxSpend, GenPreimageResult, GenTakerFundingSpendArgs, + GenTakerPaymentSpendArgs, MakerSwapTakerCoin, MmCoinEnum, NegotiateSwapContractAddrErr, + ParseCoinAssocTypes, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, RawTransactionResult, RefundFundingSecretArgs, RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SendTakerFundingArgs, SignRawTransactionRequest, SignatureResult, SpendPaymentArgs, TakerCoinSwapOpsV2, TakerSwapMakerCoin, TradePreimageFut, @@ -425,7 +425,7 @@ impl ToBytes for TestSig { fn to_bytes(&self) -> Vec { vec![] } } -impl CoinAssocTypes for TestCoin { +impl ParseCoinAssocTypes for TestCoin { type Address = String; type AddressParseError = String; type Pubkey = TestPubkey; diff --git a/mm2src/coins/utxo.rs b/mm2src/coins/utxo.rs index 1bba5184c2..033f496a72 100644 --- a/mm2src/coins/utxo.rs +++ b/mm2src/coins/utxo.rs @@ -114,7 +114,7 @@ use crate::hd_wallet::{HDAccountOps, HDAccountsMutex, HDAddress, HDAddressId, HD InvalidBip44ChainError}; use crate::hd_wallet_storage::{HDAccountStorageItem, HDWalletCoinStorage, HDWalletStorageError, HDWalletStorageResult}; use crate::utxo::tx_cache::UtxoVerboseCacheShared; -use crate::{CoinAssocTypes, ToBytes}; +use crate::{ParseCoinAssocTypes, ToBytes}; pub mod tx_cache; @@ -1048,7 +1048,7 @@ impl ToBytes for Signature { fn to_bytes(&self) -> Vec { self.to_vec() } } -impl CoinAssocTypes for T { +impl ParseCoinAssocTypes for T { type Address = Address; type AddressParseError = MmError; type Pubkey = Public; diff --git a/mm2src/coins/utxo/rpc_clients.rs b/mm2src/coins/utxo/rpc_clients.rs index 56a1c9289c..a2ed5a8aa5 100644 --- a/mm2src/coins/utxo/rpc_clients.rs +++ b/mm2src/coins/utxo/rpc_clients.rs @@ -4,7 +4,8 @@ use crate::utxo::utxo_block_header_storage::BlockHeaderStorage; use crate::utxo::{output_script, sat_from_big_decimal, GetBlockHeaderError, GetConfirmedTxError, GetTxError, GetTxHeightError, ScripthashNotification}; -use crate::{big_decimal_from_sat_unsigned, NumConversError, RpcTransportEventHandler, RpcTransportEventHandlerShared}; +use crate::{big_decimal_from_sat_unsigned, MyAddressError, NumConversError, RpcTransportEventHandler, + RpcTransportEventHandlerShared}; use async_trait::async_trait; use chain::{BlockHeader, BlockHeaderBits, BlockHeaderNonce, OutPoint, Transaction as UtxoTx, TransactionInput, TxHashAlgo}; @@ -18,6 +19,7 @@ use common::log::{debug, LogOnError}; use common::log::{error, info, warn}; use common::{median, now_float, now_ms, now_sec, OrdRange}; use derive_more::Display; +use enum_derives::EnumFromStringify; use futures::channel::oneshot as async_oneshot; use futures::compat::{Future01CompatExt, Stream01CompatExt}; use futures::future::{join_all, FutureExt, TryFutureExt}; @@ -292,11 +294,12 @@ pub struct SpentOutputInfo { pub type UtxoRpcResult = Result>; pub type UtxoRpcFut = Box> + Send + 'static>; -#[derive(Debug, Display)] +#[derive(Debug, Display, EnumFromStringify)] pub enum UtxoRpcError { Transport(JsonRpcError), ResponseParseError(JsonRpcError), InvalidResponse(String), + #[from_stringify("MyAddressError")] Internal(String), } diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index f96bf73055..0ba3c2e077 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -2173,8 +2173,7 @@ mod slp_tests { let err = match tx_err.clone() { TransactionErr::TxRecoverable(_tx, err) => err, - TransactionErr::Plain(err) => err, - TransactionErr::NftProtocolNotSupported(err) => err, + TransactionErr::Plain(err) | TransactionErr::ProtocolNotSupported(err) => err, }; println!("{:?}", err); diff --git a/mm2src/coins_activation/src/erc20_token_activation.rs b/mm2src/coins_activation/src/erc20_token_activation.rs index 027f767539..173092c65d 100644 --- a/mm2src/coins_activation/src/erc20_token_activation.rs +++ b/mm2src/coins_activation/src/erc20_token_activation.rs @@ -88,7 +88,7 @@ impl TryFromCoinProtocol for EthTokenProtocol { let erc20_protocol = Erc20Protocol::try_from_coin_protocol(proto)?; Ok(EthTokenProtocol::Erc20(erc20_protocol)) }, - CoinProtocol::Nft { platform } => Ok(EthTokenProtocol::Nft(NftProtocol { platform })), + CoinProtocol::NFT { platform } => Ok(EthTokenProtocol::Nft(NftProtocol { platform })), proto => MmError::err(proto), } } diff --git a/mm2src/coins_activation/src/prelude.rs b/mm2src/coins_activation/src/prelude.rs index 21a04ed8ad..967a4cae68 100644 --- a/mm2src/coins_activation/src/prelude.rs +++ b/mm2src/coins_activation/src/prelude.rs @@ -82,7 +82,7 @@ pub fn coin_conf_with_protocol( Ok(chain) => { let platform = chain.to_ticker(); let platform_conf = coin_conf(ctx, platform); - let nft_protocol = CoinProtocol::Nft { + let nft_protocol = CoinProtocol::NFT { platform: platform.to_string(), }; (platform_conf, nft_protocol) diff --git a/mm2src/mm2_main/Cargo.toml b/mm2src/mm2_main/Cargo.toml index 3b037954f3..800012fff0 100644 --- a/mm2src/mm2_main/Cargo.toml +++ b/mm2src/mm2_main/Cargo.toml @@ -125,6 +125,7 @@ mm2_test_helpers = { path = "../mm2_test_helpers" } mocktopus = "0.8.0" testcontainers = "0.15.0" web3 = { git = "https://github.com/KomodoPlatform/rust-web3", tag = "v0.19.0", default-features = false, features = ["http"] } +ethabi = { version = "17.0.0" } [build-dependencies] chrono = "0.4" diff --git a/mm2src/mm2_main/src/lp_ordermatch.rs b/mm2src/mm2_main/src/lp_ordermatch.rs index a1393bf71e..04699c0dee 100644 --- a/mm2src/mm2_main/src/lp_ordermatch.rs +++ b/mm2src/mm2_main/src/lp_ordermatch.rs @@ -5802,7 +5802,7 @@ fn orderbook_address( ) -> Result> { let protocol: CoinProtocol = json::from_value(conf["protocol"].clone())?; match protocol { - CoinProtocol::ERC20 { .. } | CoinProtocol::ETH | CoinProtocol::Nft { .. } => { + CoinProtocol::ERC20 { .. } | CoinProtocol::ETH | CoinProtocol::NFT { .. } => { coins::eth::addr_from_pubkey_str(pubkey) .map(OrderbookAddress::Transparent) .map_to_mm(OrderbookAddrErr::AddrFromPubkeyError) diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs b/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs index 77ce092be1..b21947792a 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs @@ -8,8 +8,8 @@ use crate::mm2::lp_swap::{broadcast_swap_v2_msg_every, check_balance_for_maker_s SwapConfirmationsSettings, TransactionIdentifier, MAKER_SWAP_V2_TYPE, MAX_STARTED_AT_DIFF}; use async_trait::async_trait; use bitcrypto::{dhash160, sha256}; -use coins::{CanRefundHtlc, CoinAssocTypes, ConfirmPaymentInput, DexFee, FeeApproxStage, FundingTxSpend, - GenTakerFundingSpendArgs, GenTakerPaymentSpendArgs, MakerCoinSwapOpsV2, MmCoin, RefundMakerPaymentArgs, +use coins::{CanRefundHtlc, ConfirmPaymentInput, DexFee, FeeApproxStage, FundingTxSpend, GenTakerFundingSpendArgs, + GenTakerPaymentSpendArgs, MakerCoinSwapOpsV2, MmCoin, ParseCoinAssocTypes, RefundMakerPaymentArgs, RefundPaymentArgs, SearchForFundingSpendErr, SendMakerPaymentArgs, SwapTxTypeWithSecretHash, TakerCoinSwapOpsV2, ToBytes, TradePreimageValue, Transaction, TxPreimageWithSig, ValidateTakerFundingArgs}; use common::executor::abortable_queue::AbortableQueue; @@ -1004,7 +1004,7 @@ impl { +struct NegotiationData { taker_payment_locktime: u64, taker_funding_locktime: u64, maker_coin_htlc_pub_from_taker: MakerCoin::Pubkey, @@ -1014,7 +1014,7 @@ struct NegotiationData { taker_secret_hash: Vec, } -impl NegotiationData { +impl NegotiationData { fn to_stored_data(&self) -> StoredNegotiationData { StoredNegotiationData { taker_payment_locktime: self.taker_payment_locktime, @@ -1048,14 +1048,14 @@ impl NegotiationData { +struct WaitingForTakerFunding { maker_coin_start_block: u64, taker_coin_start_block: u64, negotiation_data: NegotiationData, maker_payment_trade_fee: SavedTradeFee, } -impl TransitionFrom> +impl TransitionFrom> for WaitingForTakerFunding { } @@ -1133,7 +1133,7 @@ impl { +struct TakerFundingReceived { maker_coin_start_block: u64, taker_coin_start_block: u64, negotiation_data: NegotiationData, @@ -1141,8 +1141,8 @@ struct TakerFundingReceived TransitionFrom> - for TakerFundingReceived +impl + TransitionFrom> for TakerFundingReceived { } @@ -1245,7 +1245,7 @@ impl { +struct MakerPaymentSentFundingSpendGenerated { maker_coin_start_block: u64, taker_coin_start_block: u64, negotiation_data: NegotiationData, @@ -1254,7 +1254,8 @@ struct MakerPaymentSentFundingSpendGenerated TransitionFrom> +impl + TransitionFrom> for MakerPaymentSentFundingSpendGenerated { } @@ -1414,7 +1415,7 @@ pub enum MakerPaymentRefundReason { ErrorOnTakerFundingSpendSearch(String), } -struct MakerPaymentRefundRequired { +struct MakerPaymentRefundRequired { maker_coin_start_block: u64, taker_coin_start_block: u64, negotiation_data: NegotiationData, @@ -1422,12 +1423,12 @@ struct MakerPaymentRefundRequired +impl TransitionFrom> for MakerPaymentRefundRequired { } -impl +impl TransitionFrom> for MakerPaymentRefundRequired { } @@ -1548,7 +1549,7 @@ impl { +struct TakerPaymentReceived { maker_coin_start_block: u64, taker_coin_start_block: u64, maker_payment: MakerCoin::Tx, @@ -1556,7 +1557,7 @@ struct TakerPaymentReceived, } -impl +impl TransitionFrom> for TakerPaymentReceived { @@ -1728,7 +1729,7 @@ impl { +struct TakerPaymentSpent { maker_coin_start_block: u64, taker_coin_start_block: u64, maker_payment: MakerCoin::Tx, @@ -1736,8 +1737,8 @@ struct TakerPaymentSpent { taker_payment_spend: TakerCoin::Tx, } -impl TransitionFrom> - for TakerPaymentSpent +impl + TransitionFrom> for TakerPaymentSpent { } @@ -1844,15 +1845,15 @@ impl TransitionFrom> for Aborted {} impl TransitionFrom> for Aborted {} -impl TransitionFrom> - for Aborted +impl + TransitionFrom> for Aborted { } -impl TransitionFrom> - for Aborted +impl + TransitionFrom> for Aborted { } -impl +impl TransitionFrom> for Aborted { } @@ -1893,12 +1894,12 @@ impl TransitionFrom> - for Completed +impl + TransitionFrom> for Completed { } -struct MakerPaymentRefunded { +struct MakerPaymentRefunded { taker_coin: PhantomData, maker_payment: MakerCoin::Tx, maker_payment_refund: MakerCoin::Tx, @@ -1942,7 +1943,7 @@ impl +impl TransitionFrom> for MakerPaymentRefunded { } diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs b/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs index ec5c88c0c5..77b34e476f 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs @@ -8,10 +8,11 @@ use crate::mm2::lp_swap::{broadcast_swap_v2_msg_every, check_balance_for_taker_s TAKER_SWAP_V2_TYPE}; use async_trait::async_trait; use bitcrypto::{dhash160, sha256}; -use coins::{CanRefundHtlc, CoinAssocTypes, ConfirmPaymentInput, DexFee, FeeApproxStage, GenTakerFundingSpendArgs, - GenTakerPaymentSpendArgs, MakerCoinSwapOpsV2, MmCoin, RefundFundingSecretArgs, RefundPaymentArgs, - SendTakerFundingArgs, SpendMakerPaymentArgs, SwapTxTypeWithSecretHash, TakerCoinSwapOpsV2, ToBytes, - TradeFee, TradePreimageValue, Transaction, TxPreimageWithSig, ValidateMakerPaymentArgs}; +use coins::{CanRefundHtlc, ConfirmPaymentInput, DexFee, FeeApproxStage, GenTakerFundingSpendArgs, + GenTakerPaymentSpendArgs, MakerCoinSwapOpsV2, MmCoin, ParseCoinAssocTypes, RefundFundingSecretArgs, + RefundPaymentArgs, SendTakerFundingArgs, SpendMakerPaymentArgs, SwapTxTypeWithSecretHash, + TakerCoinSwapOpsV2, ToBytes, TradeFee, TradePreimageValue, Transaction, TxPreimageWithSig, + ValidateMakerPaymentArgs}; use common::executor::abortable_queue::AbortableQueue; use common::executor::{AbortableSystem, Timer}; use common::log::{debug, error, info, warn}; @@ -1153,7 +1154,7 @@ impl { +struct NegotiationData { maker_secret_hash: Vec, maker_payment_locktime: u64, maker_coin_htlc_pub_from_maker: MakerCoin::Pubkey, @@ -1163,7 +1164,7 @@ struct NegotiationData { taker_coin_maker_address: TakerCoin::Address, } -impl NegotiationData { +impl NegotiationData { fn to_stored_data(&self) -> StoredNegotiationData { StoredNegotiationData { maker_payment_locktime: self.maker_payment_locktime, @@ -1199,7 +1200,7 @@ impl NegotiationData { +struct Negotiated { maker_coin_start_block: u64, taker_coin_start_block: u64, negotiation_data: NegotiationData, @@ -1207,7 +1208,7 @@ struct Negotiated { maker_payment_spend_fee: SavedTradeFee, } -impl TransitionFrom> +impl TransitionFrom> for Negotiated { } @@ -1270,7 +1271,7 @@ impl { +struct TakerFundingSent { maker_coin_start_block: u64, taker_coin_start_block: u64, taker_funding: TakerCoin::Tx, @@ -1388,7 +1389,7 @@ impl TransitionFrom> +impl TransitionFrom> for TakerFundingSent { } @@ -1411,7 +1412,7 @@ impl { +struct MakerPaymentAndFundingSpendPreimgReceived { maker_coin_start_block: u64, taker_coin_start_block: u64, negotiation_data: NegotiationData, @@ -1420,7 +1421,8 @@ struct MakerPaymentAndFundingSpendPreimgReceived TransitionFrom> +impl + TransitionFrom> for MakerPaymentAndFundingSpendPreimgReceived { } @@ -1585,7 +1587,7 @@ impl { +struct TakerPaymentSent { maker_coin_start_block: u64, taker_coin_start_block: u64, taker_payment: TakerCoin::Tx, @@ -1593,11 +1595,11 @@ struct TakerPaymentSent { negotiation_data: NegotiationData, } -impl TransitionFrom> - for TakerPaymentSent +impl + TransitionFrom> for TakerPaymentSent { } -impl +impl TransitionFrom> for TakerPaymentSent { @@ -1750,7 +1752,7 @@ pub enum TakerFundingRefundReason { MakerPaymentNotConfirmedInTime(String), } -struct TakerFundingRefundRequired { +struct TakerFundingRefundRequired { maker_coin_start_block: u64, taker_coin_start_block: u64, taker_funding: TakerCoin::Tx, @@ -1758,17 +1760,17 @@ struct TakerFundingRefundRequired TransitionFrom> - for TakerFundingRefundRequired +impl + TransitionFrom> for TakerFundingRefundRequired { } -impl +impl TransitionFrom> for TakerFundingRefundRequired { } -impl TransitionFrom> - for TakerFundingRefundRequired +impl + TransitionFrom> for TakerFundingRefundRequired { } @@ -1842,18 +1844,18 @@ pub enum TakerPaymentRefundReason { MakerDidNotSpendInTime(String), } -struct TakerPaymentRefundRequired { +struct TakerPaymentRefundRequired { taker_payment: TakerCoin::Tx, negotiation_data: NegotiationData, reason: TakerPaymentRefundReason, } -impl TransitionFrom> - for TakerPaymentRefundRequired +impl + TransitionFrom> for TakerPaymentRefundRequired { } -impl TransitionFrom> - for TakerPaymentRefundRequired +impl + TransitionFrom> for TakerPaymentRefundRequired { } @@ -1939,7 +1941,7 @@ impl { +struct MakerPaymentConfirmed { maker_coin_start_block: u64, taker_coin_start_block: u64, maker_payment: MakerCoin::Tx, @@ -1948,7 +1950,7 @@ struct MakerPaymentConfirmed, } -impl +impl TransitionFrom> for MakerPaymentConfirmed { @@ -2035,7 +2037,7 @@ impl { +struct TakerPaymentSpent { maker_coin_start_block: u64, taker_coin_start_block: u64, maker_payment: MakerCoin::Tx, @@ -2044,8 +2046,8 @@ struct TakerPaymentSpent { negotiation_data: NegotiationData, } -impl TransitionFrom> - for TakerPaymentSpent +impl + TransitionFrom> for TakerPaymentSpent { } @@ -2134,7 +2136,7 @@ impl { +struct MakerPaymentSpent { maker_coin_start_block: u64, taker_coin_start_block: u64, maker_payment: MakerCoin::Tx, @@ -2143,8 +2145,8 @@ struct MakerPaymentSpent { maker_payment_spend: MakerCoin::Tx, } -impl TransitionFrom> - for MakerPaymentSpent +impl + TransitionFrom> for MakerPaymentSpent { } @@ -2255,19 +2257,19 @@ impl TransitionFrom> for Aborted {} impl TransitionFrom> for Aborted {} -impl TransitionFrom> +impl TransitionFrom> for Aborted { } -impl TransitionFrom> - for Aborted +impl + TransitionFrom> for Aborted { } -impl +impl TransitionFrom> for Aborted { } -impl +impl TransitionFrom> for Aborted { } @@ -2308,12 +2310,12 @@ impl TransitionFrom> - for Completed +impl + TransitionFrom> for Completed { } -struct TakerFundingRefunded { +struct TakerFundingRefunded { maker_coin: PhantomData, funding_tx: TakerCoin::Tx, funding_refund_tx: TakerCoin::Tx, @@ -2357,12 +2359,12 @@ impl +impl TransitionFrom> for TakerFundingRefunded { } -struct TakerPaymentRefunded { +struct TakerPaymentRefunded { maker_coin: PhantomData, taker_payment: TakerCoin::Tx, taker_payment_refund: TransactionIdentifier, @@ -2403,7 +2405,7 @@ impl +impl TransitionFrom> for TakerPaymentRefunded { } diff --git a/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs b/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs index 613ec4373e..f2b7c8d96b 100644 --- a/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs +++ b/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs @@ -7,7 +7,7 @@ pub use mm2_test_helpers::for_tests::{check_my_swap_status, check_recent_swaps, ETH_DEV_SWAP_CONTRACT, ETH_DEV_TOKEN_CONTRACT, MAKER_ERROR_EVENTS, MAKER_SUCCESS_EVENTS, TAKER_ERROR_EVENTS, TAKER_SUCCESS_EVENTS}; -use crate::docker_tests::eth_docker_tests::fill_eth; +use super::eth_docker_tests::{fill_eth, geth_account}; use bitcrypto::{dhash160, ChecksumType}; use chain::TransactionOutput; use coins::eth::{addr_from_raw_pubkey, eth_coin_from_conf_and_request, EthCoin}; @@ -24,6 +24,7 @@ use coins::utxo::{coin_daemon_data_dir, sat_from_big_decimal, zcash_params_path, use coins::{CoinProtocol, ConfirmPaymentInput, MarketCoinOps, PrivKeyBuildPolicy, Transaction}; use crypto::privkey::key_pair_from_seed; use crypto::Secp256k1Secret; +use ethabi::Token; use ethereum_types::{H160 as H160Eth, U256}; use futures01::Future; use http::StatusCode; @@ -48,7 +49,7 @@ use testcontainers::clients::Cli; use testcontainers::core::WaitFor; use testcontainers::{Container, GenericImage, RunnableImage}; use web3::transports::Http; -use web3::types::TransactionRequest; +use web3::types::{BlockId, BlockNumber, TransactionRequest}; use web3::Web3; lazy_static! { @@ -80,6 +81,12 @@ pub static mut GETH_ERC20_CONTRACT: H160Eth = H160Eth::zero(); pub static mut GETH_SWAP_CONTRACT: H160Eth = H160Eth::zero(); /// Swap contract (with watchers support) address on Geth dev node pub static mut GETH_WATCHERS_SWAP_CONTRACT: H160Eth = H160Eth::zero(); +/// ERC721 token address on Geth dev node +pub static mut GETH_ERC721_CONTRACT: H160Eth = H160Eth::zero(); +/// ERC1155 token address on Geth dev node +pub static mut GETH_ERC1155_CONTRACT: H160Eth = H160Eth::zero(); +/// Nft Swap contract address on Geth dev node +pub static mut GETH_NFT_SWAP_CONTRACT: H160Eth = H160Eth::zero(); pub static GETH_RPC_URL: &str = "http://127.0.0.1:8545"; pub const UTXO_ASSET_DOCKER_IMAGE: &str = "docker.io/artempikulin/testblockchain"; @@ -89,6 +96,11 @@ pub const GETH_DOCKER_IMAGE_WITH_TAG: &str = "docker.io/ethereum/client-go:stabl pub const QTUM_ADDRESS_LABEL: &str = "MM2_ADDRESS_LABEL"; +/// ERC721_TEST_TOKEN has additional mint function +pub const ERC721_TEST_ABI: &str = include_str!("../../../mm2_test_helpers/dummy_files/erc721_test_abi.json"); +/// ERC1155_TEST_TOKEN has additional mint function +pub const ERC1155_TEST_ABI: &str = include_str!("../../../mm2_test_helpers/dummy_files/erc1155_test_abi.json"); + /// Ticker of MYCOIN dockerized blockchain. pub const MYCOIN: &str = "MYCOIN"; /// Ticker of MYCOIN1 dockerized blockchain. @@ -97,6 +109,9 @@ pub const MYCOIN1: &str = "MYCOIN1"; pub const ERC20_TOKEN_BYTES: &str = "6080604052600860ff16600a0a633b9aca000260005534801561002157600080fd5b50600054600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610c69806100776000396000f3006080604052600436106100a4576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100a9578063095ea7b31461013957806318160ddd1461019e57806323b872dd146101c9578063313ce5671461024e5780635a3b7e421461027f57806370a082311461030f57806395d89b4114610366578063a9059cbb146103f6578063dd62ed3e1461045b575b600080fd5b3480156100b557600080fd5b506100be6104d2565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100fe5780820151818401526020810190506100e3565b50505050905090810190601f16801561012b5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561014557600080fd5b50610184600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061050b565b604051808215151515815260200191505060405180910390f35b3480156101aa57600080fd5b506101b36106bb565b6040518082815260200191505060405180910390f35b3480156101d557600080fd5b50610234600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106c1565b604051808215151515815260200191505060405180910390f35b34801561025a57600080fd5b506102636109a1565b604051808260ff1660ff16815260200191505060405180910390f35b34801561028b57600080fd5b506102946109a6565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156102d45780820151818401526020810190506102b9565b50505050905090810190601f1680156103015780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561031b57600080fd5b50610350600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109df565b6040518082815260200191505060405180910390f35b34801561037257600080fd5b5061037b6109f7565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103bb5780820151818401526020810190506103a0565b50505050905090810190601f1680156103e85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561040257600080fd5b50610441600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a30565b604051808215151515815260200191505060405180910390f35b34801561046757600080fd5b506104bc600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610be1565b6040518082815260200191505060405180910390f35b6040805190810160405280600881526020017f515243205445535400000000000000000000000000000000000000000000000081525081565b60008260008173ffffffffffffffffffffffffffffffffffffffff161415151561053457600080fd5b60008314806105bf57506000600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054145b15156105ca57600080fd5b82600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925856040518082815260200191505060405180910390a3600191505092915050565b60005481565b60008360008173ffffffffffffffffffffffffffffffffffffffff16141515156106ea57600080fd5b8360008173ffffffffffffffffffffffffffffffffffffffff161415151561071157600080fd5b610797600260008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205485610c06565b600260008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610860600160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205485610c06565b600160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506108ec600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205485610c1f565b600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef866040518082815260200191505060405180910390a36001925050509392505050565b600881565b6040805190810160405280600981526020017f546f6b656e20302e31000000000000000000000000000000000000000000000081525081565b60016020528060005260406000206000915090505481565b6040805190810160405280600381526020017f515443000000000000000000000000000000000000000000000000000000000081525081565b60008260008173ffffffffffffffffffffffffffffffffffffffff1614151515610a5957600080fd5b610aa2600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205484610c06565b600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610b2e600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205484610c1f565b600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a3600191505092915050565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000818310151515610c1457fe5b818303905092915050565b6000808284019050838110151515610c3357fe5b80915050929150505600a165627a7a723058207f2e5248b61b80365ea08a0f6d11ac0b47374c4dfd538de76bc2f19591bbbba40029"; pub const SWAP_CONTRACT_BYTES: &str = "608060405234801561001057600080fd5b50611437806100206000396000f3fe60806040526004361061004a5760003560e01c806302ed292b1461004f5780630716326d146100de578063152cf3af1461017b57806346fc0294146101f65780639b415b2a14610294575b600080fd5b34801561005b57600080fd5b506100dc600480360360a081101561007257600080fd5b81019080803590602001909291908035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610339565b005b3480156100ea57600080fd5b506101176004803603602081101561010157600080fd5b8101908080359060200190929190505050610867565b60405180846bffffffffffffffffffffffff19166bffffffffffffffffffffffff191681526020018367ffffffffffffffff1667ffffffffffffffff16815260200182600381111561016557fe5b60ff168152602001935050505060405180910390f35b6101f46004803603608081101561019157600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080356bffffffffffffffffffffffff19169060200190929190803567ffffffffffffffff1690602001909291905050506108bf565b005b34801561020257600080fd5b50610292600480360360a081101561021957600080fd5b81019080803590602001909291908035906020019092919080356bffffffffffffffffffffffff19169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bd9565b005b610337600480360360c08110156102aa57600080fd5b810190808035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080356bffffffffffffffffffffffff19169060200190929190803567ffffffffffffffff169060200190929190505050610fe2565b005b6001600381111561034657fe5b600080878152602001908152602001600020600001601c9054906101000a900460ff16600381111561037457fe5b1461037e57600080fd5b6000600333836003600288604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b602083106103db57805182526020820191506020810190506020830392506103b8565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa15801561041d573d6000803e3d6000fd5b5050506040513d602081101561043257600080fd5b8101908080519060200190929190505050604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b602083106104955780518252602082019150602081019050602083039250610472565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa1580156104d7573d6000803e3d6000fd5b5050506040515160601b8689604051602001808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b81526014018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401846bffffffffffffffffffffffff19166bffffffffffffffffffffffff191681526014018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401828152602001955050505050506040516020818303038152906040526040518082805190602001908083835b602083106105fc57805182526020820191506020810190506020830392506105d9565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa15801561063e573d6000803e3d6000fd5b5050506040515160601b905060008087815260200190815260200160002060000160009054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff19161461069657600080fd5b6002600080888152602001908152602001600020600001601c6101000a81548160ff021916908360038111156106c857fe5b0217905550600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561074e573373ffffffffffffffffffffffffffffffffffffffff166108fc869081150290604051600060405180830381858888f19350505050158015610748573d6000803e3d6000fd5b50610820565b60008390508073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33886040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b1580156107da57600080fd5b505af11580156107ee573d6000803e3d6000fd5b505050506040513d602081101561080457600080fd5b810190808051906020019092919050505061081e57600080fd5b505b7f36c177bcb01c6d568244f05261e2946c8c977fa50822f3fa098c470770ee1f3e8685604051808381526020018281526020019250505060405180910390a1505050505050565b60006020528060005260406000206000915090508060000160009054906101000a900460601b908060000160149054906101000a900467ffffffffffffffff169080600001601c9054906101000a900460ff16905083565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141580156108fc5750600034115b801561094057506000600381111561091057fe5b600080868152602001908152602001600020600001601c9054906101000a900460ff16600381111561093e57fe5b145b61094957600080fd5b60006003843385600034604051602001808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b81526014018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401846bffffffffffffffffffffffff19166bffffffffffffffffffffffff191681526014018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401828152602001955050505050506040516020818303038152906040526040518082805190602001908083835b60208310610a6c5780518252602082019150602081019050602083039250610a49565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa158015610aae573d6000803e3d6000fd5b5050506040515160601b90506040518060600160405280826bffffffffffffffffffffffff191681526020018367ffffffffffffffff16815260200160016003811115610af757fe5b81525060008087815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c021790555060208201518160000160146101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550604082015181600001601c6101000a81548160ff02191690836003811115610b9357fe5b02179055509050507fccc9c05183599bd3135da606eaaf535daffe256e9de33c048014cffcccd4ad57856040518082815260200191505060405180910390a15050505050565b60016003811115610be657fe5b600080878152602001908152602001600020600001601c9054906101000a900460ff166003811115610c1457fe5b14610c1e57600080fd5b600060038233868689604051602001808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b81526014018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401846bffffffffffffffffffffffff19166bffffffffffffffffffffffff191681526014018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401828152602001955050505050506040516020818303038152906040526040518082805190602001908083835b60208310610d405780518252602082019150602081019050602083039250610d1d565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa158015610d82573d6000803e3d6000fd5b5050506040515160601b905060008087815260200190815260200160002060000160009054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff1916148015610e10575060008087815260200190815260200160002060000160149054906101000a900467ffffffffffffffff1667ffffffffffffffff164210155b610e1957600080fd5b6003600080888152602001908152602001600020600001601c6101000a81548160ff02191690836003811115610e4b57fe5b0217905550600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610ed1573373ffffffffffffffffffffffffffffffffffffffff166108fc869081150290604051600060405180830381858888f19350505050158015610ecb573d6000803e3d6000fd5b50610fa3565b60008390508073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33886040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b158015610f5d57600080fd5b505af1158015610f71573d6000803e3d6000fd5b505050506040513d6020811015610f8757600080fd5b8101908080519060200190929190505050610fa157600080fd5b505b7f1797d500133f8e427eb9da9523aa4a25cb40f50ebc7dbda3c7c81778973f35ba866040518082815260200191505060405180910390a1505050505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415801561101f5750600085115b801561106357506000600381111561103357fe5b600080888152602001908152602001600020600001601c9054906101000a900460ff16600381111561106157fe5b145b61106c57600080fd5b60006003843385888a604051602001808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b81526014018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401846bffffffffffffffffffffffff19166bffffffffffffffffffffffff191681526014018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401828152602001955050505050506040516020818303038152906040526040518082805190602001908083835b6020831061118e578051825260208201915060208101905060208303925061116b565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa1580156111d0573d6000803e3d6000fd5b5050506040515160601b90506040518060600160405280826bffffffffffffffffffffffff191681526020018367ffffffffffffffff1681526020016001600381111561121957fe5b81525060008089815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c021790555060208201518160000160146101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550604082015181600001601c6101000a81548160ff021916908360038111156112b557fe5b021790555090505060008590508073ffffffffffffffffffffffffffffffffffffffff166323b872dd33308a6040518463ffffffff1660e01b8152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019350505050602060405180830381600087803b15801561137d57600080fd5b505af1158015611391573d6000803e3d6000fd5b505050506040513d60208110156113a757600080fd5b81019080805190602001909291905050506113c157600080fd5b7fccc9c05183599bd3135da606eaaf535daffe256e9de33c048014cffcccd4ad57886040518082815260200191505060405180910390a1505050505050505056fea265627a7a723158208c83db436905afce0b7be1012be64818c49323c12d451fe2ab6bce76ff6421c964736f6c63430005110032"; pub const WATCHERS_SWAP_CONTRACT_BYTES: &str = "608060405234801561000f575f80fd5b50612aa48061001d5f395ff3fe608060405260043610610085575f3560e01c806346fc02941161005857806346fc0294146101275780636a3227861461014f5780639b415b2a1461016b578063b5985c4d14610193578063cd1dde34146101bb57610085565b806302ed292b146100895780630716326d146100b15780630971fd54146100ef578063152cf3af1461010b575b5f80fd5b348015610094575f80fd5b506100af60048036038101906100aa9190611e1d565b6101e3565b005b3480156100bc575f80fd5b506100d760048036038101906100d29190611e94565b610518565b6040516100e693929190611f8e565b60405180910390f35b6101096004803603810190610104919061206f565b610568565b005b6101256004803603810190610120919061210c565b610787565b005b348015610132575f80fd5b5061014d60048036038101906101489190612170565b61099d565b005b610169600480360381019061016491906121e7565b610c4d565b005b348015610176575f80fd5b50610191600480360381019061018c91906122ab565b610f61565b005b34801561019e575f80fd5b506101b960048036038101906101b49190612334565b611203565b005b3480156101c6575f80fd5b506101e160048036038101906101dc91906123f8565b611887565b005b600160038111156101f7576101f6611f1b565b5b5f808781526020019081526020015f205f01601c9054906101000a900460ff16600381111561022957610228611f1b565b5b14610232575f80fd5b5f60033383600360028860405160200161024c91906124dc565b6040516020818303038152906040526040516102689190612562565b602060405180830381855afa158015610283573d5f803e3d5ffd5b5050506040513d601f19601f820116820180604052508101906102a6919061258c565b6040516020016102b691906124dc565b6040516020818303038152906040526040516102d29190612562565b602060405180830381855afa1580156102ed573d5f803e3d5ffd5b5050506040515160601b868960405160200161030d95949392919061263c565b6040516020818303038152906040526040516103299190612562565b602060405180830381855afa158015610344573d5f803e3d5ffd5b5050506040515160601b90505f808781526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614610397575f80fd5b60025f808881526020019081526020015f205f01601c6101000a81548160ff021916908360038111156103cd576103cc611f1b565b5b02179055505f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361044e573373ffffffffffffffffffffffffffffffffffffffff166108fc8690811502906040515f60405180830381858888f19350505050158015610448573d5f803e3d5ffd5b506104d7565b5f8390508073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33886040518363ffffffff1660e01b815260040161048d9291906126b8565b6020604051808303815f875af11580156104a9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104cd91906126f3565b6104d5575f80fd5b505b7f36c177bcb01c6d568244f05261e2946c8c977fa50822f3fa098c470770ee1f3e868560405161050892919061272d565b60405180910390a1505050505050565b5f602052805f5260405f205f91509050805f015f9054906101000a900460601b90805f0160149054906101000a900467ffffffffffffffff1690805f01601c9054906101000a900460ff16905083565b5f73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16141580156105a357505f34115b80156105f157505f60038111156105bd576105bc611f1b565b5b5f808981526020019081526020015f205f01601c9054906101000a900460ff1660038111156105ef576105ee611f1b565b5b145b6105f9575f80fd5b5f60038733885f3489898960405160200161061b9897969594939291906127e7565b6040516020818303038152906040526040516106379190612562565b602060405180830381855afa158015610652573d5f803e3d5ffd5b5050506040515160601b90506040518060600160405280826bffffffffffffffffffffffff191681526020018667ffffffffffffffff168152602001600160038111156106a2576106a1611f1b565b5b8152505f808a81526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506040820151815f01601c6101000a81548160ff0219169083600381111561073e5761073d611f1b565b5b02179055509050507fccc9c05183599bd3135da606eaaf535daffe256e9de33c048014cffcccd4ad57886040516107759190612878565b60405180910390a15050505050505050565b5f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141580156107c257505f34115b801561081057505f60038111156107dc576107db611f1b565b5b5f808681526020019081526020015f205f01601c9054906101000a900460ff16600381111561080e5761080d611f1b565b5b145b610818575f80fd5b5f60038433855f3460405160200161083495949392919061263c565b6040516020818303038152906040526040516108509190612562565b602060405180830381855afa15801561086b573d5f803e3d5ffd5b5050506040515160601b90506040518060600160405280826bffffffffffffffffffffffff191681526020018367ffffffffffffffff168152602001600160038111156108bb576108ba611f1b565b5b8152505f808781526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506040820151815f01601c6101000a81548160ff0219169083600381111561095757610956611f1b565b5b02179055509050507fccc9c05183599bd3135da606eaaf535daffe256e9de33c048014cffcccd4ad578560405161098e9190612878565b60405180910390a15050505050565b600160038111156109b1576109b0611f1b565b5b5f808781526020019081526020015f205f01601c9054906101000a900460ff1660038111156109e3576109e2611f1b565b5b146109ec575f80fd5b5f60038233868689604051602001610a0895949392919061263c565b604051602081830303815290604052604051610a249190612562565b602060405180830381855afa158015610a3f573d5f803e3d5ffd5b5050506040515160601b90505f808781526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff1916148015610ac657505f808781526020019081526020015f205f0160149054906101000a900467ffffffffffffffff1667ffffffffffffffff164210155b610ace575f80fd5b60035f808881526020019081526020015f205f01601c6101000a81548160ff02191690836003811115610b0457610b03611f1b565b5b02179055505f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610b85573373ffffffffffffffffffffffffffffffffffffffff166108fc8690811502906040515f60405180830381858888f19350505050158015610b7f573d5f803e3d5ffd5b50610c0e565b5f8390508073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33886040518363ffffffff1660e01b8152600401610bc49291906126b8565b6020604051808303815f875af1158015610be0573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c0491906126f3565b610c0c575f80fd5b505b7f1797d500133f8e427eb9da9523aa4a25cb40f50ebc7dbda3c7c81778973f35ba86604051610c3d9190612878565b60405180910390a1505050505050565b5f73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614158015610c8857505f88115b8015610cd657505f6003811115610ca257610ca1611f1b565b5b5f808b81526020019081526020015f205f01601c9054906101000a900460ff166003811115610cd457610cd3611f1b565b5b145b610cde575f80fd5b5f6003811115610cf157610cf0611f1b565b5b836003811115610d0457610d03611f1b565b5b14158015610d365750600380811115610d2057610d1f611f1b565b5b836003811115610d3357610d32611f1b565b5b14155b15610d4757803414610d46575f80fd5b5b5f60038733888b8d898989604051602001610d699897969594939291906127e7565b604051602081830303815290604052604051610d859190612562565b602060405180830381855afa158015610da0573d5f803e3d5ffd5b5050506040515160601b90506040518060600160405280826bffffffffffffffffffffffff191681526020018667ffffffffffffffff16815260200160016003811115610df057610def611f1b565b5b8152505f808c81526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506040820151815f01601c6101000a81548160ff02191690836003811115610e8c57610e8b611f1b565b5b02179055509050505f8890508073ffffffffffffffffffffffffffffffffffffffff166323b872dd33308d6040518463ffffffff1660e01b8152600401610ed593929190612891565b6020604051808303815f875af1158015610ef1573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f1591906126f3565b610f1d575f80fd5b7fccc9c05183599bd3135da606eaaf535daffe256e9de33c048014cffcccd4ad578b604051610f4c9190612878565b60405180910390a15050505050505050505050565b5f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614158015610f9c57505f85115b8015610fea57505f6003811115610fb657610fb5611f1b565b5b5f808881526020019081526020015f205f01601c9054906101000a900460ff166003811115610fe857610fe7611f1b565b5b145b610ff2575f80fd5b5f6003843385888a60405160200161100e95949392919061263c565b60405160208183030381529060405260405161102a9190612562565b602060405180830381855afa158015611045573d5f803e3d5ffd5b5050506040515160601b90506040518060600160405280826bffffffffffffffffffffffff191681526020018367ffffffffffffffff1681526020016001600381111561109557611094611f1b565b5b8152505f808981526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506040820151815f01601c6101000a81548160ff0219169083600381111561113157611130611f1b565b5b02179055509050505f8590508073ffffffffffffffffffffffffffffffffffffffff166323b872dd33308a6040518463ffffffff1660e01b815260040161117a93929190612891565b6020604051808303815f875af1158015611196573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111ba91906126f3565b6111c2575f80fd5b7fccc9c05183599bd3135da606eaaf535daffe256e9de33c048014cffcccd4ad57886040516111f19190612878565b60405180910390a15050505050505050565b6001600381111561121757611216611f1b565b5b5f808b81526020019081526020015f205f01601c9054906101000a900460ff16600381111561124957611248611f1b565b5b14611289576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161128090612920565b60405180910390fd5b5f60038587600360028c6040516020016112a391906124dc565b6040516020818303038152906040526040516112bf9190612562565b602060405180830381855afa1580156112da573d5f803e3d5ffd5b5050506040513d601f19601f820116820180604052508101906112fd919061258c565b60405160200161130d91906124dc565b6040516020818303038152906040526040516113299190612562565b602060405180830381855afa158015611344573d5f803e3d5ffd5b5050506040515160601b8a8d89898960405160200161136a9897969594939291906127e7565b6040516020818303038152906040526040516113869190612562565b602060405180830381855afa1580156113a1573d5f803e3d5ffd5b5050506040515160601b90505f808b81526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff19161461142b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161142290612988565b60405180910390fd5b60025f808c81526020019081526020015f205f01601c6101000a81548160ff0219169083600381111561146157611460611f1b565b5b02179055505f73ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff160361159e575f8060038111156114ad576114ac611f1b565b5b8560038111156114c0576114bf611f1b565b5b1480156114cb575083155b6114e057828a6114db91906129d3565b6114e2565b895b90508573ffffffffffffffffffffffffffffffffffffffff166108fc8290811502906040515f60405180830381858888f19350505050158015611527573d5f803e3d5ffd5b5060038081111561153b5761153a611f1b565b5b85600381111561154e5761154d611f1b565b5b03611598573373ffffffffffffffffffffffffffffffffffffffff166108fc8490811502906040515f60405180830381858888f19350505050158015611596573d5f803e3d5ffd5b505b50611786565b5f6003808111156115b2576115b1611f1b565b5b8560038111156115c5576115c4611f1b565b5b146115d057896115dd565b828a6115dc91906129d3565b5b90505f8890508073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb88846040518363ffffffff1660e01b815260040161161e9291906126b8565b6020604051808303815f875af115801561163a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061165e91906126f3565b61169d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161169490612a50565b60405180910390fd5b6003808111156116b0576116af611f1b565b5b8660038111156116c3576116c2611f1b565b5b03611783578073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33866040518363ffffffff1660e01b81526004016117039291906126b8565b6020604051808303815f875af115801561171f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061174391906126f3565b611782576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161177990612a50565b60405180910390fd5b5b50505b6002600381111561179a57611799611f1b565b5b8460038111156117ad576117ac611f1b565b5b036117f7578573ffffffffffffffffffffffffffffffffffffffff166108fc8390811502906040515f60405180830381858888f193505050501580156117f5573d5f803e3d5ffd5b505b8215611842573373ffffffffffffffffffffffffffffffffffffffff166108fc8390811502906040515f60405180830381858888f19350505050158015611840573d5f803e3d5ffd5b505b7f36c177bcb01c6d568244f05261e2946c8c977fa50822f3fa098c470770ee1f3e8a8960405161187392919061272d565b60405180910390a150505050505050505050565b6001600381111561189b5761189a611f1b565b5b5f808b81526020019081526020015f205f01601c9054906101000a900460ff1660038111156118cd576118cc611f1b565b5b146118d6575f80fd5b5f600385878a8a8d8989896040516020016118f89897969594939291906127e7565b6040516020818303038152906040526040516119149190612562565b602060405180830381855afa15801561192f573d5f803e3d5ffd5b5050506040515160601b90505f808b81526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff19161480156119b657505f808b81526020019081526020015f205f0160149054906101000a900467ffffffffffffffff1667ffffffffffffffff164210155b6119be575f80fd5b60035f808c81526020019081526020015f205f01601c6101000a81548160ff021916908360038111156119f4576119f3611f1b565b5b02179055505f73ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1603611b27575f806003811115611a4057611a3f611f1b565b5b856003811115611a5357611a52611f1b565b5b14611a6957828a611a6491906129d3565b611a6b565b895b90508673ffffffffffffffffffffffffffffffffffffffff166108fc8290811502906040515f60405180830381858888f19350505050158015611ab0573d5f803e3d5ffd5b505f6003811115611ac457611ac3611f1b565b5b856003811115611ad757611ad6611f1b565b5b14611b21573373ffffffffffffffffffffffffffffffffffffffff166108fc8490811502906040515f60405180830381858888f19350505050158015611b1f573d5f803e3d5ffd5b505b50611d16565b5f600380811115611b3b57611b3a611f1b565b5b856003811115611b4e57611b4d611f1b565b5b14611b595789611b66565b828a611b6591906129d3565b5b90505f8890508073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb89846040518363ffffffff1660e01b8152600401611ba79291906126b8565b6020604051808303815f875af1158015611bc3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611be791906126f3565b611bef575f80fd5b600380811115611c0257611c01611f1b565b5b866003811115611c1557611c14611f1b565b5b03611ca2578073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33866040518363ffffffff1660e01b8152600401611c559291906126b8565b6020604051808303815f875af1158015611c71573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c9591906126f3565b611c9d575f80fd5b611d13565b5f6003811115611cb557611cb4611f1b565b5b866003811115611cc857611cc7611f1b565b5b14611d12573373ffffffffffffffffffffffffffffffffffffffff166108fc8590811502906040515f60405180830381858888f19350505050158015611d10573d5f803e3d5ffd5b505b5b50505b7f1797d500133f8e427eb9da9523aa4a25cb40f50ebc7dbda3c7c81778973f35ba8a604051611d459190612878565b60405180910390a150505050505050505050565b5f80fd5b5f819050919050565b611d6f81611d5d565b8114611d79575f80fd5b50565b5f81359050611d8a81611d66565b92915050565b5f819050919050565b611da281611d90565b8114611dac575f80fd5b50565b5f81359050611dbd81611d99565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f611dec82611dc3565b9050919050565b611dfc81611de2565b8114611e06575f80fd5b50565b5f81359050611e1781611df3565b92915050565b5f805f805f60a08688031215611e3657611e35611d59565b5b5f611e4388828901611d7c565b9550506020611e5488828901611daf565b9450506040611e6588828901611d7c565b9350506060611e7688828901611e09565b9250506080611e8788828901611e09565b9150509295509295909350565b5f60208284031215611ea957611ea8611d59565b5b5f611eb684828501611d7c565b91505092915050565b5f7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000082169050919050565b611ef381611ebf565b82525050565b5f67ffffffffffffffff82169050919050565b611f1581611ef9565b82525050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b60048110611f5957611f58611f1b565b5b50565b5f819050611f6982611f48565b919050565b5f611f7882611f5c565b9050919050565b611f8881611f6e565b82525050565b5f606082019050611fa15f830186611eea565b611fae6020830185611f0c565b611fbb6040830184611f7f565b949350505050565b611fcc81611ebf565b8114611fd6575f80fd5b50565b5f81359050611fe781611fc3565b92915050565b611ff681611ef9565b8114612000575f80fd5b50565b5f8135905061201181611fed565b92915050565b60048110612023575f80fd5b50565b5f8135905061203481612017565b92915050565b5f8115159050919050565b61204e8161203a565b8114612058575f80fd5b50565b5f8135905061206981612045565b92915050565b5f805f805f805f60e0888a03121561208a57612089611d59565b5b5f6120978a828b01611d7c565b97505060206120a88a828b01611e09565b96505060406120b98a828b01611fd9565b95505060606120ca8a828b01612003565b94505060806120db8a828b01612026565b93505060a06120ec8a828b0161205b565b92505060c06120fd8a828b01611daf565b91505092959891949750929550565b5f805f806080858703121561212457612123611d59565b5b5f61213187828801611d7c565b945050602061214287828801611e09565b935050604061215387828801611fd9565b925050606061216487828801612003565b91505092959194509250565b5f805f805f60a0868803121561218957612188611d59565b5b5f61219688828901611d7c565b95505060206121a788828901611daf565b94505060406121b888828901611fd9565b93505060606121c988828901611e09565b92505060806121da88828901611e09565b9150509295509295909350565b5f805f805f805f805f6101208a8c03121561220557612204611d59565b5b5f6122128c828d01611d7c565b99505060206122238c828d01611daf565b98505060406122348c828d01611e09565b97505060606122458c828d01611e09565b96505060806122568c828d01611fd9565b95505060a06122678c828d01612003565b94505060c06122788c828d01612026565b93505060e06122898c828d0161205b565b92505061010061229b8c828d01611daf565b9150509295985092959850929598565b5f805f805f8060c087890312156122c5576122c4611d59565b5b5f6122d289828a01611d7c565b96505060206122e389828a01611daf565b95505060406122f489828a01611e09565b945050606061230589828a01611e09565b935050608061231689828a01611fd9565b92505060a061232789828a01612003565b9150509295509295509295565b5f805f805f805f805f6101208a8c03121561235257612351611d59565b5b5f61235f8c828d01611d7c565b99505060206123708c828d01611daf565b98505060406123818c828d01611d7c565b97505060606123928c828d01611e09565b96505060806123a38c828d01611e09565b95505060a06123b48c828d01611e09565b94505060c06123c58c828d01612026565b93505060e06123d68c828d0161205b565b9250506101006123e88c828d01611daf565b9150509295985092959850929598565b5f805f805f805f805f6101208a8c03121561241657612415611d59565b5b5f6124238c828d01611d7c565b99505060206124348c828d01611daf565b98505060406124458c828d01611fd9565b97505060606124568c828d01611e09565b96505060806124678c828d01611e09565b95505060a06124788c828d01611e09565b94505060c06124898c828d01612026565b93505060e061249a8c828d0161205b565b9250506101006124ac8c828d01611daf565b9150509295985092959850929598565b5f819050919050565b6124d66124d182611d5d565b6124bc565b82525050565b5f6124e782846124c5565b60208201915081905092915050565b5f81519050919050565b5f81905092915050565b5f5b8381101561252757808201518184015260208101905061250c565b5f8484015250505050565b5f61253c826124f6565b6125468185612500565b935061255681856020860161250a565b80840191505092915050565b5f61256d8284612532565b915081905092915050565b5f8151905061258681611d66565b92915050565b5f602082840312156125a1576125a0611d59565b5b5f6125ae84828501612578565b91505092915050565b5f8160601b9050919050565b5f6125cd826125b7565b9050919050565b5f6125de826125c3565b9050919050565b6125f66125f182611de2565b6125d4565b82525050565b5f819050919050565b61261661261182611ebf565b6125fc565b82525050565b5f819050919050565b61263661263182611d90565b61261c565b82525050565b5f61264782886125e5565b60148201915061265782876125e5565b6014820191506126678286612605565b60148201915061267782856125e5565b6014820191506126878284612625565b6020820191508190509695505050505050565b6126a381611de2565b82525050565b6126b281611d90565b82525050565b5f6040820190506126cb5f83018561269a565b6126d860208301846126a9565b9392505050565b5f815190506126ed81612045565b92915050565b5f6020828403121561270857612707611d59565b5b5f612715848285016126df565b91505092915050565b61272781611d5d565b82525050565b5f6040820190506127405f83018561271e565b61274d602083018461271e565b9392505050565b6004811061276557612764611f1b565b5b50565b5f81905061277582612754565b919050565b5f61278482612768565b9050919050565b5f8160f81b9050919050565b5f6127a18261278b565b9050919050565b6127b96127b48261277a565b612797565b82525050565b5f6127c982612797565b9050919050565b6127e16127dc8261203a565b6127bf565b82525050565b5f6127f2828b6125e5565b601482019150612802828a6125e5565b6014820191506128128289612605565b60148201915061282282886125e5565b6014820191506128328287612625565b60208201915061284282866127a8565b60018201915061285282856127d0565b6001820191506128628284612625565b6020820191508190509998505050505050505050565b5f60208201905061288b5f83018461271e565b92915050565b5f6060820190506128a45f83018661269a565b6128b1602083018561269a565b6128be60408301846126a9565b949350505050565b5f82825260208201905092915050565b7f5061796d656e7420776173206e6f742073656e740000000000000000000000005f82015250565b5f61290a6014836128c6565b9150612915826128d6565b602082019050919050565b5f6020820190508181035f830152612937816128fe565b9050919050565b7f496e76616c6964207061796d656e7420686173680000000000000000000000005f82015250565b5f6129726014836128c6565b915061297d8261293e565b602082019050919050565b5f6020820190508181035f83015261299f81612966565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6129dd82611d90565b91506129e883611d90565b9250828203905081811115612a00576129ff6129a6565b5b92915050565b7f546f6b656e207472616e73666572206661696c656400000000000000000000005f82015250565b5f612a3a6015836128c6565b9150612a4582612a06565b602082019050919050565b5f6020820190508181035f830152612a6781612a2e565b905091905056fea26469706673582212203106867e1b147b377237cde0aba42d82faf0282b83d7b6d62cca039d0b7f840564736f6c63430008160033"; +pub const ERC721_TEST_TOKEN_BYTES: &str = "608060405234801562000010575f80fd5b50604051620022ac380380620022ac8339818101604052810190620000369190620001ea565b8181815f9081620000489190620004a4565b5080600190816200005a9190620004a4565b505050505062000588565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b620000c6826200007e565b810181811067ffffffffffffffff82111715620000e857620000e76200008e565b5b80604052505050565b5f620000fc62000065565b90506200010a8282620000bb565b919050565b5f67ffffffffffffffff8211156200012c576200012b6200008e565b5b62000137826200007e565b9050602081019050919050565b5f5b838110156200016357808201518184015260208101905062000146565b5f8484015250505050565b5f620001846200017e846200010f565b620000f1565b905082815260208101848484011115620001a357620001a26200007a565b5b620001b084828562000144565b509392505050565b5f82601f830112620001cf57620001ce62000076565b5b8151620001e18482602086016200016e565b91505092915050565b5f80604083850312156200020357620002026200006e565b5b5f83015167ffffffffffffffff81111562000223576200022262000072565b5b6200023185828601620001b8565b925050602083015167ffffffffffffffff81111562000255576200025462000072565b5b6200026385828601620001b8565b9150509250929050565b5f81519050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f6002820490506001821680620002bc57607f821691505b602082108103620002d257620002d162000277565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f60088302620003367fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82620002f9565b620003428683620002f9565b95508019841693508086168417925050509392505050565b5f819050919050565b5f819050919050565b5f6200038c6200038662000380846200035a565b62000363565b6200035a565b9050919050565b5f819050919050565b620003a7836200036c565b620003bf620003b68262000393565b84845462000305565b825550505050565b5f90565b620003d5620003c7565b620003e28184846200039c565b505050565b5b818110156200040957620003fd5f82620003cb565b600181019050620003e8565b5050565b601f82111562000458576200042281620002d8565b6200042d84620002ea565b810160208510156200043d578190505b620004556200044c85620002ea565b830182620003e7565b50505b505050565b5f82821c905092915050565b5f6200047a5f19846008026200045d565b1980831691505092915050565b5f62000494838362000469565b9150826002028217905092915050565b620004af826200026d565b67ffffffffffffffff811115620004cb57620004ca6200008e565b5b620004d78254620002a4565b620004e48282856200040d565b5f60209050601f8311600181146200051a575f841562000505578287015190505b62000511858262000487565b86555062000580565b601f1984166200052a86620002d8565b5f5b8281101562000553578489015182556001820191506020850194506020810190506200052c565b868310156200057357848901516200056f601f89168262000469565b8355505b6001600288020188555050505b505050505050565b611d1680620005965f395ff3fe608060405234801561000f575f80fd5b50600436106100e8575f3560e01c80636352211e1161008a578063a22cb46511610064578063a22cb46514610258578063b88d4fde14610274578063c87b56dd14610290578063e985e9c5146102c0576100e8565b80636352211e146101da57806370a082311461020a57806395d89b411461023a576100e8565b8063095ea7b3116100c6578063095ea7b31461016a57806323b872dd1461018657806340c10f19146101a257806342842e0e146101be576100e8565b806301ffc9a7146100ec57806306fdde031461011c578063081812fc1461013a575b5f80fd5b610106600480360381019061010191906115a7565b6102f0565b60405161011391906115ec565b60405180910390f35b6101246103d1565b604051610131919061168f565b60405180910390f35b610154600480360381019061014f91906116e2565b610460565b604051610161919061174c565b60405180910390f35b610184600480360381019061017f919061178f565b61047b565b005b6101a0600480360381019061019b91906117cd565b610491565b005b6101bc60048036038101906101b7919061178f565b610590565b005b6101d860048036038101906101d391906117cd565b61059e565b005b6101f460048036038101906101ef91906116e2565b6105bd565b604051610201919061174c565b60405180910390f35b610224600480360381019061021f919061181d565b6105ce565b6040516102319190611857565b60405180910390f35b610242610684565b60405161024f919061168f565b60405180910390f35b610272600480360381019061026d919061189a565b610714565b005b61028e60048036038101906102899190611a04565b61072a565b005b6102aa60048036038101906102a591906116e2565b610747565b6040516102b7919061168f565b60405180910390f35b6102da60048036038101906102d59190611a84565b6107ad565b6040516102e791906115ec565b60405180910390f35b5f7f80ac58cd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806103ba57507f5b5e139f000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b806103ca57506103c98261083b565b5b9050919050565b60605f80546103df90611aef565b80601f016020809104026020016040519081016040528092919081815260200182805461040b90611aef565b80156104565780601f1061042d57610100808354040283529160200191610456565b820191905f5260205f20905b81548152906001019060200180831161043957829003601f168201915b5050505050905090565b5f61046a826108a4565b506104748261092a565b9050919050565b61048d8282610488610963565b61096a565b5050565b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610501575f6040517f64a0ae920000000000000000000000000000000000000000000000000000000081526004016104f8919061174c565b60405180910390fd5b5f610514838361050f610963565b61097c565b90508373ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161461058a578382826040517f64283d7b00000000000000000000000000000000000000000000000000000000815260040161058193929190611b1f565b60405180910390fd5b50505050565b61059a8282610b87565b5050565b6105b883838360405180602001604052805f81525061072a565b505050565b5f6105c7826108a4565b9050919050565b5f8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361063f575f6040517f89c62b64000000000000000000000000000000000000000000000000000000008152600401610636919061174c565b60405180910390fd5b60035f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20549050919050565b60606001805461069390611aef565b80601f01602080910402602001604051908101604052809291908181526020018280546106bf90611aef565b801561070a5780601f106106e15761010080835404028352916020019161070a565b820191905f5260205f20905b8154815290600101906020018083116106ed57829003601f168201915b5050505050905090565b61072661071f610963565b8383610c7a565b5050565b610735848484610491565b61074184848484610de3565b50505050565b6060610752826108a4565b505f61075c610f95565b90505f81511161077a5760405180602001604052805f8152506107a5565b8061078484610fab565b604051602001610795929190611b8e565b6040516020818303038152906040525b915050919050565b5f60055f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff16905092915050565b5f7f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b5f806108af83611075565b90505f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361092157826040517f7e2732890000000000000000000000000000000000000000000000000000000081526004016109189190611857565b60405180910390fd5b80915050919050565b5f60045f8381526020019081526020015f205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b5f33905090565b61097783838360016110ae565b505050565b5f8061098784611075565b90505f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16146109c8576109c781848661126d565b5b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610a5357610a075f855f806110ae565b600160035f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f82825403925050819055505b5f73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614610ad257600160035f8773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f82825401925050819055505b8460025f8681526020019081526020015f205f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550838573ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4809150509392505050565b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610bf7575f6040517f64a0ae92000000000000000000000000000000000000000000000000000000008152600401610bee919061174c565b60405180910390fd5b5f610c0383835f61097c565b90505f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610c75575f6040517f73c6ac6e000000000000000000000000000000000000000000000000000000008152600401610c6c919061174c565b60405180910390fd5b505050565b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610cea57816040517f5b08ba18000000000000000000000000000000000000000000000000000000008152600401610ce1919061174c565b60405180910390fd5b8060055f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051610dd691906115ec565b60405180910390a3505050565b5f8373ffffffffffffffffffffffffffffffffffffffff163b1115610f8f578273ffffffffffffffffffffffffffffffffffffffff1663150b7a02610e26610963565b8685856040518563ffffffff1660e01b8152600401610e489493929190611c03565b6020604051808303815f875af1925050508015610e8357506040513d601f19601f82011682018060405250810190610e809190611c61565b60015b610f04573d805f8114610eb1576040519150601f19603f3d011682016040523d82523d5f602084013e610eb6565b606091505b505f815103610efc57836040517f64a0ae92000000000000000000000000000000000000000000000000000000008152600401610ef3919061174c565b60405180910390fd5b805181602001fd5b63150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614610f8d57836040517f64a0ae92000000000000000000000000000000000000000000000000000000008152600401610f84919061174c565b60405180910390fd5b505b50505050565b606060405180602001604052805f815250905090565b60605f6001610fb984611330565b0190505f8167ffffffffffffffff811115610fd757610fd66118e0565b5b6040519080825280601f01601f1916602001820160405280156110095781602001600182028036833780820191505090505b5090505f82602001820190505b60011561106a578080600190039150507f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a858161105f5761105e611c8c565b5b0494505f8503611016575b819350505050919050565b5f60025f8381526020019081526020015f205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b80806110e657505f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b15611218575f6110f5846108a4565b90505f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415801561115f57508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b8015611172575061117081846107ad565b155b156111b457826040517fa9fbf51f0000000000000000000000000000000000000000000000000000000081526004016111ab919061174c565b60405180910390fd5b811561121657838573ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b8360045f8581526020019081526020015f205f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050505050565b611278838383611481565b61132b575f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036112ec57806040517f7e2732890000000000000000000000000000000000000000000000000000000081526004016112e39190611857565b60405180910390fd5b81816040517f177e802f000000000000000000000000000000000000000000000000000000008152600401611322929190611cb9565b60405180910390fd5b505050565b5f805f90507a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000831061138c577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000838161138257611381611c8c565b5b0492506040810190505b6d04ee2d6d415b85acef810000000083106113c9576d04ee2d6d415b85acef810000000083816113bf576113be611c8c565b5b0492506020810190505b662386f26fc1000083106113f857662386f26fc1000083816113ee576113ed611c8c565b5b0492506010810190505b6305f5e1008310611421576305f5e100838161141757611416611c8c565b5b0492506008810190505b612710831061144657612710838161143c5761143b611c8c565b5b0492506004810190505b60648310611469576064838161145f5761145e611c8c565b5b0492506002810190505b600a8310611478576001810190505b80915050919050565b5f8073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415801561153857508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614806114f957506114f884846107ad565b5b8061153757508273ffffffffffffffffffffffffffffffffffffffff1661151f8361092a565b73ffffffffffffffffffffffffffffffffffffffff16145b5b90509392505050565b5f604051905090565b5f80fd5b5f80fd5b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61158681611552565b8114611590575f80fd5b50565b5f813590506115a18161157d565b92915050565b5f602082840312156115bc576115bb61154a565b5b5f6115c984828501611593565b91505092915050565b5f8115159050919050565b6115e6816115d2565b82525050565b5f6020820190506115ff5f8301846115dd565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f5b8381101561163c578082015181840152602081019050611621565b5f8484015250505050565b5f601f19601f8301169050919050565b5f61166182611605565b61166b818561160f565b935061167b81856020860161161f565b61168481611647565b840191505092915050565b5f6020820190508181035f8301526116a78184611657565b905092915050565b5f819050919050565b6116c1816116af565b81146116cb575f80fd5b50565b5f813590506116dc816116b8565b92915050565b5f602082840312156116f7576116f661154a565b5b5f611704848285016116ce565b91505092915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6117368261170d565b9050919050565b6117468161172c565b82525050565b5f60208201905061175f5f83018461173d565b92915050565b61176e8161172c565b8114611778575f80fd5b50565b5f8135905061178981611765565b92915050565b5f80604083850312156117a5576117a461154a565b5b5f6117b28582860161177b565b92505060206117c3858286016116ce565b9150509250929050565b5f805f606084860312156117e4576117e361154a565b5b5f6117f18682870161177b565b93505060206118028682870161177b565b9250506040611813868287016116ce565b9150509250925092565b5f602082840312156118325761183161154a565b5b5f61183f8482850161177b565b91505092915050565b611851816116af565b82525050565b5f60208201905061186a5f830184611848565b92915050565b611879816115d2565b8114611883575f80fd5b50565b5f8135905061189481611870565b92915050565b5f80604083850312156118b0576118af61154a565b5b5f6118bd8582860161177b565b92505060206118ce85828601611886565b9150509250929050565b5f80fd5b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b61191682611647565b810181811067ffffffffffffffff82111715611935576119346118e0565b5b80604052505050565b5f611947611541565b9050611953828261190d565b919050565b5f67ffffffffffffffff821115611972576119716118e0565b5b61197b82611647565b9050602081019050919050565b828183375f83830152505050565b5f6119a86119a384611958565b61193e565b9050828152602081018484840111156119c4576119c36118dc565b5b6119cf848285611988565b509392505050565b5f82601f8301126119eb576119ea6118d8565b5b81356119fb848260208601611996565b91505092915050565b5f805f8060808587031215611a1c57611a1b61154a565b5b5f611a298782880161177b565b9450506020611a3a8782880161177b565b9350506040611a4b878288016116ce565b925050606085013567ffffffffffffffff811115611a6c57611a6b61154e565b5b611a78878288016119d7565b91505092959194509250565b5f8060408385031215611a9a57611a9961154a565b5b5f611aa78582860161177b565b9250506020611ab88582860161177b565b9150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f6002820490506001821680611b0657607f821691505b602082108103611b1957611b18611ac2565b5b50919050565b5f606082019050611b325f83018661173d565b611b3f6020830185611848565b611b4c604083018461173d565b949350505050565b5f81905092915050565b5f611b6882611605565b611b728185611b54565b9350611b8281856020860161161f565b80840191505092915050565b5f611b998285611b5e565b9150611ba58284611b5e565b91508190509392505050565b5f81519050919050565b5f82825260208201905092915050565b5f611bd582611bb1565b611bdf8185611bbb565b9350611bef81856020860161161f565b611bf881611647565b840191505092915050565b5f608082019050611c165f83018761173d565b611c23602083018661173d565b611c306040830185611848565b8181036060830152611c428184611bcb565b905095945050505050565b5f81519050611c5b8161157d565b92915050565b5f60208284031215611c7657611c7561154a565b5b5f611c8384828501611c4d565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f604082019050611ccc5f83018561173d565b611cd96020830184611848565b939250505056fea26469706673582212207439b47c2a9a1624955997732075917bbf1da26949d000c778f561eb5687576164736f6c63430008180033"; +pub const ERC1155_TEST_TOKEN_BYTES: &str = "608060405234801562000010575f80fd5b50604051620024eb380380620024eb8339818101604052810190620000369190620001ea565b8062000048816200005060201b60201c565b505062000554565b806002908162000061919062000470565b5050565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b620000c6826200007e565b810181811067ffffffffffffffff82111715620000e857620000e76200008e565b5b80604052505050565b5f620000fc62000065565b90506200010a8282620000bb565b919050565b5f67ffffffffffffffff8211156200012c576200012b6200008e565b5b62000137826200007e565b9050602081019050919050565b5f5b838110156200016357808201518184015260208101905062000146565b5f8484015250505050565b5f620001846200017e846200010f565b620000f1565b905082815260208101848484011115620001a357620001a26200007a565b5b620001b084828562000144565b509392505050565b5f82601f830112620001cf57620001ce62000076565b5b8151620001e18482602086016200016e565b91505092915050565b5f602082840312156200020257620002016200006e565b5b5f82015167ffffffffffffffff81111562000222576200022162000072565b5b6200023084828501620001b8565b91505092915050565b5f81519050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f60028204905060018216806200028857607f821691505b6020821081036200029e576200029d62000243565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f60088302620003027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82620002c5565b6200030e8683620002c5565b95508019841693508086168417925050509392505050565b5f819050919050565b5f819050919050565b5f62000358620003526200034c8462000326565b6200032f565b62000326565b9050919050565b5f819050919050565b620003738362000338565b6200038b62000382826200035f565b848454620002d1565b825550505050565b5f90565b620003a162000393565b620003ae81848462000368565b505050565b5b81811015620003d557620003c95f8262000397565b600181019050620003b4565b5050565b601f8211156200042457620003ee81620002a4565b620003f984620002b6565b8101602085101562000409578190505b620004216200041885620002b6565b830182620003b3565b50505b505050565b5f82821c905092915050565b5f620004465f198460080262000429565b1980831691505092915050565b5f62000460838362000435565b9150826002028217905092915050565b6200047b8262000239565b67ffffffffffffffff8111156200049757620004966200008e565b5b620004a3825462000270565b620004b0828285620003d9565b5f60209050601f831160018114620004e6575f8415620004d1578287015190505b620004dd858262000453565b8655506200054c565b601f198416620004f686620002a4565b5f5b828110156200051f57848901518255600182019150602085019450602081019050620004f8565b868310156200053f57848901516200053b601f89168262000435565b8355505b6001600288020188555050505b505050505050565b611f8980620005625f395ff3fe608060405234801561000f575f80fd5b5060043610610090575f3560e01c80634e1273f4116100645780634e1273f414610140578063731133e914610170578063a22cb4651461018c578063e985e9c5146101a8578063f242432a146101d857610090565b8062fdd58e1461009457806301ffc9a7146100c45780630e89341c146100f45780632eb2c2d614610124575b5f80fd5b6100ae60048036038101906100a991906113bd565b6101f4565b6040516100bb919061140a565b60405180910390f35b6100de60048036038101906100d99190611478565b610249565b6040516100eb91906114bd565b60405180910390f35b61010e600480360381019061010991906114d6565b61032a565b60405161011b919061158b565b60405180910390f35b61013e6004803603810190610139919061179b565b6103bc565b005b61015a60048036038101906101559190611926565b610463565b6040516101679190611a53565b60405180910390f35b61018a60048036038101906101859190611a73565b61056a565b005b6101a660048036038101906101a19190611b1d565b61057c565b005b6101c260048036038101906101bd9190611b5b565b610592565b6040516101cf91906114bd565b60405180910390f35b6101f260048036038101906101ed9190611b99565b610620565b005b5f805f8381526020019081526020015f205f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2054905092915050565b5f7fd9b67a26000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061031357507f0e89341c000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b806103235750610322826106c7565b5b9050919050565b60606002805461033990611c59565b80601f016020809104026020016040519081016040528092919081815260200182805461036590611c59565b80156103b05780601f10610387576101008083540402835291602001916103b0565b820191905f5260205f20905b81548152906001019060200180831161039357829003601f168201915b50505050509050919050565b5f6103c5610730565b90508073ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161415801561040a57506104088682610592565b155b1561044e5780866040517fe237d922000000000000000000000000000000000000000000000000000000008152600401610445929190611c98565b60405180910390fd5b61045b8686868686610737565b505050505050565b606081518351146104af57815183516040517f5b0599910000000000000000000000000000000000000000000000000000000081526004016104a6929190611cbf565b60405180910390fd5b5f835167ffffffffffffffff8111156104cb576104ca6115af565b5b6040519080825280602002602001820160405280156104f95781602001602082028036833780820191505090505b5090505f5b845181101561055f5761053561051d828761082b90919063ffffffff16565b610530838761083e90919063ffffffff16565b6101f4565b82828151811061054857610547611ce6565b5b6020026020010181815250508060010190506104fe565b508091505092915050565b61057684848484610851565b50505050565b61058e610587610730565b83836108e6565b5050565b5f60015f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff16905092915050565b5f610629610730565b90508073ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161415801561066e575061066c8682610592565b155b156106b25780866040517fe237d9220000000000000000000000000000000000000000000000000000000081526004016106a9929190611c98565b60405180910390fd5b6106bf8686868686610a4f565b505050505050565b5f7f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b5f33905090565b5f73ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16036107a7575f6040517f57f447ce00000000000000000000000000000000000000000000000000000000815260040161079e9190611d13565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1603610817575f6040517f01a8351400000000000000000000000000000000000000000000000000000000815260040161080e9190611d13565b60405180910390fd5b6108248585858585610b55565b5050505050565b5f60208202602084010151905092915050565b5f60208202602084010151905092915050565b5f73ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16036108c1575f6040517f57f447ce0000000000000000000000000000000000000000000000000000000081526004016108b89190611d13565b60405180910390fd5b5f806108cd8585610c01565b915091506108de5f87848487610b55565b505050505050565b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610956575f6040517fced3e10000000000000000000000000000000000000000000000000000000000815260040161094d9190611d13565b60405180910390fd5b8060015f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051610a4291906114bd565b60405180910390a3505050565b5f73ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1603610abf575f6040517f57f447ce000000000000000000000000000000000000000000000000000000008152600401610ab69190611d13565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1603610b2f575f6040517f01a83514000000000000000000000000000000000000000000000000000000008152600401610b269190611d13565b60405180910390fd5b5f80610b3b8585610c01565b91509150610b4c8787848487610b55565b50505050505050565b610b6185858585610c31565b5f73ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614610bfa575f610b9d610730565b90506001845103610be9575f610bbc5f8661083e90919063ffffffff16565b90505f610bd25f8661083e90919063ffffffff16565b9050610be2838989858589610fc1565b5050610bf8565b610bf7818787878787611170565b5b505b5050505050565b60608060405191506001825283602083015260408201905060018152826020820152604081016040529250929050565b8051825114610c7b57815181516040517f5b059991000000000000000000000000000000000000000000000000000000008152600401610c72929190611cbf565b60405180910390fd5b5f610c84610730565b90505f5b8351811015610e80575f610ca5828661083e90919063ffffffff16565b90505f610cbb838661083e90919063ffffffff16565b90505f73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614610dde575f805f8481526020019081526020015f205f8a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2054905081811015610d8a57888183856040517f03dee4c5000000000000000000000000000000000000000000000000000000008152600401610d819493929190611d2c565b60405180910390fd5b8181035f808581526020019081526020015f205f8b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2081905550505b5f73ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614610e7357805f808481526020019081526020015f205f8973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f828254610e6b9190611d9c565b925050819055505b5050806001019050610c88565b506001835103610f3b575f610e9e5f8561083e90919063ffffffff16565b90505f610eb45f8561083e90919063ffffffff16565b90508573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f628585604051610f2c929190611cbf565b60405180910390a45050610fba565b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8686604051610fb1929190611dcf565b60405180910390a45b5050505050565b5f8473ffffffffffffffffffffffffffffffffffffffff163b1115611168578373ffffffffffffffffffffffffffffffffffffffff1663f23a6e6187878686866040518663ffffffff1660e01b8152600401611021959493929190611e56565b6020604051808303815f875af192505050801561105c57506040513d601f19601f820116820180604052508101906110599190611ec2565b60015b6110dd573d805f811461108a576040519150601f19603f3d011682016040523d82523d5f602084013e61108f565b606091505b505f8151036110d557846040517f57f447ce0000000000000000000000000000000000000000000000000000000081526004016110cc9190611d13565b60405180910390fd5b805181602001fd5b63f23a6e6160e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161461116657846040517f57f447ce00000000000000000000000000000000000000000000000000000000815260040161115d9190611d13565b60405180910390fd5b505b505050505050565b5f8473ffffffffffffffffffffffffffffffffffffffff163b1115611317578373ffffffffffffffffffffffffffffffffffffffff1663bc197c8187878686866040518663ffffffff1660e01b81526004016111d0959493929190611eed565b6020604051808303815f875af192505050801561120b57506040513d601f19601f820116820180604052508101906112089190611ec2565b60015b61128c573d805f8114611239576040519150601f19603f3d011682016040523d82523d5f602084013e61123e565b606091505b505f81510361128457846040517f57f447ce00000000000000000000000000000000000000000000000000000000815260040161127b9190611d13565b60405180910390fd5b805181602001fd5b63bc197c8160e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161461131557846040517f57f447ce00000000000000000000000000000000000000000000000000000000815260040161130c9190611d13565b60405180910390fd5b505b505050505050565b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61135982611330565b9050919050565b6113698161134f565b8114611373575f80fd5b50565b5f8135905061138481611360565b92915050565b5f819050919050565b61139c8161138a565b81146113a6575f80fd5b50565b5f813590506113b781611393565b92915050565b5f80604083850312156113d3576113d2611328565b5b5f6113e085828601611376565b92505060206113f1858286016113a9565b9150509250929050565b6114048161138a565b82525050565b5f60208201905061141d5f8301846113fb565b92915050565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61145781611423565b8114611461575f80fd5b50565b5f813590506114728161144e565b92915050565b5f6020828403121561148d5761148c611328565b5b5f61149a84828501611464565b91505092915050565b5f8115159050919050565b6114b7816114a3565b82525050565b5f6020820190506114d05f8301846114ae565b92915050565b5f602082840312156114eb576114ea611328565b5b5f6114f8848285016113a9565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f5b8381101561153857808201518184015260208101905061151d565b5f8484015250505050565b5f601f19601f8301169050919050565b5f61155d82611501565b611567818561150b565b935061157781856020860161151b565b61158081611543565b840191505092915050565b5f6020820190508181035f8301526115a38184611553565b905092915050565b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6115e582611543565b810181811067ffffffffffffffff82111715611604576116036115af565b5b80604052505050565b5f61161661131f565b905061162282826115dc565b919050565b5f67ffffffffffffffff821115611641576116406115af565b5b602082029050602081019050919050565b5f80fd5b5f61166861166384611627565b61160d565b9050808382526020820190506020840283018581111561168b5761168a611652565b5b835b818110156116b457806116a088826113a9565b84526020840193505060208101905061168d565b5050509392505050565b5f82601f8301126116d2576116d16115ab565b5b81356116e2848260208601611656565b91505092915050565b5f80fd5b5f67ffffffffffffffff821115611709576117086115af565b5b61171282611543565b9050602081019050919050565b828183375f83830152505050565b5f61173f61173a846116ef565b61160d565b90508281526020810184848401111561175b5761175a6116eb565b5b61176684828561171f565b509392505050565b5f82601f830112611782576117816115ab565b5b813561179284826020860161172d565b91505092915050565b5f805f805f60a086880312156117b4576117b3611328565b5b5f6117c188828901611376565b95505060206117d288828901611376565b945050604086013567ffffffffffffffff8111156117f3576117f261132c565b5b6117ff888289016116be565b935050606086013567ffffffffffffffff8111156118205761181f61132c565b5b61182c888289016116be565b925050608086013567ffffffffffffffff81111561184d5761184c61132c565b5b6118598882890161176e565b9150509295509295909350565b5f67ffffffffffffffff8211156118805761187f6115af565b5b602082029050602081019050919050565b5f6118a361189e84611866565b61160d565b905080838252602082019050602084028301858111156118c6576118c5611652565b5b835b818110156118ef57806118db8882611376565b8452602084019350506020810190506118c8565b5050509392505050565b5f82601f83011261190d5761190c6115ab565b5b813561191d848260208601611891565b91505092915050565b5f806040838503121561193c5761193b611328565b5b5f83013567ffffffffffffffff8111156119595761195861132c565b5b611965858286016118f9565b925050602083013567ffffffffffffffff8111156119865761198561132c565b5b611992858286016116be565b9150509250929050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b6119ce8161138a565b82525050565b5f6119df83836119c5565b60208301905092915050565b5f602082019050919050565b5f611a018261199c565b611a0b81856119a6565b9350611a16836119b6565b805f5b83811015611a46578151611a2d88826119d4565b9750611a38836119eb565b925050600181019050611a19565b5085935050505092915050565b5f6020820190508181035f830152611a6b81846119f7565b905092915050565b5f805f8060808587031215611a8b57611a8a611328565b5b5f611a9887828801611376565b9450506020611aa9878288016113a9565b9350506040611aba878288016113a9565b925050606085013567ffffffffffffffff811115611adb57611ada61132c565b5b611ae78782880161176e565b91505092959194509250565b611afc816114a3565b8114611b06575f80fd5b50565b5f81359050611b1781611af3565b92915050565b5f8060408385031215611b3357611b32611328565b5b5f611b4085828601611376565b9250506020611b5185828601611b09565b9150509250929050565b5f8060408385031215611b7157611b70611328565b5b5f611b7e85828601611376565b9250506020611b8f85828601611376565b9150509250929050565b5f805f805f60a08688031215611bb257611bb1611328565b5b5f611bbf88828901611376565b9550506020611bd088828901611376565b9450506040611be1888289016113a9565b9350506060611bf2888289016113a9565b925050608086013567ffffffffffffffff811115611c1357611c1261132c565b5b611c1f8882890161176e565b9150509295509295909350565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f6002820490506001821680611c7057607f821691505b602082108103611c8357611c82611c2c565b5b50919050565b611c928161134f565b82525050565b5f604082019050611cab5f830185611c89565b611cb86020830184611c89565b9392505050565b5f604082019050611cd25f8301856113fb565b611cdf60208301846113fb565b9392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f602082019050611d265f830184611c89565b92915050565b5f608082019050611d3f5f830187611c89565b611d4c60208301866113fb565b611d5960408301856113fb565b611d6660608301846113fb565b95945050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f611da68261138a565b9150611db18361138a565b9250828201905080821115611dc957611dc8611d6f565b5b92915050565b5f6040820190508181035f830152611de781856119f7565b90508181036020830152611dfb81846119f7565b90509392505050565b5f81519050919050565b5f82825260208201905092915050565b5f611e2882611e04565b611e328185611e0e565b9350611e4281856020860161151b565b611e4b81611543565b840191505092915050565b5f60a082019050611e695f830188611c89565b611e766020830187611c89565b611e8360408301866113fb565b611e9060608301856113fb565b8181036080830152611ea28184611e1e565b90509695505050505050565b5f81519050611ebc8161144e565b92915050565b5f60208284031215611ed757611ed6611328565b5b5f611ee484828501611eae565b91505092915050565b5f60a082019050611f005f830188611c89565b611f0d6020830187611c89565b8181036040830152611f1f81866119f7565b90508181036060830152611f3381856119f7565b90508181036080830152611f478184611e1e565b9050969550505050505056fea26469706673582212203835581c6344b12728c44fa4d9e912cd60e64012c1b772bb703d1c36825c16fd64736f6c63430008180033"; +pub const NFT_SWAP_CONTRACT_BYTES: &str = "60a060405234801562000010575f80fd5b50604051620055a2380380620055a2833981810160405281019062000036919062000147565b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603620000a7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016200009e90620001fb565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff1681525050506200021b565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6200011182620000e6565b9050919050565b620001238162000105565b81146200012e575f80fd5b50565b5f81519050620001418162000118565b92915050565b5f602082840312156200015f576200015e620000e2565b5b5f6200016e8482850162000131565b91505092915050565b5f82825260208201905092915050565b7f66656541646472657373206d757374206e6f74206265207a65726f20616464725f8201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b5f620001e360238362000177565b9150620001f08262000187565b604082019050919050565b5f6020820190508181035f8301526200021481620001d5565b9050919050565b608051615360620002425f395f8181612aef01528181612b8a0152612f4801526153605ff3fe608060405260043610610113575f3560e01c80639b4603f21161009f578063cc90c19911610063578063cc90c1991461038e578063d6a71eb4146103b6578063e06cf966146103de578063efccb9eb14610408578063f23a6e611461044657610113565b80639b4603f2146102be578063b27e46fb146102da578063bc197c8114610302578063c8d9009b1461033e578063c92cd12d1461036657610113565b8063150b7a02116100e6578063150b7a02146101cb5780633e6af5f21461020757806346b95ac71461022f57806365e266171461026e5780636e6bf6d21461029657610113565b806301ffc9a71461011757806305ec158d146101535780630f235fce1461017b578063146e5b24146101a3575b5f80fd5b348015610122575f80fd5b5061013d6004803603810190610138919061386f565b610482565b60405161014a91906138b4565b60405180910390f35b34801561015e575f80fd5b506101796004803603810190610174919061398d565b610563565b005b348015610186575f80fd5b506101a1600480360381019061019c9190613a2a565b610823565b005b3480156101ae575f80fd5b506101c960048036038101906101c49190613ab3565b610add565b005b3480156101d6575f80fd5b506101f160048036038101906101ec9190613bb1565b610cc3565b6040516101fe9190613c44565b60405180910390f35b348015610212575f80fd5b5061022d60048036038101906102289190613ab3565b611112565b005b34801561023a575f80fd5b5061025560048036038101906102509190613c5d565b611423565b6040516102659493929190613d53565b60405180910390f35b348015610279575f80fd5b50610294600480360381019061028f9190613ab3565b611485565b005b3480156102a1575f80fd5b506102bc60048036038101906102b79190613a2a565b6118e9565b005b6102d860048036038101906102d39190613dc0565b611ba4565b005b3480156102e5575f80fd5b5061030060048036038101906102fb919061398d565b611eda565b005b34801561030d575f80fd5b5061032860048036038101906103239190613eb2565b612199565b6040516103359190613c44565b60405180910390f35b348015610349575f80fd5b50610364600480360381019061035f9190613a2a565b6121d5565b005b348015610371575f80fd5b5061038c6004803603810190610387919061398d565b6124fe565b005b348015610399575f80fd5b506103b460048036038101906103af9190613ab3565b61282c565b005b3480156103c1575f80fd5b506103dc60048036038101906103d79190613f89565b612bdc565b005b3480156103e9575f80fd5b506103f2612f46565b6040516103ff919061405c565b60405180910390f35b348015610413575f80fd5b5061042e60048036038101906104299190613c5d565b612f6a565b60405161043d939291906140bb565b60405180910390f35b348015610451575f80fd5b5061046c600480360381019061046791906140f0565b612fb6565b6040516104799190613c44565b60405180910390f35b5f7f4e2312e0000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061054c57507f150b7a02000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061055c575061055b8261344a565b5b9050919050565b6001600381111561057757610576613ce0565b5b5f808981526020019081526020015f205f0160189054906101000a900460ff1660038111156105a9576105a8613ce0565b5b146105e9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105e090614206565b60405180910390fd5b5f600387336002896040516020016106019190614244565b60405160208183030381529060405260405161061d91906142ca565b602060405180830381855afa158015610638573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061065b91906142f4565b888888886040516020016106759796959493929190614384565b60405160208183030381529060405260405161069191906142ca565b602060405180830381855afa1580156106ac573d5f803e3d5ffd5b5050506040515160601b90505f808981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614610736576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161072d9061444e565b60405180910390fd5b60035f808a81526020019081526020015f205f0160186101000a81548160ff0219169083600381111561076c5761076b613ce0565b5b02179055507fac509cdcc7ddb189f81fff6f4824f5c95076e64c3bdce542c50feaa6779afd73886040516107a0919061447b565b60405180910390a15f8490508073ffffffffffffffffffffffffffffffffffffffff1663f242432a303387876040518563ffffffff1660e01b81526004016107eb94939291906144d6565b5f604051808303815f87803b158015610802575f80fd5b505af1158015610814573d5f803e3d5ffd5b50505050505050505050505050565b6001600381111561083757610836613ce0565b5b5f808881526020019081526020015f205f0160189054906101000a900460ff16600381111561086957610868613ce0565b5b146108a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a090614206565b60405180910390fd5b5f60038633878787876040516020016108c79695949392919061452c565b6040516020818303038152906040526040516108e391906142ca565b602060405180830381855afa1580156108fe573d5f803e3d5ffd5b5050506040515160601b90505f808881526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614610988576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161097f9061444e565b60405180910390fd5b5f808881526020019081526020015f205f0160149054906101000a900463ffffffff1663ffffffff164210156109f3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109ea9061460b565b60405180910390fd5b60035f808981526020019081526020015f205f0160186101000a81548160ff02191690836003811115610a2957610a28613ce0565b5b02179055507f5dedc4f52b757d9112d09ca0b2f022927104d54e3f54da091587e8ad1921907287604051610a5d919061447b565b60405180910390a15f8390508073ffffffffffffffffffffffffffffffffffffffff166342842e0e3033866040518463ffffffff1660e01b8152600401610aa693929190614629565b5f604051808303815f87803b158015610abd575f80fd5b505af1158015610acf573d5f803e3d5ffd5b505050505050505050505050565b60016004811115610af157610af0613ce0565b5b60015f8981526020019081526020015f205f01601c9054906101000a900460ff166004811115610b2457610b23613ce0565b5b14610b64576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5b90614206565b60405180910390fd5b5f600387878733888888604051602001610b84979695949392919061465e565b604051602081830303815290604052604051610ba091906142ca565b602060405180830381855afa158015610bbb573d5f803e3d5ffd5b5050506040515160601b905060015f8981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614610c46576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c3d9061444e565b60405180910390fd5b600260015f8a81526020019081526020015f205f01601c6101000a81548160ff02191690836004811115610c7d57610c7c613ce0565b5b02179055507f9c45e43e2ef051f70491ffd5221bf02ab37e1324128714ef9610df5f24fc9fb588604051610cb1919061447b565b60405180910390a15050505050505050565b5f808383810190610cd49190614807565b90505f6003811115610ce957610ce8613ce0565b5b5f80835f015181526020019081526020015f205f0160189054906101000a900460ff166003811115610d1e57610d1d613ce0565b5b14610d5e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d55906148a2565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff16816020015173ffffffffffffffffffffffffffffffffffffffff1603610dd0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610dc79061490a565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff16816040015173ffffffffffffffffffffffffffffffffffffffff1603610e42576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e3990614972565b60405180910390fd5b806040015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610eb4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610eab90614a00565b60405180910390fd5b8573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614610f22576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f1990614a68565b60405180910390fd5b610f2f81602001516134b3565b15610f6f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f6690614ad0565b60405180910390fd5b5f60038260200151888460600151856080015186604001518b604051602001610f9d9695949392919061452c565b604051602081830303815290604052604051610fb991906142ca565b602060405180830381855afa158015610fd4573d5f803e3d5ffd5b5050506040515160601b90506040518060600160405280826bffffffffffffffffffffffff191681526020018360a0015163ffffffff1681526020016001600381111561102457611023613ce0565b5b8152505f80845f015181526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548163ffffffff021916908363ffffffff1602179055506040820151815f0160186101000a81548160ff021916908360038111156110bb576110ba613ce0565b5b02179055509050507ff1dc11bbb6d7542c4267ecf1d370ff4c7092518633ecae9939e8488f4e53d2ad825f01516040516110f5919061447b565b60405180910390a163150b7a0260e01b9250505095945050505050565b6001600481111561112657611125613ce0565b5b60015f8981526020019081526020015f205f01601c9054906101000a900460ff16600481111561115957611158613ce0565b5b14611199576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161119090614206565b60405180910390fd5b5f6003878787336002896040516020016111b39190614244565b6040516020818303038152906040526040516111cf91906142ca565b602060405180830381855afa1580156111ea573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061120d91906142f4565b8888604051602001611225979695949392919061465e565b60405160208183030381529060405260405161124191906142ca565b602060405180830381855afa15801561125c573d5f803e3d5ffd5b5050506040515160601b905060015f8981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff1916146112e7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112de9061444e565b60405180910390fd5b600460015f8a81526020019081526020015f205f01601c6101000a81548160ff0219169083600481111561131e5761131d613ce0565b5b02179055507f45169a52eef651b20a81474b50b8a5d83225225fcd097ef3cf7952d9ab304f278885604051611354929190614aee565b60405180910390a15f86886113699190614b42565b90505f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036113e7573373ffffffffffffffffffffffffffffffffffffffff166108fc8290811502906040515f60405180830381858888f193505050501580156113e1573d5f803e3d5ffd5b50611418565b5f83905061141633838373ffffffffffffffffffffffffffffffffffffffff166134c49092919063ffffffff16565b505b505050505050505050565b6001602052805f5260405f205f91509050805f015f9054906101000a900460601b90805f0160149054906101000a900463ffffffff1690805f0160189054906101000a900463ffffffff1690805f01601c9054906101000a900460ff16905084565b6001600481111561149957611498613ce0565b5b60015f8981526020019081526020015f205f01601c9054906101000a900460ff1660048111156114cc576114cb613ce0565b5b148061151c5750600260048111156114e7576114e6613ce0565b5b60015f8981526020019081526020015f205f01601c9054906101000a900460ff16600481111561151a57611519613ce0565b5b145b61155b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161155290614be5565b60405180910390fd5b5f60038787873388888860405160200161157b979695949392919061465e565b60405160208183030381529060405260405161159791906142ca565b602060405180830381855afa1580156115b2573d5f803e3d5ffd5b5050506040515160601b905060015f8981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff19161461163d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116349061444e565b60405180910390fd5b6002600481111561165157611650613ce0565b5b60015f8a81526020019081526020015f205f01601c9054906101000a900460ff16600481111561168457611683613ce0565b5b036116f65760015f8981526020019081526020015f205f0160189054906101000a900463ffffffff1663ffffffff164210156116f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116ec9061460b565b60405180910390fd5b5b6001600481111561170a57611709613ce0565b5b60015f8a81526020019081526020015f205f01601c9054906101000a900460ff16600481111561173d5761173c613ce0565b5b036117af5760015f8981526020019081526020015f205f0160149054906101000a900463ffffffff1663ffffffff164210156117ae576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117a590614c73565b60405180910390fd5b5b600460015f8a81526020019081526020015f205f01601c6101000a81548160ff021916908360048111156117e6576117e5613ce0565b5b02179055507fbdd7a4be6d82798a500b59077706b12d3f45acf5504828919f92501307b2b9538860405161181a919061447b565b60405180910390a15f868861182f9190614b42565b90505f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036118ad573373ffffffffffffffffffffffffffffffffffffffff166108fc8290811502906040515f60405180830381858888f193505050501580156118a7573d5f803e3d5ffd5b506118de565b5f8390506118dc33838373ffffffffffffffffffffffffffffffffffffffff166134c49092919063ffffffff16565b505b505050505050505050565b600160038111156118fd576118fc613ce0565b5b5f808881526020019081526020015f205f0160189054906101000a900460ff16600381111561192f5761192e613ce0565b5b1461196f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161196690614206565b60405180910390fd5b5f600386336002886040516020016119879190614244565b6040516020818303038152906040526040516119a391906142ca565b602060405180830381855afa1580156119be573d5f803e3d5ffd5b5050506040513d601f19601f820116820180604052508101906119e191906142f4565b8787876040516020016119f99695949392919061452c565b604051602081830303815290604052604051611a1591906142ca565b602060405180830381855afa158015611a30573d5f803e3d5ffd5b5050506040515160601b90505f808881526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614611aba576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611ab19061444e565b60405180910390fd5b60035f808981526020019081526020015f205f0160186101000a81548160ff02191690836003811115611af057611aef613ce0565b5b02179055507fac509cdcc7ddb189f81fff6f4824f5c95076e64c3bdce542c50feaa6779afd7387604051611b24919061447b565b60405180910390a15f8390508073ffffffffffffffffffffffffffffffffffffffff166342842e0e3033866040518463ffffffff1660e01b8152600401611b6d93929190614629565b5f604051808303815f87803b158015611b84575f80fd5b505af1158015611b96573d5f803e3d5ffd5b505050505050505050505050565b5f6004811115611bb757611bb6613ce0565b5b60015f8981526020019081526020015f205f01601c9054906101000a900460ff166004811115611bea57611be9613ce0565b5b14611c2a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c2190614d01565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1603611c98576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c8f90614d8f565b60405180910390fd5b5f3411611cda576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611cd190614e1d565b60405180910390fd5b853411611d1c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611d1390614eab565b60405180910390fd5b5f60038734611d2b9190614ec9565b88883389895f604051602001611d47979695949392919061465e565b604051602081830303815290604052604051611d6391906142ca565b602060405180830381855afa158015611d7e573d5f803e3d5ffd5b5050506040515160601b90506040518060800160405280826bffffffffffffffffffffffff191681526020018463ffffffff1681526020018363ffffffff16815260200160016004811115611dd657611dd5613ce0565b5b81525060015f8a81526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548163ffffffff021916908363ffffffff1602179055506040820151815f0160186101000a81548163ffffffff021916908363ffffffff1602179055506060820151815f01601c6101000a81548160ff02191690836004811115611e9157611e90613ce0565b5b02179055509050507ffc6cdccd1d98ded12074a9ebc7f6ab74fed1814ff57f4fb5202464d8938bd93588604051611ec8919061447b565b60405180910390a15050505050505050565b60016003811115611eee57611eed613ce0565b5b5f808981526020019081526020015f205f0160189054906101000a900460ff166003811115611f2057611f1f613ce0565b5b14611f60576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611f5790614206565b60405180910390fd5b5f600387338888888888604051602001611f809796959493929190614384565b604051602081830303815290604052604051611f9c91906142ca565b602060405180830381855afa158015611fb7573d5f803e3d5ffd5b5050506040515160601b90505f808981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614612041576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120389061444e565b60405180910390fd5b5f808981526020019081526020015f205f0160149054906101000a900463ffffffff1663ffffffff164210156120ac576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120a39061460b565b60405180910390fd5b60035f808a81526020019081526020015f205f0160186101000a81548160ff021916908360038111156120e2576120e1613ce0565b5b02179055507f5dedc4f52b757d9112d09ca0b2f022927104d54e3f54da091587e8ad1921907288604051612116919061447b565b60405180910390a15f8490508073ffffffffffffffffffffffffffffffffffffffff1663f242432a303387876040518563ffffffff1660e01b815260040161216194939291906144d6565b5f604051808303815f87803b158015612178575f80fd5b505af115801561218a573d5f803e3d5ffd5b50505050505050505050505050565b5f6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016121cc90614f46565b60405180910390fd5b600160038111156121e9576121e8613ce0565b5b5f808881526020019081526020015f205f0160189054906101000a900460ff16600381111561221b5761221a613ce0565b5b1461225b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161225290614206565b60405180910390fd5b3273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146122c9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016122c090614fae565b60405180910390fd5b5f60033387876002886040516020016122e29190614244565b6040516020818303038152906040526040516122fe91906142ca565b602060405180830381855afa158015612319573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061233c91906142f4565b87876040516020016123539695949392919061452c565b60405160208183030381529060405260405161236f91906142ca565b602060405180830381855afa15801561238a573d5f803e3d5ffd5b5050506040515160601b90505f808881526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614612414576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161240b9061444e565b60405180910390fd5b60025f808981526020019081526020015f205f0160186101000a81548160ff0219169083600381111561244a57612449613ce0565b5b02179055507fad62ed075fe8969df63026f45152d6e996a0697a736a8de92ee85ae9c9958cf08760405161247e919061447b565b60405180910390a15f8390508073ffffffffffffffffffffffffffffffffffffffff166342842e0e3033866040518463ffffffff1660e01b81526004016124c793929190614629565b5f604051808303815f87803b1580156124de575f80fd5b505af11580156124f0573d5f803e3d5ffd5b505050505050505050505050565b6001600381111561251257612511613ce0565b5b5f808981526020019081526020015f205f0160189054906101000a900460ff16600381111561254457612543613ce0565b5b14612584576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161257b90614206565b60405180910390fd5b3273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146125f2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016125e990614fae565b60405180910390fd5b5f600333888860028960405160200161260b9190614244565b60405160208183030381529060405260405161262791906142ca565b602060405180830381855afa158015612642573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061266591906142f4565b88888860405160200161267e9796959493929190614384565b60405160208183030381529060405260405161269a91906142ca565b602060405180830381855afa1580156126b5573d5f803e3d5ffd5b5050506040515160601b90505f808981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff19161461273f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016127369061444e565b60405180910390fd5b60025f808a81526020019081526020015f205f0160186101000a81548160ff0219169083600381111561277557612774613ce0565b5b02179055507fad62ed075fe8969df63026f45152d6e996a0697a736a8de92ee85ae9c9958cf0886040516127a9919061447b565b60405180910390a15f8490508073ffffffffffffffffffffffffffffffffffffffff1663f242432a303387876040518563ffffffff1660e01b81526004016127f494939291906144d6565b5f604051808303815f87803b15801561280b575f80fd5b505af115801561281d573d5f803e3d5ffd5b50505050505050505050505050565b600260048111156128405761283f613ce0565b5b60015f8981526020019081526020015f205f01601c9054906101000a900460ff16600481111561287357612872613ce0565b5b146128b3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016128aa9061503c565b60405180910390fd5b5f600387873388886002896040516020016128ce9190614244565b6040516020818303038152906040526040516128ea91906142ca565b602060405180830381855afa158015612905573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061292891906142f4565b8860405160200161293f979695949392919061465e565b60405160208183030381529060405260405161295b91906142ca565b602060405180830381855afa158015612976573d5f803e3d5ffd5b5050506040515160601b905060015f8981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614612a01576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016129f89061444e565b60405180910390fd5b600360015f8a81526020019081526020015f205f01601c6101000a81548160ff02191690836004811115612a3857612a37613ce0565b5b02179055507f0d0da0df275f85bed3a5fe7ae79f3559341a3f9ccd8e010133438135bda00a878884604051612a6e929190614aee565b60405180910390a15f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603612b56573373ffffffffffffffffffffffffffffffffffffffff166108fc8890811502906040515f60405180830381858888f19350505050158015612aec573d5f803e3d5ffd5b507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166108fc8790811502906040515f60405180830381858888f19350505050158015612b50573d5f803e3d5ffd5b50612bd2565b5f829050612b8533898373ffffffffffffffffffffffffffffffffffffffff166134c49092919063ffffffff16565b612bd07f0000000000000000000000000000000000000000000000000000000000000000888373ffffffffffffffffffffffffffffffffffffffff166134c49092919063ffffffff16565b505b5050505050505050565b5f6004811115612bef57612bee613ce0565b5b60015f8b81526020019081526020015f205f01601c9054906101000a900460ff166004811115612c2257612c21613ce0565b5b14612c62576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612c59906150ca565b60405180910390fd5b5f8811612ca4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612c9b90615132565b60405180910390fd5b5f8711612ce6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612cdd9061519a565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1603612d54576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612d4b90614d8f565b60405180910390fd5b5f60038989883389898d604051602001612d74979695949392919061465e565b604051602081830303815290604052604051612d9091906142ca565b602060405180830381855afa158015612dab573d5f803e3d5ffd5b5050506040515160601b90506040518060800160405280826bffffffffffffffffffffffff191681526020018463ffffffff1681526020018363ffffffff16815260200160016004811115612e0357612e02613ce0565b5b81525060015f8c81526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548163ffffffff021916908363ffffffff1602179055506040820151815f0160186101000a81548163ffffffff021916908363ffffffff1602179055506060820151815f01601c6101000a81548160ff02191690836004811115612ebe57612ebd613ce0565b5b02179055509050507ffc6cdccd1d98ded12074a9ebc7f6ab74fed1814ff57f4fb5202464d8938bd9358a604051612ef5919061447b565b60405180910390a15f879050612f3933308b8d612f129190614b42565b8473ffffffffffffffffffffffffffffffffffffffff16613543909392919063ffffffff16565b5050505050505050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b5f602052805f5260405f205f91509050805f015f9054906101000a900460601b90805f0160149054906101000a900463ffffffff1690805f0160189054906101000a900460ff16905083565b5f808383810190612fc79190614807565b90505f6003811115612fdc57612fdb613ce0565b5b5f80835f015181526020019081526020015f205f0160189054906101000a900460ff16600381111561301157613010613ce0565b5b14613051576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161304890615228565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff16816020015173ffffffffffffffffffffffffffffffffffffffff16036130c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016130ba9061490a565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff16816040015173ffffffffffffffffffffffffffffffffffffffff1603613135576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161312c90614972565b60405180910390fd5b806040015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146131a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161319e90614a00565b60405180910390fd5b8673ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614613215576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161320c90614a68565b60405180910390fd5b5f8511613257576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161324e90615290565b60405180910390fd5b61326481602001516134b3565b156132a4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161329b90614ad0565b60405180910390fd5b5f60038260200151898460600151856080015186604001518c8c6040516020016132d49796959493929190614384565b6040516020818303038152906040526040516132f091906142ca565b602060405180830381855afa15801561330b573d5f803e3d5ffd5b5050506040515160601b90506040518060600160405280826bffffffffffffffffffffffff191681526020018360a0015163ffffffff1681526020016001600381111561335b5761335a613ce0565b5b8152505f80845f015181526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548163ffffffff021916908363ffffffff1602179055506040820151815f0160186101000a81548160ff021916908360038111156133f2576133f1613ce0565b5b02179055509050507ff1dc11bbb6d7542c4267ecf1d370ff4c7092518633ecae9939e8488f4e53d2ad825f015160405161342c919061447b565b60405180910390a163f23a6e6160e01b925050509695505050505050565b5f7f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b5f80823b90505f8111915050919050565b61353e838473ffffffffffffffffffffffffffffffffffffffff1663a9059cbb85856040516024016134f79291906152ae565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506135c5565b505050565b6135bf848573ffffffffffffffffffffffffffffffffffffffff166323b872dd86868660405160240161357893929190614629565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506135c5565b50505050565b5f6135ef828473ffffffffffffffffffffffffffffffffffffffff1661365a90919063ffffffff16565b90505f81511415801561361357508080602001905181019061361191906152ff565b155b1561365557826040517f5274afe700000000000000000000000000000000000000000000000000000000815260040161364c919061405c565b60405180910390fd5b505050565b606061366783835f61366f565b905092915050565b6060814710156136b657306040517fcd7860590000000000000000000000000000000000000000000000000000000081526004016136ad919061405c565b60405180910390fd5b5f808573ffffffffffffffffffffffffffffffffffffffff1684866040516136de91906142ca565b5f6040518083038185875af1925050503d805f8114613718576040519150601f19603f3d011682016040523d82523d5f602084013e61371d565b606091505b509150915061372d868383613738565b925050509392505050565b60608261374d57613748826137c5565b6137bd565b5f825114801561377357505f8473ffffffffffffffffffffffffffffffffffffffff163b145b156137b557836040517f9996b3150000000000000000000000000000000000000000000000000000000081526004016137ac919061405c565b60405180910390fd5b8190506137be565b5b9392505050565b5f815111156137d75780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f604051905090565b5f80fd5b5f80fd5b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61384e8161381a565b8114613858575f80fd5b50565b5f8135905061386981613845565b92915050565b5f6020828403121561388457613883613812565b5b5f6138918482850161385b565b91505092915050565b5f8115159050919050565b6138ae8161389a565b82525050565b5f6020820190506138c75f8301846138a5565b92915050565b5f819050919050565b6138df816138cd565b81146138e9575f80fd5b50565b5f813590506138fa816138d6565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61392982613900565b9050919050565b6139398161391f565b8114613943575f80fd5b50565b5f8135905061395481613930565b92915050565b5f819050919050565b61396c8161395a565b8114613976575f80fd5b50565b5f8135905061398781613963565b92915050565b5f805f805f805f60e0888a0312156139a8576139a7613812565b5b5f6139b58a828b016138ec565b97505060206139c68a828b01613946565b96505060406139d78a828b016138ec565b95505060606139e88a828b016138ec565b94505060806139f98a828b01613946565b93505060a0613a0a8a828b01613979565b92505060c0613a1b8a828b01613979565b91505092959891949750929550565b5f805f805f8060c08789031215613a4457613a43613812565b5b5f613a5189828a016138ec565b9650506020613a6289828a01613946565b9550506040613a7389828a016138ec565b9450506060613a8489828a016138ec565b9350506080613a9589828a01613946565b92505060a0613aa689828a01613979565b9150509295509295509295565b5f805f805f805f60e0888a031215613ace57613acd613812565b5b5f613adb8a828b016138ec565b9750506020613aec8a828b01613979565b9650506040613afd8a828b01613979565b9550506060613b0e8a828b01613946565b9450506080613b1f8a828b016138ec565b93505060a0613b308a828b016138ec565b92505060c0613b418a828b01613946565b91505092959891949750929550565b5f80fd5b5f80fd5b5f80fd5b5f8083601f840112613b7157613b70613b50565b5b8235905067ffffffffffffffff811115613b8e57613b8d613b54565b5b602083019150836001820283011115613baa57613ba9613b58565b5b9250929050565b5f805f805f60808688031215613bca57613bc9613812565b5b5f613bd788828901613946565b9550506020613be888828901613946565b9450506040613bf988828901613979565b935050606086013567ffffffffffffffff811115613c1a57613c19613816565b5b613c2688828901613b5c565b92509250509295509295909350565b613c3e8161381a565b82525050565b5f602082019050613c575f830184613c35565b92915050565b5f60208284031215613c7257613c71613812565b5b5f613c7f848285016138ec565b91505092915050565b5f7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000082169050919050565b613cbc81613c88565b82525050565b5f63ffffffff82169050919050565b613cda81613cc2565b82525050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b60058110613d1e57613d1d613ce0565b5b50565b5f819050613d2e82613d0d565b919050565b5f613d3d82613d21565b9050919050565b613d4d81613d33565b82525050565b5f608082019050613d665f830187613cb3565b613d736020830186613cd1565b613d806040830185613cd1565b613d8d6060830184613d44565b95945050505050565b613d9f81613cc2565b8114613da9575f80fd5b50565b5f81359050613dba81613d96565b92915050565b5f805f805f805f60e0888a031215613ddb57613dda613812565b5b5f613de88a828b016138ec565b9750506020613df98a828b01613979565b9650506040613e0a8a828b01613946565b9550506060613e1b8a828b016138ec565b9450506080613e2c8a828b016138ec565b93505060a0613e3d8a828b01613dac565b92505060c0613e4e8a828b01613dac565b91505092959891949750929550565b5f8083601f840112613e7257613e71613b50565b5b8235905067ffffffffffffffff811115613e8f57613e8e613b54565b5b602083019150836020820283011115613eab57613eaa613b58565b5b9250929050565b5f805f805f805f8060a0898b031215613ece57613ecd613812565b5b5f613edb8b828c01613946565b9850506020613eec8b828c01613946565b975050604089013567ffffffffffffffff811115613f0d57613f0c613816565b5b613f198b828c01613e5d565b9650965050606089013567ffffffffffffffff811115613f3c57613f3b613816565b5b613f488b828c01613e5d565b9450945050608089013567ffffffffffffffff811115613f6b57613f6a613816565b5b613f778b828c01613b5c565b92509250509295985092959890939650565b5f805f805f805f805f6101208a8c031215613fa757613fa6613812565b5b5f613fb48c828d016138ec565b9950506020613fc58c828d01613979565b9850506040613fd68c828d01613979565b9750506060613fe78c828d01613946565b9650506080613ff88c828d01613946565b95505060a06140098c828d016138ec565b94505060c061401a8c828d016138ec565b93505060e061402b8c828d01613dac565b92505061010061403d8c828d01613dac565b9150509295985092959850929598565b6140568161391f565b82525050565b5f60208201905061406f5f83018461404d565b92915050565b6004811061408657614085613ce0565b5b50565b5f81905061409682614075565b919050565b5f6140a582614089565b9050919050565b6140b58161409b565b82525050565b5f6060820190506140ce5f830186613cb3565b6140db6020830185613cd1565b6140e860408301846140ac565b949350505050565b5f805f805f8060a0878903121561410a57614109613812565b5b5f61411789828a01613946565b965050602061412889828a01613946565b955050604061413989828a01613979565b945050606061414a89828a01613979565b935050608087013567ffffffffffffffff81111561416b5761416a613816565b5b61417789828a01613b5c565b92509250509295509295509295565b5f82825260208201905092915050565b7f496e76616c6964207061796d656e742073746174652e204d75737420626520505f8201527f61796d656e7453656e7400000000000000000000000000000000000000000000602082015250565b5f6141f0602a83614186565b91506141fb82614196565b604082019050919050565b5f6020820190508181035f83015261421d816141e4565b9050919050565b5f819050919050565b61423e614239826138cd565b614224565b82525050565b5f61424f828461422d565b60208201915081905092915050565b5f81519050919050565b5f81905092915050565b5f5b8381101561428f578082015181840152602081019050614274565b5f8484015250505050565b5f6142a48261425e565b6142ae8185614268565b93506142be818560208601614272565b80840191505092915050565b5f6142d5828461429a565b915081905092915050565b5f815190506142ee816138d6565b92915050565b5f6020828403121561430957614308613812565b5b5f614316848285016142e0565b91505092915050565b5f8160601b9050919050565b5f6143358261431f565b9050919050565b5f6143468261432b565b9050919050565b61435e6143598261391f565b61433c565b82525050565b5f819050919050565b61437e6143798261395a565b614364565b82525050565b5f61438f828a61434d565b60148201915061439f828961434d565b6014820191506143af828861422d565b6020820191506143bf828761422d565b6020820191506143cf828661434d565b6014820191506143df828561436d565b6020820191506143ef828461436d565b60208201915081905098975050505050505050565b7f496e76616c6964207061796d656e7448617368000000000000000000000000005f82015250565b5f614438601383614186565b915061444382614404565b602082019050919050565b5f6020820190508181035f8301526144658161442c565b9050919050565b614475816138cd565b82525050565b5f60208201905061448e5f83018461446c565b92915050565b61449d8161395a565b82525050565b5f82825260208201905092915050565b50565b5f6144c15f836144a3565b91506144cc826144b3565b5f82019050919050565b5f60a0820190506144e95f83018761404d565b6144f6602083018661404d565b6145036040830185614494565b6145106060830184614494565b8181036080830152614521816144b6565b905095945050505050565b5f614537828961434d565b601482019150614547828861434d565b601482019150614557828761422d565b602082019150614567828661422d565b602082019150614577828561434d565b601482019150614587828461436d565b602082019150819050979650505050505050565b7f43757272656e742074696d657374616d70206469646e277420657863656564205f8201527f7061796d656e7420726566756e64206c6f636b2074696d650000000000000000602082015250565b5f6145f5603883614186565b91506146008261459b565b604082019050919050565b5f6020820190508181035f830152614622816145e9565b9050919050565b5f60608201905061463c5f83018661404d565b614649602083018561404d565b6146566040830184614494565b949350505050565b5f614669828a61436d565b602082019150614679828961436d565b602082019150614689828861434d565b601482019150614699828761434d565b6014820191506146a9828661422d565b6020820191506146b9828561422d565b6020820191506146c9828461434d565b60148201915081905098975050505050505050565b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b614728826146e2565b810181811067ffffffffffffffff82111715614747576147466146f2565b5b80604052505050565b5f614759613809565b9050614765828261471f565b919050565b5f60c0828403121561477f5761477e6146de565b5b61478960c0614750565b90505f614798848285016138ec565b5f8301525060206147ab84828501613946565b60208301525060406147bf84828501613946565b60408301525060606147d3848285016138ec565b60608301525060806147e7848285016138ec565b60808301525060a06147fb84828501613dac565b60a08301525092915050565b5f60c0828403121561481c5761481b613812565b5b5f6148298482850161476a565b91505092915050565b7f4d616b657220455243373231207061796d656e74206d75737420626520556e695f8201527f6e697469616c697a656400000000000000000000000000000000000000000000602082015250565b5f61488c602a83614186565b915061489782614832565b604082019050919050565b5f6020820190508181035f8301526148b981614880565b9050919050565b7f54616b6572206d757374206e6f74206265207a65726f206164647265737300005f82015250565b5f6148f4601e83614186565b91506148ff826148c0565b602082019050919050565b5f6020820190508181035f830152614921816148e8565b9050919050565b7f546f6b656e206d757374206e6f74206265207a65726f206164647265737300005f82015250565b5f61495c601e83614186565b915061496782614928565b602082019050919050565b5f6020820190508181035f83015261498981614950565b9050919050565b7f546f6b656e206164647265737320646f6573206e6f74206d617463682073656e5f8201527f6465720000000000000000000000000000000000000000000000000000000000602082015250565b5f6149ea602383614186565b91506149f582614990565b604082019050919050565b5f6020820190508181035f830152614a17816149de565b9050919050565b7f4f70657261746f72206d757374206265207468652073656e64657200000000005f82015250565b5f614a52601b83614186565b9150614a5d82614a1e565b602082019050919050565b5f6020820190508181035f830152614a7f81614a46565b9050919050565b7f54616b65722063616e6e6f74206265206120636f6e74726163740000000000005f82015250565b5f614aba601a83614186565b9150614ac582614a86565b602082019050919050565b5f6020820190508181035f830152614ae781614aae565b9050919050565b5f604082019050614b015f83018561446c565b614b0e602083018461446c565b9392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f614b4c8261395a565b9150614b578361395a565b9250828201905080821115614b6f57614b6e614b15565b5b92915050565b7f496e76616c6964207061796d656e742073746174652e204d75737420626520505f8201527f61796d656e7453656e74206f722054616b6572417070726f7665640000000000602082015250565b5f614bcf603b83614186565b9150614bda82614b75565b604082019050919050565b5f6020820190508181035f830152614bfc81614bc3565b9050919050565b7f43757272656e742074696d657374616d70206469646e277420657863656564205f8201527f7061796d656e74207072652d617070726f7665206c6f636b2074696d65000000602082015250565b5f614c5d603d83614186565b9150614c6882614c03565b604082019050919050565b5f6020820190508181035f830152614c8a81614c51565b9050919050565b7f54616b6572207061796d656e7420697320616c726561647920696e697469616c5f8201527f697a656400000000000000000000000000000000000000000000000000000000602082015250565b5f614ceb602483614186565b9150614cf682614c91565b604082019050919050565b5f6020820190508181035f830152614d1881614cdf565b9050919050565b7f5265636569766572206d757374206e6f74206265207a65726f206164647265735f8201527f7300000000000000000000000000000000000000000000000000000000000000602082015250565b5f614d79602183614186565b9150614d8482614d1f565b604082019050919050565b5f6020820190508181035f830152614da681614d6d565b9050919050565b7f4554482076616c7565206d7573742062652067726561746572207468616e207a5f8201527f65726f0000000000000000000000000000000000000000000000000000000000602082015250565b5f614e07602383614186565b9150614e1282614dad565b604082019050919050565b5f6020820190508181035f830152614e3481614dfb565b9050919050565b7f4554482076616c7565206d7573742062652067726561746572207468616e20645f8201527f6578206665650000000000000000000000000000000000000000000000000000602082015250565b5f614e95602683614186565b9150614ea082614e3b565b604082019050919050565b5f6020820190508181035f830152614ec281614e89565b9050919050565b5f614ed38261395a565b9150614ede8361395a565b9250828203905081811115614ef657614ef5614b15565b5b92915050565b7f4261746368207472616e7366657273206e6f7420737570706f727465640000005f82015250565b5f614f30601d83614186565b9150614f3b82614efc565b602082019050919050565b5f6020820190508181035f830152614f5d81614f24565b9050919050565b7f43616c6c6572206d75737420626520616e20454f4100000000000000000000005f82015250565b5f614f98601583614186565b9150614fa382614f64565b602082019050919050565b5f6020820190508181035f830152614fc581614f8c565b9050919050565b7f496e76616c6964207061796d656e742073746174652e204d75737420626520545f8201527f616b6572417070726f7665640000000000000000000000000000000000000000602082015250565b5f615026602c83614186565b915061503182614fcc565b604082019050919050565b5f6020820190508181035f8301526150538161501a565b9050919050565b7f4552433230207632207061796d656e7420697320616c726561647920696e69745f8201527f69616c697a656400000000000000000000000000000000000000000000000000602082015250565b5f6150b4602783614186565b91506150bf8261505a565b604082019050919050565b5f6020820190508181035f8301526150e1816150a8565b9050919050565b7f416d6f756e74206d757374206e6f74206265207a65726f0000000000000000005f82015250565b5f61511c601783614186565b9150615127826150e8565b602082019050919050565b5f6020820190508181035f83015261514981615110565b9050919050565b7f44657820666565206d757374206e6f74206265207a65726f00000000000000005f82015250565b5f615184601883614186565b915061518f82615150565b602082019050919050565b5f6020820190508181035f8301526151b181615178565b9050919050565b7f4d616b65722045524331313535207061796d656e74206d75737420626520556e5f8201527f696e697469616c697a6564000000000000000000000000000000000000000000602082015250565b5f615212602b83614186565b915061521d826151b8565b604082019050919050565b5f6020820190508181035f83015261523f81615206565b9050919050565b7f56616c7565206d7573742062652067726561746572207468616e2030000000005f82015250565b5f61527a601c83614186565b915061528582615246565b602082019050919050565b5f6020820190508181035f8301526152a78161526e565b9050919050565b5f6040820190506152c15f83018561404d565b6152ce6020830184614494565b9392505050565b6152de8161389a565b81146152e8575f80fd5b50565b5f815190506152f9816152d5565b92915050565b5f6020828403121561531457615313613812565b5b5f615321848285016152eb565b9150509291505056fea26469706673582212200d86b0f6898fb823c55626c3b02a7098bc8622606b092e0f458df6c86ce2967864736f6c63430008180033"; pub trait CoinDockerOps { fn rpc_client(&self) -> &UtxoRpcClientEnum; @@ -1028,8 +1043,19 @@ pub fn withdraw_max_and_send_v1(mm: &MarketMakerIt, coin: &str, to: &str) -> Tra tx_details } +async fn get_current_gas_limit(web3: &Web3) { + match web3.eth().block(BlockId::Number(BlockNumber::Latest)).await { + Ok(Some(block)) => { + log!("Current gas limit: {}", block.gas_limit); + }, + Ok(None) => log!("Latest block information is not available."), + Err(e) => log!("Failed to fetch the latest block: {}", e), + } +} + pub fn init_geth_node() { unsafe { + block_on(get_current_gas_limit(&GETH_WEB3)); let accounts = block_on(GETH_WEB3.eth().accounts()).unwrap(); GETH_ACCOUNT = accounts[0]; log!("GETH ACCOUNT {:?}", GETH_ACCOUNT); @@ -1146,6 +1172,124 @@ pub fn init_geth_node() { thread::sleep(Duration::from_millis(100)); } + let name = Token::String("MyNFT".into()); + let symbol = Token::String("MNFT".into()); + let params = ethabi::encode(&[name, symbol]); + let erc721_data = format!("{}{}", ERC721_TEST_TOKEN_BYTES, hex::encode(params)); + + let tx_request_deploy_erc721 = TransactionRequest { + from: GETH_ACCOUNT, + to: None, + gas: None, + gas_price: None, + value: None, + data: Some(hex::decode(erc721_data).unwrap().into()), + nonce: None, + condition: None, + transaction_type: None, + access_list: None, + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + }; + let deploy_erc721_tx_hash = block_on(GETH_WEB3.eth().send_transaction(tx_request_deploy_erc721)).unwrap(); + log!("Sent ERC721 deploy transaction {:?}", deploy_erc721_tx_hash); + + loop { + let deploy_erc721_tx_receipt = match block_on(GETH_WEB3.eth().transaction_receipt(deploy_erc721_tx_hash)) { + Ok(receipt) => receipt, + Err(_) => { + thread::sleep(Duration::from_millis(100)); + continue; + }, + }; + + if let Some(receipt) = deploy_erc721_tx_receipt { + GETH_ERC721_CONTRACT = receipt.contract_address.unwrap(); + log!("GETH_ERC721_CONTRACT {:?}", GETH_ERC721_CONTRACT); + break; + } + thread::sleep(Duration::from_millis(100)); + } + + let uri = Token::String("MyNFTUri".into()); + let params = ethabi::encode(&[uri]); + let erc1155_data = format!("{}{}", ERC1155_TEST_TOKEN_BYTES, hex::encode(params)); + + let tx_request_deploy_erc1155 = TransactionRequest { + from: GETH_ACCOUNT, + to: None, + gas: None, + gas_price: None, + value: None, + data: Some(hex::decode(erc1155_data).unwrap().into()), + nonce: None, + condition: None, + transaction_type: None, + access_list: None, + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + }; + let deploy_erc1155_tx_hash = block_on(GETH_WEB3.eth().send_transaction(tx_request_deploy_erc1155)).unwrap(); + log!("Sent ERC1155 deploy transaction {:?}", deploy_erc721_tx_hash); + + loop { + let deploy_erc1155_tx_receipt = match block_on(GETH_WEB3.eth().transaction_receipt(deploy_erc1155_tx_hash)) + { + Ok(receipt) => receipt, + Err(_) => { + thread::sleep(Duration::from_millis(100)); + continue; + }, + }; + + if let Some(receipt) = deploy_erc1155_tx_receipt { + GETH_ERC1155_CONTRACT = receipt.contract_address.unwrap(); + log!("GETH_ERC1155_CONTRACT {:?}", GETH_ERC1155_CONTRACT); + break; + } + thread::sleep(Duration::from_millis(100)); + } + + let dex_fee_address = Token::Address(geth_account()); + let params = ethabi::encode(&[dex_fee_address]); + let nft_swap_data = format!("{}{}", NFT_SWAP_CONTRACT_BYTES, hex::encode(params)); + + let tx_request_deploy_nft_swap_contract = TransactionRequest { + from: GETH_ACCOUNT, + to: None, + gas: None, + gas_price: None, + value: None, + data: Some(hex::decode(nft_swap_data).unwrap().into()), + nonce: None, + condition: None, + transaction_type: None, + access_list: None, + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + }; + let deploy_nft_swap_tx_hash = + block_on(GETH_WEB3.eth().send_transaction(tx_request_deploy_nft_swap_contract)).unwrap(); + log!("Sent deploy nft swap contract transaction {:?}", deploy_swap_tx_hash); + + loop { + let deploy_nft_swap_tx_receipt = + match block_on(GETH_WEB3.eth().transaction_receipt(deploy_nft_swap_tx_hash)) { + Ok(receipt) => receipt, + Err(_) => { + thread::sleep(Duration::from_millis(100)); + continue; + }, + }; + + if let Some(receipt) = deploy_nft_swap_tx_receipt { + GETH_NFT_SWAP_CONTRACT = receipt.contract_address.unwrap(); + log!("GETH_NFT_SWAP_CONTRACT {:?}", GETH_SWAP_CONTRACT); + break; + } + thread::sleep(Duration::from_millis(100)); + } + let alice_passphrase = get_passphrase!(".env.client", "ALICE_PASSPHRASE").unwrap(); let alice_keypair = key_pair_from_seed(&alice_passphrase).unwrap(); let alice_eth_addr = addr_from_raw_pubkey(alice_keypair.public()).unwrap(); diff --git a/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs b/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs index b908e51bbf..a8125ef3b0 100644 --- a/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs @@ -1,14 +1,19 @@ -use crate::docker_tests::docker_tests_common::{random_secp256k1_secret, GETH_ACCOUNT, GETH_ERC20_CONTRACT, - GETH_NONCE_LOCK, GETH_SWAP_CONTRACT, GETH_WATCHERS_SWAP_CONTRACT, - GETH_WEB3, MM_CTX}; -use bitcrypto::dhash160; -use coins::eth::{checksum_address, eth_coin_from_conf_and_request, EthCoin, ERC20_ABI}; -use coins::{CoinProtocol, ConfirmPaymentInput, FoundSwapTxSpend, MarketCoinOps, PrivKeyBuildPolicy, RefundPaymentArgs, - SearchForSwapTxSpendInput, SendPaymentArgs, SpendPaymentArgs, SwapOps, SwapTxTypeWithSecretHash}; +use super::docker_tests_common::{random_secp256k1_secret, ERC1155_TEST_ABI, ERC721_TEST_ABI, GETH_ACCOUNT, + GETH_ERC1155_CONTRACT, GETH_ERC20_CONTRACT, GETH_ERC721_CONTRACT, + GETH_NFT_SWAP_CONTRACT, GETH_NONCE_LOCK, GETH_SWAP_CONTRACT, + GETH_WATCHERS_SWAP_CONTRACT, GETH_WEB3, MM_CTX}; +use bitcrypto::{dhash160, sha256}; +use coins::eth::{checksum_address, eth_addr_to_hex, eth_coin_from_conf_and_request, EthCoin, ERC20_ABI}; +use coins::nft::nft_structs::{Chain, ContractType, NftInfo}; +use coins::{CoinProtocol, ConfirmPaymentInput, FoundSwapTxSpend, MakerNftSwapOpsV2, MarketCoinOps, NftSwapInfo, + ParseCoinAssocTypes, PrivKeyBuildPolicy, RefundPaymentArgs, SearchForSwapTxSpendInput, + SendNftMakerPaymentArgs, SendPaymentArgs, SpendNftMakerPaymentArgs, SpendPaymentArgs, SwapOps, + SwapTxTypeWithSecretHash, ToBytes, Transaction, ValidateNftMakerPaymentArgs}; use common::{block_on, now_sec}; use ethereum_types::U256; use futures01::Future; -use mm2_test_helpers::for_tests::{erc20_dev_conf, eth_dev_conf}; +use mm2_number::{BigDecimal, BigUint}; +use mm2_test_helpers::for_tests::{erc20_dev_conf, eth_dev_conf, nft_dev_conf}; use std::thread; use std::time::Duration; use web3::contract::{Contract, Options}; @@ -18,13 +23,18 @@ use web3::types::{Address, TransactionRequest, H256}; /// # Safety /// /// GETH_ACCOUNT is set once during initialization before tests start -fn geth_account() -> Address { unsafe { GETH_ACCOUNT } } +pub fn geth_account() -> Address { unsafe { GETH_ACCOUNT } } /// # Safety /// /// GETH_SWAP_CONTRACT is set once during initialization before tests start pub fn swap_contract() -> Address { unsafe { GETH_SWAP_CONTRACT } } +/// # Safety +/// +/// GETH_NFT_SWAP_CONTRACT is set once during initialization before tests start +pub fn nft_swap_contract() -> Address { unsafe { GETH_NFT_SWAP_CONTRACT } } + /// # Safety /// /// GETH_WATCHERS_SWAP_CONTRACT is set once during initialization before tests start @@ -38,6 +48,16 @@ pub fn erc20_contract() -> Address { unsafe { GETH_ERC20_CONTRACT } } /// Return ERC20 dev token contract address in checksum format pub fn erc20_contract_checksum() -> String { checksum_address(&format!("{:02x}", erc20_contract())) } +/// # Safety +/// +/// GETH_ERC721_CONTRACT is set once during initialization before tests start +pub fn erc721_contract() -> Address { unsafe { GETH_ERC721_CONTRACT } } + +/// # Safety +/// +/// GETH_ERC1155_CONTRACT is set once during initialization before tests start +pub fn erc1155_contract() -> Address { unsafe { GETH_ERC1155_CONTRACT } } + fn wait_for_confirmation(tx_hash: H256) { loop { match block_on(GETH_WEB3.eth().transaction_receipt(tx_hash)) { @@ -86,14 +106,130 @@ fn fill_erc20(to_addr: Address, amount: U256) { wait_for_confirmation(tx_hash); } +pub(crate) fn mint_erc721(to_addr: Address, token_id: U256) { + let _guard = GETH_NONCE_LOCK.lock().unwrap(); + let erc721_contract = Contract::from_json(GETH_WEB3.eth(), erc721_contract(), ERC721_TEST_ABI.as_bytes()).unwrap(); + + let options = Options { + gas: Some(U256::from(150_000)), + ..Options::default() + }; + + let tx_hash = block_on(erc721_contract.call( + "mint", + (Token::Address(to_addr), Token::Uint(token_id)), + geth_account(), + options, + )) + .unwrap(); + wait_for_confirmation(tx_hash); + + let owner: Address = + block_on(erc721_contract.query("ownerOf", Token::Uint(token_id), None, Options::default(), None)).unwrap(); + + assert_eq!( + owner, to_addr, + "The ownership of the tokenID {:?} does not match the expected address {:?}.", + token_id, to_addr + ); +} + +fn erc712_owner(token_id: U256) -> Address { + let _guard = GETH_NONCE_LOCK.lock().unwrap(); + let erc721_contract = Contract::from_json(GETH_WEB3.eth(), erc721_contract(), ERC721_TEST_ABI.as_bytes()).unwrap(); + block_on(erc721_contract.query("ownerOf", Token::Uint(token_id), None, Options::default(), None)).unwrap() +} + +pub(crate) fn mint_erc1155(to_addr: Address, token_id: U256, amount: U256) { + let _guard = GETH_NONCE_LOCK.lock().unwrap(); + let erc1155_contract = + Contract::from_json(GETH_WEB3.eth(), erc1155_contract(), ERC1155_TEST_ABI.as_bytes()).unwrap(); + + let tx_hash = block_on(erc1155_contract.call( + "mint", + ( + Token::Address(to_addr), + Token::Uint(token_id), + Token::Uint(amount), + Token::Bytes("".into()), + ), + geth_account(), + Options::default(), + )) + .unwrap(); + wait_for_confirmation(tx_hash); + + // Check the balance of the token for the to_addr + let balance: U256 = block_on(erc1155_contract.query( + "balanceOf", + (Token::Address(to_addr), Token::Uint(token_id)), + None, + Options::default(), + None, + )) + .unwrap(); + + assert_eq!( + balance, amount, + "The balance of tokenId {:?} for address {:?} does not match the expected amount {:?}.", + token_id, to_addr, amount + ); +} + +fn erc1155_balance(wallet_addr: Address, token_id: U256) -> U256 { + let _guard = GETH_NONCE_LOCK.lock().unwrap(); + let erc1155_contract = + Contract::from_json(GETH_WEB3.eth(), erc1155_contract(), ERC1155_TEST_ABI.as_bytes()).unwrap(); + block_on(erc1155_contract.query( + "balanceOf", + (Token::Address(wallet_addr), Token::Uint(token_id)), + None, + Options::default(), + None, + )) + .unwrap() +} + +pub(crate) async fn fill_erc1155_info(eth_coin: &EthCoin, tokens_id: u32, amount: u32) { + let nft_infos_lock = eth_coin.nfts_infos.clone(); + let mut nft_infos = nft_infos_lock.lock().await; + + let erc1155_nft_info = NftInfo { + token_address: erc1155_contract(), + token_id: BigUint::from(tokens_id), + chain: Chain::Eth, + contract_type: ContractType::Erc1155, + amount: BigDecimal::from(amount), + }; + let erc1155_address_str = eth_addr_to_hex(&erc1155_contract()); + let erc1155_key = format!("{},{}", erc1155_address_str, tokens_id); + nft_infos.insert(erc1155_key, erc1155_nft_info); +} + +pub(crate) async fn fill_erc721_info(eth_coin: &EthCoin, tokens_id: u32) { + let nft_infos_lock = eth_coin.nfts_infos.clone(); + let mut nft_infos = nft_infos_lock.lock().await; + + let erc721_nft_info = NftInfo { + token_address: erc721_contract(), + token_id: BigUint::from(tokens_id), + chain: Chain::Eth, + contract_type: ContractType::Erc721, + amount: BigDecimal::from(1), + }; + let erc721_address_str = eth_addr_to_hex(&erc721_contract()); + let erc721_key = format!("{},{}", erc721_address_str, tokens_id); + nft_infos.insert(erc721_key, erc721_nft_info); +} + /// Creates ETH protocol coin supplied with 100 ETH -pub fn eth_coin_with_random_privkey(swap_contract: Address) -> EthCoin { +pub fn eth_coin_with_random_privkey(swap_contract_address: Address) -> EthCoin { let eth_conf = eth_dev_conf(); let req = json!({ "method": "enable", "coin": "ETH", "urls": ["http://127.0.0.1:8545"], - "swap_contract_address": swap_contract, + "swap_contract_address": swap_contract_address, }); let secret = random_secp256k1_secret(); @@ -114,13 +250,13 @@ pub fn eth_coin_with_random_privkey(swap_contract: Address) -> EthCoin { } /// Creates ERC20 protocol coin supplied with 1 ETH and 100 token -pub fn erc20_coin_with_random_privkey(swap_contract: Address) -> EthCoin { +pub fn erc20_coin_with_random_privkey(swap_contract_address: Address) -> EthCoin { let erc20_conf = erc20_dev_conf(&erc20_contract_checksum()); let req = json!({ "method": "enable", "coin": "ERC20DEV", "urls": ["http://127.0.0.1:8545"], - "swap_contract_address": swap_contract, + "swap_contract_address": swap_contract_address, }); let erc20_coin = block_on(eth_coin_from_conf_and_request( @@ -144,6 +280,53 @@ pub fn erc20_coin_with_random_privkey(swap_contract: Address) -> EthCoin { erc20_coin } +pub enum TestNftType { + Erc1155 { token_id: u32, amount: u32 }, + Erc721 { token_id: u32 }, +} + +/// Generates a global NFT coin instance with a random private key and an initial 100 ETH balance. +/// Optionally mints a specified NFT (either ERC721 or ERC1155) to the global NFT address, +/// with details recorded in the `nfts_infos` field based on the provided `nft_type`. +pub fn global_nft_with_random_privkey(swap_contract_address: Address, nft_type: Option) -> EthCoin { + let nft_conf = nft_dev_conf(); + let req = json!({ + "method": "enable", + "coin": "NFT_ETH", + "urls": ["http://127.0.0.1:8545"], + "swap_contract_address": swap_contract_address, + }); + + let global_nft = block_on(eth_coin_from_conf_and_request( + &MM_CTX, + "NFT_ETH", + &nft_conf, + &req, + CoinProtocol::NFT { + platform: "ETH".to_string(), + }, + PrivKeyBuildPolicy::IguanaPrivKey(random_secp256k1_secret()), + )) + .unwrap(); + + fill_eth(global_nft.my_address, U256::from(10).pow(U256::from(20))); + + if let Some(nft_type) = nft_type { + match nft_type { + TestNftType::Erc1155 { token_id, amount } => { + mint_erc1155(global_nft.my_address, U256::from(token_id), U256::from(amount)); + block_on(fill_erc1155_info(&global_nft, token_id, amount)); + }, + TestNftType::Erc721 { token_id } => { + mint_erc721(global_nft.my_address, U256::from(token_id)); + block_on(fill_erc721_info(&global_nft, token_id)); + }, + } + } + + global_nft +} + #[test] fn send_and_refund_eth_maker_payment() { let eth_coin = eth_coin_with_random_privkey(swap_contract()); @@ -448,3 +631,170 @@ fn send_and_spend_erc20_maker_payment() { let expected = FoundSwapTxSpend::Spent(payment_spend); assert_eq!(expected, search_tx); } + +#[test] +fn send_and_spend_erc721_maker_payment() { + // TODO: Evaluate implementation strategy — either employing separate contracts for maker and taker + // functionalities for both coins and NFTs, or utilizing the Diamond Standard (EIP-2535) for a unified contract approach. + // Decision will inform whether to maintain multiple "swap_contract_address" fields in `EthCoin` for distinct contract types + // or a singular field for a Diamond Standard-compatible contract address. + + let erc721_nft = TestNftType::Erc721 { token_id: 2 }; + + let maker_global_nft = global_nft_with_random_privkey(nft_swap_contract(), Some(erc721_nft)); + let taker_global_nft = global_nft_with_random_privkey(nft_swap_contract(), None); + + let time_lock = now_sec() + 1000; + let maker_pubkey = maker_global_nft.derive_htlc_pubkey(&[]); + let taker_pubkey = taker_global_nft.derive_htlc_pubkey(&[]); + + let maker_secret = &[1; 32]; + let maker_secret_hash = sha256(maker_secret).to_vec(); + + let nft_swap_info = NftSwapInfo { + token_address: &erc721_contract(), + token_id: &BigUint::from(2u32).to_bytes(), + contract_type: &ContractType::Erc721, + swap_contract_address: &nft_swap_contract(), + }; + + let send_payment_args: SendNftMakerPaymentArgs = SendNftMakerPaymentArgs { + time_lock, + taker_secret_hash: &[0; 32], + maker_secret_hash: &maker_secret_hash, + amount: 1.into(), + taker_pub: &taker_global_nft.parse_pubkey(&taker_pubkey).unwrap(), + swap_unique_data: &[], + nft_swap_info: &nft_swap_info, + }; + let maker_payment = block_on(maker_global_nft.send_nft_maker_payment_v2(send_payment_args)).unwrap(); + println!("Maker sent ERC721 NFT Payment tx hash {:02x}", maker_payment.tx_hash()); + + let confirm_input = ConfirmPaymentInput { + payment_tx: maker_payment.tx_hex(), + confirmations: 1, + requires_nota: false, + wait_until: now_sec() + 70, + check_every: 1, + }; + maker_global_nft.wait_for_confirmations(confirm_input).wait().unwrap(); + + let validate_args = ValidateNftMakerPaymentArgs { + maker_payment_tx: &maker_payment, + time_lock, + taker_secret_hash: &[0; 32], + maker_secret_hash: &maker_secret_hash, + amount: 1.into(), + taker_pub: &taker_global_nft.parse_pubkey(&taker_pubkey).unwrap(), + maker_pub: &maker_global_nft.parse_pubkey(&maker_pubkey).unwrap(), + swap_unique_data: &[], + nft_swap_info: &nft_swap_info, + }; + block_on(maker_global_nft.validate_nft_maker_payment_v2(validate_args)).unwrap(); + + let spend_payment_args = SpendNftMakerPaymentArgs { + maker_payment_tx: &maker_payment, + time_lock, + taker_secret_hash: &[0; 32], + maker_secret_hash: &maker_secret_hash, + maker_secret, + maker_pub: &maker_global_nft.parse_pubkey(&maker_pubkey).unwrap(), + swap_unique_data: &[], + contract_type: &ContractType::Erc721, + swap_contract_address: &nft_swap_contract(), + }; + let spend_tx = block_on(taker_global_nft.spend_nft_maker_payment_v2(spend_payment_args)).unwrap(); + + let confirm_input = ConfirmPaymentInput { + payment_tx: spend_tx.tx_hex(), + confirmations: 1, + requires_nota: false, + wait_until: now_sec() + 70, + check_every: 1, + }; + taker_global_nft.wait_for_confirmations(confirm_input).wait().unwrap(); + + let new_owner = erc712_owner(U256::from(2)); + assert_eq!(new_owner, taker_global_nft.my_address); +} + +#[test] +fn send_and_spend_erc1155_maker_payment() { + let erc1155_nft = TestNftType::Erc1155 { token_id: 4, amount: 3 }; + + let maker_global_nft = global_nft_with_random_privkey(nft_swap_contract(), Some(erc1155_nft)); + let taker_global_nft = global_nft_with_random_privkey(nft_swap_contract(), None); + + let time_lock = now_sec() + 1000; + let maker_pubkey = maker_global_nft.derive_htlc_pubkey(&[]); + let taker_pubkey = taker_global_nft.derive_htlc_pubkey(&[]); + + let maker_secret = &[1; 32]; + let maker_secret_hash = sha256(maker_secret).to_vec(); + + let nft_swap_info = NftSwapInfo { + token_address: &erc1155_contract(), + token_id: &BigUint::from(4u32).to_bytes(), + contract_type: &ContractType::Erc1155, + swap_contract_address: &nft_swap_contract(), + }; + + let send_payment_args: SendNftMakerPaymentArgs = SendNftMakerPaymentArgs { + time_lock, + taker_secret_hash: &[0; 32], + maker_secret_hash: &maker_secret_hash, + amount: 3.into(), + taker_pub: &taker_global_nft.parse_pubkey(&taker_pubkey).unwrap(), + swap_unique_data: &[], + nft_swap_info: &nft_swap_info, + }; + let maker_payment = block_on(maker_global_nft.send_nft_maker_payment_v2(send_payment_args)).unwrap(); + println!("Maker sent ERC1155 NFT Payment tx hash {:02x}", maker_payment.tx_hash()); + + let confirm_input = ConfirmPaymentInput { + payment_tx: maker_payment.tx_hex(), + confirmations: 1, + requires_nota: false, + wait_until: now_sec() + 60, + check_every: 1, + }; + maker_global_nft.wait_for_confirmations(confirm_input).wait().unwrap(); + + let validate_args = ValidateNftMakerPaymentArgs { + maker_payment_tx: &maker_payment, + time_lock, + taker_secret_hash: &[0; 32], + maker_secret_hash: &maker_secret_hash, + amount: 3.into(), + taker_pub: &taker_global_nft.parse_pubkey(&taker_pubkey).unwrap(), + maker_pub: &maker_global_nft.parse_pubkey(&maker_pubkey).unwrap(), + swap_unique_data: &[], + nft_swap_info: &nft_swap_info, + }; + block_on(maker_global_nft.validate_nft_maker_payment_v2(validate_args)).unwrap(); + + let spend_payment_args = SpendNftMakerPaymentArgs { + maker_payment_tx: &maker_payment, + time_lock, + taker_secret_hash: &[0; 32], + maker_secret_hash: &maker_secret_hash, + maker_secret, + maker_pub: &maker_global_nft.parse_pubkey(&maker_pubkey).unwrap(), + swap_unique_data: &[], + contract_type: &ContractType::Erc1155, + swap_contract_address: &nft_swap_contract(), + }; + let spend_tx = block_on(taker_global_nft.spend_nft_maker_payment_v2(spend_payment_args)).unwrap(); + + let confirm_input = ConfirmPaymentInput { + payment_tx: spend_tx.tx_hex(), + confirmations: 1, + requires_nota: false, + wait_until: now_sec() + 60, + check_every: 1, + }; + taker_global_nft.wait_for_confirmations(confirm_input).wait().unwrap(); + + let balance = erc1155_balance(taker_global_nft.my_address, U256::from(4)); + assert_eq!(balance, U256::from(3)); +} diff --git a/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs b/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs index 202d6697f6..0dbadb9cb4 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs @@ -1,11 +1,10 @@ use crate::{generate_utxo_coin_with_random_privkey, MYCOIN, MYCOIN1}; use bitcrypto::dhash160; use coins::utxo::UtxoCommonOps; -use coins::{CoinAssocTypes, ConfirmPaymentInput, DexFee, FundingTxSpend, GenTakerFundingSpendArgs, - GenTakerPaymentSpendArgs, MakerCoinSwapOpsV2, MarketCoinOps, RefundFundingSecretArgs, - RefundMakerPaymentArgs, RefundPaymentArgs, SendMakerPaymentArgs, SendTakerFundingArgs, - SwapTxTypeWithSecretHash, TakerCoinSwapOpsV2, Transaction, ValidateMakerPaymentArgs, - ValidateTakerFundingArgs}; +use coins::{ConfirmPaymentInput, DexFee, FundingTxSpend, GenTakerFundingSpendArgs, GenTakerPaymentSpendArgs, + MakerCoinSwapOpsV2, MarketCoinOps, ParseCoinAssocTypes, RefundFundingSecretArgs, RefundMakerPaymentArgs, + RefundPaymentArgs, SendMakerPaymentArgs, SendTakerFundingArgs, SwapTxTypeWithSecretHash, + TakerCoinSwapOpsV2, Transaction, ValidateMakerPaymentArgs, ValidateTakerFundingArgs}; use common::{block_on, now_sec, DEX_FEE_ADDR_RAW_PUBKEY}; use futures01::Future; use mm2_number::MmNumber; diff --git a/mm2src/mm2_test_helpers/dummy_files/erc1155_test_abi.json b/mm2src/mm2_test_helpers/dummy_files/erc1155_test_abi.json new file mode 100644 index 0000000000..8b2e2c2f80 --- /dev/null +++ b/mm2src/mm2_test_helpers/dummy_files/erc1155_test_abi.json @@ -0,0 +1,455 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "uri", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ERC1155InsufficientBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "approver", + "type": "address" + } + ], + "name": "ERC1155InvalidApprover", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "idsLength", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "valuesLength", + "type": "uint256" + } + ], + "name": "ERC1155InvalidArrayLength", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "ERC1155InvalidOperator", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "ERC1155InvalidReceiver", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "ERC1155InvalidSender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "ERC1155MissingApprovalForAll", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + } + ], + "name": "TransferBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "TransferSingle", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "value", + "type": "string" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "URI", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "accounts", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + } + ], + "name": "balanceOfBatch", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeBatchTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "uri", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/mm2src/mm2_test_helpers/dummy_files/erc721_test_abi.json b/mm2src/mm2_test_helpers/dummy_files/erc721_test_abi.json new file mode 100644 index 0000000000..697eccce5c --- /dev/null +++ b/mm2src/mm2_test_helpers/dummy_files/erc721_test_abi.json @@ -0,0 +1,469 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "ERC721IncorrectOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ERC721InsufficientApproval", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "approver", + "type": "address" + } + ], + "name": "ERC721InvalidApprover", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "ERC721InvalidOperator", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "ERC721InvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "ERC721InvalidReceiver", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "ERC721InvalidSender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ERC721NonexistentToken", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/mm2src/mm2_test_helpers/src/for_tests.rs b/mm2src/mm2_test_helpers/src/for_tests.rs index 1728d92464..439a33813f 100644 --- a/mm2src/mm2_test_helpers/src/for_tests.rs +++ b/mm2src/mm2_test_helpers/src/for_tests.rs @@ -813,6 +813,23 @@ pub fn erc20_dev_conf(contract_address: &str) -> Json { }) } +/// global NFT configuration used for dockerized Geth dev node +pub fn nft_dev_conf() -> Json { + json!({ + "coin": "NFT_ETH", + "name": "nftdev", + "chain_id": 1337, + "mm2": 1, + "derivation_path": "m/44'/60'", + "protocol": { + "type": "NFT", + "protocol_data": { + "platform": "ETH" + } + } + }) +} + pub fn eth_sepolia_conf() -> Json { json!({ "coin": "ETH",