protected virtual async Task <TezosTransaction> CreatePaymentTxAsync( Swap swap, int lockTimeSeconds, CancellationToken cancellationToken = default) { var xtzConfig = XtzConfig; Log.Debug("Create {@currency} payment transaction from address {@address} for swap {@swapId}", Currency, swap.FromAddress, swap.Id); var requiredAmountInMtz = AmountHelper .QtyToSellAmount(swap.Side, swap.Qty, swap.Price, xtzConfig.DigitsMultiplier) .ToMicroTez(); // maker network fee if (swap.MakerNetworkFee > 0) { var makerNetworkFeeInMtz = swap.MakerNetworkFee.ToMicroTez(); if (makerNetworkFeeInMtz < requiredAmountInMtz) // network fee size check { requiredAmountInMtz += makerNetworkFeeInMtz; } } var refundTimeStampUtcInSec = new DateTimeOffset(swap.TimeStamp.ToUniversalTime().AddSeconds(lockTimeSeconds)).ToUnixTimeSeconds(); var rewardForRedeemInMtz = swap.IsInitiator ? swap.PartyRewardForRedeem.ToMicroTez() : 0; var walletAddress = await _account .GetAddressAsync(swap.FromAddress, cancellationToken) .ConfigureAwait(false); Log.Debug("Available balance: {@balance}", walletAddress.Balance); var balanceInMtz = walletAddress.Balance.ToMicroTez(); var isRevealed = await _account .IsRevealedSourceAsync(walletAddress.Address, cancellationToken) .ConfigureAwait(false); var feeAmountInMtz = xtzConfig.InitiateFee + (isRevealed ? 0 : xtzConfig.RevealFee); var storageLimitInMtz = xtzConfig.InitiateStorageLimit * xtzConfig.StorageFeeMultiplier; if (balanceInMtz < feeAmountInMtz + storageLimitInMtz + requiredAmountInMtz) { Log.Error( "Insufficient funds at {@address}. Balance: {@balance}, required: {@required}, " + "feeAmount: {@feeAmount}, storageLimit: {@storageLimit}, missing: {@result}.", walletAddress.Address, balanceInMtz, requiredAmountInMtz, feeAmountInMtz, storageLimitInMtz, balanceInMtz - feeAmountInMtz - storageLimitInMtz - requiredAmountInMtz); return(null); } return(new TezosTransaction { Currency = xtzConfig.Name, CreationTime = DateTime.UtcNow, From = walletAddress.Address, To = xtzConfig.SwapContractAddress, Amount = Math.Round(requiredAmountInMtz, 0), Fee = feeAmountInMtz, GasLimit = xtzConfig.InitiateGasLimit, StorageLimit = xtzConfig.InitiateStorageLimit, Params = CreateInitParams(swap, refundTimeStampUtcInSec, (long) rewardForRedeemInMtz), Type = BlockchainTransactionType.Output | BlockchainTransactionType.SwapPayment, UseRun = true, UseSafeStorageLimit = true, UseOfflineCounter = true }); }
protected virtual async Task <IEnumerable <TezosTransaction> > CreatePaymentTxsAsync( Swap swap, int lockTimeSeconds, CancellationToken cancellationToken = default) { var xtz = Xtz; Log.Debug("Create payment transactions for swap {@swapId}", swap.Id); var requiredAmountInMtz = AmountHelper .QtyToAmount(swap.Side, swap.Qty, swap.Price, xtz.DigitsMultiplier) .ToMicroTez(); var refundTimeStampUtcInSec = new DateTimeOffset(swap.TimeStamp.ToUniversalTime().AddSeconds(lockTimeSeconds)).ToUnixTimeSeconds(); var isInitTx = true; var rewardForRedeemInMtz = swap.IsInitiator ? swap.PartyRewardForRedeem.ToMicroTez() : 0; var unspentAddresses = (await _account .GetUnspentAddressesAsync(cancellationToken) .ConfigureAwait(false)) .ToList() .SortList(new AvailableBalanceAscending()); var transactions = new List <TezosTransaction>(); foreach (var walletAddress in unspentAddresses) { Log.Debug("Create swap payment tx from address {@address} for swap {@swapId}", walletAddress.Address, swap.Id); var balanceInTz = (await _account .GetAddressBalanceAsync( address: walletAddress.Address, cancellationToken: cancellationToken) .ConfigureAwait(false)) .Available; Log.Debug("Available balance: {@balance}", balanceInTz); var balanceInMtz = balanceInTz.ToMicroTez(); var isRevealed = await _account .IsRevealedSourceAsync(walletAddress.Address, cancellationToken) .ConfigureAwait(false); var feeAmountInMtz = isInitTx ? xtz.InitiateFee + (isRevealed ? 0 : xtz.RevealFee) : xtz.AddFee + (isRevealed ? 0 : xtz.RevealFee); var storageLimitInMtz = isInitTx ? xtz.InitiateStorageLimit * xtz.StorageFeeMultiplier : xtz.AddStorageLimit * xtz.StorageFeeMultiplier; var amountInMtz = Math.Min(balanceInMtz - feeAmountInMtz - storageLimitInMtz, requiredAmountInMtz); if (amountInMtz <= 0) { Log.Warning( "Insufficient funds at {@address}. Balance: {@balance}, " + "feeAmount: {@feeAmount}, storageLimit: {@storageLimit}, result: {@result}.", walletAddress.Address, balanceInMtz, feeAmountInMtz, storageLimitInMtz, amountInMtz); continue; } requiredAmountInMtz -= amountInMtz; if (isInitTx) { transactions.Add(new TezosTransaction { Currency = xtz, CreationTime = DateTime.UtcNow, From = walletAddress.Address, To = xtz.SwapContractAddress, Amount = Math.Round(amountInMtz, 0), Fee = feeAmountInMtz, GasLimit = xtz.InitiateGasLimit, StorageLimit = xtz.InitiateStorageLimit, Params = InitParams(swap, refundTimeStampUtcInSec, (long)rewardForRedeemInMtz), UseDefaultFee = true, Type = BlockchainTransactionType.Output | BlockchainTransactionType.SwapPayment }); } else { transactions.Add(new TezosTransaction { Currency = xtz, CreationTime = DateTime.UtcNow, From = walletAddress.Address, To = xtz.SwapContractAddress, Amount = Math.Round(amountInMtz, 0), Fee = feeAmountInMtz, GasLimit = xtz.AddGasLimit, StorageLimit = xtz.AddStorageLimit, UseDefaultFee = true, Params = AddParams(swap), Type = BlockchainTransactionType.Output | BlockchainTransactionType.SwapPayment }); } if (isInitTx) { isInitTx = false; } if (requiredAmountInMtz == 0) { break; } } if (requiredAmountInMtz > 0) { Log.Warning("Insufficient funds (left {@requredAmount}).", requiredAmountInMtz); return(Enumerable.Empty <TezosTransaction>()); } return(transactions); }