示例#1
0
        private async Task UpdateWithTransactionDetails(SignalCircleTransfer transfer)
        {
            using var activity = MyTelemetry.StartActivity("Handle event SignalCircleTransfer");
            transfer.AddToActivityAsTag("circle-transfer");
            try
            {
                await using var context = new DatabaseContext(_dbContextOptionsBuilder.Options);
                var withdrawal = await context.Withdrawals
                                 .Where(e => e.Integration == "Circle" && e.ExternalSystemId == transfer.PaymentInfo.Id)
                                 .FirstOrDefaultAsync();

                if (withdrawal == null)
                {
                    _logger.LogError("Unable to find withdrawal with external  system id {withdrawalId}",
                                     transfer.PaymentInfo.Id);
                    return;
                }

                withdrawal.Txid = transfer.PaymentInfo.TransactionHash;
                await context.UpdateAsync(withdrawal);
            }
            catch (Exception)
            {
                _logger.LogError("Unable to update withdrawal details {withdrawalId}",
                                 transfer.PaymentInfo.Id);
            }
        }
示例#2
0
 private async ValueTask HandleSignal(SignalCircleTransfer signal)
 {
     using var activity = MyTelemetry.StartActivity("Handle Event SignalBitGoTransfer");
     try
     {
         await _service.HandledDepositAsync(signal);
     }
     catch (Exception ex)
     {
         ex.FailActivity();
         throw;
     }
 }
示例#3
0
        private async ValueTask HandleSignal(SignalCircleTransfer signal)
        {
            using var activity = MyTelemetry.StartActivity("Handle event SignalCircleTransfer");
            signal.AddToActivityAsJsonTag("circle-signal");

            _logger.LogInformation("SignalCircleTransfer is received: {jsonText}", JsonConvert.SerializeObject(signal));

            if (signal.PaymentInfo == null)
            {
                _logger.LogError("Cannot handle Circle signal, transfer do not found {jsonText}",
                                 JsonConvert.SerializeObject(signal));
                Activity.Current?.SetStatus(Status.Error);
                return;
            }

            if (signal.PaymentInfo.Source.Type != "wallet")
            {
                _logger.LogInformation("Skip non-withdrawal transfer {type}", signal.PaymentInfo.Source.Type);
                return;
            }

            if (signal.PaymentInfo.Status == PaymentStatus.Pending)
            {
                await UpdateWithTransactionDetails(signal);

                return;
            }

            if (signal.PaymentInfo.Status != PaymentStatus.Complete)
            {
                _logger.LogInformation("SignalCircleTransfer is not completed, skipping");
                return;
            }

            await HandleTransactionFee(signal);

            _logger.LogInformation("SignalCircleTransfer is handled");
        }
        public async Task HandledDepositAsync(SignalCircleTransfer transfer)
        {
            transfer.AddToActivityAsJsonTag("circle-transfer");

            _logger.LogInformation("Request to handle transfer from Circle: {transferJson}",
                                   JsonConvert.SerializeObject(transfer));

            if (transfer.PaymentInfo.Source.Type != "blockchain" && transfer.PaymentInfo.Source.Type != "card")
            {
                _logger.LogInformation("Skip withdrawal transfer {type}", transfer.PaymentInfo.Source.Type);
                return;
            }

            transfer.BrokerId.AddToActivityAsTag("brokerId");
            transfer.WalletId.AddToActivityAsTag("walletId");
            transfer.ClientId.AddToActivityAsTag("clientId");

            var    circleAsset = _circleAssetMapper.CircleAssetToAsset(transfer.BrokerId, transfer.PaymentInfo.Amount.Currency);
            string asset       = null;

            if (transfer.PaymentInfo.Source.Type == "card")
            {
                asset = circleAsset?.AssetSymbol;
            }
            else if (transfer.PaymentInfo.Source.Type == "blockchain")
            {
                asset = circleAsset?.AssetTokenSymbol;
            }

            if (string.IsNullOrEmpty(asset))
            {
                _logger.LogError("Unknown circle asset {asset}", transfer.PaymentInfo.Amount.Currency);
                return;
            }

            var accuracy = _assetsDictionary.GetAssetById(new AssetIdentity
            {
                Symbol   = asset,
                BrokerId = transfer.BrokerId
            }).Accuracy;

            var amount    = (decimal)double.Parse(transfer.PaymentInfo.Amount.Amount);
            var feeAmount = (decimal)double.Parse(string.IsNullOrEmpty(transfer.PaymentInfo?.Fees?.Amount)
                ? "0.0"
                : transfer.PaymentInfo.Fees.Amount);

            var roundedAmount = Math.Round(amount - feeAmount, accuracy, MidpointRounding.ToNegativeInfinity);

            var isBlockchain     = transfer.PaymentInfo.Source.Type == "blockchain";
            var circleBlockchain =
                _circleBlockchainMapper.BlockchainToCircleBlockchain(transfer.BrokerId,
                                                                     transfer.PaymentInfo.Source.Chain);

            try
            {
                await using var ctx = DatabaseContext.Create(_dbContextOptionsBuilder);
                var existingEntity =
                    await ctx.Deposits.Where(e => e.TransactionId == transfer.PaymentInfo.Id).FirstOrDefaultAsync();

                if (existingEntity == null)
                {
                    _logger.LogInformation("Got unknown deposit from circle, adding entry");
                    existingEntity = new DepositEntity
                    {
                        BrokerId      = transfer.BrokerId,
                        WalletId      = transfer.WalletId,
                        ClientId      = transfer.ClientId,
                        TransactionId = transfer.PaymentInfo.Id,
                        Amount        = (decimal)roundedAmount,
                        AssetSymbol   = asset,
                        Comment       = isBlockchain
                            ? $"Circle blockchain {transfer.PaymentInfo.Source.Chain} deposit [{asset}]"
                            :
                                        $"Circle card deposit [{asset}:{transfer.WalletId}, card: {transfer.PaymentInfo.Source.Id}]",
                        Integration    = isBlockchain ? "CircleBlockchain" : "CircleCard",
                        Txid           = isBlockchain ? transfer.PaymentInfo.TransactionHash : transfer.PaymentInfo.Id,
                        Status         = ConvertExternalStatus(transfer.PaymentInfo.Status),
                        EventDate      = DateTime.UtcNow,
                        UpdateDate     = DateTime.UtcNow,
                        FeeAmount      = feeAmount,
                        FeeAssetSymbol = string.IsNullOrEmpty(transfer.PaymentInfo?.Fees?.Currency)
                            ? transfer.PaymentInfo.Amount.Currency
                            : transfer.PaymentInfo.Fees.Currency,
                        Network = circleBlockchain != null
                            ? circleBlockchain.Blockchain
                            : transfer.PaymentInfo.Source.Chain,
                        MatchingEngineId = "Deposit:" + Guid.NewGuid(),
                    };
                }
                else
                {
                    existingEntity.Amount         = roundedAmount;
                    existingEntity.UpdateDate     = DateTime.UtcNow;
                    existingEntity.FeeAmount      = feeAmount;
                    existingEntity.FeeAssetSymbol = string.IsNullOrEmpty(transfer.PaymentInfo?.Fees?.Currency)
                        ? transfer.PaymentInfo.Amount.Currency
                        : transfer.PaymentInfo.Fees.Currency;
                    existingEntity.Status = ConvertExternalStatus(transfer.PaymentInfo.Status);
                }

                await ctx.UpsertAsync(existingEntity);

                await _depositPublisher.PublishAsync(new Deposit(existingEntity));
            }
            catch (Exception)
            {
                _logger.LogError("Unable to update deposit entry");
                throw;
            }

            _logger.LogInformation("Deposit request from Circle {transferIdString} is handled",
                                   transfer.PaymentInfo.Id);
        }
