public override async Task <PricesResponse> GetPrices(PricesRequest request, ServerCallContext context)
        {
            var entities = _pricesReader.Get(PriceEntity.GetPk());

            List <PriceUpdate> result;

            if (entities.Any())
            {
                result = _mapper.Map <List <PriceUpdate> >(entities);
            }
            else
            {
                var marketData = await _marketDataClient.GetMarketDataAsync(new Empty());

                result = _mapper.Map <List <PriceUpdate> >(marketData.Items.ToList());
            }

            if (request.AssetPairIds.Any())
            {
                result = result.Where(x =>
                                      request.AssetPairIds.Contains(x.AssetPairId, StringComparer.InvariantCultureIgnoreCase))
                         .ToList();
            }

            var response = new PricesResponse();

            response.Payload.AddRange(result);

            return(response);
        }
        async Task <IWatchList> IWatchListsClient.GetCustomWatchListAsync(string clientId, string watchListId)
        {
            try
            {
                var data = _readerWatchListCustomNoSql.Get(WatchListCustomNoSql.GeneratePartitionKey(clientId), WatchListCustomNoSql.GenerateRowKey(watchListId));

                if (data != null)
                {
                    return(data);
                }
            }
            catch (Exception ex)
            {
                _log.Error(ex, $"Cannot read from MyNoSQL. Table: ${WatchListCustomNoSql.TableNameCustomWatchList}, PK: {WatchListCustomNoSql.GeneratePartitionKey(clientId)}", ex);
            }

            try
            {
                var result = await HttpClient.WatchListGetCustomAsync(watchListId, clientId);

                var data = FromWatchListResponse(result);
                return(data);
            }
            catch (Exception ex)
            {
                _log.Error(ex, $"Cannot read from API. Method: WatchListGetCustomAsync, clientId: {clientId}, watchListId: {watchListId}");
                throw;
            }
        }
Example #3
0
        public override Task <GetApprovalResponse> GetApprovalResults(GetApprovalResultsRequest request, ServerCallContext context)
        {
            var vaultId = context.GetVaultId();

            var list = _dataReader.Get()
                       .Where(e => e.VaultId == vaultId)
                       .Where(e => !e.IsOpen)
                       .ToList();

            var resp = new GetApprovalResponse();

            foreach (var entity in list)
            {
                var item = new GetApprovalResponse.Types.ApprovalResponse
                {
                    ValidatorId = entity.ValidatorId,
                    TransferSigningRequestId    = entity.TransferSigningRequestId,
                    ResolutionDocumentEncBase64 = entity.ResolutionDocumentEncBase64,
                    Signature = entity.ResolutionSignature
                };

                resp.Payload.Add(item);

                _logger.LogInformation("GetApprovalResults response. TransferSigningRequestId={TransferSigningRequestId}; VaultId={VaultId}; ValidatorId={ValidatorId}", item.TransferSigningRequestId, vaultId, item.ValidatorId);
            }

            return(Task.FromResult(resp));
        }
        public async Task <OperationUpdateList> GetOperationsHistory(GetOperationsRequest request)
        {
            var data = _reader
                       .Get(OperationHistoryNoSqlEntity.GeneratePartitionKey(request.WalletId, request.AssetId))
                       .Where(e => e.Entity.TimeStamp < request.LastDate)
                       .Take(request.BatchSize)
                       .Select(t => t.Entity)
                       .OrderByDescending(e => e.TimeStamp)
                       .ToList();

            var cacheSize = _reader
                            .Get(OperationHistoryNoSqlEntity.GeneratePartitionKey(request.WalletId, request.AssetId)).Count;

            if (data.Count == request.BatchSize || (cacheSize < _maxCachedEntities && cacheSize > 0))
            {
                return new OperationUpdateList()
                       {
                           OperationUpdates = data
                       }
            }
            ;

            return(await _operationHistoryService.GetBalanceUpdatesAsync(request));
        }
    }
        public long ConvertAmountToBitgo(string coin, double amount)
        {
            var coinSettings = _bitgoCoins.Get(BitgoCoinEntity.GeneratePartitionKey(), BitgoCoinEntity.GenerateRowKey(coin));

            if (coinSettings == null)
            {
                throw new System.Exception($"Do not found settings for bitgo coin {coin} in nosql table {BitgoCoinEntity.TableName}");
            }

            return(coinSettings.AmountToAbsoluteValue(amount));
        }
        public (string, string) AssetToBitgoCoinAndWallet(string brokerId, string assetSymbol)
        {
            var map = _assetMap.Get(BitgoAssetMapEntity.GeneratePartitionKey(brokerId), BitgoAssetMapEntity.GenerateRowKey(assetSymbol));

            if (map == null)
            {
                return(string.Empty, string.Empty);
            }

            return(map.BitgoCoin, map.BitgoWalletId);
        }
