Ejemplo n.º 1
0
        public override Empty ChargeTransactionFees(ChargeTransactionFeesInput input)
        {
            if (input.Equals(new ChargeTransactionFeesInput()))
            {
                return(new Empty());
            }

            ChargeFirstSufficientToken(input.SymbolToAmount, out var symbol,
                                       out var amount, out var existingBalance);

            if (State.PreviousBlockTransactionFeeTokenSymbolList.Value == null)
            {
                State.PreviousBlockTransactionFeeTokenSymbolList.Value = new TokenSymbolList();
            }

            if (!State.PreviousBlockTransactionFeeTokenSymbolList.Value.SymbolList.Contains(symbol))
            {
                State.PreviousBlockTransactionFeeTokenSymbolList.Value.SymbolList.Add(symbol);
            }

            var fromAddress = Context.Sender;

            State.Balances[fromAddress][symbol]    = existingBalance.Sub(amount);
            State.ChargedFees[fromAddress][symbol] = State.ChargedFees[fromAddress][symbol].Add(amount);
            return(new Empty());
        }
Ejemplo n.º 2
0
        private bool ChargeSizeFee(ChargeTransactionFeesInput input, ref TransactionFeeBill bill)
        {
            string symbolChargedForBaseFee = null;
            var    amountChargedForBaseFee = 0L;
            var    symbolToPayTxFee        = input.PrimaryTokenSymbol;

            if (bill.FeesMap.Any())
            {
                symbolChargedForBaseFee = bill.FeesMap.First().Key;
                amountChargedForBaseFee = bill.FeesMap.First().Value;
            }

            var availableBalance = symbolChargedForBaseFee == symbolToPayTxFee
                                   // Available balance need to deduct amountChargedForBaseFee
                ? GetBalance(Context.Sender, symbolToPayTxFee).Sub(amountChargedForBaseFee)
                : GetBalance(Context.Sender, symbolToPayTxFee);

            var txSizeFeeAmount = input.TransactionSizeFee;

            if (input.SymbolsToPayTxSizeFee.Any())
            {
                var allSymbolToTxFee = input.SymbolsToPayTxSizeFee;
                var availableSymbol  = allSymbolToTxFee.FirstOrDefault(x =>
                                                                       GetBalanceCalculatedBaseOnPrimaryToken(x, symbolChargedForBaseFee,
                                                                                                              amountChargedForBaseFee) >= txSizeFeeAmount) ??
                                       allSymbolToTxFee.FirstOrDefault(x =>
                                                                       GetBalanceCalculatedBaseOnPrimaryToken(x, symbolChargedForBaseFee,
                                                                                                              amountChargedForBaseFee) > 0);
                if (availableSymbol != null && availableSymbol.TokenSymbol != symbolToPayTxFee)
                {
                    symbolToPayTxFee = availableSymbol.TokenSymbol;
                    txSizeFeeAmount  = txSizeFeeAmount.Mul(availableSymbol.AddedTokenWeight)
                                       .Div(availableSymbol.BaseTokenWeight);
                    availableBalance = symbolChargedForBaseFee == symbolToPayTxFee
                        ? GetBalance(Context.Sender, symbolToPayTxFee).Sub(amountChargedForBaseFee)
                        : GetBalance(Context.Sender, symbolToPayTxFee);
                }
            }

            var chargeAmount = availableBalance > txSizeFeeAmount
                ? txSizeFeeAmount
                : availableBalance;

            if (symbolToPayTxFee == null)
            {
                return(availableBalance >= txSizeFeeAmount);
            }

            if (symbolChargedForBaseFee == symbolToPayTxFee)
            {
                bill.FeesMap[symbolToPayTxFee] =
                    bill.FeesMap[symbolToPayTxFee].Add(chargeAmount);
            }
            else
            {
                bill.FeesMap.Add(symbolToPayTxFee, chargeAmount);
            }

            return(availableBalance >= txSizeFeeAmount);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Related transactions will be generated by acs1 pre-plugin service,
        /// and will be executed before the origin transaction.
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public override BoolValue ChargeTransactionFees(ChargeTransactionFeesInput input)
        {
            Assert(input.MethodName != null && input.ContractAddress != null, "Invalid charge transaction fees input.");

            // Primary token not created yet.
            if (string.IsNullOrEmpty(input.PrimaryTokenSymbol))
            {
                return(new BoolValue {
                    Value = true
                });
            }

            // Record tx fee bill during current charging process.
            var bill = new TransactionFeeBill();

            var fromAddress = Context.Sender;
            var methodFees  = Context.Call <MethodFees>(input.ContractAddress, nameof(GetMethodFee),
                                                        new StringValue {
                Value = input.MethodName
            });
            var successToChargeBaseFee = true;

            if (methodFees != null && methodFees.Fees.Any())
            {
                successToChargeBaseFee = ChargeBaseFee(GetBaseFeeDictionary(methodFees), ref bill);
            }

            var successToChargeSizeFee = true;

            if (!IsMethodFeeSetToZero(methodFees))
            {
                // Then also do not charge size fee.
                successToChargeSizeFee = ChargeSizeFee(input, ref bill);
            }

            // Update balances.
            foreach (var tokenToAmount in bill.FeesMap)
            {
                ModifyBalance(fromAddress, tokenToAmount.Key, -tokenToAmount.Value);
                Context.Fire(new TransactionFeeCharged
                {
                    Symbol = tokenToAmount.Key,
                    Amount = tokenToAmount.Value
                });
                if (tokenToAmount.Value == 0)
                {
                    Context.LogDebug(() => $"Maybe incorrect charged tx fee of {tokenToAmount.Key}: it's 0.");
                }
            }

            return(new BoolValue {
                Value = successToChargeBaseFee && successToChargeSizeFee
            });
        }
Ejemplo n.º 4
0
        public async Task Set_Repeat_Token_Test()
        {
            await IssueTokenAsync(NativeTokenSymbol, 100000_00000000);
            await SetPrimaryTokenSymbolAsync();

            var address        = DefaultSender;
            var methodName     = nameof(TokenContractContainer.TokenContractStub.Transfer);
            var basicMethodFee = 1000;
            var methodFee      = new MethodFees
            {
                MethodName = methodName,
                Fees       =
                {
                    new MethodFee
                    {
                        Symbol   = NativeTokenSymbol,
                        BasicFee = basicMethodFee
                    },
                    new MethodFee
                    {
                        Symbol   = NativeTokenSymbol,
                        BasicFee = basicMethodFee
                    }
                }
            };
            var sizeFee = 0;

            await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress,
                                                                nameof(TokenContractImplContainer.TokenContractImplStub.SetMethodFee), methodFee);

            var beforeChargeBalance = await GetBalanceAsync(address, NativeTokenSymbol);

            var chargeTransactionFeesInput = new ChargeTransactionFeesInput
            {
                MethodName         = methodName,
                ContractAddress    = TokenContractAddress,
                TransactionSizeFee = sizeFee,
            };

            var chargeFeeRet = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput);

            chargeFeeRet.Output.Success.ShouldBeTrue();
            var afterChargeBalance = await GetBalanceAsync(address, NativeTokenSymbol);

            beforeChargeBalance.Sub(afterChargeBalance).ShouldBe(basicMethodFee.Add(basicMethodFee));
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Related transactions will be generated by acs1 pre-plugin service,
        /// and will be executed before the origin transaction.
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public override TransactionFee ChargeTransactionFees(ChargeTransactionFeesInput input)
        {
            Assert(input.MethodName != null && input.ContractAddress != null, "Invalid charge transaction fees input.");

            var transactionFee = new TransactionFee();

            // Primary token not created yet.
            if (string.IsNullOrEmpty(input.PrimaryTokenSymbol))
            {
                return(transactionFee);
            }

            // Record tx fee bill during current charging process.
            var bill = new TransactionFeeBill();

            var fromAddress = Context.Sender;
            var methodFees  = Context.Call <MethodFees>(input.ContractAddress, nameof(GetMethodFee),
                                                        new StringValue {
                Value = input.MethodName
            });
            var successToChargeBaseFee = true;

            if (methodFees != null && methodFees.Fees.Any())
            {
                successToChargeBaseFee = ChargeBaseFee(GetBaseFeeDictionary(methodFees), ref bill);
            }

            var successToChargeSizeFee = ChargeSizeFee(input, ref bill);

            // Update the bill.
            var oldBill = State.ChargedFees[fromAddress];

            State.ChargedFees[fromAddress] = oldBill == null ? bill : oldBill + bill;

            // Update balances.
            foreach (var tokenToAmount in bill.TokenToAmount)
            {
                State.Balances[fromAddress][tokenToAmount.Key] =
                    State.Balances[fromAddress][tokenToAmount.Key].Sub(tokenToAmount.Value);
                transactionFee.Value[tokenToAmount.Key] = tokenToAmount.Value;
            }

            transactionFee.IsFailedToCharge = !successToChargeBaseFee || !successToChargeSizeFee;
            return(transactionFee);
        }
