public async ValueTask <bool> DeleteBitgoCoinEntityAsync(string coin)
        {
            if (string.IsNullOrEmpty(coin))
            {
                throw new Exception("Cannot update coin. Coin cannot be empty");
            }

            var entity = await _bitgoCoins.GetAsync(BitgoCoinEntity.GeneratePartitionKey(),
                                                    BitgoCoinEntity.GenerateRowKey(coin));

            if (entity != null)
            {
                var assets = await _assetMap.GetAsync();

                var existAssets = assets.Any(e => e.BitgoCoin == coin);
                if (existAssets)
                {
                    throw new Exception("Cannot delete coin. Asset used it as coin");
                }

                await _bitgoCoins.DeleteAsync(BitgoCoinEntity.GeneratePartitionKey(),
                                              BitgoCoinEntity.GenerateRowKey(coin));
            }

            return(true);
        }
        public async ValueTask <bool> UpdateBitgoCoinEntityAsync(string coin, int accuracy, int requiredConfirmations, bool isMainNet)
        {
            if (string.IsNullOrEmpty(coin))
            {
                throw new Exception("Cannot update coin. Coin cannot be empty");
            }
            if (accuracy < 0)
            {
                throw new Exception("Cannot update coin. Accuracy can't be less then 0");
            }
            if (requiredConfirmations < 0)
            {
                throw new Exception("Cannot update coin. RequiredConfirmations can't be less then 0");
            }

            var entity = BitgoCoinEntity.Create(coin, accuracy, requiredConfirmations, isMainNet);

            var existingItem = await _bitgoCoins.GetAsync(entity.PartitionKey, entity.RowKey);

            if (existingItem == null)
            {
                throw new Exception("Cannot update coin. Coin not found");
            }

            await _bitgoCoins.InsertOrReplaceAsync(entity);

            return(true);
        }
        private static async Task GenerateAsstToBitgoMap()
        {
            var broker         = "jetwallet";
            var nosqlWriterUrl = "http://192.168.10.80:5123";

            var clientAsset =
                new MyNoSqlServerDataWriter <BitgoAssetMapEntity>(() => nosqlWriterUrl, BitgoAssetMapEntity.TableName,
                                                                  true);

            var list = new List <BitgoAssetMapEntity>();

            list.Add(BitgoAssetMapEntity.Create(broker, "BTC", "6054ba9ca9cc0e0024a867a7d8b401b2", "", "tbtc", 0));
            list.Add(BitgoAssetMapEntity.Create(broker, "XLM", "6054bc003dc1af002b0d54bf5b552f28", "", "txlm", 0));
            list.Add(BitgoAssetMapEntity.Create(broker, "LTC", "6054be73b765620006aa87311f43bd47", "", "tltc", 0));
            list.Add(BitgoAssetMapEntity.Create(broker, "XRP", "60584aaded0090000628ce59c01f3a5e", "", "txrp", 0));
            list.Add(BitgoAssetMapEntity.Create(broker, "BCH", "60584b79fd3e0500669e2cf9654d726b", "", "tbch", 0));
            list.Add(BitgoAssetMapEntity.Create(broker, "ALGO", "60584becbc3e2600240548d78e61c02b", "", "talgo", 0));
            list.Add(BitgoAssetMapEntity.Create(broker, "EOS", "60584dcc6f5d31001d5a59371aeeb60a", "", "teos", 0));

            await clientAsset.CleanAndKeepMaxPartitions(0);

            await clientAsset.BulkInsertOrReplaceAsync(list);


            var clientCoin =
                new MyNoSqlServerDataWriter <BitgoCoinEntity>(() => nosqlWriterUrl, BitgoCoinEntity.TableName, true);

            var listCoin = new List <BitgoCoinEntity>();

            listCoin.Add(BitgoCoinEntity.Create("algo", 6, 1, false));
            listCoin.Add(BitgoCoinEntity.Create("bch", 8, 1, false));
            listCoin.Add(BitgoCoinEntity.Create("btc", 8, 1, false));
            listCoin.Add(BitgoCoinEntity.Create("dash", 6, 1, false));
            listCoin.Add(BitgoCoinEntity.Create("eos", 4, 1, false));
            //listCoin.Add(BitgoCoinEntity.Create("eth", 18));
            //listCoin.Add(BitgoCoinEntity.Create("hbar", 0));
            listCoin.Add(BitgoCoinEntity.Create("ltc", 8, 1, false));
            listCoin.Add(BitgoCoinEntity.Create("trx", 6, 1, false));
            listCoin.Add(BitgoCoinEntity.Create("xlm", 7, 1, false));
            listCoin.Add(BitgoCoinEntity.Create("xrp", 6, 1, false));
            listCoin.Add(BitgoCoinEntity.Create("zec", 8, 1, false));

            listCoin.Add(BitgoCoinEntity.Create("talgo", 6, 1, false));
            listCoin.Add(BitgoCoinEntity.Create("tbch", 8, 1, false));
            listCoin.Add(BitgoCoinEntity.Create("tbtc", 8, 1, false));
            listCoin.Add(BitgoCoinEntity.Create("tdash", 6, 1, false));
            listCoin.Add(BitgoCoinEntity.Create("teos", 4, 1, false));
            //listCoin.Add(BitgoCoinEntity.Create("teth", 18));
            //listCoin.Add(BitgoCoinEntity.Create("thbar", 0));
            listCoin.Add(BitgoCoinEntity.Create("tltc", 8, 1, false));
            listCoin.Add(BitgoCoinEntity.Create("ttrx", 6, 1, false));
            listCoin.Add(BitgoCoinEntity.Create("txlm", 7, 1, false));
            listCoin.Add(BitgoCoinEntity.Create("txrp", 6, 1, false));
            listCoin.Add(BitgoCoinEntity.Create("tzec", 8, 1, false));

            await clientCoin.CleanAndKeepMaxRecords(BitgoCoinEntity.TableName, 0);

            await clientCoin.BulkInsertOrReplaceAsync(listCoin);
        }
        public double ConvertAmountFromBitgo(string coin, long 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.AmountFromAbsoluteValue(amount));
        }
        public decimal ConvertAmountToBitgo(string coin, double amount)
        {
            var coinSettings = _bitgoCoins.Get(BitgoCoinEntity.GeneratePartitionKey(),
                                               BitgoCoinEntity.GenerateRowKey(coin));

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

            return(coinSettings.AmountToAbsoluteValueDecimal(amount));
        }
        public int GetRequiredConfirmations(string coin)
        {
            var coinSettings = _bitgoCoins.Get(BitgoCoinEntity.GeneratePartitionKey(),
                                               BitgoCoinEntity.GenerateRowKey(coin));

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

            return(coinSettings.RequiredConfirmations);
        }
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
        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);
        }
