Exemple #1
0
        public void TestNoteGenerationAndExtraction()
        {
            var withdrawalId = 101l;
            var service      = new FireblocksWithdrawalNoteService();

            var note     = service.GenerateManualNote(withdrawalId);
            var actualId = service.GetWithdrawalIdFromNote(note);

            Assert.AreEqual(withdrawalId, actualId);
        }
Exemple #2
0
        private async ValueTask HandleSignal(FireblocksWithdrawalSignal signal)
        {
            long withdrawalId = -1;

            using var activity = MyTelemetry.StartActivity("Handle event FireblocksWithdrawalSignal");
            signal.AddToActivityAsJsonTag("fireblocks-withdrawal-signal");

            var logContext = JsonConvert.SerializeObject(signal);

            _logger.LogInformation("FireblocksWithdrawalSignal is received: {jsonText}", logContext);
            var containsExternalId    = signal.ExternalId != null && signal.ExternalId.Contains("fire_tx_");
            var containsNote          = !string.IsNullOrEmpty(signal.InternalNote);
            var withdrawalIdExtracted = false;
            var idIsFromNote          = false;

            if (!containsExternalId && !containsNote)
            {
                _logger.LogInformation("FireblocksWithdrawalSignal does not contain fire_tx_ nor InternalNote : {jsonText}", logContext);
                return;
            }

            if (containsExternalId)
            {
                var step1 = signal.ExternalId.Replace("fire_tx_", "");
                var ids   = step1.Split('_', StringSplitOptions.RemoveEmptyEntries);

                if (!long.TryParse(ids[0], out withdrawalId))
                {
                    _logger.LogError("Can't parse external id tx {@context}", signal);
                    return;
                }

                withdrawalIdExtracted = true;
            }

            if (containsNote)
            {
                var extractedId = _fireblocksWithdrawalNoteService.GetWithdrawalIdFromNote(signal.InternalNote);

                if (extractedId != null)
                {
                    withdrawalIdExtracted = true;
                    withdrawalId          = extractedId.Value;
                    idIsFromNote          = true;
                }
            }

            if (!withdrawalIdExtracted)
            {
                _logger.LogError("Unable to extract withdrawal id {context}", logContext);
                return;
            }

            try
            {
                await using var context = new DatabaseContext(_dbContextOptionsBuilder.Options);
                var withdrawal = await context.Withdrawals
                                 .Where(e => e.Id == withdrawalId)
                                 .FirstOrDefaultAsync();

                if (withdrawal != null)
                {
                    if (signal.Status == Fireblocks.Webhook.Domain.Models.Withdrawals.FireblocksWithdrawalStatus.Completed)
                    {
                        withdrawal.ActualFee            = signal.FeeAmount;
                        withdrawal.ActualFeeAssetSymbol = signal.AssetSymbol;
                        withdrawal.Txid = signal.TransactionId;

                        if (idIsFromNote &&
                            (withdrawal.Amount != signal.Amount ||
                             withdrawal.ToAddress != signal.DestinationAddress ||
                             (withdrawal.ToAddress == signal.DestinationAddress &&
                              (!string.IsNullOrEmpty(withdrawal.ToTag) &&
                               withdrawal.ToTag != signal.DestinationTag))))
                        {
                            withdrawal.WorkflowState = WithdrawalWorkflowState.Failed;
                            _logger.LogError("FIREBLOCKS MANUAL WITHDRAWAL WAS WRONG! ID {withdrawalId}; Signal: {context}",
                                             withdrawal.Id,
                                             logContext);
                        }
                        else
                        {
                            withdrawal.Status = Domain.Models.WithdrawalStatus.Success;
                        }
                    }

                    if (signal.Status == Fireblocks.Webhook.Domain.Models.Withdrawals.FireblocksWithdrawalStatus.Failed)
                    {
                        _logger.LogError("Fireblocks transaction failed: {@context}", new
                        {
                            Signal = signal
                        });
                        await _cryptoWithdrawalService.RefundWithdrawalInMeAsync(withdrawal);
                    }

                    await context.UpdateAsync(withdrawal);

                    if (withdrawal.WorkflowState == WithdrawalWorkflowState.OK)
                    {
                        await PublishSuccess(withdrawal);
                    }
                    else
                    {
                        await Publish(withdrawal);
                    }
                }
                else
                {
                    _logger.LogError("Unable to record actual tx fee for withdrawal with id {withdrawalId}; {logContext}",
                                     withdrawalId, logContext);
                }
            }
            catch (Exception e)
            {
                _logger.LogError(e, "Unable to record actual tx fee for withdrawal with id {withdrawalId}; {logContext}",
                                 withdrawalId, logContext);
            }
        }