Ejemplo n.º 6
0
        public override Empty ChargeTransactionFees(ChargeTransactionFeesInput input)
        {
            if (input.Equals(new ChargeTransactionFeesInput()))
            {
                return(new Empty());
            }

            var tokenInfo = AssertValidToken(input.Symbol, input.Amount);

            Assert(tokenInfo.Symbol == State.NativeTokenSymbol.Value, "The paid fee is not in native token.");
            var fromAddress     = Context.Sender;
            var existingBalance = State.Balances[fromAddress][input.Symbol];

            Assert(existingBalance >= input.Amount, "Insufficient balance.");
            State.Balances[fromAddress][input.Symbol]    = existingBalance.Sub(input.Amount);
            State.ChargedFees[fromAddress][input.Symbol] =
                State.ChargedFees[fromAddress][input.Symbol].Add(input.Amount);
            return(new Empty());
        }
Ejemplo n.º 7
0
        private bool ChargeSizeFee(ChargeTransactionFeesInput input, ref TransactionFeeBill bill)
        {
            string symbolChargedForBaseFee = null;
            var    amountChargedForBaseFee = 0L;

            if (bill.TokenToAmount.Any())
            {
                symbolChargedForBaseFee = bill.TokenToAmount.First().Key;
                amountChargedForBaseFee = bill.TokenToAmount.First().Value;
            }

            var availableBalance = symbolChargedForBaseFee == input.PrimaryTokenSymbol
                                   // Available balance need to deduct amountChargedForBaseFee
                ? State.Balances[Context.Sender][input.PrimaryTokenSymbol].Sub(amountChargedForBaseFee)
                : State.Balances[Context.Sender][input.PrimaryTokenSymbol];
            var txSizeFeeAmount = input.TransactionSizeFee;
            var chargeAmount    = availableBalance > txSizeFeeAmount // Is available balance enough to pay tx size fee?
                ? txSizeFeeAmount
                : availableBalance;

            if (input.PrimaryTokenSymbol == null)
            {
                return(availableBalance >= txSizeFeeAmount);
            }

            if (symbolChargedForBaseFee == input.PrimaryTokenSymbol)
            {
                bill.TokenToAmount[input.PrimaryTokenSymbol] =
                    bill.TokenToAmount[input.PrimaryTokenSymbol].Add(chargeAmount);
            }
            else
            {
                bill.TokenToAmount.Add(input.PrimaryTokenSymbol, chargeAmount);
            }

            return(availableBalance >= txSizeFeeAmount);
        }