Example #7
0
        public IBitGoApi GetByUser(string brokerId, string userId, string coinId, string newApiKey)
        {
            var bitGoUser = _bitgoUserReader.Get(
                BitGoUserNoSqlEntity.GeneratePartitionKey(brokerId),
                BitGoUserNoSqlEntity.GenerateRowKey(BitGoUserNoSqlEntity.TechSignerId,
                                                    coinId)) ??
                            _bitgoUserReader.Get(
                BitGoUserNoSqlEntity.GeneratePartitionKey(brokerId),
                BitGoUserNoSqlEntity.GenerateRowKey(BitGoUserNoSqlEntity.TechSignerId,
                                                    BitGoUserNoSqlEntity.DefaultCoin));

            var apiKeyEnc = bitGoUser?.User?.ApiKey;

            if (string.IsNullOrEmpty(apiKeyEnc) && string.IsNullOrEmpty(newApiKey))
            {
                _logger.LogError("Tech account is not configured, id = {techSignerName}",
                                 BitGoUserNoSqlEntity.TechSignerId);
                return(null);
            }

            lock (_clients)
            {
                if (!string.IsNullOrEmpty(apiKeyEnc) && _clients.TryGetValue(apiKeyEnc, out var api))
                {
                    return(api);
                }
            }

            var coin = _bitgoCointReader.Get(BitgoCoinEntity.GeneratePartitionKey(),
                                             BitgoCoinEntity.GenerateRowKey(coinId));

            if (coin == null)
            {
                _logger.LogError("Cannot fond bitgo coin id = {symbol}", coinId);
                return(null);
            }

            var apiKey = string.IsNullOrEmpty(apiKeyEnc) ? newApiKey : _encryptionService.Decrypt(apiKeyEnc);

            var client = new BitGoClient(
                apiKey, Program.Settings.BitgoExpressUrlMainNet,
                apiKey, Program.Settings.BitgoExpressUrlTestNet);

            lock (_clients)
            {
                _clients[apiKey] = coin.IsMainNet ? client.MainNet : client.TestNet;
                return(_clients[apiKey]);
            }
        }
Example #8
0
        public override async Task <OrdersResponse> GetActiveOrders(OrdersRequest request, ServerCallContext context)
        {
            var result = await _validationService.ValidateOrdersRequestAsync(request.AssetPairId, request.Offset, request.Take);

            if (result != null)
            {
                return(new OrdersResponse
                {
                    Error = new Error
                    {
                        Code = (int)result.Code,
                        Message = result.Message
                    }
                });
            }

            var statuses = new List <string> {
                OrderStatus.Placed.ToString(), OrderStatus.PartiallyMatched.ToString()
            };

            var orders = _ordersReader.Get(context.GetHttpContext().User.GetWalletId(), request.Offset, request.Take,
                                           x => (string.IsNullOrEmpty(request.AssetPairId) || x.AssetPairId == request.AssetPairId) && statuses.Contains(x.Status));

            var res = new OrdersResponse();

            res.Payload.AddRange(_mapper.Map <List <Order> >(orders));
            return(res);
        }
Example #9
0
        public async Task <GetDepositAddressResponse> GetDepositAddressAsync(GetDepositAddressRequest request)
        {
            var entity = _depositAddressReader.Get(DepositAddressEntity.GeneratePartitionKey(request.WalletId),
                                                   DepositAddressEntity.GenerateRowKey(request.AssetSymbol, request.Blockchain));

            if (entity != null)
            {
                return(new GetDepositAddressResponse()
                {
                    Address = entity.Address,
                    Error = GetDepositAddressResponse.ErrorCode.Ok
                });
            }

            return(await _service.GetDepositAddressAsync(request));
        }
Example #10
0
        public async Task <IActionResult> GetTickers([FromQuery] string[] assetPairIds)
        {
            var entities = _tickersReader.Get(TickerEntity.GetPk());

            List <TickerModel> result;

            if (entities.Any())
            {
                result = _mapper.Map <List <TickerModel> >(entities);
            }
            else
            {
                var marketData = await _marketDataClient.GetMarketDataAsync(new Empty());

                result = _mapper.Map <List <TickerModel> >(marketData.Items.ToList());
            }

            if (assetPairIds.Any())
            {
                result = result.Where(x =>
                                      assetPairIds.Contains(x.AssetPairId, StringComparer.InvariantCultureIgnoreCase))
                         .ToList();
            }

            return(Ok(ResponseModel <IReadOnlyCollection <TickerModel> > .Ok(result)));
        }
