Esempio n. 1
0
 public TezosHistoryService(ILogger <TezosHistoryService> logger, TezosMonitorClient tezosMonitorClient, IOptions <TezosConfig> tezosConfig, IPushHistoryService pushHistoryService)
 {
     _logger             = logger;
     _tezosMonitorClient = tezosMonitorClient.Client;
     _tezosConfig        = tezosConfig.Value;
     _pushHistoryService = pushHistoryService;
 }
Esempio n. 2
0
        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."),
            });
Esempio n. 4
0
        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}")
            });
Esempio n. 5
0
        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));
        }
Esempio n. 8
0
 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."),
     });
Esempio n. 9
0
        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" })
            };
        }
Esempio n. 10
0
        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."),
            });
Esempio n. 12
0
        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);
        }
Esempio n. 13
0
        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"));
        }
Esempio n. 14
0
 public TezosRevealChecker(TezosConfig tezos)
 {
     _tezos     = tezos;
     _addresses = new Dictionary <string, TezosAddressInfo>();
 }
Esempio n. 15
0
        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);
        }
Esempio n. 16
0
        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));
        }
Esempio n. 17
0
        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));
        }
Esempio n. 18
0
        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);
        }
Esempio n. 21
0
        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);
        }