Ejemplo n.º 8
0
        public async Task <IEnumerable <Transaction> > GetPreTransactionsAsync(
            IReadOnlyList <ServiceDescriptor> descriptors, ITransactionContext transactionContext)
        {
            try
            {
                var context = _contextService.Create();

                if (_transactionFeeExemptionService.IsFree(transactionContext.Transaction))
                {
                    return(new List <Transaction>());
                }

                context.TransactionContext = transactionContext;
                var tokenContractAddress = context.GetContractAddressByName(TokenSmartContractAddressNameProvider.Name);

                if (context.CurrentHeight < Constants.GenesisBlockHeight + 1 || tokenContractAddress == null)
                {
                    return(new List <Transaction>());
                }

                if (!IsAcs1(descriptors) && transactionContext.Transaction.To != tokenContractAddress)
                {
                    return(new List <Transaction>());
                }

                var tokenStub = GetTokenContractStub(transactionContext.Transaction.From, tokenContractAddress);
                if (transactionContext.Transaction.To == tokenContractAddress &&
                    transactionContext.Transaction.MethodName == nameof(tokenStub.ChargeTransactionFees))
                {
                    // Skip ChargeTransactionFees itself
                    return(new List <Transaction>());
                }

                var txSize       = transactionContext.Transaction.Size();
                var chainContext = new ChainContext
                {
                    BlockHash   = transactionContext.PreviousBlockHash,
                    BlockHeight = transactionContext.BlockHeight - 1
                };
                var txCost = await _calStrategy.GetCostAsync(chainContext, txSize);

                var chargeTransactionFeesInput = new ChargeTransactionFeesInput
                {
                    MethodName         = transactionContext.Transaction.MethodName,
                    ContractAddress    = transactionContext.Transaction.To,
                    TransactionSizeFee = txCost,
                    PrimaryTokenSymbol = await _primaryTokenSymbolProvider.GetPrimaryTokenSymbol(),
                };
                var symbolListToPayTxSizeFee =
                    await _symbolListToPayTxFeeService.GetExtraAcceptedTokensInfoAsync(chainContext);

                if (symbolListToPayTxSizeFee != null)
                {
                    foreach (var tokenInfo in symbolListToPayTxSizeFee)
                    {
                        chargeTransactionFeesInput.SymbolsToPayTxSizeFee.Add(new SymbolToPayTXSizeFee
                        {
                            TokenSymbol      = tokenInfo.TokenSymbol,
                            BaseTokenWeight  = tokenInfo.BaseTokenWeight,
                            AddedTokenWeight = tokenInfo.AddedTokenWeight
                        });
                    }
                }

                var chargeFeeTransaction = (await tokenStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput))
                                           .Transaction;
                return(new List <Transaction>
                {
                    chargeFeeTransaction
                });
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Failed to generate ChargeTransactionFees tx.");
                throw;
            }
        }
        public async Task <IEnumerable <Transaction> > GetPreTransactionsAsync(
            IReadOnlyList <ServiceDescriptor> descriptors, ITransactionContext transactionContext)
        {
            try
            {
                var chainContext = new ChainContext
                {
                    BlockHash   = transactionContext.PreviousBlockHash,
                    BlockHeight = transactionContext.BlockHeight - 1
                };

                var tokenContractAddress = await _smartContractAddressService.GetAddressByContractNameAsync(chainContext,
                                                                                                            TokenSmartContractAddressNameProvider.StringName);

                if (transactionContext.BlockHeight < AElfConstants.GenesisBlockHeight + 1 || tokenContractAddress == null)
                {
                    return(new List <Transaction>());
                }

                if (!IsTargetAcsSymbol(descriptors) && transactionContext.Transaction.To != tokenContractAddress)
                {
                    return(new List <Transaction>());
                }

                var tokenStub = _contractReaderFactory.Create(new ContractReaderContext
                {
                    Sender          = transactionContext.Transaction.From,
                    ContractAddress = tokenContractAddress
                });

                if (transactionContext.Transaction.To == tokenContractAddress &&
                    transactionContext.Transaction.MethodName == nameof(tokenStub.ChargeTransactionFees))
                {
                    // Skip ChargeTransactionFees itself
                    return(new List <Transaction>());
                }

                var txCost = await _txFeeService.CalculateFeeAsync(transactionContext, chainContext);

                var chargeTransactionFeesInput = new ChargeTransactionFeesInput
                {
                    MethodName         = transactionContext.Transaction.MethodName,
                    ContractAddress    = transactionContext.Transaction.To,
                    TransactionSizeFee = txCost,
                };

                var transactionSizeFeeSymbols =
                    await _transactionSizeFeeSymbolsProvider.GetTransactionSizeFeeSymbolsAsync(chainContext);

                if (transactionSizeFeeSymbols != null)
                {
                    foreach (var transactionSizeFeeSymbol in transactionSizeFeeSymbols.TransactionSizeFeeSymbolList)
                    {
                        chargeTransactionFeesInput.SymbolsToPayTxSizeFee.Add(new SymbolToPayTxSizeFee
                        {
                            TokenSymbol      = transactionSizeFeeSymbol.TokenSymbol,
                            BaseTokenWeight  = transactionSizeFeeSymbol.BaseTokenWeight,
                            AddedTokenWeight = transactionSizeFeeSymbol.AddedTokenWeight
                        });
                    }
                }

                var chargeFeeTransaction = tokenStub.ChargeTransactionFees.GetTransaction(chargeTransactionFeesInput);
                return(new List <Transaction>
                {
                    chargeFeeTransaction
                });
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Failed to generate ChargeTransactionFees tx.");
                throw;
            }
        }