Example #11
0
        public override Task <ActiveValidatorsResponse> GetActiveValidators(ActiveValidatorsRequest request, ServerCallContext context)
        {
            var tenantId = request.TenantId;

            var listAll = _validatorLinkReader.Get()
                          .Where(v => v.TenantId == tenantId)
                          .Where(v => v.IsAccepted)
                          .Where(v => !v.IsBlocked)
                          .Select(v => new { v.ValidatorId, v.PublicKeyPem });

            var response = new ActiveValidatorsResponse();

            var hashset = new HashSet <string>();

            foreach (var item in listAll)
            {
                if (!hashset.Contains(item.ValidatorId))
                {
                    response.ActiveValidatorsRequest.Add(new ActiveValidatorsResponse.Types.ActiveValidator()
                    {
                        ValidatorId           = item.ValidatorId,
                        ValidatorPublicKeyPem = item.PublicKeyPem
                    });
                    hashset.Add(item.ValidatorId);
                }
            }

            _logger.LogInformation("Return validator list TenantId={TenantId}; Count={Count}", tenantId, response.ActiveValidatorsRequest.Count);

            return(Task.FromResult(response));
        }
Example #12
0
        private async Task <BitgoCoinEntity> GetCoin(string bitgoCoin)
        {
            var coin = _bitgoCoinReader.Get(BitgoCoinEntity.GeneratePartitionKey(), BitgoCoinEntity.GenerateRowKey(bitgoCoin));

            if (coin == null)
            {
                await Task.Delay(5000);

                coin = _bitgoCoinReader.Get(BitgoCoinEntity.GeneratePartitionKey(), BitgoCoinEntity.GenerateRowKey(bitgoCoin));
            }

            if (coin == null)
            {
                throw new Exception($"BitGo coin '{bitgoCoin}' does not exist.");
            }

            return(coin);
        }
        IAssetPair IMarketProfileClient.Get(string id)
        {
            try
            {
                var data = _readerAssetPairNoSql.Get(
                    AssetPairPriceNoSql.GeneratePartitionKey(),
                    AssetPairPriceNoSql.GenerateRowKey(id));

                return(data?.AssetPair);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Cannot read from MyNoSQL. Table: ${AssetPairPriceNoSql.TableName}, " +
                                  $"PK: {AssetPairPriceNoSql.GeneratePartitionKey()}, " +
                                  $"RK: {AssetPairPriceNoSql.GenerateRowKey(id)}, Ex: {ex}");
                throw;
            }
        }
Example #14
0
        public async Task <IReadOnlyList <AssetPair> > GetAssetPairsByTenant(string tenantId)
        {
            var pairs = _assetPairsReader.Get(AssetPairsEntity.GetPartitionKey(tenantId), AssetPairsEntity.GetRowKey());

            if (pairs?.AssetPairs == null)
            {
                return(new List <AssetPair>());
            }

            return(pairs.AssetPairs);
        }
Example #15
0
        public async Task <IReadOnlyList <Asset> > GetAssetsByTenant(string tenantId)
        {
            var assets = _assetsReader.Get(AssetsEntity.GetPartitionKey(tenantId), AssetsEntity.GetRowKey());

            if (assets?.Assets == null)
            {
                return(new List <Asset>());
            }

            return(assets.Assets);
        }
        public async Task <ClientProfileEntity> GetClientProfile(string tenantId, long clientId)
        {
            var profile = _clientProfileDataReader.Get(ClientProfileEntity.GeneratePartitionKey(tenantId),
                                                       ClientProfileEntity.GenerateRowKey(clientId));

            if (profile == null)
            {
                profile = await CreateClientProfile(tenantId, clientId);
            }

            return(profile);
        }
        public async Task RemoveValidatorApiKeyAsync([FromBody] ValidatorRequest request)
        {
            var(auth, tenantId, adminId, adminEmail) = Authorize();

            if (!auth)
            {
                return;
            }

            var entity = _validationReader.Get(ValidatorLinkEntity.GeneratePartitionKey(tenantId),
                                               ValidatorLinkEntity.GenerateRowKey(request.ApiKeyId));

            if (entity == null)
            {
                return;
            }

            await _validationWriter.DeleteAsync(entity.PartitionKey, entity.RowKey);

            _logger.LogInformation("Removed validator Api Key: {ApiKeyId}; AdminId: {AdminId}; Name: {Name}; Device: {Device}; TenantId: {TenantId}", request.ApiKeyId, adminId, entity.Name, entity.DeviceInfo, tenantId);
        }
