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;
 }
Example #2
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 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));
        }