Example #9
0
        public async ValueTask <bool> UpdateBitgoAssetMapEntityAsync(string brokerId, string assetSymbol,
                                                                     string bitgoWalletId, string enabledBitgoWalletIds, string bitgoCoin, double minBalance, string tagSeparator)
        {
            if (string.IsNullOrEmpty(brokerId))
            {
                throw new Exception("Cannot update asset map. BrokerId cannot be empty");
            }
            if (string.IsNullOrEmpty(assetSymbol))
            {
                throw new Exception("Cannot update asset map. AssetSymbol cannot be empty");
            }
            if (string.IsNullOrEmpty(bitgoWalletId))
            {
                throw new Exception("Cannot update asset map. BitgoWalletId cannot be empty");
            }
            if (string.IsNullOrEmpty(bitgoCoin))
            {
                throw new Exception("Cannot update asset map. BitgoCoin cannot be empty");
            }

            var coin = await _bitgoCoins.GetAsync(BitgoCoinEntity.GeneratePartitionKey(),
                                                  BitgoCoinEntity.GenerateRowKey(bitgoCoin));

            if (coin == null)
            {
                throw new Exception("Cannot update asset map. Unknown BitgoCoin.");
            }

            var entity = BitgoAssetMapEntity.Create(brokerId, assetSymbol, bitgoWalletId,
                                                    enabledBitgoWalletIds, bitgoCoin, minBalance, tagSeparator);

            var existingEntity = await _assetMap.GetAsync(entity.PartitionKey, entity.RowKey);

            if (existingEntity == null)
            {
                throw new Exception("Cannot update asset map. Asset map not found");
            }

            await _assetMap.InsertOrReplaceAsync(entity);

            return(true);
        }
        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);
        }