Example #18
0
        public async Task <GetBlockchainAddressInfoResponse> GetBlockchainAddressDetailsAsync(
            GetAddressInfoRequest request)
        {
            var entity = _blockchainDepositAddressReader.Get(
                BlockchainDepositAddressEntity.GeneratePartitionKey(request.AssetSymbol, request.Blockchain,
                                                                    request.Address), BlockchainDepositAddressEntity.GenerateRowKey());

            if (entity != null)
            {
                return new GetBlockchainAddressInfoResponse
                       {
                           Address = new DepositAddress(entity.Address)
                       }
            }
            ;

            return(await _service.GetBlockchainAddressDetailsAsync(request));
        }
    }
Example #19
0
        public override async Task <PingResponse> GetPing(PingRequest request, ServerCallContext context)
        {
            var validatorId = context.GetHttpContext().User.GetClaimOrDefault(Claims.KeyKeeperId);
            var publicKey   = context.GetHttpContext().User.GetClaimOrDefault(Claims.PublicKeyPem);

            if (string.IsNullOrEmpty(publicKey))
            {
                return(new PingResponse()
                {
                    Error = new ValidatorApiError()
                    {
                        Code = ValidatorApiError.Types.ErrorCodes.InternalServerError,
                        Message = "Incorrect Bearer Token."
                    }
                });
            }

            var message = _pingMessageReader.Get(PingMessageMyNoSqlEntity.GeneratePartitionKey(validatorId),
                                                 PingMessageMyNoSqlEntity.GenerateRowKey());

            var response = new PingResponse();

            if (message == null)
            {
                response.MessageEnc       = string.Empty;
                response.SignatureMessage = string.Empty;
            }
            else
            {
                var asynccrypto = new AsymmetricEncryptionService();
                var messageEnc  = asynccrypto.Encrypt(Encoding.UTF8.GetBytes(message.Message), publicKey);

                response.MessageEnc       = Convert.ToBase64String(messageEnc);
                response.SignatureMessage = "not-implemented-please-skip";

                await _pingMessageWriter.DeleteAsync(message.PartitionKey, message.RowKey);
            }

            _logger.LogInformation("GetPing response. ValidatorId='{ValidatorId}'; HasMessage={HasMessage}", validatorId, !string.IsNullOrEmpty(response.MessageEnc));

            return(response);
        }