Ejemplo n.º 10
0
        public async Task ChargeTransactionFees_With_Different_Transaction_Size_Fee_Token(int[] order, long[] balance,
                                                                                          int[] baseWeight, int[] tokenWeight, long sizeFee, string chargeSymbol, long chargeAmount, bool isSuccess)
        {
            await SetPrimaryTokenSymbolAsync();

            var methodName     = nameof(TokenContractContainer.TokenContractStub.Transfer);
            var basicMethodFee = 1000;
            var methodFee      = new MethodFees
            {
                MethodName = methodName,
                Fees       =
                {
                    new MethodFee
                    {
                        Symbol   = NativeTokenSymbol,
                        BasicFee = basicMethodFee
                    }
                }
            };

            await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress,
                                                                nameof(TokenContractImplContainer.TokenContractImplStub.SetMethodFee), methodFee);

            var tokenSymbolList   = new [] { NativeTokenSymbol, "CWJ", "YPA" };
            var tokenCount        = 3;
            var orderedSymbolList = new string[tokenCount];
            var index             = 0;

            foreach (var o in order)
            {
                orderedSymbolList[index++] = tokenSymbolList[o - 1];
            }

            var sizeFeeSymbolList = new SymbolListToPayTxSizeFee();

            for (var i = 0; i < tokenCount; i++)
            {
                var tokenSymbol = orderedSymbolList[i];
                if (tokenSymbol != NativeTokenSymbol)
                {
                    await CreateTokenAsync(DefaultSender, tokenSymbol);
                }
                if (balance[i] > 0)
                {
                    await IssueTokenAsync(tokenSymbol, balance[i]);
                }
                sizeFeeSymbolList.SymbolsToPayTxSizeFee.Add(new SymbolToPayTxSizeFee
                {
                    TokenSymbol      = tokenSymbol,
                    AddedTokenWeight = tokenWeight[i],
                    BaseTokenWeight  = baseWeight[i]
                });
            }

            await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress,
                                                                nameof(TokenContractImplContainer.TokenContractImplStub.SetSymbolsToPayTxSizeFee), sizeFeeSymbolList);

            var beforeBalanceList = await GetDefaultBalancesAsync(orderedSymbolList);

            var chargeTransactionFeesInput = new ChargeTransactionFeesInput
            {
                MethodName         = methodName,
                ContractAddress    = TokenContractAddress,
                TransactionSizeFee = sizeFee,
            };

            chargeTransactionFeesInput.SymbolsToPayTxSizeFee.AddRange(sizeFeeSymbolList.SymbolsToPayTxSizeFee);

            var chargeFeeRet = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput);

            chargeFeeRet.Output.Success.ShouldBe(isSuccess);
            var afterBalanceList = await GetDefaultBalancesAsync(orderedSymbolList);

            for (var i = 0; i < tokenCount; i++)
            {
                var balanceDiff = beforeBalanceList[i] - afterBalanceList[i];
                if (orderedSymbolList[i] == chargeSymbol)
                {
                    balanceDiff.ShouldBe(chargeAmount);
                }
                else
                {
                    if (orderedSymbolList[i] == NativeTokenSymbol)
                    {
                        balanceDiff -= basicMethodFee;
                    }
                    balanceDiff.ShouldBe(0);
                }
            }
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Related transactions will be generated by acs1 pre-plugin service,
        /// and will be executed before the origin transaction.
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public override TransactionFee ChargeTransactionFees(ChargeTransactionFeesInput input)
        {
            Assert(input.MethodName != null && input.ContractAddress != null, "Invalid charge transaction fees input.");

            var result = new TransactionFee();

            if (input.PrimaryTokenSymbol == string.Empty)
            {
                // Primary token not created yet.
                return(result);
            }

            var fee = Context.Call <MethodFees>(input.ContractAddress, nameof(GetMethodFee),
                                                new StringValue {
                Value = input.MethodName
            });

            var bill = new TransactionFeeBill();

            var fromAddress = Context.Sender;

            if (fee != null && fee.Fees.Any())
            {
                if (!ChargeFirstSufficientToken(fee.Fees.ToDictionary(f => f.Symbol, f => f.BasicFee), out var symbol,
                                                out var amount, out var existingBalance))
                {
                    Assert(false, "Failed to charge first sufficient token.");
                }

                bill.TokenToAmount.Add(symbol, amount);
                // Charge base fee.
                State.Balances[fromAddress][symbol] = existingBalance.Sub(amount);
                result.Value.Add(symbol, amount);
            }

            var balanceAfterChargingBaseFee = State.Balances[fromAddress][input.PrimaryTokenSymbol];
            var txSizeFeeAmount             = input.TransactionSizeFee;

            txSizeFeeAmount = balanceAfterChargingBaseFee > txSizeFeeAmount // Enough to pay tx size fee.
                ? txSizeFeeAmount
                                                                            // It's safe to convert from long to int here because
                                                                            // balanceAfterChargingBaseFee <= txSizeFeeAmount and
                                                                            // typeof(txSizeFeeAmount) == int
                : (int)balanceAfterChargingBaseFee;

            bill += new TransactionFeeBill
            {
                TokenToAmount =
                {
                    {
                        input.PrimaryTokenSymbol,
                        txSizeFeeAmount
                    }
                }
            };

            // Charge tx size fee.
            var finalBalanceOfNativeSymbol = balanceAfterChargingBaseFee.Sub(txSizeFeeAmount);

            State.Balances[fromAddress][input.PrimaryTokenSymbol] = finalBalanceOfNativeSymbol;

            // Record the bill finally.
            var oldBill = State.ChargedFees[fromAddress];

            State.ChargedFees[fromAddress] = oldBill == null ? bill : oldBill + bill;

            // If balanceAfterChargingBaseFee < txSizeFeeAmount, make sender's balance of native symbol to 0 and make current execution failed.
            Assert(
                balanceAfterChargingBaseFee >=
                input.TransactionSizeFee,
                $"Insufficient balance to pay tx size fee: {balanceAfterChargingBaseFee} < {input.TransactionSizeFee}.\n " +
                $"Primary token: {input.PrimaryTokenSymbol}");

            if (result.Value.ContainsKey(input.PrimaryTokenSymbol))
            {
                result.Value[input.PrimaryTokenSymbol] =
                    result.Value[input.PrimaryTokenSymbol].Add(txSizeFeeAmount);
            }
            else
            {
                result.Value.Add(input.PrimaryTokenSymbol, txSizeFeeAmount);
            }

            return(result);
        }
