private async Task <decimal> FeeByType( BlockchainTransactionType type, string from, bool isFirstTx, CancellationToken cancellationToken = default) { var xtz = Xtz; var isRevealed = await IsRevealedSourceAsync(from, cancellationToken) .ConfigureAwait(false); if (type.HasFlag(BlockchainTransactionType.SwapPayment) && isFirstTx) { return(xtz.InitiateFee.ToTez() + (isRevealed ? 0 : xtz.RevealFee.ToTez())); } if (type.HasFlag(BlockchainTransactionType.SwapPayment) && !isFirstTx) { return(xtz.AddFee.ToTez() + (isRevealed ? 0 : xtz.RevealFee.ToTez())); } if (type.HasFlag(BlockchainTransactionType.SwapRefund)) { return(xtz.RefundFee.ToTez() + (isRevealed ? 0 : xtz.RevealFee.ToTez())); } if (type.HasFlag(BlockchainTransactionType.SwapRedeem)) { return(xtz.RedeemFee.ToTez() + (isRevealed ? 0 : xtz.RevealFee.ToTez())); } return(xtz.Fee.ToTez() + (isRevealed ? 0 : xtz.RevealFee.ToTez())); }
private decimal StorageFeeByType(BlockchainTransactionType type) { var fa12 = Fa12Config; if (type.HasFlag(BlockchainTransactionType.TokenApprove)) { return(fa12.ApproveStorageLimit.ToTez()); } if (type.HasFlag(BlockchainTransactionType.SwapPayment)) { return(((fa12.ApproveStorageLimit * 2 + fa12.InitiateStorageLimit) * fa12.StorageFeeMultiplier).ToTez()); } if (type.HasFlag(BlockchainTransactionType.SwapRefund)) { return(((fa12.RefundStorageLimit - fa12.ActivationStorage) * fa12.StorageFeeMultiplier).ToTez()); } if (type.HasFlag(BlockchainTransactionType.SwapRedeem)) { return(((fa12.RedeemStorageLimit - fa12.ActivationStorage) * fa12.StorageFeeMultiplier).ToTez()); } return(((fa12.TransferStorageLimit - fa12.ActivationStorage) * fa12.StorageFeeMultiplier).ToTez()); }
private decimal GasLimitByType(BlockchainTransactionType type) { var erc20 = Erc20Config; if (type.HasFlag(BlockchainTransactionType.TokenApprove)) { return(erc20.ApproveGasLimit); } if (type.HasFlag(BlockchainTransactionType.SwapPayment)) // todo: recheck { return(erc20.ApproveGasLimit * 2 + erc20.InitiateWithRewardGasLimit); } if (type.HasFlag(BlockchainTransactionType.SwapRefund)) { return(erc20.RefundGasLimit); } if (type.HasFlag(BlockchainTransactionType.SwapRedeem)) { return(erc20.RedeemGasLimit); } return(erc20.TransferGasLimit); }
public abstract Task <(decimal, decimal, decimal)> EstimateMaxAmountToSendAsync( string to, BlockchainTransactionType type, decimal fee = 0, decimal feePrice = 0, bool reserve = false, CancellationToken cancellationToken = default);
private async Task <decimal> FeeByType( BlockchainTransactionType type, string from, CancellationToken cancellationToken = default) { var xtz = Config; var isRevealed = await IsRevealedSourceAsync(from, cancellationToken) .ConfigureAwait(false); var revealFeeInTez = !isRevealed ? xtz.RevealFee.ToTez() : 0; if (type.HasFlag(BlockchainTransactionType.SwapPayment)) { return(xtz.InitiateFee.ToTez() + revealFeeInTez); } if (type.HasFlag(BlockchainTransactionType.SwapRefund)) { return(xtz.RefundFee.ToTez() + revealFeeInTez); } if (type.HasFlag(BlockchainTransactionType.SwapRedeem)) { return(xtz.RedeemFee.ToTez() + revealFeeInTez); } return(xtz.Fee.ToTez() + revealFeeInTez); }
public abstract Task <decimal?> EstimateFeeAsync( string to, decimal amount, BlockchainTransactionType type, decimal fee = 0, decimal feePrice = 0, CancellationToken cancellationToken = default);
private decimal StorageFeeByTypeAsync( BlockchainTransactionType type, bool isFirstTx) { var fa12 = Fa12; if (type.HasFlag(BlockchainTransactionType.TokenApprove)) { return(fa12.ApproveStorageLimit); } if (type.HasFlag(BlockchainTransactionType.SwapPayment) && isFirstTx) { return((fa12.ApproveStorageLimit * 2 + fa12.InitiateStorageLimit) / fa12.StorageFeeMultiplier); } if (type.HasFlag(BlockchainTransactionType.SwapPayment) && !isFirstTx) { return((fa12.ApproveStorageLimit * 2 + fa12.AddStorageLimit) / fa12.StorageFeeMultiplier); } if (type.HasFlag(BlockchainTransactionType.SwapRefund)) { return((fa12.RefundStorageLimit - fa12.ActivationStorage) / fa12.StorageFeeMultiplier); } if (type.HasFlag(BlockchainTransactionType.SwapRedeem)) { return((fa12.RedeemStorageLimit - fa12.ActivationStorage) / fa12.StorageFeeMultiplier); } return((fa12.TransferStorageLimit - fa12.ActivationStorage) / fa12.StorageFeeMultiplier); }
private async Task <decimal> FeeByType( BlockchainTransactionType type, string from, CancellationToken cancellationToken = default) { var fa2 = FA2; var isRevealed = await IsRevealedSourceAsync(from, cancellationToken) .ConfigureAwait(false); if (type.HasFlag(BlockchainTransactionType.TokenApprove)) { return(fa2.ApproveFee.ToTez()); } if (type.HasFlag(BlockchainTransactionType.SwapPayment)) { return(fa2.ApproveFee.ToTez() + fa2.InitiateFee.ToTez() + (isRevealed ? 0 : fa2.RevealFee.ToTez())); } if (type.HasFlag(BlockchainTransactionType.SwapRefund)) { return(fa2.RefundFee.ToTez() + (isRevealed ? 0 : fa2.RevealFee.ToTez())); } if (type.HasFlag(BlockchainTransactionType.SwapRedeem)) { return(fa2.RedeemFee.ToTez() + (isRevealed ? 0 : fa2.RevealFee.ToTez())); } return(fa2.TransferFee.ToTez() + (isRevealed ? 0 : fa2.RevealFee.ToTez())); }
public override async Task <IEnumerable <WalletAddress> > GetUnspentAddressesAsync( string toAddress, decimal amount, decimal fee, decimal feePrice, FeeUsagePolicy feeUsagePolicy, AddressUsagePolicy addressUsagePolicy, BlockchainTransactionType transactionType, CancellationToken cancellationToken = default) { var unspentAddresses = (await DataRepository .GetUnspentAddressesAsync(Currency) .ConfigureAwait(false)) .ToList(); var selectedAddresses = (await SelectUnspentAddresses( from: unspentAddresses, amount: amount, fee: fee, feePrice: feePrice, feeUsagePolicy: feeUsagePolicy, addressUsagePolicy: addressUsagePolicy, transactionType: transactionType) .ConfigureAwait(false)) .ToList(); if (toAddress != null && selectedAddresses.Any(a => a.WalletAddress.Address == toAddress)) { selectedAddresses.RemoveAll(a => a.WalletAddress.Address != toAddress); } return(ResolvePublicKeys(selectedAddresses .Select(w => w.WalletAddress) .ToList())); }
public virtual Task <decimal> EstimateMaxFeeAsync( string to, decimal amount, BlockchainTransactionType type, CancellationToken cancellationToken = default) { return(Task.FromResult(0m)); }
public abstract Task <IEnumerable <WalletAddress> > GetUnspentAddressesAsync( string toAddress, decimal amount, decimal fee, decimal feePrice, FeeUsagePolicy feeUsagePolicy, AddressUsagePolicy addressUsagePolicy, BlockchainTransactionType transactionType, CancellationToken cancellationToken = default);
public async Task <decimal> EstimateFeeAsync( BlockchainTransactionType type, CancellationToken cancellationToken = default) { var gasPrice = Math.Floor(await EthConfig .GetGasPriceAsync(cancellationToken) .ConfigureAwait(false)); return(EthConfig.GetFeeAmount(GasLimitByType(type), gasPrice)); }
public Task <decimal> EstimateMaxFeeAsync( string currency, string to, decimal amount, BlockchainTransactionType type, CancellationToken cancellationToken = default) { return(GetCurrencyAccount(currency) .EstimateMaxFeeAsync(to, amount, type, cancellationToken)); }
public Task <(decimal, decimal, decimal)> EstimateMaxAmountToSendAsync( string currency, string to, BlockchainTransactionType type, decimal fee = 0, decimal feePrice = 0, bool reserve = false, CancellationToken cancellationToken = default) { return(GetCurrencyAccount(currency) .EstimateMaxAmountToSendAsync(to, type, fee, feePrice, reserve, cancellationToken)); }
public async Task <SelectedWalletAddress> CalculateFundsUsageAsync( string from, string to, decimal amount, decimal fee, FeeUsagePolicy feeUsagePolicy, BlockchainTransactionType transactionType, CancellationToken cancellationToken = default) { var xtz = Config; var fromAddress = await GetAddressAsync(from, cancellationToken) .ConfigureAwait(false); if (fromAddress == null) { return(null); // invalid address } var txFeeInTez = feeUsagePolicy == FeeUsagePolicy.EstimatedFee ? await FeeByType( type : transactionType, from : fromAddress.Address, cancellationToken : cancellationToken) .ConfigureAwait(false) : fee; var storageFeeInTez = await StorageFeeByTypeAsync( type : transactionType, to : to, cancellationToken : cancellationToken) .ConfigureAwait(false); var restBalanceInTez = fromAddress.AvailableBalance() - amount - txFeeInTez - storageFeeInTez - xtz.MicroTezReserve.ToTez(); if (restBalanceInTez < 0) { return(null); // insufficient funds } return(new SelectedWalletAddress { WalletAddress = fromAddress, UsedAmount = amount, UsedFee = txFeeInTez, UsedStorageFee = storageFeeInTez }); }
public async Task <decimal> EstimateFeeAsync( string from, BlockchainTransactionType type, CancellationToken cancellationToken = default) { var txFeeInTez = await FeeByType( type : type, from : from, cancellationToken : cancellationToken) .ConfigureAwait(false); var storageFeeInTez = StorageFeeByType(type); return(txFeeInTez + storageFeeInTez); }
public async Task <FundsUsageEstimation> EstimateMaxAmountAsync( string to, BlockchainTransactionType type, CancellationToken cancellationToken = default) { var currency = BtcBasedCurrency; var unspentOutputs = (await DataRepository .GetAvailableOutputsAsync(Currency, currency.OutputType(), currency.TransactionType) .ConfigureAwait(false)) .ToList(); if (!unspentOutputs.Any()) { return(null); } var availableAmountInSatoshi = unspentOutputs.Sum(o => o.Value); var estimatedSigSize = BitcoinBasedCurrency.EstimateSigSize(unspentOutputs); var testTx = currency .CreatePaymentTx( unspentOutputs: unspentOutputs, destinationAddress: currency.TestAddress(), changeAddress: currency.TestAddress(), amount: availableAmountInSatoshi, fee: 0, lockTime: DateTimeOffset.MinValue); // requiredFee = txSize * feeRate without dust, because all coins must be send to one address var requiredFeeInSatoshi = (long)((testTx.VirtualSize() + estimatedSigSize) * currency.FeeRate); var amount = currency.SatoshiToCoin(Math.Max(availableAmountInSatoshi - requiredFeeInSatoshi, 0)); var fee = currency.SatoshiToCoin(requiredFeeInSatoshi); return(new FundsUsageEstimation { TotalAmount = amount, TotalFee = fee, UsedAddresses = unspentOutputs.Select(o => new AddressUsageEstimation { Address = $"{o.TxId}:{o.Index}", Amount = currency.SatoshiToCoin(o.Value) }) }); }
public override async Task <decimal> EstimateMaxFeeAsync( string to, decimal amount, BlockchainTransactionType type, CancellationToken cancellationToken = default) { var eth = Eth; var erc20 = Erc20; var unspentAddresses = (await DataRepository .GetUnspentAddressesAsync(Currency) .ConfigureAwait(false)) .ToList(); if (!unspentAddresses.Any()) { return(0m); // insufficient funds } var selectedAddresses = (await SelectUnspentAddresses( from: unspentAddresses, amount: amount, fee: 0, feePrice: erc20.GasPriceInGwei, feeUsagePolicy: FeeUsagePolicy.EstimatedFee, addressUsagePolicy: AddressUsagePolicy.UseMaximumChainBalanceFirst, //todo: calc efficiency for UseMaximumBalanceFirst transactionType: type) .ConfigureAwait(false)) .ToList(); if (!selectedAddresses.Any()) { return(0m); // insufficient funds } decimal maxTxFee = 0m; foreach (var addr in selectedAddresses) { var ethAddress = await DataRepository .GetWalletAddressAsync(eth.Name, addr.WalletAddress.Address) .ConfigureAwait(false); maxTxFee = maxTxFee > 0 ? Math.Min(maxTxFee, ethAddress.AvailableBalance()) : ethAddress.AvailableBalance(); } return(maxTxFee * selectedAddresses.Count()); }
public static TransactionType GetType(BlockchainTransactionType type) { if (type.HasFlag(BlockchainTransactionType.SwapPayment)) { return(TransactionType.SwapPayment); } if (type.HasFlag(BlockchainTransactionType.SwapRedeem)) { return(TransactionType.SwapRedeem); } if (type.HasFlag(BlockchainTransactionType.SwapRefund)) { return(TransactionType.SwapRefund); } if (type.HasFlag(BlockchainTransactionType.SwapCall)) { return(TransactionType.SwapCall); } if (type.HasFlag(BlockchainTransactionType.TokenCall)) { return(TransactionType.TokenCall); } if (type.HasFlag(BlockchainTransactionType.TokenApprove)) { return(TransactionType.TokenApprove); } if (type.HasFlag(BlockchainTransactionType.Input) && type.HasFlag(BlockchainTransactionType.Output)) { return(TransactionType.Output); } if (type.HasFlag(BlockchainTransactionType.Input)) { return(TransactionType.Input); } return(TransactionType.Output); }
public override async Task <decimal?> EstimateFeeAsync( string to, decimal amount, BlockchainTransactionType type, decimal fee = 0, decimal feePrice = 0, CancellationToken cancellationToken = default) { var unspentAddresses = (await DataRepository .GetUnspentAddressesAsync(Currency) .ConfigureAwait(false)) .ToList(); if (!type.HasFlag(BlockchainTransactionType.SwapRedeem) && !type.HasFlag(BlockchainTransactionType.SwapRefund)) { unspentAddresses = unspentAddresses .Where(w => w.Address != to) .ToList(); } if (!unspentAddresses.Any()) { return(null); // insufficient funds } var selectedAddresses = (await SelectUnspentAddressesAsync( from: unspentAddresses, to: to, amount: amount, fee: fee, feePrice: 0, feeUsagePolicy: fee == 0 ? FeeUsagePolicy.EstimatedFee : FeeUsagePolicy.FeeForAllTransactions, addressUsagePolicy: AddressUsagePolicy.UseMinimalBalanceFirst, transactionType: type, cancellationToken: cancellationToken) .ConfigureAwait(false)) .ToList(); if (!selectedAddresses.Any()) { return(null); // insufficient funds } return(selectedAddresses.Sum(s => s.UsedFee)); }
public static string GetDescription( BlockchainTransactionType type, decimal amount, decimal netAmount, int amountDigits, string currencyCode) { if (type.HasFlag(BlockchainTransactionType.SwapPayment)) { return($"Swap payment {Math.Abs(amount).ToString("0." + new string('#', amountDigits))} {currencyCode}"); } else if (type.HasFlag(BlockchainTransactionType.SwapRefund)) { return($"Swap refund {Math.Abs(netAmount).ToString("0." + new string('#', amountDigits))} {currencyCode}"); } else if (type.HasFlag(BlockchainTransactionType.SwapRedeem)) { return($"Swap redeem {Math.Abs(netAmount).ToString("0." + new string('#', amountDigits))} {currencyCode}"); } else if (type.HasFlag(BlockchainTransactionType.TokenApprove)) { return($"Token approve"); } else if (type.HasFlag(BlockchainTransactionType.TokenCall)) { return($"Token call"); } else if (type.HasFlag(BlockchainTransactionType.SwapCall)) { return($"Token swap call"); } else if (amount <= 0) { return($"Sent {Math.Abs(netAmount).ToString("0." + new string('#', amountDigits))} {currencyCode}"); } else if (amount > 0) { return($"Received {Math.Abs(netAmount).ToString("0." + new string('#', amountDigits))} {currencyCode}"); } else { return("Unknown transaction"); } }
public override async Task <(decimal, decimal, decimal)> EstimateMaxAmountToSendAsync( string to, BlockchainTransactionType type, decimal fee = 0, decimal feePrice = 0, bool reserve = false, CancellationToken cancellationToken = default) { var currency = BtcBasedCurrency; var unspentOutputs = (await DataRepository .GetAvailableOutputsAsync(Currency, currency.OutputType(), currency.TransactionType) .ConfigureAwait(false)) .ToList(); if (!unspentOutputs.Any()) { return(0m, 0m, 0m); } var availableAmountInSatoshi = unspentOutputs.Sum(o => o.Value); var estimatedSigSize = BitcoinBasedCurrency.EstimateSigSize(unspentOutputs); var testTx = currency .CreatePaymentTx( unspentOutputs: unspentOutputs, destinationAddress: currency.TestAddress(), changeAddress: currency.TestAddress(), amount: availableAmountInSatoshi, fee: 0, lockTime: DateTimeOffset.MinValue); // requiredFee = txSize * feeRate without dust, because all coins must be send to one address var requiredFeeInSatoshi = (long)((testTx.VirtualSize() + estimatedSigSize) * currency.FeeRate); var amount = currency.SatoshiToCoin(Math.Max(availableAmountInSatoshi - requiredFeeInSatoshi, 0)); fee = currency.SatoshiToCoin(requiredFeeInSatoshi); return(amount, fee, 0m); }
private decimal GasLimitByType(BlockchainTransactionType type) { var eth = EthConfig; if (type.HasFlag(BlockchainTransactionType.SwapPayment)) { return(eth.InitiateWithRewardGasLimit); } if (type.HasFlag(BlockchainTransactionType.SwapRefund)) { return(eth.RefundGasLimit); } if (type.HasFlag(BlockchainTransactionType.SwapRedeem)) { return(eth.RedeemGasLimit); } return(eth.GasLimit); }
public Task <IEnumerable <WalletAddress> > GetUnspentAddressesAsync( string currency, string toAddress, decimal amount, decimal fee, decimal feePrice, FeeUsagePolicy feeUsagePolicy, AddressUsagePolicy addressUsagePolicy, BlockchainTransactionType transactionType, CancellationToken cancellationToken = default) { return(GetCurrencyAccount(currency) .GetUnspentAddressesAsync( toAddress: toAddress, amount: amount, fee: fee, feePrice: feePrice, feeUsagePolicy: feeUsagePolicy, addressUsagePolicy: addressUsagePolicy, transactionType: transactionType, cancellationToken: cancellationToken)); }
public override async Task <IEnumerable <WalletAddress> > GetUnspentAddressesAsync( string toAddress, decimal amount, decimal fee, decimal feePrice, FeeUsagePolicy feeUsagePolicy, AddressUsagePolicy addressUsagePolicy, BlockchainTransactionType transactionType, CancellationToken cancellationToken = default) { var unspentAddresses = (await DataRepository .GetUnspentAddressesAsync(Currency) .ConfigureAwait(false)) .ToList(); if (!transactionType.HasFlag(BlockchainTransactionType.SwapRedeem) && !transactionType.HasFlag(BlockchainTransactionType.SwapRefund)) { unspentAddresses = unspentAddresses .Where(w => w.Address != toAddress) .ToList(); } var selectedAddresses = await SelectUnspentAddressesAsync( from : unspentAddresses, to : toAddress, amount : amount, fee : fee, feePrice : 0, feeUsagePolicy : feeUsagePolicy, addressUsagePolicy : addressUsagePolicy, transactionType : transactionType, cancellationToken : cancellationToken) .ConfigureAwait(false); return(ResolvePublicKeys(selectedAddresses .Select(w => w.WalletAddress) .ToList())); }
private async Task <decimal> StorageFeeByTypeAsync( BlockchainTransactionType type, string to, bool isFirstTx, CancellationToken cancellationToken = default) { var xtz = Xtz; var isActive = to != null ? await IsAllocatedDestinationAsync(type, to, cancellationToken) .ConfigureAwait(false) : false; if (type.HasFlag(BlockchainTransactionType.SwapPayment) && isFirstTx) { return(xtz.InitiateStorageLimit / xtz.StorageFeeMultiplier); } if (type.HasFlag(BlockchainTransactionType.SwapPayment) && !isFirstTx) { return(xtz.AddStorageLimit / xtz.StorageFeeMultiplier); } if (type.HasFlag(BlockchainTransactionType.SwapRefund)) { return(isActive ? Math.Max((xtz.RefundStorageLimit - xtz.ActivationStorage) / xtz.StorageFeeMultiplier, 0) // without activation storage fee : xtz.RefundStorageLimit / xtz.StorageFeeMultiplier); } if (type.HasFlag(BlockchainTransactionType.SwapRedeem)) { return(isActive ? Math.Max((xtz.RedeemStorageLimit - xtz.ActivationStorage) / xtz.StorageFeeMultiplier, 0) // without activation storage fee : xtz.RedeemStorageLimit / xtz.StorageFeeMultiplier); } return(isActive || !isFirstTx ? Math.Max((xtz.StorageLimit - xtz.ActivationStorage) / xtz.StorageFeeMultiplier, 0) // without activation storage fee : xtz.StorageLimit / xtz.StorageFeeMultiplier); }
private decimal GasLimitByType(BlockchainTransactionType type, bool isFirstTx) { var eth = Eth; if (type.HasFlag(BlockchainTransactionType.SwapPayment) && isFirstTx) { return(eth.InitiateWithRewardGasLimit); } if (type.HasFlag(BlockchainTransactionType.SwapPayment) && !isFirstTx) { return(eth.AddGasLimit); } if (type.HasFlag(BlockchainTransactionType.SwapRefund)) { return(eth.RefundGasLimit); } if (type.HasFlag(BlockchainTransactionType.SwapRedeem)) { return(eth.RedeemGasLimit); } return(eth.GasLimit); }
public async Task <bool> IsAllocatedDestinationAsync( BlockchainTransactionType type, string to, CancellationToken cancellationToken = default) { if (to != null) { return(await _tezosAllocationChecker .IsAllocatedAsync(to, cancellationToken) .ConfigureAwait(false)); } if (type == BlockchainTransactionType.SwapRedeem) // || type == BlockchainTransactionType.SwapRefund) { return(false); } else if (type == BlockchainTransactionType.SwapRefund) { return(false); } return(false); }
private async Task <decimal> FeeByType( BlockchainTransactionType type, string from, CancellationToken cancellationToken = default) { var fa12 = Fa12Config; var isRevealed = from != null && await _tezosAccount .IsRevealedSourceAsync(from, cancellationToken) .ConfigureAwait(false); var revealFeeInTez = !isRevealed ? fa12.RevealFee.ToTez() : 0; if (type.HasFlag(BlockchainTransactionType.TokenApprove)) { return(fa12.ApproveFee.ToTez()); } if (type.HasFlag(BlockchainTransactionType.SwapPayment)) { return(fa12.ApproveFee.ToTez() * 2 + fa12.InitiateFee.ToTez() + revealFeeInTez); } if (type.HasFlag(BlockchainTransactionType.SwapRefund)) { return(fa12.RefundFee.ToTez() + revealFeeInTez); } if (type.HasFlag(BlockchainTransactionType.SwapRedeem)) { return(fa12.RedeemFee.ToTez() + revealFeeInTez); } return(fa12.TransferFee.ToTez() + revealFeeInTez); }
private async Task <decimal> StorageFeeByTypeAsync( BlockchainTransactionType type, string to, CancellationToken cancellationToken = default) { var xtz = Config; var isActive = await IsAllocatedDestinationAsync(to, cancellationToken) .ConfigureAwait(false); if (type.HasFlag(BlockchainTransactionType.SwapPayment)) { return((xtz.InitiateStorageLimit * xtz.StorageFeeMultiplier).ToTez()); } if (type.HasFlag(BlockchainTransactionType.SwapRefund)) { return((isActive ? Math.Max((xtz.RefundStorageLimit - xtz.ActivationStorage) * xtz.StorageFeeMultiplier, 0) // without activation storage fee : xtz.RefundStorageLimit *xtz.StorageFeeMultiplier) .ToTez()); } if (type.HasFlag(BlockchainTransactionType.SwapRedeem)) { return((isActive ? Math.Max((xtz.RedeemStorageLimit - xtz.ActivationStorage) * xtz.StorageFeeMultiplier, 0) // without activation storage fee : xtz.RedeemStorageLimit *xtz.StorageFeeMultiplier) .ToTez()); } return((isActive ? Math.Max((xtz.StorageLimit - xtz.ActivationStorage) * xtz.StorageFeeMultiplier, 0) // without activation storage fee : xtz.StorageLimit *xtz.StorageFeeMultiplier) .ToTez()); }