Example #20
0
        public async Task <IReadOnlyCollection <Balance> > GetBalancesAsync(string walletId)
        {
            var result          = new List <Balance>();
            var balanceEntities = _balancesReader.Get(walletId);

            if (balanceEntities.Any())
            {
                result.AddRange(_mapper.Map <IReadOnlyCollection <Balance> >(balanceEntities));
            }
            else
            {
                var balances = await _balanceClient.GetBalanceAsync(walletId);

                result.AddRange(balances);
            }

            await SetBalancesAccuracyAsync(result);

            return(result);
        }
        async Task <List <string> > IAvailableAssetClient.GetAssetIds(string clientId, bool isIosDevice)
        {
            try
            {
                var data = _readerAssetConditionNoSql.Get(
                    AssetConditionNoSql.GeneratePartitionKey(clientId),
                    AssetConditionNoSql.GenerateRowKey());

                if (data?.AssetConditions != null)
                {
                    return(data.AssetConditions.Where(o => o.AvailableToClient == true).Select(o => o.Asset).ToList());
                }
            }
            catch (Exception ex)
            {
                _log.Error(ex, $"Cannot read from MyNoSQL. Table: ${AssetConditionNoSql.TableName}, PK: {AssetConditionNoSql.GeneratePartitionKey(clientId)}, RK: {AssetConditionNoSql.GenerateRowKey()}");
                throw;
            }

            var result = await HttpClient.ClientGetAssetIdsAsync(clientId, isIosDevice);

            return(result.ToList());
        }
        public async Task <IActionResult> GetActiveOrders(
            [FromQuery] string assetPairId = null,
            [FromQuery] int?offset         = 0,
            [FromQuery] int?take           = 100
            )
        {
            var result = await _validationService.ValidateOrdersRequestAsync(assetPairId, offset, take);

            if (result != null)
            {
                throw HftApiException.Create(result.Code, result.Message).AddField(result.FieldName);
            }

            var statuses = new List <string> {
                OrderStatus.Placed.ToString(), OrderStatus.PartiallyMatched.ToString()
            };

            var orders = _ordersReader.Get(User.GetWalletId(), offset ?? 0, take ?? 100,
                                           x => (string.IsNullOrEmpty(assetPairId) || x.AssetPairId == assetPairId) && statuses.Contains(x.Status));

            var ordersModel = _mapper.Map <IReadOnlyCollection <OrderModel> >(orders);

            return(Ok(ResponseModel <IReadOnlyCollection <OrderModel> > .Ok(ordersModel)));
        }
        public async Task <SendTransactionResponse> SignAndSendTransactionAsync(SendTransactionRequest request)
        {
            _logger.LogInformation("Transfer Request: {jsonText}", JsonConvert.SerializeObject(request));

            try
            {
                var client = _bitGoClientService.GetByUser(request.BrokerId, BitGoUserNoSqlEntity.TechSignerId, request.BitgoCoin);
                if (client == null)
                {
                    throw new Exception($"Tech account is not configured, id = {BitGoUserNoSqlEntity.TechSignerId}, coin = {request.BitgoCoin}");
                }

                var wallet = _myNoSqlServerWalletDataReader.Get(
                    BitGoWalletNoSqlEntity.GeneratePartitionKey(request.BrokerId),
                    BitGoWalletNoSqlEntity.GenerateRowKey(request.BitgoWalletId));

                if (string.IsNullOrEmpty(wallet?.Wallet?.ApiKey))
                {
                    _logger.LogError("Cannot find pass phase for wallet {bitgoWalletIdText}", request.BitgoWalletId);
                    throw new Exception($"Cannot find pass phase for wallet {request.BitgoWalletId}");
                }

                var walletPass = _encryptionService.Decrypt(wallet.Wallet.ApiKey);

                var result = await client.SendCoinsAsync(request.BitgoCoin, request.BitgoWalletId,
                                                         walletPass, request.SequenceId,
                                                         request.Amount, request.Address);

                if (!result.Success)
                {
                    switch (result.Error.Code)
                    {
                    case "DuplicateSequenceIdError":
                    {
                        var transaction = await client.GetTransferBySequenceIdAsync(request.BitgoCoin,
                                                                                    request.BitgoWalletId, request.SequenceId);

                        if (!transaction.Success || transaction.Data == null)
                        {
                            _logger.LogError("Transfer is Duplicate, but cannot found transaction: {jsonText}",
                                             JsonConvert.SerializeObject(transaction.Error));

                            return(new SendTransactionResponse()
                                {
                                    Error = result.Error
                                });
                        }

                        _logger.LogInformation("Transfer is Duplicate, Result: {jsonText}",
                                               JsonConvert.SerializeObject(transaction.Data));

                        return(new SendTransactionResponse()
                            {
                                DuplicateTransaction = transaction.Data
                            });
                    }

                    case "needs unlock":
                        await _sessionPublisher.PublishAsync(new SignalBitGoSessionStateUpdate()
                        {
                            State = BitGoSessionState.Locked
                        });

                        return(new SendTransactionResponse()
                        {
                            Error =
                            {
                                Code         = "needs unlock",
                                ErrorMessage = "Session is locked"
                            }
                        });
                    }
                }

                if (!result.Success)
                {
                    _logger.LogError("Transfer Result: {jsonText}", JsonConvert.SerializeObject(result.Error));
                    return(new SendTransactionResponse()
                    {
                        Error = result.Error
                    });
                }

                await AddVolumeToApiKey(request.BrokerId, _encryptionService.GetSha256Hash(wallet.Wallet.ApiKey),
                                        request.BitgoCoin,
                                        _assetMapper.ConvertAmountFromBitgo(request.BitgoCoin, long.Parse(request.Amount)));

                _logger.LogInformation("Transfer Result: {jsonText}", JsonConvert.SerializeObject(result.Data));
                return(new SendTransactionResponse()
                {
                    Result = result.Data
                });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Transfer Request ERROR: {jsonText}. Error: {message}",
                                 JsonConvert.SerializeObject(request), ex.Message);
                throw;
            }
        }
Example #24
0
 public SessionEntity GetSession(string sessionId)
 {
     return(_sessionsReader.Get(SessionEntity.GetPk(), sessionId.ToSha256()));
 }