示例#5
0
        private async Task HandleTransactionFee(SignalCircleTransfer transfer)
        {
            using var activity = MyTelemetry.StartActivity("Handle event SignalCircleTransfer");
            transfer.AddToActivityAsTag("circle-transfer");
            try
            {
                await using var context = new DatabaseContext(_dbContextOptionsBuilder.Options);
                var withdrawal = await context.Withdrawals
                                 .Where(e => e.Integration == "Circle" && e.ExternalSystemId == transfer.PaymentInfo.Id)
                                 .FirstOrDefaultAsync();

                if (withdrawal == null)
                {
                    _logger.LogError("Unable to find withdrawal with external  system id {withdrawalId}",
                                     transfer.PaymentInfo.Id);
                    return;
                }

                withdrawal.Status = Domain.Models.WithdrawalStatus.Success;
                withdrawal.Txid   = transfer.PaymentInfo.TransactionHash;

                if (!string.IsNullOrEmpty(transfer.PaymentInfo?.Fees?.Currency))
                {
                    var asset = _circleAssetMapper.CircleAssetToAsset(withdrawal.BrokerId,
                                                                      transfer.PaymentInfo.Fees.Currency);
                    if (asset == null)
                    {
                        _logger.LogError("Unknown fees asset: {asset}", transfer.PaymentInfo.Fees.Currency);
                        await context.UpdateAsync(withdrawal);

                        return;
                    }

                    var feeStr = transfer.PaymentInfo.Fees.Amount;

                    if (!decimal.TryParse(feeStr, out var fee))
                    {
                        _logger.LogError(
                            "Cannot read fee from circle transaction. FeeString {feeString}, coin: {coin}, id: {id}",
                            feeStr, asset.AssetTokenSymbol, transfer.PaymentInfo.Id);
                        activity?.SetStatus(Status.Error);
                        await context.UpdateAsync(withdrawal);

                        return;
                    }

                    withdrawal.WalletId.AddToActivityAsTag("walletId");

                    var request = new BlockchainFeeApplyGrpcRequest()
                    {
                        WalletId      = withdrawal.WalletId,
                        BrokerId      = withdrawal.BrokerId,
                        TransactionId = transfer.PaymentInfo.TransactionHash,
                        AssetSymbol   = asset.AssetSymbol,
                        FeeAmount     = fee
                    };

                    request.AddToActivityAsJsonTag("fee-apply-request");
                    var result = await _changeBalanceService.BlockchainFeeApplyAsync(request);

                    if (result.ErrorCode != ChangeBalanceGrpcResponse.ErrorCodeEnum.Ok || !result.Result)
                    {
                        _logger.LogError("Cannot apply fee. Request: {requestText}. Error: {jsonText}",
                                         JsonConvert.SerializeObject(request), JsonConvert.SerializeObject(result));
                        activity?.SetStatus(Status.Error);
                    }
                    else
                    {
                        _logger.LogInformation("Success apply fee. Request: {requestText}",
                                               JsonConvert.SerializeObject(request));
                    }

                    withdrawal.ActualFee            = fee;
                    withdrawal.ActualFeeAssetSymbol = asset.AssetTokenSymbol;
                }

                await context.UpdateAsync(withdrawal);
            }
            catch (Exception)
            {
                _logger.LogError("Unable to record actual tx fee for withdrawal with id {withdrawalId}",
                                 transfer.PaymentInfo.Id);
            }
        }