public TezosHistoryService(ILogger <TezosHistoryService> logger, TezosMonitorClient tezosMonitorClient, IOptions <TezosConfig> tezosConfig, IPushHistoryService pushHistoryService) { _logger = logger; _tezosMonitorClient = tezosMonitorClient.Client; _tezosConfig = tezosConfig.Value; _pushHistoryService = pushHistoryService; }
private static void GetConfiguration() { var configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddEnvironmentVariables() .Build(); _azureConfig = configuration.GetSection("Azure").Get <AzureConfig>(); _tezosConfig = configuration.GetSection("Tezos").Get <TezosConfig>(); if (string.IsNullOrEmpty(_tezosConfig.NodeUrl)) { throw new ArgumentException( $"Tezos:NodeUrl configuration is empty. Please provide a valid URL in appsettings.json or ENV variables."); } if (string.IsNullOrEmpty(_azureConfig.AzureFunctionUrl)) { throw new ArgumentException( $"Azure:AzureFunctionUrl configuration is empty. Please provide a valid URL in appsettings.json or ENV variables."); } if (string.IsNullOrEmpty(_azureConfig.AzureFunctionKey)) { throw new ArgumentException( $"Azure:AzureFunctionKey configuration is empty. Please provide a valid key in appsettings.json or ENV variables."); } }
public static IWalletViewModel CreateViewModel( IAtomexApp app, IDialogViewer dialogViewer, IMenuSelector menuSelector, IConversionViewModel conversionViewModel, CurrencyConfig currency) { return(currency switch { BitcoinBasedConfig _ or Erc20Config _ or EthereumConfig _ => new WalletViewModel( app: app, dialogViewer: dialogViewer, menuSelector: menuSelector, conversionViewModel: conversionViewModel, currency: currency), Fa12Config _ => new Fa12WalletViewModel( app: app, dialogViewer: dialogViewer, menuSelector: menuSelector, conversionViewModel: conversionViewModel, currency: currency), TezosConfig _ => new TezosWalletViewModel( app: app, dialogViewer: dialogViewer, menuSelector: menuSelector, conversionViewModel: conversionViewModel, currency: currency), _ => throw new NotSupportedException($"Can't create wallet view model for {currency.Name}. This currency is not supported."), });
public static ICurrencySwap Create( CurrencyConfig currency, IAccount account) { return(currency switch { BitcoinBasedConfig _ => new BitcoinBasedSwap( account: account.GetCurrencyAccount <BitcoinBasedAccount>(currency.Name), currencies: account.Currencies), Erc20Config _ => new Erc20Swap( account: account.GetCurrencyAccount <Erc20Account>(currency.Name), ethereumAccount: account.GetCurrencyAccount <EthereumAccount>("ETH"), currencies: account.Currencies), EthereumConfig _ => new EthereumSwap( account: account.GetCurrencyAccount <EthereumAccount>(currency.Name), currencies: account.Currencies), Fa12Config _ => new Fa12Swap( account: account.GetCurrencyAccount <Fa12Account>(currency.Name), tezosAccount: account.GetCurrencyAccount <TezosAccount>(TezosConfig.Xtz), currencies: account.Currencies), TezosConfig _ => new TezosSwap( account: account.GetCurrencyAccount <TezosAccount>(currency.Name), currencies: account.Currencies), _ => throw new NotSupportedException($"Not supported currency {currency.Name}") });
public TezosTokenTransferViewModel(TokenTransfer tx, TezosConfig tezosConfig) { _tezosConfig = tezosConfig; Transaction = tx ?? throw new ArgumentNullException(nameof(tx)); Id = tx.Hash; State = Transaction.State; Type = GetType(Transaction.Type); From = tx.From; To = tx.To; Amount = GetAmount(tx); AmountFormat = $"F{Math.Min(tx.Token.Decimals, MaxAmountDecimals)}"; CurrencyCode = tx.Token.Symbol; Time = tx.CreationTime ?? DateTime.UtcNow; Alias = tx.Alias; TxExplorerUri = $"{_tezosConfig.TxExplorerUri}{Id}"; Description = GetDescription( type: tx.Type, amount: Amount, netAmount: Amount, amountDigits: tx.Token.Decimals, currencyCode: tx.Token.Symbol); }
public TezosTransactionViewModel(TezosTransaction tx, TezosConfig tezosConfig) : base(tx, tezosConfig, GetAmount(tx, tezosConfig), GetFee(tx)) { From = tx.From; To = tx.To; GasLimit = tx.GasLimit; Fee = TezosConfig.MtzToTz(tx.Fee); IsInternal = tx.IsInternal; }
public static Task StartSwapInitiatedControlAsync( Swap swap, CurrencyConfig currency, TezosConfig tezos, long refundTimeStamp, TimeSpan interval, Func <Swap, CancellationToken, Task> initiatedHandler, Func <Swap, CancellationToken, Task> canceledHandler, CancellationToken cancellationToken = default) { return(Task.Run(async() => { try { while (!cancellationToken.IsCancellationRequested) { if (swap.IsCanceled) { canceledHandler?.Invoke(swap, cancellationToken); break; } var isInitiatedResult = await IsInitiatedAsync( swap: swap, currency: currency, tezos: tezos, refundTimeStamp: refundTimeStamp, cancellationToken: cancellationToken) .ConfigureAwait(false); if (isInitiatedResult.HasError && isInitiatedResult.Error.Code != Errors.RequestError) { canceledHandler?.Invoke(swap, cancellationToken); break; } else if (!isInitiatedResult.HasError && isInitiatedResult.Value) { initiatedHandler?.Invoke(swap, cancellationToken); break; } await Task.Delay(interval, cancellationToken) .ConfigureAwait(false); } } catch (OperationCanceledException) { Log.Debug("StartSwapInitiatedControlAsync canceled."); } catch (Exception e) { Log.Error(e, "StartSwapInitiatedControlAsync error."); } }, cancellationToken)); }
public static SendViewModel CreateViewModel(IAtomexApp app, CurrencyViewModel currencyViewModel) { return(currencyViewModel.Currency switch { BitcoinBasedConfig _ => (SendViewModel) new BitcoinBasedSendViewModel(app, currencyViewModel), Erc20Config _ => (SendViewModel) new Erc20SendViewModel(app, currencyViewModel), EthereumConfig _ => (SendViewModel) new EthereumSendViewModel(app, currencyViewModel), Fa12Config _ => (SendViewModel) new Fa12SendViewModel(app, currencyViewModel), TezosConfig _ => (SendViewModel) new TezosSendViewModel(app, currencyViewModel), _ => throw new NotSupportedException($"Can't create send view model for {currencyViewModel.Currency.Name}. This currency is not supported."), });
public TzktApi(TezosConfig currency) { _currency = currency; _baseUri = currency.BaseUri; _rpcNodeUri = currency.RpcNodeUri; _headers = new HttpRequestHeaders { new KeyValuePair <string, IEnumerable <string> >("User-Agent", new string[] { "Atomex" }) }; }
private static decimal GetFee(TezosTransaction tx) { var result = 0m; if (tx.Type.HasFlag(BlockchainTransactionType.Output)) { result += TezosConfig.MtzToTz(tx.Fee); } tx.InternalTxs?.ForEach(t => result += GetFee(t)); return(result); }
public static CurrencyViewModel CreateViewModel( IAtomexApp app, CurrencyConfig currency, bool loadTransactions = true) { return(currency switch { BitcoinBasedConfig _ or Erc20Config _ or EthereumConfig _ => new CurrencyViewModel(app, currency, loadTransactions), Fa12Config _ => new Fa12CurrencyViewModel(app, currency), TezosConfig _ => new TezosCurrencyViewModel(app, currency), _ => throw new NotSupportedException($"Can't create currency view model for {currency.Name}. This currency is not supported."), });
private static decimal GetAmount(TezosTransaction tx, TezosConfig tezosConfig) { var result = 0m; if (tx.Type.HasFlag(BlockchainTransactionType.Input)) { result += tx.Amount / tezosConfig.DigitsMultiplier; } var includeFee = tezosConfig.Name == tezosConfig.FeeCurrencyName; var fee = includeFee ? tx.Fee : 0; if (tx.Type.HasFlag(BlockchainTransactionType.Output)) { result += -(tx.Amount + fee) / tezosConfig.DigitsMultiplier; } tx.InternalTxs?.ForEach(t => result += GetAmount(t, tezosConfig)); return(result); }
public static async Task <Result <byte[]> > IsRedeemedAsync( Swap swap, CurrencyConfig currency, TezosConfig tezos, int attempts, int attemptIntervalInSec, CancellationToken cancellationToken = default) { var attempt = 0; while (!cancellationToken.IsCancellationRequested && attempt < attempts) { ++attempt; var isRedeemedResult = await IsRedeemedAsync( swap : swap, currency : currency, tezos : tezos, cancellationToken : cancellationToken) .ConfigureAwait(false); if (isRedeemedResult.HasError && isRedeemedResult.Error.Code != Errors.RequestError) // has error { return(isRedeemedResult); } if (!isRedeemedResult.HasError) { return(isRedeemedResult); } await Task.Delay(TimeSpan.FromSeconds(attemptIntervalInSec), cancellationToken) .ConfigureAwait(false); } return(new Error(Errors.MaxAttemptsCountReached, "Max attempts count reached for redeem check")); }
public TezosRevealChecker(TezosConfig tezos) { _tezos = tezos; _addresses = new Dictionary <string, TezosAddressInfo>(); }
public static async Task <Result <byte[]> > IsRedeemedAsync( Swap swap, CurrencyConfig currency, TezosConfig tezos, CancellationToken cancellationToken = default) { try { Log.Debug("Tezos FA12: check redeem event"); var fa12 = (Fa12Config)currency; var contractAddress = fa12.SwapContractAddress; var blockchainApi = (ITezosBlockchainApi)tezos.BlockchainApi; var txsResult = await blockchainApi .TryGetTransactionsAsync(contractAddress, cancellationToken : cancellationToken) .ConfigureAwait(false); if (txsResult == null) { return(new Error(Errors.RequestError, $"Connection error while getting txs from contract {contractAddress}")); } if (txsResult.HasError) { Log.Error("Error while get transactions from contract {@contract}. Code: {@code}. Description: {@desc}", contractAddress, txsResult.Error.Code, txsResult.Error.Description); return(txsResult.Error); } var txs = txsResult.Value ?.Cast <TezosTransaction>() .ToList(); if (txs != null) { foreach (var tx in txs) { if (tx.To == contractAddress && IsSwapRedeem(tx, swap.SecretHash)) { // redeem! var secret = GetSecret(tx); Log.Debug("Redeem event received with secret {@secret}", Convert.ToBase64String(secret)); return(secret); } if (tx.BlockInfo?.BlockTime == null) { continue; } var blockTimeUtc = tx.BlockInfo.BlockTime.Value.ToUniversalTime(); var swapTimeUtc = swap.TimeStamp.ToUniversalTime(); if (blockTimeUtc < swapTimeUtc) { break; } } } } catch (Exception e) { Log.Error(e, "Tezos FA12 redeem control task error"); return(new Error(Errors.InternalError, e.Message)); } return((byte[])null); }
public static Task StartSwapRedeemedControlAsync( Swap swap, CurrencyConfig currency, TezosConfig tezos, DateTime refundTimeUtc, TimeSpan interval, bool cancelOnlyIfRefundTimeReached = true, Func <Swap, byte[], CancellationToken, Task> redeemedHandler = null, Func <Swap, DateTime, CancellationToken, Task> canceledHandler = null, CancellationToken cancellationToken = default) { return(Task.Run(async() => { try { while (!cancellationToken.IsCancellationRequested) { var isRedeemedResult = await IsRedeemedAsync( swap: swap, currency: currency, tezos: tezos, cancellationToken: cancellationToken) .ConfigureAwait(false); if (isRedeemedResult.HasError && isRedeemedResult.Error.Code != Errors.RequestError) // has error { await canceledHandler .Invoke(swap, refundTimeUtc, cancellationToken) .ConfigureAwait(false); break; } else if (!isRedeemedResult.HasError && isRedeemedResult.Value != null) // has secret { await redeemedHandler .Invoke(swap, isRedeemedResult.Value, cancellationToken) .ConfigureAwait(false); break; } if (!cancelOnlyIfRefundTimeReached || DateTime.UtcNow >= refundTimeUtc) { await canceledHandler .Invoke(swap, refundTimeUtc, cancellationToken) .ConfigureAwait(false); break; } await Task.Delay(interval, cancellationToken) .ConfigureAwait(false); } } catch (OperationCanceledException) { Log.Debug("StartSwapRedeemedControlAsync canceled."); } catch (Exception e) { Log.Error(e, "StartSwapRedeemedControlAsync error."); } }, cancellationToken)); }
public override Task UpdateBalanceAsync( string address, CancellationToken cancellationToken = default) { return(Task.Run(async() => { try { var xtz = Config; var walletAddress = await DataRepository .GetWalletAddressAsync(Currency, address) .ConfigureAwait(false); if (walletAddress == null) { return; } var balanceResult = await xtz.BlockchainApi .TryGetBalanceAsync(address, cancellationToken: cancellationToken) .ConfigureAwait(false); if (balanceResult.HasError) { Log.Error("Error while balance update for {@address} with code {@code} and description {@description}", address, balanceResult.Error.Code, balanceResult.Error.Description); return; } var balance = balanceResult.Value; //.ToTez(); // calculate unconfirmed balances var unconfirmedTxs = (await DataRepository .GetUnconfirmedTransactionsAsync(Currency, xtz.TransactionType) .ConfigureAwait(false)) .Cast <TezosTransaction>() .ToList(); var unconfirmedInternalTxs = unconfirmedTxs.Aggregate(new List <TezosTransaction>(), (list, tx) => { if (tx.InternalTxs != null) { list.AddRange(tx.InternalTxs); } return list; }); var unconfirmedIncome = 0m; var unconfirmedOutcome = 0m; foreach (var utx in unconfirmedTxs.Concat(unconfirmedInternalTxs)) { var isFailed = utx.State == BlockchainTransactionState.Failed; unconfirmedIncome += address == utx.To && !isFailed ? TezosConfig.MtzToTz(utx.Amount) : 0; unconfirmedOutcome += address == utx.From && !isFailed ? -TezosConfig.MtzToTz(utx.Amount + utx.Fee + utx.Burn) : 0; } var balanceDifference = balance - walletAddress.Balance; var unconfirmedIncomeDifference = unconfirmedIncome - walletAddress.UnconfirmedIncome; var unconfirmedOutcomeDifference = unconfirmedOutcome - walletAddress.UnconfirmedOutcome; if (balanceDifference != 0 || unconfirmedIncomeDifference != 0 || unconfirmedOutcomeDifference != 0) { walletAddress.Balance = balance; walletAddress.UnconfirmedIncome = unconfirmedIncome; walletAddress.UnconfirmedOutcome = unconfirmedOutcome; walletAddress.HasActivity = true; await DataRepository.UpsertAddressAsync(walletAddress) .ConfigureAwait(false); Balance += balanceDifference; UnconfirmedIncome += unconfirmedIncomeDifference; UnconfirmedOutcome += unconfirmedOutcomeDifference; RaiseBalanceUpdated(new CurrencyEventArgs(Currency)); } } catch (OperationCanceledException) { Log.Debug($"{Currency} UpdateBalanceAsync canceled."); } catch (Exception e) { Log.Error(e, "Tezos UpdateBalanceAsync error."); } }, cancellationToken)); }
public override Task UpdateBalanceAsync( CancellationToken cancellationToken = default) { return(Task.Run(async() => { try { var xtz = Config; var txs = (await DataRepository .GetTransactionsAsync(Currency, xtz.TransactionType) .ConfigureAwait(false)) .Cast <TezosTransaction>() .ToList(); var internalTxs = txs.Aggregate(new List <TezosTransaction>(), (list, tx) => { if (tx.InternalTxs != null) { list.AddRange(tx.InternalTxs); } return list; }); // calculate unconfirmed balances var totalUnconfirmedIncome = 0m; var totalUnconfirmedOutcome = 0m; var addresses = new Dictionary <string, WalletAddress>(); foreach (var tx in txs.Concat(internalTxs)) { var selfAddresses = new HashSet <string>(); var isFromSelf = await IsSelfAddressAsync(tx.From, cancellationToken) .ConfigureAwait(false); //if (tx.Type.HasFlag(BlockchainTransactionType.Output)) if (isFromSelf) { selfAddresses.Add(tx.From); } var isToSelf = await IsSelfAddressAsync(tx.To, cancellationToken) .ConfigureAwait(false); //if (tx.Type.HasFlag(BlockchainTransactionType.Input)) if (isToSelf) { selfAddresses.Add(tx.To); } foreach (var address in selfAddresses) { var isIncome = address == tx.To; var isOutcome = address == tx.From; var isConfirmed = tx.IsConfirmed; var isFailed = tx.State == BlockchainTransactionState.Failed; var income = isIncome && !isFailed ? TezosConfig.MtzToTz(tx.Amount) : 0; var outcome = isOutcome && !isFailed ? -TezosConfig.MtzToTz(tx.Amount + tx.Fee + tx.Burn) : 0; if (addresses.TryGetValue(address, out var walletAddress)) { walletAddress.UnconfirmedIncome += !isConfirmed ? income : 0; walletAddress.UnconfirmedOutcome += !isConfirmed ? outcome : 0; } else { walletAddress = await DataRepository .GetWalletAddressAsync(Currency, address) .ConfigureAwait(false); if (walletAddress == null) { continue; } walletAddress.UnconfirmedIncome = !isConfirmed ? income : 0; walletAddress.UnconfirmedOutcome = !isConfirmed ? outcome : 0; walletAddress.HasActivity = true; addresses.Add(address, walletAddress); } totalUnconfirmedIncome += !isConfirmed ? income : 0; totalUnconfirmedOutcome += !isConfirmed ? outcome : 0; } } var totalBalance = 0m; foreach (var wa in addresses.Values) { var balanceResult = await xtz.BlockchainApi .TryGetBalanceAsync( address: wa.Address, cancellationToken: cancellationToken) .ConfigureAwait(false); if (balanceResult.HasError) { Log.Error("Error while getting balance for {@address} with code {@code} and description {@description}", wa.Address, balanceResult.Error.Code, balanceResult.Error.Description); continue; // todo: may be return? } wa.Balance = balanceResult.Value; //.ToTez(); totalBalance += wa.Balance; } // upsert addresses await DataRepository .UpsertAddressesAsync(addresses.Values) .ConfigureAwait(false); Balance = totalBalance; UnconfirmedIncome = totalUnconfirmedIncome; UnconfirmedOutcome = totalUnconfirmedOutcome; RaiseBalanceUpdated(new CurrencyEventArgs(Currency)); } catch (OperationCanceledException) { Log.Debug($"{Currency} UpdateBalanceAsync canceled."); } catch (Exception e) { Log.Error(e, "Tezos UpdateBalanceAsync error."); } }, cancellationToken)); }
public async Task <(bool result, bool isRunSuccess, bool hasReveal)> FillOperationsAsync( SecureBytes securePublicKey, TezosConfig tezosConfig, int headOffset = 0, bool isAlreadyRevealed = false, CancellationToken cancellationToken = default) { using var publicKey = securePublicKey.ToUnsecuredBytes(); var rpc = new Rpc(tezosConfig.RpcNodeUri); var managerKey = await rpc .GetManagerKey(From) .ConfigureAwait(false); var actualHead = await rpc .GetHeader() .ConfigureAwait(false); if (Head == null) { Head = await rpc .GetHeader(headOffset) .ConfigureAwait(false); } Operations = new JArray(); var gas = GasLimit.ToString(CultureInfo.InvariantCulture); var storage = StorageLimit.ToString(CultureInfo.InvariantCulture); var revealed = managerKey.Value <string>() != null || isAlreadyRevealed; UsedCounters = revealed ? 1 : 2; var counter = UseOfflineCounter ? await TezosCounter.Instance .GetOfflineCounterAsync( address : From, head : actualHead["hash"].ToString(), rpcNodeUri : tezosConfig.RpcNodeUri, numberOfCounters : UsedCounters) .ConfigureAwait(false) : await TezosCounter.Instance .GetCounterAsync( address : From, head : actualHead["hash"].ToString(), rpcNodeUri : tezosConfig.RpcNodeUri) .ConfigureAwait(false); if (!revealed) { var revealOp = new JObject { ["kind"] = Internal.OperationType.Reveal, ["fee"] = "0", ["public_key"] = Base58Check.Encode(publicKey, Prefix.Edpk), ["source"] = From, ["storage_limit"] = "0", ["gas_limit"] = tezosConfig.RevealGasLimit.ToString(), ["counter"] = counter.ToString() }; Operations.AddFirst(revealOp); counter++; } var operation = new JObject { ["kind"] = OperationType, ["source"] = From, ["fee"] = ((int)Fee).ToString(CultureInfo.InvariantCulture), ["counter"] = counter.ToString(), ["gas_limit"] = gas, ["storage_limit"] = storage, }; if (OperationType == Internal.OperationType.Transaction) { operation["amount"] = Math.Round(Amount, 0).ToString(CultureInfo.InvariantCulture); operation["destination"] = To; } else if (OperationType == Internal.OperationType.Delegation) { operation["delegate"] = To; } else { throw new NotSupportedException($"Operation type {OperationType} not supporeted yet."); } Operations.Add(operation); if (Params != null) { operation["parameters"] = Params; } var isRunSuccess = false; if (UseRun) { var fill = await rpc .AutoFillOperations(tezosConfig, Head, Operations, UseSafeStorageLimit) .ConfigureAwait(false); if (!fill) { Log.Warning("Operation autofilling error"); } else { Fee = Operations.Last["fee"].Value <decimal>().ToTez(); isRunSuccess = true; } } return( result : true, isRunSuccess : isRunSuccess, hasReveal : !revealed ); }
public static async Task <Result <bool> > IsInitiatedAsync( Swap swap, CurrencyConfig currency, TezosConfig tezos, long refundTimeStamp, CancellationToken cancellationToken = default) { try { Log.Debug("Tezos FA12: check initiated event"); var fa12 = (Fa12Config)currency; var side = swap.Symbol .OrderSideForBuyCurrency(swap.PurchasedCurrency) .Opposite(); var requiredAmountInTokenDigits = AmountHelper .QtyToSellAmount(side, swap.Qty, swap.Price, fa12.DigitsMultiplier) .ToTokenDigits(fa12.DigitsMultiplier); var requiredRewardForRedeemInTokenDigits = swap.IsAcceptor ? swap.RewardForRedeem.ToTokenDigits(fa12.DigitsMultiplier) : 0; var contractAddress = fa12.SwapContractAddress; var detectedAmountInTokenDigits = 0m; var detectedRedeemFeeAmountInTokenDigits = 0m; long detectedRefundTimestamp = 0; var blockchainApi = (ITezosBlockchainApi)tezos.BlockchainApi; var txsResult = await blockchainApi .TryGetTransactionsAsync(contractAddress, cancellationToken : cancellationToken) .ConfigureAwait(false); if (txsResult == null) { return(new Error(Errors.RequestError, $"Connection error while getting txs from contract {contractAddress}")); } if (txsResult.HasError) { Log.Error("Error while get transactions from contract {@contract}. Code: {@code}. Description: {@desc}", contractAddress, txsResult.Error.Code, txsResult.Error.Description); return(txsResult.Error); } var txs = txsResult.Value ?.Cast <TezosTransaction>() .ToList(); if (txs == null || !txs.Any()) { return(false); } foreach (var tx in txs) { if (tx.IsConfirmed && tx.To == contractAddress) { var detectedPayment = false; if (IsSwapInit(tx, swap.SecretHash.ToHexString(), fa12.TokenContractAddress, swap.ToAddress, refundTimeStamp)) { // init payment to secret hash! detectedPayment = true; detectedAmountInTokenDigits += GetAmount(tx); detectedRedeemFeeAmountInTokenDigits = GetRedeemFee(tx); detectedRefundTimestamp = GetRefundTimestamp(tx); } ///not implemented //else if (IsSwapAdd(tx, swap.SecretHash)) //{ // detectedPayment = true; // detectedAmountInMtz += tx.Amount; //} if (detectedPayment && detectedAmountInTokenDigits >= requiredAmountInTokenDigits) { if (swap.IsAcceptor && detectedRedeemFeeAmountInTokenDigits != requiredRewardForRedeemInTokenDigits) { Log.Debug( "Invalid redeem fee in initiated event. Expected value is {@expected}, actual is {@actual}", requiredRewardForRedeemInTokenDigits, detectedRedeemFeeAmountInTokenDigits); return(new Error( code: Errors.InvalidRewardForRedeem, description: $"Invalid redeem fee in initiated event. Expected value is {requiredRewardForRedeemInTokenDigits}, actual is {detectedRedeemFeeAmountInTokenDigits}")); } if (detectedRefundTimestamp != refundTimeStamp) { Log.Debug( "Invalid refund timestamp in initiated event. Expected value is {@expected}, actual is {@actual}", refundTimeStamp, detectedRefundTimestamp); return(new Error( code: Errors.InvalidRewardForRedeem, description: $"Invalid refund timestamp in initiated event. Expected value is {refundTimeStamp}, actual is {detectedRefundTimestamp}")); } return(true); // todo: check also token contract transfers } } if (tx.BlockInfo?.BlockTime == null) { continue; } var blockTimeUtc = tx.BlockInfo.BlockTime.Value.ToUniversalTime(); var swapTimeUtc = swap.TimeStamp.ToUniversalTime(); if (blockTimeUtc < swapTimeUtc) { return(false); } } } catch (Exception e) { Log.Error(e, "Tezos token swap initiated control task error"); return(new Error(Errors.InternalError, e.Message)); } return(false); }
public static IEnumerable <SwapDetailingInfo> GetSwapDetailingInfo(Swap swap, ICurrencies currencies) { var soldCurrencyConfig = currencies.GetByName(swap.SoldCurrency); var purchaseCurrencyConfig = currencies.GetByName(swap.PurchasedCurrency); IList <SwapDetailingInfo> result = new List <SwapDetailingInfo>(); if (swap.StateFlags.HasFlag(SwapStateFlags.HasSecretHash) && swap.Status.HasFlag(SwapStatus.Initiated) && swap.Status.HasFlag(SwapStatus.Accepted)) { result.Add(new SwapDetailingInfo { Status = SwapDetailingStatus.Initialization, IsCompleted = true, Description = "Orders matched" }); result.Add(new SwapDetailingInfo { Status = SwapDetailingStatus.Initialization, IsCompleted = true, Description = "Credentials exchanged" }); } else { if (swap.IsCanceled) { result.Add(new SwapDetailingInfo { Status = SwapDetailingStatus.Initialization, IsCompleted = false, Description = "Error during orders matching and credentials exchanging" }); return(result); } result.Add(new SwapDetailingInfo { Status = SwapDetailingStatus.Initialization, IsCompleted = false, Description = "Waiting while orders are matched and credentials exchanged" }); return(result); } if (swap.StateFlags.HasFlag(SwapStateFlags.HasPartyPayment) && swap.StateFlags.HasFlag(SwapStateFlags.IsPartyPaymentConfirmed)) { var swapDetailingStep = new SwapDetailingInfo { Status = SwapDetailingStatus.Exchanging, IsCompleted = false, Description = $"{swap.PurchasedCurrency} counterparty payment", ExplorerLink = new DetailsLink { Text = "transaction confirmed", Url = purchaseCurrencyConfig switch { EthereumConfig ethereumConfig => $"{ethereumConfig.AddressExplorerUri}{ethereumConfig.SwapContractAddress}", TezosConfig tezosConfig => $"{tezosConfig.AddressExplorerUri}{tezosConfig.SwapContractAddress}", _ => null } } }; result.Add(swapDetailingStep); } else { if (swap.IsCanceled) { var swapDetailingStep = new SwapDetailingInfo { Status = SwapDetailingStatus.Exchanging, IsCompleted = false, Description = $"Counterparty {swap.PurchasedCurrency} payment", ExplorerLink = new DetailsLink { Text = "transaction failed", Url = purchaseCurrencyConfig switch { EthereumConfig ethereumConfig => $"{ethereumConfig.AddressExplorerUri}{ethereumConfig.SwapContractAddress}", TezosConfig tezosConfig => $"{tezosConfig.AddressExplorerUri}{tezosConfig.SwapContractAddress}", _ => null } } }; result.Add(swapDetailingStep); return(result); } else { var swapDetailingStep = new SwapDetailingInfo { Status = SwapDetailingStatus.Exchanging, IsCompleted = false, Description = $"Waiting for confirmation counterparty {swap.PurchasedCurrency}", ExplorerLink = new DetailsLink { Text = "payment transaction", Url = purchaseCurrencyConfig switch { EthereumConfig ethereumConfig => $"{ethereumConfig.AddressExplorerUri}{ethereumConfig.SwapContractAddress}", TezosConfig tezosConfig => $"{tezosConfig.AddressExplorerUri}{tezosConfig.SwapContractAddress}", _ => null } } }; result.Add(swapDetailingStep); return(result); } } if (swap.StateFlags.HasFlag(SwapStateFlags.IsPaymentConfirmed) || swap.IsComplete || swap.IsRefunded || swap.IsUnsettled || swap.IsCanceled && swap.StateFlags.HasFlag(SwapStateFlags.HasSecret)) { var swapDetailingStep = new SwapDetailingInfo { Status = SwapDetailingStatus.Exchanging, IsCompleted = true, Description = $"Your {swap.SoldCurrency} payment", ExplorerLink = new DetailsLink { Text = "transaction confirmed", Url = soldCurrencyConfig switch { EthereumConfig ethereumConfig => $"{ethereumConfig.AddressExplorerUri}{ethereumConfig.SwapContractAddress}", TezosConfig tezosConfig => $"{tezosConfig.AddressExplorerUri}{tezosConfig.SwapContractAddress}", _ => null } } }; result.Add(swapDetailingStep); } else { if (swap.IsCanceled) { var swapDetailingStep = new SwapDetailingInfo { Status = SwapDetailingStatus.Exchanging, IsCompleted = false, Description = $"Your {swap.SoldCurrency} payment", ExplorerLink = new DetailsLink { Text = "transaction failed", Url = soldCurrencyConfig switch { EthereumConfig ethereumConfig => $"{ethereumConfig.AddressExplorerUri}{ethereumConfig.SwapContractAddress}", TezosConfig tezosConfig => $"{tezosConfig.AddressExplorerUri}{tezosConfig.SwapContractAddress}", _ => null } } }; result.Add(swapDetailingStep); return(result); } // your payment broadcasted but not confirmed. if (swap.StateFlags.HasFlag(SwapStateFlags.IsPaymentBroadcast)) { var swapDetailingStep = new SwapDetailingInfo { Status = SwapDetailingStatus.Exchanging, IsCompleted = false, Description = $"Waiting for confirmation your {swap.SoldCurrency}", ExplorerLink = new DetailsLink { Text = "payment transaction", Url = soldCurrencyConfig switch { EthereumConfig ethereumConfig => $"{ethereumConfig.AddressExplorerUri}{ethereumConfig.SwapContractAddress}", TezosConfig tezosConfig => $"{tezosConfig.AddressExplorerUri}{tezosConfig.SwapContractAddress}", _ => null } } }; result.Add(swapDetailingStep); return(result); } // your payment not yet created. result.Add(new SwapDetailingInfo { Status = SwapDetailingStatus.Exchanging, IsCompleted = false, Description = $"Creating your {swap.SoldCurrency} payment transaction." }); return(result); } if (swap.StateFlags.HasFlag(SwapStateFlags.HasSecret)) { var swapDetailingStep = new SwapDetailingInfo { Status = SwapDetailingStatus.Completion, IsCompleted = false, Description = $"Counterparty {swap.SoldCurrency} redeem", ExplorerLink = new DetailsLink { Text = "transaction confirmed", Url = soldCurrencyConfig switch { EthereumConfig ethereumConfig => $"{ethereumConfig.AddressExplorerUri}{ethereumConfig.SwapContractAddress}", TezosConfig tezosConfig => $"{tezosConfig.AddressExplorerUri}{tezosConfig.SwapContractAddress}", _ => null } } }; result.Add(swapDetailingStep); } else { if (swap.IsCanceled) { var swapDetailingStep = new SwapDetailingInfo { Status = SwapDetailingStatus.Completion, IsCompleted = false, Description = $"Counterparty {swap.SoldCurrency} redeem", ExplorerLink = new DetailsLink { Text = "transaction failed", Url = soldCurrencyConfig switch { EthereumConfig ethereumConfig => $"{ethereumConfig.AddressExplorerUri}{ethereumConfig.SwapContractAddress}", TezosConfig tezosConfig => $"{tezosConfig.AddressExplorerUri}{tezosConfig.SwapContractAddress}", _ => null } } }; result.Add(swapDetailingStep); return(result); } else { var swapDetailingStep = new SwapDetailingInfo { Status = SwapDetailingStatus.Completion, IsCompleted = false, Description = $"Waiting for confirmation counterparty {swap.SoldCurrency}", ExplorerLink = new DetailsLink { Text = "redeem transaction", Url = soldCurrencyConfig switch { EthereumConfig ethereumConfig => $"{ethereumConfig.AddressExplorerUri}{ethereumConfig.SwapContractAddress}", TezosConfig tezosConfig => $"{tezosConfig.AddressExplorerUri}{tezosConfig.SwapContractAddress}", _ => null } } }; result.Add(swapDetailingStep); return(result); } } if (swap.StateFlags.HasFlag(SwapStateFlags.IsRedeemConfirmed)) { var swapDetailingStep = new SwapDetailingInfo { Status = SwapDetailingStatus.Completion, IsCompleted = true, Description = $"Your {swap.PurchasedCurrency} redeem", ExplorerLink = new DetailsLink { Text = "transaction confirmed", Url = purchaseCurrencyConfig switch { EthereumConfig ethereumConfig => $"{ethereumConfig.AddressExplorerUri}{ethereumConfig.SwapContractAddress}", TezosConfig tezosConfig => $"{tezosConfig.AddressExplorerUri}{tezosConfig.SwapContractAddress}", _ => null } } }; result.Add(swapDetailingStep); } else { if (swap.StateFlags.HasFlag(SwapStateFlags.IsRefundConfirmed)) { var swapDetailingStep = new SwapDetailingInfo { Status = SwapDetailingStatus.Completion, IsCompleted = true, Description = $"Your {swap.SoldCurrency} refund", ExplorerLink = new DetailsLink { Text = "transaction confirmed", Url = soldCurrencyConfig switch { EthereumConfig ethereumConfig => $"{ethereumConfig.AddressExplorerUri}{ethereumConfig.SwapContractAddress}", TezosConfig tezosConfig => $"{tezosConfig.AddressExplorerUri}{tezosConfig.SwapContractAddress}", _ => null } } }; result.Add(swapDetailingStep); } else if (swap.StateFlags.HasFlag(SwapStateFlags.IsRefundBroadcast)) { var swapDetailingStep = new SwapDetailingInfo { Status = SwapDetailingStatus.Completion, IsCompleted = true, Description = $"Waiting for confirmation your {swap.SoldCurrency}", ExplorerLink = new DetailsLink { Text = "refund transaction", Url = soldCurrencyConfig switch { EthereumConfig ethereumConfig => $"{ethereumConfig.AddressExplorerUri}{ethereumConfig.SwapContractAddress}", TezosConfig tezosConfig => $"{tezosConfig.AddressExplorerUri}{tezosConfig.SwapContractAddress}", _ => null } } }; result.Add(swapDetailingStep); } else { if (swap.IsCanceled) { var swapDetailingStep = new SwapDetailingInfo { Status = SwapDetailingStatus.Completion, IsCompleted = false, Description = $"Your {swap.PurchasedCurrency} redeem", ExplorerLink = new DetailsLink { Text = "transaction failed", Url = purchaseCurrencyConfig switch { EthereumConfig ethereumConfig => $"{ethereumConfig.AddressExplorerUri}{ethereumConfig.SwapContractAddress}", TezosConfig tezosConfig => $"{tezosConfig.AddressExplorerUri}{tezosConfig.SwapContractAddress}", _ => null } } }; result.Add(swapDetailingStep); } else { var swapDetailingStep = new SwapDetailingInfo { Status = SwapDetailingStatus.Completion, IsCompleted = false, Description = $"Waiting for confirmation your {swap.PurchasedCurrency}", ExplorerLink = new DetailsLink { Text = "redeem transaction", Url = purchaseCurrencyConfig switch { EthereumConfig ethereumConfig => $"{ethereumConfig.AddressExplorerUri}{ethereumConfig.SwapContractAddress}", TezosConfig tezosConfig => $"{tezosConfig.AddressExplorerUri}{tezosConfig.SwapContractAddress}", _ => null } } }; result.Add(swapDetailingStep); } } } return(result); }