Example #25
0
        public override async Task <AcceptResponse> Accept(AcceptRequest request, ServerCallContext context)
        {
            var validatorId = request.ValidatorId;

            var validatorLinkEntity = _validatorLinkReader.Get().FirstOrDefault(v => v.InvitationToken == request.InviteId);

            if (validatorLinkEntity == null)
            {
                _logger.LogInformation("Cannot accept invitation: 'Invitation token is not correct'. InviteId='{InviteId}'; ValidatorId='{ValidatorId}'", request.InviteId, request.ValidatorId);

                return(new AcceptResponse()
                {
                    Error = new ValidatorApiError()
                    {
                        Code = ValidatorApiError.Types.ErrorCodes.WrongInvitation,
                        Message = "Invitation token is not correct"
                    }
                });
            }

            if (validatorLinkEntity.IsAccepted)
            {
                _logger.LogInformation("Cannot accept invitation: 'Invitation token already accepted'. InviteId='{InviteId}'; ValidatorId='{ValidatorId}'", request.InviteId, request.ValidatorId);

                return(new AcceptResponse()
                {
                    Error = new ValidatorApiError()
                    {
                        Code = ValidatorApiError.Types.ErrorCodes.WrongInvitation,
                        Message = "Invitation token already accepted"
                    }
                });
            }

            if (string.IsNullOrEmpty(request.PublicKeyPem))
            {
                _logger.LogInformation("Cannot accept invitation: 'PublicKeyPem cannot be empty'. InviteId='{InviteId}'; ValidatorId='{ValidatorId}'", request.InviteId, request.ValidatorId);

                return(new AcceptResponse()
                {
                    Error = new ValidatorApiError()
                    {
                        Code = ValidatorApiError.Types.ErrorCodes.WrongInvitation,
                        Message = "PublicKeyPem cannot be empty"
                    }
                });
            }

            var token = GenerateJwtToken(validatorId,
                                         request.PublicKeyPem,
                                         validatorLinkEntity.ApiKeyId,
                                         validatorLinkEntity.TenantId);

            var resp = new AcceptResponse
            {
                ApiKey      = token,
                Name        = validatorLinkEntity.Name,
                Position    = validatorLinkEntity.Position,
                Description = validatorLinkEntity.Description
            };

            validatorLinkEntity.ValidatorId              = request.ValidatorId;
            validatorLinkEntity.PublicKeyPem             = request.PublicKeyPem;
            validatorLinkEntity.IsAccepted               = true;
            validatorLinkEntity.DeviceInfo               = request.DeviceInfo;
            validatorLinkEntity.PushNotificationFcmToken = request.PushNotificationFCMToken;
            await _validatorLinkWriter.InsertOrReplaceAsync(validatorLinkEntity);

            _logger.LogInformation("Invitation accepted. InviteId='{InviteId}'; ValidatorId='{ValidatorId}'; PushNotificationFcmToken='{PushNotificationFcmToken}'", request.InviteId, request.ValidatorId, request.PushNotificationFCMToken);

            return(resp);
        }
        public async Task HandledDepositAsync(SignalBitGoTransfer transferId)
        {
            transferId.AddToActivityAsJsonTag("bitgo signal");

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

            var(brokerId, assetSymbol) = _assetMapper.BitgoCoinToAsset(transferId.Coin, transferId.WalletId);

            if (string.IsNullOrEmpty(brokerId) || string.IsNullOrEmpty(assetSymbol))
            {
                _logger.LogWarning("Cannot handle BitGo deposit, asset do not found {transferJson}",
                                   JsonConvert.SerializeObject(transferId));
                return;
            }

            var coin = _bitgoCoinReader.Get(BitgoCoinEntity.GeneratePartitionKey(),
                                            BitgoCoinEntity.GenerateRowKey(transferId.Coin));

            var transferResp =
                await _bitgoClient.Get(coin.IsMainNet)
                .GetTransferAsync(transferId.Coin, transferId.WalletId, transferId.TransferId);

            var transfer = transferResp.Data;

            if (transfer == null)
            {
                _logger.LogWarning("Cannot handle BitGo deposit, transfer do not found {transferJson}",
                                   JsonConvert.SerializeObject(transferId));
                Activity.Current?.SetStatus(Status.Error);
                return;
            }

            transfer.AddToActivityAsJsonTag("bitgo-transfer");

            _logger.LogInformation("Transfer from BitGo: {transferJson}", JsonConvert.SerializeObject(transfer));

            var requirement = _assetMapper.GetRequiredConfirmations(transfer.Coin);

            if (transfer.Confirmations < requirement)
            {
                _logger.LogError(
                    $"Transaction do not has enough conformations. Transaction has: {transfer.Confirmations}, requirement: {requirement}");
                Activity.Current?.SetStatus(Status.Error);
                throw new Exception(
                          $"Transaction do not has enough conformations. Transaction has: {transfer.Confirmations}, requirement: {requirement}");
            }

            if (!_assetMapper.IsWalletEnabled(transfer.Coin, transfer.WalletId))
            {
                _logger.LogError(
                    "Transfer {transferIdString} from BitGo is skipped, Wallet do not include in enabled wallet list",
                    transfer.TransferId);
                Activity.Current?.SetStatus(Status.Error);
                return;
            }

            foreach (var entryGroup in transfer.Entries
                     .Where(e => e.Value > 0 && !string.IsNullOrEmpty(e.Label) && e.WalletId == transferId.WalletId &&
                            (e.Token == null || e.Token == transferId.Coin))
                     .GroupBy(e => e.Label))
            {
                var label  = entryGroup.Key;
                var wallet = _walletMapper.BitgoLabelToWallet(label);
                if (wallet == null)
                {
                    _logger.LogWarning("Cannot found wallet for transfer entry with label={label} address={address}",
                                       label, entryGroup.First().Address);
                    continue;
                }

                wallet.WalletId.AddToActivityAsTag("walletId");
                wallet.ClientId.AddToActivityAsTag("clientId");

                var bitgoAmount = entryGroup.Sum(e => e.Value);
                var meAmount    = _assetMapper.ConvertAmountFromBitgo(transferId.Coin, bitgoAmount);

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

                var roundedAmount = Math.Round(meAmount, accuracy, MidpointRounding.ToNegativeInfinity);

                try
                {
                    await using var ctx = DatabaseContext.Create(_dbContextOptionsBuilder);
                    await ctx.InsertAsync(new DepositEntity
                    {
                        BrokerId      = wallet.BrokerId,
                        WalletId      = wallet.WalletId,
                        ClientId      = wallet.ClientId,
                        TransactionId = $"{transfer.TransferId}:{wallet.WalletId}",
                        Amount        = (decimal)roundedAmount,
                        AssetSymbol   = assetSymbol,
                        Comment       =
                            $"Bitgo transfer [{transferId.Coin}:{transferId.WalletId}] entry label='{label}', count entry={entryGroup.Count()}",
                        Integration    = "BitGo",
                        Txid           = transfer.TxId,
                        Status         = DepositStatus.New,
                        EventDate      = DateTime.UtcNow,
                        UpdateDate     = DateTime.UtcNow,
                        FeeAmount      = 0,
                        FeeAssetSymbol = assetSymbol,
                        CardLast4      = string.Empty
                    });
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    throw;
                }
            }

            _logger.LogInformation("Deposit request from BitGo {transferIdString} is handled", transfer.TransferId);
        }
