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
            });
        }
Example #2
0
        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);
        }