static void Main(string[] args) { try { var enumeratorService = new TransactionEnumeratorService(); var feeCalculator = new TransactionFeeCalculatorService(); var feeSender = new TransactionFeeOutputService(); using (var transactions = enumeratorService.GetEnumerator()) { while (transactions.MoveNext()) { var current = transactions.Current; TransactionFee fee = null; if (!current.IsEmpty) { fee = feeCalculator.CalculateFee(current); } feeSender.Send(fee); } } } catch (FileNotFoundException) { Console.WriteLine("The 'transactions.txt' file is missing"); } catch (Exception ex) { Console.WriteLine($"Something went wrong: {ex.Message}"); } Console.ReadLine(); }
public void ContractAggregate_DisableTransactionFee_TransactionFeeIsDisabled(CalculationType calculationType, FeeType feeType) { ContractAggregate aggregate = ContractAggregate.Create(TestData.ContractId); aggregate.Create(TestData.EstateId, TestData.OperatorId, TestData.ContractDescription); aggregate.AddVariableValueProduct(TestData.ProductId, TestData.ProductName, TestData.ProductDisplayText); List <Product> products = aggregate.GetProducts(); Product product = products.Single(); aggregate.AddTransactionFee(product, TestData.TransactionFeeId, TestData.TransactionFeeDescription, calculationType, feeType, TestData.TransactionFeeValue); List <Product> productsAfterFeeAdded = aggregate.GetProducts(); Product productWithFees = productsAfterFeeAdded.Single(); productWithFees.TransactionFees.ShouldHaveSingleItem(); TransactionFee fee = productWithFees.TransactionFees.Single(); fee.IsEnabled.ShouldBeTrue(); aggregate.DisableTransactionFee(TestData.ProductId, TestData.TransactionFeeId); productsAfterFeeAdded = aggregate.GetProducts(); productWithFees = productsAfterFeeAdded.Single(); productWithFees.TransactionFees.ShouldHaveSingleItem(); fee = productWithFees.TransactionFees.Single(); fee.IsEnabled.ShouldBeFalse(); }
private async Task <bool> ExecutePluginOnPreTransactionStageAsync(IExecutive executive, ITransactionContext txContext, Timestamp currentBlockTime, IChainContext internalChainContext, TieredStateCache internalStateCache, CancellationToken cancellationToken) { var trace = txContext.Trace; foreach (var plugin in _prePlugins) { var transactions = await plugin.GetPreTransactionsAsync(executive.Descriptors, txContext); foreach (var preTx in transactions) { var singleTxExecutingDto = new SingleTransactionExecutingDto { Depth = 0, ChainContext = internalChainContext, Transaction = preTx, CurrentBlockTime = currentBlockTime }; var preTrace = await ExecuteOneAsync(singleTxExecutingDto, cancellationToken); if (preTrace == null) { return(false); } trace.PreTransactions.Add(preTx); trace.PreTraces.Add(preTrace); if (preTx.MethodName == "ChargeTransactionFees") { var txFee = new TransactionFee(); txFee.MergeFrom(preTrace.ReturnValue); trace.TransactionFee = txFee; } if (!preTrace.IsSuccessful()) { return(false); } var stateSets = preTrace.GetStateSets().ToList(); internalStateCache.Update(stateSets); var parentStateCache = txContext.StateCache as TieredStateCache; parentStateCache?.Update(stateSets); if (trace.TransactionFee == null || !trace.TransactionFee.IsFailedToCharge) { continue; } preTrace.ExecutionStatus = ExecutionStatus.Executed; return(false); } } return(true); }
private void PlayEvent(TransactionFeeForProductDisabledEvent domainEvent) { // Find the product Product product = this.Products.Single(p => p.ProductId == domainEvent.ProductId); TransactionFee transactionFee = product.TransactionFees.Single(t => t.TransactionFeeId == domainEvent.TransactionFeeId); transactionFee.IsEnabled = false; }
public ActionResult Create(TransactionFeeClientFeeGroup transactionFeeClientFeeGroup) { //Check Access Rights to Domain if (!hierarchyRepository.AdminHasDomainWriteAccess(groupName)) { ViewData["Message"] = "You do not have access to this item"; return(View("Error")); } //Get ClientFeeGroup ClientFeeGroup clientFeeGroup = new ClientFeeGroup(); clientFeeGroup = clientFeeGroupRepository.GetGroup(transactionFeeClientFeeGroup.ClientFeeGroupId); //Check Exists if (clientFeeGroup == null) { ViewData["ActionMethod"] = "CreateGet1"; return(View("RecordDoesNotExistError")); } //Get TransactionFee TransactionFee transactionFee = new TransactionFee(); int productId = transactionFeeClientFeeGroup.ProductId; if (productId == 1) { transactionFee = transactionFeeAirRepository.GetItem(transactionFeeClientFeeGroup.TransactionFeeId); } else { transactionFee = transactionFeeCarHotelRepository.GetItem(transactionFeeClientFeeGroup.TransactionFeeId); } //Check Exists if (transactionFee == null) { ViewData["ActionMethod"] = "CreateGet2"; return(View("RecordDoesNotExistError")); } //Database Update try { transactionFeeClientFeeGroupRepository.Add(transactionFeeClientFeeGroup); } catch (SqlException ex) { LogRepository logRepository = new LogRepository(); logRepository.LogError(ex.Message); ViewData["Message"] = "There was a problem with your request, please see the log file or contact an administrator for details"; return(View("Error")); } return(RedirectToAction("List", new { id = transactionFeeClientFeeGroup.ClientFeeGroupId })); }
/// <summary> /// Updates the gas price estimate given the new GasPrice. /// </summary> /// <param name="gasPrice"> The new GasPrice to set. </param> private void UpdateGasPriceEstimate(GasPrice gasPrice) { TransactionGasPrice = gasPrice; string txFeeText = $"~ {TransactionFee.ToString().LimitEnd(14).TrimEnd('0')}<style=Symbol> ETH</style> | {currencyManager.GetCurrencyFormattedValue(tradableAssetPriceManager.GetPrice("ETH") * TransactionFee)}"; transactionFeeText.text = TransactionFee < lockPRPSPopup.EtherBalance ? txFeeText : "Not enough ETH"; transactionFeeText.color = transactionFeeText.text == "Not enough ETH" ? UIColors.Red : UIColors.White; }
public TransactionFeeVM(TransactionFee transactionFee, string name, bool fromGlobalFlag, string fromCode, bool toGlobalFlag, string toCode, bool routingViceVersaFlag) { TransactionFee = transactionFee; Name = name; FromGlobalFlag = fromGlobalFlag; FromCode = fromCode; ToGlobalFlag = toGlobalFlag; ToCode = toCode; RoutingViceVersaFlag = routingViceVersaFlag; }
private bool IsFairFee(Transaction signedCloseTx, ulong theirFee) { var deviationMax = _configuration.ClosingFeeDeviationMaximumPercentage; ulong ourFeeRate = _blockchainClientService.GetFeeRatePerKw(3); var size = signedCloseTx.GetSerializedSize(2, SerializationType.Network); var fee = TransactionFee.CalculateFee(ourFeeRate, (ulong)size); return(theirFee < fee + fee * deviationMax && theirFee > fee - fee * deviationMax); }
public void Send(TransactionFee transactionFee) { if (transactionFee != null) { var output = TransactionFeeConvertor.Convert(transactionFee); Console.WriteLine(output); } else { Console.WriteLine(""); } }
public ClientFeeGroupTransactionFeeVM(IEnumerable <SelectListItem> transactionFees, int originalTransactionFeeId, IEnumerable <SelectListItem> products, TransactionFeeClientFeeGroup transactionFeeClientFeeGroup, ClientFeeGroup clientFeeGroup, TransactionFee transactionFee, int feeTypeId, string feeTypeDisplayName, string feeTypeDisplayNameShort, bool hasDomainWriteAccess) { TransactionFeeClientFeeGroup = transactionFeeClientFeeGroup; Products = products; ClientFeeGroup = clientFeeGroup; OriginalTransactionFeeId = originalTransactionFeeId; TransactionFees = transactionFees; TransactionFee = transactionFee; FeeTypeId = feeTypeId; FeeTypeDisplayName = feeTypeDisplayName; FeeTypeDisplayNameShort = feeTypeDisplayNameShort; HasDomainWriteAccess = hasDomainWriteAccess; }
public void TestShouldMatchPrice1() { var transaction = new Transaction { Amount = 120, MerchantName = "ABC", TransactionDate = DateTime.ParseExact("2019-01-01", "yyyy-MM-dd" , CultureInfo.InvariantCulture) }; var transactionFee = new TransactionFee(transaction, new BasicTransactionFeeCalculator()); Assert.AreEqual(transactionFee.GetFeeInfo(), "2019-01-01 ABC\t1.20"); }
private void TransactionFeeHandler(TransactionFee tf) { // вычисляем стоимость комиссии float local_fee = tf.Account.Position.PositionPrice * fee; // снимаем деньги tf.Account.GetFee((decimal)local_fee); // возвращаем аккаунт в ClosePosition Sender.Tell(new ClosePositionActor.AccountFromTransactionFee(tf.Account)); // сообщаем NotifyActor что очередная позиция обработана var selectNotifyActoer = Context.ActorSelection("/user/RequestResolverActor/AccountDeskActor/NotifyActor"); selectNotifyActoer.Tell(new NotifyActor.AccountCounterMessage()); }
// GET: Edit A TransactionFeeClientFeeGroup public ActionResult Edit(int cid, int tId) { //Get Item From Database TransactionFeeClientFeeGroup transactionFeeClientFeeGroup = new TransactionFeeClientFeeGroup(); transactionFeeClientFeeGroup = transactionFeeClientFeeGroupRepository.GetItem(cid, tId); //Check Exists if (transactionFeeClientFeeGroup == null) { ViewData["ActionMethod"] = "EditGet"; return(View("RecordDoesNotExistError")); } ClientFeeGroupTransactionFeeVM clientFeeGroupTransactionFeeVM = new ClientFeeGroupTransactionFeeVM(); clientFeeGroupTransactionFeeVM.TransactionFeeClientFeeGroup = transactionFeeClientFeeGroup; //Get ClientFeeGroup ClientFeeGroup clientFeeGroup = new ClientFeeGroup(); clientFeeGroup = clientFeeGroupRepository.GetGroup(cid); clientFeeGroupTransactionFeeVM.ClientFeeGroup = clientFeeGroup; clientFeeGroupTransactionFeeVM.OriginalTransactionFeeId = tId; TransactionFee transactionFee = new TransactionFee(); transactionFee = transactionFeeRepository.GetItem(tId); int productId = (int)transactionFee.ProductId; transactionFeeClientFeeGroup.ProductId = productId; SelectList transactionFeeList = new SelectList(transactionFeeClientFeeGroupRepository.GetUnUsedTransactionFees(cid, productId, transactionFee.TransactionFeeId).ToList(), "TransactionFeeId", "TransactionFeeDescription"); clientFeeGroupTransactionFeeVM.TransactionFees = transactionFeeList; ProductRepository productRepository = new ProductRepository(); SelectList productList = new SelectList(productRepository.GetTransactionFeeProducts().ToList(), "ProductId", "ProductName", productId); clientFeeGroupTransactionFeeVM.Products = productList; return(View(clientFeeGroupTransactionFeeVM)); }
/// <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); }
public FundingTransaction CreateFundingTransaction(ulong amount, ulong feeRate, ECKeyPair pubKey1, ECKeyPair pubKey2) { var script = MultiSignaturePubKey.GenerateMultisigPubKey(pubKey1, pubKey2); var redeemScript = script.WitHash.ScriptPubKey; var unspent = _blockchainClientService.ListUtxo(1, Int32.MaxValue, _walletService.PubKeyAddress); _logger.LogInformation("Transaction Funding: found " + unspent.Count + " UTXO to fund the funding transaction"); var totalAmount = (ulong)unspent.Select(u => u.AmountSatoshi).Sum(); var fee = TransactionFee.CalculateFee(feeRate, 1000); var neededAmount = fee + amount; if (neededAmount > totalAmount) { throw new FundingException($"Not enough funds. Needed at least {neededAmount} Satoshi, but only {totalAmount} Satoshi are available."); } (Transaction fundingTransaction, List <Coin> inputCoins) = CreateFundingTransactionFromUtxo(amount, unspent, script, feeRate); ushort fundingOutputIndex = (ushort)fundingTransaction.Outputs.FindIndex(o => o.ScriptPubKey == redeemScript); return(new FundingTransaction(fundingTransaction, fundingOutputIndex, inputCoins, _walletService.PubKeyAddress.ScriptPubKey.ToBytes())); }
private void RespondWithClosingSigned(IPeer peer, LocalChannel channel, ulong fee = 0) { var oldState = channel.State; ulong ourFeeRate = _blockchainClientService.GetFeeRatePerKw(3); channel.State = LocalChannelState.ClosingSigned; var builder = new CloseChannelTransactionBuilder(channel, _networkParameters); builder.FeeSatoshi = 0; var zeroFeeTx = builder.Build(); builder.FeeSatoshi = fee != 0 ? fee : TransactionFee.CalculateFee(ourFeeRate, (ulong)zeroFeeTx.GetSerializedSize(2, SerializationType.Network)); ClosingSignedMessage closingSignedMessage = new ClosingSignedMessage(); closingSignedMessage.FeeSatoshi = builder.FeeSatoshi; closingSignedMessage.ChannelId = channel.ChannelId.HexToByteArray(); closingSignedMessage.Signature = builder.Sign().ToRawSignature(); peer.Messaging.Send(closingSignedMessage); _channelLoggingService.LogStateUpdate(channel, oldState, "Respond with closing signed"); }
public void ContractAggregate_AddTransactionFee_FixedValueProduct_TransactionFeeAdded(CalculationType calculationType, FeeType feeType) { ContractAggregate aggregate = ContractAggregate.Create(TestData.ContractId); aggregate.Create(TestData.EstateId, TestData.OperatorId, TestData.ContractDescription); aggregate.AddFixedValueProduct(TestData.ProductId, TestData.ProductName, TestData.ProductDisplayText, TestData.ProductFixedValue); List <Product> products = aggregate.GetProducts(); Product product = products.Single(); aggregate.AddTransactionFee(product, TestData.TransactionFeeId, TestData.TransactionFeeDescription, calculationType, feeType, TestData.TransactionFeeValue); List <Product> productsAfterFeeAdded = aggregate.GetProducts(); Product productWithFees = productsAfterFeeAdded.Single(); productWithFees.TransactionFees.ShouldHaveSingleItem(); TransactionFee fee = productWithFees.TransactionFees.Single(); fee.Description.ShouldBe(TestData.TransactionFeeDescription); fee.TransactionFeeId.ShouldBe(TestData.TransactionFeeId); fee.CalculationType.ShouldBe(calculationType); fee.FeeType.ShouldBe(feeType); fee.Value.ShouldBe(TestData.TransactionFeeValue); }
private async Task <bool> ExecutePluginOnPreTransactionStageAsync(IExecutive executive, ITransactionContext txContext, Timestamp currentBlockTime, IChainContext internalChainContext, TieredStateCache internalStateCache, CancellationToken cancellationToken) { var trace = txContext.Trace; foreach (var plugin in _prePlugins) { var transactions = await plugin.GetPreTransactionsAsync(executive.Descriptors, txContext); foreach (var preTx in transactions) { TransactionTrace preTrace; try { var singleTxExecutingDto = new SingleTransactionExecutingDto { Depth = 0, ChainContext = internalChainContext, Transaction = preTx, CurrentBlockTime = currentBlockTime }; preTrace = await ExecuteOneAsync(singleTxExecutingDto, cancellationToken).WithCancellation(cancellationToken); } catch (OperationCanceledException) { Logger.LogTrace("execute transaction timeout"); return(false); } if (preTrace == null) { return(false); } trace.PreTransactions.Add(preTx); trace.PreTraces.Add(preTrace); if (preTx.MethodName == "ChargeTransactionFees") { var txFee = new TransactionFee(); txFee.MergeFrom(preTrace.ReturnValue); trace.TransactionFee = txFee; } if (!preTrace.IsSuccessful()) { trace.ExecutionStatus = IsTransactionCanceled(preTrace) ? ExecutionStatus.Canceled : ExecutionStatus.Prefailed; preTrace.SurfaceUpError(); trace.Error += preTrace.Error; return(false); } var stateSets = preTrace.GetStateSets().ToList(); internalStateCache.Update(stateSets); var parentStateCache = txContext.StateCache as TieredStateCache; parentStateCache?.Update(stateSets); } } return(true); }
/// <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); }
/// <summary> /// Returns true if CoinInfo instances are equal /// </summary> /// <param name="other">Instance of CoinInfo to be compared</param> /// <returns>Boolean</returns> public bool Equals(CoinInfo other) { if (ReferenceEquals(null, other)) { return(false); } if (ReferenceEquals(this, other)) { return(true); } return (( CoinType == other.CoinType || CoinType != null && CoinType.Equals(other.CoinType) ) && ( WalletName == other.WalletName || WalletName != null && WalletName.Equals(other.WalletName) ) && ( Name == other.Name || Name != null && Name.Equals(other.Name) ) && ( Symbol == other.Symbol || Symbol != null && Symbol.Equals(other.Symbol) ) && ( WalletSymbol == other.WalletSymbol || WalletSymbol != null && WalletSymbol.Equals(other.WalletSymbol) ) && ( WalletType == other.WalletType || WalletType != null && WalletType.Equals(other.WalletType) ) && ( TransactionFee == other.TransactionFee || TransactionFee != null && TransactionFee.Equals(other.TransactionFee) ) && ( Precision == other.Precision || Precision != null && Precision.Equals(other.Precision) ) && ( BackingCoinType == other.BackingCoinType || BackingCoinType != null && BackingCoinType.Equals(other.BackingCoinType) ) && ( SupportsOutputMemos == other.SupportsOutputMemos || SupportsOutputMemos != null && SupportsOutputMemos.Equals(other.SupportsOutputMemos) ) && ( Restricted == other.Restricted || Restricted != null && Restricted.Equals(other.Restricted) ) && ( Authorized == other.Authorized || Authorized != null && Authorized.Equals(other.Authorized) ) && ( NotAuthorizedReasons == other.NotAuthorizedReasons || NotAuthorizedReasons != null && NotAuthorizedReasons.SequenceEqual(other.NotAuthorizedReasons) )); }
/// <summary> /// Gets the hash code /// </summary> /// <returns>Hash code</returns> public override int GetHashCode() { unchecked // Overflow is fine, just wrap { var hashCode = 41; // Suitable nullity checks etc, of course :) if (CoinType != null) { hashCode = hashCode * 59 + CoinType.GetHashCode(); } if (WalletName != null) { hashCode = hashCode * 59 + WalletName.GetHashCode(); } if (Name != null) { hashCode = hashCode * 59 + Name.GetHashCode(); } if (Symbol != null) { hashCode = hashCode * 59 + Symbol.GetHashCode(); } if (WalletSymbol != null) { hashCode = hashCode * 59 + WalletSymbol.GetHashCode(); } if (WalletType != null) { hashCode = hashCode * 59 + WalletType.GetHashCode(); } if (TransactionFee != null) { hashCode = hashCode * 59 + TransactionFee.GetHashCode(); } if (Precision != null) { hashCode = hashCode * 59 + Precision.GetHashCode(); } if (BackingCoinType != null) { hashCode = hashCode * 59 + BackingCoinType.GetHashCode(); } if (SupportsOutputMemos != null) { hashCode = hashCode * 59 + SupportsOutputMemos.GetHashCode(); } if (Restricted != null) { hashCode = hashCode * 59 + Restricted.GetHashCode(); } if (Authorized != null) { hashCode = hashCode * 59 + Authorized.GetHashCode(); } if (NotAuthorizedReasons != null) { hashCode = hashCode * 59 + NotAuthorizedReasons.GetHashCode(); } return(hashCode); } }
public void Insert(int TxType,long TxKey,decimal Amount,int Currency,DateTime DateCreated,string Description,int? FeeType) { TransactionFee item = new TransactionFee(); item.TxType = TxType; item.TxKey = TxKey; item.Amount = Amount; item.Currency = Currency; item.DateCreated = DateCreated; item.Description = Description; item.FeeType = FeeType; item.Save(UserName); }
public static string Convert(TransactionFee transactionFee) { return ($"{transactionFee.Date:yyyy-MM-dd} {transactionFee.MerchantName} {transactionFee.Amount.ToString("##0.00", NumberFormat.Nfi)}"); }