Example #27
0
        public OrderBookEntity OrderBook(string tenantId, string assetPairId)
        {
            var book = _orderBookDataReader.Get(OrderBookEntity.GeneratePartitionKey(tenantId), OrderBookEntity.GenerateRowKey(assetPairId));

            return(book);
        }
Example #28
0
        public void Start()
        {
            _pricesReader.SubscribeToChanges(prices =>
            {
                var tasks = new List <Task>();

                foreach (var price in prices)
                {
                    tasks.Add(_priceStream.WriteToStreamAsync(_mapper.Map <PriceUpdate>(price), price.AssetPairId));
                }

                Task.WhenAll(tasks).GetAwaiter().GetResult();
            });

            _candlesReader.SubscribeToChanges(candles =>
            {
                var tasks = new List <Task>();

                foreach (var candle in candles)
                {
                    var key = $"{candle.AssetPairId}_{candle.PriceType}_{candle.TimeInterval}";
                    tasks.Add(_candlesStream.WriteToStreamAsync(_mapper.Map <CandleUpdate>(candle), key));
                }

                Task.WhenAll(tasks).GetAwaiter().GetResult();
            });

            _tickersReader.SubscribeToChanges(tickers =>
            {
                var tasks = new List <Task>();

                foreach (var ticker in tickers)
                {
                    var priceEntity = _pricesReader.Get(PriceEntity.GetPk(), ticker.AssetPairId);

                    var priceUpdate = new PriceUpdate
                    {
                        AssetPairId    = ticker.AssetPairId,
                        VolumeBase24H  = ticker.VolumeBase.ToString(CultureInfo.InvariantCulture),
                        VolumeQuote24H = ticker.VolumeQuote.ToString(CultureInfo.InvariantCulture),
                        PriceChange24H = ticker.PriceChange.ToString(CultureInfo.InvariantCulture),
                        Timestamp      = Timestamp.FromDateTime(ticker.UpdatedDt.ToUniversalTime())
                    };

                    if (priceEntity != null)
                    {
                        priceUpdate.Ask = priceEntity.Ask.ToString(CultureInfo.InvariantCulture);
                        priceUpdate.Bid = priceEntity.Bid.ToString(CultureInfo.InvariantCulture);
                    }

                    tasks.Add(_priceStream.WriteToStreamAsync(priceUpdate, priceUpdate.AssetPairId));
                }

                Task.WhenAll(tasks).GetAwaiter().GetResult();
            });

            _orderbooksReader.SubscribeToChanges(orderbooks =>
            {
                var tasks = new List <Task>();

                foreach (var orderbook in orderbooks)
                {
                    var item = _mapper.Map <Orderbook>(orderbook);
                    item.Asks.AddRange(_mapper.Map <List <Orderbook.Types.PriceVolume> >(orderbook.Asks));
                    item.Bids.AddRange(_mapper.Map <List <Orderbook.Types.PriceVolume> >(orderbook.Bids));
                    tasks.Add(_orderbookStream.WriteToStreamAsync(item, orderbook.AssetPairId));
                }

                Task.WhenAll(tasks).GetAwaiter().GetResult();
            });

            _sessionsReader.SubscribeToChanges(sessions => { });

            Console.WriteLine("Stream services started.");
        }
        public async Task SendPushNotifications(ApprovalRequestMyNoSqlEntity approvalRequest)
        {
            var traceLog = string.Empty;

            try
            {
                if (!_isActive)
                {
                    return;
                }

                var validators = _validatorLinkReader.Get()
                                 .Where(v => v.TenantId == approvalRequest.TenantId)
                                 .Where(v => v.ValidatorId == approvalRequest.ValidatorId)
                                 .Where(v => v.IsAccepted)
                                 .Where(v => !v.IsBlocked)
                                 .Where(v => !string.IsNullOrEmpty(v.DeviceInfo))
                                 .Where(v => !string.IsNullOrEmpty(v.PushNotificationFcmToken));

                var hashset = new HashSet <string>();
                var tokens  = new List <string>();

                foreach (var validator in validators)
                {
                    if (!hashset.Contains(validator.DeviceInfo))
                    {
                        tokens.Add(validator.PushNotificationFcmToken);
                        hashset.Add(validator.DeviceInfo);
                    }
                }

                if (!tokens.Any())
                {
                    _logger.LogInformation(
                        "Push notification for TransferSigningRequestId={TransferSigningRequestId} is sent to {ValidatorId}. SuccessCount: {SuccessCount}. FailureCount: {FailureCount}.",
                        approvalRequest.TransferSigningRequestId,
                        approvalRequest.ValidatorId,
                        0,
                        0);
                    return;
                }

                var message = new MulticastMessage()
                {
                    Notification = new Notification()
                    {
                        Title = "New Approval Request",
                        Body  =
                            "You receive a new approval request. Please check the transfer details in the application."
                    },
                    Tokens = tokens
                };

                traceLog = JsonConvert.SerializeObject(message);

                var response = await FirebaseMessaging.DefaultInstance.SendMulticastAsync(message);

                _logger.LogInformation(
                    "Push notification for TransferSigningRequestId={TransferSigningRequestId} is sent to {ValidatorId}. SuccessCount: {SuccessCount}. FailureCount: {FailureCount}.",
                    approvalRequest.TransferSigningRequestId,
                    approvalRequest.ValidatorId,
                    response.SuccessCount,
                    response.FailureCount);

                if (response.FailureCount > 0)
                {
                    var tokensList = JsonConvert.SerializeObject(tokens);

                    foreach (var result in response.Responses.Where(r => r.IsSuccess == false))
                    {
                        _logger.LogWarning(
                            "Fail push notification. TransferSigningRequestId: {TransferSigningRequestId}; ValidatorId: {ValidatorId}; MessageId: {MessageId}; MessagingErrorCode: {MessagingErrorCode}; Message: {Message}; Tokens: {TokensList}",
                            approvalRequest.TransferSigningRequestId,
                            approvalRequest.ValidatorId,
                            result.MessageId,
                            result.Exception.MessagingErrorCode,
                            result.Exception.Message,
                            tokensList);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(traceLog);
                _logger.LogError(ex, "Cannot send push notification to validator. TransferSigningRequestId={TransferSigningRequestId}; ValidatorId={ValidatorId}", approvalRequest.TransferSigningRequestId, approvalRequest.ValidatorId);
            }
        }
Example #30
0
        public IReadOnlyList <PriceEntity> GetPrices(string tenantId)
        {
            var prices = _priceDataReader.Get(PriceEntity.GeneratePartitionKey(tenantId));

            return(prices);
        }