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]); } }
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; } }