Ejemplo n.º 12
0
        /// <summary>
        /// Related transactions will be generated by acs1 pre-plugin service,
        /// and will be executed before the origin transaction.
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public override ChargeTransactionFeesOutput ChargeTransactionFees(ChargeTransactionFeesInput input)
        {
            AssertTransactionGeneratedByPlugin();
            Assert(input.MethodName != null && input.ContractAddress != null, "Invalid charge transaction fees input.");

            // Primary token not created yet.
            if (State.ChainPrimaryTokenSymbol.Value == null)
            {
                return(new ChargeTransactionFeesOutput {
                    Success = true
                });
            }

            // Record tx fee bill during current charging process.
            var bill = new TransactionFeeBill();

            var fromAddress = Context.Sender;
            var methodFees  = Context.Call <MethodFees>(input.ContractAddress, nameof(GetMethodFee),
                                                        new StringValue {
                Value = input.MethodName
            });
            var successToChargeBaseFee = true;

            if (methodFees != null && methodFees.Fees.Any())
            {
                successToChargeBaseFee = ChargeBaseFee(GetBaseFeeDictionary(methodFees), ref bill);
            }

            var successToChargeSizeFee = true;

            if (!IsMethodFeeSetToZero(methodFees))
            {
                // Then also do not charge size fee.
                successToChargeSizeFee = ChargeSizeFee(input, ref bill);
            }

            // Update balances.
            foreach (var tokenToAmount in bill.FeesMap)
            {
                ModifyBalance(fromAddress, tokenToAmount.Key, -tokenToAmount.Value);
                Context.Fire(new TransactionFeeCharged
                {
                    Symbol = tokenToAmount.Key,
                    Amount = tokenToAmount.Value
                });
                if (tokenToAmount.Value == 0)
                {
                    Context.LogDebug(() => $"Maybe incorrect charged tx fee of {tokenToAmount.Key}: it's 0.");
                }
            }

            var chargingResult = successToChargeBaseFee && successToChargeSizeFee;
            var chargingOutput = new ChargeTransactionFeesOutput {
                Success = chargingResult
            };

            if (!chargingResult)
            {
                chargingOutput.ChargingInformation = "Transaction fee not enough.";
            }
            return(chargingOutput);
        }