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); }
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); }
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())); }
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); }
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)); }
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 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 <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); }
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 decimal StorageFeeByTypeAsync( BlockchainTransactionType type) { var nyx = NYX; if (type.HasFlag(BlockchainTransactionType.TokenApprove)) { return(nyx.ApproveStorageLimit); } if (type.HasFlag(BlockchainTransactionType.SwapPayment)) { return((nyx.ApproveStorageLimit + nyx.InitiateStorageLimit) / nyx.StorageFeeMultiplier); } if (type.HasFlag(BlockchainTransactionType.SwapRefund)) { return((nyx.RefundStorageLimit - nyx.ActivationStorage) / nyx.StorageFeeMultiplier); } if (type.HasFlag(BlockchainTransactionType.SwapRedeem)) { return((nyx.RedeemStorageLimit - nyx.ActivationStorage) / nyx.StorageFeeMultiplier); } return((nyx.TransferStorageLimit - nyx.ActivationStorage) / nyx.StorageFeeMultiplier); }
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()); }
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 xtz = Xtz; 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(0m, 0m, 0m); } // minimum balance first unspentAddresses = unspentAddresses .ToList() .SortList(new AvailableBalanceAscending()); // use only max balance for swap payment if (type.HasFlag(BlockchainTransactionType.SwapPayment)) { unspentAddresses.RemoveRange(0, unspentAddresses.Count - 1); } var amount = 0m; var feeAmount = 0m; var reserveFee = ReserveFee(); foreach (var address in unspentAddresses) { var xtzAddress = await DataRepository .GetWalletAddressAsync(xtz.Name, address.Address) .ConfigureAwait(false); if (xtzAddress == null) { continue; } var availableBalanceInTez = xtzAddress.AvailableBalance(); var feeInTez = await FeeByType( type : type, from : address.Address, cancellationToken : cancellationToken) .ConfigureAwait(false); var storageFeeInTez = StorageFeeByTypeAsync( type: type); availableBalanceInTez = availableBalanceInTez - feeInTez - storageFeeInTez - ((reserve && address == unspentAddresses.Last()) ? reserveFee : 0) - xtz.MicroTezReserve.ToTez(); if (availableBalanceInTez < 0) { continue; } amount += address.AvailableBalance(); feeAmount += fee == 0 ? feeInTez : availableBalanceInTez + feeInTez; } return(amount, feeAmount, reserveFee); }
public override async Task <(decimal, decimal, decimal)> EstimateMaxAmountToSendAsync( string to, BlockchainTransactionType type, decimal feePerTx = 0, decimal feePrice = 0, bool reserve = false, CancellationToken cancellationToken = default) { var eth = Eth; 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(0m, 0m, 0m); } // minimum balance first unspentAddresses = unspentAddresses .ToList() .SortList(new AvailableBalanceAscending()); var isFirstTx = true; var amount = 0m; var fee = 0m; var reserveFeeInEth = ReserveFee(); foreach (var address in unspentAddresses) { var feeInEth = eth.GetFeeAmount(feePerTx == 0 ? GasLimitByType(type, isFirstTx) : feePerTx, feePrice == 0 ? eth.GasPriceInGwei : feePrice); var usedAmountInEth = Math.Max(address.AvailableBalance() - feeInEth - (reserve && address == unspentAddresses.Last() ? reserveFeeInEth : 0), 0); if (usedAmountInEth <= 0) { continue; } amount += usedAmountInEth; fee += feeInEth; if (isFirstTx) { isFirstTx = false; } } return(amount, fee, 0m); }
public override async Task <(decimal, decimal, decimal)> EstimateMaxAmountToSendAsync( string to, BlockchainTransactionType type, decimal feeAmount = 0, decimal feePrice = 0, bool reserve = false, 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(0m, 0m, 0m); } // minimum balance first unspentAddresses = unspentAddresses .ToList() .SortList(new AvailableBalanceAscending()); var isFirstTx = true; var amount = 0m; var fee = 0m; var reserveFee = ReserveFee(); foreach (var address in unspentAddresses) { var feeInTez = await FeeByType( type : type, from : address.Address, isFirstTx : isFirstTx, cancellationToken : cancellationToken) .ConfigureAwait(false); var storageFeeInTez = await StorageFeeByTypeAsync( type : type, to : to, isFirstTx : isFirstTx, cancellationToken : cancellationToken) .ConfigureAwait(false); var usedAmountInTez = Math.Max(address.AvailableBalance() - feeInTez - storageFeeInTez - (reserve && address == unspentAddresses.Last() ? reserveFee : 0) - Xtz.MicroTezReserve.ToTez(), 0); if (usedAmountInTez <= 0) { continue; } amount += usedAmountInTez; fee += feeInTez; if (isFirstTx) { isFirstTx = false; } } return(amount, fee, reserveFee); }